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:
Stephen Hemminger 2021-04-27 19:39:39 -07:00
commit 2363bc99f9
53 changed files with 2469 additions and 665 deletions

View File

@ -10,6 +10,9 @@ void print_vlan_info(struct rtattr *tb, int ifindex);
int print_linkinfo(struct nlmsghdr *n, void *arg);
int print_mdb_mon(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_mdb(int argc, char **argv);

View File

@ -19,7 +19,7 @@
static unsigned int filter_index;
static const char *port_states[] = {
static const char *stp_states[] = {
[BR_STATE_DISABLED] = "disabled",
[BR_STATE_LISTENING] = "listening",
[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, "> ");
}
static void print_portstate(__u8 state)
void print_stp_state(__u8 state)
{
if (state <= BR_STATE_BLOCKING)
print_string(PRINT_ANY, "state",
"state %s ", port_states[state]);
"state %s ", stp_states[state]);
else
print_uint(PRINT_ANY, "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)
{
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);
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])
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 ",
rta_getattr_u8(prtb[IFLA_BRPORT_ISOLATED]));
} 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) {
NEXT_ARG();
char *endptr;
size_t nstates = ARRAY_SIZE(port_states);
state = strtol(*argv, &endptr, 10);
if (!(**argv != '\0' && *endptr == '\0')) {
for (state = 0; state < nstates; state++)
if (strcasecmp(port_states[state], *argv) == 0)
break;
if (state == nstates) {
state = parse_stp_state(*argv);
if (state == -1) {
fprintf(stderr,
"Error: invalid STP port state\n");
return -1;

View File

@ -16,9 +16,9 @@
#include <arpa/inet.h>
#include "libnetlink.h"
#include "utils.h"
#include "br_common.h"
#include "rt_names.h"
#include "utils.h"
#include "json_print.h"
#ifndef MDBA_RTA

View File

@ -31,7 +31,7 @@ static int prefix_banner;
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);
}
@ -67,6 +67,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
print_nlmsg_timestamp(fp, n);
return 0;
case RTM_NEWVLAN:
case RTM_DELVLAN:
if (prefix_banner)
fprintf(fp, "[VLAN]");
return print_vlan_rtm(n, arg, true);
default:
return 0;
}
@ -79,6 +85,7 @@ int do_monitor(int argc, char **argv)
int llink = 0;
int lneigh = 0;
int lmdb = 0;
int lvlan = 0;
rtnl_close(&rth);
@ -95,8 +102,12 @@ int do_monitor(int argc, char **argv)
} else if (matches(*argv, "mdb") == 0) {
lmdb = 1;
groups = 0;
} else if (matches(*argv, "vlan") == 0) {
lvlan = 1;
groups = 0;
} else if (strcmp(*argv, "all") == 0) {
groups = ~RTMGRP_TC;
lvlan = 1;
prefix_banner = 1;
} else if (matches(*argv, "help") == 0) {
usage();
@ -134,6 +145,12 @@ int do_monitor(int argc, char **argv)
if (rtnl_open(&rth, groups) < 0)
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);
if (rtnl_listen(&rth, accept_msg, stdout) < 0)

View File

@ -16,6 +16,7 @@
#include "utils.h"
static unsigned int filter_index, filter_vlan;
static int vlan_rtm_cur_ifidx = -1;
enum vlan_show_subject {
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"
" [ pvid ] [ untagged ]\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 { tunnelshow } [ dev DEV ] [ vid VLAN_ID ]\n");
exit(-1);
@ -241,6 +243,100 @@ static int vlan_modify(int cmd, int argc, char **argv)
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
* we need to make it a tristate:
* 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);
}
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_lluint(PRINT_ANY, "rx_bytes", "RX: %llu bytes",
vstats->rx_bytes);
@ -441,6 +531,16 @@ static void print_one_vlan_stats(const struct bridge_vlan_xstats *vstats)
vstats->tx_bytes);
print_lluint(PRINT_ANY, "tx_packets", " %llu packets\n",
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();
}
@ -521,6 +621,115 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg)
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)
{
char *filter_dev = NULL;
@ -549,6 +758,34 @@ static int vlan_show(int argc, char **argv, int subject)
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 (rtnl_linkdump_req_filter(&rth, PF_BRIDGE,
(compress_vlans ?
@ -602,6 +839,7 @@ static int vlan_show(int argc, char **argv, int subject)
}
}
out:
delete_json_obj();
fflush(stdout);
return 0;
@ -667,6 +905,8 @@ int do_vlan(int argc, char **argv)
if (matches(*argv, "tunnelshow") == 0) {
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)
usage();
} else {

File diff suppressed because it is too large Load Diff

View File

@ -30,38 +30,11 @@ struct mnlg_socket {
unsigned int seq;
};
static struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
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)
int mnlg_socket_send(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh)
{
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 {
bool found;
uint32_t id;
@ -141,15 +114,17 @@ static int get_group_id_cb(const struct nlmsghdr *nlh, void *data)
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 group_info group_info;
int err;
nlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);
mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->id);
nlh = _mnlu_gen_socket_cmd_prepare(nlg, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST | NLM_F_ACK,
GENL_ID_CTRL, 1);
mnl_attr_put_u16(nlh, CTRL_ATTR_FAMILY_ID, nlg->family);
err = mnlg_socket_send(nlg, nlh);
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.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)
return err;
@ -174,85 +149,7 @@ int mnlg_socket_group_add(struct mnlg_socket *nlg, const char *group_name)
return 0;
}
static int get_family_id_attr_cb(const struct nlattr *attr, void *data)
{
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)
int mnlg_socket_get_fd(struct mnlu_gen_socket *nlg)
{
return mnl_socket_get_fd(nlg->nl);
}

View File

@ -14,15 +14,10 @@
#include <libmnl/libmnl.h>
struct mnlg_socket;
struct mnlu_gen_socket;
struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,
uint16_t flags);
int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh);
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);
int mnlg_socket_send(struct mnlu_gen_socket *nlg, const struct nlmsghdr *nlh);
int mnlg_socket_group_add(struct mnlu_gen_socket *nlg, const char *group_name);
int mnlg_socket_get_fd(struct mnlu_gen_socket *nlg);
#endif /* _MNLG_H_ */

View File

@ -17,6 +17,7 @@
16 dhcp
18 keepalived
42 babel
99 openr
186 bgp
187 isis
188 ospf

View File

@ -81,6 +81,7 @@ _PRINT_FUNC(0xhex, unsigned long long)
_PRINT_FUNC(luint, unsigned long)
_PRINT_FUNC(lluint, unsigned long long)
_PRINT_FUNC(float, double)
_PRINT_FUNC(tv, const struct timeval *)
#undef _PRINT_FUNC
#define _PRINT_NAME_VALUE_FUNC(type_name, type, format_char) \

View File

@ -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_init_handle(struct rtnl_handle *grth, const char *family,
int *genl_family);

View File

@ -69,6 +69,8 @@ int rtnl_neightbldump_req(struct rtnl_handle *rth, int family)
__attribute__((warn_unused_result));
int rtnl_mdbdump_req(struct rtnl_handle *rth, int family)
__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)
__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,
req_filter_fn_t filter_fn)
__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 {
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))))
#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
* messages from dump file */
#define NLMSG_TSTAMP 15

View File

@ -13,6 +13,10 @@ struct mnlu_gen_socket {
int mnlu_gen_socket_open(struct mnlu_gen_socket *nlg, const char *family_name,
uint8_t version);
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,
uint8_t cmd, uint16_t flags);
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);
int mnlu_socket_recv_run(struct mnl_socket *nl, unsigned int seq, void *buf, size_t buf_size,
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__ */

View File

@ -93,7 +93,717 @@ union bpf_iter_link_info {
} 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 {
BPF_MAP_CREATE,
BPF_MAP_LOOKUP_ELEM,
@ -247,6 +957,7 @@ enum bpf_attach_type {
BPF_XDP_CPUMAP,
BPF_SK_LOOKUP,
BPF_XDP,
BPF_SK_SKB_VERDICT,
__MAX_BPF_ATTACH_TYPE
};
@ -393,11 +1104,24 @@ enum bpf_link_type {
* is struct/union.
*/
#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
* offset to another bpf function
*/
#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 */
enum {
@ -720,7 +1444,7 @@ union bpf_attr {
* parsed and used to produce a manual page. The workflow is the following,
* and requires the rst2man utility:
*
* $ ./scripts/bpf_helpers_doc.py \
* $ ./scripts/bpf_doc.py \
* --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
* $ rst2man /tmp/bpf-helpers.rst > /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
* 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
* packet buffer. Therefore, at load time, all checks on pointers
* 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_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) \
FN(unspec), \
@ -4081,6 +4837,7 @@ union bpf_attr {
FN(ima_inode_hash), \
FN(sock_from_file), \
FN(check_mtu), \
FN(for_each_map_elem), \
/* */
/* 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_UDP = (1ULL << 4),
BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5),
BPF_F_ADJ_ROOM_ENCAP_L2_ETH = (1ULL << 6),
};
enum {
@ -5211,7 +5969,10 @@ struct bpf_pidns_info {
/* User accessible data for SK_LOOKUP programs. Add new fields at the end. */
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 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */

View File

@ -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_KFLAG(info) ((info) >> 31)
@ -72,7 +72,8 @@ struct btf_type {
#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
#define BTF_KIND_VAR 14 /* Variable */
#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)
/* For some specific BTF_KIND, "struct btf_type" is immediately

View File

@ -140,6 +140,9 @@ struct icmp6hdr {
#define ICMPV6_UNK_OPTION 2
#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
*/

View File

@ -174,10 +174,21 @@ enum mptcp_event_attr {
MPTCP_ATTR_FLAGS, /* u16 */
MPTCP_ATTR_TIMEOUT, /* u32 */
MPTCP_ATTR_IF_IDX, /* s32 */
MPTCP_ATTR_RESET_REASON,/* u32 */
MPTCP_ATTR_RESET_FLAGS, /* u32 */
__MPTCP_ATTR_AFTER_LAST
};
#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 */

View File

@ -21,7 +21,10 @@ struct nexthop_grp {
};
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,
};
@ -52,8 +55,50 @@ enum {
NHA_FDB, /* flag; nexthop belongs to a bridge fdb */
/* 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,
};
#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

View File

@ -190,6 +190,8 @@ enum {
TCA_POLICE_PAD,
TCA_POLICE_RATE64,
TCA_POLICE_PEAKRATE64,
TCA_POLICE_PKTRATE64,
TCA_POLICE_PKTBURST64,
__TCA_POLICE_MAX
#define TCA_POLICE_RESULT TCA_POLICE_RESULT
};

View File

@ -178,6 +178,13 @@ enum {
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,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@ -283,6 +290,7 @@ enum {
#define RTPROT_MROUTED 17 /* Multicast daemon */
#define RTPROT_KEEPALIVED 18 /* Keepalived daemon */
#define RTPROT_BABEL 42 /* Babel daemon */
#define RTPROT_OPENR 99 /* Open Routing (Open/R) Routes */
#define RTPROT_BGP 186 /* BGP Routes */
#define RTPROT_ISIS 187 /* ISIS Routes */
#define RTPROT_OSPF 188 /* OSPF Routes */

View File

@ -308,6 +308,7 @@ char *find_cgroup2_mount(bool do_mount);
__u64 get_cgroup2_id(const char *path);
char *get_cgroup2_path(__u64 id, bool full);
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,
struct rtattr *tb[]);

16
ip/ip.c
View File

@ -125,7 +125,7 @@ static const struct cmd {
{ 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;
@ -134,7 +134,8 @@ static int do_cmd(const char *argv0, int argc, char **argv)
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;
}
@ -143,7 +144,7 @@ static int ip_batch_cmd(int argc, char *argv[], void *data)
const int *orig_family = data;
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)
@ -313,11 +314,14 @@ int main(int argc, char **argv)
rtnl_set_strict_dump(&rth);
if (strlen(basename) > 2)
return do_cmd(basename+2, argc, argv);
if (strlen(basename) > 2) {
int ret = do_cmd(basename+2, argc, argv, false);
if (ret != EXIT_FAILURE)
return ret;
}
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);
usage();

View File

@ -54,6 +54,7 @@ int print_rule(struct nlmsghdr *n, void *arg);
int print_netconf(struct rtnl_ctrl_data *ctrl,
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_nsid_socket_init(void);
int print_nsid(struct nlmsghdr *n, void *arg);

View File

@ -91,6 +91,12 @@ static int accept_msg(struct rtnl_ctrl_data *ctrl,
print_nexthop(n, arg);
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_DELLINK:
ll_remember_index(n, NULL);

View File

@ -17,12 +17,13 @@ static void usage(void)
{
fprintf(stderr,
"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 show [ id ID ]\n"
" ip mptcp endpoint flush\n"
" ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
" ip mptcp limits show\n"
" ip mptcp monitor\n"
"FLAG-LIST := [ FLAG-LIST ] FLAG\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;
__u32 index = 0;
__u32 flags = 0;
__u16 port = 0;
__u8 id = 0;
ll_init_map(&rth);
@ -123,6 +125,10 @@ static int mptcp_parse_opt(int argc, char **argv, struct nlmsghdr *n,
if (!index)
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) {
addr_set = true;
} 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);
if (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) {
int type;
@ -181,8 +189,8 @@ static int print_mptcp_addrinfo(struct rtattr *addrinfo)
__u8 family = AF_UNSPEC, addr_attr_type;
const char *ifname;
unsigned int flags;
__u16 id, port;
int index;
__u16 id;
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 ",
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]) {
id = rta_getattr_u8(tb[MPTCP_PM_ADDR_ATTR_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;
}
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)
{
if (argc == 0)
@ -429,6 +546,14 @@ int do_mptcp(int argc, char **argv)
MPTCP_PM_CMD_GET_LIMITS);
}
if (matches(*argv, "monitor") == 0) {
NEXT_ARG_FWD();
if (argc == 0)
return mptcp_monitor();
goto unknown;
}
unknown:
fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
*argv);

View File

@ -21,6 +21,8 @@ static struct {
unsigned int master;
unsigned int proto;
unsigned int fdb;
unsigned int id;
unsigned int nhid;
} filter;
enum {
@ -39,11 +41,19 @@ static void usage(void)
"Usage: ip nexthop { list | flush } [ protocol ID ] SELECTOR\n"
" ip nexthop { add | replace } id ID NH [ protocol 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"
" [ groups ] [ fdb ]\n"
"BUCKET_SELECTOR := SELECTOR | [ nhid ID ]\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"
"TYPE := { mpath | resilient }\n"
"TYPE_ARGS := [ RESILIENT_ARGS ]\n"
"RESILIENT_ARGS := [ buckets BUCKETS ] [ idle_timer IDLE ]\n"
" [ unbalanced_timer UNBALANCED ]\n"
"ENCAPTYPE := [ mpls ]\n"
"ENCAPHDR := [ MPLSLABEL ]\n");
exit(-1);
@ -80,6 +90,36 @@ static int nh_dump_filter(struct nlmsghdr *nlh, int reqlen)
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 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);
}
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)
{
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)
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);
@ -241,6 +368,12 @@ int print_nexthop(struct nlmsghdr *n, void *arg)
if (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])
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;
}
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)
{
struct nexthop_grp *grps = NULL;
@ -331,6 +508,110 @@ out:
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)
{
struct {
@ -347,12 +628,9 @@ static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
while (argc > 0) {
if (!strcmp(*argv, "id")) {
__u32 id;
NEXT_ARG();
if (get_unsigned(&id, *argv, 0))
invarg("invalid id value", *argv);
addattr32(&req.n, sizeof(req), NHA_ID, id);
addattr32(&req.n, sizeof(req), NHA_ID,
ipnh_parse_id(*argv));
} else if (!strcmp(*argv, "dev")) {
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))
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) {
__u32 prot;
@ -464,6 +744,24 @@ static int ipnh_get_id(__u32 id)
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)
{
unsigned int all = (argc == 0);
@ -489,12 +787,8 @@ static int ipnh_list_flush(int argc, char **argv, int action)
if (!filter.master)
invarg("VRF does not exist\n", *argv);
} else if (!strcmp(*argv, "id")) {
__u32 id;
NEXT_ARG();
if (get_unsigned(&id, *argv, 0))
invarg("invalid id value", *argv);
return ipnh_get_id(id);
return ipnh_list_flush_id(ipnh_parse_id(*argv), action);
} else if (!matches(*argv, "protocol")) {
__u32 proto;
@ -540,8 +834,7 @@ static int ipnh_get(int argc, char **argv)
while (argc > 0) {
if (!strcmp(*argv, "id")) {
NEXT_ARG();
if (get_unsigned(&id, *argv, 0))
invarg("invalid id value", *argv);
id = ipnh_parse_id(*argv);
} else {
usage();
}
@ -556,6 +849,151 @@ static int ipnh_get(int argc, char **argv)
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)
{
if (argc < 1)
@ -581,6 +1019,9 @@ int do_ipnh(int argc, char **argv)
if (!matches(*argv, "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"))
usage();

View File

@ -260,35 +260,6 @@ static void print_flags(long flags)
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)
{
glob_t globbuf = { };
@ -346,7 +317,7 @@ static void show_processes(const char *name)
} else if (err == 2 &&
!strcmp("iff", key) &&
!strcmp(name, value)) {
char *pname = pid_name(pid);
char *pname = get_task_name(pid);
print_string(PRINT_ANY, "name",
"%s", pname ? : "<NULL>");

View File

@ -907,6 +907,14 @@ void xfrm_xfrma_print(struct rtattr *tb[], __u16 family,
fprintf(fp, "if_id %#x", if_id);
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)

View File

@ -63,7 +63,7 @@ static void usage(void)
" [ coa ADDR[/PLEN] ] [ ctx CTX ] [ extra-flag EXTRA-FLAG-LIST ]\n"
" [ offload [dev DEV] dir DIR ]\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"
" [ reqid REQID ] [ seq SEQ ] [ min SPI max SPI ]\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};
bool is_if_id_set = false;
__u32 if_id = 0;
__u32 tfcpad = 0;
while (argc > 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))
invarg("value after \"if_id\" is invalid", *argv);
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 {
/* try to assume ALGO */
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)
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)) {
switch (req.xsinfo.mode) {
case XFRM_MODE_TRANSPORT:

View File

@ -316,3 +316,27 @@ int get_command_name(const char *pid, char *comm, size_t len)
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;
}

View File

@ -299,6 +299,19 @@ int print_color_null(enum output_type type,
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) */
void print_nl(void)
{

View File

@ -67,6 +67,72 @@ int genl_resolve_family(struct rtnl_handle *grth, const char *family)
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_family)
{

View File

@ -282,6 +282,32 @@ int rtnl_nexthopdump_req(struct rtnl_handle *rth, int family,
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,
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);
}
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)
{
struct {

View File

@ -196,19 +196,28 @@ void mnlu_gen_socket_close(struct mnlu_gen_socket *nlg)
free(nlg->buf);
}
struct nlmsghdr *mnlu_gen_socket_cmd_prepare(struct mnlu_gen_socket *nlg,
uint8_t cmd, uint16_t flags)
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 genlmsghdr hdr = {};
struct nlmsghdr *nlh;
hdr.cmd = cmd;
hdr.version = nlg->version;
nlh = mnlu_msg_prepare(nlg->buf, nlg->family, flags, &hdr, sizeof(hdr));
hdr.version = version;
nlh = mnlu_msg_prepare(nlg->buf, id, flags, &hdr, sizeof(hdr));
nlg->seq = nlh->nlmsg_seq;
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,
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;
}
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);
}

View File

@ -138,13 +138,22 @@ bridge \- show / manipulate bridge addresses and devices
.BR pvid " ] [ " untagged " ] [ "
.BR self " ] [ " master " ] "
.ti -8
.BR "bridge vlan set"
.B dev
.I DEV
.B vid
.IR VID " [ "
.B state
.IR STP_STATE " ] "
.ti -8
.BR "bridge vlan" " [ " show " | " tunnelshow " ] [ "
.B dev
.IR DEV " ]"
.ti -8
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " ]"
.BR "bridge monitor" " [ " all " | " neigh " | " link " | " mdb " | " vlan " ]"
.SH OPTIONS
@ -162,7 +171,7 @@ As a rule, the information is statistics or some time values.
.TP
.BR "\-d" , " \-details"
print detailed information about MDB router ports.
print detailed information about bridge vlan filter entries or MDB router ports.
.TP
.BR "\-n" , " \-net" , " \-netns " <NETNS>
@ -813,10 +822,70 @@ The
.BR "pvid " and " untagged"
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.
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
With the
.B -statistics
@ -842,7 +911,7 @@ command is the first in the command line and then the object list follows:
.I OBJECT-LIST
is the list of object types that we want to monitor.
It may contain
.BR link ", " fdb ", and " mdb "."
.BR link ", " fdb ", " vlan " and " mdb "."
If no
.B file
argument is given,

View File

@ -20,6 +20,8 @@ ip-mptcp \- MPTCP path manager configuration
.ti -8
.BR "ip mptcp endpoint add "
.IR IFADDR
.RB "[ " port
.IR PORT " ]"
.RB "[ " dev
.IR IFNAME " ]"
.RB "[ " id
@ -65,6 +67,9 @@ ip-mptcp \- MPTCP path manager configuration
.ti -8
.BR "ip mptcp limits show"
.ti -8
.BR "ip mptcp monitor"
.SH DESCRIPTION
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
.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
is a unique numeric identifier for the given endpoint
@ -137,5 +148,10 @@ each accepted ADD_ADDR option, respecting the
.IR SUBFLOW_NR
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
Original Manpage by Paolo Abeni <pabeni@redhat.com>

View File

@ -28,6 +28,14 @@ ip-nexthop \- nexthop object management
.BR "ip nexthop" " { " get " | " del " } 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
.IR SELECTOR " := "
.RB "[ " id
@ -41,6 +49,12 @@ ip-nexthop \- nexthop object management
.BR groups " ] [ "
.BR fdb " ]"
.ti -8
.IR BUCKET_SELECTOR " := "
.IR SELECTOR
.RB " | [ " nhid
.IR ID " ]"
.ti -8
.IR NH " := { "
.BR blackhole " | [ "
@ -54,7 +68,9 @@ ip-nexthop \- nexthop object management
.BR fdb " ] | "
.B group
.IR GROUP " [ "
.BR fdb " ] } "
.BR fdb " ] [ "
.B type
.IR TYPE " [ " TYPE_ARGS " ] ] }"
.ti -8
.IR ENCAP " := [ "
@ -71,6 +87,23 @@ ip-nexthop \- nexthop object management
.IR GROUP " := "
.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
.B ip nexthop
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
.TP
.BI group " GROUP"
.BI group " GROUP [ " type " TYPE [ TYPE_ARGS ] ]"
create a nexthop group. Group specification is id with an optional
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
.B blackhole
create a blackhole nexthop
@ -171,6 +244,37 @@ as show.
ip nexthop get id 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
.PP
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
fdb nexthops.
.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
.br
.BR ip (8)

View File

@ -65,6 +65,8 @@ ip-xfrm \- transform configuration
.IR MASK " ] ]"
.RB "[ " if_id
.IR IF-ID " ]"
.RB "[ " tfcpad
.IR LENGTH " ]"
.ti -8
.B "ip xfrm state allocspi"

View File

@ -5,9 +5,11 @@ police - policing action
.SH SYNOPSIS
.in +8
.ti -8
.BR tc " ... " "action police"
.BR tc " ... " "action police ["
.BI rate " RATE " burst
.IR BYTES [\fB/ BYTES "] ["
.IR BYTES [\fB/ BYTES "] ] ["
.BI pkts_rate " RATE " pkts_burst
.IR PACKETS "] ["
.B mtu
.IR BYTES [\fB/ BYTES "] ] ["
.BI peakrate " RATE"
@ -34,19 +36,29 @@ police - policing action
.SH DESCRIPTION
The
.B police
action allows to limit bandwidth of traffic matched by the filter it is
attached to. Basically there are two different algorithms available to measure
the packet rate: The first one uses an internal dual token bucket and is
configured using the
action allows limiting of the byte or packet rate of traffic matched by the
filter it is attached to.
.P
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
parameters. The second one uses an in-kernel sampling mechanism. It can be
fine-tuned using the
.B estimator
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
.TP
.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
.B conform-exceed
option.
@ -55,6 +67,15 @@ option.
Set the maximum allowed burst in bytes, optionally followed by a slash ('/')
sign and cell size which must be a power of 2.
.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]"
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

View File

@ -195,30 +195,6 @@ void print_qp_type(struct rd *rd, uint32_t 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,
struct nlattr *nlattr)
{

View File

@ -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,
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_link(struct rd *rd, uint32_t idx, const char *name, uint32_t port,
struct nlattr **nla_line);

View File

@ -38,7 +38,8 @@ struct action_util police_action_util = {
static void usage(void)
{
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"
" [ linklayer TYPE ] [ CONTROL ]\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;
struct rtattr *tail;
__u64 rate64 = 0, prate64 = 0;
__u64 pps64 = 0, ppsburst64 = 0;
if (a) /* new way of doing things */
NEXT_ARG();
@ -144,6 +146,18 @@ static int act_parse_police(struct action_util *a, int *argc_p, char ***argv_p,
NEXT_ARG();
if (get_linklayer(&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) {
usage();
} else {
@ -161,8 +175,8 @@ action_ctrl_ok:
return -1;
/* Must at least do late binding, use TB or ewma policing */
if (!rate64 && !avrate && !p.index && !mtu) {
fprintf(stderr, "'rate' or 'avrate' or 'mtu' MUST be specified.\n");
if (!rate64 && !avrate && !p.index && !mtu && !pps64) {
fprintf(stderr, "'rate' or 'avrate' or 'mtu' or 'pkts_rate' MUST be specified.\n");
return -1;
}
@ -172,6 +186,18 @@ action_ctrl_ok:
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 (!rate64) {
fprintf(stderr, "'peakrate' requires 'rate'.\n");
@ -223,6 +249,12 @@ action_ctrl_ok:
if (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);
res = 0;
@ -244,6 +276,7 @@ static int print_police(struct action_util *a, FILE *f, struct rtattr *arg)
unsigned int buffer;
unsigned int linklayer;
__u64 rate64, prate64;
__u64 pps64, ppsburst64;
if (arg == NULL)
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 ",
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, "");
if (tb[TCA_POLICE_RESULT]) {

View File

@ -21,9 +21,6 @@
#include <linux/genetlink.h>
#include <linux/if.h>
#include <libmnl/libmnl.h>
#include <sys/socket.h>
#include "utils.h"
#include "cmdl.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)
{
int netid;
char mnl_msg[MNL_SOCKET_BUFFER_SIZE];
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");
return -1;
}
@ -399,7 +396,6 @@ static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd,
{
int err;
char *media;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct opt *opt;
struct nlattr *attrs;
struct opt opts[] = {
@ -435,7 +431,8 @@ static int cmd_bearer_add_media(struct nlmsghdr *nlh, const struct cmd *cmd,
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");
return -1;
}
@ -482,7 +479,6 @@ static int cmd_bearer_enable(struct nlmsghdr *nlh, const struct cmd *cmd,
int err;
struct opt *opt;
struct nlattr *nest;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct opt opts[] = {
{ "device", 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;
}
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");
return -1;
}
@ -563,7 +560,6 @@ static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
int err;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *nest;
struct opt opts[] = {
{ "device", OPT_KEYVAL, NULL },
@ -584,7 +580,8 @@ static int cmd_bearer_disable(struct nlmsghdr *nlh, const struct cmd *cmd,
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");
return -1;
}
@ -628,7 +625,6 @@ static int cmd_bearer_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
int err;
int val;
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *props;
struct nlattr *attrs;
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");
return -1;
}
@ -876,7 +873,6 @@ static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd,
{
int err;
char *media;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct opt *opt;
struct cb_data cb_data = {0};
struct nlattr *attrs;
@ -918,7 +914,8 @@ static int cmd_bearer_get_media(struct nlmsghdr *nlh, const struct cmd *cmd,
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");
return -1;
}
@ -956,7 +953,6 @@ static int cmd_bearer_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
{
int err;
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *attrs;
struct opt opts[] = {
{ "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");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
fprintf(stderr, "Usage: %s bearer list\n", cmdl->argv[0]);
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");
return -1;
}

View File

@ -13,8 +13,6 @@
#include <string.h>
#include <errno.h>
#include <libmnl/libmnl.h>
#include "cmdl.h"
static const struct cmd *find_cmd(const struct cmd *cmds, char *str)

View File

@ -17,7 +17,6 @@
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int err = 0;
if (help_flag) {
@ -68,7 +66,7 @@ static int cmd_link_list(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_LINK_GET);
nlh = msg_init(TIPC_NL_LINK_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -126,7 +124,6 @@ static int cmd_link_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *attrs;
struct opt *opt;
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)
return -EINVAL;
nlh = msg_init(buf, TIPC_NL_LINK_GET);
nlh = msg_init(TIPC_NL_LINK_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -241,7 +238,6 @@ static int cmd_link_get_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
int prop = TIPC_NLA_PROP_BROADCAST;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *attrs;
if (help_flag) {
@ -249,7 +245,7 @@ static int cmd_link_get_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_LINK_GET);
nlh = msg_init(TIPC_NL_LINK_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -284,7 +280,6 @@ static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
char *link;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct opt *opt;
struct nlattr *nest;
struct opt opts[] = {
@ -302,7 +297,7 @@ static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS);
nlh = msg_init(TIPC_NL_LINK_RESET_STATS);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -550,7 +545,6 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
char *link = NULL;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct opt *opt;
struct opt opts[] = {
{ "link", OPT_KEYVAL, NULL },
@ -564,7 +558,7 @@ static int cmd_link_stat_show(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_LINK_GET);
nlh = msg_init(TIPC_NL_LINK_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -626,7 +620,6 @@ static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
{
int val;
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *props;
struct nlattr *attrs;
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)
return -EINVAL;
nlh = msg_init(buf, TIPC_NL_LINK_SET);
nlh = msg_init(TIPC_NL_LINK_SET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *props;
struct nlattr *attrs;
struct opt *opt;
@ -734,7 +726,7 @@ static int cmd_link_set_bcast(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_LINK_SET);
nlh = msg_init(TIPC_NL_LINK_SET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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)
{
int size;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *attrs;
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));
nlh = msg_init(buf, TIPC_NL_MON_SET);
nlh = msg_init(TIPC_NL_MON_SET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int err = 0;
if (help_flag) {
@ -849,7 +839,7 @@ static int cmd_link_mon_summary(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_MON_GET);
nlh = msg_init(TIPC_NL_MON_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -1004,11 +994,10 @@ exit:
static int link_mon_peer_list(uint32_t mon_ref)
{
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *nest;
int err = 0;
nlh = msg_init(buf, TIPC_NL_MON_PEER_GET);
nlh = msg_init(TIPC_NL_MON_PEER_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
char bname[TIPC_MAX_BEARER_NAME] = {0};
struct opt opts[] = {
{ "media", OPT_KEYVAL, NULL },
@ -1112,7 +1100,7 @@ static int cmd_link_mon_list(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_MON_GET);
nlh = msg_init(TIPC_NL_MON_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
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) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;

View File

@ -15,9 +15,7 @@
#include <errno.h>
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
fprintf(stderr, "Usage: %s media list\n", cmdl->argv[0]);
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");
return -1;
}
@ -89,7 +86,6 @@ static int cmd_media_get_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *nest;
struct opt *opt;
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)
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");
return -1;
}
@ -179,7 +176,6 @@ static int cmd_media_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
{
int val;
int prop;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *props;
struct nlattr *attrs;
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)
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");
return -1;
}

View File

@ -13,13 +13,13 @@
#include <time.h>
#include <errno.h>
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "mnl_utils.h"
#include "msg.h"
extern struct mnlu_gen_socket tipc_nlg;
int parse_attrs(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
@ -30,141 +30,23 @@ int parse_attrs(const struct nlattr *attr, void *data)
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)
{
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)
{
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 genlmsghdr *genl;
family = get_family();
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;
nlh = mnlu_gen_socket_cmd_prepare(&tipc_nlg, cmd, 0);
return nlh;
}

View File

@ -12,7 +12,7 @@
#ifndef _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_dumpit(struct nlmsghdr *nlh, mnl_cb_t callback, void *data);
int parse_attrs(const struct nlattr *attr, void *data);

View File

@ -15,7 +15,6 @@
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.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)
{
int iteration = 0;
char buf[MNL_SOCKET_BUFFER_SIZE];
int rc = 0;
if (help_flag) {
@ -90,7 +88,8 @@ static int cmd_nametable_show(struct nlmsghdr *nlh, const struct cmd *cmd,
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");
return -1;
}

View File

@ -17,7 +17,6 @@
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
fprintf(stderr, "Usage: %s node list\n", cmdl->argv[0]);
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");
return -1;
}
@ -73,7 +71,6 @@ static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
char *str;
uint32_t addr;
struct nlattr *nest;
char buf[MNL_SOCKET_BUFFER_SIZE];
if (cmdl->argc != cmdl->optind + 1) {
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)
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");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
uint8_t id[16] = {0,};
uint64_t *w0 = (uint64_t *) &id[0];
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;
}
nlh = msg_init(buf, TIPC_NL_NET_SET);
nlh = msg_init(TIPC_NL_NET_SET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -201,7 +198,6 @@ static int cmd_node_set_key(struct nlmsghdr *nlh, const struct cmd *cmd,
};
struct nlattr *nest;
struct opt *opt_algname, *opt_nodeid, *opt_master, *opt_rekeying;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint8_t id[TIPC_NODEID_LEN] = {0,};
uint32_t rekeying = 0;
bool has_key = false;
@ -262,7 +258,7 @@ get_ops:
}
/* Init & do the command */
nlh = msg_init(buf, TIPC_NL_KEY_SET);
nlh = msg_init(TIPC_NL_KEY_SET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;
@ -287,15 +283,13 @@ get_ops:
static int cmd_node_flush_key(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
(cmd->help)(cmdl);
return -EINVAL;
}
/* Init & do the command */
nlh = msg_init(buf, TIPC_NL_KEY_FLUSH);
nlh = msg_init(TIPC_NL_KEY_FLUSH);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
(cmd->help)(cmdl);
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_NET_GET);
nlh = msg_init(TIPC_NL_NET_GET);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
(cmd->help)(cmdl);
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");
return -1;
}
@ -389,7 +380,6 @@ static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
struct cmdl *cmdl, void *data)
{
int netid;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlattr *nest;
if (help_flag) {
@ -397,7 +387,8 @@ static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
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");
return -1;
}

View File

@ -17,7 +17,6 @@
#include <linux/tipc_netlink.h>
#include <linux/tipc.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.h"
#include "msg.h"
@ -30,7 +29,6 @@ static int cmd_peer_rm_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
char *str;
uint32_t addr;
struct nlattr *nest;
char buf[MNL_SOCKET_BUFFER_SIZE];
if ((cmdl->argc != cmdl->optind + 1) || help_flag) {
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)
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");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
__u8 id[16] = {0,};
__u64 *w0 = (__u64 *)&id[0];
__u64 *w1 = (__u64 *)&id[8];
@ -81,7 +79,7 @@ static int cmd_peer_rm_nodeid(struct nlmsghdr *nlh, const struct cmd *cmd,
return -EINVAL;
}
nlh = msg_init(buf, TIPC_NL_PEER_REMOVE);
nlh = msg_init(TIPC_NL_PEER_REMOVE);
if (!nlh) {
fprintf(stderr, "error, message initialisation failed\n");
return -1;

View File

@ -15,7 +15,6 @@
#include <linux/tipc.h>
#include <linux/tipc_netlink.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include "cmdl.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)
{
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
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");
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,
struct cmdl *cmdl, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
if (help_flag) {
fprintf(stderr, "Usage: %s socket list\n", cmdl->argv[0]);
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");
return -1;
}

View File

@ -13,7 +13,11 @@
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <linux/tipc_netlink.h>
#include <libmnl/libmnl.h>
#include <errno.h>
#include "mnl_utils.h"
#include "bearer.h"
#include "link.h"
#include "nametable.h"
@ -26,6 +30,7 @@
int help_flag;
int json;
struct mnlu_gen_socket tipc_nlg;
static void about(struct cmdl *cmdl)
{
@ -110,8 +115,20 @@ int main(int argc, char *argv[])
cmdl.argc = argc;
cmdl.argv = argv;
if ((res = run_cmd(NULL, &cmd, cmds, &cmdl, NULL)) != 0)
return 1;
res = mnlu_gen_socket_open(&tipc_nlg, TIPC_GENL_V2_NAME,
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;
}