diff options
Diffstat (limited to 'ip/iproute.c')
-rw-r--r-- | ip/iproute.c | 655 |
1 files changed, 408 insertions, 247 deletions
diff --git a/ip/iproute.c b/ip/iproute.c index 7f81145e..e81bc05e 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -48,7 +48,7 @@ static const char *mx_names[RTAX_MAX+1] = { [RTAX_SSTHRESH] = "ssthresh", [RTAX_CWND] = "cwnd", [RTAX_ADVMSS] = "advmss", - [RTAX_REORDERING]="reordering", + [RTAX_REORDERING] = "reordering", [RTAX_HOPLIMIT] = "hoplimit", [RTAX_INITCWND] = "initcwnd", [RTAX_FEATURES] = "features", @@ -65,30 +65,33 @@ static void usage(void) fprintf(stderr, " ip route save SELECTOR\n"); fprintf(stderr, " ip route restore\n"); fprintf(stderr, " ip route showdump\n"); - fprintf(stderr, " ip route get ADDRESS [ from ADDRESS iif STRING ]\n"); + fprintf(stderr, " ip route get [ ROUTE_GET_FLAGS ] ADDRESS\n"); + fprintf(stderr, " [ from ADDRESS iif STRING ]\n"); fprintf(stderr, " [ oif STRING ] [ tos TOS ]\n"); - fprintf(stderr, " [ mark NUMBER ] [ uid NUMBER ]\n"); + fprintf(stderr, " [ mark NUMBER ] [ vrf NAME ]\n"); + fprintf(stderr, " [ uid NUMBER ]\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"); fprintf(stderr, " [ table TABLE_ID ] [ proto RTPROTO ]\n"); fprintf(stderr, " [ scope SCOPE ] [ metric METRIC ]\n"); + fprintf(stderr, " [ ttl-propagate { enabled | disabled } ]\n"); fprintf(stderr, "INFO_SPEC := NH OPTIONS FLAGS [ nexthop NH ]...\n"); fprintf(stderr, "NH := [ encap ENCAPTYPE ENCAPHDR ] [ via [ FAMILY ] ADDRESS ]\n"); fprintf(stderr, " [ dev STRING ] [ weight NUMBER ] NHFLAGS\n"); fprintf(stderr, "FAMILY := [ inet | inet6 | ipx | dnet | mpls | bridge | link ]\n"); fprintf(stderr, "OPTIONS := FLAGS [ mtu NUMBER ] [ advmss NUMBER ] [ as [ to ] ADDRESS ]\n"); fprintf(stderr, " [ rtt TIME ] [ rttvar TIME ] [ reordering NUMBER ]\n"); - fprintf(stderr, " [ window NUMBER] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"); + fprintf(stderr, " [ window NUMBER ] [ cwnd NUMBER ] [ initcwnd NUMBER ]\n"); fprintf(stderr, " [ ssthresh NUMBER ] [ realms REALM ] [ src ADDRESS ]\n"); fprintf(stderr, " [ rto_min TIME ] [ hoplimit NUMBER ] [ initrwnd NUMBER ]\n"); fprintf(stderr, " [ features FEATURES ] [ quickack BOOL ] [ congctl NAME ]\n"); fprintf(stderr, " [ pref PREF ] [ expires TIME ]\n"); - fprintf(stderr, "TYPE := [ unicast | local | broadcast | multicast | throw |\n"); - fprintf(stderr, " unreachable | prohibit | blackhole | nat ]\n"); + fprintf(stderr, "TYPE := { unicast | local | broadcast | multicast | throw |\n"); + fprintf(stderr, " unreachable | prohibit | blackhole | nat }\n"); fprintf(stderr, "TABLE_ID := [ local | main | default | all | NUMBER ]\n"); fprintf(stderr, "SCOPE := [ host | link | global | NUMBER ]\n"); fprintf(stderr, "NHFLAGS := [ onlink | pervasive ]\n"); @@ -97,8 +100,11 @@ static void usage(void) fprintf(stderr, "TIME := NUMBER[s|ms]\n"); fprintf(stderr, "BOOL := [1|0]\n"); fprintf(stderr, "FEATURES := ecn\n"); - fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 ]\n"); - fprintf(stderr, "ENCAPHDR := [ MPLSLABEL ]\n"); + fprintf(stderr, "ENCAPTYPE := [ mpls | ip | ip6 | seg6 | seg6local ]\n"); + fprintf(stderr, "ENCAPHDR := [ MPLSLABEL | SEG6HDR ]\n"); + fprintf(stderr, "SEG6HDR := [ mode SEGMODE ] segs ADDR1,ADDRi,ADDRn [hmac HMACKEYID] [cleanup]\n"); + fprintf(stderr, "SEGMODE := [ encap | inline ]\n"); + fprintf(stderr, "ROUTE_GET_FLAGS := [ fibmatch ]\n"); exit(-1); } @@ -113,7 +119,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; @@ -131,7 +137,7 @@ static int flush_update(void) { if (rtnl_send_check(&rth, filter.flushb, filter.flushp) < 0) { perror("Failed to send flush request"); - return -1; + return -2; } filter.flushp = 0; return 0; @@ -140,10 +146,10 @@ static int flush_update(void) static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) { struct rtmsg *r = NLMSG_DATA(n); - inet_prefix dst; - inet_prefix src; - inet_prefix via; - inet_prefix prefsrc; + inet_prefix dst = { .family = r->rtm_family }; + inet_prefix src = { .family = r->rtm_family }; + inet_prefix via = { .family = r->rtm_family }; + inet_prefix prefsrc = { .family = r->rtm_family }; __u32 table; static int ip6_multiple_tables; @@ -178,7 +184,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; @@ -198,8 +205,10 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return 0; if (filter.rvia.family) { int family = r->rtm_family; + if (tb[RTA_VIA]) { struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + family = via->rtvia_family; } if (family != filter.rvia.family) @@ -208,31 +217,24 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) return 0; - memset(&dst, 0, sizeof(dst)); - dst.family = r->rtm_family; if (tb[RTA_DST]) memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len+7)/8); if (filter.rsrc.family || filter.msrc.family) { - memset(&src, 0, sizeof(src)); - src.family = r->rtm_family; if (tb[RTA_SRC]) memcpy(&src.data, RTA_DATA(tb[RTA_SRC]), (r->rtm_src_len+7)/8); } - if (filter.rvia.bitlen>0) { - memset(&via, 0, sizeof(via)); - via.family = r->rtm_family; + if (filter.rvia.bitlen > 0) { if (tb[RTA_GATEWAY]) memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len/8); if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *rtvia = RTA_DATA(tb[RTA_VIA]); + via.family = rtvia->rtvia_family; memcpy(&via.data, rtvia->rtvia_addr, len); } } - if (filter.rprefsrc.bitlen>0) { - memset(&prefsrc, 0, sizeof(prefsrc)); - prefsrc.family = r->rtm_family; + if (filter.rprefsrc.bitlen > 0) { if (tb[RTA_PREFSRC]) memcpy(&prefsrc.data, RTA_DATA(tb[RTA_PREFSRC]), host_len/8); } @@ -255,6 +257,7 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) return 0; if (filter.realmmask) { __u32 realms = 0; + if (tb[RTA_FLOW]) realms = rta_getattr_u32(tb[RTA_FLOW]); if ((realms^filter.realm)&filter.realmmask) @@ -262,22 +265,25 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) } if (filter.iifmask) { int iif = 0; + if (tb[RTA_IIF]) - iif = *(int*)RTA_DATA(tb[RTA_IIF]); + iif = rta_getattr_u32(tb[RTA_IIF]); if ((iif^filter.iif)&filter.iifmask) return 0; } if (filter.oifmask) { int oif = 0; + if (tb[RTA_OIF]) - oif = *(int*)RTA_DATA(tb[RTA_OIF]); + oif = rta_getattr_u32(tb[RTA_OIF]); if ((oif^filter.oif)&filter.oifmask) return 0; } if (filter.markmask) { int mark = 0; + if (tb[RTA_MARK]) - mark = *(int *)RTA_DATA(tb[RTA_MARK]); + mark = rta_getattr_u32(tb[RTA_MARK]); if ((mark ^ filter.mark) & filter.markmask) return 0; } @@ -286,7 +292,7 @@ static int filter_nlmsg(struct nlmsghdr *n, struct rtattr **tb, int host_len) r->rtm_dst_len == 0 && r->rtm_type == RTN_UNREACHABLE && tb[RTA_PRIORITY] && - *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1) + rta_getattr_u32(tb[RTA_PRIORITY]) == -1) return 0; return 1; @@ -297,30 +303,31 @@ static void print_rtax_features(FILE *fp, unsigned int features) unsigned int of = features; if (features & RTAX_FEATURE_ECN) { - fprintf(fp, " ecn"); + fprintf(fp, "ecn "); features &= ~RTAX_FEATURE_ECN; } if (features) - fprintf(fp, " 0x%x", of); + fprintf(fp, "0x%x ", of); } int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = (FILE *)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; - char abuf[256]; - int host_len; + struct rtattr *tb[RTA_MAX+1]; + int host_len, family; __u32 table; + int ret; + SPRINT_BUF(b1); static int hz; if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { fprintf(stderr, "Not a route: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); - return 0; + return -1; } if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) return 0; @@ -340,16 +347,17 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (filter.flushb) { struct nlmsghdr *fn; + if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) { - if (flush_update()) - return -1; + if ((ret = flush_update()) < 0) + return ret; } - fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp)); + fn = (struct nlmsghdr *)(filter.flushb + NLMSG_ALIGN(filter.flushp)); memcpy(fn, n, n->nlmsg_len); fn->nlmsg_type = RTM_DELROUTE; fn->nlmsg_flags = NLM_F_REQUEST; fn->nlmsg_seq = ++rth.seq; - filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb; + filter.flushp = (((char *)fn) + n->nlmsg_len) - filter.flushb; filter.flushed++; if (show_stats < 2) return 0; @@ -357,23 +365,19 @@ 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]) { + family = get_real_family(r->rtm_type, r->rtm_family); if (r->rtm_dst_len != host_len) { - fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_DST]), - RTA_DATA(tb[RTA_DST]), - abuf, sizeof(abuf)), - r->rtm_dst_len - ); + fprintf(fp, "%s/%u ", + rt_addr_n2a_rta(family, tb[RTA_DST]), + r->rtm_dst_len); } else { - fprintf(fp, "%s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_DST]), - RTA_DATA(tb[RTA_DST]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "%s ", + format_host_rta(family, tb[RTA_DST])); } } else if (r->rtm_dst_len) { fprintf(fp, "0/%d ", r->rtm_dst_len); @@ -381,29 +385,21 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "default "); } if (tb[RTA_SRC]) { + family = get_real_family(r->rtm_type, r->rtm_family); if (r->rtm_src_len != host_len) { - fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_SRC]), - RTA_DATA(tb[RTA_SRC]), - abuf, sizeof(abuf)), - r->rtm_src_len - ); + fprintf(fp, "from %s/%u ", + rt_addr_n2a_rta(family, tb[RTA_SRC]), + r->rtm_src_len); } else { - fprintf(fp, "from %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_SRC]), - RTA_DATA(tb[RTA_SRC]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "from %s ", + format_host_rta(family, tb[RTA_SRC])); } } else if (r->rtm_src_len) { fprintf(fp, "from 0/%u ", r->rtm_src_len); } if (tb[RTA_NEWDST]) { - fprintf(fp, "as to %s ", format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_NEWDST]), - RTA_DATA(tb[RTA_NEWDST]), - abuf, sizeof(abuf)) - ); + fprintf(fp, "as to %s ", + format_host_rta(r->rtm_family, tb[RTA_NEWDST])); } if (tb[RTA_ENCAP]) @@ -416,42 +412,36 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { fprintf(fp, "via %s ", - format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_GATEWAY]), - RTA_DATA(tb[RTA_GATEWAY]), - abuf, sizeof(abuf))); + format_host_rta(r->rtm_family, tb[RTA_GATEWAY])); } if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + fprintf(fp, "via %s %s ", family_name(via->rtvia_family), - format_host(via->rtvia_family, len, via->rtvia_addr, - abuf, sizeof(abuf))); + format_host(via->rtvia_family, len, via->rtvia_addr)); } if (tb[RTA_OIF] && filter.oifmask != -1) - fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); + fprintf(fp, "dev %s ", ll_index_to_name(rta_getattr_u32(tb[RTA_OIF]))); if (table && (table != RT_TABLE_MAIN || show_details > 0) && !filter.tb) - fprintf(fp, " table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); + fprintf(fp, "table %s ", rtnl_rttable_n2a(table, b1, sizeof(b1))); if (!(r->rtm_flags&RTM_F_CLONED)) { if ((r->rtm_protocol != RTPROT_BOOT || show_details > 0) && filter.protocolmask != -1) - fprintf(fp, " proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); + fprintf(fp, "proto %s ", rtnl_rtprot_n2a(r->rtm_protocol, b1, sizeof(b1))); if ((r->rtm_scope != RT_SCOPE_UNIVERSE || show_details > 0) && filter.scopemask != -1) - fprintf(fp, " scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); + fprintf(fp, "scope %s ", rtnl_rtscope_n2a(r->rtm_scope, b1, sizeof(b1))); } if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { /* Do not use format_host(). It is our local addr and symbolic name will not be useful. */ - fprintf(fp, " src %s ", - rt_addr_n2a(r->rtm_family, - RTA_PAYLOAD(tb[RTA_PREFSRC]), - RTA_DATA(tb[RTA_PREFSRC]), - abuf, sizeof(abuf))); + fprintf(fp, "src %s ", + rt_addr_n2a_rta(r->rtm_family, tb[RTA_PREFSRC])); } if (tb[RTA_PRIORITY]) - fprintf(fp, " metric %u ", rta_getattr_u32(tb[RTA_PRIORITY])); + fprintf(fp, "metric %u ", rta_getattr_u32(tb[RTA_PRIORITY])); if (r->rtm_flags & RTNH_F_DEAD) fprintf(fp, "dead "); if (r->rtm_flags & RTNH_F_ONLINK) @@ -464,19 +454,23 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "notify "); if (r->rtm_flags & RTNH_F_LINKDOWN) fprintf(fp, "linkdown "); + if (r->rtm_flags & RTNH_F_UNRESOLVED) + fprintf(fp, "unresolved "); if (tb[RTA_MARK]) { - unsigned int mark = *(unsigned int*)RTA_DATA(tb[RTA_MARK]); + unsigned int mark = rta_getattr_u32(tb[RTA_MARK]); + if (mark) { if (mark >= 16) - fprintf(fp, " mark 0x%x", mark); + fprintf(fp, "mark 0x%x ", mark); else - fprintf(fp, " mark %u", mark); + fprintf(fp, "mark %u ", mark); } } if (tb[RTA_FLOW] && filter.realmmask != ~0U) { __u32 to = rta_getattr_u32(tb[RTA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; fprintf(fp, "realm%s ", from ? "s" : ""); if (from) { @@ -496,7 +490,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "%s cache ", _SL_); -#define PRTFL(fl,flname) if (flags&RTCF_##fl) { \ +#define PRTFL(fl, flname) if (flags&RTCF_##fl) { \ flags &= ~RTCF_##fl; \ fprintf(fp, "%s" flname "%s", first ? "<" : "", flags ? "," : "> "); \ first = 0; } @@ -519,28 +513,30 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "%s%x> ", first ? "<" : "", flags); if (tb[RTA_CACHEINFO]) { struct rta_cacheinfo *ci = RTA_DATA(tb[RTA_CACHEINFO]); + if (!hz) hz = get_user_hz(); if (ci->rta_expires != 0) - fprintf(fp, " expires %dsec", ci->rta_expires/hz); + fprintf(fp, "expires %dsec ", ci->rta_expires/hz); if (ci->rta_error != 0) - fprintf(fp, " error %d", ci->rta_error); + fprintf(fp, "error %d ", ci->rta_error); if (show_stats) { if (ci->rta_clntref) - fprintf(fp, " users %d", ci->rta_clntref); + fprintf(fp, "users %d ", ci->rta_clntref); if (ci->rta_used != 0) - fprintf(fp, " used %d", ci->rta_used); + fprintf(fp, "used %d ", ci->rta_used); if (ci->rta_lastuse != 0) - fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + fprintf(fp, "age %dsec ", ci->rta_lastuse/hz); } if (ci->rta_id) - fprintf(fp, " ipid 0x%04x", ci->rta_id); + fprintf(fp, "ipid 0x%04x ", ci->rta_id); if (ci->rta_ts || ci->rta_tsage) - fprintf(fp, " ts 0x%x tsage %dsec", + fprintf(fp, "ts 0x%x tsage %dsec ", ci->rta_ts, ci->rta_tsage); } } else if (r->rtm_family == AF_INET6) { struct rta_cacheinfo *ci = NULL; + if (tb[RTA_CACHEINFO]) ci = RTA_DATA(tb[RTA_CACHEINFO]); if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { @@ -549,58 +545,58 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (r->rtm_flags & RTM_F_CLONED) fprintf(fp, "%s cache ", _SL_); if (ci->rta_expires) - fprintf(fp, " expires %dsec", ci->rta_expires/hz); + fprintf(fp, "expires %dsec ", ci->rta_expires/hz); if (ci->rta_error != 0) - fprintf(fp, " error %d", ci->rta_error); + fprintf(fp, "error %d ", ci->rta_error); if (show_stats) { if (ci->rta_clntref) - fprintf(fp, " users %d", ci->rta_clntref); + fprintf(fp, "users %d ", ci->rta_clntref); if (ci->rta_used != 0) - fprintf(fp, " used %d", ci->rta_used); + fprintf(fp, "used %d ", ci->rta_used); if (ci->rta_lastuse != 0) - fprintf(fp, " age %dsec", ci->rta_lastuse/hz); + fprintf(fp, "age %dsec ", ci->rta_lastuse/hz); } } else if (ci) { if (ci->rta_error != 0) - fprintf(fp, " error %d", ci->rta_error); + fprintf(fp, "error %d ", ci->rta_error); } } if (tb[RTA_METRICS]) { int i; - unsigned mxlock = 0; + unsigned int mxlock = 0; struct rtattr *mxrta[RTAX_MAX+1]; parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), RTA_PAYLOAD(tb[RTA_METRICS])); if (mxrta[RTAX_LOCK]) - mxlock = *(unsigned*)RTA_DATA(mxrta[RTAX_LOCK]); + mxlock = rta_getattr_u32(mxrta[RTAX_LOCK]); - for (i=2; i<= RTAX_MAX; i++) { - __u32 val; + for (i = 2; i <= RTAX_MAX; i++) { + __u32 val = 0U; - if (mxrta[i] == NULL) + if (mxrta[i] == NULL && !(mxlock & (1 << i))) continue; - if (i != RTAX_CC_ALGO) + if (mxrta[i] != NULL && i != RTAX_CC_ALGO) val = rta_getattr_u32(mxrta[i]); if (i == RTAX_HOPLIMIT && (int)val == -1) continue; - if (i < sizeof(mx_names)/sizeof(char*) && mx_names[i]) - fprintf(fp, " %s", mx_names[i]); + if (i < sizeof(mx_names)/sizeof(char *) && mx_names[i]) + fprintf(fp, "%s ", mx_names[i]); else - fprintf(fp, " metric %d", i); + fprintf(fp, "metric %d ", i); if (mxlock & (1<<i)) - fprintf(fp, " lock"); + fprintf(fp, "lock "); switch (i) { case RTAX_FEATURES: print_rtax_features(fp, val); break; default: - fprintf(fp, " %u", val); + fprintf(fp, "%u ", val); break; case RTAX_RTT: @@ -612,22 +608,23 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) val /= 4; if (val >= 1000) - fprintf(fp, " %gs", val/1e3); + fprintf(fp, "%gs ", val/1e3); else - fprintf(fp, " %ums", val); + fprintf(fp, "%ums ", val); break; case RTAX_CC_ALGO: - fprintf(fp, " %s", rta_getattr_str(mxrta[i])); + fprintf(fp, "%s ", rta_getattr_str(mxrta[i])); break; } } } if (tb[RTA_IIF] && filter.iifmask != -1) { - fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); + fprintf(fp, "iif %s ", + ll_index_to_name(rta_getattr_u32(tb[RTA_IIF]))); } if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); - int first = 0; + int first = 1; len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); @@ -637,12 +634,14 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (nh->rtnh_len > len) break; if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { - if (first) - fprintf(fp, " Oifs:"); - else + if (first) { + fprintf(fp, "Oifs: "); + first = 0; + } else { fprintf(fp, " "); + } } else - fprintf(fp, "%s\tnexthop", _SL_); + fprintf(fp, "%s\tnexthop ", _SL_); if (nh->rtnh_len > sizeof(*nh)) { parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); @@ -650,58 +649,67 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) lwt_print_encap(fp, tb[RTA_ENCAP_TYPE], tb[RTA_ENCAP]); - + if (tb[RTA_NEWDST]) { + fprintf(fp, "as to %s ", + format_host_rta(r->rtm_family, + tb[RTA_NEWDST])); + } if (tb[RTA_GATEWAY]) { - fprintf(fp, " via %s ", - format_host(r->rtm_family, - RTA_PAYLOAD(tb[RTA_GATEWAY]), - RTA_DATA(tb[RTA_GATEWAY]), - abuf, sizeof(abuf))); + fprintf(fp, "via %s ", + format_host_rta(r->rtm_family, + tb[RTA_GATEWAY])); } if (tb[RTA_VIA]) { size_t len = RTA_PAYLOAD(tb[RTA_VIA]) - 2; struct rtvia *via = RTA_DATA(tb[RTA_VIA]); + fprintf(fp, "via %s %s ", family_name(via->rtvia_family), - format_host(via->rtvia_family, len, via->rtvia_addr, - abuf, sizeof(abuf))); + format_host(via->rtvia_family, len, via->rtvia_addr)); } if (tb[RTA_FLOW]) { __u32 to = rta_getattr_u32(tb[RTA_FLOW]); __u32 from = to>>16; + to &= 0xFFFF; - fprintf(fp, " realm%s ", from ? "s" : ""); + fprintf(fp, "realm%s ", from ? "s" : ""); if (from) { fprintf(fp, "%s/", rtnl_rtrealm_n2a(from, b1, sizeof(b1))); } - fprintf(fp, "%s", + fprintf(fp, "%s ", rtnl_rtrealm_n2a(to, b1, sizeof(b1))); } } if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { - fprintf(fp, " %s", ll_index_to_name(nh->rtnh_ifindex)); + fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex)); if (nh->rtnh_hops != 1) fprintf(fp, "(ttl>%d)", nh->rtnh_hops); + fprintf(fp, " "); } else { - fprintf(fp, " dev %s", ll_index_to_name(nh->rtnh_ifindex)); - fprintf(fp, " weight %d", nh->rtnh_hops+1); + fprintf(fp, "dev %s ", ll_index_to_name(nh->rtnh_ifindex)); + if (r->rtm_family != AF_MPLS) + fprintf(fp, "weight %d ", + nh->rtnh_hops+1); } if (nh->rtnh_flags & RTNH_F_DEAD) - fprintf(fp, " dead"); + fprintf(fp, "dead "); if (nh->rtnh_flags & RTNH_F_ONLINK) - fprintf(fp, " onlink"); + fprintf(fp, "onlink "); if (nh->rtnh_flags & RTNH_F_PERVASIVE) - fprintf(fp, " pervasive"); + fprintf(fp, "pervasive "); + if (nh->rtnh_flags & RTNH_F_OFFLOAD) + fprintf(fp, "offload "); if (nh->rtnh_flags & RTNH_F_LINKDOWN) - fprintf(fp, " linkdown"); + fprintf(fp, "linkdown "); len -= NLMSG_ALIGN(nh->rtnh_len); nh = RTNH_NEXT(nh); } } if (tb[RTA_PREF]) { unsigned int pref = rta_getattr_u8(tb[RTA_PREF]); - fprintf(fp, " pref "); + + fprintf(fp, "pref "); switch (pref) { case ICMPV6_ROUTER_PREF_LOW: @@ -717,6 +725,13 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) fprintf(fp, "%u", pref); } } + if (tb[RTA_TTL_PROPAGATE]) { + fprintf(fp, "ttl-propagate "); + if (rta_getattr_u8(tb[RTA_TTL_PROPAGATE])) + fprintf(fp, "enabled"); + else + fprintf(fp, "disabled"); + } fprintf(fp, "\n"); fflush(fp); return 0; @@ -733,6 +748,7 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, if (strcmp(*argv, "via") == 0) { inet_prefix addr; int family; + NEXT_ARG(); family = read_family(*argv); if (family == AF_UNSPEC) @@ -747,16 +763,17 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen; } else { rta_addattr_l(rta, 4096, RTA_VIA, &addr.family, addr.bytelen+2); - rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen+2; + rtnh->rtnh_len += RTA_SPACE(addr.bytelen+2); } } else if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); if ((rtnh->rtnh_ifindex = ll_name_to_index(*argv)) == 0) { fprintf(stderr, "Cannot find device \"%s\"\n", *argv); - exit(1); + return -1; } } else if (strcmp(*argv, "weight") == 0) { - unsigned w; + unsigned int w; + NEXT_ARG(); if (get_unsigned(&w, *argv, 0) || w == 0 || w > 256) invarg("\"weight\" is invalid\n", *argv); @@ -765,6 +782,7 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, rtnh->rtnh_flags |= RTNH_F_ONLINK; } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("\"realm\" value is invalid\n", *argv); @@ -775,6 +793,16 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r, lwt_parse_encap(rta, 4096, &argc, &argv); rtnh->rtnh_len += rta->rta_len - len; + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + get_addr(&addr, *argv, r->rtm_family); + rta_addattr_l(rta, 4096, RTA_NEWDST, &addr.data, + addr.bytelen); + rtnh->rtnh_len += sizeof(struct rtattr) + addr.bytelen; } else break; } @@ -787,7 +815,7 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char **argv) { char buf[1024]; - struct rtattr *rta = (void*)buf; + struct rtattr *rta = (void *)buf; struct rtnexthop *rtnh; rta->rta_type = RTA_MULTIPATH; @@ -815,16 +843,23 @@ static int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, return 0; } -static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) +static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv) { struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST | flags, + .n.nlmsg_type = cmd, + .r.rtm_family = preferred_family, + .r.rtm_table = RT_TABLE_MAIN, + .r.rtm_scope = RT_SCOPE_NOWHERE, + }; char mxbuf[256]; - struct rtattr * mxrta = (void*)mxbuf; - unsigned mxlock = 0; + struct rtattr *mxrta = (void *)mxbuf; + unsigned int mxlock = 0; char *d = NULL; int gw_ok = 0; int dst_ok = 0; @@ -833,16 +868,6 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) int table_ok = 0; int raw = 0; int type_ok = 0; - static int hz; - - memset(&req, 0, sizeof(req)); - - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST|flags; - req.n.nlmsg_type = cmd; - req.r.rtm_family = preferred_family; - req.r.rtm_table = RT_TABLE_MAIN; - req.r.rtm_scope = RT_SCOPE_NOWHERE; if (cmd != RTM_DELROUTE) { req.r.rtm_protocol = RTPROT_BOOT; @@ -856,6 +881,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) while (argc > 0) { if (strcmp(*argv, "src") == 0) { inet_prefix addr; + NEXT_ARG(); get_addr(&addr, *argv, req.r.rtm_family); if (req.r.rtm_family == AF_UNSPEC) @@ -863,6 +889,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); } else if (strcmp(*argv, "as") == 0) { inet_prefix addr; + NEXT_ARG(); if (strcmp(*argv, "to") == 0) { NEXT_ARG(); @@ -874,6 +901,11 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) } else if (strcmp(*argv, "via") == 0) { inet_prefix addr; int family; + + if (gw_ok) { + invarg("use nexthop syntax to specify multiple via\n", + *argv); + } gw_ok = 1; NEXT_ARG(); family = read_family(*argv); @@ -885,11 +917,14 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (req.r.rtm_family == AF_UNSPEC) req.r.rtm_family = addr.family; if (addr.family == req.r.rtm_family) - addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); + addattr_l(&req.n, sizeof(req), RTA_GATEWAY, + &addr.data, addr.bytelen); else - addattr_l(&req.n, sizeof(req), RTA_VIA, &addr.family, addr.bytelen+2); + addattr_l(&req.n, sizeof(req), RTA_VIA, + &addr.family, addr.bytelen+2); } else if (strcmp(*argv, "from") == 0) { inet_prefix addr; + NEXT_ARG(); get_prefix(&addr, *argv, req.r.rtm_family); if (req.r.rtm_family == AF_UNSPEC) @@ -900,35 +935,38 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) } else if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("\"tos\" value is invalid\n", *argv); req.r.rtm_tos = tos; - } else if (strcmp(*argv, "expires") == 0 ) { + } else if (strcmp(*argv, "expires") == 0) { __u32 expires; + NEXT_ARG(); if (get_u32(&expires, *argv, 0)) invarg("\"expires\" value is invalid\n", *argv); - if (!hz) - hz = get_user_hz(); - addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires*hz); + addattr32(&req.n, sizeof(req), RTA_EXPIRES, expires); } else if (matches(*argv, "metric") == 0 || matches(*argv, "priority") == 0 || strcmp(*argv, "preference") == 0) { __u32 metric; + NEXT_ARG(); if (get_u32(&metric, *argv, 0)) invarg("\"metric\" value is invalid\n", *argv); addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric); } else if (strcmp(*argv, "scope") == 0) { __u32 scope = 0; + NEXT_ARG(); if (rtnl_rtscope_a2n(&scope, *argv)) invarg("invalid \"scope\" value\n", *argv); req.r.rtm_scope = scope; scope_ok = 1; } else if (strcmp(*argv, "mtu") == 0) { - unsigned mtu; + unsigned int mtu; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_MTU); @@ -938,7 +976,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"mtu\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); } else if (strcmp(*argv, "hoplimit") == 0) { - unsigned hoplimit; + unsigned int hoplimit; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_HOPLIMIT); @@ -948,7 +987,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"hoplimit\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit); } else if (strcmp(*argv, "advmss") == 0) { - unsigned mss; + unsigned int mss; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_ADVMSS); @@ -958,7 +998,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"mss\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss); } else if (matches(*argv, "reordering") == 0) { - unsigned reord; + unsigned int reord; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_REORDERING); @@ -968,7 +1009,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"reordering\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord); } else if (strcmp(*argv, "rtt") == 0) { - unsigned rtt; + unsigned int rtt; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_RTT); @@ -979,7 +1021,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT, (raw) ? rtt : rtt * 8); } else if (strcmp(*argv, "rto_min") == 0) { - unsigned rto_min; + unsigned int rto_min; + NEXT_ARG(); mxlock |= (1<<RTAX_RTO_MIN); if (get_time_rtt(&rto_min, *argv, &raw)) @@ -988,7 +1031,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN, rto_min); } else if (matches(*argv, "window") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_WINDOW); @@ -998,7 +1042,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"window\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win); } else if (matches(*argv, "cwnd") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_CWND); @@ -1008,7 +1053,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"cwnd\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win); } else if (matches(*argv, "initcwnd") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_INITCWND); @@ -1018,7 +1064,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) invarg("\"initcwnd\" value is invalid\n", *argv); rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win); } else if (matches(*argv, "initrwnd") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_INITRWND); @@ -1042,7 +1089,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features); } else if (matches(*argv, "quickack") == 0) { - unsigned quickack; + unsigned int quickack; + NEXT_ARG(); if (get_unsigned(&quickack, *argv, 0)) invarg("\"quickack\" value is invalid\n", *argv); @@ -1058,7 +1106,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr_l(mxrta, sizeof(mxbuf), RTAX_CC_ALGO, *argv, strlen(*argv)); } else if (matches(*argv, "rttvar") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_RTTVAR); @@ -1069,7 +1118,8 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR, (raw) ? win : win * 4); } else if (matches(*argv, "ssthresh") == 0) { - unsigned win; + unsigned int win; + NEXT_ARG(); if (strcmp(*argv, "lock") == 0) { mxlock |= (1<<RTAX_SSTHRESH); @@ -1080,6 +1130,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win); } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("\"realm\" value is invalid\n", *argv); @@ -1091,12 +1142,14 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) break; } else if (matches(*argv, "protocol") == 0) { __u32 prot; + NEXT_ARG(); if (rtnl_rtprot_a2n(&prot, *argv)) invarg("\"protocol\" value is invalid\n", *argv); req.r.rtm_protocol = prot; } else if (matches(*argv, "table") == 0) { __u32 tid; + NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) invarg("\"table\" value is invalid\n", *argv); @@ -1107,12 +1160,27 @@ static int iproute_modify(int cmd, unsigned 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(); d = *argv; } else if (matches(*argv, "pref") == 0) { __u8 pref; + NEXT_ARG(); if (strcmp(*argv, "low") == 0) pref = ICMPV6_ROUTER_PREF_LOW; @@ -1125,7 +1193,7 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) addattr8(&req.n, sizeof(req), RTA_PREF, pref); } else if (strcmp(*argv, "encap") == 0) { char buf[1024]; - struct rtattr *rta = (void*)buf; + struct rtattr *rta = (void *)buf; rta->rta_type = RTA_ENCAP; rta->rta_len = RTA_LENGTH(0); @@ -1134,6 +1202,20 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (rta->rta_len > RTA_LENGTH(0)) addraw_l(&req.n, 1024, RTA_DATA(rta), RTA_PAYLOAD(rta)); + } else if (strcmp(*argv, "ttl-propagate") == 0) { + __u8 ttl_prop; + + NEXT_ARG(); + if (matches(*argv, "enabled") == 0) + ttl_prop = 1; + else if (matches(*argv, "disabled") == 0) + ttl_prop = 0; + else + invarg("\"ttl-propagate\" value is invalid\n", + *argv); + + addattr8(&req.n, sizeof(req), RTA_TTL_PROPAGATE, + ttl_prop); } else { int type; inet_prefix dst; @@ -1166,16 +1248,14 @@ static int iproute_modify(int cmd, unsigned flags, int argc, char **argv) if (!dst_ok) usage(); - if (d || nhs_ok) { + if (d) { int idx; - if (d) { - if ((idx = ll_name_to_index(d)) == 0) { - fprintf(stderr, "Cannot find device \"%s\"\n", d); - return -1; - } - addattr32(&req.n, sizeof(req), RTA_OIF, idx); + if ((idx = ll_name_to_index(d)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", d); + return -1; } + addattr32(&req.n, sizeof(req), RTA_OIF, idx); } if (mxrta->rta_len > RTA_LENGTH(0)) { @@ -1231,22 +1311,17 @@ static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) struct { struct nlmsghdr nlh; struct rtmsg rtm; - } req; - struct sockaddr_nl nladdr; - - memset(&nladdr, 0, sizeof(nladdr)); - memset(&req, 0, sizeof(req)); - nladdr.nl_family = AF_NETLINK; - - req.nlh.nlmsg_len = sizeof(req); - req.nlh.nlmsg_type = RTM_GETROUTE; - req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; - req.nlh.nlmsg_pid = 0; - req.nlh.nlmsg_seq = rth->dump = ++rth->seq; - req.rtm.rtm_family = family; - req.rtm.rtm_flags |= RTM_F_CLONED; - - return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); + } req = { + .nlh.nlmsg_len = sizeof(req), + .nlh.nlmsg_type = RTM_GETROUTE, + .nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST, + .nlh.nlmsg_seq = rth->dump = ++rth->seq, + .rtm.rtm_family = family, + .rtm.rtm_flags = RTM_F_CLONED, + }; + struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; + + return sendto(rth->fd, (void *)&req, sizeof(req), 0, (struct sockaddr *)&nladdr, sizeof(nladdr)); } static int iproute_flush_cache(void) @@ -1254,19 +1329,19 @@ static int iproute_flush_cache(void) #define ROUTE_FLUSH_PATH "/proc/sys/net/ipv4/route/flush" int len; - int flush_fd = open (ROUTE_FLUSH_PATH, O_WRONLY); + int flush_fd = open(ROUTE_FLUSH_PATH, O_WRONLY); char *buffer = "-1"; if (flush_fd < 0) { - fprintf (stderr, "Cannot open \"%s\": %s\n", + fprintf(stderr, "Cannot open \"%s\": %s\n", ROUTE_FLUSH_PATH, strerror(errno)); return -1; } - len = strlen (buffer); + len = strlen(buffer); - if ((write (flush_fd, (void *)buffer, len)) < len) { - fprintf (stderr, "Cannot flush routing cache\n"); + if ((write(flush_fd, (void *)buffer, len)) < len) { + fprintf(stderr, "Cannot flush routing cache\n"); close(flush_fd); return -1; } @@ -1326,6 +1401,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) char *od = NULL; unsigned int mark = 0; rtnl_filter_t filter_fn; + int ret; if (action == IPROUTE_SAVE) { if (save_route_prep()) @@ -1346,6 +1422,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) while (argc > 0) { if (matches(*argv, "table") == 0) { __u32 tid; + NEXT_ARG(); if (rtnl_rttable_a2n(&tid, *argv)) { if (strcmp(*argv, "all") == 0) { @@ -1359,12 +1436,22 @@ 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<<RTN_BROADCAST); } else if (matches(*argv, "cached") == 0 || matches(*argv, "cloned") == 0) { filter.cloned = 1; } else if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("TOS value is invalid\n", *argv); @@ -1372,6 +1459,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) filter.tosmask = -1; } else if (matches(*argv, "protocol") == 0) { __u32 prot = 0; + NEXT_ARG(); filter.protocolmask = -1; if (rtnl_rtprot_a2n(&prot, *argv)) { @@ -1383,6 +1471,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) filter.protocol = prot; } else if (matches(*argv, "scope") == 0) { __u32 scope = 0; + NEXT_ARG(); filter.scopemask = -1; if (rtnl_rtscope_a2n(&scope, *argv)) { @@ -1394,11 +1483,11 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) filter.scope = scope; } else if (matches(*argv, "type") == 0) { 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<<type); } else if (strcmp(*argv, "dev") == 0 || strcmp(*argv, "oif") == 0) { NEXT_ARG(); @@ -1408,10 +1497,12 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) id = *argv; } else if (strcmp(*argv, "mark") == 0) { NEXT_ARG(); - get_unsigned(&mark, *argv, 0); + if (get_unsigned(&mark, *argv, 0)) + invarg("invalid mark value", *argv); filter.markmask = -1; } else if (strcmp(*argv, "via") == 0) { int family; + NEXT_ARG(); family = read_family(*argv); if (family == AF_UNSPEC) @@ -1424,6 +1515,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) get_prefix(&filter.rprefsrc, *argv, do_ipv6); } else if (matches(*argv, "realms") == 0) { __u32 realm; + NEXT_ARG(); if (get_rt_realms_or_raw(&realm, *argv)) invarg("invalid realms\n", *argv); @@ -1519,31 +1611,31 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) for (;;) { if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { perror("Cannot send dump request"); - exit(1); + return -2; } filter.flushed = 0; if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Flush terminated\n"); - exit(1); + return -2; } if (filter.flushed == 0) { if (show_stats) { if (round == 0 && (!filter.cloned || do_ipv6 == AF_INET6)) printf("Nothing to flush.\n"); else - printf("*** Flush is complete after %d round%s ***\n", round, round>1?"s":""); + printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":""); } fflush(stdout); return 0; } round++; - if (flush_update() < 0) - exit(1); + if ((ret = flush_update()) < 0) + return ret; if (time(0) - start > 30) { printf("\n*** Flush not completed after %ld seconds, %d entries remain ***\n", (long)(time(0) - start), filter.flushed); - exit(1); + return -1; } if (show_stats) { @@ -1556,21 +1648,21 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action) if (!filter.cloned) { if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { perror("Cannot send dump request"); - exit(1); + return -2; } } else { if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { perror("Cannot send dump request"); - exit(1); + return -2; } } if (rtnl_dump_filter(&rth, filter_fn, stdout) < 0) { fprintf(stderr, "Dump terminated\n"); - exit(1); + return -2; } - exit(0); + return 0; } @@ -1579,41 +1671,35 @@ static int iproute_get(int argc, char **argv) struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; - } req; + char buf[1024]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = preferred_family, + }; char *idev = NULL; char *odev = NULL; int connected = 0; + int fib_match = 0; int from_ok = 0; unsigned int mark = 0; - memset(&req, 0, sizeof(req)); - iproute_reset_filter(0); filter.cloned = 2; - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; - req.n.nlmsg_type = RTM_GETROUTE; - req.r.rtm_family = preferred_family; - req.r.rtm_table = 0; - req.r.rtm_protocol = 0; - req.r.rtm_scope = 0; - req.r.rtm_type = 0; - req.r.rtm_src_len = 0; - req.r.rtm_dst_len = 0; - req.r.rtm_tos = 0; - while (argc > 0) { if (strcmp(*argv, "tos") == 0 || matches(*argv, "dsfield") == 0) { __u32 tos; + NEXT_ARG(); if (rtnl_dsfield_a2n(&tos, *argv)) invarg("TOS value is invalid\n", *argv); req.r.rtm_tos = tos; } else if (matches(*argv, "from") == 0) { inet_prefix addr; + NEXT_ARG(); if (matches(*argv, "help") == 0) usage(); @@ -1629,7 +1715,8 @@ static int iproute_get(int argc, char **argv) idev = *argv; } else if (matches(*argv, "mark") == 0) { NEXT_ARG(); - get_unsigned(&mark, *argv, 0); + if (get_unsigned(&mark, *argv, 0)) + invarg("invalid mark value", *argv); } else if (matches(*argv, "oif") == 0 || strcmp(*argv, "dev") == 0) { NEXT_ARG(); @@ -1638,6 +1725,11 @@ static int iproute_get(int argc, char **argv) req.r.rtm_flags |= RTM_F_NOTIFY; } else if (matches(*argv, "connected") == 0) { connected = 1; + } else if (matches(*argv, "vrf") == 0) { + NEXT_ARG(); + if (!name_is_vrf(*argv)) + invarg("Invalid VRF\n", *argv); + odev = *argv; } else if (matches(*argv, "uid") == 0) { uid_t uid; @@ -1645,8 +1737,21 @@ static int iproute_get(int argc, char **argv) if (get_unsigned(&uid, *argv, 0)) invarg("invalid UID\n", *argv); addattr32(&req.n, sizeof(req), RTA_UID, uid); + } else if (matches(*argv, "fibmatch") == 0) { + fib_match = 1; + } else if (strcmp(*argv, "as") == 0) { + inet_prefix addr; + + NEXT_ARG(); + if (strcmp(*argv, "to") == 0) + NEXT_ARG(); + get_addr(&addr, *argv, req.r.rtm_family); + if (req.r.rtm_family == AF_UNSPEC) + req.r.rtm_family = addr.family; + addattr_l(&req.n, sizeof(req), RTA_NEWDST, &addr.data, addr.bytelen); } else { inet_prefix addr; + if (strcmp(*argv, "to") == 0) { NEXT_ARG(); } @@ -1664,7 +1769,7 @@ static int iproute_get(int argc, char **argv) if (req.r.rtm_dst_len == 0) { fprintf(stderr, "need at least a destination address\n"); - exit(1); + return -1; } if (idev || odev) { @@ -1692,6 +1797,8 @@ static int iproute_get(int argc, char **argv) req.r.rtm_family = AF_INET; req.r.rtm_flags |= RTM_F_LOOKUP_TABLE; + if (fib_match) + req.r.rtm_flags |= RTM_F_FIB_MATCH; if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) return -2; @@ -1699,11 +1806,11 @@ static int iproute_get(int argc, char **argv) if (connected && !from_ok) { struct rtmsg *r = NLMSG_DATA(&req.n); int len = req.n.nlmsg_len; - struct rtattr * tb[RTA_MAX+1]; + struct rtattr *tb[RTA_MAX+1]; - if (print_route(NULL, &req.n, (void*)stdout) < 0) { + if (print_route(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); - exit(1); + return -1; } if (req.n.nlmsg_type != RTM_NEWROUTE) { @@ -1740,7 +1847,7 @@ static int iproute_get(int argc, char **argv) return -2; } - if (print_route(NULL, &req.n, (void*)stdout) < 0) { + if (print_route(NULL, &req.n, (void *)stdout) < 0) { fprintf(stderr, "An error :-)\n"); return -1; } @@ -1748,12 +1855,42 @@ static int iproute_get(int argc, char **argv) return 0; } +static int rtattr_cmp(const struct rtattr *rta1, const struct rtattr *rta2) +{ + if (!rta1 || !rta2 || rta1->rta_len != rta2->rta_len) + return 1; + + return memcmp(RTA_DATA(rta1), RTA_DATA(rta2), RTA_PAYLOAD(rta1)); +} + static int restore_handler(const struct sockaddr_nl *nl, struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg) { - int ret; + struct rtmsg *r = NLMSG_DATA(n); + struct rtattr *tb[RTA_MAX+1]; + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + int ret, prio = *(int *)arg; + + parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); + + /* Restore routes in correct order: + * 0. ones for local addresses, + * 1. ones for local networks, + * 2. others (remote networks/hosts). + */ + if (!prio && !tb[RTA_GATEWAY] && (!tb[RTA_PREFSRC] || + !rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST]))) + goto restore; + else if (prio == 1 && !tb[RTA_GATEWAY] && tb[RTA_PREFSRC] && + rtattr_cmp(tb[RTA_PREFSRC], tb[RTA_DST])) + goto restore; + else if (prio == 2 && tb[RTA_GATEWAY]) + goto restore; + return 0; + +restore: n->nlmsg_flags |= NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; ll_init_map(&rth); @@ -1786,10 +1923,31 @@ static int route_dump_check_magic(void) static int iproute_restore(void) { + int pos, prio; + if (route_dump_check_magic()) - exit(-1); + return -1; + + pos = ftell(stdin); + if (pos == -1) { + perror("Failed to restore: ftell"); + return -1; + } + + for (prio = 0; prio < 3; prio++) { + int err; + + err = rtnl_from_file(stdin, &restore_handler, &prio); + if (err) + return -2; - exit(rtnl_from_file(stdin, &restore_handler, NULL)); + if (fseek(stdin, pos, SEEK_SET) == -1) { + perror("Failed to restore: fseek"); + return -1; + } + } + + return 0; } static int show_handler(const struct sockaddr_nl *nl, @@ -1803,9 +1961,12 @@ static int show_handler(const struct sockaddr_nl *nl, static int iproute_showdump(void) { if (route_dump_check_magic()) - exit(-1); + return -1; + + if (rtnl_from_file(stdin, &show_handler, NULL)) + return -2; - exit(rtnl_from_file(stdin, &show_handler, NULL)); + return 0; } void iproute_reset_filter(int ifindex) |