@@ -43,9 +43,12 @@ static void usage(void)
" [ groups ] [ fdb ]\n"
"NH := { blackhole | [ via ADDRESS ] [ dev DEV ] [ onlink ]\n"
" [ encap ENCAPTYPE ENCAPHDR ] |\n"
- " group GROUP [ fdb ] [ type TYPE ] }\n"
+ " group GROUP [ fdb ] [ type TYPE [ TYPE_ARGS ] ] }\n"
"GROUP := [ <id[,weight]>/<id[,weight]>/... ]\n"
- "TYPE := { mpath }\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);
@@ -203,6 +206,66 @@ 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();
+}
+
int print_nexthop(struct nlmsghdr *n, void *arg)
{
struct nhmsg *nhm = NLMSG_DATA(n);
@@ -229,7 +292,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);
@@ -243,6 +306,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]);
@@ -333,10 +402,70 @@ 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)
{
@@ -349,6 +478,15 @@ static void parse_nh_group_type(struct nlmsghdr *n, int maxlen, int *argcp,
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;
@@ -56,7 +56,7 @@ ip-nexthop \- nexthop object management
.IR GROUP " [ "
.BR fdb " ] [ "
.B type
-.IR TYPE " ] } "
+.IR TYPE " [ " TYPE_ARGS " ] ] }"
.ti -8
.IR ENCAP " := [ "
@@ -75,7 +75,20 @@ ip-nexthop \- nexthop object management
.ti -8
.IR TYPE " := { "
-.BR mpath " }"
+.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
@@ -128,7 +141,7 @@ is a set of encapsulation attributes specific to the
.in -2
.TP
-.BI group " GROUP [ " type " TYPE ]"
+.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
@@ -139,6 +152,37 @@ is a string specifying the nexthop group type. Namely:
.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
@@ -225,6 +269,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)