#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#include <syslog.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include "rt_names.h"
#include "utils.h"
#include "ip_common.h"
-#include "xdp.h"
#include "namespace.h"
#define IPLINK_IOCTL_COMPAT 1
#define LIBDIR "/usr/lib"
#endif
+#ifndef GSO_MAX_SIZE
+#define GSO_MAX_SIZE 65536
+#endif
+#ifndef GSO_MAX_SEGS
+#define GSO_MAX_SEGS 65535
+#endif
+
+
static void usage(void) __attribute__((noreturn));
static int iplink_have_newlink(void);
" [ broadcast LLADDR ]\n"
" [ mtu MTU ]\n"
" [ netns { PID | NAME } ]\n"
- " [ link-netnsid ID ]\n"
+ " [ link-netns NAME | link-netnsid ID ]\n"
" [ alias NAME ]\n"
" [ vf NUM [ mac LLADDR ]\n"
" [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n"
" [ master DEVICE ][ vrf NAME ]\n"
" [ nomaster ]\n"
" [ addrgenmode { eui64 | none | stable_secret | random } ]\n"
- " [ protodown { on | off } ]\n"
+ " [ protodown { on | off } ]\n"
+ " [ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n"
"\n"
" ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n");
"\n"
" ip link help [ TYPE ]\n"
"\n"
- "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"
+ "TYPE := { vlan | veth | vcan | vxcan | 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");
+ " gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n"
+ " vti | nlmon | team_slave | bond_slave | bridge_slave |\n"
+ " ipvlan | ipvtap | geneve | vrf | macsec | netdevsim | rmnet }\n");
}
exit(-1);
}
#if IPLINK_IOCTL_COMPAT
static int have_rtnl_newlink = -1;
-static int accept_msg(const struct sockaddr_nl *who,
- struct rtnl_ctrl_data *ctrl,
+static int accept_msg(struct rtnl_ctrl_data *ctrl,
struct nlmsghdr *n, void *arg)
{
struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
}
#endif /* ! IPLINK_IOCTL_COMPAT */
-static int nl_get_ll_addr_len(unsigned int dev_index)
+static int nl_get_ll_addr_len(const char *ifname)
{
int len;
+ int dev_index = ll_name_to_index(ifname);
struct iplink_req req = {
.n = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
.ifi_index = dev_index,
}
};
+ struct nlmsghdr *answer;
struct rtattr *tb[IFLA_MAX+1];
- if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0)
+ if (dev_index == 0)
return -1;
- len = req.n.nlmsg_len - NLMSG_LENGTH(sizeof(req.i));
- if (len < 0)
+ if (rtnl_talk(&rth, &req.n, &answer) < 0)
return -1;
- parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(&req.i), len, NLA_F_NESTED);
- if (!tb[IFLA_ADDRESS])
+ len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ if (len < 0) {
+ free(answer);
return -1;
+ }
- return RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)),
+ len, NLA_F_NESTED);
+ if (!tb[IFLA_ADDRESS]) {
+ free(answer);
+ return -1;
+ }
+
+ len = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+ free(answer);
+ return len;
}
static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp,
{
int argc = *argcp;
char **argv = *argvp;
+ unsigned int vci;
NEXT_ARG();
- if (get_unsigned(&ivvip->vlan, *argv, 0))
+ if (get_unsigned(&vci, *argv, 0) || vci > 4095)
invarg("Invalid \"vlan\" value\n", *argv);
+ ivvip->vlan = vci;
ivvip->vf = vf;
ivvip->qos = 0;
ivvip->vlan_proto = htons(ETH_P_8021Q);
}
static int iplink_parse_vf(int vf, int *argcp, char ***argvp,
- struct iplink_req *req, int dev_index)
+ struct iplink_req *req, const char *dev)
{
char new_rate_api = 0, count = 0, override_legacy_rate = 0;
struct ifla_vf_rate tivt;
NEXT_ARG();
if (matches(*argv, "mac") == 0) {
struct ifla_vf_mac ivm = { 0 };
- int halen = nl_get_ll_addr_len(dev_index);
+ int halen = nl_get_ll_addr_len(dev);
NEXT_ARG();
ivm.vf = vf;
int tmin, tmax;
if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) {
- ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev_index);
+ ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev);
if (tivt.min_tx_rate == -1)
tivt.min_tx_rate = tmin;
if (tivt.max_tx_rate == -1)
tivt.max_tx_rate = tmax;
}
+
+ if (tivt.max_tx_rate && tivt.min_tx_rate > tivt.max_tx_rate) {
+ fprintf(stderr,
+ "Invalid min_tx_rate %d - must be <= max_tx_rate %d\n",
+ tivt.min_tx_rate, tivt.max_tx_rate);
+ return -1;
+ }
+
addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt,
sizeof(tivt));
}
return 0;
}
-int iplink_parse(int argc, char **argv, struct iplink_req *req,
- char **name, char **type, char **link, char **dev,
- int *group, int *index)
+int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type)
{
+ char *name = NULL;
+ char *dev = NULL;
+ char *link = NULL;
int ret, len;
char abuf[32];
int qlen = -1;
int vf = -1;
int numtxqueues = -1;
int numrxqueues = -1;
- int dev_index = 0;
int link_netnsid = -1;
+ int index = 0;
+ int group = -1;
int addr_len = 0;
- *group = -1;
ret = argc;
while (argc > 0) {
req->i.ifi_flags &= ~IFF_UP;
} else if (strcmp(*argv, "name") == 0) {
NEXT_ARG();
- *name = *argv;
+ if (name)
+ duparg("name", *argv);
+ if (check_ifname(*argv))
+ invarg("\"name\" not a valid ifname", *argv);
+ name = *argv;
+ if (!dev)
+ dev = name;
} else if (strcmp(*argv, "index") == 0) {
NEXT_ARG();
- *index = atoi(*argv);
- if (*index < 0)
+ if (index)
+ duparg("index", *argv);
+ index = atoi(*argv);
+ if (index <= 0)
invarg("Invalid \"index\" value", *argv);
} else if (matches(*argv, "link") == 0) {
NEXT_ARG();
- *link = *argv;
+ link = *argv;
} else if (matches(*argv, "address") == 0) {
NEXT_ARG();
addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
bool offload = strcmp(*argv, "xdpoffload") == 0;
NEXT_ARG();
- if (xdp_parse(&argc, &argv, req, generic, drv,
- offload))
+ if (xdp_parse(&argc, &argv, req, dev,
+ generic, drv, offload))
exit(-1);
+
+ if (offload && name == dev)
+ dev = NULL;
} else if (strcmp(*argv, "netns") == 0) {
NEXT_ARG();
if (netns != -1)
vflist = addattr_nest(&req->n, sizeof(*req),
IFLA_VFINFO_LIST);
- if (dev_index == 0)
+ if (!dev)
missarg("dev");
- len = iplink_parse_vf(vf, &argc, &argv, req, dev_index);
+ len = iplink_parse_vf(vf, &argc, &argv, req, dev);
if (len < 0)
return -1;
addattr_nest_end(&req->n, vflist);
+
+ if (name == dev)
+ dev = NULL;
} else if (matches(*argv, "master") == 0) {
int ifindex;
break;
} else if (matches(*argv, "alias") == 0) {
NEXT_ARG();
+ len = strlen(*argv);
+ if (len >= IFALIASZ)
+ invarg("alias too long\n", *argv);
addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
- *argv, strlen(*argv));
- argc--; argv++;
- break;
+ *argv, len);
} else if (strcmp(*argv, "group") == 0) {
NEXT_ARG();
- if (*group != -1)
+ if (group != -1)
duparg("group", *argv);
- if (rtnl_group_a2n(group, *argv))
+ if (rtnl_group_a2n(&group, *argv))
invarg("Invalid \"group\" value\n", *argv);
+ addattr32(&req->n, sizeof(*req), IFLA_GROUP, group);
} else if (strcmp(*argv, "mode") == 0) {
int mode;
IFLA_INET6_ADDR_GEN_MODE, mode);
addattr_nest_end(&req->n, afs6);
addattr_nest_end(&req->n, afs);
+ } else if (matches(*argv, "link-netns") == 0) {
+ NEXT_ARG();
+ if (link_netnsid != -1)
+ duparg("link-netns/link-netnsid", *argv);
+ link_netnsid = get_netnsid_from_name(*argv);
+ /* No nsid? Try to assign one. */
+ if (link_netnsid < 0)
+ set_netnsid_from_name(*argv, -1);
+ link_netnsid = get_netnsid_from_name(*argv);
+ if (link_netnsid < 0)
+ invarg("Invalid \"link-netns\" value\n",
+ *argv);
+ addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID,
+ link_netnsid);
} else if (matches(*argv, "link-netnsid") == 0) {
NEXT_ARG();
if (link_netnsid != -1)
- duparg("link-netnsid", *argv);
+ duparg("link-netns/link-netnsid", *argv);
if (get_integer(&link_netnsid, *argv, 0))
invarg("Invalid \"link-netnsid\" value\n",
*argv);
return on_off("protodown", *argv);
addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN,
proto_down);
+ } else if (strcmp(*argv, "gso_max_size") == 0) {
+ unsigned int max_size;
+
+ NEXT_ARG();
+ if (get_unsigned(&max_size, *argv, 0) ||
+ max_size > GSO_MAX_SIZE)
+ invarg("Invalid \"gso_max_size\" value\n",
+ *argv);
+ addattr32(&req->n, sizeof(*req),
+ IFLA_GSO_MAX_SIZE, max_size);
+ } else if (strcmp(*argv, "gso_max_segs") == 0) {
+ unsigned int max_segs;
+
+ NEXT_ARG();
+ if (get_unsigned(&max_segs, *argv, 0) ||
+ max_segs > GSO_MAX_SEGS)
+ invarg("Invalid \"gso_max_segs\" value\n",
+ *argv);
+ addattr32(&req->n, sizeof(*req),
+ IFLA_GSO_MAX_SEGS, max_segs);
} else {
if (matches(*argv, "help") == 0)
usage();
if (strcmp(*argv, "dev") == 0)
NEXT_ARG();
- if (*dev)
+ if (dev != name)
duparg2("dev", *argv);
- *dev = *argv;
- dev_index = ll_name_to_index(*dev);
+ if (check_ifname(*argv))
+ invarg("\"dev\" not a valid ifname", *argv);
+ dev = *argv;
}
argc--; argv++;
}
- if (dev_index && addr_len) {
- int halen = nl_get_ll_addr_len(dev_index);
+ ret -= argc;
+
+ /* Allow "ip link add dev" and "ip link add name" */
+ if (!name)
+ name = dev;
+ else if (!dev)
+ dev = name;
+ else if (!strcmp(name, dev))
+ name = dev;
+
+ if (dev && addr_len) {
+ int halen = nl_get_ll_addr_len(dev);
if (halen >= 0 && halen != addr_len) {
fprintf(stderr,
}
}
- 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;
- char *type = NULL;
- int index = -1;
- int group;
- struct link_util *lu = NULL;
- 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;
-
- ret = iplink_parse(argc, argv,
- &req, &name, &type, &link, &dev, &group, &index);
- if (ret < 0)
- return ret;
-
- argc -= ret;
- argv += ret;
+ if (!(req->n.nlmsg_flags & NLM_F_CREATE) && index) {
+ fprintf(stderr,
+ "index can be used only when creating devices.\n");
+ exit(-1);
+ }
if (group != -1) {
- if (dev)
- addattr_l(&req.n, sizeof(req), IFLA_GROUP,
- &group, sizeof(group));
- else {
+ if (!dev) {
if (argc) {
fprintf(stderr,
"Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n",
*argv);
- return -1;
+ exit(-1);
}
- if (flags & NLM_F_CREATE) {
+ if (req->n.nlmsg_flags & NLM_F_CREATE) {
fprintf(stderr,
"group cannot be used when creating devices.\n");
- return -1;
+ exit(-1);
}
- req.i.ifi_index = 0;
- addattr32(&req.n, sizeof(req), IFLA_GROUP, group);
- if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
- return -2;
- return 0;
+ *type = NULL;
+ return ret;
}
}
- if (!(flags & NLM_F_CREATE)) {
+ if (!(req->n.nlmsg_flags & NLM_F_CREATE)) {
if (!dev) {
fprintf(stderr,
"Not enough information: \"dev\" argument is required.\n");
exit(-1);
}
- if (cmd == RTM_NEWLINK && index != -1) {
+
+ req->i.ifi_index = ll_name_to_index(dev);
+ if (!req->i.ifi_index)
+ return nodev(dev);
+
+ /* Not renaming to the same name */
+ if (name == dev)
+ name = NULL;
+ } else {
+ if (name != dev) {
fprintf(stderr,
- "index can be used only when creating devices.\n");
+ "both \"name\" and \"dev\" cannot be used when creating devices.\n");
exit(-1);
}
- req.i.ifi_index = ll_name_to_index(dev);
- if (req.i.ifi_index == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n", dev);
- return -1;
- }
- } else {
- /* Allow "ip link add dev" and "ip link add name" */
- if (!name)
- name = dev;
-
if (link) {
int ifindex;
ifindex = ll_name_to_index(link);
- if (ifindex == 0) {
- fprintf(stderr, "Cannot find device \"%s\"\n",
- link);
- return -1;
- }
- addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
+ if (!ifindex)
+ return nodev(link);
+ addattr32(&req->n, sizeof(*req), IFLA_LINK, ifindex);
}
- if (index == -1)
- req.i.ifi_index = 0;
- else
- req.i.ifi_index = index;
+ req->i.ifi_index = index;
}
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);
}
+ return ret;
+}
+
+static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
+{
+ char *type = NULL;
+ 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;
+
+ ret = iplink_parse(argc, argv, &req, &type);
+ if (ret < 0)
+ return ret;
+
if (type) {
+ struct link_util *lu;
struct rtattr *linkinfo;
char *ulinep = strchr(type, '_');
int iflatype;
iflatype = IFLA_INFO_SLAVE_DATA;
else
iflatype = IFLA_INFO_DATA;
+
+ argc -= ret;
+ argv += ret;
+
if (lu && argc) {
- struct rtattr *data
- = addattr_nest(&req.n,
- sizeof(req), iflatype);
+ struct rtattr *data;
+
+ data = addattr_nest(&req.n, sizeof(req), iflatype);
if (lu->parse_opt &&
lu->parse_opt(lu, argc, argv, &req.n))
return -1;
}
- if (rtnl_talk(&rth, &req.n, NULL, 0) < 0)
+ if (rtnl_talk(&rth, &req.n, NULL) < 0)
return -2;
return 0;
}
-int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
+int iplink_get(char *name, __u32 filt_mask)
{
- int len;
struct iplink_req req = {
.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
- .n.nlmsg_flags = NLM_F_REQUEST | flags,
+ .n.nlmsg_flags = NLM_F_REQUEST,
.n.nlmsg_type = RTM_GETLINK,
.i.ifi_family = preferred_family,
};
- struct {
- struct nlmsghdr n;
- char buf[16384];
- } answer;
+ struct nlmsghdr *answer;
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)
+ if (rtnl_talk(&rth, &req.n, &answer) < 0)
return -2;
open_json_object(NULL);
- if (brief)
- print_linkinfo_brief(NULL, &answer.n, stdout, NULL);
- else
- print_linkinfo(NULL, &answer.n, stdout);
+ print_linkinfo(answer, stdout);
close_json_object();
+ free(answer);
return 0;
}
int fd;
int err;
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
fd = get_ctl_fd();
if (fd < 0)
return -1;
int fd;
int err;
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
- strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr.ifr_newname, newdev, IFNAMSIZ);
fd = get_ctl_fd();
if (fd < 0)
return -1;
if (s < 0)
return -1;
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
perror("SIOCSIFXQLEN");
close(s);
if (s < 0)
return -1;
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
perror("SIOCSIFMTU");
close(s);
return -1;
}
- strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("SIOCGIFINDEX");
close(s);
int alen;
memset(ifr, 0, sizeof(*ifr));
- strncpy(ifr->ifr_name, dev, IFNAMSIZ);
+ strlcpy(ifr->ifr_name, dev, IFNAMSIZ);
ifr->ifr_hwaddr.sa_family = hatype;
alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
if (alen < 0)
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();
if (dev)
duparg2("dev", *argv);
+ if (check_ifname(*argv))
+ invarg("\"dev\" not a valid ifname", *argv);
dev = *argv;
}
argc--; argv++;
}
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;
int ifindex;
};
-static int print_af_stats(const struct sockaddr_nl *who,
- struct nlmsghdr *n,
- void *arg)
+static int print_af_stats(struct nlmsghdr *n, void *arg)
{
struct if_stats_msg *ifsm = NLMSG_DATA(n);
struct rtattr *tb[IFLA_STATS_MAX+1];
}
}
- if (rtnl_wilddump_stats_req_filter(&rth, AF_UNSPEC,
- RTM_GETSTATS,
- filt_mask) < 0) {
+ if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) {
perror("Cannont send dump request");
return 1;
}