commit 9764c8d5063de9d76326f028fce66f6e4e49bb5a
Author: Pavel Šimerda <psimerda@redhat.com>
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 <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/sockios.h>
+#include <linux/net_namespace.h>
#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 <mci@owl.openwall.com>
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 " <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