commit 9764c8d5063de9d76326f028fce66f6e4e49bb5a Author: Pavel Šimerda Date: Thu May 28 12:17:39 2015 +0200 backport selected ip-link/ip-address features and documentation diff --git a/ip/ip.c b/ip/ip.c index 86f8b45..7f2c206 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -22,8 +22,11 @@ #include "SNAPSHOT.h" #include "utils.h" #include "ip_common.h" +#include "namespace.h" int preferred_family = AF_UNSPEC; +int human_readable = 0; +int use_iec = 0; int show_stats = 0; int show_details = 0; int resolve_hosts = 0; @@ -33,6 +36,7 @@ char * _SL_ = NULL; int force = 0; int max_flush_loops = 10; int batch_mode = 0; +bool do_all = false; struct rtnl_handle rth = { .fd = -1 }; @@ -47,11 +51,12 @@ static void usage(void) " tunnel | tuntap | maddr | mroute | mrule | monitor | xfrm |\n" " netns | l2tp | tcp_metrics | token }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" +" -h[uman-readable] | -iec |\n" " -f[amily] { inet | inet6 | ipx | dnet | bridge | link } |\n" " -4 | -6 | -I | -D | -B | -0 |\n" " -l[oops] { maximum-addr-flush-attempts } |\n" " -o[neline] | -t[imestamp] | -b[atch] [filename] |\n" -" -rc[vbuf] [size]}\n"); +" -rc[vbuf] [size] | -n[etns] name | -a[ll] }\n"); exit(-1); } @@ -65,7 +70,7 @@ static const struct cmd { const char *cmd; int (*func)(int argc, char **argv); } cmds[] = { - { "address", do_ipaddr }, + { "address", do_ipaddr }, { "addrlabel", do_ipaddrlabel }, { "maddress", do_multiaddr }, { "route", do_iproute }, @@ -212,6 +217,11 @@ int main(int argc, char **argv) preferred_family = AF_DECnet; } else if (strcmp(opt, "-B") == 0) { preferred_family = AF_BRIDGE; + } else if (matches(opt, "-human") == 0 || + matches(opt, "-human-readable") == 0) { + ++human_readable; + } else if (matches(opt, "-iec") == 0) { + ++use_iec; } else if (matches(opt, "-stats") == 0 || matches(opt, "-statistics") == 0) { ++show_stats; @@ -253,6 +263,12 @@ int main(int argc, char **argv) rcvbuf = size; } else if (matches(opt, "-help") == 0) { usage(); + } else if (matches(opt, "-netns") == 0) { + NEXT_ARG(); + if (netns_switch(argv[1])) + exit(-1); + } else if (matches(opt, "-all") == 0) { + do_all = true; } else { fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt); exit(-1); diff --git a/ip/ipaddress.c b/ip/ipaddress.c index 5b9a438..c99a078 100644 --- a/ip/ipaddress.c +++ b/ip/ipaddress.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "rt_names.h" #include "utils.h" @@ -69,7 +70,7 @@ static void usage(void) } fprintf(stderr, "Usage: ip addr {add|change|replace} IFADDR dev STRING [ LIFETIME ]\n"); fprintf(stderr, " [ CONFFLAG-LIST ]\n"); - fprintf(stderr, " ip addr del IFADDR dev STRING\n"); + fprintf(stderr, " ip addr del IFADDR dev STRING [mngtmpaddr]\n"); fprintf(stderr, " ip addr {show|save|flush} [ dev STRING ] [ scope SCOPE-ID ]\n"); fprintf(stderr, " [ to PREFIX ] [ FLAG-LIST ] [ label PATTERN ] [up]\n"); fprintf(stderr, " ip addr {showdump|restore}\n"); @@ -82,7 +83,7 @@ static void usage(void) fprintf(stderr, " tentative | deprecated | dadfailed | temporary |\n"); fprintf(stderr, " CONFFLAG-LIST ]\n"); fprintf(stderr, "CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n"); - fprintf(stderr, "CONFFLAG := [ home | nodad ]\n"); + fprintf(stderr, "CONFFLAG := [ home | nodad | mngtmpaddr | noprefixroute ]\n"); fprintf(stderr, "LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n"); fprintf(stderr, "LFT := forever | SECONDS\n"); @@ -223,12 +224,36 @@ static void print_linktype(FILE *fp, struct rtattr *tb) } } +static void print_af_spec(FILE *fp, struct rtattr *af_spec_attr) +{ + struct rtattr *inet6_attr; + struct rtattr *tb[IFLA_INET6_MAX + 1]; + + inet6_attr = parse_rtattr_one_nested(AF_INET6, af_spec_attr); + if (!inet6_attr) + return; + + parse_rtattr_nested(tb, IFLA_INET6_MAX, inet6_attr); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + switch (rta_getattr_u8(tb[IFLA_INET6_ADDR_GEN_MODE])) { + case IN6_ADDR_GEN_MODE_EUI64: + fprintf(fp, "addrgenmode eui64 "); + break; + case IN6_ADDR_GEN_MODE_NONE: + fprintf(fp, "addrgenmode none "); + break; + } + } +} + static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) { struct ifla_vf_mac *vf_mac; struct ifla_vf_vlan *vf_vlan; struct ifla_vf_tx_rate *vf_tx_rate; struct ifla_vf_spoofchk *vf_spoofchk; + struct ifla_vf_link_state *vf_linkstate; struct rtattr *vf[IFLA_VF_MAX+1]; struct rtattr *tmp; SPRINT_BUF(b1); @@ -255,6 +280,20 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) else vf_spoofchk = RTA_DATA(vf[IFLA_VF_SPOOFCHK]); + if (vf_spoofchk) { + /* Check if the link state vf info type is supported by + * this kernel. + */ + tmp = (struct rtattr *)((char *)vf[IFLA_VF_SPOOFCHK] + + vf[IFLA_VF_SPOOFCHK]->rta_len); + + if (tmp->rta_type != IFLA_VF_LINK_STATE) + vf_linkstate = NULL; + else + vf_linkstate = RTA_DATA(vf[IFLA_VF_LINK_STATE]); + } else + vf_linkstate = NULL; + fprintf(fp, "\n vf %d MAC %s", vf_mac->vf, ll_addr_n2a((unsigned char *)&vf_mac->mac, ETH_ALEN, 0, b1, sizeof(b1))); @@ -270,99 +309,203 @@ static void print_vfinfo(FILE *fp, struct rtattr *vfinfo) else fprintf(fp, ", spoof checking off"); } + if (vf_linkstate) { + if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_AUTO) + fprintf(fp, ", link-state auto"); + else if (vf_linkstate->link_state == IFLA_VF_LINK_STATE_ENABLE) + fprintf(fp, ", link-state enable"); + else + fprintf(fp, ", link-state disable"); + } } -static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s) { - fprintf(fp, "%s", _SL_); +static void print_num(FILE *fp, unsigned width, uint64_t count) +{ + const char *prefix = "kMGTPE"; + const unsigned int base = use_iec ? 1024 : 1000; + uint64_t powi = 1; + uint16_t powj = 1; + uint8_t precision = 2; + char buf[64]; + + if (!human_readable || count < base) { + fprintf(fp, "%-*"PRIu64" ", width, count); + return; + } + + /* increase value by a factor of 1000/1024 and print + * if result is something a human can read */ + for(;;) { + powi *= base; + if (count / base < powi) + break; + + if (!prefix[1]) + break; + ++prefix; + } + + /* try to guess a good number of digits for precision */ + for (; precision > 0; precision--) { + powj *= 10; + if (count / powi < powj) + break; + } + + snprintf(buf, sizeof(buf), "%.*f%c%s", precision, + (double) count / powi, *prefix, use_iec ? "i" : ""); + + fprintf(fp, "%-*s ", width, buf); +} + +static void print_link_stats64(FILE *fp, const struct rtnl_link_stats64 *s, + const struct rtattr *carrier_changes) +{ + /* RX stats */ fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", s->rx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"", - (uint64_t)s->rx_bytes, - (uint64_t)s->rx_packets, - (uint64_t)s->rx_errors, - (uint64_t)s->rx_dropped, - (uint64_t)s->rx_over_errors, - (uint64_t)s->multicast); + + fprintf(fp, " "); + print_num(fp, 10, s->rx_bytes); + print_num(fp, 8, s->rx_packets); + print_num(fp, 7, s->rx_errors); + print_num(fp, 7, s->rx_dropped); + print_num(fp, 7, s->rx_over_errors); + print_num(fp, 7, s->multicast); if (s->rx_compressed) - fprintf(fp, " %-7"PRIu64"", - (uint64_t)s->rx_compressed); + print_num(fp, 7, s->rx_compressed); + + /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); - fprintf(fp, " %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"", - (uint64_t)s->rx_length_errors, - (uint64_t)s->rx_crc_errors, - (uint64_t)s->rx_frame_errors, - (uint64_t)s->rx_fifo_errors, - (uint64_t)s->rx_missed_errors); + fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + + fprintf(fp, " "); + print_num(fp, 8, s->rx_length_errors); + print_num(fp, 7, s->rx_crc_errors); + print_num(fp, 7, s->rx_frame_errors); + print_num(fp, 7, s->rx_fifo_errors); + print_num(fp, 7, s->rx_missed_errors); } fprintf(fp, "%s", _SL_); + + /* TX stats */ fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", - (uint64_t)s->tx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " %-10"PRIu64" %-8"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"", - (uint64_t)s->tx_bytes, - (uint64_t)s->tx_packets, - (uint64_t)s->tx_errors, - (uint64_t)s->tx_dropped, - (uint64_t)s->tx_carrier_errors, - (uint64_t)s->collisions); + s->tx_compressed ? "compressed" : "", _SL_); + + + fprintf(fp, " "); + print_num(fp, 10, s->tx_bytes); + print_num(fp, 8, s->tx_packets); + print_num(fp, 7, s->tx_errors); + print_num(fp, 7, s->tx_dropped); + print_num(fp, 7, s->tx_carrier_errors); + print_num(fp, 7, s->collisions); if (s->tx_compressed) - fprintf(fp, " %-7"PRIu64"", - (uint64_t)s->tx_compressed); + print_num(fp, 7, s->tx_compressed); + + /* TX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " TX errors: aborted fifo window heartbeat%s", _SL_); - fprintf(fp, " %-7"PRIu64" %-7"PRIu64" %-7"PRIu64" %-7"PRIu64"", - (uint64_t)s->tx_aborted_errors, - (uint64_t)s->tx_fifo_errors, - (uint64_t)s->tx_window_errors, - (uint64_t)s->tx_heartbeat_errors); + fprintf(fp, " TX errors: aborted fifo window heartbeat"); + if (carrier_changes) + fprintf(fp, " transns"); + fprintf(fp, "%s", _SL_); + + fprintf(fp, " "); + print_num(fp, 8, s->tx_aborted_errors); + print_num(fp, 7, s->tx_fifo_errors); + print_num(fp, 7, s->tx_window_errors); + print_num(fp, 7, s->tx_heartbeat_errors); + if (carrier_changes) + print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes)); } } -static void print_link_stats(FILE *fp, const struct rtnl_link_stats *s) +static void print_link_stats32(FILE *fp, const struct rtnl_link_stats *s, + const struct rtattr *carrier_changes) { - fprintf(fp, "%s", _SL_); + /* RX stats */ fprintf(fp, " RX: bytes packets errors dropped overrun mcast %s%s", s->rx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", - s->rx_bytes, s->rx_packets, s->rx_errors, - s->rx_dropped, s->rx_over_errors, - s->multicast - ); + + + fprintf(fp, " "); + print_num(fp, 10, s->rx_bytes); + print_num(fp, 8, s->rx_packets); + print_num(fp, 7, s->rx_errors); + print_num(fp, 7, s->rx_dropped); + print_num(fp, 7, s->rx_over_errors); + print_num(fp, 7, s->multicast); if (s->rx_compressed) - fprintf(fp, " %-7u", s->rx_compressed); + print_num(fp, 7, s->rx_compressed); + + /* RX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); - fprintf(fp, " %-7u %-7u %-7u %-7u %-7u", - s->rx_length_errors, - s->rx_crc_errors, - s->rx_frame_errors, - s->rx_fifo_errors, - s->rx_missed_errors - ); + fprintf(fp, " RX errors: length crc frame fifo missed%s", _SL_); + fprintf(fp, " "); + print_num(fp, 8, s->rx_length_errors); + print_num(fp, 7, s->rx_crc_errors); + print_num(fp, 7, s->rx_frame_errors); + print_num(fp, 7, s->rx_fifo_errors); + print_num(fp, 7, s->rx_missed_errors); } fprintf(fp, "%s", _SL_); + + /* TX stats */ fprintf(fp, " TX: bytes packets errors dropped carrier collsns %s%s", s->tx_compressed ? "compressed" : "", _SL_); - fprintf(fp, " %-10u %-8u %-7u %-7u %-7u %-7u", - s->tx_bytes, s->tx_packets, s->tx_errors, - s->tx_dropped, s->tx_carrier_errors, s->collisions); + + fprintf(fp, " "); + print_num(fp, 10, s->tx_bytes); + print_num(fp, 8, s->tx_packets); + print_num(fp, 7, s->tx_errors); + print_num(fp, 7, s->tx_dropped); + print_num(fp, 7, s->tx_carrier_errors); + print_num(fp, 7, s->collisions); if (s->tx_compressed) - fprintf(fp, " %-7u", s->tx_compressed); + print_num(fp, 7, s->tx_compressed); + + /* TX error stats */ if (show_stats > 1) { fprintf(fp, "%s", _SL_); - fprintf(fp, " TX errors: aborted fifo window heartbeat%s", _SL_); - fprintf(fp, " %-7u %-7u %-7u %-7u", - s->tx_aborted_errors, - s->tx_fifo_errors, - s->tx_window_errors, - s->tx_heartbeat_errors - ); + fprintf(fp, " TX errors: aborted fifo window heartbeat"); + if (carrier_changes) + fprintf(fp, " transns"); + fprintf(fp, "%s", _SL_); + + fprintf(fp, " "); + print_num(fp, 8, s->tx_aborted_errors); + print_num(fp, 7, s->tx_fifo_errors); + print_num(fp, 7, s->tx_window_errors); + print_num(fp, 7, s->tx_heartbeat_errors); + if (carrier_changes) + print_num(fp, 7, *(uint32_t*)RTA_DATA(carrier_changes)); } } +static void __print_link_stats(FILE *fp, struct rtattr **tb) +{ + if (tb[IFLA_STATS64]) + print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64]), + tb[IFLA_CARRIER_CHANGES]); + else if (tb[IFLA_STATS]) + print_link_stats32(fp, RTA_DATA(tb[IFLA_STATS]), + tb[IFLA_CARRIER_CHANGES]); +} + +static void print_link_stats(FILE *fp, struct nlmsghdr *n) +{ + struct ifinfomsg *ifi = NLMSG_DATA(n); + struct rtattr * tb[IFLA_MAX+1]; + + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); + __print_link_stats(fp, tb); + fprintf(fp, "%s", _SL_); +} + int print_linkinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { @@ -411,9 +554,13 @@ int print_linkinfo(const struct sockaddr_nl *who, if (iflink == 0) fprintf(fp, "@NONE: "); else { - fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); - m_flag = ll_index_to_flags(iflink); - m_flag = !(m_flag & IFF_UP); + if (tb[IFLA_LINK_NETNSID]) + fprintf(fp, "@if%d: ", iflink); + else { + fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); + m_flag = ll_index_to_flags(iflink); + m_flag = !(m_flag & IFF_UP); + } } } else { fprintf(fp, ": "); @@ -461,26 +608,36 @@ int print_linkinfo(const struct sockaddr_nl *who, } } - if (do_link && tb[IFLA_PROMISCUITY] && show_details) + if (tb[IFLA_LINK_NETNSID]) { + int id = *(int*)RTA_DATA(tb[IFLA_LINK_NETNSID]); + + if (id >= 0) + fprintf(fp, " link-netnsid %d", id); + else + fprintf(fp, " link-netnsid unknown"); + } + + if (tb[IFLA_PROMISCUITY] && show_details) fprintf(fp, " promiscuity %u ", *(int*)RTA_DATA(tb[IFLA_PROMISCUITY])); - if (do_link && tb[IFLA_LINKINFO] && show_details) + if (tb[IFLA_LINKINFO] && show_details) print_linktype(fp, tb[IFLA_LINKINFO]); - if (do_link && tb[IFLA_IFALIAS]) { + if (do_link && tb[IFLA_AF_SPEC] && show_details) + print_af_spec(fp, tb[IFLA_AF_SPEC]); + + if ((do_link || show_details) && tb[IFLA_IFALIAS]) { fprintf(fp, "%s alias %s", _SL_, rta_getattr_str(tb[IFLA_IFALIAS])); } if (do_link && show_stats) { - if (tb[IFLA_STATS64]) - print_link_stats64(fp, RTA_DATA(tb[IFLA_STATS64])); - else if (tb[IFLA_STATS]) - print_link_stats(fp, RTA_DATA(tb[IFLA_STATS])); + fprintf(fp, "%s", _SL_); + __print_link_stats(fp, tb); } - if (do_link && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) { + if ((do_link || show_details) && tb[IFLA_VFINFO_LIST] && tb[IFLA_NUM_VF]) { struct rtattr *i, *vflist = tb[IFLA_VFINFO_LIST]; int rem = RTA_PAYLOAD(vflist); for (i = RTA_DATA(vflist); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) @@ -489,7 +646,7 @@ int print_linkinfo(const struct sockaddr_nl *who, fprintf(fp, "\n"); fflush(fp); - return 0; + return 1; } static int flush_update(void) @@ -512,10 +669,17 @@ static int set_lifetime(unsigned int *lifetime, char *argv) return 0; } +static unsigned int get_ifa_flags(struct ifaddrmsg *ifa, + struct rtattr *ifa_flags_attr) +{ + return ifa_flags_attr ? rta_getattr_u32(ifa_flags_attr) : + ifa->ifa_flags; +} + int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { - FILE *fp = (FILE*)arg; + FILE *fp = arg; struct ifaddrmsg *ifa = NLMSG_DATA(n); int len = n->nlmsg_len; int deprecated = 0; @@ -536,7 +700,10 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, if (filter.flushb && n->nlmsg_type != RTM_NEWADDR) return 0; - parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), + n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + + ifa_flags = get_ifa_flags(ifa, rta_tb[IFA_FLAGS]); if (!rta_tb[IFA_LOCAL]) rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; @@ -547,7 +714,7 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, return 0; if ((filter.scope^ifa->ifa_scope)&filter.scopemask) return 0; - if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + if ((filter.flags ^ ifa_flags) & filter.flagmask) return 0; if (filter.label) { SPRINT_BUF(b1); @@ -613,7 +780,8 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, abuf, sizeof(abuf))); if (rta_tb[IFA_ADDRESS] == NULL || - memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { + memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), + ifa->ifa_family == AF_INET ? 4 : 16) == 0) { fprintf(fp, "/%d ", ifa->ifa_prefixlen); } else { fprintf(fp, " peer %s/%d ", @@ -640,36 +808,43 @@ int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n, abuf, sizeof(abuf))); } fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); - ifa_flags = ifa->ifa_flags; - if (ifa->ifa_flags&IFA_F_SECONDARY) { + if (ifa_flags & IFA_F_SECONDARY) { ifa_flags &= ~IFA_F_SECONDARY; if (ifa->ifa_family == AF_INET6) fprintf(fp, "temporary "); else fprintf(fp, "secondary "); } - if (ifa->ifa_flags&IFA_F_TENTATIVE) { + if (ifa_flags & IFA_F_TENTATIVE) { ifa_flags &= ~IFA_F_TENTATIVE; fprintf(fp, "tentative "); } - if (ifa->ifa_flags&IFA_F_DEPRECATED) { + if (ifa_flags & IFA_F_DEPRECATED) { ifa_flags &= ~IFA_F_DEPRECATED; deprecated = 1; fprintf(fp, "deprecated "); } - if (ifa->ifa_flags&IFA_F_HOMEADDRESS) { + if (ifa_flags & IFA_F_HOMEADDRESS) { ifa_flags &= ~IFA_F_HOMEADDRESS; fprintf(fp, "home "); } - if (ifa->ifa_flags&IFA_F_NODAD) { + if (ifa_flags & IFA_F_NODAD) { ifa_flags &= ~IFA_F_NODAD; fprintf(fp, "nodad "); } - if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { + if (ifa_flags & IFA_F_MANAGETEMPADDR) { + ifa_flags &= ~IFA_F_MANAGETEMPADDR; + fprintf(fp, "mngtmpaddr "); + } + if (ifa_flags & IFA_F_NOPREFIXROUTE) { + ifa_flags &= ~IFA_F_NOPREFIXROUTE; + fprintf(fp, "noprefixroute "); + } + if (!(ifa_flags & IFA_F_PERMANENT)) { fprintf(fp, "dynamic "); } else ifa_flags &= ~IFA_F_PERMANENT; - if (ifa->ifa_flags&IFA_F_DADFAILED) { + if (ifa_flags & IFA_F_DADFAILED) { ifa_flags &= ~IFA_F_DADFAILED; fprintf(fp, "dadfailed "); } @@ -896,6 +1071,8 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo) for (a = ainfo->head; a; a = a->next) { struct nlmsghdr *n = &a->h; struct ifaddrmsg *ifa = NLMSG_DATA(n); + struct rtattr *tb[IFA_MAX + 1]; + unsigned int ifa_flags; if (ifa->ifa_index != ifi->ifi_index) continue; @@ -904,11 +1081,13 @@ static void ipaddr_filter(struct nlmsg_chain *linfo, struct nlmsg_chain *ainfo) continue; if ((filter.scope^ifa->ifa_scope)&filter.scopemask) continue; - if ((filter.flags^ifa->ifa_flags)&filter.flagmask) + + parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); + ifa_flags = get_ifa_flags(ifa, tb[IFA_FLAGS]); + + if ((filter.flags ^ ifa_flags) & filter.flagmask) continue; if (filter.pfx.family || filter.label) { - struct rtattr *tb[IFA_MAX+1]; - parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); if (!tb[IFA_LOCAL]) tb[IFA_LOCAL] = tb[IFA_ADDRESS]; @@ -1084,6 +1263,12 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) } else if (strcmp(*argv, "nodad") == 0) { filter.flags |= IFA_F_NODAD; filter.flagmask |= IFA_F_NODAD; + } else if (strcmp(*argv, "mngtmpaddr") == 0) { + filter.flags |= IFA_F_MANAGETEMPADDR; + filter.flagmask |= IFA_F_MANAGETEMPADDR; + } else if (strcmp(*argv, "noprefixroute") == 0) { + filter.flags |= IFA_F_NOPREFIXROUTE; + filter.flagmask |= IFA_F_NOPREFIXROUTE; } else if (strcmp(*argv, "dadfailed") == 0) { filter.flags |= IFA_F_DADFAILED; filter.flagmask |= IFA_F_DADFAILED; @@ -1163,11 +1348,15 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action) } for (l = linfo.head; l; l = l->next) { - if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { + int res = 0; + + if (no_link || (res = print_linkinfo(NULL, &l->h, stdout)) >= 0) { struct ifinfomsg *ifi = NLMSG_DATA(&l->h); if (filter.family != AF_PACKET) print_selected_addrinfo(ifi->ifi_index, ainfo.head, stdout); + if (res > 0 && !do_link && show_stats) + print_link_stats(stdout, &l->h); } } fflush(stdout); @@ -1222,6 +1411,7 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv) __u32 preferred_lft = INFINITY_LIFE_TIME; __u32 valid_lft = INFINITY_LIFE_TIME; struct ifa_cacheinfo cinfo; + unsigned int ifa_flags = 0; memset(&req, 0, sizeof(req)); @@ -1299,9 +1489,13 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv) if (set_lifetime(&preferred_lft, *argv)) invarg("preferred_lft value", *argv); } else if (strcmp(*argv, "home") == 0) { - req.ifa.ifa_flags |= IFA_F_HOMEADDRESS; + ifa_flags |= IFA_F_HOMEADDRESS; } else if (strcmp(*argv, "nodad") == 0) { - req.ifa.ifa_flags |= IFA_F_NODAD; + ifa_flags |= IFA_F_NODAD; + } else if (strcmp(*argv, "mngtmpaddr") == 0) { + ifa_flags |= IFA_F_MANAGETEMPADDR; + } else if (strcmp(*argv, "noprefixroute") == 0) { + ifa_flags |= IFA_F_NOPREFIXROUTE; } else { if (strcmp(*argv, "local") == 0) { NEXT_ARG(); @@ -1319,6 +1513,11 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv) } argc--; argv++; } + if (ifa_flags <= 0xff) + req.ifa.ifa_flags = ifa_flags; + else + addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags); + if (d == NULL) { fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); return -1; diff --git a/ip/iplink.c b/ip/iplink.c index dc98019..e17d5df 100644 --- a/ip/iplink.c +++ b/ip/iplink.c @@ -31,6 +31,7 @@ #include "rt_names.h" #include "utils.h" #include "ip_common.h" +#include "namespace.h" #define IPLINK_IOCTL_COMPAT 1 #ifndef LIBDIR @@ -70,6 +71,7 @@ void iplink_usage(void) fprintf(stderr, " [ mtu MTU ]\n"); fprintf(stderr, " [ netns PID ]\n"); fprintf(stderr, " [ netns NAME ]\n"); + fprintf(stderr, " [ link-netnsid ID ]\n"); fprintf(stderr, " [ alias NAME ]\n"); fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n"); fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n"); @@ -77,8 +79,11 @@ void iplink_usage(void) fprintf(stderr, " [ rate TXRATE ] ] \n"); fprintf(stderr, " [ spoofchk { on | off} ] ] \n"); + fprintf(stderr, " [ query_rss { on | off} ] ] \n"); + fprintf(stderr, " [ state { auto | enable | disable} ] ]\n"); fprintf(stderr, " [ master DEVICE ]\n"); fprintf(stderr, " [ nomaster ]\n"); + fprintf(stderr, " [ addrgenmode { eui64 | none } ]\n"); fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up]\n"); if (iplink_have_newlink()) { @@ -144,6 +149,15 @@ static int get_link_mode(const char *mode) return -1; } +static int get_addr_gen_mode(const char *mode) +{ + if (strcasecmp(mode, "eui64") == 0) + return IN6_ADDR_GEN_MODE_EUI64; + if (strcasecmp(mode, "none") == 0) + return IN6_ADDR_GEN_MODE_NONE; + return -1; +} + #if IPLINK_IOCTL_COMPAT static int have_rtnl_newlink = -1; @@ -176,8 +190,13 @@ static int iplink_have_newlink(void) req.n.nlmsg_type = RTM_NEWLINK; req.i.ifi_family = AF_UNSPEC; - rtnl_send(&rth, &req.n, req.n.nlmsg_len); - rtnl_listen(&rth, accept_msg, NULL); + if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { + perror("Could not check for " + "link configuration over netlink support"); + have_rtnl_newlink = 0; + } else { + rtnl_listen(&rth, accept_msg, NULL); + } } return have_rtnl_newlink; } @@ -254,7 +273,44 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp, invarg("Invalid \"spoofchk\" value\n", *argv); ivs.vf = vf; addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); + } else if (matches(*argv, "state") == 0) { + struct ifla_vf_link_state ivl; + NEXT_ARG(); + if (matches(*argv, "auto") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_AUTO; + else if (matches(*argv, "enable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; + else if (matches(*argv, "disable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; + else + invarg("Invalid \"state\" value\n", *argv); + ivl.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl)); + } else if (matches(*argv, "query_rss") == 0) { + struct ifla_vf_rss_query_en ivs; + NEXT_ARG(); + if (matches(*argv, "on") == 0) + ivs.setting = 1; + else if (matches(*argv, "off") == 0) + ivs.setting = 0; + else + invarg("Invalid \"query_rss\" value\n", *argv); + ivs.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs)); + } else if (matches(*argv, "state") == 0) { + struct ifla_vf_link_state ivl; + NEXT_ARG(); + if (matches(*argv, "auto") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_AUTO; + else if (matches(*argv, "enable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; + else if (matches(*argv, "disable") == 0) + ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; + else + invarg("Invalid \"state\" value\n", *argv); + ivl.vf = vf; + addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl)); } else { /* rewind arg */ PREV_ARG(); @@ -284,6 +340,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, int vf = -1; int numtxqueues = -1; int numrxqueues = -1; + int link_netnsid = -1; *group = -1; ret = argc; @@ -334,7 +391,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, NEXT_ARG(); if (netns != -1) duparg("netns", *argv); - if ((netns = get_netns_fd(*argv)) >= 0) + if ((netns = netns_get_fd(*argv)) >= 0) addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4); else if (get_integer(&netns, *argv, 0) == 0) addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4); @@ -466,6 +523,26 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req, invarg("Invalid \"numrxqueues\" value\n", *argv); addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, &numrxqueues, 4); + } else if (matches(*argv, "addrgenmode") == 0) { + struct rtattr *afs, *afs6; + int mode; + NEXT_ARG(); + mode = get_addr_gen_mode(*argv); + if (mode < 0) + invarg("Invalid address generation mode\n", *argv); + afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC); + afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6); + addattr8(&req->n, sizeof(*req), IFLA_INET6_ADDR_GEN_MODE, mode); + addattr_nest_end(&req->n, afs6); + addattr_nest_end(&req->n, afs); + } else if (matches(*argv, "link-netnsid") == 0) { + NEXT_ARG(); + if (link_netnsid != -1) + duparg("link-netnsid", *argv); + if (get_integer(&link_netnsid, *argv, 0)) + invarg("Invalid \"link-netnsid\" value\n", *argv); + addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, + link_netnsid); } else { if (strcmp(*argv, "dev") == 0) { NEXT_ARG(); diff --git a/ip/iplink_vxlan.c b/ip/iplink_vxlan.c index 1025326..43b8abc 100644 --- a/ip/iplink_vxlan.c +++ b/ip/iplink_vxlan.c @@ -23,11 +23,12 @@ static void explain(void) { - fprintf(stderr, "Usage: ... vxlan id VNI [ group ADDR ] [ local ADDR ]\n"); + fprintf(stderr, "Usage: ... vxlan id VNI [ { group | remote } ADDR ] [ local ADDR ]\n"); fprintf(stderr, " [ ttl TTL ] [ tos TOS ] [ dev PHYS_DEV ]\n"); - fprintf(stderr, " [ port MIN MAX ] [ [no]learning ]\n"); - fprintf(stderr, " [ [no]proxy ] [ [no]rsc ]\n"); + fprintf(stderr, " [ dstport PORT ] [ srcport MIN MAX ]\n"); + fprintf(stderr, " [ [no]learning ] [ [no]proxy ] [ [no]rsc ]\n"); fprintf(stderr, " [ [no]l2miss ] [ [no]l3miss ]\n"); + fprintf(stderr, " [ gbp ]\n"); fprintf(stderr, "\n"); fprintf(stderr, "Where: VNI := 0-16777215\n"); fprintf(stderr, " ADDR := { IP_ADDRESS | any }\n"); @@ -42,6 +43,10 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, int vni_set = 0; __u32 saddr = 0; __u32 gaddr = 0; + __u32 daddr = 0; + struct in6_addr saddr6 = IN6ADDR_ANY_INIT; + struct in6_addr gaddr6 = IN6ADDR_ANY_INIT; + struct in6_addr daddr6 = IN6ADDR_ANY_INIT; unsigned link = 0; __u8 tos = 0; __u8 ttl = 0; @@ -53,6 +58,12 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, __u8 noage = 0; __u32 age = 0; __u32 maxaddr = 0; + __u16 dstport = 0; + __u8 gbp = 0; + __u8 udpcsum = 0; + __u8 udp6zerocsumtx = 0; + __u8 udp6zerocsumrx = 0; + int dst_port_set = 0; struct ifla_vxlan_port_range range = { 0, 0 }; while (argc > 0) { @@ -65,15 +76,30 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, vni_set = 1; } else if (!matches(*argv, "group")) { NEXT_ARG(); - gaddr = get_addr32(*argv); - - if (!IN_MULTICAST(ntohl(gaddr))) - invarg("invald group address", *argv); + if (!inet_get_addr(*argv, &gaddr, &gaddr6)) { + fprintf(stderr, "Invalid address \"%s\"\n", *argv); + return -1; + } + if (!IN6_IS_ADDR_MULTICAST(&gaddr6) && !IN_MULTICAST(ntohl(gaddr))) + invarg("invalid group address", *argv); + } else if (!matches(*argv, "remote")) { + NEXT_ARG(); + if (!inet_get_addr(*argv, &daddr, &daddr6)) { + fprintf(stderr, "Invalid address \"%s\"\n", *argv); + return -1; + } + if (IN6_IS_ADDR_MULTICAST(&daddr6) || IN_MULTICAST(ntohl(daddr))) + invarg("invalid remote address", *argv); } else if (!matches(*argv, "local")) { NEXT_ARG(); - if (strcmp(*argv, "any")) - saddr = get_addr32(*argv); - if (IN_MULTICAST(ntohl(saddr))) + if (strcmp(*argv, "any")) { + if (!inet_get_addr(*argv, &saddr, &saddr6)) { + fprintf(stderr, "Invalid address \"%s\"\n", *argv); + return -1; + } + } + + if (IN_MULTICAST(ntohl(saddr)) || IN6_IS_ADDR_MULTICAST(&saddr6)) invarg("invalid local address", *argv); } else if (!matches(*argv, "dev")) { NEXT_ARG(); @@ -115,7 +141,8 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, maxaddr = 0; else if (get_u32(&maxaddr, *argv, 0)) invarg("max addresses", *argv); - } else if (!matches(*argv, "port")) { + } else if (!matches(*argv, "port") || + !matches(*argv, "srcport")) { __u16 minport, maxport; NEXT_ARG(); if (get_u16(&minport, *argv, 0)) @@ -125,6 +152,11 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, invarg("max port", *argv); range.low = htons(minport); range.high = htons(maxport); + } else if (!matches(*argv, "dstport")){ + NEXT_ARG(); + if (get_u16(&dstport, *argv, 0)) + invarg("dst port", *argv); + dst_port_set = 1; } else if (!matches(*argv, "nolearning")) { learning = 0; } else if (!matches(*argv, "learning")) { @@ -145,6 +177,20 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, l3miss = 0; } else if (!matches(*argv, "l3miss")) { l3miss = 1; + } else if (!matches(*argv, "gbp")) { + gbp = 1; + } else if (!matches(*argv, "udpcsum")) { + udpcsum = 1; + } else if (!matches(*argv, "noudpcsum")) { + udpcsum = 0; + } else if (!matches(*argv, "udp6zerocsumtx")) { + udp6zerocsumtx = 1; + } else if (!matches(*argv, "noudp6zerocsumtx")) { + udp6zerocsumtx = 0; + } else if (!matches(*argv, "udp6zerocsumrx")) { + udp6zerocsumrx = 1; + } else if (!matches(*argv, "noudp6zerocsumrx")) { + udp6zerocsumrx = 0; } else if (matches(*argv, "help") == 0) { explain(); return -1; @@ -160,11 +206,35 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, fprintf(stderr, "vxlan: missing virtual network identifier\n"); return -1; } + + if (!dst_port_set) { + fprintf(stderr, "vxlan: destination port not specified\n" + "Will use Linux kernel default (non-standard value)\n"); + fprintf(stderr, + "Use 'dstport 4789' to get the IANA assigned value\n" + "Use 'dstport 0' to get default and quiet this message\n"); + } + if ((gaddr && daddr) || + (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) && + memcmp(&daddr6, &in6addr_any, sizeof(daddr6)))) { + fprintf(stderr, "vxlan: both group and remote cannot be specified\n"); + return -1; + } addattr32(n, 1024, IFLA_VXLAN_ID, vni); if (gaddr) addattr_l(n, 1024, IFLA_VXLAN_GROUP, &gaddr, 4); + else if (daddr) + addattr_l(n, 1024, IFLA_VXLAN_GROUP, &daddr, 4); + if (memcmp(&gaddr6, &in6addr_any, sizeof(gaddr6)) != 0) + addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &gaddr6, sizeof(struct in6_addr)); + else if (memcmp(&daddr6, &in6addr_any, sizeof(daddr6)) != 0) + addattr_l(n, 1024, IFLA_VXLAN_GROUP6, &daddr6, sizeof(struct in6_addr)); + if (saddr) addattr_l(n, 1024, IFLA_VXLAN_LOCAL, &saddr, 4); + else if (memcmp(&saddr6, &in6addr_any, sizeof(saddr6)) != 0) + addattr_l(n, 1024, IFLA_VXLAN_LOCAL6, &saddr6, sizeof(struct in6_addr)); + if (link) addattr32(n, 1024, IFLA_VXLAN_LINK, link); addattr8(n, 1024, IFLA_VXLAN_TTL, ttl); @@ -174,6 +244,10 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, addattr8(n, 1024, IFLA_VXLAN_RSC, rsc); addattr8(n, 1024, IFLA_VXLAN_L2MISS, l2miss); addattr8(n, 1024, IFLA_VXLAN_L3MISS, l3miss); + addattr8(n, 1024, IFLA_VXLAN_UDP_CSUM, udpcsum); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, udp6zerocsumtx); + addattr8(n, 1024, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, udp6zerocsumrx); + if (noage) addattr32(n, 1024, IFLA_VXLAN_AGEING, 0); else if (age) @@ -184,6 +258,13 @@ static int vxlan_parse_opt(struct link_util *lu, int argc, char **argv, addattr_l(n, 1024, IFLA_VXLAN_PORT_RANGE, &range, sizeof(range)); + if (dstport) + addattr16(n, 1024, IFLA_VXLAN_PORT, htons(dstport)); + + if (gbp) + addattr_l(n, 1024, IFLA_VXLAN_GBP, NULL, 0); + + return 0; } @@ -208,9 +289,25 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_VXLAN_GROUP]) { __be32 addr = rta_getattr_u32(tb[IFLA_VXLAN_GROUP]); - if (addr) - fprintf(f, "group %s ", - format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + if (addr) { + if (IN_MULTICAST(ntohl(addr))) + fprintf(f, "group %s ", + format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + else + fprintf(f, "remote %s ", + format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + } + } else if (tb[IFLA_VXLAN_GROUP6]) { + struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_GROUP6]), sizeof(struct in6_addr)); + if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) { + if (IN6_IS_ADDR_MULTICAST(&addr)) + fprintf(f, "group %s ", + format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); + else + fprintf(f, "remote %s ", + format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); + } } if (tb[IFLA_VXLAN_LOCAL]) { @@ -218,6 +315,12 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (addr) fprintf(f, "local %s ", format_host(AF_INET, 4, &addr, s1, sizeof(s1))); + } else if (tb[IFLA_VXLAN_LOCAL6]) { + struct in6_addr addr; + memcpy(&addr, RTA_DATA(tb[IFLA_VXLAN_LOCAL6]), sizeof(struct in6_addr)); + if (memcmp(&addr, &in6addr_any, sizeof(addr)) != 0) + fprintf(f, "local %s ", + format_host(AF_INET6, sizeof(struct in6_addr), &addr, s1, sizeof(s1))); } if (tb[IFLA_VXLAN_LINK] && @@ -233,9 +336,13 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_VXLAN_PORT_RANGE]) { const struct ifla_vxlan_port_range *r = RTA_DATA(tb[IFLA_VXLAN_PORT_RANGE]); - fprintf(f, "port %u %u ", ntohs(r->low), ntohs(r->high)); + fprintf(f, "srcport %u %u ", ntohs(r->low), ntohs(r->high)); } + if (tb[IFLA_VXLAN_PORT]) + fprintf(f, "dstport %u ", + ntohs(rta_getattr_u16(tb[IFLA_VXLAN_PORT]))); + if (tb[IFLA_VXLAN_LEARNING] && !rta_getattr_u8(tb[IFLA_VXLAN_LEARNING])) fputs("nolearning ", f); @@ -277,6 +384,20 @@ static void vxlan_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[]) if (tb[IFLA_VXLAN_LIMIT] && (maxaddr = rta_getattr_u32(tb[IFLA_VXLAN_LIMIT]) != 0)) fprintf(f, "maxaddr %u ", maxaddr); + + if (tb[IFLA_VXLAN_UDP_CSUM] && rta_getattr_u8(tb[IFLA_VXLAN_UDP_CSUM])) + fputs("udpcsum ", f); + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX] && + rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_TX])) + fputs("udp6zerocsumtx ", f); + + if (tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX] && + rta_getattr_u8(tb[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) + fputs("udp6zerocsumrx ", f); + + if (tb[IFLA_VXLAN_GBP]) + fputs("gbp ", f); } struct link_util vxlan_link_util = { diff --git a/lib/libnetlink.c b/lib/libnetlink.c index 9e2a795..baac6ae 100644 --- a/lib/libnetlink.c +++ b/lib/libnetlink.c @@ -701,6 +701,18 @@ int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int l return i; } +struct rtattr *parse_rtattr_one(int type, struct rtattr *rta, int len) +{ + while (RTA_OK(rta, len)) { + if (rta->rta_type == type) + return rta; + rta = RTA_NEXT(rta, len); + } + if (len) + fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); + return NULL; +} + int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len) { diff --git a/man/man8/ip-link.8.in b/man/man8/ip-link.8.in index 86e0bc9..a015b82 100644 --- a/man/man8/ip-link.8.in +++ b/man/man8/ip-link.8.in @@ -16,6 +16,7 @@ ip-link \- network device configuration .ti -8 .IR OPTIONS " := { " \fB\-V\fR[\fIersion\fR] | +\fB\-h\fR[\fIuman-readable\fR] | \fB\-s\fR[\fItatistics\fR] | \fB\-r\fR[\fIesolve\fR] | \fB\-f\fR[\fIamily\fR] { @@ -120,9 +121,11 @@ ip-link \- network device configuration ] | .br .B master -.IR DEVICE +.IR DEVICE " |" .br -.B nomaster +.B nomaster " |" +.br +.B addrgenmode { eui64 | none } .BR " }" @@ -197,6 +200,65 @@ specifies the number of transmit queues for new device. specifies the number of receive queues for new device. .TP +VLAN Type Support +For a link of type +.I VLAN +the following additional arguments are supported: + +.BI "ip link add link " DEVICE +.BI name " NAME " +.BI type " vlan " +.R " [ " +.BI protocol " VLAN_PROTO " +.R " ] " +.BI id " VLANID " +.R " [ " +.BR reorder_hdr " { " on " | " off " } " +.R " ] " +.R " [ " +.BR gvrp " { " on " | " off " } " +.R " ] " +.R " [ " +.BR mvrp " { " on " | " off " } " +.R " ] " +.R " [ " +.BR loose_binding " { " on " | " off " } " +.R " ] " +.R " [ " +.BI ingress-qos-map " QOS-MAP " +.R " ] " +.R " [ " +.BI egress-qos-map " QOS-MAP " +.R " ] " + +.in +8 +.sp +.BI protocol " VLAN_PROTO " +- either 802.1Q or 802.1ad. + +.BI id " VLANID " +- specifies the VID. + +.BR reorder_hdr " { " on " | " off " } " +- specifies whether ethernet headers are reordered or not. + +.BR gvrp " { " on " | " off " } " +- specifies whether this VLAN should be registered using GARP VLAN Registration Protocol. + +.BR mvrp " { " on " | " off " } " +- specifies whether this VLAN should be registered using Multiple VLAN Registration Protocol. + +.BR loose_binding " { " on " | " off " } " +- specifies whether the VLAN device state is bound to the physical device state. + +.BI ingress-qos-map " QOS-MAP " +- defines a mapping between priority code points on incoming frames. The format is FROM:TO with multiple mappings separated by spaces. + +.BI egress-qos-map " QOS-MAP " +- the same as ingress-qos-map but for outgoing frames. +.in -8 + +.TP VXLAN Type Support For a link of type .I VXLAN @@ -206,8 +268,8 @@ the following additional arguments are supported: .BI type " vxlan " id " ID .R " [ " .BI dev " PHYS_DEV " -.R " ] [ " -.BI group " IPADDR " +.RB " ] [ { " group " | " remote " } " +.I IPADDR .R " ] [ " .BI local " IPADDR " .R " ] [ " @@ -226,6 +288,8 @@ the following additional arguments are supported: .I "[no]l2miss " .R " ] [ " .I "[no]l3miss " +.R " ] [ " +.B gbp .R " ]" .in +8 @@ -240,6 +304,17 @@ Identifier) to use. .sp .BI group " IPADDR" - specifies the multicast IP address to join. +This parameter cannot be specified with the +.B remote +parameter. + +.sp +.BI remote " IPADDR" +- specifies the unicast destination IP address to use in outgoing packets +when the destination link layer address is not known in the VXLAN device +forwarding database. This parameter cannot be specified with the +.B group +parameter. .sp .BI local " IPADDR" @@ -279,6 +354,49 @@ are entered into the VXLAN device forwarding database. .I [no]l3miss - specifies if netlink IP ADDR miss notifications are generated. +.sp +.B gbp +- enables the Group Policy extension (VXLAN-GBP). + +.in +4 +Allows to transport group policy context across VXLAN network peers. +If enabled, includes the mark of a packet in the VXLAN header for outgoing +packets and fills the packet mark based on the information found in the +VXLAN header for incomming packets. + +Format of upper 16 bits of packet mark (flags); + +.in +2 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.br +|-|-|-|-|-|-|-|-|-|D|-|-|A|-|-|-| +.br ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +.B D := +Don't Learn bit. When set, this bit indicates that the egress +VTEP MUST NOT learn the source address of the encapsulated frame. + +.B A := +Indicates that the group policy has already been applied to +this packet. Policies MUST NOT be applied by devices when the A bit is set. +.in -2 + +Format of lower 16 bits of packet mark (policy ID): + +.in +2 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.br +| Group Policy ID | +.br ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +.in -2 + +Example: + iptables -A OUTPUT [...] -j MARK --set-mark 0x800FF + +.in -4 + .in -8 .SS ip link delete - delete virtual link @@ -366,14 +484,29 @@ the interface is .IR "POINTOPOINT" . .TP -.BI netns " PID" -move the device to the network namespace associated with the process -.IR "PID". - -.TP -.BI netns " NETNSNAME" +.BI netns " NETNSNAME " \fR| " PID" move the device to the network namespace associated with name -.IR "NETNSNAME". +.IR "NETNSNAME " or +.RI process " PID". + +Some devices are not allowed to change network namespace: loopback, bridge, +ppp, wireless. These are network namespace local devices. In such case +.B ip +tool will return "Invalid argument" error. It is possible to find out if device is local +to a single network namespace by checking +.B netns-local +flag in the output of the +.BR ethtool ":" + +.in +8 +.B ethtool -k +.I DEVICE +.in -8 + +To change network namespace for wireless devices the +.B iw +tool can be used. But it allows to change network namespace only for physical devices and by process +.IR PID . .TP .BI alias " NAME" @@ -442,6 +575,10 @@ set master device of the device (enslave device). .BI nomaster unset master device of the device (release device). +.TP +.BR "addrgenmode eui64 " or " addrgenmode none" +allow to ipv6 set address generation mode + .PP .B Warning: If multiple parameter changes are requested, @@ -471,6 +608,27 @@ specifies what group of devices to show. .B up only display running interfaces. +.TP +The show command has additional formatting options: + +.RS +.TP +.BR "\-s" , " \-stats", " \-statistics" +output more statistics about packet usage. + +.TP +.BR "\-d", " \-details" +output more detailed information. + +.TP +.BR "\-h", " \-human", " \-human-readble" +output statistics with human readable values number followed by suffix + +.TP +.BR "\-iec" +print human readable rates in IEC units (ie. 1K = 1024). +.RE + .SH "EXAMPLES" .PP ip link show @@ -495,7 +653,8 @@ Removes vlan device. .SH SEE ALSO .br -.BR ip (8) +.BR ip (8), +.BR ip-netns (8) .SH AUTHOR Original Manpage by Michail Litvak diff --git a/man/man8/ip.8 b/man/man8/ip.8 index 9065b3a..0713756 100644 --- a/man/man8/ip.8 +++ b/man/man8/ip.8 @@ -31,7 +31,8 @@ ip \- show / manipulate routing, devices, policy routing and tunnels \fB\-r\fR[\fIesolve\fR] | \fB\-f\fR[\fIamily\fR] { .BR inet " | " inet6 " | " ipx " | " dnet " | " link " } | " -\fB\-o\fR[\fIneline\fR] } +\fB\-o\fR[\fIneline\fR] | +\fB\-n\fR[\fIetns\fR] name } .SH OPTIONS @@ -130,6 +131,26 @@ the output. use the system's name resolver to print DNS names instead of host addresses. +.TP +.BR "\-n" , " \-net" , " \-netns " +switches +.B ip +to the specified network namespace +.IR NETNS . +Actually it just simplifies executing of: + +.B ip netns exec +.IR NETNS +.B ip +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" + +to + +.B ip +.RI "-n[etns] " NETNS " [ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" + .SH IP - COMMAND SYNTAX .SS