aboutsummaryrefslogtreecommitdiff
path: root/ip/iplink.c
diff options
context:
space:
mode:
Diffstat (limited to 'ip/iplink.c')
-rw-r--r--ip/iplink.c723
1 files changed, 535 insertions, 188 deletions
diff --git a/ip/iplink.c b/ip/iplink.c
index 5ab9d613..e4b81b54 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -26,12 +26,13 @@
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
-#include <linux/sockios.h>
#include <stdbool.h>
+#include <linux/mpls.h>
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
+#include "xdp.h"
#include "namespace.h"
#define IPLINK_IOCTL_COMPAT 1
@@ -45,56 +46,75 @@ static int iplink_have_newlink(void);
void iplink_usage(void)
{
if (iplink_have_newlink()) {
- fprintf(stderr, "Usage: ip link add [link DEV] [ name ] NAME\n");
- fprintf(stderr, " [ txqueuelen PACKETS ]\n");
- fprintf(stderr, " [ address LLADDR ]\n");
- fprintf(stderr, " [ broadcast LLADDR ]\n");
- fprintf(stderr, " [ mtu MTU ] [index IDX ]\n");
- fprintf(stderr, " [ numtxqueues QUEUE_COUNT ]\n");
- fprintf(stderr, " [ numrxqueues QUEUE_COUNT ]\n");
- fprintf(stderr, " type TYPE [ ARGS ]\n");
- fprintf(stderr, " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n");
- fprintf(stderr, "\n");
- fprintf(stderr, " ip link set { DEVICE | dev DEVICE | group DEVGROUP } [ { up | down } ]\n");
+ fprintf(stderr,
+ "Usage: ip link add [link DEV] [ name ] NAME\n"
+ " [ txqueuelen PACKETS ]\n"
+ " [ address LLADDR ]\n"
+ " [ broadcast LLADDR ]\n"
+ " [ mtu MTU ] [index IDX ]\n"
+ " [ numtxqueues QUEUE_COUNT ]\n"
+ " [ numrxqueues QUEUE_COUNT ]\n"
+ " type TYPE [ ARGS ]\n"
+ "\n"
+ " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n"
+ "\n"
+ " ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n"
+ " [ { up | down } ]\n"
+ " [ type TYPE ARGS ]\n");
} else
- fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n");
-
- fprintf(stderr, " [ arp { on | off } ]\n");
- fprintf(stderr, " [ dynamic { on | off } ]\n");
- fprintf(stderr, " [ multicast { on | off } ]\n");
- fprintf(stderr, " [ allmulticast { on | off } ]\n");
- fprintf(stderr, " [ promisc { on | off } ]\n");
- fprintf(stderr, " [ trailers { on | off } ]\n");
- fprintf(stderr, " [ txqueuelen PACKETS ]\n");
- fprintf(stderr, " [ name NEWNAME ]\n");
- fprintf(stderr, " [ address LLADDR ]\n");
- fprintf(stderr, " [ broadcast LLADDR ]\n");
- 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");
-
- 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 | stable_secret | random } ]\n");
- fprintf(stderr, " [ protodown { on | off } ]\n");
- fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [type TYPE]\n");
+ fprintf(stderr,
+ "Usage: ip link set DEVICE [ { up | down } ]\n");
+
+ fprintf(stderr,
+ " [ arp { on | off } ]\n"
+ " [ dynamic { on | off } ]\n"
+ " [ multicast { on | off } ]\n"
+ " [ allmulticast { on | off } ]\n"
+ " [ promisc { on | off } ]\n"
+ " [ trailers { on | off } ]\n"
+ " [ carrier { on | off } ]\n"
+ " [ txqueuelen PACKETS ]\n"
+ " [ name NEWNAME ]\n"
+ " [ address LLADDR ]\n"
+ " [ broadcast LLADDR ]\n"
+ " [ mtu MTU ]\n"
+ " [ netns { PID | NAME } ]\n"
+ " [ link-netnsid ID ]\n"
+ " [ alias NAME ]\n"
+ " [ vf NUM [ mac LLADDR ]\n"
+ " [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n"
+ " [ rate TXRATE ]\n"
+ " [ max_tx_rate TXRATE ]\n"
+ " [ min_tx_rate TXRATE ]\n"
+ " [ spoofchk { on | off} ]\n"
+ " [ query_rss { on | off} ]\n"
+ " [ state { auto | enable | disable} ] ]\n"
+ " [ trust { on | off} ] ]\n"
+ " [ node_guid { eui64 } ]\n"
+ " [ port_guid { eui64 } ]\n"
+ " [ xdp { off |\n"
+ " object FILE [ section NAME ] [ verbose ] |\n"
+ " pinned FILE } ]\n"
+ " [ master DEVICE ][ vrf NAME ]\n"
+ " [ nomaster ]\n"
+ " [ addrgenmode { eui64 | none | stable_secret | random } ]\n"
+ " [ protodown { on | off } ]\n"
+ "\n"
+ " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n");
+
+ fprintf(stderr, "\n ip link xstats type TYPE [ ARGS ]\n");
+ fprintf(stderr, "\n ip link afstats [ dev DEVICE ]\n");
if (iplink_have_newlink()) {
- fprintf(stderr, " ip link help [ TYPE ]\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n");
- fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n");
- fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n");
- fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf }\n");
+ fprintf(stderr,
+ "\n"
+ " ip link help [ TYPE ]\n"
+ "\n"
+ "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"
+ " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n"
+ " gre | gretap | erspan | ip6gre | ip6gretap | vti | nlmon |\n"
+ " team_slave | bond_slave | ipvlan | geneve | bridge_slave |\n"
+ " vrf | macsec }\n");
}
exit(-1);
}
@@ -115,15 +135,14 @@ static int on_off(const char *msg, const char *realval)
static void *BODY; /* cached dlopen(NULL) handle */
static struct link_util *linkutil_list;
-static struct link_util *__get_link_kind(const char *id, bool slave)
+struct link_util *get_link_kind(const char *id)
{
void *dlh;
char buf[256];
struct link_util *l;
for (l = linkutil_list; l; l = l->next)
- if (strcmp(l->id, id) == 0 &&
- l->slave == slave)
+ if (strcmp(l->id, id) == 0)
return l;
snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id);
@@ -138,10 +157,7 @@ static struct link_util *__get_link_kind(const char *id, bool slave)
}
}
- if (slave)
- snprintf(buf, sizeof(buf), "%s_slave_link_util", id);
- else
- snprintf(buf, sizeof(buf), "%s_link_util", id);
+ snprintf(buf, sizeof(buf), "%s_link_util", id);
l = dlsym(dlh, buf);
if (l == NULL)
return NULL;
@@ -151,16 +167,6 @@ static struct link_util *__get_link_kind(const char *id, bool slave)
return l;
}
-struct link_util *get_link_kind(const char *id)
-{
- return __get_link_kind(id, false);
-}
-
-struct link_util *get_link_slave_kind(const char *id)
-{
- return __get_link_kind(id, true);
-}
-
static int get_link_mode(const char *mode)
{
if (strcasecmp(mode, "default") == 0)
@@ -206,16 +212,14 @@ static int iplink_have_newlink(void)
struct nlmsghdr n;
struct ifinfomsg i;
char buf[1024];
- } req;
+ } req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .n.nlmsg_type = RTM_NEWLINK,
+ .i.ifi_family = AF_UNSPEC,
+ };
if (have_rtnl_newlink < 0) {
- memset(&req, 0, sizeof(req));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
- req.n.nlmsg_type = RTM_NEWLINK;
- req.i.ifi_family = AF_UNSPEC;
-
if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) {
perror("request send failed");
exit(1);
@@ -231,11 +235,89 @@ static int iplink_have_newlink(void)
}
#endif /* ! IPLINK_IOCTL_COMPAT */
-struct iplink_req {
- struct nlmsghdr n;
- struct ifinfomsg i;
- char buf[1024];
-};
+static int nl_get_ll_addr_len(unsigned int dev_index)
+{
+ int len;
+ struct iplink_req req = {
+ .n = {
+ .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .nlmsg_type = RTM_GETLINK,
+ .nlmsg_flags = NLM_F_REQUEST
+ },
+ .i = {
+ .ifi_family = preferred_family,
+ .ifi_index = dev_index,
+ }
+ };
+ struct rtattr *tb[IFLA_MAX+1];
+
+ if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
+ return -1;
+
+ len = req.n.nlmsg_len - NLMSG_LENGTH(sizeof(req.i));
+ if (len < 0)
+ return -1;
+
+ parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(&req.i), len, NLA_F_NESTED);
+ if (!tb[IFLA_ADDRESS])
+ return -1;
+
+ return RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+}
+
+static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
+ struct ifla_vf_vlan_info *ivvip)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ NEXT_ARG();
+ if (get_unsigned(&ivvip->vlan, *argv, 0))
+ invarg("Invalid \"vlan\" value\n", *argv);
+
+ ivvip->vf = vf;
+ ivvip->qos = 0;
+ ivvip->vlan_proto = htons(ETH_P_8021Q);
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (matches(*argv, "qos") == 0) {
+ NEXT_ARG();
+ if (get_unsigned(&ivvip->qos, *argv, 0))
+ invarg("Invalid \"qos\" value\n", *argv);
+ } else {
+ /* rewind arg */
+ PREV_ARG();
+ }
+ }
+ if (NEXT_ARG_OK()) {
+ NEXT_ARG();
+ if (matches(*argv, "proto") == 0) {
+ NEXT_ARG();
+ if (ll_proto_a2n(&ivvip->vlan_proto, *argv))
+ invarg("protocol is invalid\n", *argv);
+ if (ivvip->vlan_proto != htons(ETH_P_8021AD) &&
+ ivvip->vlan_proto != htons(ETH_P_8021Q)) {
+ SPRINT_BUF(b1);
+ SPRINT_BUF(b2);
+ char msg[64 + sizeof(b1) + sizeof(b2)];
+
+ sprintf(msg,
+ "Invalid \"vlan protocol\" value - supported %s, %s\n",
+ ll_proto_n2a(htons(ETH_P_8021Q),
+ b1, sizeof(b1)),
+ ll_proto_n2a(htons(ETH_P_8021AD),
+ b2, sizeof(b2)));
+ invarg(msg, *argv);
+ }
+ } else {
+ /* rewind arg */
+ PREV_ARG();
+ }
+ }
+
+ *argcp = argc;
+ *argvp = argv;
+}
static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
struct iplink_req *req, int dev_index)
@@ -273,35 +355,58 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
while (NEXT_ARG_OK()) {
NEXT_ARG();
if (matches(*argv, "mac") == 0) {
- struct ifla_vf_mac ivm;
+ struct ifla_vf_mac ivm = { 0 };
+ int halen = nl_get_ll_addr_len(dev_index);
NEXT_ARG();
ivm.vf = vf;
len = ll_addr_a2n((char *)ivm.mac, 32, *argv);
if (len < 0)
return -1;
- addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm));
+ if (halen > 0 && len != halen) {
+ fprintf(stderr,
+ "Invalid address length %d - must be %d bytes\n",
+ len, halen);
+ return -1;
+ }
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC,
+ &ivm, sizeof(ivm));
} else if (matches(*argv, "vlan") == 0) {
- struct ifla_vf_vlan ivv;
+ struct ifla_vf_vlan_info ivvi;
- NEXT_ARG();
- if (get_unsigned(&ivv.vlan, *argv, 0))
- invarg("Invalid \"vlan\" value\n", *argv);
+ iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi);
+ /* support the old interface in case of older kernel*/
+ if (ivvi.vlan_proto == htons(ETH_P_8021Q)) {
+ struct ifla_vf_vlan ivv;
- ivv.vf = vf;
- ivv.qos = 0;
- if (NEXT_ARG_OK()) {
- NEXT_ARG();
- if (matches(*argv, "qos") == 0) {
+ ivv.vf = ivvi.vf;
+ ivv.vlan = ivvi.vlan;
+ ivv.qos = ivvi.qos;
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN, &ivv, sizeof(ivv));
+ } else {
+ struct rtattr *vfvlanlist;
+
+ vfvlanlist = addattr_nest(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_LIST);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_INFO, &ivvi,
+ sizeof(ivvi));
+
+ while (NEXT_ARG_OK()) {
NEXT_ARG();
- if (get_unsigned(&ivv.qos, *argv, 0))
- invarg("Invalid \"qos\" value\n", *argv);
- } else {
- /* rewind arg */
- PREV_ARG();
+ if (matches(*argv, "vlan") != 0) {
+ PREV_ARG();
+ break;
+ }
+ iplink_parse_vf_vlan_info(vf, &argc,
+ &argv, &ivvi);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_VF_VLAN_INFO, &ivvi,
+ sizeof(ivvi));
}
+ addattr_nest_end(&req->n, vfvlanlist);
}
- addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv));
} else if (matches(*argv, "rate") == 0) {
struct ifla_vf_tx_rate ivt;
@@ -341,7 +446,8 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
else
return on_off("spoofchk", *argv);
ivs.vf = vf;
- addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs));
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK,
+ &ivs, sizeof(ivs));
} else if (matches(*argv, "query_rss") == 0) {
struct ifla_vf_rss_query_en ivs;
@@ -354,7 +460,22 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
else
return on_off("query_rss", *argv);
ivs.vf = vf;
- addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs));
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN,
+ &ivs, sizeof(ivs));
+
+ } else if (matches(*argv, "trust") == 0) {
+ struct ifla_vf_trust ivt;
+
+ NEXT_ARG();
+ if (matches(*argv, "on") == 0)
+ ivt.setting = 1;
+ else if (matches(*argv, "off") == 0)
+ ivt.setting = 0;
+ else
+ invarg("Invalid \"trust\" value\n", *argv);
+ ivt.vf = vf;
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST,
+ &ivt, sizeof(ivt));
} else if (matches(*argv, "state") == 0) {
struct ifla_vf_link_state ivl;
@@ -369,7 +490,30 @@ static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
else
invarg("Invalid \"state\" value\n", *argv);
ivl.vf = vf;
- addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl));
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE,
+ &ivl, sizeof(ivl));
+ } else if (matches(*argv, "node_guid") == 0) {
+ struct ifla_vf_guid ivg;
+
+ NEXT_ARG();
+ ivg.vf = vf;
+ if (get_guid(&ivg.guid, *argv)) {
+ invarg("Invalid GUID format\n", *argv);
+ return -1;
+ }
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID,
+ &ivg, sizeof(ivg));
+ } else if (matches(*argv, "port_guid") == 0) {
+ struct ifla_vf_guid ivg;
+
+ NEXT_ARG();
+ ivg.vf = vf;
+ if (get_guid(&ivg.guid, *argv)) {
+ invarg("Invalid GUID format\n", *argv);
+ return -1;
+ }
+ addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID,
+ &ivg, sizeof(ivg));
} else {
/* rewind arg */
PREV_ARG();
@@ -415,6 +559,7 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
int numrxqueues = -1;
int dev_index = 0;
int link_netnsid = -1;
+ int addr_len = 0;
*group = -1;
ret = argc;
@@ -428,6 +573,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
req->i.ifi_flags &= ~IFF_UP;
} else if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
+ if (check_ifname(*argv))
+ invarg("\"name\" not a valid ifname", *argv);
*name = *argv;
} else if (strcmp(*argv, "index") == 0) {
NEXT_ARG();
@@ -439,17 +586,19 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
*link = *argv;
} else if (matches(*argv, "address") == 0) {
NEXT_ARG();
- len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
- if (len < 0)
+ addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+ if (addr_len < 0)
return -1;
- addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_ADDRESS, abuf, addr_len);
} else if (matches(*argv, "broadcast") == 0 ||
strcmp(*argv, "brd") == 0) {
NEXT_ARG();
len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
if (len < 0)
return -1;
- addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_BROADCAST, abuf, len);
} else if (matches(*argv, "txqueuelen") == 0 ||
strcmp(*argv, "qlen") == 0 ||
matches(*argv, "txqlen") == 0) {
@@ -458,7 +607,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
duparg("txqueuelen", *argv);
if (get_integer(&qlen, *argv, 0))
invarg("Invalid \"txqueuelen\" value\n", *argv);
- addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_TXQLEN, &qlen, 4);
} else if (strcmp(*argv, "mtu") == 0) {
NEXT_ARG();
if (mtu != -1)
@@ -466,15 +616,29 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
if (get_integer(&mtu, *argv, 0))
invarg("Invalid \"mtu\" value\n", *argv);
addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
+ } else if (strcmp(*argv, "xdpgeneric") == 0 ||
+ strcmp(*argv, "xdpdrv") == 0 ||
+ strcmp(*argv, "xdpoffload") == 0 ||
+ strcmp(*argv, "xdp") == 0) {
+ bool generic = strcmp(*argv, "xdpgeneric") == 0;
+ bool drv = strcmp(*argv, "xdpdrv") == 0;
+ bool offload = strcmp(*argv, "xdpoffload") == 0;
+
+ NEXT_ARG();
+ if (xdp_parse(&argc, &argv, req, generic, drv,
+ offload))
+ exit(-1);
} else if (strcmp(*argv, "netns") == 0) {
NEXT_ARG();
if (netns != -1)
duparg("netns", *argv);
netns = netns_get_fd(*argv);
if (netns >= 0)
- addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
+ 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);
+ addattr_l(&req->n, sizeof(*req),
+ IFLA_NET_NS_PID, &netns, 4);
else
invarg("Invalid \"netns\" value\n", *argv);
} else if (strcmp(*argv, "multicast") == 0) {
@@ -527,6 +691,18 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
req->i.ifi_flags |= IFF_NOARP;
else
return on_off("arp", *argv);
+ } else if (strcmp(*argv, "carrier") == 0) {
+ int carrier;
+
+ NEXT_ARG();
+ if (strcmp(*argv, "on") == 0)
+ carrier = 1;
+ else if (strcmp(*argv, "off") == 0)
+ carrier = 0;
+ else
+ return on_off("carrier", *argv);
+
+ addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier);
} else if (strcmp(*argv, "vf") == 0) {
struct rtattr *vflist;
@@ -552,6 +728,17 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
invarg("Device does not exist\n", *argv);
addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
&ifindex, 4);
+ } else if (strcmp(*argv, "vrf") == 0) {
+ int ifindex;
+
+ NEXT_ARG();
+ ifindex = ll_name_to_index(*argv);
+ if (!ifindex)
+ invarg("Not a valid VRF name\n", *argv);
+ if (!name_is_vrf(*argv))
+ invarg("Not a valid VRF name\n", *argv);
+ addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+ &ifindex, sizeof(ifindex));
} else if (matches(*argv, "nomaster") == 0) {
int ifindex = 0;
@@ -606,7 +793,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
if (numtxqueues != -1)
duparg("numtxqueues", *argv);
if (get_integer(&numtxqueues, *argv, 0))
- invarg("Invalid \"numtxqueues\" value\n", *argv);
+ invarg("Invalid \"numtxqueues\" value\n",
+ *argv);
addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES,
&numtxqueues, 4);
} else if (matches(*argv, "numrxqueues") == 0) {
@@ -614,7 +802,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
if (numrxqueues != -1)
duparg("numrxqueues", *argv);
if (get_integer(&numrxqueues, *argv, 0))
- invarg("Invalid \"numrxqueues\" value\n", *argv);
+ invarg("Invalid \"numrxqueues\" value\n",
+ *argv);
addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
&numrxqueues, 4);
} else if (matches(*argv, "addrgenmode") == 0) {
@@ -624,10 +813,12 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
NEXT_ARG();
mode = get_addr_gen_mode(*argv);
if (mode < 0)
- invarg("Invalid address generation mode\n", *argv);
+ 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);
+ 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) {
@@ -635,7 +826,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
if (link_netnsid != -1)
duparg("link-netnsid", *argv);
if (get_integer(&link_netnsid, *argv, 0))
- invarg("Invalid \"link-netnsid\" value\n", *argv);
+ invarg("Invalid \"link-netnsid\" value\n",
+ *argv);
addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
link_netnsid);
} else if (strcmp(*argv, "protodown") == 0) {
@@ -658,18 +850,30 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
NEXT_ARG();
if (*dev)
duparg2("dev", *argv);
+ if (check_ifname(*argv))
+ invarg("\"dev\" not a valid ifname", *argv);
*dev = *argv;
dev_index = ll_name_to_index(*dev);
}
argc--; argv++;
}
+ if (dev_index && addr_len) {
+ int halen = nl_get_ll_addr_len(dev_index);
+
+ if (halen >= 0 && halen != addr_len) {
+ fprintf(stderr,
+ "Invalid address length %d - must be %d bytes\n",
+ addr_len, halen);
+ return -1;
+ }
+ }
+
return ret - argc;
}
static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
{
- int len;
char *dev = NULL;
char *name = NULL;
char *link = NULL;
@@ -677,17 +881,16 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
int index = -1;
int group;
struct link_util *lu = NULL;
- struct iplink_req req;
+ struct iplink_req req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .n.nlmsg_flags = NLM_F_REQUEST | flags,
+ .n.nlmsg_type = cmd,
+ .i.ifi_family = preferred_family,
+ };
int ret;
- memset(&req, 0, sizeof(req));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|flags;
- req.n.nlmsg_type = cmd;
- req.i.ifi_family = preferred_family;
-
- ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group, &index);
+ ret = iplink_parse(argc, argv,
+ &req, &name, &type, &link, &dev, &group, &index);
if (ret < 0)
return ret;
@@ -700,14 +903,14 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
&group, sizeof(group));
else {
if (argc) {
- fprintf(stderr, "Garbage instead of arguments "
- "\"%s ...\". Try \"ip link "
- "help\".\n", *argv);
+ fprintf(stderr,
+ "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n",
+ *argv);
return -1;
}
if (flags & NLM_F_CREATE) {
- fprintf(stderr, "group cannot be used when "
- "creating devices.\n");
+ fprintf(stderr,
+ "group cannot be used when creating devices.\n");
return -1;
}
@@ -721,13 +924,13 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
if (!(flags & NLM_F_CREATE)) {
if (!dev) {
- fprintf(stderr, "Not enough information: \"dev\" "
- "argument is required.\n");
+ fprintf(stderr,
+ "Not enough information: \"dev\" argument is required.\n");
exit(-1);
}
if (cmd == RTM_NEWLINK && index != -1) {
- fprintf(stderr, "index can be used only when "
- "creating devices.\n");
+ fprintf(stderr,
+ "index can be used only when creating devices.\n");
exit(-1);
}
@@ -760,38 +963,28 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
}
if (name) {
- len = strlen(name) + 1;
- if (len == 1)
- invarg("\"\" is not a valid device identifier\n", "name");
- if (len > IFNAMSIZ)
- invarg("\"name\" too long\n", name);
- addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
+ addattr_l(&req.n, sizeof(req),
+ IFLA_IFNAME, name, strlen(name) + 1);
}
if (type) {
struct rtattr *linkinfo;
- char slavebuf[128], *ulinep = strchr(type, '_');
+ char *ulinep = strchr(type, '_');
int iflatype;
linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
strlen(type));
- if (ulinep && !strcmp(ulinep, "_slave")) {
- strncpy(slavebuf, type, sizeof(slavebuf));
- slavebuf[sizeof(slavebuf) - 1] = '\0';
- ulinep = strchr(slavebuf, '_');
- /* check in case it was after sizeof(slavebuf) - 1*/
- if (ulinep)
- *ulinep = '\0';
- lu = get_link_slave_kind(slavebuf);
+ lu = get_link_kind(type);
+ if (ulinep && !strcmp(ulinep, "_slave"))
iflatype = IFLA_INFO_SLAVE_DATA;
- } else {
- lu = get_link_kind(type);
+ else
iflatype = IFLA_INFO_DATA;
- }
if (lu && argc) {
- struct rtattr *data = addattr_nest(&req.n, sizeof(req), iflatype);
+ struct rtattr *data
+ = addattr_nest(&req.n,
+ sizeof(req), iflatype);
if (lu->parse_opt &&
lu->parse_opt(lu, argc, argv, &req.n))
@@ -801,14 +994,15 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
} else if (argc) {
if (matches(*argv, "help") == 0)
usage();
- fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
- "Try \"ip link help\".\n", *argv);
+ fprintf(stderr,
+ "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n",
+ *argv);
return -1;
}
addattr_nest_end(&req.n, linkinfo);
} else if (flags & NLM_F_CREATE) {
- fprintf(stderr, "Not enough information: \"type\" argument "
- "is required\n");
+ fprintf(stderr,
+ "Not enough information: \"type\" argument is required\n");
return -1;
}
@@ -820,38 +1014,37 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
{
- int len;
- struct iplink_req req;
+ struct iplink_req req = {
+ .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .n.nlmsg_flags = NLM_F_REQUEST | flags,
+ .n.nlmsg_type = RTM_GETLINK,
+ .i.ifi_family = preferred_family,
+ };
struct {
struct nlmsghdr n;
- char buf[16384];
+ char buf[32768];
} answer;
- memset(&req, 0, sizeof(req));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|flags;
- req.n.nlmsg_type = RTM_GETLINK;
- req.i.ifi_family = preferred_family;
-
if (name) {
- len = strlen(name) + 1;
- if (len == 1)
- invarg("\"\" is not a valid device identifier\n",
- "name");
- if (len > IFNAMSIZ)
- invarg("\"name\" too long\n", name);
- addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
+ addattr_l(&req.n, sizeof(req),
+ IFLA_IFNAME, name, strlen(name) + 1);
}
addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0)
return -2;
+ if (answer.n.nlmsg_len > sizeof(answer.buf)) {
+ fprintf(stderr, "Message truncated from %u to %zu\n",
+ answer.n.nlmsg_len, sizeof(answer.buf));
+ return -2;
+ }
+ open_json_object(NULL);
if (brief)
- print_linkinfo_brief(NULL, &answer.n, stdout);
+ print_linkinfo_brief(NULL, &answer.n, stdout, NULL);
else
print_linkinfo(NULL, &answer.n, stdout);
+ close_json_object();
return 0;
}
@@ -927,16 +1120,14 @@ static int do_changename(const char *dev, const char *newdev)
static int set_qlen(const char *dev, int qlen)
{
- struct ifreq ifr;
+ struct ifreq ifr = { .ifr_qlen = qlen };
int s;
s = get_ctl_fd();
if (s < 0)
return -1;
- memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
- ifr.ifr_qlen = qlen;
if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
perror("SIOCSIFXQLEN");
close(s);
@@ -949,16 +1140,14 @@ static int set_qlen(const char *dev, int qlen)
static int set_mtu(const char *dev, int mtu)
{
- struct ifreq ifr;
+ struct ifreq ifr = { .ifr_mtu = mtu };
int s;
s = get_ctl_fd();
if (s < 0)
return -1;
- memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
- ifr.ifr_mtu = mtu;
if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
perror("SIOCSIFMTU");
close(s);
@@ -971,8 +1160,11 @@ static int set_mtu(const char *dev, int mtu)
static int get_address(const char *dev, int *htype)
{
- struct ifreq ifr;
- struct sockaddr_ll me;
+ struct ifreq ifr = {};
+ struct sockaddr_ll me = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_LOOP),
+ };
socklen_t alen;
int s;
@@ -982,7 +1174,6 @@ static int get_address(const char *dev, int *htype)
return -1;
}
- memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("SIOCGIFINDEX");
@@ -990,10 +1181,7 @@ static int get_address(const char *dev, int *htype)
return -1;
}
- memset(&me, 0, sizeof(me));
- me.sll_family = AF_PACKET;
me.sll_ifindex = ifr.ifr_ifindex;
- me.sll_protocol = htons(ETH_P_LOOP);
if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) {
perror("bind");
close(s);
@@ -1023,7 +1211,9 @@ static int parse_address(const char *dev, int hatype, int halen,
if (alen < 0)
return -1;
if (alen != halen) {
- fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
+ fprintf(stderr,
+ "Wrong address (%s) length: expected %d bytes\n",
+ lla, halen);
return -1;
}
return 0;
@@ -1067,6 +1257,8 @@ static int do_set(int argc, char **argv)
flags &= ~IFF_UP;
} else if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
+ if (check_ifname(*argv))
+ invarg("\"name\" not a valid ifname", *argv);
newname = *argv;
} else if (matches(*argv, "address") == 0) {
NEXT_ARG();
@@ -1157,13 +1349,16 @@ static int do_set(int argc, char **argv)
if (dev)
duparg2("dev", *argv);
+ if (check_ifname(*argv))
+ invarg("\"dev\" not a valid ifname", *argv);
dev = *argv;
}
argc--; argv++;
}
if (!dev) {
- fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
+ fprintf(stderr,
+ "Not enough of information: \"dev\" argument is required.\n");
exit(-1);
}
@@ -1172,18 +1367,18 @@ static int do_set(int argc, char **argv)
if (halen < 0)
return -1;
if (newaddr) {
- if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
+ if (parse_address(dev, htype, halen,
+ newaddr, &ifr0) < 0)
return -1;
}
if (newbrd) {
- if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
+ if (parse_address(dev, htype, halen,
+ newbrd, &ifr1) < 0)
return -1;
}
}
if (newname && strcmp(dev, newname)) {
- if (strlen(newname) == 0)
- invarg("\"\" is not a valid device identifier\n", "name");
if (do_changename(dev, newname) < 0)
return -1;
dev = newname;
@@ -1212,6 +1407,150 @@ static int do_set(int argc, char **argv)
}
#endif /* IPLINK_IOCTL_COMPAT */
+static void print_mpls_stats(FILE *fp, struct rtattr *attr)
+{
+ struct rtattr *mrtb[MPLS_STATS_MAX+1];
+ struct mpls_link_stats *stats;
+
+ parse_rtattr(mrtb, MPLS_STATS_MAX, RTA_DATA(attr),
+ RTA_PAYLOAD(attr));
+ if (!mrtb[MPLS_STATS_LINK])
+ return;
+
+ stats = RTA_DATA(mrtb[MPLS_STATS_LINK]);
+
+ fprintf(fp, " mpls:\n");
+ fprintf(fp, " RX: bytes packets errors dropped noroute\n");
+ fprintf(fp, " ");
+ print_num(fp, 10, stats->rx_bytes);
+ print_num(fp, 8, stats->rx_packets);
+ print_num(fp, 7, stats->rx_errors);
+ print_num(fp, 8, stats->rx_dropped);
+ print_num(fp, 7, stats->rx_noroute);
+ fprintf(fp, "\n");
+ fprintf(fp, " TX: bytes packets errors dropped\n");
+ fprintf(fp, " ");
+ print_num(fp, 10, stats->tx_bytes);
+ print_num(fp, 8, stats->tx_packets);
+ print_num(fp, 7, stats->tx_errors);
+ print_num(fp, 7, stats->tx_dropped);
+ fprintf(fp, "\n");
+}
+
+static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr)
+{
+ bool if_printed = false;
+ struct rtattr *i;
+ int rem;
+
+ rem = RTA_PAYLOAD(attr);
+ for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+ if (preferred_family != AF_UNSPEC &&
+ i->rta_type != preferred_family)
+ continue;
+
+ if (!if_printed) {
+ fprintf(fp, "%u: %s\n", ifindex,
+ ll_index_to_name(ifindex));
+ if_printed = true;
+ }
+
+ switch (i->rta_type) {
+ case AF_MPLS:
+ print_mpls_stats(fp, i);
+ break;
+ default:
+ fprintf(fp, " unknown af(%d)\n", i->rta_type);
+ break;
+ }
+ }
+}
+
+struct af_stats_ctx {
+ FILE *fp;
+ int ifindex;
+};
+
+static int print_af_stats(const struct sockaddr_nl *who,
+ struct nlmsghdr *n,
+ void *arg)
+{
+ struct if_stats_msg *ifsm = NLMSG_DATA(n);
+ struct rtattr *tb[IFLA_STATS_MAX+1];
+ int len = n->nlmsg_len;
+ struct af_stats_ctx *ctx = arg;
+ FILE *fp = ctx->fp;
+
+ len -= NLMSG_LENGTH(sizeof(*ifsm));
+ if (len < 0) {
+ fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+ return -1;
+ }
+
+ if (ctx->ifindex && ifsm->ifindex != ctx->ifindex)
+ return 0;
+
+ parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len);
+
+ if (tb[IFLA_STATS_AF_SPEC])
+ print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]);
+
+ fflush(fp);
+ return 0;
+}
+
+static int iplink_afstats(int argc, char **argv)
+{
+ __u32 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_AF_SPEC);
+ const char *filter_dev = NULL;
+ struct af_stats_ctx ctx = {
+ .fp = stdout,
+ .ifindex = 0,
+ };
+
+ while (argc > 0) {
+ if (strcmp(*argv, "dev") == 0) {
+ NEXT_ARG();
+ if (filter_dev)
+ duparg2("dev", *argv);
+ filter_dev = *argv;
+ } else if (matches(*argv, "help") == 0) {
+ usage();
+ } else {
+ fprintf(stderr,
+ "Command \"%s\" is unknown, try \"ip link help\".\n",
+ *argv);
+ exit(-1);
+ }
+
+ argv++; argc--;
+ }
+
+ if (filter_dev) {
+ ctx.ifindex = ll_name_to_index(filter_dev);
+ if (ctx.ifindex <= 0) {
+ fprintf(stderr,
+ "Device \"%s\" does not exist.\n",
+ filter_dev);
+ return -1;
+ }
+ }
+
+ if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
+ RTM_GETSTATS,
+ filt_mask) < 0) {
+ perror("Cannont send dump request");
+ return 1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_af_stats, &ctx) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return 1;
+ }
+
+ return 0;
+}
+
static void do_help(int argc, char **argv)
{
struct link_util *lu = NULL;
@@ -1261,6 +1600,14 @@ int do_iplink(int argc, char **argv)
matches(*argv, "list") == 0)
return ipaddr_list_link(argc-1, argv+1);
+ if (matches(*argv, "xstats") == 0)
+ return iplink_ifla_xstats(argc-1, argv+1);
+
+ if (matches(*argv, "afstats") == 0) {
+ iplink_afstats(argc-1, argv+1);
+ return 0;
+ }
+
if (matches(*argv, "help") == 0) {
do_help(argc-1, argv+1);
return 0;