unsigned int master;
unsigned int proto;
unsigned int fdb;
+ unsigned int id;
+ unsigned int nhid;
} filter;
enum {
"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 ] |\n"
" group GROUP [ fdb ] [ type TYPE [ TYPE_ARGS ] ] }\n"
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)
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);
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;
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)
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();