From cfcabf18d84a2f4908cb5b4489f67c2cd3f70426 Mon Sep 17 00:00:00 2001 From: Amir Vadai Date: Mon, 4 Jul 2016 10:34:11 +0300 Subject: [PATCH 1/7] tc: flower: Add skip_{hw|sw} support On devices that support TC flower offloads, these flags enable a filter to be added only to HW or only to SW. skip_sw and skip_hw are mutually exclusive flags. By default without any flags, the filter is added to both HW and SW, but no error checks are done in case of failure to add to HW. With skip-sw, failure to add to HW is treated as an error. Here is a sample script that adds 2 filters, one with skip_sw and the other with skip_hw flag. # add ingress qdisc tc qdisc add dev enp0s9 ingress # enable hw tc offload. ethtool -K enp0s9 hw-tc-offload on # add a flower filter with skip-sw flag. tc filter add dev enp0s9 protocol ip parent ffff: flower \ ip_proto 1 indev enp0s9 skip_sw \ action drop # add a flower filter with skip-hw flag. tc filter add dev enp0s9 protocol ip parent ffff: flower \ ip_proto 3 indev enp0s9 skip_hw \ action drop Signed-off-by: Amir Vadai Acked-by: Jiri Pirko --- man/man8/tc-flower.8 | 11 ++++++++++- tc/f_flower.c | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/man/man8/tc-flower.8 b/man/man8/tc-flower.8 index df4d8e19..9ae10e6f 100644 --- a/man/man8/tc-flower.8 +++ b/man/man8/tc-flower.8 @@ -18,7 +18,9 @@ flower \- flow based traffic control filter .ti -8 .IR MATCH " := { " .B indev -.IR ifname " | { " +.IR ifname " | " +.BR skip_sw " | " skip_hw +.R " | { " .BR dst_mac " | " src_mac " } " .IR mac_address " | " .BR eth_type " { " ipv4 " | " ipv6 " | " @@ -55,6 +57,13 @@ is the name of an interface which must exist at the time of .B tc invocation. .TP +.BI skip_sw +Do not process filter by software. If hardware has no offload support for this +filter, or TC offload is not enabled for the interface, operation will fail. +.TP +.BI skip_hw +Do not process filter by hardware. +.TP .BI dst_mac " mac_address" .TQ .BI src_mac " mac_address" diff --git a/tc/f_flower.c b/tc/f_flower.c index fd2014b3..7b46ceb1 100644 --- a/tc/f_flower.c +++ b/tc/f_flower.c @@ -25,6 +25,7 @@ static void explain(void) { fprintf(stderr, "Usage: ... flower [ MATCH-LIST ]\n"); + fprintf(stderr, " [ skip_sw | skip_hw ]\n"); fprintf(stderr, " [ action ACTION-SPEC ] [ classid CLASSID ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: MATCH-LIST := [ MATCH-LIST ] MATCH\n"); @@ -167,6 +168,7 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, struct rtattr *tail; __be16 eth_type = TC_H_MIN(t->tcm_info); __u8 ip_proto = 0xff; + __u32 flags = 0; if (handle) { ret = get_u32(&t->tcm_handle, handle, 0); @@ -196,6 +198,10 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, return -1; } addattr_l(n, MAX_MSG, TCA_FLOWER_CLASSID, &handle, 4); + } else if (matches(*argv, "skip_hw") == 0) { + flags |= TCA_CLS_FLAGS_SKIP_HW; + } else if (matches(*argv, "skip_sw") == 0) { + flags |= TCA_CLS_FLAGS_SKIP_SW; } else if (matches(*argv, "indev") == 0) { char ifname[IFNAMSIZ]; @@ -294,6 +300,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle, } parse_done: + addattr32(n, MAX_MSG, TCA_FLOWER_FLAGS, flags); + ret = addattr16(n, MAX_MSG, TCA_FLOWER_KEY_ETH_TYPE, eth_type); if (ret) { fprintf(stderr, "Illegal \"eth_type\"(0x%x)\n", @@ -498,6 +506,15 @@ static int flower_print_opt(struct filter_util *qu, FILE *f, tb[TCA_FLOWER_KEY_TCP_SRC], tb[TCA_FLOWER_KEY_UDP_SRC]); + if (tb[TCA_FLOWER_FLAGS]) { + __u32 flags = rta_getattr_u32(tb[TCA_FLOWER_FLAGS]); + + if (flags & TCA_CLS_FLAGS_SKIP_HW) + fprintf(f, "\n skip_hw"); + if (flags & TCA_CLS_FLAGS_SKIP_SW) + fprintf(f, "\n skip_sw"); + } + if (tb[TCA_FLOWER_ACT]) { tc_print_action(f, tb[TCA_FLOWER_ACT]); } From 7dc0e974f16b5dd827d1ceb4323376012490a608 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 29 Jun 2016 11:26:57 -0700 Subject: [PATCH 2/7] ip vrf: Add name_is_vrf Add name_is_vrf function to determine if given name corresponds to a VRF device. Signed-off-by: David Ahern --- ip/ip_common.h | 2 ++ ip/iplink_vrf.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/ip/ip_common.h b/ip/ip_common.h index e8da9e03..b747ea47 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -90,6 +90,8 @@ struct link_util *get_link_slave_kind(const char *slave_kind); void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len); +bool name_is_vrf(const char *name); + #ifndef INFINITY_LIFE_TIME #define INFINITY_LIFE_TIME 0xFFFFFFFFU #endif diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c index e3c7b465..166f566e 100644 --- a/ip/iplink_vrf.c +++ b/ip/iplink_vrf.c @@ -96,3 +96,53 @@ struct link_util vrf_slave_link_util = { .print_opt = vrf_slave_print_opt, .slave = true, }; + +bool name_is_vrf(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct { + struct nlmsghdr n; + char buf[8192]; + } answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct ifinfomsg *ifi; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0) + return false; + + ifi = NLMSG_DATA(&answer.n); + len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + return false; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + return false; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND]) + return false; + + return strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf") == 0; +} From 104444c2019a60671683da5ccfb6273e8238b050 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 29 Jun 2016 11:26:58 -0700 Subject: [PATCH 3/7] ip link/addr: Add support for vrf keyword Add vrf keyword to 'ip link' and 'ip addr' commands (common list code). Allows: 1. Adding a link to a VRF $ ip link set NAME vrf NAME Removing a link from a VRF still uses 'ip link set NAME nomaster' 2. Showing links associated with a VRF: $ ip link show vrf NAME 3. List addresses associated with links in a VRF $ ip -br addr show vrf red Signed-off-by: David Ahern --- ip/ipaddress.c | 12 +++++++++++- ip/iplink.c | 15 +++++++++++++-- man/man8/ip-address.8.in | 6 ++++++ man/man8/ip-link.8.in | 10 ++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 02bc9029..88e96622 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -80,7 +80,7 @@ static void usage(void) fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label LABEL ] [up]\n"); fprintf(stderr, " ip address [ show [ dev IFNAME ] [ scope SCOPE-ID ] [ master DEVICE ]\n"); fprintf(stderr, " [ type TYPE ] [ to PREFIX ] [ FLAG-LIST ]\n"); - fprintf(stderr, " [ label LABEL ] [up] ]\n"); + fprintf(stderr, " [ label LABEL ] [up] [ vrf NAME ] ]\n"); fprintf(stderr, " ip address {showdump|restore}\n"); fprintf(stderr, "IFADDR := PREFIX | ADDR peer PREFIX\n"); fprintf(stderr, " [ broadcast ADDR ] [ anycast ADDR ]\n"); @@ -1620,6 +1620,16 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) if (!ifindex) invarg("Device does not exist\n", *argv); filter.master = ifindex; + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + filter.master = ifindex; } else if (strcmp(*argv, "type") == 0) { int soff; diff --git a/ip/iplink.c b/ip/iplink.c index b1f8a379..f2a2e13c 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -82,11 +82,11 @@ void iplink_usage(void) fprintf(stderr, " [ query_rss { on | off} ]\n"); fprintf(stderr, " [ state { auto | enable | disable} ] ]\n"); fprintf(stderr, " [ trust { on | off} ] ]\n"); - fprintf(stderr, " [ master DEVICE ]\n"); + fprintf(stderr, " [ master DEVICE ][ vrf NAME ]\n"); fprintf(stderr, " [ nomaster ]\n"); fprintf(stderr, " [ addrgenmode { eui64 | none | stable_secret | random } ]\n"); fprintf(stderr, " [ protodown { on | off } ]\n"); - fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [type TYPE]\n"); + fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"); if (iplink_have_newlink()) { fprintf(stderr, " ip link help [ TYPE ]\n"); @@ -603,6 +603,17 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, invarg("Device does not exist\n", *argv); addattr_l(&req->n, sizeof(*req), IFLA_MASTER, &ifindex, 4); + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + addattr_l(&req->n, sizeof(*req), IFLA_MASTER, + &ifindex, sizeof(ifindex)); } else if (matches(*argv, "nomaster") == 0) { int ifindex = 0; diff --git a/man/man8/ip-address.8.in b/man/man8/ip-address.8.in index 3cbe4181..7cb92717 100644 --- a/man/man8/ip-address.8.in +++ b/man/man8/ip-address.8.in @@ -45,6 +45,8 @@ ip-address \- protocol address management .IR DEVICE " ] [ " .B type .IR TYPE " ] [ " +.B vrf +.IR NAME " ] [ " .BR up " ] ]" .ti -8 @@ -279,6 +281,10 @@ is a usual shell style pattern. .BI master " DEVICE" only list interfaces enslaved to this master device. +.TP +.BI vrf " NAME " +only list interfaces enslaved to this vrf. + .TP .BI type " TYPE" only list interfaces of the given type. diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 375b4d08..ad18f755 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -154,6 +154,9 @@ ip-link \- network device configuration .br .RB "[ " nomaster " ]" .br +.RB "[ " vrf +.IR NAME " ]" +.br .RB "[ " addrgenmode " { " eui64 " | " none " | " stable_secret " | " random " } ]" @@ -167,6 +170,8 @@ ip-link \- network device configuration .IR DEVICE " ] [" .B type .IR TYPE " ]" +.B vrf +.IR NAME " ]" .ti -8 .B ip link help @@ -1255,6 +1260,11 @@ only display running interfaces. .I DEVICE specifies the master device which enslaves devices to show. +.TP +.BI vrf " NAME " +.I NAME +speficies the VRF which enslaves devices to show. + .TP .BI type " TYPE " .I TYPE From 5db1adae2a8f73563f8a3945cbdf2945975fe3c2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 29 Jun 2016 11:26:59 -0700 Subject: [PATCH 4/7] ip neigh: Add support for keyword Add vrf keyword to 'ip neigh' commands. Allows listing neighbor entries for all links associated with a given VRF. Signed-off-by: David Ahern --- ip/ipneigh.c | 14 +++++++++++++- man/man8/ip-neighbour.8 | 8 +++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ip/ipneigh.c b/ip/ipneigh.c index 4ddb747e..3e444712 100644 --- a/ip/ipneigh.c +++ b/ip/ipneigh.c @@ -48,7 +48,8 @@ static void usage(void) { fprintf(stderr, "Usage: ip neigh { add | del | change | replace }\n" " { ADDR [ lladdr LLADDR ] [ nud STATE ] | proxy ADDR } [ dev DEV ]\n"); - fprintf(stderr, " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n\n"); + fprintf(stderr, " ip neigh { show | flush } [ proxy ] [ to PREFIX ] [ dev DEV ] [ nud STATE ]\n"); + fprintf(stderr, " [ vrf NAME ]\n\n"); fprintf(stderr, "STATE := { permanent | noarp | stale | reachable | none |\n" " incomplete | delay | probe | failed }\n"); exit(-1); @@ -385,6 +386,17 @@ static int do_show_or_flush(int argc, char **argv, int flush) invarg("Device does not exist\n", *argv); addattr32(&req.n, sizeof(req), NDA_MASTER, ifindex); filter.master = ifindex; + } else if (strcmp(*argv, "vrf") == 0) { + int ifindex; + + NEXT_ARG(); + ifindex = ll_name_to_index(*argv); + if (!ifindex) + invarg("Not a valid VRF name\n", *argv); + if (!name_is_vrf(*argv)) + invarg("Not a valid VRF name\n", *argv); + addattr32(&req.n, sizeof(req), NDA_MASTER, ifindex); + filter.master = ifindex; } else if (strcmp(*argv, "unused") == 0) { filter.unused_only = 1; } else if (strcmp(*argv, "nud") == 0) { diff --git a/man/man8/ip-neighbour.8 b/man/man8/ip-neighbour.8 index b292e181..bbfe8e72 100644 --- a/man/man8/ip-neighbour.8 +++ b/man/man8/ip-neighbour.8 @@ -31,7 +31,9 @@ ip-neighbour \- neighbour/arp tables management. .B dev .IR DEV " ] [ " .B nud -.IR STATE " ]" +.IR STATE " ] [ " +.B vrf +.IR NAME " ] " .ti -8 .IR STATE " := {" @@ -163,6 +165,10 @@ the prefix selecting the neighbours to list. .BI dev " NAME" only list the neighbours attached to this device. +.TP +.BI vrf " NAME" +only list the neighbours for given VRF. + .TP .BI proxy list neighbour proxies. From d84b1878eaae882e066b7d051eb6af13cf64152e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 29 Jun 2016 11:27:00 -0700 Subject: [PATCH 5/7] ip route: Change type mask to bitmask Allow option to select multiple route types to show or exlude specific route types. Signed-off-by: David Ahern --- ip/iproute.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ip/iproute.c b/ip/iproute.c index 7c0f5a4f..e785d401 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -113,7 +113,7 @@ static struct int flushe; int protocol, protocolmask; int scope, scopemask; - int type, typemask; + __u64 typemask; int tos, tosmask; int iif, iifmask; int oif, oifmask; @@ -178,7 +178,8 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return 0; if ((filter.scope^r->rtm_scope)&filter.scopemask) return 0; - if ((filter.type^r->rtm_type)&filter.typemask) + + if (filter.typemask && !(filter.typemask & (1 << r->rtm_type))) return 0; if ((filter.tos^r->rtm_tos)&filter.tosmask) return 0; @@ -365,7 +366,8 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (n->nlmsg_type == RTM_DELROUTE) fprintf(fp, "Deleted "); - if ((r->rtm_type != RTN_UNICAST || show_details > 0) && !filter.type) + if ((r->rtm_type != RTN_UNICAST || show_details > 0) && + (!filter.typemask || (filter.typemask & (1 << r->rtm_type)))) fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); if (tb[RTA_DST]) { @@ -1430,10 +1432,9 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) int type; NEXT_ARG(); - filter.typemask = -1; if (rtnl_rtntype_a2n(&type, *argv)) invarg("node type value is invalid\n", *argv); - filter.type = type; + filter.typemask = (1< Date: Wed, 29 Jun 2016 11:27:01 -0700 Subject: [PATCH 6/7] ip vrf: Add ipvrf_get_table Add ipvrf_get_table to lookup table id for device name. Returns 0 on any error or if name is not a VRF device. Signed-off-by: David Ahern --- ip/ip_common.h | 1 + ip/iplink_vrf.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/ip/ip_common.h b/ip/ip_common.h index b747ea47..c8188122 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -90,6 +90,7 @@ struct link_util *get_link_slave_kind(const char *slave_kind); void br_dump_bridge_id(const struct ifla_bridge_id *id, char *buf, size_t len); +__u32 ipvrf_get_table(const char *name); bool name_is_vrf(const char *name); #ifndef INFINITY_LIFE_TIME diff --git a/ip/iplink_vrf.c b/ip/iplink_vrf.c index 166f566e..015b41e1 100644 --- a/ip/iplink_vrf.c +++ b/ip/iplink_vrf.c @@ -97,6 +97,69 @@ struct link_util vrf_slave_link_util = { .slave = true, }; +/* returns table id if name is a VRF device */ +__u32 ipvrf_get_table(const char *name) +{ + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req = { + .n = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + .nlmsg_flags = NLM_F_REQUEST, + .nlmsg_type = RTM_GETLINK, + }, + .i = { + .ifi_family = preferred_family, + }, + }; + struct { + struct nlmsghdr n; + char buf[8192]; + } answer; + struct rtattr *tb[IFLA_MAX+1]; + struct rtattr *li[IFLA_INFO_MAX+1]; + struct rtattr *vrf_attr[IFLA_VRF_MAX + 1]; + struct ifinfomsg *ifi; + __u32 tb_id = 0; + int len; + + addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, strlen(name) + 1); + + if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0) + return 0; + + ifi = NLMSG_DATA(&answer.n); + len = answer.n.nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + if (len < 0) { + fprintf(stderr, "BUG: Invalid response to link query.\n"); + return 0; + } + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); + + if (!tb[IFLA_LINKINFO]) + return 0; + + parse_rtattr_nested(li, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); + + if (!li[IFLA_INFO_KIND] || !li[IFLA_INFO_DATA]) + return 0; + + if (strcmp(RTA_DATA(li[IFLA_INFO_KIND]), "vrf")) + return 0; + + parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, li[IFLA_INFO_DATA]); + if (vrf_attr[IFLA_VRF_TABLE]) + tb_id = rta_getattr_u32(vrf_attr[IFLA_VRF_TABLE]); + + if (!tb_id) + fprintf(stderr, "BUG: VRF %s is missing table id\n", name); + + return tb_id; +} + bool name_is_vrf(const char *name) { struct { From 0130f0120b84870ca1d31f9e215793594c98a38b Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 29 Jun 2016 11:27:02 -0700 Subject: [PATCH 7/7] ip route: Add support for vrf keyword Add vrf keyword to 'ip route' commands. Allows: 1. Users can list routes by VRF name: $ ip route show vrf NAME VRF tables have all routes including local and broadcast routes. The VRF keyword filters LOCAL and BROADCAST routes; to see all routes the table option can be used. Or to see local routes only for a VRF: $ ip route show vrf NAME type local 2. Add or delete a route for a VRF: $ ip route {add|delete} vrf NAME 3. Do a route lookup for a VRF: $ ip route get vrf NAME ADDRESS Signed-off-by: David Ahern --- ip/iproute.c | 32 ++++++++++++++++++++++++++++++-- man/man8/ip-route.8.in | 19 ++++++++++++++++++- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/ip/iproute.c b/ip/iproute.c index e785d401..24f6b010 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -67,10 +67,10 @@ static void usage(void) fprintf(stderr, " ip route showdump\n"); fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); - fprintf(stderr, " [ mark NUMBER ]\n"); + fprintf(stderr, " [ mark NUMBER ] [ vrf NAME ]\n"); fprintf(stderr, " ip route { add | del | change | append | replace } ROUTE\n"); fprintf(stderr, "SELECTOR := [ root PREFIX ] [ match PREFIX ] [ exact PREFIX ]\n"); - fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); + fprintf(stderr, " [ table TABLE_ID ] [ vrf NAME ] [ proto RTPROTO ]\n"); fprintf(stderr, " [ type TYPE ] [ scope SCOPE ]\n"); fprintf(stderr, "ROUTE := NODE_SPEC [ INFO_SPEC ]\n"); fprintf(stderr, "NODE_SPEC := [ TYPE ] PREFIX [ tos TOS ]\n"); @@ -1138,6 +1138,20 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) addattr32(&req.n, sizeof(req), RTA_TABLE, tid); } table_ok = 1; + } else if (matches(*argv, "vrf") == 0) { + __u32 tid; + + NEXT_ARG(); + tid = ipvrf_get_table(*argv); + if (tid == 0) + invarg("Invalid VRF\n", *argv); + if (tid < 256) + req.r.rtm_table = tid; + else { + req.r.rtm_table = RT_TABLE_UNSPEC; + addattr32(&req.n, sizeof(req), RTA_TABLE, tid); + } + table_ok = 1; } else if (strcmp(*argv, "dev") == 0 || strcmp(*argv, "oif") == 0) { NEXT_ARG(); @@ -1392,6 +1406,15 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) } } else filter.tb = tid; + } else if (matches(*argv, "vrf") == 0) { + __u32 tid; + + NEXT_ARG(); + tid = ipvrf_get_table(*argv); + if (tid == 0) + invarg("Invalid VRF\n", *argv); + filter.tb = tid; + filter.typemask = ~(1 << RTN_LOCAL | 1<