Merge git://git.kernel.org/pub/scm/network/iproute2/iproute2-next
Required manual fix of devlink/devlink.c Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
This commit is contained in:
commit
2363bc99f9
|
|
@ -10,6 +10,9 @@ void print_vlan_info(struct rtattr *tb, int ifindex);
|
|||
int print_linkinfo(struct nlmsghdr *n, void *arg);
|
||||
int print_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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
254
bridge/vlan.c
254
bridge/vlan.c
|
|
@ -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
121
devlink/mnlg.c
121
devlink/mnlg.c
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
16 dhcp
|
||||
18 keepalived
|
||||
42 babel
|
||||
99 openr
|
||||
186 bgp
|
||||
187 isis
|
||||
188 ospf
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
16
ip/ip.c
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
129
ip/ipmptcp.c
129
ip/ipmptcp.c
|
|
@ -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);
|
||||
|
|
|
|||
469
ip/ipnexthop.c
469
ip/ipnexthop.c
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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>");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
24
lib/fs.c
24
lib/fs.c
|
|
@ -316,3 +316,27 @@ int get_command_name(const char *pid, char *comm, size_t len)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *get_task_name(pid_t pid)
|
||||
{
|
||||
char *comm;
|
||||
FILE *f;
|
||||
|
||||
if (!pid)
|
||||
return NULL;
|
||||
|
||||
if (asprintf(&comm, "/proc/%d/comm", pid) < 0)
|
||||
return NULL;
|
||||
|
||||
f = fopen(comm, "r");
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
if (fscanf(f, "%ms\n", &comm) != 1)
|
||||
comm = NULL;
|
||||
|
||||
fclose(f);
|
||||
|
||||
return comm;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -299,6 +299,19 @@ int print_color_null(enum output_type type,
|
|||
return ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
24
rdma/res.c
24
rdma/res.c
|
|
@ -195,30 +195,6 @@ void print_qp_type(struct rd *rd, uint32_t val)
|
|||
qp_types_to_str(val));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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]) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
37
tipc/link.c
37
tipc/link.c
|
|
@ -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;
|
||||
|
|
|
|||
15
tipc/media.c
15
tipc/media.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
132
tipc/msg.c
132
tipc/msg.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
33
tipc/node.c
33
tipc/node.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
21
tipc/tipc.c
21
tipc/tipc.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue