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;
+ struct nexthop_grp *grps = NULL;
int count = 0, i;
+ int err = -1;
char *sep, *wsep;
if (*argv != '\0')
argv = sep + 1;
}
- return addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
+ err = addattr_l(n, maxlen, NHA_GROUP, grps, count * sizeof(*grps));
+out:
+ free(grps);
+ 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 {