Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
Required manual fix of devlink/devlink.c Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
This commit is contained in:
commit
2363bc99f9
|
|
@ -10,6 +10,9 @@ void print_vlan_info(struct rtattr *tb, int ifindex);
|
||||||
int print_linkinfo(struct nlmsghdr *n, void *arg);
|
int print_linkinfo(struct nlmsghdr *n, void *arg);
|
||||||
int print_mdb_mon(struct nlmsghdr *n, void *arg);
|
int print_mdb_mon(struct nlmsghdr *n, void *arg);
|
||||||
int print_fdb(struct nlmsghdr *n, void *arg);
|
int print_fdb(struct nlmsghdr *n, void *arg);
|
||||||
|
void print_stp_state(__u8 state);
|
||||||
|
int parse_stp_state(const char *arg);
|
||||||
|
int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor);
|
||||||
|
|
||||||
int do_fdb(int argc, char **argv);
|
int do_fdb(int argc, char **argv);
|
||||||
int do_mdb(int argc, char **argv);
|
int do_mdb(int argc, char **argv);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
static unsigned int filter_index;
|
static unsigned int filter_index;
|
||||||
|
|
||||||
static const char *port_states[] = {
|
static const char *stp_states[] = {
|
||||||
[BR_STATE_DISABLED] = "disabled",
|
[BR_STATE_DISABLED] = "disabled",
|
||||||
[BR_STATE_LISTENING] = "listening",
|
[BR_STATE_LISTENING] = "listening",
|
||||||
[BR_STATE_LEARNING] = "learning",
|
[BR_STATE_LEARNING] = "learning",
|
||||||
|
|
@ -68,16 +68,31 @@ static void print_link_flags(FILE *fp, unsigned int flags, unsigned int mdown)
|
||||||
close_json_array(PRINT_ANY, "> ");
|
close_json_array(PRINT_ANY, "> ");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_portstate(__u8 state)
|
void print_stp_state(__u8 state)
|
||||||
{
|
{
|
||||||
if (state <= BR_STATE_BLOCKING)
|
if (state <= BR_STATE_BLOCKING)
|
||||||
print_string(PRINT_ANY, "state",
|
print_string(PRINT_ANY, "state",
|
||||||
"state %s ", port_states[state]);
|
"state %s ", stp_states[state]);
|
||||||
else
|
else
|
||||||
print_uint(PRINT_ANY, "state",
|
print_uint(PRINT_ANY, "state",
|
||||||
"state (%d) ", state);
|
"state (%d) ", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_stp_state(const char *arg)
|
||||||
|
{
|
||||||
|
size_t nstates = ARRAY_SIZE(stp_states);
|
||||||
|
int state;
|
||||||
|
|
||||||
|
for (state = 0; state < nstates; state++)
|
||||||
|
if (strcmp(stp_states[state], arg) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (state == nstates)
|
||||||
|
state = -1;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
static void print_hwmode(__u16 mode)
|
static void print_hwmode(__u16 mode)
|
||||||
{
|
{
|
||||||
if (mode >= ARRAY_SIZE(hw_mode))
|
if (mode >= ARRAY_SIZE(hw_mode))
|
||||||
|
|
@ -96,7 +111,7 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
||||||
parse_rtattr_nested(prtb, IFLA_BRPORT_MAX, attr);
|
parse_rtattr_nested(prtb, IFLA_BRPORT_MAX, attr);
|
||||||
|
|
||||||
if (prtb[IFLA_BRPORT_STATE])
|
if (prtb[IFLA_BRPORT_STATE])
|
||||||
print_portstate(rta_getattr_u8(prtb[IFLA_BRPORT_STATE]));
|
print_stp_state(rta_getattr_u8(prtb[IFLA_BRPORT_STATE]));
|
||||||
|
|
||||||
if (prtb[IFLA_BRPORT_PRIORITY])
|
if (prtb[IFLA_BRPORT_PRIORITY])
|
||||||
print_uint(PRINT_ANY, "priority",
|
print_uint(PRINT_ANY, "priority",
|
||||||
|
|
@ -161,7 +176,7 @@ static void print_protinfo(FILE *fp, struct rtattr *attr)
|
||||||
print_on_off(PRINT_ANY, "isolated", "isolated %s ",
|
print_on_off(PRINT_ANY, "isolated", "isolated %s ",
|
||||||
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
|
||||||
} else
|
} else
|
||||||
print_portstate(rta_getattr_u8(attr));
|
print_stp_state(rta_getattr_u8(attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -359,14 +374,11 @@ static int brlink_modify(int argc, char **argv)
|
||||||
} else if (strcmp(*argv, "state") == 0) {
|
} else if (strcmp(*argv, "state") == 0) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
char *endptr;
|
char *endptr;
|
||||||
size_t nstates = ARRAY_SIZE(port_states);
|
|
||||||
|
|
||||||
state = strtol(*argv, &endptr, 10);
|
state = strtol(*argv, &endptr, 10);
|
||||||
if (!(**argv != '\0' && *endptr == '\0')) {
|
if (!(**argv != '\0' && *endptr == '\0')) {
|
||||||
for (state = 0; state < nstates; state++)
|
state = parse_stp_state(*argv);
|
||||||
if (strcasecmp(port_states[state], *argv) == 0)
|
if (state == -1) {
|
||||||
break;
|
|
||||||
if (state == nstates) {
|
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Error: invalid STP port state\n");
|
"Error: invalid STP port state\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,9 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "libnetlink.h"
|
#include "libnetlink.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "br_common.h"
|
#include "br_common.h"
|
||||||
#include "rt_names.h"
|
#include "rt_names.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "json_print.h"
|
#include "json_print.h"
|
||||||
|
|
||||||
#ifndef MDBA_RTA
|
#ifndef MDBA_RTA
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ static int prefix_banner;
|
||||||
|
|
||||||
static void usage(void)
|
static void usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | all]\n");
|
fprintf(stderr, "Usage: bridge monitor [file | link | fdb | mdb | vlan | all]\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +67,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
|
||||||
print_nlmsg_timestamp(fp, n);
|
print_nlmsg_timestamp(fp, n);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case RTM_NEWVLAN:
|
||||||
|
case RTM_DELVLAN:
|
||||||
|
if (prefix_banner)
|
||||||
|
fprintf(fp, "[VLAN]");
|
||||||
|
return print_vlan_rtm(n, arg, true);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -79,6 +85,7 @@ int do_monitor(int argc, char **argv)
|
||||||
int llink = 0;
|
int llink = 0;
|
||||||
int lneigh = 0;
|
int lneigh = 0;
|
||||||
int lmdb = 0;
|
int lmdb = 0;
|
||||||
|
int lvlan = 0;
|
||||||
|
|
||||||
rtnl_close(&rth);
|
rtnl_close(&rth);
|
||||||
|
|
||||||
|
|
@ -95,8 +102,12 @@ int do_monitor(int argc, char **argv)
|
||||||
} else if (matches(*argv, "mdb") == 0) {
|
} else if (matches(*argv, "mdb") == 0) {
|
||||||
lmdb = 1;
|
lmdb = 1;
|
||||||
groups = 0;
|
groups = 0;
|
||||||
|
} else if (matches(*argv, "vlan") == 0) {
|
||||||
|
lvlan = 1;
|
||||||
|
groups = 0;
|
||||||
} else if (strcmp(*argv, "all") == 0) {
|
} else if (strcmp(*argv, "all") == 0) {
|
||||||
groups = ~RTMGRP_TC;
|
groups = ~RTMGRP_TC;
|
||||||
|
lvlan = 1;
|
||||||
prefix_banner = 1;
|
prefix_banner = 1;
|
||||||
} else if (matches(*argv, "help") == 0) {
|
} else if (matches(*argv, "help") == 0) {
|
||||||
usage();
|
usage();
|
||||||
|
|
@ -134,6 +145,12 @@ int do_monitor(int argc, char **argv)
|
||||||
|
|
||||||
if (rtnl_open(&rth, groups) < 0)
|
if (rtnl_open(&rth, groups) < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
|
if (lvlan && rtnl_add_nl_group(&rth, RTNLGRP_BRVLAN) < 0) {
|
||||||
|
fprintf(stderr, "Failed to add bridge vlan group to list\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
ll_init_map(&rth);
|
ll_init_map(&rth);
|
||||||
|
|
||||||
if (rtnl_listen(&rth, accept_msg, stdout) < 0)
|
if (rtnl_listen(&rth, accept_msg, stdout) < 0)
|
||||||
|
|
|
||||||
254
bridge/vlan.c
254
bridge/vlan.c
|
|
@ -16,6 +16,7 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static unsigned int filter_index, filter_vlan;
|
static unsigned int filter_index, filter_vlan;
|
||||||
|
static int vlan_rtm_cur_ifidx = -1;
|
||||||
|
|
||||||
enum vlan_show_subject {
|
enum vlan_show_subject {
|
||||||
VLAN_SHOW_VLAN,
|
VLAN_SHOW_VLAN,
|
||||||
|
|
@ -33,6 +34,7 @@ static void usage(void)
|
||||||
"Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
|
"Usage: bridge vlan { add | del } vid VLAN_ID dev DEV [ tunnel_info id TUNNEL_ID ]\n"
|
||||||
" [ pvid ] [ untagged ]\n"
|
" [ pvid ] [ untagged ]\n"
|
||||||
" [ self ] [ master ]\n"
|
" [ self ] [ master ]\n"
|
||||||
|
" bridge vlan { set } vid VLAN_ID dev DEV [ state STP_STATE ]\n"
|
||||||
" bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
|
" bridge vlan { show } [ dev DEV ] [ vid VLAN_ID ]\n"
|
||||||
" bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
|
" bridge vlan { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
|
@ -241,6 +243,100 @@ static int vlan_modify(int cmd, int argc, char **argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vlan_option_set(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct nlmsghdr n;
|
||||||
|
struct br_vlan_msg bvm;
|
||||||
|
char buf[1024];
|
||||||
|
} req = {
|
||||||
|
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)),
|
||||||
|
.n.nlmsg_flags = NLM_F_REQUEST,
|
||||||
|
.n.nlmsg_type = RTM_NEWVLAN,
|
||||||
|
.bvm.family = PF_BRIDGE,
|
||||||
|
};
|
||||||
|
struct bridge_vlan_info vinfo = {};
|
||||||
|
struct rtattr *afspec;
|
||||||
|
short vid_end = -1;
|
||||||
|
char *d = NULL;
|
||||||
|
short vid = -1;
|
||||||
|
int state = -1;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp(*argv, "dev") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
d = *argv;
|
||||||
|
} else if (strcmp(*argv, "vid") == 0) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
p = strchr(*argv, '-');
|
||||||
|
if (p) {
|
||||||
|
*p = '\0';
|
||||||
|
p++;
|
||||||
|
vid = atoi(*argv);
|
||||||
|
vid_end = atoi(p);
|
||||||
|
if (vid >= vid_end || vid_end >= 4096) {
|
||||||
|
fprintf(stderr, "Invalid VLAN range \"%hu-%hu\"\n",
|
||||||
|
vid, vid_end);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vid = atoi(*argv);
|
||||||
|
}
|
||||||
|
} else if (strcmp(*argv, "state") == 0) {
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
state = strtol(*argv, &endptr, 10);
|
||||||
|
if (!(**argv != '\0' && *endptr == '\0'))
|
||||||
|
state = parse_stp_state(*argv);
|
||||||
|
if (state == -1) {
|
||||||
|
fprintf(stderr, "Error: invalid STP state\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (matches(*argv, "help") == 0)
|
||||||
|
NEXT_ARG();
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d == NULL || vid == -1) {
|
||||||
|
fprintf(stderr, "Device and VLAN ID are required arguments.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.bvm.ifindex = ll_name_to_index(d);
|
||||||
|
if (req.bvm.ifindex == 0) {
|
||||||
|
fprintf(stderr, "Cannot find network device \"%s\"\n", d);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid >= 4096) {
|
||||||
|
fprintf(stderr, "Invalid VLAN ID \"%hu\"\n", vid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
afspec = addattr_nest(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY);
|
||||||
|
afspec->rta_type |= NLA_F_NESTED;
|
||||||
|
|
||||||
|
vinfo.flags = BRIDGE_VLAN_INFO_ONLY_OPTS;
|
||||||
|
vinfo.vid = vid;
|
||||||
|
addattr_l(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_INFO, &vinfo,
|
||||||
|
sizeof(vinfo));
|
||||||
|
if (vid_end != -1)
|
||||||
|
addattr16(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_RANGE,
|
||||||
|
vid_end);
|
||||||
|
if (state >= 0)
|
||||||
|
addattr8(&req.n, sizeof(req), BRIDGE_VLANDB_ENTRY_STATE, state);
|
||||||
|
addattr_nest_end(&req.n, afspec);
|
||||||
|
|
||||||
|
if (rtnl_talk(&rth, &req.n, NULL) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* In order to use this function for both filtering and non-filtering cases
|
/* In order to use this function for both filtering and non-filtering cases
|
||||||
* we need to make it a tristate:
|
* we need to make it a tristate:
|
||||||
* return -1 - if filtering we've gone over so don't continue
|
* return -1 - if filtering we've gone over so don't continue
|
||||||
|
|
@ -422,14 +518,8 @@ static void print_vlan_flags(__u16 flags)
|
||||||
close_json_array(PRINT_JSON, NULL);
|
close_json_array(PRINT_JSON, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
|
static void __print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
|
||||||
{
|
{
|
||||||
open_json_object(NULL);
|
|
||||||
|
|
||||||
print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
|
|
||||||
print_vlan_flags(vstats->flags);
|
|
||||||
print_nl();
|
|
||||||
|
|
||||||
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
|
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
|
||||||
print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
|
print_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
|
||||||
vstats->rx_bytes);
|
vstats->rx_bytes);
|
||||||
|
|
@ -441,6 +531,16 @@ static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
|
||||||
vstats->tx_bytes);
|
vstats->tx_bytes);
|
||||||
print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
|
print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
|
||||||
vstats->tx_packets);
|
vstats->tx_packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
|
||||||
|
{
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
print_hu(PRINT_ANY, "vid", "%hu", vstats->vid);
|
||||||
|
print_vlan_flags(vstats->flags);
|
||||||
|
print_nl();
|
||||||
|
__print_one_vlan_stats(vstats);
|
||||||
|
|
||||||
close_json_object();
|
close_json_object();
|
||||||
}
|
}
|
||||||
|
|
@ -521,6 +621,115 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_vlan_rtm(struct nlmsghdr *n, void *arg, bool monitor)
|
||||||
|
{
|
||||||
|
struct rtattr *vtb[BRIDGE_VLANDB_ENTRY_MAX + 1], *a;
|
||||||
|
struct br_vlan_msg *bvm = NLMSG_DATA(n);
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
int rem;
|
||||||
|
|
||||||
|
if (n->nlmsg_type != RTM_NEWVLAN && n->nlmsg_type != RTM_DELVLAN &&
|
||||||
|
n->nlmsg_type != RTM_GETVLAN) {
|
||||||
|
fprintf(stderr, "Unknown vlan rtm message: %08x %08x %08x\n",
|
||||||
|
n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(sizeof(*bvm));
|
||||||
|
if (len < 0) {
|
||||||
|
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bvm->family != AF_BRIDGE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (filter_index && filter_index != bvm->ifindex)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (n->nlmsg_type == RTM_DELVLAN)
|
||||||
|
print_bool(PRINT_ANY, "deleted", "Deleted ", true);
|
||||||
|
|
||||||
|
if (monitor)
|
||||||
|
vlan_rtm_cur_ifidx = -1;
|
||||||
|
|
||||||
|
if (vlan_rtm_cur_ifidx != -1 && vlan_rtm_cur_ifidx != bvm->ifindex) {
|
||||||
|
close_vlan_port();
|
||||||
|
vlan_rtm_cur_ifidx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rem = len;
|
||||||
|
for (a = BRVLAN_RTA(bvm); RTA_OK(a, rem); a = RTA_NEXT(a, rem)) {
|
||||||
|
struct bridge_vlan_xstats vstats;
|
||||||
|
struct bridge_vlan_info *vinfo;
|
||||||
|
__u32 vrange = 0;
|
||||||
|
__u8 state = 0;
|
||||||
|
|
||||||
|
parse_rtattr_flags(vtb, BRIDGE_VLANDB_ENTRY_MAX, RTA_DATA(a),
|
||||||
|
RTA_PAYLOAD(a), NLA_F_NESTED);
|
||||||
|
vinfo = RTA_DATA(vtb[BRIDGE_VLANDB_ENTRY_INFO]);
|
||||||
|
|
||||||
|
memset(&vstats, 0, sizeof(vstats));
|
||||||
|
if (vtb[BRIDGE_VLANDB_ENTRY_RANGE])
|
||||||
|
vrange = rta_getattr_u16(vtb[BRIDGE_VLANDB_ENTRY_RANGE]);
|
||||||
|
else
|
||||||
|
vrange = vinfo->vid;
|
||||||
|
|
||||||
|
if (vtb[BRIDGE_VLANDB_ENTRY_STATE])
|
||||||
|
state = rta_getattr_u8(vtb[BRIDGE_VLANDB_ENTRY_STATE]);
|
||||||
|
|
||||||
|
if (vtb[BRIDGE_VLANDB_ENTRY_STATS]) {
|
||||||
|
struct rtattr *stb[BRIDGE_VLANDB_STATS_MAX+1];
|
||||||
|
struct rtattr *attr;
|
||||||
|
|
||||||
|
attr = vtb[BRIDGE_VLANDB_ENTRY_STATS];
|
||||||
|
parse_rtattr(stb, BRIDGE_VLANDB_STATS_MAX, RTA_DATA(attr),
|
||||||
|
RTA_PAYLOAD(attr));
|
||||||
|
|
||||||
|
if (stb[BRIDGE_VLANDB_STATS_RX_BYTES]) {
|
||||||
|
attr = stb[BRIDGE_VLANDB_STATS_RX_BYTES];
|
||||||
|
vstats.rx_bytes = rta_getattr_u64(attr);
|
||||||
|
}
|
||||||
|
if (stb[BRIDGE_VLANDB_STATS_RX_PACKETS]) {
|
||||||
|
attr = stb[BRIDGE_VLANDB_STATS_RX_PACKETS];
|
||||||
|
vstats.rx_packets = rta_getattr_u64(attr);
|
||||||
|
}
|
||||||
|
if (stb[BRIDGE_VLANDB_STATS_TX_PACKETS]) {
|
||||||
|
attr = stb[BRIDGE_VLANDB_STATS_TX_PACKETS];
|
||||||
|
vstats.tx_packets = rta_getattr_u64(attr);
|
||||||
|
}
|
||||||
|
if (stb[BRIDGE_VLANDB_STATS_TX_BYTES]) {
|
||||||
|
attr = stb[BRIDGE_VLANDB_STATS_TX_BYTES];
|
||||||
|
vstats.tx_bytes = rta_getattr_u64(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vlan_rtm_cur_ifidx != bvm->ifindex) {
|
||||||
|
open_vlan_port(bvm->ifindex, VLAN_SHOW_VLAN);
|
||||||
|
open_json_object(NULL);
|
||||||
|
vlan_rtm_cur_ifidx = bvm->ifindex;
|
||||||
|
} else {
|
||||||
|
open_json_object(NULL);
|
||||||
|
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
|
||||||
|
}
|
||||||
|
print_range("vlan", vinfo->vid, vrange);
|
||||||
|
print_vlan_flags(vinfo->flags);
|
||||||
|
print_nl();
|
||||||
|
print_string(PRINT_FP, NULL, "%-" __stringify(IFNAMSIZ) "s ", "");
|
||||||
|
print_stp_state(state);
|
||||||
|
print_nl();
|
||||||
|
if (show_stats)
|
||||||
|
__print_one_vlan_stats(&vstats);
|
||||||
|
close_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_vlan_rtm_filter(struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
return print_vlan_rtm(n, arg, false);
|
||||||
|
}
|
||||||
|
|
||||||
static int vlan_show(int argc, char **argv, int subject)
|
static int vlan_show(int argc, char **argv, int subject)
|
||||||
{
|
{
|
||||||
char *filter_dev = NULL;
|
char *filter_dev = NULL;
|
||||||
|
|
@ -549,6 +758,34 @@ static int vlan_show(int argc, char **argv, int subject)
|
||||||
|
|
||||||
new_json_obj(json);
|
new_json_obj(json);
|
||||||
|
|
||||||
|
/* if show_details is true then use the new bridge vlan dump format */
|
||||||
|
if (show_details && subject == VLAN_SHOW_VLAN) {
|
||||||
|
__u32 dump_flags = show_stats ? BRIDGE_VLANDB_DUMPF_STATS : 0;
|
||||||
|
|
||||||
|
if (rtnl_brvlandump_req(&rth, PF_BRIDGE, dump_flags) < 0) {
|
||||||
|
perror("Cannot send dump request");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_json_context()) {
|
||||||
|
printf("%-" __stringify(IFNAMSIZ) "s %-"
|
||||||
|
__stringify(VLAN_ID_LEN) "s", "port",
|
||||||
|
"vlan-id");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rtnl_dump_filter(&rth, print_vlan_rtm_filter, &subject);
|
||||||
|
if (ret < 0) {
|
||||||
|
fprintf(stderr, "Dump terminated\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vlan_rtm_cur_ifidx != -1)
|
||||||
|
close_vlan_port();
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (!show_stats) {
|
if (!show_stats) {
|
||||||
if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
|
if (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
|
||||||
(compress_vlans ?
|
(compress_vlans ?
|
||||||
|
|
@ -602,6 +839,7 @@ static int vlan_show(int argc, char **argv, int subject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
delete_json_obj();
|
delete_json_obj();
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -667,6 +905,8 @@ int do_vlan(int argc, char **argv)
|
||||||
if (matches(*argv, "tunnelshow") == 0) {
|
if (matches(*argv, "tunnelshow") == 0) {
|
||||||
return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO);
|
return vlan_show(argc-1, argv+1, VLAN_SHOW_TUNNELINFO);
|
||||||
}
|
}
|
||||||
|
if (matches(*argv, "set") == 0)
|
||||||
|
return vlan_option_set(argc-1, argv+1);
|
||||||
if (matches(*argv, "help") == 0)
|
if (matches(*argv, "help") == 0)
|
||||||
usage();
|
usage();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
121
devlink/mnlg.c
121
devlink/mnlg.c
|
|
@ -30,38 +30,11 @@ struct mnlg_socket {
|
||||||
unsigned int seq;
|
unsigned int seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
int mnlg_socket_send(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh)
|
||||||
uint16_t flags, uint32_t id,
|
|
||||||
uint8_t version)
|
|
||||||
{
|
|
||||||
struct genlmsghdr genl = {
|
|
||||||
.cmd = cmd,
|
|
||||||
.version = version,
|
|
||||||
};
|
|
||||||
struct nlmsghdr *nlh;
|
|
||||||
|
|
||||||
nlh = mnlu_msg_prepare(nlg->buf, id, flags, &genl, sizeof(genl));
|
|
||||||
nlg->seq = nlh->nlmsg_seq;
|
|
||||||
return nlh;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
|
||||||
uint16_t flags)
|
|
||||||
{
|
|
||||||
return __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)
|
|
||||||
{
|
{
|
||||||
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
return mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)
|
|
||||||
{
|
|
||||||
return mnlu_socket_recv_run(nlg->nl, nlg->seq, nlg->buf, MNL_SOCKET_BUFFER_SIZE,
|
|
||||||
data_cb, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct group_info {
|
struct group_info {
|
||||||
bool found;
|
bool found;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
@ -141,15 +114,17 @@ static int get_group_id_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
return MNL_CB_OK;
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
|
int mnlg_socket_group_add(struct mnlu_gen_socket *nlg, const char *group_name)
|
||||||
{
|
{
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
struct group_info group_info;
|
struct group_info group_info;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
|
nlh = _mnlu_gen_socket_cmd_prepare(nlg, CTRL_CMD_GETFAMILY,
|
||||||
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
|
NLM_F_REQUEST | NLM_F_ACK,
|
||||||
mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id);
|
GENL_ID_CTRL, 1);
|
||||||
|
|
||||||
|
mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->family);
|
||||||
|
|
||||||
err = mnlg_socket_send(nlg, nlh);
|
err = mnlg_socket_send(nlg, nlh);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -157,7 +132,7 @@ int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
|
||||||
|
|
||||||
group_info.found = false;
|
group_info.found = false;
|
||||||
group_info.name = group_name;
|
group_info.name = group_name;
|
||||||
err = mnlg_socket_recv_run(nlg, get_group_id_cb, &group_info);
|
err = mnlu_gen_socket_recv_run(nlg, get_group_id_cb, &group_info);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -174,85 +149,7 @@ int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
|
int mnlg_socket_get_fd(struct mnlu_gen_socket *nlg)
|
||||||
{
|
|
||||||
const struct nlattr **tb = data;
|
|
||||||
int type = mnl_attr_get_type(attr);
|
|
||||||
|
|
||||||
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
|
|
||||||
if (type == CTRL_ATTR_FAMILY_ID &&
|
|
||||||
mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
tb[type] = attr;
|
|
||||||
return MNL_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_family_id_cb(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
uint32_t *p_id = data;
|
|
||||||
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
|
|
||||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
|
||||||
|
|
||||||
mnl_attr_parse(nlh, sizeof(*genl), get_family_id_attr_cb, tb);
|
|
||||||
if (!tb[CTRL_ATTR_FAMILY_ID])
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
|
|
||||||
return MNL_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)
|
|
||||||
{
|
|
||||||
struct mnlg_socket *nlg;
|
|
||||||
struct nlmsghdr *nlh;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
nlg = malloc(sizeof(*nlg));
|
|
||||||
if (!nlg)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
nlg->buf = malloc(MNL_SOCKET_BUFFER_SIZE);
|
|
||||||
if (!nlg->buf)
|
|
||||||
goto err_buf_alloc;
|
|
||||||
|
|
||||||
nlg->nl = mnlu_socket_open(NETLINK_GENERIC);
|
|
||||||
if (!nlg->nl)
|
|
||||||
goto err_socket_open;
|
|
||||||
|
|
||||||
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
|
|
||||||
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
|
|
||||||
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);
|
|
||||||
|
|
||||||
err = mnlg_socket_send(nlg, nlh);
|
|
||||||
if (err < 0)
|
|
||||||
goto err_mnlg_socket_send;
|
|
||||||
|
|
||||||
err = mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id);
|
|
||||||
if (err < 0)
|
|
||||||
goto err_mnlg_socket_recv_run;
|
|
||||||
|
|
||||||
nlg->version = version;
|
|
||||||
return nlg;
|
|
||||||
|
|
||||||
err_mnlg_socket_recv_run:
|
|
||||||
err_mnlg_socket_send:
|
|
||||||
mnl_socket_close(nlg->nl);
|
|
||||||
err_socket_open:
|
|
||||||
free(nlg->buf);
|
|
||||||
err_buf_alloc:
|
|
||||||
free(nlg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mnlg_socket_close(struct mnlg_socket *nlg)
|
|
||||||
{
|
|
||||||
mnl_socket_close(nlg->nl);
|
|
||||||
free(nlg->buf);
|
|
||||||
free(nlg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int mnlg_socket_get_fd(struct mnlg_socket *nlg)
|
|
||||||
{
|
{
|
||||||
return mnl_socket_get_fd(nlg->nl);
|
return mnl_socket_get_fd(nlg->nl);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,10 @@
|
||||||
|
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
|
|
||||||
struct mnlg_socket;
|
struct mnlu_gen_socket;
|
||||||
|
|
||||||
struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
|
int mnlg_socket_send(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh);
|
||||||
uint16_t flags);
|
int mnlg_socket_group_add(struct mnlu_gen_socket *nlg, const char *group_name);
|
||||||
int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh);
|
int mnlg_socket_get_fd(struct mnlu_gen_socket *nlg);
|
||||||
int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data);
|
|
||||||
int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name);
|
|
||||||
struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version);
|
|
||||||
void mnlg_socket_close(struct mnlg_socket *nlg);
|
|
||||||
int mnlg_socket_get_fd(struct mnlg_socket *nlg);
|
|
||||||
|
|
||||||
#endif /* _MNLG_H_ */
|
#endif /* _MNLG_H_ */
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
16 dhcp
|
16 dhcp
|
||||||
18 keepalived
|
18 keepalived
|
||||||
42 babel
|
42 babel
|
||||||
|
99 openr
|
||||||
186 bgp
|
186 bgp
|
||||||
187 isis
|
187 isis
|
||||||
188 ospf
|
188 ospf
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ _PRINT_FUNC(0xhex, unsigned long long)
|
||||||
_PRINT_FUNC(luint, unsigned long)
|
_PRINT_FUNC(luint, unsigned long)
|
||||||
_PRINT_FUNC(lluint, unsigned long long)
|
_PRINT_FUNC(lluint, unsigned long long)
|
||||||
_PRINT_FUNC(float, double)
|
_PRINT_FUNC(float, double)
|
||||||
|
_PRINT_FUNC(tv, const struct timeval *)
|
||||||
#undef _PRINT_FUNC
|
#undef _PRINT_FUNC
|
||||||
|
|
||||||
#define _PRINT_NAME_VALUE_FUNC(type_name, type, format_char) \
|
#define _PRINT_NAME_VALUE_FUNC(type_name, type, format_char) \
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ struct { \
|
||||||
}, \
|
}, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 genl_family, const char *group);
|
||||||
int genl_resolve_family(struct rtnl_handle *grth, const char *family);
|
int genl_resolve_family(struct rtnl_handle *grth, const char *family);
|
||||||
int genl_init_handle(struct rtnl_handle *grth, const char *family,
|
int genl_init_handle(struct rtnl_handle *grth, const char *family,
|
||||||
int *genl_family);
|
int *genl_family);
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
|
int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
|
int rtnl_brvlandump_req(struct rtnl_handle *rth, int family, __u32 dump_flags)
|
||||||
|
__attribute__((warn_unused_result));
|
||||||
int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
|
int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
|
@ -97,6 +99,9 @@ int rtnl_dump_request_n(struct rtnl_handle *rth, struct nlmsghdr *n)
|
||||||
int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
|
int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
|
||||||
req_filter_fn_t filter_fn)
|
req_filter_fn_t filter_fn)
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
|
int rtnl_nexthop_bucket_dump_req(struct rtnl_handle *rth, int family,
|
||||||
|
req_filter_fn_t filter_fn)
|
||||||
|
__attribute__((warn_unused_result));
|
||||||
|
|
||||||
struct rtnl_ctrl_data {
|
struct rtnl_ctrl_data {
|
||||||
int nsid;
|
int nsid;
|
||||||
|
|
@ -280,6 +285,11 @@ int rtnl_from_file(FILE *, rtnl_listen_filter_t handler,
|
||||||
((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg))))
|
((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct if_stats_msg))))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef BRVLAN_RTA
|
||||||
|
#define BRVLAN_RTA(r) \
|
||||||
|
((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct br_vlan_msg))))
|
||||||
|
#endif
|
||||||
|
|
||||||
/* User defined nlmsg_type which is used mostly for logging netlink
|
/* User defined nlmsg_type which is used mostly for logging netlink
|
||||||
* messages from dump file */
|
* messages from dump file */
|
||||||
#define NLMSG_TSTAMP 15
|
#define NLMSG_TSTAMP 15
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@ struct mnlu_gen_socket {
|
||||||
int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
|
int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
|
||||||
uint8_t version);
|
uint8_t version);
|
||||||
void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
|
void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg);
|
||||||
|
struct nlmsghdr *
|
||||||
|
_mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||||
|
uint8_t cmd, uint16_t flags,
|
||||||
|
uint32_t id, uint8_t version);
|
||||||
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||||
uint8_t cmd, uint16_t flags);
|
uint8_t cmd, uint16_t flags);
|
||||||
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
||||||
|
|
@ -23,5 +27,7 @@ struct nlmsghdr *mnlu_msg_prepare(void *buf, uint32_t nlmsg_type, uint16_t flags
|
||||||
void *extra_header, size_t extra_header_size);
|
void *extra_header, size_t extra_header_size);
|
||||||
int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
|
int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
|
||||||
mnl_cb_t cb, void *data);
|
mnl_cb_t cb, void *data);
|
||||||
|
int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
|
||||||
|
void *data);
|
||||||
|
|
||||||
#endif /* __MNL_UTILS_H__ */
|
#endif /* __MNL_UTILS_H__ */
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,717 @@ union bpf_iter_link_info {
|
||||||
} map;
|
} map;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* BPF syscall commands, see bpf(2) man-page for details. */
|
/* BPF syscall commands, see bpf(2) man-page for more details. */
|
||||||
|
/**
|
||||||
|
* DOC: eBPF Syscall Preamble
|
||||||
|
*
|
||||||
|
* The operation to be performed by the **bpf**\ () system call is determined
|
||||||
|
* by the *cmd* argument. Each operation takes an accompanying argument,
|
||||||
|
* provided via *attr*, which is a pointer to a union of type *bpf_attr* (see
|
||||||
|
* below). The size argument is the size of the union pointed to by *attr*.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* DOC: eBPF Syscall Commands
|
||||||
|
*
|
||||||
|
* BPF_MAP_CREATE
|
||||||
|
* Description
|
||||||
|
* Create a map and return a file descriptor that refers to the
|
||||||
|
* map. The close-on-exec file descriptor flag (see **fcntl**\ (2))
|
||||||
|
* is automatically enabled for the new file descriptor.
|
||||||
|
*
|
||||||
|
* Applying **close**\ (2) to the file descriptor returned by
|
||||||
|
* **BPF_MAP_CREATE** will delete the map (but see NOTES).
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_MAP_LOOKUP_ELEM
|
||||||
|
* Description
|
||||||
|
* Look up an element with a given *key* in the map referred to
|
||||||
|
* by the file descriptor *map_fd*.
|
||||||
|
*
|
||||||
|
* The *flags* argument may be specified as one of the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* **BPF_F_LOCK**
|
||||||
|
* Look up the value of a spin-locked map without
|
||||||
|
* returning the lock. This must be specified if the
|
||||||
|
* elements contain a spinlock.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_UPDATE_ELEM
|
||||||
|
* Description
|
||||||
|
* Create or update an element (key/value pair) in a specified map.
|
||||||
|
*
|
||||||
|
* The *flags* argument should be specified as one of the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* **BPF_ANY**
|
||||||
|
* Create a new element or update an existing element.
|
||||||
|
* **BPF_NOEXIST**
|
||||||
|
* Create a new element only if it did not exist.
|
||||||
|
* **BPF_EXIST**
|
||||||
|
* Update an existing element.
|
||||||
|
* **BPF_F_LOCK**
|
||||||
|
* Update a spin_lock-ed map element.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**,
|
||||||
|
* **E2BIG**, **EEXIST**, or **ENOENT**.
|
||||||
|
*
|
||||||
|
* **E2BIG**
|
||||||
|
* The number of elements in the map reached the
|
||||||
|
* *max_entries* limit specified at map creation time.
|
||||||
|
* **EEXIST**
|
||||||
|
* If *flags* specifies **BPF_NOEXIST** and the element
|
||||||
|
* with *key* already exists in the map.
|
||||||
|
* **ENOENT**
|
||||||
|
* If *flags* specifies **BPF_EXIST** and the element with
|
||||||
|
* *key* does not exist in the map.
|
||||||
|
*
|
||||||
|
* BPF_MAP_DELETE_ELEM
|
||||||
|
* Description
|
||||||
|
* Look up and delete an element by key in a specified map.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_GET_NEXT_KEY
|
||||||
|
* Description
|
||||||
|
* Look up an element by key in a specified map and return the key
|
||||||
|
* of the next element. Can be used to iterate over all elements
|
||||||
|
* in the map.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* The following cases can be used to iterate over all elements of
|
||||||
|
* the map:
|
||||||
|
*
|
||||||
|
* * If *key* is not found, the operation returns zero and sets
|
||||||
|
* the *next_key* pointer to the key of the first element.
|
||||||
|
* * If *key* is found, the operation returns zero and sets the
|
||||||
|
* *next_key* pointer to the key of the next element.
|
||||||
|
* * If *key* is the last element, returns -1 and *errno* is set
|
||||||
|
* to **ENOENT**.
|
||||||
|
*
|
||||||
|
* May set *errno* to **ENOMEM**, **EFAULT**, **EPERM**, or
|
||||||
|
* **EINVAL** on error.
|
||||||
|
*
|
||||||
|
* BPF_PROG_LOAD
|
||||||
|
* Description
|
||||||
|
* Verify and load an eBPF program, returning a new file
|
||||||
|
* descriptor associated with the program.
|
||||||
|
*
|
||||||
|
* Applying **close**\ (2) to the file descriptor returned by
|
||||||
|
* **BPF_PROG_LOAD** will unload the eBPF program (but see NOTES).
|
||||||
|
*
|
||||||
|
* The close-on-exec file descriptor flag (see **fcntl**\ (2)) is
|
||||||
|
* automatically enabled for the new file descriptor.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_OBJ_PIN
|
||||||
|
* Description
|
||||||
|
* Pin an eBPF program or map referred by the specified *bpf_fd*
|
||||||
|
* to the provided *pathname* on the filesystem.
|
||||||
|
*
|
||||||
|
* The *pathname* argument must not contain a dot (".").
|
||||||
|
*
|
||||||
|
* On success, *pathname* retains a reference to the eBPF object,
|
||||||
|
* preventing deallocation of the object when the original
|
||||||
|
* *bpf_fd* is closed. This allow the eBPF object to live beyond
|
||||||
|
* **close**\ (\ *bpf_fd*\ ), and hence the lifetime of the parent
|
||||||
|
* process.
|
||||||
|
*
|
||||||
|
* Applying **unlink**\ (2) or similar calls to the *pathname*
|
||||||
|
* unpins the object from the filesystem, removing the reference.
|
||||||
|
* If no other file descriptors or filesystem nodes refer to the
|
||||||
|
* same object, it will be deallocated (see NOTES).
|
||||||
|
*
|
||||||
|
* The filesystem type for the parent directory of *pathname* must
|
||||||
|
* be **BPF_FS_MAGIC**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_OBJ_GET
|
||||||
|
* Description
|
||||||
|
* Open a file descriptor for the eBPF object pinned to the
|
||||||
|
* specified *pathname*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_PROG_ATTACH
|
||||||
|
* Description
|
||||||
|
* Attach an eBPF program to a *target_fd* at the specified
|
||||||
|
* *attach_type* hook.
|
||||||
|
*
|
||||||
|
* The *attach_type* specifies the eBPF attachment point to
|
||||||
|
* attach the program to, and must be one of *bpf_attach_type*
|
||||||
|
* (see below).
|
||||||
|
*
|
||||||
|
* The *attach_bpf_fd* must be a valid file descriptor for a
|
||||||
|
* loaded eBPF program of a cgroup, flow dissector, LIRC, sockmap
|
||||||
|
* or sock_ops type corresponding to the specified *attach_type*.
|
||||||
|
*
|
||||||
|
* The *target_fd* must be a valid file descriptor for a kernel
|
||||||
|
* object which depends on the attach type of *attach_bpf_fd*:
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_DEVICE**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SKB**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCK**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCKOPT**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SYSCTL**,
|
||||||
|
* **BPF_PROG_TYPE_SOCK_OPS**
|
||||||
|
*
|
||||||
|
* Control Group v2 hierarchy with the eBPF controller
|
||||||
|
* enabled. Requires the kernel to be compiled with
|
||||||
|
* **CONFIG_CGROUP_BPF**.
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_FLOW_DISSECTOR**
|
||||||
|
*
|
||||||
|
* Network namespace (eg /proc/self/ns/net).
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_LIRC_MODE2**
|
||||||
|
*
|
||||||
|
* LIRC device path (eg /dev/lircN). Requires the kernel
|
||||||
|
* to be compiled with **CONFIG_BPF_LIRC_MODE2**.
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_SK_SKB**,
|
||||||
|
* **BPF_PROG_TYPE_SK_MSG**
|
||||||
|
*
|
||||||
|
* eBPF map of socket type (eg **BPF_MAP_TYPE_SOCKHASH**).
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_PROG_DETACH
|
||||||
|
* Description
|
||||||
|
* Detach the eBPF program associated with the *target_fd* at the
|
||||||
|
* hook specified by *attach_type*. The program must have been
|
||||||
|
* previously attached using **BPF_PROG_ATTACH**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_PROG_TEST_RUN
|
||||||
|
* Description
|
||||||
|
* Run the eBPF program associated with the *prog_fd* a *repeat*
|
||||||
|
* number of times against a provided program context *ctx_in* and
|
||||||
|
* data *data_in*, and return the modified program context
|
||||||
|
* *ctx_out*, *data_out* (for example, packet data), result of the
|
||||||
|
* execution *retval*, and *duration* of the test run.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* **ENOSPC**
|
||||||
|
* Either *data_size_out* or *ctx_size_out* is too small.
|
||||||
|
* **ENOTSUPP**
|
||||||
|
* This command is not supported by the program type of
|
||||||
|
* the program referred to by *prog_fd*.
|
||||||
|
*
|
||||||
|
* BPF_PROG_GET_NEXT_ID
|
||||||
|
* Description
|
||||||
|
* Fetch the next eBPF program currently loaded into the kernel.
|
||||||
|
*
|
||||||
|
* Looks for the eBPF program with an id greater than *start_id*
|
||||||
|
* and updates *next_id* on success. If no other eBPF programs
|
||||||
|
* remain with ids higher than *start_id*, returns -1 and sets
|
||||||
|
* *errno* to **ENOENT**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, or when no id remains, -1
|
||||||
|
* is returned and *errno* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_GET_NEXT_ID
|
||||||
|
* Description
|
||||||
|
* Fetch the next eBPF map currently loaded into the kernel.
|
||||||
|
*
|
||||||
|
* Looks for the eBPF map with an id greater than *start_id*
|
||||||
|
* and updates *next_id* on success. If no other eBPF maps
|
||||||
|
* remain with ids higher than *start_id*, returns -1 and sets
|
||||||
|
* *errno* to **ENOENT**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, or when no id remains, -1
|
||||||
|
* is returned and *errno* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_PROG_GET_FD_BY_ID
|
||||||
|
* Description
|
||||||
|
* Open a file descriptor for the eBPF program corresponding to
|
||||||
|
* *prog_id*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_MAP_GET_FD_BY_ID
|
||||||
|
* Description
|
||||||
|
* Open a file descriptor for the eBPF map corresponding to
|
||||||
|
* *map_id*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_OBJ_GET_INFO_BY_FD
|
||||||
|
* Description
|
||||||
|
* Obtain information about the eBPF object corresponding to
|
||||||
|
* *bpf_fd*.
|
||||||
|
*
|
||||||
|
* Populates up to *info_len* bytes of *info*, which will be in
|
||||||
|
* one of the following formats depending on the eBPF object type
|
||||||
|
* of *bpf_fd*:
|
||||||
|
*
|
||||||
|
* * **struct bpf_prog_info**
|
||||||
|
* * **struct bpf_map_info**
|
||||||
|
* * **struct bpf_btf_info**
|
||||||
|
* * **struct bpf_link_info**
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_PROG_QUERY
|
||||||
|
* Description
|
||||||
|
* Obtain information about eBPF programs associated with the
|
||||||
|
* specified *attach_type* hook.
|
||||||
|
*
|
||||||
|
* The *target_fd* must be a valid file descriptor for a kernel
|
||||||
|
* object which depends on the attach type of *attach_bpf_fd*:
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_DEVICE**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SKB**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCK**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SOCKOPT**,
|
||||||
|
* **BPF_PROG_TYPE_CGROUP_SYSCTL**,
|
||||||
|
* **BPF_PROG_TYPE_SOCK_OPS**
|
||||||
|
*
|
||||||
|
* Control Group v2 hierarchy with the eBPF controller
|
||||||
|
* enabled. Requires the kernel to be compiled with
|
||||||
|
* **CONFIG_CGROUP_BPF**.
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_FLOW_DISSECTOR**
|
||||||
|
*
|
||||||
|
* Network namespace (eg /proc/self/ns/net).
|
||||||
|
*
|
||||||
|
* **BPF_PROG_TYPE_LIRC_MODE2**
|
||||||
|
*
|
||||||
|
* LIRC device path (eg /dev/lircN). Requires the kernel
|
||||||
|
* to be compiled with **CONFIG_BPF_LIRC_MODE2**.
|
||||||
|
*
|
||||||
|
* **BPF_PROG_QUERY** always fetches the number of programs
|
||||||
|
* attached and the *attach_flags* which were used to attach those
|
||||||
|
* programs. Additionally, if *prog_ids* is nonzero and the number
|
||||||
|
* of attached programs is less than *prog_cnt*, populates
|
||||||
|
* *prog_ids* with the eBPF program ids of the programs attached
|
||||||
|
* at *target_fd*.
|
||||||
|
*
|
||||||
|
* The following flags may alter the result:
|
||||||
|
*
|
||||||
|
* **BPF_F_QUERY_EFFECTIVE**
|
||||||
|
* Only return information regarding programs which are
|
||||||
|
* currently effective at the specified *target_fd*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_RAW_TRACEPOINT_OPEN
|
||||||
|
* Description
|
||||||
|
* Attach an eBPF program to a tracepoint *name* to access kernel
|
||||||
|
* internal arguments of the tracepoint in their raw form.
|
||||||
|
*
|
||||||
|
* The *prog_fd* must be a valid file descriptor associated with
|
||||||
|
* a loaded eBPF program of type **BPF_PROG_TYPE_RAW_TRACEPOINT**.
|
||||||
|
*
|
||||||
|
* No ABI guarantees are made about the content of tracepoint
|
||||||
|
* arguments exposed to the corresponding eBPF program.
|
||||||
|
*
|
||||||
|
* Applying **close**\ (2) to the file descriptor returned by
|
||||||
|
* **BPF_RAW_TRACEPOINT_OPEN** will delete the map (but see NOTES).
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_BTF_LOAD
|
||||||
|
* Description
|
||||||
|
* Verify and load BPF Type Format (BTF) metadata into the kernel,
|
||||||
|
* returning a new file descriptor associated with the metadata.
|
||||||
|
* BTF is described in more detail at
|
||||||
|
* https://www.kernel.org/doc/html/latest/bpf/btf.html.
|
||||||
|
*
|
||||||
|
* The *btf* parameter must point to valid memory providing
|
||||||
|
* *btf_size* bytes of BTF binary metadata.
|
||||||
|
*
|
||||||
|
* The returned file descriptor can be passed to other **bpf**\ ()
|
||||||
|
* subcommands such as **BPF_PROG_LOAD** or **BPF_MAP_CREATE** to
|
||||||
|
* associate the BTF with those objects.
|
||||||
|
*
|
||||||
|
* Similar to **BPF_PROG_LOAD**, **BPF_BTF_LOAD** has optional
|
||||||
|
* parameters to specify a *btf_log_buf*, *btf_log_size* and
|
||||||
|
* *btf_log_level* which allow the kernel to return freeform log
|
||||||
|
* output regarding the BTF verification process.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_BTF_GET_FD_BY_ID
|
||||||
|
* Description
|
||||||
|
* Open a file descriptor for the BPF Type Format (BTF)
|
||||||
|
* corresponding to *btf_id*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_TASK_FD_QUERY
|
||||||
|
* Description
|
||||||
|
* Obtain information about eBPF programs associated with the
|
||||||
|
* target process identified by *pid* and *fd*.
|
||||||
|
*
|
||||||
|
* If the *pid* and *fd* are associated with a tracepoint, kprobe
|
||||||
|
* or uprobe perf event, then the *prog_id* and *fd_type* will
|
||||||
|
* be populated with the eBPF program id and file descriptor type
|
||||||
|
* of type **bpf_task_fd_type**. If associated with a kprobe or
|
||||||
|
* uprobe, the *probe_offset* and *probe_addr* will also be
|
||||||
|
* populated. Optionally, if *buf* is provided, then up to
|
||||||
|
* *buf_len* bytes of *buf* will be populated with the name of
|
||||||
|
* the tracepoint, kprobe or uprobe.
|
||||||
|
*
|
||||||
|
* The resulting *prog_id* may be introspected in deeper detail
|
||||||
|
* using **BPF_PROG_GET_FD_BY_ID** and **BPF_OBJ_GET_INFO_BY_FD**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_LOOKUP_AND_DELETE_ELEM
|
||||||
|
* Description
|
||||||
|
* Look up an element with the given *key* in the map referred to
|
||||||
|
* by the file descriptor *fd*, and if found, delete the element.
|
||||||
|
*
|
||||||
|
* The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types
|
||||||
|
* implement this command as a "pop" operation, deleting the top
|
||||||
|
* element rather than one corresponding to *key*.
|
||||||
|
* The *key* and *key_len* parameters should be zeroed when
|
||||||
|
* issuing this operation for these map types.
|
||||||
|
*
|
||||||
|
* This command is only valid for the following map types:
|
||||||
|
* * **BPF_MAP_TYPE_QUEUE**
|
||||||
|
* * **BPF_MAP_TYPE_STACK**
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_FREEZE
|
||||||
|
* Description
|
||||||
|
* Freeze the permissions of the specified map.
|
||||||
|
*
|
||||||
|
* Write permissions may be frozen by passing zero *flags*.
|
||||||
|
* Upon success, no future syscall invocations may alter the
|
||||||
|
* map state of *map_fd*. Write operations from eBPF programs
|
||||||
|
* are still possible for a frozen map.
|
||||||
|
*
|
||||||
|
* Not supported for maps of type **BPF_MAP_TYPE_STRUCT_OPS**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_BTF_GET_NEXT_ID
|
||||||
|
* Description
|
||||||
|
* Fetch the next BPF Type Format (BTF) object currently loaded
|
||||||
|
* into the kernel.
|
||||||
|
*
|
||||||
|
* Looks for the BTF object with an id greater than *start_id*
|
||||||
|
* and updates *next_id* on success. If no other BTF objects
|
||||||
|
* remain with ids higher than *start_id*, returns -1 and sets
|
||||||
|
* *errno* to **ENOENT**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, or when no id remains, -1
|
||||||
|
* is returned and *errno* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_LOOKUP_BATCH
|
||||||
|
* Description
|
||||||
|
* Iterate and fetch multiple elements in a map.
|
||||||
|
*
|
||||||
|
* Two opaque values are used to manage batch operations,
|
||||||
|
* *in_batch* and *out_batch*. Initially, *in_batch* must be set
|
||||||
|
* to NULL to begin the batched operation. After each subsequent
|
||||||
|
* **BPF_MAP_LOOKUP_BATCH**, the caller should pass the resultant
|
||||||
|
* *out_batch* as the *in_batch* for the next operation to
|
||||||
|
* continue iteration from the current point.
|
||||||
|
*
|
||||||
|
* The *keys* and *values* are output parameters which must point
|
||||||
|
* to memory large enough to hold *count* items based on the key
|
||||||
|
* and value size of the map *map_fd*. The *keys* buffer must be
|
||||||
|
* of *key_size* * *count*. The *values* buffer must be of
|
||||||
|
* *value_size* * *count*.
|
||||||
|
*
|
||||||
|
* The *elem_flags* argument may be specified as one of the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* **BPF_F_LOCK**
|
||||||
|
* Look up the value of a spin-locked map without
|
||||||
|
* returning the lock. This must be specified if the
|
||||||
|
* elements contain a spinlock.
|
||||||
|
*
|
||||||
|
* On success, *count* elements from the map are copied into the
|
||||||
|
* user buffer, with the keys copied into *keys* and the values
|
||||||
|
* copied into the corresponding indices in *values*.
|
||||||
|
*
|
||||||
|
* If an error is returned and *errno* is not **EFAULT**, *count*
|
||||||
|
* is set to the number of successfully processed elements.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* May set *errno* to **ENOSPC** to indicate that *keys* or
|
||||||
|
* *values* is too small to dump an entire bucket during
|
||||||
|
* iteration of a hash-based map type.
|
||||||
|
*
|
||||||
|
* BPF_MAP_LOOKUP_AND_DELETE_BATCH
|
||||||
|
* Description
|
||||||
|
* Iterate and delete all elements in a map.
|
||||||
|
*
|
||||||
|
* This operation has the same behavior as
|
||||||
|
* **BPF_MAP_LOOKUP_BATCH** with two exceptions:
|
||||||
|
*
|
||||||
|
* * Every element that is successfully returned is also deleted
|
||||||
|
* from the map. This is at least *count* elements. Note that
|
||||||
|
* *count* is both an input and an output parameter.
|
||||||
|
* * Upon returning with *errno* set to **EFAULT**, up to
|
||||||
|
* *count* elements may be deleted without returning the keys
|
||||||
|
* and values of the deleted elements.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_MAP_UPDATE_BATCH
|
||||||
|
* Description
|
||||||
|
* Update multiple elements in a map by *key*.
|
||||||
|
*
|
||||||
|
* The *keys* and *values* are input parameters which must point
|
||||||
|
* to memory large enough to hold *count* items based on the key
|
||||||
|
* and value size of the map *map_fd*. The *keys* buffer must be
|
||||||
|
* of *key_size* * *count*. The *values* buffer must be of
|
||||||
|
* *value_size* * *count*.
|
||||||
|
*
|
||||||
|
* Each element specified in *keys* is sequentially updated to the
|
||||||
|
* value in the corresponding index in *values*. The *in_batch*
|
||||||
|
* and *out_batch* parameters are ignored and should be zeroed.
|
||||||
|
*
|
||||||
|
* The *elem_flags* argument should be specified as one of the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* **BPF_ANY**
|
||||||
|
* Create new elements or update a existing elements.
|
||||||
|
* **BPF_NOEXIST**
|
||||||
|
* Create new elements only if they do not exist.
|
||||||
|
* **BPF_EXIST**
|
||||||
|
* Update existing elements.
|
||||||
|
* **BPF_F_LOCK**
|
||||||
|
* Update spin_lock-ed map elements. This must be
|
||||||
|
* specified if the map value contains a spinlock.
|
||||||
|
*
|
||||||
|
* On success, *count* elements from the map are updated.
|
||||||
|
*
|
||||||
|
* If an error is returned and *errno* is not **EFAULT**, *count*
|
||||||
|
* is set to the number of successfully processed elements.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, or
|
||||||
|
* **E2BIG**. **E2BIG** indicates that the number of elements in
|
||||||
|
* the map reached the *max_entries* limit specified at map
|
||||||
|
* creation time.
|
||||||
|
*
|
||||||
|
* May set *errno* to one of the following error codes under
|
||||||
|
* specific circumstances:
|
||||||
|
*
|
||||||
|
* **EEXIST**
|
||||||
|
* If *flags* specifies **BPF_NOEXIST** and the element
|
||||||
|
* with *key* already exists in the map.
|
||||||
|
* **ENOENT**
|
||||||
|
* If *flags* specifies **BPF_EXIST** and the element with
|
||||||
|
* *key* does not exist in the map.
|
||||||
|
*
|
||||||
|
* BPF_MAP_DELETE_BATCH
|
||||||
|
* Description
|
||||||
|
* Delete multiple elements in a map by *key*.
|
||||||
|
*
|
||||||
|
* The *keys* parameter is an input parameter which must point
|
||||||
|
* to memory large enough to hold *count* items based on the key
|
||||||
|
* size of the map *map_fd*, that is, *key_size* * *count*.
|
||||||
|
*
|
||||||
|
* Each element specified in *keys* is sequentially deleted. The
|
||||||
|
* *in_batch*, *out_batch*, and *values* parameters are ignored
|
||||||
|
* and should be zeroed.
|
||||||
|
*
|
||||||
|
* The *elem_flags* argument may be specified as one of the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* **BPF_F_LOCK**
|
||||||
|
* Look up the value of a spin-locked map without
|
||||||
|
* returning the lock. This must be specified if the
|
||||||
|
* elements contain a spinlock.
|
||||||
|
*
|
||||||
|
* On success, *count* elements from the map are updated.
|
||||||
|
*
|
||||||
|
* If an error is returned and *errno* is not **EFAULT**, *count*
|
||||||
|
* is set to the number of successfully processed elements. If
|
||||||
|
* *errno* is **EFAULT**, up to *count* elements may be been
|
||||||
|
* deleted.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_LINK_CREATE
|
||||||
|
* Description
|
||||||
|
* Attach an eBPF program to a *target_fd* at the specified
|
||||||
|
* *attach_type* hook and return a file descriptor handle for
|
||||||
|
* managing the link.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_LINK_UPDATE
|
||||||
|
* Description
|
||||||
|
* Update the eBPF program in the specified *link_fd* to
|
||||||
|
* *new_prog_fd*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_LINK_GET_FD_BY_ID
|
||||||
|
* Description
|
||||||
|
* Open a file descriptor for the eBPF Link corresponding to
|
||||||
|
* *link_id*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_LINK_GET_NEXT_ID
|
||||||
|
* Description
|
||||||
|
* Fetch the next eBPF link currently loaded into the kernel.
|
||||||
|
*
|
||||||
|
* Looks for the eBPF link with an id greater than *start_id*
|
||||||
|
* and updates *next_id* on success. If no other eBPF links
|
||||||
|
* remain with ids higher than *start_id*, returns -1 and sets
|
||||||
|
* *errno* to **ENOENT**.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, or when no id remains, -1
|
||||||
|
* is returned and *errno* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_ENABLE_STATS
|
||||||
|
* Description
|
||||||
|
* Enable eBPF runtime statistics gathering.
|
||||||
|
*
|
||||||
|
* Runtime statistics gathering for the eBPF runtime is disabled
|
||||||
|
* by default to minimize the corresponding performance overhead.
|
||||||
|
* This command enables statistics globally.
|
||||||
|
*
|
||||||
|
* Multiple programs may independently enable statistics.
|
||||||
|
* After gathering the desired statistics, eBPF runtime statistics
|
||||||
|
* may be disabled again by calling **close**\ (2) for the file
|
||||||
|
* descriptor returned by this function. Statistics will only be
|
||||||
|
* disabled system-wide when all outstanding file descriptors
|
||||||
|
* returned by prior calls for this subcommand are closed.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_ITER_CREATE
|
||||||
|
* Description
|
||||||
|
* Create an iterator on top of the specified *link_fd* (as
|
||||||
|
* previously created using **BPF_LINK_CREATE**) and return a
|
||||||
|
* file descriptor that can be used to trigger the iteration.
|
||||||
|
*
|
||||||
|
* If the resulting file descriptor is pinned to the filesystem
|
||||||
|
* using **BPF_OBJ_PIN**, then subsequent **read**\ (2) syscalls
|
||||||
|
* for that path will trigger the iterator to read kernel state
|
||||||
|
* using the eBPF program attached to *link_fd*.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* A new file descriptor (a nonnegative integer), or -1 if an
|
||||||
|
* error occurred (in which case, *errno* is set appropriately).
|
||||||
|
*
|
||||||
|
* BPF_LINK_DETACH
|
||||||
|
* Description
|
||||||
|
* Forcefully detach the specified *link_fd* from its
|
||||||
|
* corresponding attachment point.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* BPF_PROG_BIND_MAP
|
||||||
|
* Description
|
||||||
|
* Bind a map to the lifetime of an eBPF program.
|
||||||
|
*
|
||||||
|
* The map identified by *map_fd* is bound to the program
|
||||||
|
* identified by *prog_fd* and only released when *prog_fd* is
|
||||||
|
* released. This may be used in cases where metadata should be
|
||||||
|
* associated with a program which otherwise does not contain any
|
||||||
|
* references to the map (for example, embedded in the eBPF
|
||||||
|
* program instructions).
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* Returns zero on success. On error, -1 is returned and *errno*
|
||||||
|
* is set appropriately.
|
||||||
|
*
|
||||||
|
* NOTES
|
||||||
|
* eBPF objects (maps and programs) can be shared between processes.
|
||||||
|
*
|
||||||
|
* * After **fork**\ (2), the child inherits file descriptors
|
||||||
|
* referring to the same eBPF objects.
|
||||||
|
* * File descriptors referring to eBPF objects can be transferred over
|
||||||
|
* **unix**\ (7) domain sockets.
|
||||||
|
* * File descriptors referring to eBPF objects can be duplicated in the
|
||||||
|
* usual way, using **dup**\ (2) and similar calls.
|
||||||
|
* * File descriptors referring to eBPF objects can be pinned to the
|
||||||
|
* filesystem using the **BPF_OBJ_PIN** command of **bpf**\ (2).
|
||||||
|
*
|
||||||
|
* An eBPF object is deallocated only after all file descriptors referring
|
||||||
|
* to the object have been closed and no references remain pinned to the
|
||||||
|
* filesystem or attached (for example, bound to a program or device).
|
||||||
|
*/
|
||||||
enum bpf_cmd {
|
enum bpf_cmd {
|
||||||
BPF_MAP_CREATE,
|
BPF_MAP_CREATE,
|
||||||
BPF_MAP_LOOKUP_ELEM,
|
BPF_MAP_LOOKUP_ELEM,
|
||||||
|
|
@ -247,6 +957,7 @@ enum bpf_attach_type {
|
||||||
BPF_XDP_CPUMAP,
|
BPF_XDP_CPUMAP,
|
||||||
BPF_SK_LOOKUP,
|
BPF_SK_LOOKUP,
|
||||||
BPF_XDP,
|
BPF_XDP,
|
||||||
|
BPF_SK_SKB_VERDICT,
|
||||||
__MAX_BPF_ATTACH_TYPE
|
__MAX_BPF_ATTACH_TYPE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -393,11 +1104,24 @@ enum bpf_link_type {
|
||||||
* is struct/union.
|
* is struct/union.
|
||||||
*/
|
*/
|
||||||
#define BPF_PSEUDO_BTF_ID 3
|
#define BPF_PSEUDO_BTF_ID 3
|
||||||
|
/* insn[0].src_reg: BPF_PSEUDO_FUNC
|
||||||
|
* insn[0].imm: insn offset to the func
|
||||||
|
* insn[1].imm: 0
|
||||||
|
* insn[0].off: 0
|
||||||
|
* insn[1].off: 0
|
||||||
|
* ldimm64 rewrite: address of the function
|
||||||
|
* verifier type: PTR_TO_FUNC.
|
||||||
|
*/
|
||||||
|
#define BPF_PSEUDO_FUNC 4
|
||||||
|
|
||||||
/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
|
/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
|
||||||
* offset to another bpf function
|
* offset to another bpf function
|
||||||
*/
|
*/
|
||||||
#define BPF_PSEUDO_CALL 1
|
#define BPF_PSEUDO_CALL 1
|
||||||
|
/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL,
|
||||||
|
* bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel
|
||||||
|
*/
|
||||||
|
#define BPF_PSEUDO_KFUNC_CALL 2
|
||||||
|
|
||||||
/* flags for BPF_MAP_UPDATE_ELEM command */
|
/* flags for BPF_MAP_UPDATE_ELEM command */
|
||||||
enum {
|
enum {
|
||||||
|
|
@ -720,7 +1444,7 @@ union bpf_attr {
|
||||||
* parsed and used to produce a manual page. The workflow is the following,
|
* parsed and used to produce a manual page. The workflow is the following,
|
||||||
* and requires the rst2man utility:
|
* and requires the rst2man utility:
|
||||||
*
|
*
|
||||||
* $ ./scripts/bpf_helpers_doc.py \
|
* $ ./scripts/bpf_doc.py \
|
||||||
* --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
|
* --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
|
||||||
* $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
|
* $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
|
||||||
* $ man /tmp/bpf-helpers.7
|
* $ man /tmp/bpf-helpers.7
|
||||||
|
|
@ -1765,6 +2489,10 @@ union bpf_attr {
|
||||||
* Use with ENCAP_L3/L4 flags to further specify the tunnel
|
* Use with ENCAP_L3/L4 flags to further specify the tunnel
|
||||||
* type; *len* is the length of the inner MAC header.
|
* type; *len* is the length of the inner MAC header.
|
||||||
*
|
*
|
||||||
|
* * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**:
|
||||||
|
* Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the
|
||||||
|
* L2 type as Ethernet.
|
||||||
|
*
|
||||||
* A call to this helper is susceptible to change the underlying
|
* A call to this helper is susceptible to change the underlying
|
||||||
* packet buffer. Therefore, at load time, all checks on pointers
|
* packet buffer. Therefore, at load time, all checks on pointers
|
||||||
* previously done by the verifier are invalidated and must be
|
* previously done by the verifier are invalidated and must be
|
||||||
|
|
@ -3915,6 +4643,34 @@ union bpf_attr {
|
||||||
* * **BPF_MTU_CHK_RET_FRAG_NEEDED**
|
* * **BPF_MTU_CHK_RET_FRAG_NEEDED**
|
||||||
* * **BPF_MTU_CHK_RET_SEGS_TOOBIG**
|
* * **BPF_MTU_CHK_RET_SEGS_TOOBIG**
|
||||||
*
|
*
|
||||||
|
* long bpf_for_each_map_elem(struct bpf_map *map, void *callback_fn, void *callback_ctx, u64 flags)
|
||||||
|
* Description
|
||||||
|
* For each element in **map**, call **callback_fn** function with
|
||||||
|
* **map**, **callback_ctx** and other map-specific parameters.
|
||||||
|
* The **callback_fn** should be a static function and
|
||||||
|
* the **callback_ctx** should be a pointer to the stack.
|
||||||
|
* The **flags** is used to control certain aspects of the helper.
|
||||||
|
* Currently, the **flags** must be 0.
|
||||||
|
*
|
||||||
|
* The following are a list of supported map types and their
|
||||||
|
* respective expected callback signatures:
|
||||||
|
*
|
||||||
|
* BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH,
|
||||||
|
* BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH,
|
||||||
|
* BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY
|
||||||
|
*
|
||||||
|
* long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx);
|
||||||
|
*
|
||||||
|
* For per_cpu maps, the map_value is the value on the cpu where the
|
||||||
|
* bpf_prog is running.
|
||||||
|
*
|
||||||
|
* If **callback_fn** return 0, the helper will continue to the next
|
||||||
|
* element. If return value is 1, the helper will skip the rest of
|
||||||
|
* elements and return. Other return values are not used now.
|
||||||
|
*
|
||||||
|
* Return
|
||||||
|
* The number of traversed map elements for success, **-EINVAL** for
|
||||||
|
* invalid **flags**.
|
||||||
*/
|
*/
|
||||||
#define __BPF_FUNC_MAPPER(FN) \
|
#define __BPF_FUNC_MAPPER(FN) \
|
||||||
FN(unspec), \
|
FN(unspec), \
|
||||||
|
|
@ -4081,6 +4837,7 @@ union bpf_attr {
|
||||||
FN(ima_inode_hash), \
|
FN(ima_inode_hash), \
|
||||||
FN(sock_from_file), \
|
FN(sock_from_file), \
|
||||||
FN(check_mtu), \
|
FN(check_mtu), \
|
||||||
|
FN(for_each_map_elem), \
|
||||||
/* */
|
/* */
|
||||||
|
|
||||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||||
|
|
@ -4174,6 +4931,7 @@ enum {
|
||||||
BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3),
|
BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3),
|
||||||
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4),
|
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4),
|
||||||
BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5),
|
BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5),
|
||||||
|
BPF_F_ADJ_ROOM_ENCAP_L2_ETH = (1ULL << 6),
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
@ -5211,7 +5969,10 @@ struct bpf_pidns_info {
|
||||||
|
|
||||||
/* User accessible data for SK_LOOKUP programs. Add new fields at the end. */
|
/* User accessible data for SK_LOOKUP programs. Add new fields at the end. */
|
||||||
struct bpf_sk_lookup {
|
struct bpf_sk_lookup {
|
||||||
__bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */
|
union {
|
||||||
|
__bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */
|
||||||
|
__u64 cookie; /* Non-zero if socket was selected in PROG_TEST_RUN */
|
||||||
|
};
|
||||||
|
|
||||||
__u32 family; /* Protocol family (AF_INET, AF_INET6) */
|
__u32 family; /* Protocol family (AF_INET, AF_INET6) */
|
||||||
__u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */
|
__u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ struct btf_type {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
|
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f)
|
||||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||||
#define BTF_INFO_KFLAG(info) ((info) >> 31)
|
#define BTF_INFO_KFLAG(info) ((info) >> 31)
|
||||||
|
|
||||||
|
|
@ -72,7 +72,8 @@ struct btf_type {
|
||||||
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
|
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
|
||||||
#define BTF_KIND_VAR 14 /* Variable */
|
#define BTF_KIND_VAR 14 /* Variable */
|
||||||
#define BTF_KIND_DATASEC 15 /* Section */
|
#define BTF_KIND_DATASEC 15 /* Section */
|
||||||
#define BTF_KIND_MAX BTF_KIND_DATASEC
|
#define BTF_KIND_FLOAT 16 /* Floating point */
|
||||||
|
#define BTF_KIND_MAX BTF_KIND_FLOAT
|
||||||
#define NR_BTF_KINDS (BTF_KIND_MAX + 1)
|
#define NR_BTF_KINDS (BTF_KIND_MAX + 1)
|
||||||
|
|
||||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,9 @@ struct icmp6hdr {
|
||||||
#define ICMPV6_UNK_OPTION 2
|
#define ICMPV6_UNK_OPTION 2
|
||||||
#define ICMPV6_HDR_INCOMP 3
|
#define ICMPV6_HDR_INCOMP 3
|
||||||
|
|
||||||
|
/* Codes for EXT_ECHO (PROBE) */
|
||||||
|
#define ICMPV6_EXT_ECHO_REQUEST 160
|
||||||
|
#define ICMPV6_EXT_ECHO_REPLY 161
|
||||||
/*
|
/*
|
||||||
* constants for (set|get)sockopt
|
* constants for (set|get)sockopt
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -174,10 +174,21 @@ enum mptcp_event_attr {
|
||||||
MPTCP_ATTR_FLAGS, /* u16 */
|
MPTCP_ATTR_FLAGS, /* u16 */
|
||||||
MPTCP_ATTR_TIMEOUT, /* u32 */
|
MPTCP_ATTR_TIMEOUT, /* u32 */
|
||||||
MPTCP_ATTR_IF_IDX, /* s32 */
|
MPTCP_ATTR_IF_IDX, /* s32 */
|
||||||
|
MPTCP_ATTR_RESET_REASON,/* u32 */
|
||||||
|
MPTCP_ATTR_RESET_FLAGS, /* u32 */
|
||||||
|
|
||||||
__MPTCP_ATTR_AFTER_LAST
|
__MPTCP_ATTR_AFTER_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MPTCP_ATTR_MAX (__MPTCP_ATTR_AFTER_LAST - 1)
|
#define MPTCP_ATTR_MAX (__MPTCP_ATTR_AFTER_LAST - 1)
|
||||||
|
|
||||||
|
/* MPTCP Reset reason codes, rfc8684 */
|
||||||
|
#define MPTCP_RST_EUNSPEC 0
|
||||||
|
#define MPTCP_RST_EMPTCP 1
|
||||||
|
#define MPTCP_RST_ERESOURCE 2
|
||||||
|
#define MPTCP_RST_EPROHIBIT 3
|
||||||
|
#define MPTCP_RST_EWQ2BIG 4
|
||||||
|
#define MPTCP_RST_EBADPERF 5
|
||||||
|
#define MPTCP_RST_EMIDDLEBOX 6
|
||||||
|
|
||||||
#endif /* _MPTCP_H */
|
#endif /* _MPTCP_H */
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ struct nexthop_grp {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */
|
NEXTHOP_GRP_TYPE_MPATH, /* hash-threshold nexthop group
|
||||||
|
* default type if not specified
|
||||||
|
*/
|
||||||
|
NEXTHOP_GRP_TYPE_RES, /* resilient nexthop group */
|
||||||
__NEXTHOP_GRP_TYPE_MAX,
|
__NEXTHOP_GRP_TYPE_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -52,8 +55,50 @@ enum {
|
||||||
NHA_FDB, /* flag; nexthop belongs to a bridge fdb */
|
NHA_FDB, /* flag; nexthop belongs to a bridge fdb */
|
||||||
/* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */
|
/* if NHA_FDB is added, OIF, BLACKHOLE, ENCAP cannot be set */
|
||||||
|
|
||||||
|
/* nested; resilient nexthop group attributes */
|
||||||
|
NHA_RES_GROUP,
|
||||||
|
/* nested; nexthop bucket attributes */
|
||||||
|
NHA_RES_BUCKET,
|
||||||
|
|
||||||
__NHA_MAX,
|
__NHA_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NHA_MAX (__NHA_MAX - 1)
|
#define NHA_MAX (__NHA_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NHA_RES_GROUP_UNSPEC,
|
||||||
|
/* Pad attribute for 64-bit alignment. */
|
||||||
|
NHA_RES_GROUP_PAD = NHA_RES_GROUP_UNSPEC,
|
||||||
|
|
||||||
|
/* u16; number of nexthop buckets in a resilient nexthop group */
|
||||||
|
NHA_RES_GROUP_BUCKETS,
|
||||||
|
/* clock_t as u32; nexthop bucket idle timer (per-group) */
|
||||||
|
NHA_RES_GROUP_IDLE_TIMER,
|
||||||
|
/* clock_t as u32; nexthop unbalanced timer */
|
||||||
|
NHA_RES_GROUP_UNBALANCED_TIMER,
|
||||||
|
/* clock_t as u64; nexthop unbalanced time */
|
||||||
|
NHA_RES_GROUP_UNBALANCED_TIME,
|
||||||
|
|
||||||
|
__NHA_RES_GROUP_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NHA_RES_GROUP_MAX (__NHA_RES_GROUP_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NHA_RES_BUCKET_UNSPEC,
|
||||||
|
/* Pad attribute for 64-bit alignment. */
|
||||||
|
NHA_RES_BUCKET_PAD = NHA_RES_BUCKET_UNSPEC,
|
||||||
|
|
||||||
|
/* u16; nexthop bucket index */
|
||||||
|
NHA_RES_BUCKET_INDEX,
|
||||||
|
/* clock_t as u64; nexthop bucket idle time */
|
||||||
|
NHA_RES_BUCKET_IDLE_TIME,
|
||||||
|
/* u32; nexthop id assigned to the nexthop bucket */
|
||||||
|
NHA_RES_BUCKET_NH_ID,
|
||||||
|
|
||||||
|
__NHA_RES_BUCKET_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NHA_RES_BUCKET_MAX (__NHA_RES_BUCKET_MAX - 1)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,8 @@ enum {
|
||||||
TCA_POLICE_PAD,
|
TCA_POLICE_PAD,
|
||||||
TCA_POLICE_RATE64,
|
TCA_POLICE_RATE64,
|
||||||
TCA_POLICE_PEAKRATE64,
|
TCA_POLICE_PEAKRATE64,
|
||||||
|
TCA_POLICE_PKTRATE64,
|
||||||
|
TCA_POLICE_PKTBURST64,
|
||||||
__TCA_POLICE_MAX
|
__TCA_POLICE_MAX
|
||||||
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
|
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,13 @@ enum {
|
||||||
RTM_GETVLAN,
|
RTM_GETVLAN,
|
||||||
#define RTM_GETVLAN RTM_GETVLAN
|
#define RTM_GETVLAN RTM_GETVLAN
|
||||||
|
|
||||||
|
RTM_NEWNEXTHOPBUCKET = 116,
|
||||||
|
#define RTM_NEWNEXTHOPBUCKET RTM_NEWNEXTHOPBUCKET
|
||||||
|
RTM_DELNEXTHOPBUCKET,
|
||||||
|
#define RTM_DELNEXTHOPBUCKET RTM_DELNEXTHOPBUCKET
|
||||||
|
RTM_GETNEXTHOPBUCKET,
|
||||||
|
#define RTM_GETNEXTHOPBUCKET RTM_GETNEXTHOPBUCKET
|
||||||
|
|
||||||
__RTM_MAX,
|
__RTM_MAX,
|
||||||
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
|
||||||
};
|
};
|
||||||
|
|
@ -283,6 +290,7 @@ enum {
|
||||||
#define RTPROT_MROUTED 17 /* Multicast daemon */
|
#define RTPROT_MROUTED 17 /* Multicast daemon */
|
||||||
#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
|
#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
|
||||||
#define RTPROT_BABEL 42 /* Babel daemon */
|
#define RTPROT_BABEL 42 /* Babel daemon */
|
||||||
|
#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */
|
||||||
#define RTPROT_BGP 186 /* BGP Routes */
|
#define RTPROT_BGP 186 /* BGP Routes */
|
||||||
#define RTPROT_ISIS 187 /* ISIS Routes */
|
#define RTPROT_ISIS 187 /* ISIS Routes */
|
||||||
#define RTPROT_OSPF 188 /* OSPF Routes */
|
#define RTPROT_OSPF 188 /* OSPF Routes */
|
||||||
|
|
|
||||||
|
|
@ -308,6 +308,7 @@ char *find_cgroup2_mount(bool do_mount);
|
||||||
__u64 get_cgroup2_id(const char *path);
|
__u64 get_cgroup2_id(const char *path);
|
||||||
char *get_cgroup2_path(__u64 id, bool full);
|
char *get_cgroup2_path(__u64 id, bool full);
|
||||||
int get_command_name(const char *pid, char *comm, size_t len);
|
int get_command_name(const char *pid, char *comm, size_t len);
|
||||||
|
char *get_task_name(pid_t pid);
|
||||||
|
|
||||||
int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
|
int get_rtnl_link_stats_rta(struct rtnl_link_stats64 *stats64,
|
||||||
struct rtattr *tb[]);
|
struct rtattr *tb[]);
|
||||||
|
|
|
||||||
16
ip/ip.c
16
ip/ip.c
|
|
@ -125,7 +125,7 @@ static const struct cmd {
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
static int do_cmd(const char *argv0, int argc, char **argv)
|
static int do_cmd(const char *argv0, int argc, char **argv, bool final)
|
||||||
{
|
{
|
||||||
const struct cmd *c;
|
const struct cmd *c;
|
||||||
|
|
||||||
|
|
@ -134,7 +134,8 @@ static int do_cmd(const char *argv0, int argc, char **argv)
|
||||||
return -(c->func(argc-1, argv+1));
|
return -(c->func(argc-1, argv+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0);
|
if (final)
|
||||||
|
fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv0);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +144,7 @@ static int ip_batch_cmd(int argc, char *argv[], void *data)
|
||||||
const int *orig_family = data;
|
const int *orig_family = data;
|
||||||
|
|
||||||
preferred_family = *orig_family;
|
preferred_family = *orig_family;
|
||||||
return do_cmd(argv[0], argc, argv);
|
return do_cmd(argv[0], argc, argv, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int batch(const char *name)
|
static int batch(const char *name)
|
||||||
|
|
@ -313,11 +314,14 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
rtnl_set_strict_dump(&rth);
|
rtnl_set_strict_dump(&rth);
|
||||||
|
|
||||||
if (strlen(basename) > 2)
|
if (strlen(basename) > 2) {
|
||||||
return do_cmd(basename+2, argc, argv);
|
int ret = do_cmd(basename+2, argc, argv, false);
|
||||||
|
if (ret != EXIT_FAILURE)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
return do_cmd(argv[1], argc-1, argv+1);
|
return do_cmd(argv[1], argc-1, argv+1, true);
|
||||||
|
|
||||||
rtnl_close(&rth);
|
rtnl_close(&rth);
|
||||||
usage();
|
usage();
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ int print_rule(struct nlmsghdr *n, void *arg);
|
||||||
int print_netconf(struct rtnl_ctrl_data *ctrl,
|
int print_netconf(struct rtnl_ctrl_data *ctrl,
|
||||||
struct nlmsghdr *n, void *arg);
|
struct nlmsghdr *n, void *arg);
|
||||||
int print_nexthop(struct nlmsghdr *n, void *arg);
|
int print_nexthop(struct nlmsghdr *n, void *arg);
|
||||||
|
int print_nexthop_bucket(struct nlmsghdr *n, void *arg);
|
||||||
void netns_map_init(void);
|
void netns_map_init(void);
|
||||||
void netns_nsid_socket_init(void);
|
void netns_nsid_socket_init(void);
|
||||||
int print_nsid(struct nlmsghdr *n, void *arg);
|
int print_nsid(struct nlmsghdr *n, void *arg);
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
|
||||||
print_nexthop(n, arg);
|
print_nexthop(n, arg);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case RTM_NEWNEXTHOPBUCKET:
|
||||||
|
case RTM_DELNEXTHOPBUCKET:
|
||||||
|
print_headers(fp, "[NEXTHOPBUCKET]", ctrl);
|
||||||
|
print_nexthop_bucket(n, arg);
|
||||||
|
return 0;
|
||||||
|
|
||||||
case RTM_NEWLINK:
|
case RTM_NEWLINK:
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
ll_remember_index(n, NULL);
|
ll_remember_index(n, NULL);
|
||||||
|
|
|
||||||
129
ip/ipmptcp.c
129
ip/ipmptcp.c
|
|
@ -17,12 +17,13 @@ static void usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
|
"Usage: ip mptcp endpoint add ADDRESS [ dev NAME ] [ id ID ]\n"
|
||||||
" [ FLAG-LIST ]\n"
|
" [ port NR ] [ FLAG-LIST ]\n"
|
||||||
" ip mptcp endpoint delete id ID\n"
|
" ip mptcp endpoint delete id ID\n"
|
||||||
" ip mptcp endpoint show [ id ID ]\n"
|
" ip mptcp endpoint show [ id ID ]\n"
|
||||||
" ip mptcp endpoint flush\n"
|
" ip mptcp endpoint flush\n"
|
||||||
" ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
|
" ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
|
||||||
" ip mptcp limits show\n"
|
" ip mptcp limits show\n"
|
||||||
|
" ip mptcp monitor\n"
|
||||||
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
|
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
|
||||||
"FLAG := [ signal | subflow | backup ]\n");
|
"FLAG := [ signal | subflow | backup ]\n");
|
||||||
|
|
||||||
|
|
@ -97,6 +98,7 @@ static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
|
||||||
bool id_set = false;
|
bool id_set = false;
|
||||||
__u32 index = 0;
|
__u32 index = 0;
|
||||||
__u32 flags = 0;
|
__u32 flags = 0;
|
||||||
|
__u16 port = 0;
|
||||||
__u8 id = 0;
|
__u8 id = 0;
|
||||||
|
|
||||||
ll_init_map(&rth);
|
ll_init_map(&rth);
|
||||||
|
|
@ -123,6 +125,10 @@ static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
|
||||||
if (!index)
|
if (!index)
|
||||||
invarg("device does not exist\n", ifname);
|
invarg("device does not exist\n", ifname);
|
||||||
|
|
||||||
|
} else if (matches(*argv, "port") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u16(&port, *argv, 0))
|
||||||
|
invarg("expected port", *argv);
|
||||||
} else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
|
} else if (get_addr(&address, *argv, AF_UNSPEC) == 0) {
|
||||||
addr_set = true;
|
addr_set = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -145,6 +151,8 @@ static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
|
||||||
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_FLAGS, flags);
|
||||||
if (index)
|
if (index)
|
||||||
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
|
addattr32(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_IF_IDX, index);
|
||||||
|
if (port)
|
||||||
|
addattr16(n, MPTCP_BUFLEN, MPTCP_PM_ADDR_ATTR_PORT, port);
|
||||||
if (addr_set) {
|
if (addr_set) {
|
||||||
int type;
|
int type;
|
||||||
|
|
||||||
|
|
@ -181,8 +189,8 @@ static int print_mptcp_addrinfo(struct rtattr *addrinfo)
|
||||||
__u8 family = AF_UNSPEC, addr_attr_type;
|
__u8 family = AF_UNSPEC, addr_attr_type;
|
||||||
const char *ifname;
|
const char *ifname;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
__u16 id, port;
|
||||||
int index;
|
int index;
|
||||||
__u16 id;
|
|
||||||
|
|
||||||
parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
|
parse_rtattr_nested(tb, MPTCP_PM_ADDR_ATTR_MAX, addrinfo);
|
||||||
|
|
||||||
|
|
@ -196,6 +204,11 @@ static int print_mptcp_addrinfo(struct rtattr *addrinfo)
|
||||||
print_string(PRINT_ANY, "address", "%s ",
|
print_string(PRINT_ANY, "address", "%s ",
|
||||||
format_host_rta(family, tb[addr_attr_type]));
|
format_host_rta(family, tb[addr_attr_type]));
|
||||||
}
|
}
|
||||||
|
if (tb[MPTCP_PM_ADDR_ATTR_PORT]) {
|
||||||
|
port = rta_getattr_u16(tb[MPTCP_PM_ADDR_ATTR_PORT]);
|
||||||
|
if (port)
|
||||||
|
print_uint(PRINT_ANY, "port", "port %u ", port);
|
||||||
|
}
|
||||||
if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
|
if (tb[MPTCP_PM_ADDR_ATTR_ID]) {
|
||||||
id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
|
id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
|
||||||
print_uint(PRINT_ANY, "id", "id %u ", id);
|
print_uint(PRINT_ANY, "id", "id %u ", id);
|
||||||
|
|
@ -385,6 +398,110 @@ static int mptcp_limit_get_set(int argc, char **argv, int cmd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * const event_to_str[] = {
|
||||||
|
[MPTCP_EVENT_CREATED] = "CREATED",
|
||||||
|
[MPTCP_EVENT_ESTABLISHED] = "ESTABLISHED",
|
||||||
|
[MPTCP_EVENT_CLOSED] = "CLOSED",
|
||||||
|
[MPTCP_EVENT_ANNOUNCED] = "ANNOUNCED",
|
||||||
|
[MPTCP_EVENT_REMOVED] = "REMOVED",
|
||||||
|
[MPTCP_EVENT_SUB_ESTABLISHED] = "SF_ESTABLISHED",
|
||||||
|
[MPTCP_EVENT_SUB_CLOSED] = "SF_CLOSED",
|
||||||
|
[MPTCP_EVENT_SUB_PRIORITY] = "SF_PRIO",
|
||||||
|
};
|
||||||
|
|
||||||
|
static void print_addr(const char *key, int af, struct rtattr *value)
|
||||||
|
{
|
||||||
|
void *data = RTA_DATA(value);
|
||||||
|
char str[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
if (inet_ntop(af, data, str, sizeof(str)))
|
||||||
|
printf(" %s=%s", key, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
|
||||||
|
struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
const struct genlmsghdr *ghdr = NLMSG_DATA(n);
|
||||||
|
struct rtattr *tb[MPTCP_ATTR_MAX + 1];
|
||||||
|
int len = n->nlmsg_len;
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||||
|
if (len < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (n->nlmsg_type != genl_family)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (timestamp)
|
||||||
|
print_timestamp(stdout);
|
||||||
|
|
||||||
|
if (ghdr->cmd >= ARRAY_SIZE(event_to_str)) {
|
||||||
|
printf("[UNKNOWN %u]\n", ghdr->cmd);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_to_str[ghdr->cmd] == NULL) {
|
||||||
|
printf("[UNKNOWN %u]\n", ghdr->cmd);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[%14s]", event_to_str[ghdr->cmd]);
|
||||||
|
|
||||||
|
parse_rtattr(tb, MPTCP_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
|
||||||
|
|
||||||
|
printf(" token=%08x", rta_getattr_u32(tb[MPTCP_ATTR_TOKEN]));
|
||||||
|
|
||||||
|
if (tb[MPTCP_ATTR_REM_ID])
|
||||||
|
printf(" remid=%u", rta_getattr_u8(tb[MPTCP_ATTR_REM_ID]));
|
||||||
|
if (tb[MPTCP_ATTR_LOC_ID])
|
||||||
|
printf(" locid=%u", rta_getattr_u8(tb[MPTCP_ATTR_LOC_ID]));
|
||||||
|
|
||||||
|
if (tb[MPTCP_ATTR_SADDR4])
|
||||||
|
print_addr("saddr4", AF_INET, tb[MPTCP_ATTR_SADDR4]);
|
||||||
|
if (tb[MPTCP_ATTR_DADDR4])
|
||||||
|
print_addr("daddr4", AF_INET, tb[MPTCP_ATTR_DADDR4]);
|
||||||
|
if (tb[MPTCP_ATTR_SADDR6])
|
||||||
|
print_addr("saddr6", AF_INET6, tb[MPTCP_ATTR_SADDR6]);
|
||||||
|
if (tb[MPTCP_ATTR_DADDR6])
|
||||||
|
print_addr("daddr6", AF_INET6, tb[MPTCP_ATTR_DADDR6]);
|
||||||
|
if (tb[MPTCP_ATTR_SPORT])
|
||||||
|
printf(" sport=%u", rta_getattr_be16(tb[MPTCP_ATTR_SPORT]));
|
||||||
|
if (tb[MPTCP_ATTR_DPORT])
|
||||||
|
printf(" dport=%u", rta_getattr_be16(tb[MPTCP_ATTR_DPORT]));
|
||||||
|
if (tb[MPTCP_ATTR_BACKUP])
|
||||||
|
printf(" backup=%d", rta_getattr_u8(tb[MPTCP_ATTR_BACKUP]));
|
||||||
|
if (tb[MPTCP_ATTR_ERROR])
|
||||||
|
printf(" error=%d", rta_getattr_u8(tb[MPTCP_ATTR_ERROR]));
|
||||||
|
if (tb[MPTCP_ATTR_FLAGS])
|
||||||
|
printf(" flags=%x", rta_getattr_u16(tb[MPTCP_ATTR_FLAGS]));
|
||||||
|
if (tb[MPTCP_ATTR_TIMEOUT])
|
||||||
|
printf(" timeout=%u", rta_getattr_u32(tb[MPTCP_ATTR_TIMEOUT]));
|
||||||
|
if (tb[MPTCP_ATTR_IF_IDX])
|
||||||
|
printf(" ifindex=%d", rta_getattr_s32(tb[MPTCP_ATTR_IF_IDX]));
|
||||||
|
if (tb[MPTCP_ATTR_RESET_REASON])
|
||||||
|
printf(" reset_reason=%u", rta_getattr_u32(tb[MPTCP_ATTR_RESET_REASON]));
|
||||||
|
if (tb[MPTCP_ATTR_RESET_FLAGS])
|
||||||
|
printf(" reset_flags=0x%x", rta_getattr_u32(tb[MPTCP_ATTR_RESET_FLAGS]));
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
out:
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_monitor(void)
|
||||||
|
{
|
||||||
|
if (genl_add_mcast_grp(&genl_rth, genl_family, MPTCP_PM_EV_GRP_NAME) < 0) {
|
||||||
|
perror("can't subscribe to mptcp events");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_listen(&genl_rth, mptcp_monitor_msg, stdout) < 0)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int do_mptcp(int argc, char **argv)
|
int do_mptcp(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc == 0)
|
if (argc == 0)
|
||||||
|
|
@ -429,6 +546,14 @@ int do_mptcp(int argc, char **argv)
|
||||||
MPTCP_PM_CMD_GET_LIMITS);
|
MPTCP_PM_CMD_GET_LIMITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matches(*argv, "monitor") == 0) {
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
if (argc == 0)
|
||||||
|
return mptcp_monitor();
|
||||||
|
|
||||||
|
goto unknown;
|
||||||
|
}
|
||||||
|
|
||||||
unknown:
|
unknown:
|
||||||
fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
|
fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
|
||||||
*argv);
|
*argv);
|
||||||
|
|
|
||||||
469
ip/ipnexthop.c
469
ip/ipnexthop.c
|
|
@ -21,6 +21,8 @@ static struct {
|
||||||
unsigned int master;
|
unsigned int master;
|
||||||
unsigned int proto;
|
unsigned int proto;
|
||||||
unsigned int fdb;
|
unsigned int fdb;
|
||||||
|
unsigned int id;
|
||||||
|
unsigned int nhid;
|
||||||
} filter;
|
} filter;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
@ -39,11 +41,19 @@ static void usage(void)
|
||||||
"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
|
"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
|
||||||
" ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
|
" ip nexthop { add | replace } id ID NH [ protocol ID ]\n"
|
||||||
" ip nexthop { get | del } id ID\n"
|
" ip nexthop { get | del } id ID\n"
|
||||||
|
" ip nexthop bucket list BUCKET_SELECTOR\n"
|
||||||
|
" ip nexthop bucket get id ID index INDEX\n"
|
||||||
"SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
|
"SELECTOR := [ id ID ] [ dev DEV ] [ vrf NAME ] [ master DEV ]\n"
|
||||||
" [ groups ] [ fdb ]\n"
|
" [ groups ] [ fdb ]\n"
|
||||||
|
"BUCKET_SELECTOR := SELECTOR | [ nhid ID ]\n"
|
||||||
"NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
|
"NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
|
||||||
" [ encap ENCAPTYPE ENCAPHDR ] | group GROUP [ fdb ] }\n"
|
" [ encap ENCAPTYPE ENCAPHDR ] |\n"
|
||||||
|
" group GROUP [ fdb ] [ type TYPE [ TYPE_ARGS ] ] }\n"
|
||||||
"GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
|
"GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
|
||||||
|
"TYPE := { mpath | resilient }\n"
|
||||||
|
"TYPE_ARGS := [ RESILIENT_ARGS ]\n"
|
||||||
|
"RESILIENT_ARGS := [ buckets BUCKETS ] [ idle_timer IDLE ]\n"
|
||||||
|
" [ unbalanced_timer UNBALANCED ]\n"
|
||||||
"ENCAPTYPE := [ mpls ]\n"
|
"ENCAPTYPE := [ mpls ]\n"
|
||||||
"ENCAPHDR := [ MPLSLABEL ]\n");
|
"ENCAPHDR := [ MPLSLABEL ]\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
|
@ -80,6 +90,36 @@ static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int nh_dump_bucket_filter(struct nlmsghdr *nlh, int reqlen)
|
||||||
|
{
|
||||||
|
struct rtattr *nest;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
err = nh_dump_filter(nlh, reqlen);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (filter.id) {
|
||||||
|
err = addattr32(nlh, reqlen, NHA_ID, filter.id);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter.nhid) {
|
||||||
|
nest = addattr_nest(nlh, reqlen, NHA_RES_BUCKET);
|
||||||
|
nest->rta_type |= NLA_F_NESTED;
|
||||||
|
|
||||||
|
err = addattr32(nlh, reqlen, NHA_RES_BUCKET_NH_ID,
|
||||||
|
filter.nhid);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
addattr_nest_end(nlh, nest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static struct rtnl_handle rth_del = { .fd = -1 };
|
static struct rtnl_handle rth_del = { .fd = -1 };
|
||||||
|
|
||||||
static int delete_nexthop(__u32 id)
|
static int delete_nexthop(__u32 id)
|
||||||
|
|
@ -201,6 +241,93 @@ static void print_nh_group(FILE *fp, const struct rtattr *grps_attr)
|
||||||
close_json_array(PRINT_JSON, NULL);
|
close_json_array(PRINT_JSON, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *nh_group_type_name(__u16 type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case NEXTHOP_GRP_TYPE_MPATH:
|
||||||
|
return "mpath";
|
||||||
|
case NEXTHOP_GRP_TYPE_RES:
|
||||||
|
return "resilient";
|
||||||
|
default:
|
||||||
|
return "<unknown type>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_nh_group_type(FILE *fp, const struct rtattr *grp_type_attr)
|
||||||
|
{
|
||||||
|
__u16 type = rta_getattr_u16(grp_type_attr);
|
||||||
|
|
||||||
|
if (type == NEXTHOP_GRP_TYPE_MPATH)
|
||||||
|
/* Do not print type in order not to break existing output. */
|
||||||
|
return;
|
||||||
|
|
||||||
|
print_string(PRINT_ANY, "type", "type %s ", nh_group_type_name(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_nh_res_group(FILE *fp, const struct rtattr *res_grp_attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[NHA_RES_GROUP_MAX + 1];
|
||||||
|
struct rtattr *rta;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, NHA_RES_GROUP_MAX, res_grp_attr);
|
||||||
|
|
||||||
|
open_json_object("resilient_args");
|
||||||
|
|
||||||
|
if (tb[NHA_RES_GROUP_BUCKETS])
|
||||||
|
print_uint(PRINT_ANY, "buckets", "buckets %u ",
|
||||||
|
rta_getattr_u16(tb[NHA_RES_GROUP_BUCKETS]));
|
||||||
|
|
||||||
|
if (tb[NHA_RES_GROUP_IDLE_TIMER]) {
|
||||||
|
rta = tb[NHA_RES_GROUP_IDLE_TIMER];
|
||||||
|
__jiffies_to_tv(&tv, rta_getattr_u32(rta));
|
||||||
|
print_tv(PRINT_ANY, "idle_timer", "idle_timer %g ", &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[NHA_RES_GROUP_UNBALANCED_TIMER]) {
|
||||||
|
rta = tb[NHA_RES_GROUP_UNBALANCED_TIMER];
|
||||||
|
__jiffies_to_tv(&tv, rta_getattr_u32(rta));
|
||||||
|
print_tv(PRINT_ANY, "unbalanced_timer", "unbalanced_timer %g ",
|
||||||
|
&tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[NHA_RES_GROUP_UNBALANCED_TIME]) {
|
||||||
|
rta = tb[NHA_RES_GROUP_UNBALANCED_TIME];
|
||||||
|
__jiffies_to_tv(&tv, rta_getattr_u32(rta));
|
||||||
|
print_tv(PRINT_ANY, "unbalanced_time", "unbalanced_time %g ",
|
||||||
|
&tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
close_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_nh_res_bucket(FILE *fp, const struct rtattr *res_bucket_attr)
|
||||||
|
{
|
||||||
|
struct rtattr *tb[NHA_RES_BUCKET_MAX + 1];
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, NHA_RES_BUCKET_MAX, res_bucket_attr);
|
||||||
|
|
||||||
|
open_json_object("bucket");
|
||||||
|
|
||||||
|
if (tb[NHA_RES_BUCKET_INDEX])
|
||||||
|
print_uint(PRINT_ANY, "index", "index %u ",
|
||||||
|
rta_getattr_u16(tb[NHA_RES_BUCKET_INDEX]));
|
||||||
|
|
||||||
|
if (tb[NHA_RES_BUCKET_IDLE_TIME]) {
|
||||||
|
struct rtattr *rta = tb[NHA_RES_BUCKET_IDLE_TIME];
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
__jiffies_to_tv(&tv, rta_getattr_u64(rta));
|
||||||
|
print_tv(PRINT_ANY, "idle_time", "idle_time %g ", &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tb[NHA_RES_BUCKET_NH_ID])
|
||||||
|
print_uint(PRINT_ANY, "nhid", "nhid %u ",
|
||||||
|
rta_getattr_u32(tb[NHA_RES_BUCKET_NH_ID]));
|
||||||
|
|
||||||
|
close_json_object();
|
||||||
|
}
|
||||||
|
|
||||||
int print_nexthop(struct nlmsghdr *n, void *arg)
|
int print_nexthop(struct nlmsghdr *n, void *arg)
|
||||||
{
|
{
|
||||||
struct nhmsg *nhm = NLMSG_DATA(n);
|
struct nhmsg *nhm = NLMSG_DATA(n);
|
||||||
|
|
@ -227,7 +354,7 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
|
||||||
if (filter.proto && filter.proto != nhm->nh_protocol)
|
if (filter.proto && filter.proto != nhm->nh_protocol)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
parse_rtattr(tb, NHA_MAX, RTM_NHA(nhm), len);
|
parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
|
||||||
|
|
||||||
open_json_object(NULL);
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
|
@ -241,6 +368,12 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
|
||||||
if (tb[NHA_GROUP])
|
if (tb[NHA_GROUP])
|
||||||
print_nh_group(fp, tb[NHA_GROUP]);
|
print_nh_group(fp, tb[NHA_GROUP]);
|
||||||
|
|
||||||
|
if (tb[NHA_GROUP_TYPE])
|
||||||
|
print_nh_group_type(fp, tb[NHA_GROUP_TYPE]);
|
||||||
|
|
||||||
|
if (tb[NHA_RES_GROUP])
|
||||||
|
print_nh_res_group(fp, tb[NHA_RES_GROUP]);
|
||||||
|
|
||||||
if (tb[NHA_ENCAP])
|
if (tb[NHA_ENCAP])
|
||||||
lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
|
lwt_print_encap(fp, tb[NHA_ENCAP_TYPE], tb[NHA_ENCAP]);
|
||||||
|
|
||||||
|
|
@ -275,6 +408,50 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
|
||||||
|
{
|
||||||
|
struct nhmsg *nhm = NLMSG_DATA(n);
|
||||||
|
struct rtattr *tb[NHA_MAX+1];
|
||||||
|
FILE *fp = (FILE *)arg;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (n->nlmsg_type != RTM_DELNEXTHOPBUCKET &&
|
||||||
|
n->nlmsg_type != RTM_NEWNEXTHOPBUCKET) {
|
||||||
|
fprintf(stderr, "Not a nexthop bucket: %08x %08x %08x\n",
|
||||||
|
n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = n->nlmsg_len - NLMSG_SPACE(sizeof(*nhm));
|
||||||
|
if (len < 0) {
|
||||||
|
close_json_object();
|
||||||
|
fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len, NLA_F_NESTED);
|
||||||
|
|
||||||
|
open_json_object(NULL);
|
||||||
|
|
||||||
|
if (n->nlmsg_type == RTM_DELNEXTHOP)
|
||||||
|
print_bool(PRINT_ANY, "deleted", "Deleted ", true);
|
||||||
|
|
||||||
|
if (tb[NHA_ID])
|
||||||
|
print_uint(PRINT_ANY, "id", "id %u ",
|
||||||
|
rta_getattr_u32(tb[NHA_ID]));
|
||||||
|
|
||||||
|
if (tb[NHA_RES_BUCKET])
|
||||||
|
print_nh_res_bucket(fp, tb[NHA_RES_BUCKET]);
|
||||||
|
|
||||||
|
print_rt_flags(fp, nhm->nh_flags);
|
||||||
|
|
||||||
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
||||||
|
close_json_object();
|
||||||
|
fflush(fp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
|
static int add_nh_group_attr(struct nlmsghdr *n, int maxlen, char *argv)
|
||||||
{
|
{
|
||||||
struct nexthop_grp *grps = NULL;
|
struct nexthop_grp *grps = NULL;
|
||||||
|
|
@ -331,6 +508,110 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int read_nh_group_type(const char *name)
|
||||||
|
{
|
||||||
|
if (strcmp(name, "mpath") == 0)
|
||||||
|
return NEXTHOP_GRP_TYPE_MPATH;
|
||||||
|
else if (strcmp(name, "resilient") == 0)
|
||||||
|
return NEXTHOP_GRP_TYPE_RES;
|
||||||
|
|
||||||
|
return __NEXTHOP_GRP_TYPE_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_nh_group_type_res(struct nlmsghdr *n, int maxlen, int *argcp,
|
||||||
|
char ***argvp)
|
||||||
|
{
|
||||||
|
char **argv = *argvp;
|
||||||
|
struct rtattr *nest;
|
||||||
|
int argc = *argcp;
|
||||||
|
|
||||||
|
if (!NEXT_ARG_OK())
|
||||||
|
return;
|
||||||
|
|
||||||
|
nest = addattr_nest(n, maxlen, NHA_RES_GROUP);
|
||||||
|
nest->rta_type |= NLA_F_NESTED;
|
||||||
|
|
||||||
|
NEXT_ARG_FWD();
|
||||||
|
while (argc > 0) {
|
||||||
|
if (strcmp(*argv, "buckets") == 0) {
|
||||||
|
__u16 buckets;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u16(&buckets, *argv, 0))
|
||||||
|
invarg("invalid buckets value", *argv);
|
||||||
|
|
||||||
|
addattr16(n, maxlen, NHA_RES_GROUP_BUCKETS, buckets);
|
||||||
|
} else if (strcmp(*argv, "idle_timer") == 0) {
|
||||||
|
__u32 idle_timer;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_unsigned(&idle_timer, *argv, 0) ||
|
||||||
|
idle_timer >= ~0UL / 100)
|
||||||
|
invarg("invalid idle timer value", *argv);
|
||||||
|
|
||||||
|
addattr32(n, maxlen, NHA_RES_GROUP_IDLE_TIMER,
|
||||||
|
idle_timer * 100);
|
||||||
|
} else if (strcmp(*argv, "unbalanced_timer") == 0) {
|
||||||
|
__u32 unbalanced_timer;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_unsigned(&unbalanced_timer, *argv, 0) ||
|
||||||
|
unbalanced_timer >= ~0UL / 100)
|
||||||
|
invarg("invalid unbalanced timer value", *argv);
|
||||||
|
|
||||||
|
addattr32(n, maxlen, NHA_RES_GROUP_UNBALANCED_TIMER,
|
||||||
|
unbalanced_timer * 100);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* argv is currently the first unparsed argument, but ipnh_modify()
|
||||||
|
* will move to the next, so step back.
|
||||||
|
*/
|
||||||
|
*argcp = argc + 1;
|
||||||
|
*argvp = argv - 1;
|
||||||
|
|
||||||
|
addattr_nest_end(n, nest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_nh_group_type(struct nlmsghdr *n, int maxlen, int *argcp,
|
||||||
|
char ***argvp)
|
||||||
|
{
|
||||||
|
char **argv = *argvp;
|
||||||
|
int argc = *argcp;
|
||||||
|
__u16 type;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
type = read_nh_group_type(*argv);
|
||||||
|
if (type > NEXTHOP_GRP_TYPE_MAX)
|
||||||
|
invarg("\"type\" value is invalid\n", *argv);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case NEXTHOP_GRP_TYPE_MPATH:
|
||||||
|
/* No additional arguments */
|
||||||
|
break;
|
||||||
|
case NEXTHOP_GRP_TYPE_RES:
|
||||||
|
parse_nh_group_type_res(n, maxlen, &argc, &argv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
*argcp = argc;
|
||||||
|
*argvp = argv;
|
||||||
|
|
||||||
|
addattr16(n, maxlen, NHA_GROUP_TYPE, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipnh_parse_id(const char *argv)
|
||||||
|
{
|
||||||
|
__u32 id;
|
||||||
|
|
||||||
|
if (get_unsigned(&id, argv, 0))
|
||||||
|
invarg("invalid id value", argv);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
|
static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -347,12 +628,9 @@ static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
|
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
if (!strcmp(*argv, "id")) {
|
if (!strcmp(*argv, "id")) {
|
||||||
__u32 id;
|
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_unsigned(&id, *argv, 0))
|
addattr32(&req.n, sizeof(req), NHA_ID,
|
||||||
invarg("invalid id value", *argv);
|
ipnh_parse_id(*argv));
|
||||||
addattr32(&req.n, sizeof(req), NHA_ID, id);
|
|
||||||
} else if (!strcmp(*argv, "dev")) {
|
} else if (!strcmp(*argv, "dev")) {
|
||||||
int ifindex;
|
int ifindex;
|
||||||
|
|
||||||
|
|
@ -407,6 +685,8 @@ static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
|
|
||||||
if (add_nh_group_attr(&req.n, sizeof(req), *argv))
|
if (add_nh_group_attr(&req.n, sizeof(req), *argv))
|
||||||
invarg("\"group\" value is invalid\n", *argv);
|
invarg("\"group\" value is invalid\n", *argv);
|
||||||
|
} else if (!strcmp(*argv, "type")) {
|
||||||
|
parse_nh_group_type(&req.n, sizeof(req), &argc, &argv);
|
||||||
} else if (matches(*argv, "protocol") == 0) {
|
} else if (matches(*argv, "protocol") == 0) {
|
||||||
__u32 prot;
|
__u32 prot;
|
||||||
|
|
||||||
|
|
@ -464,6 +744,24 @@ static int ipnh_get_id(__u32 id)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ipnh_list_flush_id(__u32 id, int action)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (action == IPNH_LIST)
|
||||||
|
return ipnh_get_id(id);
|
||||||
|
|
||||||
|
if (rtnl_open(&rth_del, 0) < 0) {
|
||||||
|
fprintf(stderr, "Cannot open rtnetlink\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = delete_nexthop(id);
|
||||||
|
rtnl_close(&rth_del);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int ipnh_list_flush(int argc, char **argv, int action)
|
static int ipnh_list_flush(int argc, char **argv, int action)
|
||||||
{
|
{
|
||||||
unsigned int all = (argc == 0);
|
unsigned int all = (argc == 0);
|
||||||
|
|
@ -489,12 +787,8 @@ static int ipnh_list_flush(int argc, char **argv, int action)
|
||||||
if (!filter.master)
|
if (!filter.master)
|
||||||
invarg("VRF does not exist\n", *argv);
|
invarg("VRF does not exist\n", *argv);
|
||||||
} else if (!strcmp(*argv, "id")) {
|
} else if (!strcmp(*argv, "id")) {
|
||||||
__u32 id;
|
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_unsigned(&id, *argv, 0))
|
return ipnh_list_flush_id(ipnh_parse_id(*argv), action);
|
||||||
invarg("invalid id value", *argv);
|
|
||||||
return ipnh_get_id(id);
|
|
||||||
} else if (!matches(*argv, "protocol")) {
|
} else if (!matches(*argv, "protocol")) {
|
||||||
__u32 proto;
|
__u32 proto;
|
||||||
|
|
||||||
|
|
@ -540,8 +834,7 @@ static int ipnh_get(int argc, char **argv)
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
if (!strcmp(*argv, "id")) {
|
if (!strcmp(*argv, "id")) {
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_unsigned(&id, *argv, 0))
|
id = ipnh_parse_id(*argv);
|
||||||
invarg("invalid id value", *argv);
|
|
||||||
} else {
|
} else {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
@ -556,6 +849,151 @@ static int ipnh_get(int argc, char **argv)
|
||||||
return ipnh_get_id(id);
|
return ipnh_get_id(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ipnh_bucket_list(int argc, char **argv)
|
||||||
|
{
|
||||||
|
while (argc > 0) {
|
||||||
|
if (!matches(*argv, "dev")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
filter.ifindex = ll_name_to_index(*argv);
|
||||||
|
if (!filter.ifindex)
|
||||||
|
invarg("Device does not exist\n", *argv);
|
||||||
|
} else if (!matches(*argv, "master")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
filter.master = ll_name_to_index(*argv);
|
||||||
|
if (!filter.master)
|
||||||
|
invarg("Device does not exist\n", *argv);
|
||||||
|
} else if (matches(*argv, "vrf") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!name_is_vrf(*argv))
|
||||||
|
invarg("Invalid VRF\n", *argv);
|
||||||
|
filter.master = ll_name_to_index(*argv);
|
||||||
|
if (!filter.master)
|
||||||
|
invarg("VRF does not exist\n", *argv);
|
||||||
|
} else if (!strcmp(*argv, "id")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
filter.id = ipnh_parse_id(*argv);
|
||||||
|
} else if (!strcmp(*argv, "nhid")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
filter.nhid = ipnh_parse_id(*argv);
|
||||||
|
} else if (matches(*argv, "help") == 0) {
|
||||||
|
usage();
|
||||||
|
} else {
|
||||||
|
invarg("", *argv);
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtnl_nexthop_bucket_dump_req(&rth, preferred_family,
|
||||||
|
nh_dump_bucket_filter) < 0) {
|
||||||
|
perror("Cannot send dump request");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_json_obj(json);
|
||||||
|
|
||||||
|
if (rtnl_dump_filter(&rth, print_nexthop_bucket, stdout) < 0) {
|
||||||
|
fprintf(stderr, "Dump terminated\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_json_obj();
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipnh_bucket_get_id(__u32 id, __u16 bucket_index)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct nlmsghdr n;
|
||||||
|
struct nhmsg nhm;
|
||||||
|
char buf[1024];
|
||||||
|
} req = {
|
||||||
|
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
|
||||||
|
.n.nlmsg_flags = NLM_F_REQUEST,
|
||||||
|
.n.nlmsg_type = RTM_GETNEXTHOPBUCKET,
|
||||||
|
.nhm.nh_family = preferred_family,
|
||||||
|
};
|
||||||
|
struct nlmsghdr *answer;
|
||||||
|
struct rtattr *nest;
|
||||||
|
|
||||||
|
addattr32(&req.n, sizeof(req), NHA_ID, id);
|
||||||
|
|
||||||
|
nest = addattr_nest(&req.n, sizeof(req), NHA_RES_BUCKET);
|
||||||
|
nest->rta_type |= NLA_F_NESTED;
|
||||||
|
|
||||||
|
addattr16(&req.n, sizeof(req), NHA_RES_BUCKET_INDEX, bucket_index);
|
||||||
|
|
||||||
|
addattr_nest_end(&req.n, nest);
|
||||||
|
|
||||||
|
if (rtnl_talk(&rth, &req.n, &answer) < 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
new_json_obj(json);
|
||||||
|
|
||||||
|
if (print_nexthop_bucket(answer, (void *)stdout) < 0) {
|
||||||
|
free(answer);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_json_obj();
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
free(answer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipnh_bucket_get(int argc, char **argv)
|
||||||
|
{
|
||||||
|
bool bucket_valid = false;
|
||||||
|
__u16 bucket_index;
|
||||||
|
__u32 id = 0;
|
||||||
|
|
||||||
|
while (argc > 0) {
|
||||||
|
if (!strcmp(*argv, "id")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
id = ipnh_parse_id(*argv);
|
||||||
|
} else if (!strcmp(*argv, "index")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u16(&bucket_index, *argv, 0))
|
||||||
|
invarg("invalid bucket index value", *argv);
|
||||||
|
bucket_valid = true;
|
||||||
|
} else {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
argc--; argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id || !bucket_valid) {
|
||||||
|
usage();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipnh_bucket_get_id(id, bucket_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_ipnh_bucket(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
return ipnh_bucket_list(0, NULL);
|
||||||
|
|
||||||
|
if (!matches(*argv, "list") ||
|
||||||
|
!matches(*argv, "show") ||
|
||||||
|
!matches(*argv, "lst"))
|
||||||
|
return ipnh_bucket_list(argc-1, argv+1);
|
||||||
|
|
||||||
|
if (!matches(*argv, "get"))
|
||||||
|
return ipnh_bucket_get(argc-1, argv+1);
|
||||||
|
|
||||||
|
if (!matches(*argv, "help"))
|
||||||
|
usage();
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"Command \"%s\" is unknown, try \"ip nexthop help\".\n", *argv);
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
int do_ipnh(int argc, char **argv)
|
int do_ipnh(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (argc < 1)
|
if (argc < 1)
|
||||||
|
|
@ -581,6 +1019,9 @@ int do_ipnh(int argc, char **argv)
|
||||||
if (!matches(*argv, "flush"))
|
if (!matches(*argv, "flush"))
|
||||||
return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
|
return ipnh_list_flush(argc-1, argv+1, IPNH_FLUSH);
|
||||||
|
|
||||||
|
if (!matches(*argv, "bucket"))
|
||||||
|
return do_ipnh_bucket(argc-1, argv+1);
|
||||||
|
|
||||||
if (!matches(*argv, "help"))
|
if (!matches(*argv, "help"))
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -260,35 +260,6 @@ static void print_flags(long flags)
|
||||||
close_json_array(PRINT_JSON, NULL);
|
close_json_array(PRINT_JSON, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *pid_name(pid_t pid)
|
|
||||||
{
|
|
||||||
char *comm;
|
|
||||||
FILE *f;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
err = asprintf(&comm, "/proc/%d/comm", pid);
|
|
||||||
if (err < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
f = fopen(comm, "r");
|
|
||||||
free(comm);
|
|
||||||
if (!f) {
|
|
||||||
perror("fopen");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fscanf(f, "%ms\n", &comm) != 1) {
|
|
||||||
perror("fscanf");
|
|
||||||
comm = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (fclose(f))
|
|
||||||
perror("fclose");
|
|
||||||
|
|
||||||
return comm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void show_processes(const char *name)
|
static void show_processes(const char *name)
|
||||||
{
|
{
|
||||||
glob_t globbuf = { };
|
glob_t globbuf = { };
|
||||||
|
|
@ -346,7 +317,7 @@ static void show_processes(const char *name)
|
||||||
} else if (err == 2 &&
|
} else if (err == 2 &&
|
||||||
!strcmp("iff", key) &&
|
!strcmp("iff", key) &&
|
||||||
!strcmp(name, value)) {
|
!strcmp(name, value)) {
|
||||||
char *pname = pid_name(pid);
|
char *pname = get_task_name(pid);
|
||||||
|
|
||||||
print_string(PRINT_ANY, "name",
|
print_string(PRINT_ANY, "name",
|
||||||
"%s", pname ? : "<NULL>");
|
"%s", pname ? : "<NULL>");
|
||||||
|
|
|
||||||
|
|
@ -907,6 +907,14 @@ void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
|
||||||
fprintf(fp, "if_id %#x", if_id);
|
fprintf(fp, "if_id %#x", if_id);
|
||||||
fprintf(fp, "%s", _SL_);
|
fprintf(fp, "%s", _SL_);
|
||||||
}
|
}
|
||||||
|
if (tb[XFRMA_TFCPAD]) {
|
||||||
|
__u32 tfcpad = rta_getattr_u32(tb[XFRMA_TFCPAD]);
|
||||||
|
|
||||||
|
if (prefix)
|
||||||
|
fputs(prefix, fp);
|
||||||
|
fprintf(fp, "tfcpad %u", tfcpad);
|
||||||
|
fprintf(fp, "%s", _SL_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xfrm_selector_iszero(struct xfrm_selector *s)
|
static int xfrm_selector_iszero(struct xfrm_selector *s)
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ static void usage(void)
|
||||||
" [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n"
|
" [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n"
|
||||||
" [ offload [dev DEV] dir DIR ]\n"
|
" [ offload [dev DEV] dir DIR ]\n"
|
||||||
" [ output-mark OUTPUT-MARK [ mask MASK ] ]\n"
|
" [ output-mark OUTPUT-MARK [ mask MASK ] ]\n"
|
||||||
" [ if_id IF_ID ]\n"
|
" [ if_id IF_ID ] [ tfcpad LENGTH ]\n"
|
||||||
"Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n"
|
"Usage: ip xfrm state allocspi ID [ mode MODE ] [ mark MARK [ mask MASK ] ]\n"
|
||||||
" [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n"
|
" [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\n"
|
||||||
"Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n"
|
"Usage: ip xfrm state { delete | get } ID [ mark MARK [ mask MASK ] ]\n"
|
||||||
|
|
@ -331,6 +331,7 @@ static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
struct xfrm_mark output_mark = {0, 0};
|
struct xfrm_mark output_mark = {0, 0};
|
||||||
bool is_if_id_set = false;
|
bool is_if_id_set = false;
|
||||||
__u32 if_id = 0;
|
__u32 if_id = 0;
|
||||||
|
__u32 tfcpad = 0;
|
||||||
|
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
if (strcmp(*argv, "mode") == 0) {
|
if (strcmp(*argv, "mode") == 0) {
|
||||||
|
|
@ -465,6 +466,10 @@ static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
if (get_u32(&if_id, *argv, 0))
|
if (get_u32(&if_id, *argv, 0))
|
||||||
invarg("value after \"if_id\" is invalid", *argv);
|
invarg("value after \"if_id\" is invalid", *argv);
|
||||||
is_if_id_set = true;
|
is_if_id_set = true;
|
||||||
|
} else if (strcmp(*argv, "tfcpad") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (get_u32(&tfcpad, *argv, 0))
|
||||||
|
invarg("value after \"tfcpad\" is invalid", *argv);
|
||||||
} else {
|
} else {
|
||||||
/* try to assume ALGO */
|
/* try to assume ALGO */
|
||||||
int type = xfrm_algotype_getbyname(*argv);
|
int type = xfrm_algotype_getbyname(*argv);
|
||||||
|
|
@ -650,6 +655,9 @@ static int xfrm_state_modify(int cmd, unsigned int flags, int argc, char **argv)
|
||||||
if (is_if_id_set)
|
if (is_if_id_set)
|
||||||
addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id);
|
addattr32(&req.n, sizeof(req.buf), XFRMA_IF_ID, if_id);
|
||||||
|
|
||||||
|
if (tfcpad)
|
||||||
|
addattr32(&req.n, sizeof(req.buf), XFRMA_TFCPAD, tfcpad);
|
||||||
|
|
||||||
if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
|
if (xfrm_xfrmproto_is_ipsec(req.xsinfo.id.proto)) {
|
||||||
switch (req.xsinfo.mode) {
|
switch (req.xsinfo.mode) {
|
||||||
case XFRM_MODE_TRANSPORT:
|
case XFRM_MODE_TRANSPORT:
|
||||||
|
|
|
||||||
24
lib/fs.c
24
lib/fs.c
|
|
@ -316,3 +316,27 @@ int get_command_name(const char *pid, char *comm, size_t len)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *get_task_name(pid_t pid)
|
||||||
|
{
|
||||||
|
char *comm;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (!pid)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
f = fopen(comm, "r");
|
||||||
|
if (!f)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (fscanf(f, "%ms\n", &comm) != 1)
|
||||||
|
comm = NULL;
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,19 @@ int print_color_null(enum output_type type,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int print_color_tv(enum output_type type,
|
||||||
|
enum color_attr color,
|
||||||
|
const char *key,
|
||||||
|
const char *fmt,
|
||||||
|
const struct timeval *tv)
|
||||||
|
{
|
||||||
|
double usecs = tv->tv_usec;
|
||||||
|
double secs = tv->tv_sec;
|
||||||
|
double time = secs + usecs / 1000000;
|
||||||
|
|
||||||
|
return print_color_float(type, color, key, fmt, time);
|
||||||
|
}
|
||||||
|
|
||||||
/* Print line separator (if not in JSON mode) */
|
/* Print line separator (if not in JSON mode) */
|
||||||
void print_nl(void)
|
void print_nl(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,72 @@ int genl_resolve_family(struct rtnl_handle *grth, const char *family)
|
||||||
return fnum;
|
return fnum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int genl_parse_grps(struct rtattr *attr, const char *name, unsigned int *id)
|
||||||
|
{
|
||||||
|
const struct rtattr *pos;
|
||||||
|
|
||||||
|
rtattr_for_each_nested(pos, attr) {
|
||||||
|
struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
|
||||||
|
|
||||||
|
parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, pos);
|
||||||
|
|
||||||
|
if (tb[CTRL_ATTR_MCAST_GRP_NAME] && tb[CTRL_ATTR_MCAST_GRP_ID]) {
|
||||||
|
if (strcmp(name, rta_getattr_str(tb[CTRL_ATTR_MCAST_GRP_NAME])) == 0) {
|
||||||
|
*id = rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 fnum, const char *group)
|
||||||
|
{
|
||||||
|
GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
|
||||||
|
NLM_F_REQUEST);
|
||||||
|
struct rtattr *tb[CTRL_ATTR_MAX + 1];
|
||||||
|
struct nlmsghdr *answer = NULL;
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
struct rtattr *attrs;
|
||||||
|
int len, ret = -1;
|
||||||
|
unsigned int id;
|
||||||
|
|
||||||
|
addattr16(&req.n, sizeof(req), CTRL_ATTR_FAMILY_ID, fnum);
|
||||||
|
|
||||||
|
if (rtnl_talk(grth, &req.n, &answer) < 0) {
|
||||||
|
fprintf(stderr, "Error talking to the kernel\n");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ghdr = NLMSG_DATA(answer);
|
||||||
|
len = answer->nlmsg_len;
|
||||||
|
|
||||||
|
if (answer->nlmsg_type != GENL_ID_CTRL)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
len -= NLMSG_LENGTH(GENL_HDRLEN);
|
||||||
|
if (len < 0)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
|
||||||
|
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
|
||||||
|
|
||||||
|
if (tb[CTRL_ATTR_MCAST_GROUPS] == NULL) {
|
||||||
|
fprintf(stderr, "Missing mcast groups TLV\n");
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genl_parse_grps(tb[CTRL_ATTR_MCAST_GROUPS], group, &id) < 0)
|
||||||
|
goto err_free;
|
||||||
|
|
||||||
|
ret = rtnl_add_nl_group(grth, id);
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
free(answer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int genl_init_handle(struct rtnl_handle *grth, const char *family,
|
int genl_init_handle(struct rtnl_handle *grth, const char *family,
|
||||||
int *genl_family)
|
int *genl_family)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,32 @@ int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
|
||||||
return send(rth->fd, &req, sizeof(req), 0);
|
return send(rth->fd, &req, sizeof(req), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rtnl_nexthop_bucket_dump_req(struct rtnl_handle *rth, int family,
|
||||||
|
req_filter_fn_t filter_fn)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct nlmsghdr nlh;
|
||||||
|
struct nhmsg nhm;
|
||||||
|
char buf[128];
|
||||||
|
} req = {
|
||||||
|
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)),
|
||||||
|
.nlh.nlmsg_type = RTM_GETNEXTHOPBUCKET,
|
||||||
|
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||||
|
.nlh.nlmsg_seq = rth->dump = ++rth->seq,
|
||||||
|
.nhm.nh_family = family,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (filter_fn) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = filter_fn(&req.nlh, sizeof(req));
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return send(rth->fd, &req, sizeof(req), 0);
|
||||||
|
}
|
||||||
|
|
||||||
int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
|
int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
|
||||||
req_filter_fn_t filter_fn)
|
req_filter_fn_t filter_fn)
|
||||||
{
|
{
|
||||||
|
|
@ -424,6 +450,25 @@ int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
|
||||||
return send(rth->fd, &req, sizeof(req), 0);
|
return send(rth->fd, &req, sizeof(req), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rtnl_brvlandump_req(struct rtnl_handle *rth, int family, __u32 dump_flags)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
struct nlmsghdr nlh;
|
||||||
|
struct br_vlan_msg bvm;
|
||||||
|
char buf[256];
|
||||||
|
} req = {
|
||||||
|
.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct br_vlan_msg)),
|
||||||
|
.nlh.nlmsg_type = RTM_GETVLAN,
|
||||||
|
.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
|
||||||
|
.nlh.nlmsg_seq = rth->dump = ++rth->seq,
|
||||||
|
.bvm.family = family,
|
||||||
|
};
|
||||||
|
|
||||||
|
addattr32(&req.nlh, sizeof(req), BRIDGE_VLANDB_DUMP_FLAGS, dump_flags);
|
||||||
|
|
||||||
|
return send(rth->fd, &req, sizeof(req), 0);
|
||||||
|
}
|
||||||
|
|
||||||
int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
|
int rtnl_netconfdump_req(struct rtnl_handle *rth, int family)
|
||||||
{
|
{
|
||||||
struct {
|
struct {
|
||||||
|
|
|
||||||
|
|
@ -196,19 +196,28 @@ void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg)
|
||||||
free(nlg->buf);
|
free(nlg->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
struct nlmsghdr *
|
||||||
uint8_t cmd, uint16_t flags)
|
_mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||||
|
uint8_t cmd, uint16_t flags,
|
||||||
|
uint32_t id, uint8_t version)
|
||||||
{
|
{
|
||||||
struct genlmsghdr hdr = {};
|
struct genlmsghdr hdr = {};
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
|
||||||
hdr.cmd = cmd;
|
hdr.cmd = cmd;
|
||||||
hdr.version = nlg->version;
|
hdr.version = version;
|
||||||
nlh = mnlu_msg_prepare(nlg->buf, nlg->family, flags, &hdr, sizeof(hdr));
|
nlh = mnlu_msg_prepare(nlg->buf, id, flags, &hdr, sizeof(hdr));
|
||||||
nlg->seq = nlh->nlmsg_seq;
|
nlg->seq = nlh->nlmsg_seq;
|
||||||
return nlh;
|
return nlh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
|
||||||
|
uint8_t cmd, uint16_t flags)
|
||||||
|
{
|
||||||
|
return _mnlu_gen_socket_cmd_prepare(nlg, cmd, flags, nlg->family,
|
||||||
|
nlg->version);
|
||||||
|
}
|
||||||
|
|
||||||
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh,
|
||||||
mnl_cb_t data_cb, void *data)
|
mnl_cb_t data_cb, void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -229,3 +238,11 @@ int mnlu_gen_socket_sndrcv(struct mnlu_gen_socket *nlg, const struct nlmsghdr *n
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mnlu_gen_socket_recv_run(struct mnlu_gen_socket *nlg, mnl_cb_t cb,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
return mnlu_socket_recv_run(nlg->nl, nlg->seq, nlg->buf,
|
||||||
|
MNL_SOCKET_BUFFER_SIZE,
|
||||||
|
cb, data);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,13 +138,22 @@ bridge \- show / manipulate bridge addresses and devices
|
||||||
.BR pvid " ] [ " untagged " ] [ "
|
.BR pvid " ] [ " untagged " ] [ "
|
||||||
.BR self " ] [ " master " ] "
|
.BR self " ] [ " master " ] "
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "bridge vlan set"
|
||||||
|
.B dev
|
||||||
|
.I DEV
|
||||||
|
.B vid
|
||||||
|
.IR VID " [ "
|
||||||
|
.B state
|
||||||
|
.IR STP_STATE " ] "
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
|
.BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
|
||||||
.B dev
|
.B dev
|
||||||
.IR DEV " ]"
|
.IR DEV " ]"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " ]"
|
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " | " vlan " ]"
|
||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
|
|
||||||
|
|
@ -162,7 +171,7 @@ As a rule, the information is statistics or some time values.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR "\-d" , " \-details"
|
.BR "\-d" , " \-details"
|
||||||
print detailed information about MDB router ports.
|
print detailed information about bridge vlan filter entries or MDB router ports.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BR "\-n" , " \-net" , " \-netns " <NETNS>
|
.BR "\-n" , " \-net" , " \-netns " <NETNS>
|
||||||
|
|
@ -813,10 +822,70 @@ The
|
||||||
.BR "pvid " and " untagged"
|
.BR "pvid " and " untagged"
|
||||||
flags are ignored.
|
flags are ignored.
|
||||||
|
|
||||||
|
.SS bridge vlan set - change vlan filter entry's options
|
||||||
|
|
||||||
|
This command changes vlan filter entry's options.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI dev " NAME"
|
||||||
|
the interface with which this vlan is associated.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI vid " VID"
|
||||||
|
the VLAN ID that identifies the vlan.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI state " STP_STATE "
|
||||||
|
the operation state of the vlan. One may enter STP state name (case insensitive), or one of the
|
||||||
|
numbers below. Negative inputs are ignored, and unrecognized names return an
|
||||||
|
error. Note that the state is set only for the vlan of the specified device, e.g. if it is
|
||||||
|
a bridge port then the state will be set only for the vlan of the port.
|
||||||
|
|
||||||
|
.B 0
|
||||||
|
- vlan is in STP
|
||||||
|
.B DISABLED
|
||||||
|
state. Make this vlan completely inactive for STP. This is also called
|
||||||
|
BPDU filter and could be used to disable STP on an untrusted vlan.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B 1
|
||||||
|
- vlan is in STP
|
||||||
|
.B LISTENING
|
||||||
|
state. Only valid if STP is enabled on the bridge. In this
|
||||||
|
state the vlan listens for STP BPDUs and drops all other traffic frames.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B 2
|
||||||
|
- vlan is in STP
|
||||||
|
.B LEARNING
|
||||||
|
state. Only valid if STP is enabled on the bridge. In this
|
||||||
|
state the vlan will accept traffic only for the purpose of updating MAC
|
||||||
|
address tables.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B 3
|
||||||
|
- vlan is in STP
|
||||||
|
.B FORWARDING
|
||||||
|
state. This is the default vlan state.
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B 4
|
||||||
|
- vlan is in STP
|
||||||
|
.B BLOCKING
|
||||||
|
state. Only valid if STP is enabled on the bridge. This state
|
||||||
|
is used during the STP election process. In this state, the vlan will only process
|
||||||
|
STP BPDUs.
|
||||||
|
.sp
|
||||||
|
|
||||||
.SS bridge vlan show - list vlan configuration.
|
.SS bridge vlan show - list vlan configuration.
|
||||||
|
|
||||||
This command displays the current VLAN filter table.
|
This command displays the current VLAN filter table.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
With the
|
||||||
|
.B -details
|
||||||
|
option, the command becomes verbose. It displays the per-vlan options.
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
With the
|
With the
|
||||||
.B -statistics
|
.B -statistics
|
||||||
|
|
@ -842,7 +911,7 @@ command is the first in the command line and then the object list follows:
|
||||||
.I OBJECT-LIST
|
.I OBJECT-LIST
|
||||||
is the list of object types that we want to monitor.
|
is the list of object types that we want to monitor.
|
||||||
It may contain
|
It may contain
|
||||||
.BR link ", " fdb ", and " mdb "."
|
.BR link ", " fdb ", " vlan " and " mdb "."
|
||||||
If no
|
If no
|
||||||
.B file
|
.B file
|
||||||
argument is given,
|
argument is given,
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ ip-mptcp \- MPTCP path manager configuration
|
||||||
.ti -8
|
.ti -8
|
||||||
.BR "ip mptcp endpoint add "
|
.BR "ip mptcp endpoint add "
|
||||||
.IR IFADDR
|
.IR IFADDR
|
||||||
|
.RB "[ " port
|
||||||
|
.IR PORT " ]"
|
||||||
.RB "[ " dev
|
.RB "[ " dev
|
||||||
.IR IFNAME " ]"
|
.IR IFNAME " ]"
|
||||||
.RB "[ " id
|
.RB "[ " id
|
||||||
|
|
@ -65,6 +67,9 @@ ip-mptcp \- MPTCP path manager configuration
|
||||||
.ti -8
|
.ti -8
|
||||||
.BR "ip mptcp limits show"
|
.BR "ip mptcp limits show"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip mptcp monitor"
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
|
||||||
MPTCP is a transport protocol built on top of TCP that allows TCP
|
MPTCP is a transport protocol built on top of TCP that allows TCP
|
||||||
|
|
@ -87,6 +92,12 @@ ip mptcp endpoint flush flush all existing MPTCP endpoints
|
||||||
.TE
|
.TE
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
|
.IR PORT
|
||||||
|
When a port number is specified, incoming MPTCP subflows for already
|
||||||
|
established MPTCP sockets will be accepted on the specified port, regardless
|
||||||
|
the original listener port accepting the first MPTCP subflow and/or
|
||||||
|
this peer being actually on the client side.
|
||||||
|
|
||||||
.IR ID
|
.IR ID
|
||||||
is a unique numeric identifier for the given endpoint
|
is a unique numeric identifier for the given endpoint
|
||||||
|
|
||||||
|
|
@ -137,5 +148,10 @@ each accepted ADD_ADDR option, respecting the
|
||||||
.IR SUBFLOW_NR
|
.IR SUBFLOW_NR
|
||||||
limit.
|
limit.
|
||||||
|
|
||||||
|
.sp
|
||||||
|
.PP
|
||||||
|
.B monitor
|
||||||
|
displays creation and deletion of MPTCP connections as well as addition or removal of remote addresses and subflows.
|
||||||
|
|
||||||
.SH AUTHOR
|
.SH AUTHOR
|
||||||
Original Manpage by Paolo Abeni <pabeni@redhat.com>
|
Original Manpage by Paolo Abeni <pabeni@redhat.com>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,14 @@ ip-nexthop \- nexthop object management
|
||||||
.BR "ip nexthop" " { " get " | " del " } id "
|
.BR "ip nexthop" " { " get " | " del " } id "
|
||||||
.I ID
|
.I ID
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BI "ip nexthop bucket list " BUCKET_SELECTOR
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.BR "ip nexthop bucket get " id
|
||||||
|
.I ID
|
||||||
|
.RI "index " INDEX
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.IR SELECTOR " := "
|
.IR SELECTOR " := "
|
||||||
.RB "[ " id
|
.RB "[ " id
|
||||||
|
|
@ -41,6 +49,12 @@ ip-nexthop \- nexthop object management
|
||||||
.BR groups " ] [ "
|
.BR groups " ] [ "
|
||||||
.BR fdb " ]"
|
.BR fdb " ]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR BUCKET_SELECTOR " := "
|
||||||
|
.IR SELECTOR
|
||||||
|
.RB " | [ " nhid
|
||||||
|
.IR ID " ]"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.IR NH " := { "
|
.IR NH " := { "
|
||||||
.BR blackhole " | [ "
|
.BR blackhole " | [ "
|
||||||
|
|
@ -54,7 +68,9 @@ ip-nexthop \- nexthop object management
|
||||||
.BR fdb " ] | "
|
.BR fdb " ] | "
|
||||||
.B group
|
.B group
|
||||||
.IR GROUP " [ "
|
.IR GROUP " [ "
|
||||||
.BR fdb " ] } "
|
.BR fdb " ] [ "
|
||||||
|
.B type
|
||||||
|
.IR TYPE " [ " TYPE_ARGS " ] ] }"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.IR ENCAP " := [ "
|
.IR ENCAP " := [ "
|
||||||
|
|
@ -71,6 +87,23 @@ ip-nexthop \- nexthop object management
|
||||||
.IR GROUP " := "
|
.IR GROUP " := "
|
||||||
.BR id "[," weight "[/...]"
|
.BR id "[," weight "[/...]"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TYPE " := { "
|
||||||
|
.BR mpath " | " resilient " }"
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR TYPE_ARGS " := [ "
|
||||||
|
.IR RESILIENT_ARGS " ] "
|
||||||
|
|
||||||
|
.ti -8
|
||||||
|
.IR RESILIENT_ARGS " := "
|
||||||
|
.RB "[ " buckets
|
||||||
|
.IR BUCKETS " ] [ "
|
||||||
|
.B idle_timer
|
||||||
|
.IR IDLE " ] [ "
|
||||||
|
.B unbalanced_timer
|
||||||
|
.IR UNBALANCED " ]"
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.B ip nexthop
|
.B ip nexthop
|
||||||
is used to manipulate entries in the kernel's nexthop tables.
|
is used to manipulate entries in the kernel's nexthop tables.
|
||||||
|
|
@ -122,9 +155,49 @@ is a set of encapsulation attributes specific to the
|
||||||
.in -2
|
.in -2
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI group " GROUP"
|
.BI group " GROUP [ " type " TYPE [ TYPE_ARGS ] ]"
|
||||||
create a nexthop group. Group specification is id with an optional
|
create a nexthop group. Group specification is id with an optional
|
||||||
weight (id,weight) and a '/' as a separator between entries.
|
weight (id,weight) and a '/' as a separator between entries.
|
||||||
|
.sp
|
||||||
|
.I TYPE
|
||||||
|
is a string specifying the nexthop group type. Namely:
|
||||||
|
|
||||||
|
.in +8
|
||||||
|
.BI mpath
|
||||||
|
- Multipath nexthop group backed by the hash-threshold algorithm. The
|
||||||
|
default when the type is unspecified.
|
||||||
|
.sp
|
||||||
|
.BI resilient
|
||||||
|
- Resilient nexthop group. Group is resilient to addition and deletion of
|
||||||
|
nexthops.
|
||||||
|
|
||||||
|
.sp
|
||||||
|
.in -8
|
||||||
|
.I TYPE_ARGS
|
||||||
|
is a set of attributes specific to the
|
||||||
|
.I TYPE.
|
||||||
|
|
||||||
|
.in +8
|
||||||
|
.B resilient
|
||||||
|
.in +2
|
||||||
|
.B buckets
|
||||||
|
.I BUCKETS
|
||||||
|
- Number of nexthop buckets. Cannot be changed for an existing group
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.B idle_timer
|
||||||
|
.I IDLE
|
||||||
|
- Time in seconds in which a nexthop bucket does not see traffic and is
|
||||||
|
therefore considered idle. Default is 120 seconds
|
||||||
|
|
||||||
|
.B unbalanced_timer
|
||||||
|
.I UNBALANCED
|
||||||
|
- Time in seconds in which a nexthop group is unbalanced and is therefore
|
||||||
|
considered unbalanced. The kernel will try to rebalance unbalanced groups, which
|
||||||
|
might result in some flows being reset. A value of 0 means that no
|
||||||
|
rebalancing will take place. Default is 0 seconds
|
||||||
|
.in -2
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B blackhole
|
.B blackhole
|
||||||
create a blackhole nexthop
|
create a blackhole nexthop
|
||||||
|
|
@ -171,6 +244,37 @@ as show.
|
||||||
ip nexthop get id ID
|
ip nexthop get id ID
|
||||||
get a single nexthop by id
|
get a single nexthop by id
|
||||||
|
|
||||||
|
.TP
|
||||||
|
ip nexthop bucket show
|
||||||
|
show the contents of the nexthop bucket table or the nexthop buckets
|
||||||
|
selected by some criteria.
|
||||||
|
.RS
|
||||||
|
.TP
|
||||||
|
.BI id " ID "
|
||||||
|
.in +0
|
||||||
|
show the nexthop buckets that belong to a nexthop group with a given id
|
||||||
|
.TP
|
||||||
|
.BI nhid " ID "
|
||||||
|
.in +0
|
||||||
|
show the nexthop buckets that hold a nexthop with a given id
|
||||||
|
.TP
|
||||||
|
.BI dev " DEV "
|
||||||
|
.in +0
|
||||||
|
show the nexthop buckets using the given device
|
||||||
|
.TP
|
||||||
|
.BI vrf " NAME "
|
||||||
|
.in +0
|
||||||
|
show the nexthop buckets using devices associated with the vrf name
|
||||||
|
.TP
|
||||||
|
.BI master " DEV "
|
||||||
|
.in +0
|
||||||
|
show the nexthop buckets using devices enslaved to given master device
|
||||||
|
.RE
|
||||||
|
|
||||||
|
.TP
|
||||||
|
ip nexthop bucket get id ID index INDEX
|
||||||
|
get a single nexthop bucket by nexthop group id and bucket index
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
.PP
|
.PP
|
||||||
ip nexthop ls
|
ip nexthop ls
|
||||||
|
|
@ -210,6 +314,11 @@ ip nexthop add id 7 group 5/6 fdb
|
||||||
Adds a fdb nexthop group with id 7. A fdb nexthop group can only have
|
Adds a fdb nexthop group with id 7. A fdb nexthop group can only have
|
||||||
fdb nexthops.
|
fdb nexthops.
|
||||||
.RE
|
.RE
|
||||||
|
.PP
|
||||||
|
ip nexthop add id 10 group 1/2 type resilient buckets 32
|
||||||
|
.RS 4
|
||||||
|
Add a resilient nexthop group with id 10 and 32 nexthop buckets.
|
||||||
|
.RE
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.br
|
.br
|
||||||
.BR ip (8)
|
.BR ip (8)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ ip-xfrm \- transform configuration
|
||||||
.IR MASK " ] ]"
|
.IR MASK " ] ]"
|
||||||
.RB "[ " if_id
|
.RB "[ " if_id
|
||||||
.IR IF-ID " ]"
|
.IR IF-ID " ]"
|
||||||
|
.RB "[ " tfcpad
|
||||||
|
.IR LENGTH " ]"
|
||||||
|
|
||||||
.ti -8
|
.ti -8
|
||||||
.B "ip xfrm state allocspi"
|
.B "ip xfrm state allocspi"
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ police - policing action
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.in +8
|
.in +8
|
||||||
.ti -8
|
.ti -8
|
||||||
.BR tc " ... " "action police"
|
.BR tc " ... " "action police ["
|
||||||
.BI rate " RATE " burst
|
.BI rate " RATE " burst
|
||||||
.IR BYTES [\fB/ BYTES "] ["
|
.IR BYTES [\fB/ BYTES "] ] ["
|
||||||
|
.BI pkts_rate " RATE " pkts_burst
|
||||||
|
.IR PACKETS "] ["
|
||||||
.B mtu
|
.B mtu
|
||||||
.IR BYTES [\fB/ BYTES "] ] ["
|
.IR BYTES [\fB/ BYTES "] ] ["
|
||||||
.BI peakrate " RATE"
|
.BI peakrate " RATE"
|
||||||
|
|
@ -34,19 +36,29 @@ police - policing action
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
The
|
The
|
||||||
.B police
|
.B police
|
||||||
action allows to limit bandwidth of traffic matched by the filter it is
|
action allows limiting of the byte or packet rate of traffic matched by the
|
||||||
attached to. Basically there are two different algorithms available to measure
|
filter it is attached to.
|
||||||
the packet rate: The first one uses an internal dual token bucket and is
|
.P
|
||||||
configured using the
|
There are two different algorithms available to measure the byte rate: The
|
||||||
|
first one uses an internal dual token bucket and is configured using the
|
||||||
.BR rate ", " burst ", " mtu ", " peakrate ", " overhead " and " linklayer
|
.BR rate ", " burst ", " mtu ", " peakrate ", " overhead " and " linklayer
|
||||||
parameters. The second one uses an in-kernel sampling mechanism. It can be
|
parameters. The second one uses an in-kernel sampling mechanism. It can be
|
||||||
fine-tuned using the
|
fine-tuned using the
|
||||||
.B estimator
|
.B estimator
|
||||||
filter parameter.
|
filter parameter.
|
||||||
|
.P
|
||||||
|
There is one algorithm available to measure packet rate and it is similar to
|
||||||
|
the first algorithm described for byte rate. It is configured using the
|
||||||
|
.BR pkt_rate " and " pkt_burst
|
||||||
|
parameters.
|
||||||
|
.P
|
||||||
|
At least one of the
|
||||||
|
.BR rate " and " pkt_rate "
|
||||||
|
parameters must be configured.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
.BI rate " RATE"
|
.BI rate " RATE"
|
||||||
The maximum traffic rate of packets passing this action. Those exceeding it will
|
The maximum byte rate of packets passing this action. Those exceeding it will
|
||||||
be treated as defined by the
|
be treated as defined by the
|
||||||
.B conform-exceed
|
.B conform-exceed
|
||||||
option.
|
option.
|
||||||
|
|
@ -55,6 +67,15 @@ option.
|
||||||
Set the maximum allowed burst in bytes, optionally followed by a slash ('/')
|
Set the maximum allowed burst in bytes, optionally followed by a slash ('/')
|
||||||
sign and cell size which must be a power of 2.
|
sign and cell size which must be a power of 2.
|
||||||
.TP
|
.TP
|
||||||
|
.BI pkt_rate " RATE"
|
||||||
|
The maximum packet rate or packets passing this action. Those exceeding it will
|
||||||
|
be treated as defined by the
|
||||||
|
.B conform-exceed
|
||||||
|
option.
|
||||||
|
.TP
|
||||||
|
.BI pkt_burst " PACKETS"
|
||||||
|
Set the maximum allowed burst in packets.
|
||||||
|
.TP
|
||||||
.BI mtu " BYTES\fR[\fB/\fIBYTES\fR]"
|
.BI mtu " BYTES\fR[\fB/\fIBYTES\fR]"
|
||||||
This is the maximum packet size handled by the policer (larger ones will be
|
This is the maximum packet size handled by the policer (larger ones will be
|
||||||
handled like they exceeded the configured rate). Setting this value correctly
|
handled like they exceeded the configured rate). Setting this value correctly
|
||||||
|
|
|
||||||
24
rdma/res.c
24
rdma/res.c
|
|
@ -195,30 +195,6 @@ void print_qp_type(struct rd *rd, uint32_t val)
|
||||||
qp_types_to_str(val));
|
qp_types_to_str(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_task_name(uint32_t pid)
|
|
||||||
{
|
|
||||||
char *comm;
|
|
||||||
FILE *f;
|
|
||||||
|
|
||||||
if (!pid)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
f = fopen(comm, "r");
|
|
||||||
free(comm);
|
|
||||||
if (!f)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (fscanf(f, "%ms\n", &comm) != 1)
|
|
||||||
comm = NULL;
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
return comm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_key(struct rd *rd, const char *name, uint64_t val,
|
void print_key(struct rd *rd, const char *name, uint64_t val,
|
||||||
struct nlattr *nlattr)
|
struct nlattr *nlattr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,6 @@ filters qp_valid_filters[MAX_NUMBER_OF_FILTERS] = {
|
||||||
RES_FUNC(res_qp, RDMA_NLDEV_CMD_RES_QP_GET, qp_valid_filters, false,
|
RES_FUNC(res_qp, RDMA_NLDEV_CMD_RES_QP_GET, qp_valid_filters, false,
|
||||||
RDMA_NLDEV_ATTR_RES_LQPN);
|
RDMA_NLDEV_ATTR_RES_LQPN);
|
||||||
|
|
||||||
char *get_task_name(uint32_t pid);
|
|
||||||
void print_dev(struct rd *rd, uint32_t idx, const char *name);
|
void print_dev(struct rd *rd, uint32_t idx, const char *name);
|
||||||
void print_link(struct rd *rd, uint32_t idx, const char *name, uint32_t port,
|
void print_link(struct rd *rd, uint32_t idx, const char *name, uint32_t port,
|
||||||
struct nlattr **nla_line);
|
struct nlattr **nla_line);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ struct action_util police_action_util = {
|
||||||
static void usage(void)
|
static void usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Usage: ... police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ]\n"
|
"Usage: ... police [ rate BPS burst BYTES[/BYTES] ] \n"
|
||||||
|
" [ pkts_rate RATE pkts_burst PACKETS ] [ mtu BYTES[/BYTES] ]\n"
|
||||||
" [ peakrate BPS ] [ avrate BPS ] [ overhead BYTES ]\n"
|
" [ peakrate BPS ] [ avrate BPS ] [ overhead BYTES ]\n"
|
||||||
" [ linklayer TYPE ] [ CONTROL ]\n"
|
" [ linklayer TYPE ] [ CONTROL ]\n"
|
||||||
"Where: CONTROL := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT]\n"
|
"Where: CONTROL := conform-exceed <EXCEEDACT>[/NOTEXCEEDACT]\n"
|
||||||
|
|
@ -67,6 +68,7 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
|
||||||
int Rcell_log = -1, Pcell_log = -1;
|
int Rcell_log = -1, Pcell_log = -1;
|
||||||
struct rtattr *tail;
|
struct rtattr *tail;
|
||||||
__u64 rate64 = 0, prate64 = 0;
|
__u64 rate64 = 0, prate64 = 0;
|
||||||
|
__u64 pps64 = 0, ppsburst64 = 0;
|
||||||
|
|
||||||
if (a) /* new way of doing things */
|
if (a) /* new way of doing things */
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
|
|
@ -144,6 +146,18 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
if (get_linklayer(&linklayer, *argv))
|
if (get_linklayer(&linklayer, *argv))
|
||||||
invarg("linklayer", *argv);
|
invarg("linklayer", *argv);
|
||||||
|
} else if (matches(*argv, "pkts_rate") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (pps64)
|
||||||
|
duparg("pkts_rate", *argv);
|
||||||
|
if (get_u64(&pps64, *argv, 10))
|
||||||
|
invarg("pkts_rate", *argv);
|
||||||
|
} else if (matches(*argv, "pkts_burst") == 0) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (ppsburst64)
|
||||||
|
duparg("pkts_burst", *argv);
|
||||||
|
if (get_u64(&ppsburst64, *argv, 10))
|
||||||
|
invarg("pkts_burst", *argv);
|
||||||
} else if (strcmp(*argv, "help") == 0) {
|
} else if (strcmp(*argv, "help") == 0) {
|
||||||
usage();
|
usage();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -161,8 +175,8 @@ action_ctrl_ok:
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Must at least do late binding, use TB or ewma policing */
|
/* Must at least do late binding, use TB or ewma policing */
|
||||||
if (!rate64 && !avrate && !p.index && !mtu) {
|
if (!rate64 && !avrate && !p.index && !mtu && !pps64) {
|
||||||
fprintf(stderr, "'rate' or 'avrate' or 'mtu' MUST be specified.\n");
|
fprintf(stderr, "'rate' or 'avrate' or 'mtu' or 'pkts_rate' MUST be specified.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +186,18 @@ action_ctrl_ok:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When the packets TB policer is used, pkts_burst is required */
|
||||||
|
if (pps64 && !ppsburst64) {
|
||||||
|
fprintf(stderr, "'pkts_burst' requires 'pkts_rate'.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* forbid rate and pkts_rate in same action */
|
||||||
|
if (pps64 && rate64) {
|
||||||
|
fprintf(stderr, "'rate' and 'pkts_rate' are not allowed in same action.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (prate64) {
|
if (prate64) {
|
||||||
if (!rate64) {
|
if (!rate64) {
|
||||||
fprintf(stderr, "'peakrate' requires 'rate'.\n");
|
fprintf(stderr, "'peakrate' requires 'rate'.\n");
|
||||||
|
|
@ -223,6 +249,12 @@ action_ctrl_ok:
|
||||||
if (presult)
|
if (presult)
|
||||||
addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult);
|
addattr32(n, MAX_MSG, TCA_POLICE_RESULT, presult);
|
||||||
|
|
||||||
|
if (pps64) {
|
||||||
|
addattr64(n, MAX_MSG, TCA_POLICE_PKTRATE64, pps64);
|
||||||
|
ppsburst64 = tc_calc_xmittime(pps64, ppsburst64);
|
||||||
|
addattr64(n, MAX_MSG, TCA_POLICE_PKTBURST64, ppsburst64);
|
||||||
|
}
|
||||||
|
|
||||||
addattr_nest_end(n, tail);
|
addattr_nest_end(n, tail);
|
||||||
res = 0;
|
res = 0;
|
||||||
|
|
||||||
|
|
@ -244,6 +276,7 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
||||||
unsigned int buffer;
|
unsigned int buffer;
|
||||||
unsigned int linklayer;
|
unsigned int linklayer;
|
||||||
__u64 rate64, prate64;
|
__u64 rate64, prate64;
|
||||||
|
__u64 pps64, ppsburst64;
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -287,6 +320,17 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
|
||||||
tc_print_rate(PRINT_FP, NULL, "avrate %s ",
|
tc_print_rate(PRINT_FP, NULL, "avrate %s ",
|
||||||
rta_getattr_u32(tb[TCA_POLICE_AVRATE]));
|
rta_getattr_u32(tb[TCA_POLICE_AVRATE]));
|
||||||
|
|
||||||
|
if ((tb[TCA_POLICE_PKTRATE64] &&
|
||||||
|
RTA_PAYLOAD(tb[TCA_POLICE_PKTRATE64]) >= sizeof(pps64)) &&
|
||||||
|
(tb[TCA_POLICE_PKTBURST64] &&
|
||||||
|
RTA_PAYLOAD(tb[TCA_POLICE_PKTBURST64]) >= sizeof(ppsburst64))) {
|
||||||
|
pps64 = rta_getattr_u64(tb[TCA_POLICE_PKTRATE64]);
|
||||||
|
ppsburst64 = rta_getattr_u64(tb[TCA_POLICE_PKTBURST64]);
|
||||||
|
ppsburst64 = tc_calc_xmitsize(pps64, ppsburst64);
|
||||||
|
fprintf(f, "pkts_rate %llu ", pps64);
|
||||||
|
fprintf(f, "pkts_burst %llu ", ppsburst64);
|
||||||
|
}
|
||||||
|
|
||||||
print_action_control(f, "action ", p->action, "");
|
print_action_control(f, "action ", p->action, "");
|
||||||
|
|
||||||
if (tb[TCA_POLICE_RESULT]) {
|
if (tb[TCA_POLICE_RESULT]) {
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,6 @@
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <linux/if.h>
|
#include <linux/if.h>
|
||||||
|
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -101,11 +98,11 @@ static int get_netid_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
|
||||||
static int generate_multicast(short af, char *buf, int bufsize)
|
static int generate_multicast(short af, char *buf, int bufsize)
|
||||||
{
|
{
|
||||||
int netid;
|
|
||||||
char mnl_msg[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
|
int netid;
|
||||||
|
|
||||||
if (!(nlh = msg_init(mnl_msg, TIPC_NL_NET_GET))) {
|
nlh = msg_init(TIPC_NL_NET_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialization failed\n");
|
fprintf(stderr, "error, message initialization failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -399,7 +396,6 @@ static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
char *media;
|
char *media;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
|
|
@ -435,7 +431,8 @@ static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_ADD))) {
|
nlh = msg_init(TIPC_NL_BEARER_ADD);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -482,7 +479,6 @@ static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
int err;
|
int err;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
{ "device", OPT_KEYVAL, NULL },
|
{ "device", OPT_KEYVAL, NULL },
|
||||||
{ "domain", OPT_KEYVAL, NULL },
|
{ "domain", OPT_KEYVAL, NULL },
|
||||||
|
|
@ -508,7 +504,8 @@ static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_ENABLE))) {
|
nlh = msg_init(TIPC_NL_BEARER_ENABLE);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error: message initialisation failed\n");
|
fprintf(stderr, "error: message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -563,7 +560,6 @@ static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
{ "device", OPT_KEYVAL, NULL },
|
{ "device", OPT_KEYVAL, NULL },
|
||||||
|
|
@ -584,7 +580,8 @@ static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_DISABLE))) {
|
nlh = msg_init(TIPC_NL_BEARER_DISABLE);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -628,7 +625,6 @@ static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
int err;
|
int err;
|
||||||
int val;
|
int val;
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *props;
|
struct nlattr *props;
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
|
|
@ -675,7 +671,8 @@ static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_SET))) {
|
nlh = msg_init(TIPC_NL_BEARER_SET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -876,7 +873,6 @@ static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
char *media;
|
char *media;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct cb_data cb_data = {0};
|
struct cb_data cb_data = {0};
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
|
|
@ -918,7 +914,8 @@ static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) {
|
nlh = msg_init(TIPC_NL_BEARER_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -956,7 +953,6 @@ static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
{ "device", OPT_KEYVAL, NULL },
|
{ "device", OPT_KEYVAL, NULL },
|
||||||
|
|
@ -1001,7 +997,8 @@ static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) {
|
nlh = msg_init(TIPC_NL_BEARER_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -1056,14 +1053,13 @@ static int bearer_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_bearer_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_bearer_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
fprintf(stderr, "Usage: %s bearer list\n", cmdl->argv[0]);
|
fprintf(stderr, "Usage: %s bearer list\n", cmdl->argv[0]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_BEARER_GET))) {
|
nlh = msg_init(TIPC_NL_BEARER_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
|
|
||||||
static const struct cmd *find_cmd(const struct cmd *cmds, char *str)
|
static const struct cmd *find_cmd(const struct cmd *cmds, char *str)
|
||||||
|
|
|
||||||
37
tipc/link.c
37
tipc/link.c
|
|
@ -17,7 +17,6 @@
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc.h>
|
#include <linux/tipc.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -60,7 +59,6 @@ static int link_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -68,7 +66,7 @@ static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_GET);
|
nlh = msg_init(TIPC_NL_LINK_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -126,7 +124,6 @@ static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
|
|
@ -151,7 +148,7 @@ static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (parse_opts(opts, cmdl) < 0)
|
if (parse_opts(opts, cmdl) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_GET);
|
nlh = msg_init(TIPC_NL_LINK_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -241,7 +238,6 @@ static int cmd_link_get_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int prop = TIPC_NLA_PROP_BROADCAST;
|
int prop = TIPC_NLA_PROP_BROADCAST;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -249,7 +245,7 @@ static int cmd_link_get_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_GET);
|
nlh = msg_init(TIPC_NL_LINK_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -284,7 +280,6 @@ static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char *link;
|
char *link;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
|
|
@ -302,7 +297,7 @@ static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS);
|
nlh = msg_init(TIPC_NL_LINK_RESET_STATS);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -550,7 +545,6 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char *link = NULL;
|
char *link = NULL;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
{ "link", OPT_KEYVAL, NULL },
|
{ "link", OPT_KEYVAL, NULL },
|
||||||
|
|
@ -564,7 +558,7 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_GET);
|
nlh = msg_init(TIPC_NL_LINK_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -626,7 +620,6 @@ static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{
|
{
|
||||||
int val;
|
int val;
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *props;
|
struct nlattr *props;
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
|
|
@ -658,7 +651,7 @@ static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (parse_opts(opts, cmdl) < 0)
|
if (parse_opts(opts, cmdl) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_SET);
|
nlh = msg_init(TIPC_NL_LINK_SET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -704,7 +697,6 @@ static void cmd_link_set_bcast_help(struct cmdl *cmdl)
|
||||||
static int cmd_link_set_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_link_set_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *props;
|
struct nlattr *props;
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
|
|
@ -734,7 +726,7 @@ static int cmd_link_set_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_LINK_SET);
|
nlh = msg_init(TIPC_NL_LINK_SET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -788,7 +780,6 @@ static int cmd_link_mon_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int size;
|
int size;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
|
|
||||||
if (cmdl->argc != cmdl->optind + 1) {
|
if (cmdl->argc != cmdl->optind + 1) {
|
||||||
|
|
@ -797,7 +788,7 @@ static int cmd_link_mon_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
}
|
}
|
||||||
size = atoi(shift_cmdl(cmdl));
|
size = atoi(shift_cmdl(cmdl));
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_MON_SET);
|
nlh = msg_init(TIPC_NL_MON_SET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -841,7 +832,6 @@ static int link_mon_summary_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -849,7 +839,7 @@ static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_MON_GET);
|
nlh = msg_init(TIPC_NL_MON_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1004,11 +994,10 @@ exit:
|
||||||
static int link_mon_peer_list(uint32_t mon_ref)
|
static int link_mon_peer_list(uint32_t mon_ref)
|
||||||
{
|
{
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_MON_PEER_GET);
|
nlh = msg_init(TIPC_NL_MON_PEER_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1080,7 +1069,6 @@ static void cmd_link_mon_list_udp_help(struct cmdl *cmdl, char *media)
|
||||||
static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
char bname[TIPC_MAX_BEARER_NAME] = {0};
|
char bname[TIPC_MAX_BEARER_NAME] = {0};
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
{ "media", OPT_KEYVAL, NULL },
|
{ "media", OPT_KEYVAL, NULL },
|
||||||
|
|
@ -1112,7 +1100,7 @@ static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_MON_GET);
|
nlh = msg_init(TIPC_NL_MON_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -1176,9 +1164,8 @@ static int link_mon_get_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_link_mon_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_link_mon_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_MON_GET);
|
nlh = msg_init(TIPC_NL_MON_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
15
tipc/media.c
15
tipc/media.c
|
|
@ -15,9 +15,7 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc.h>
|
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -45,14 +43,13 @@ static int media_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_media_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_media_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
fprintf(stderr, "Usage: %s media list\n", cmdl->argv[0]);
|
fprintf(stderr, "Usage: %s media list\n", cmdl->argv[0]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_GET))) {
|
nlh = msg_init(TIPC_NL_MEDIA_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +86,6 @@ static int cmd_media_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
struct opt opts[] = {
|
struct opt opts[] = {
|
||||||
|
|
@ -116,7 +112,8 @@ static int cmd_media_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (parse_opts(opts, cmdl) < 0)
|
if (parse_opts(opts, cmdl) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_GET))) {
|
nlh = msg_init(TIPC_NL_MEDIA_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +176,6 @@ static int cmd_media_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
{
|
{
|
||||||
int val;
|
int val;
|
||||||
int prop;
|
int prop;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *props;
|
struct nlattr *props;
|
||||||
struct nlattr *attrs;
|
struct nlattr *attrs;
|
||||||
struct opt *opt;
|
struct opt *opt;
|
||||||
|
|
@ -213,7 +209,8 @@ static int cmd_media_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (parse_opts(opts, cmdl) < 0)
|
if (parse_opts(opts, cmdl) < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_MEDIA_SET))) {
|
nlh = msg_init(TIPC_NL_MEDIA_SET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
132
tipc/msg.c
132
tipc/msg.c
|
|
@ -13,13 +13,13 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <linux/tipc_netlink.h>
|
|
||||||
#include <linux/tipc.h>
|
|
||||||
#include <linux/genetlink.h>
|
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
|
|
||||||
|
#include "mnl_utils.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
||||||
|
extern struct mnlu_gen_socket tipc_nlg;
|
||||||
|
|
||||||
int parse_attrs(const struct nlattr *attr, void *data)
|
int parse_attrs(const struct nlattr *attr, void *data)
|
||||||
{
|
{
|
||||||
const struct nlattr **tb = data;
|
const struct nlattr **tb = data;
|
||||||
|
|
@ -30,141 +30,23 @@ int parse_attrs(const struct nlattr *attr, void *data)
|
||||||
return MNL_CB_OK;
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int family_id_cb(const struct nlmsghdr *nlh, void *data)
|
|
||||||
{
|
|
||||||
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
|
|
||||||
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
|
|
||||||
int *id = data;
|
|
||||||
|
|
||||||
mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, tb);
|
|
||||||
if (!tb[CTRL_ATTR_FAMILY_ID])
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
|
|
||||||
*id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
|
|
||||||
|
|
||||||
return MNL_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mnl_socket *msg_send(struct nlmsghdr *nlh)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct mnl_socket *nl;
|
|
||||||
|
|
||||||
nl = mnl_socket_open(NETLINK_GENERIC);
|
|
||||||
if (nl == NULL) {
|
|
||||||
perror("mnl_socket_open");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID);
|
|
||||||
if (ret < 0) {
|
|
||||||
perror("mnl_socket_bind");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
|
|
||||||
if (ret < 0) {
|
|
||||||
perror("mnl_socket_send");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int msg_recv(struct mnl_socket *nl, mnl_cb_t callback, void *data, int seq)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
unsigned int portid;
|
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
portid = mnl_socket_get_portid(nl);
|
|
||||||
|
|
||||||
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
|
||||||
while (ret > 0) {
|
|
||||||
ret = mnl_cb_run(buf, ret, seq, portid, callback, data);
|
|
||||||
if (ret <= 0)
|
|
||||||
break;
|
|
||||||
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
if (ret == -1)
|
|
||||||
perror("error");
|
|
||||||
|
|
||||||
mnl_socket_close(nl);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int msg_query(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
|
|
||||||
{
|
|
||||||
unsigned int seq;
|
|
||||||
struct mnl_socket *nl;
|
|
||||||
|
|
||||||
seq = time(NULL);
|
|
||||||
nlh->nlmsg_seq = seq;
|
|
||||||
|
|
||||||
nl = msg_send(nlh);
|
|
||||||
if (!nl)
|
|
||||||
return -ENOTSUP;
|
|
||||||
|
|
||||||
return msg_recv(nl, callback, data, seq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_family(void)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
int nl_family;
|
|
||||||
struct nlmsghdr *nlh;
|
|
||||||
struct genlmsghdr *genl;
|
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
nlh = mnl_nlmsg_put_header(buf);
|
|
||||||
nlh->nlmsg_type = GENL_ID_CTRL;
|
|
||||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
|
||||||
|
|
||||||
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
|
|
||||||
genl->cmd = CTRL_CMD_GETFAMILY;
|
|
||||||
genl->version = 1;
|
|
||||||
|
|
||||||
mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
|
|
||||||
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TIPC_GENL_V2_NAME);
|
|
||||||
|
|
||||||
if ((err = msg_query(nlh, family_id_cb, &nl_family)))
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return nl_family;
|
|
||||||
}
|
|
||||||
|
|
||||||
int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
|
int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
|
||||||
{
|
{
|
||||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||||
return msg_query(nlh, callback, data);
|
return mnlu_gen_socket_sndrcv(&tipc_nlg, nlh, callback, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
|
int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data)
|
||||||
{
|
{
|
||||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||||
return msg_query(nlh, callback, data);
|
return mnlu_gen_socket_sndrcv(&tipc_nlg, nlh, callback, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct nlmsghdr *msg_init(char *buf, int cmd)
|
struct nlmsghdr *msg_init(int cmd)
|
||||||
{
|
{
|
||||||
int family;
|
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
struct genlmsghdr *genl;
|
|
||||||
|
|
||||||
family = get_family();
|
nlh = mnlu_gen_socket_cmd_prepare(&tipc_nlg, cmd, 0);
|
||||||
if (family <= 0) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Unable to get TIPC nl family id (module loaded?)\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
nlh = mnl_nlmsg_put_header(buf);
|
|
||||||
nlh->nlmsg_type = family;
|
|
||||||
|
|
||||||
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
|
|
||||||
genl->cmd = cmd;
|
|
||||||
genl->version = 1;
|
|
||||||
|
|
||||||
return nlh;
|
return nlh;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
#ifndef _TIPC_MSG_H
|
#ifndef _TIPC_MSG_H
|
||||||
#define _TIPC_MSG_H
|
#define _TIPC_MSG_H
|
||||||
|
|
||||||
struct nlmsghdr *msg_init(char *buf, int cmd);
|
struct nlmsghdr *msg_init(int cmd);
|
||||||
int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
|
int msg_doit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
|
||||||
int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
|
int msg_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
|
||||||
int parse_attrs(const struct nlattr *attr, void *data);
|
int parse_attrs(const struct nlattr *attr, void *data);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc.h>
|
#include <linux/tipc.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -82,7 +81,6 @@ static int cmd_nametable_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int iteration = 0;
|
int iteration = 0;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -90,7 +88,8 @@ static int cmd_nametable_show(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_NAME_TABLE_GET))) {
|
nlh = msg_init(TIPC_NL_NAME_TABLE_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
tipc/node.c
33
tipc/node.c
|
|
@ -17,7 +17,6 @@
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc.h>
|
#include <linux/tipc.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -52,14 +51,13 @@ static int node_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_node_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_node_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
fprintf(stderr, "Usage: %s node list\n", cmdl->argv[0]);
|
fprintf(stderr, "Usage: %s node list\n", cmdl->argv[0]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_NODE_GET))) {
|
nlh = msg_init(TIPC_NL_NODE_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +71,6 @@ static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
char *str;
|
char *str;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (cmdl->argc != cmdl->optind + 1) {
|
if (cmdl->argc != cmdl->optind + 1) {
|
||||||
fprintf(stderr, "Usage: %s node set address ADDRESS\n",
|
fprintf(stderr, "Usage: %s node set address ADDRESS\n",
|
||||||
|
|
@ -86,7 +83,8 @@ static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) {
|
nlh = msg_init(TIPC_NL_NET_SET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +124,6 @@ static int cmd_node_get_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
static int cmd_node_set_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_node_set_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
uint8_t id[16] = {0,};
|
uint8_t id[16] = {0,};
|
||||||
uint64_t *w0 = (uint64_t *) &id[0];
|
uint64_t *w0 = (uint64_t *) &id[0];
|
||||||
uint64_t *w1 = (uint64_t *) &id[8];
|
uint64_t *w1 = (uint64_t *) &id[8];
|
||||||
|
|
@ -145,7 +142,7 @@ static int cmd_node_set_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_NET_SET);
|
nlh = msg_init(TIPC_NL_NET_SET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -201,7 +198,6 @@ static int cmd_node_set_key(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
};
|
};
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
struct opt *opt_algname, *opt_nodeid, *opt_master, *opt_rekeying;
|
struct opt *opt_algname, *opt_nodeid, *opt_master, *opt_rekeying;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
uint8_t id[TIPC_NODEID_LEN] = {0,};
|
uint8_t id[TIPC_NODEID_LEN] = {0,};
|
||||||
uint32_t rekeying = 0;
|
uint32_t rekeying = 0;
|
||||||
bool has_key = false;
|
bool has_key = false;
|
||||||
|
|
@ -262,7 +258,7 @@ get_ops:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init & do the command */
|
/* Init & do the command */
|
||||||
nlh = msg_init(buf, TIPC_NL_KEY_SET);
|
nlh = msg_init(TIPC_NL_KEY_SET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -287,15 +283,13 @@ get_ops:
|
||||||
static int cmd_node_flush_key(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_node_flush_key(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
(cmd->help)(cmdl);
|
(cmd->help)(cmdl);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init & do the command */
|
/* Init & do the command */
|
||||||
nlh = msg_init(buf, TIPC_NL_KEY_FLUSH);
|
nlh = msg_init(TIPC_NL_KEY_FLUSH);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -332,14 +326,12 @@ static int nodeid_get_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_node_get_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_node_get_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
(cmd->help)(cmdl);
|
(cmd->help)(cmdl);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_NET_GET);
|
nlh = msg_init(TIPC_NL_NET_GET);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -370,14 +362,13 @@ static int netid_get_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_node_get_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_node_get_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
(cmd->help)(cmdl);
|
(cmd->help)(cmdl);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_NET_GET))) {
|
nlh = msg_init(TIPC_NL_NET_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -389,7 +380,6 @@ static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
int netid;
|
int netid;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
|
|
@ -397,7 +387,8 @@ static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) {
|
nlh = msg_init(TIPC_NL_NET_SET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/tipc.h>
|
#include <linux/tipc.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -30,7 +29,6 @@ static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
char *str;
|
char *str;
|
||||||
uint32_t addr;
|
uint32_t addr;
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if ((cmdl->argc != cmdl->optind + 1) || help_flag) {
|
if ((cmdl->argc != cmdl->optind + 1) || help_flag) {
|
||||||
fprintf(stderr, "Usage: %s peer remove address ADDRESS\n",
|
fprintf(stderr, "Usage: %s peer remove address ADDRESS\n",
|
||||||
|
|
@ -47,7 +45,8 @@ static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
if (!addr)
|
if (!addr)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_PEER_REMOVE))) {
|
nlh = msg_init(TIPC_NL_PEER_REMOVE);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +61,6 @@ static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
static int cmd_peer_rm_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_peer_rm_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
__u8 id[16] = {0,};
|
__u8 id[16] = {0,};
|
||||||
__u64 *w0 = (__u64 *)&id[0];
|
__u64 *w0 = (__u64 *)&id[0];
|
||||||
__u64 *w1 = (__u64 *)&id[8];
|
__u64 *w1 = (__u64 *)&id[8];
|
||||||
|
|
@ -81,7 +79,7 @@ static int cmd_peer_rm_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlh = msg_init(buf, TIPC_NL_PEER_REMOVE);
|
nlh = msg_init(TIPC_NL_PEER_REMOVE);
|
||||||
if (!nlh) {
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
#include <linux/tipc.h>
|
#include <linux/tipc.h>
|
||||||
#include <linux/tipc_netlink.h>
|
#include <linux/tipc_netlink.h>
|
||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <libmnl/libmnl.h>
|
|
||||||
|
|
||||||
#include "cmdl.h"
|
#include "cmdl.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
@ -46,10 +45,10 @@ static int publ_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int publ_list(uint32_t sock)
|
static int publ_list(uint32_t sock)
|
||||||
{
|
{
|
||||||
struct nlmsghdr *nlh;
|
struct nlmsghdr *nlh;
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
struct nlattr *nest;
|
struct nlattr *nest;
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_PUBL_GET))) {
|
nlh = msg_init(TIPC_NL_PUBL_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -103,14 +102,13 @@ static int sock_list_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
static int cmd_socket_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
static int cmd_socket_list(struct nlmsghdr *nlh, const struct cmd *cmd,
|
||||||
struct cmdl *cmdl, void *data)
|
struct cmdl *cmdl, void *data)
|
||||||
{
|
{
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
|
|
||||||
if (help_flag) {
|
if (help_flag) {
|
||||||
fprintf(stderr, "Usage: %s socket list\n", cmdl->argv[0]);
|
fprintf(stderr, "Usage: %s socket list\n", cmdl->argv[0]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(nlh = msg_init(buf, TIPC_NL_SOCK_GET))) {
|
nlh = msg_init(TIPC_NL_SOCK_GET);
|
||||||
|
if (!nlh) {
|
||||||
fprintf(stderr, "error, message initialisation failed\n");
|
fprintf(stderr, "error, message initialisation failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
tipc/tipc.c
21
tipc/tipc.c
|
|
@ -13,7 +13,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <linux/tipc_netlink.h>
|
||||||
|
#include <libmnl/libmnl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "mnl_utils.h"
|
||||||
#include "bearer.h"
|
#include "bearer.h"
|
||||||
#include "link.h"
|
#include "link.h"
|
||||||
#include "nametable.h"
|
#include "nametable.h"
|
||||||
|
|
@ -26,6 +30,7 @@
|
||||||
|
|
||||||
int help_flag;
|
int help_flag;
|
||||||
int json;
|
int json;
|
||||||
|
struct mnlu_gen_socket tipc_nlg;
|
||||||
|
|
||||||
static void about(struct cmdl *cmdl)
|
static void about(struct cmdl *cmdl)
|
||||||
{
|
{
|
||||||
|
|
@ -110,8 +115,20 @@ int main(int argc, char *argv[])
|
||||||
cmdl.argc = argc;
|
cmdl.argc = argc;
|
||||||
cmdl.argv = argv;
|
cmdl.argv = argv;
|
||||||
|
|
||||||
if ((res = run_cmd(NULL, &cmd, cmds, &cmdl, NULL)) != 0)
|
res = mnlu_gen_socket_open(&tipc_nlg, TIPC_GENL_V2_NAME,
|
||||||
return 1;
|
TIPC_GENL_V2_VERSION);
|
||||||
|
if (res) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Unable to get TIPC nl family id (module loaded?)\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = run_cmd(NULL, &cmd, cmds, &cmdl, &tipc_nlg);
|
||||||
|
if (res != 0) {
|
||||||
|
mnlu_gen_socket_close(&tipc_nlg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnlu_gen_socket_close(&tipc_nlg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue