X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=system-linux.c;h=acfd40eaf4f780a8b23ee78bad88e9bc55a5c3e5;hb=5e02f94411b06f192fb2a7d9be9abde3549153a8;hp=0ac4ae626c10307bc728a5019be9bd1b0b5d60db;hpb=7573880ac042c6e5c8d48b1ad83d357b5e02743b;p=oweals%2Fnetifd.git diff --git a/system-linux.c b/system-linux.c index 0ac4ae6..acfd40e 100644 --- a/system-linux.c +++ b/system-linux.c @@ -5,6 +5,7 @@ * Copyright (C) 2013 Steven Barth * Copyright (C) 2014 Gioacchino Mazzurco * Copyright (C) 2017 Matthias Schiffer + * Copyright (C) 2018 Hans Dedecker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -104,12 +106,12 @@ handler_nl_event(struct uloop_fd *u, unsigned int events) switch(err) { case ENOBUFS: - // Increase rx buffer size on netlink socket + /* Increase rx buffer size on netlink socket */ ev->bufsize *= 2; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) goto abort; - // Request full dump since some info got dropped + /* Request full dump since some info got dropped */ struct rtgenmsg msg = { .rtgen_family = AF_UNSPEC }; nl_send_simple(ev->sock, RTM_GETLINK, NLM_F_DUMP, &msg, sizeof(msg)); break; @@ -137,8 +139,10 @@ create_socket(int protocol, int groups) if (groups) nl_join_groups(sock, groups); - if (nl_connect(sock, protocol)) + if (nl_connect(sock, protocol)) { + nl_socket_free(sock); return NULL; + } return sock; } @@ -166,13 +170,28 @@ create_event_socket(struct event_socket *ev, int protocol, if (!create_raw_event_socket(ev, protocol, 0, handler_nl_event, ULOOP_ERROR_CB)) return false; - // Install the valid custom callback handler + /* Install the valid custom callback handler */ nl_socket_modify_cb(ev->sock, NL_CB_VALID, NL_CB_CUSTOM, cb, NULL); - // Disable sequence number checking on event sockets + /* Disable sequence number checking on event sockets */ nl_socket_disable_seq_check(ev->sock); - // Increase rx buffer size to 65K on event sockets + /* Increase rx buffer size to 65K on event sockets */ + ev->bufsize = 65535; + if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) + return false; + + return true; +} + +static bool +create_hotplug_event_socket(struct event_socket *ev, int protocol, + void (*cb)(struct uloop_fd *u, unsigned int events)) +{ + if (!create_raw_event_socket(ev, protocol, 1, cb, ULOOP_ERROR_CB)) + return false; + + /* Increase rx buffer size to 65K on event sockets */ ev->bufsize = 65535; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) return false; @@ -240,7 +259,7 @@ int system_init(void) sock_ioctl = socket(AF_LOCAL, SOCK_DGRAM, 0); system_fd_set_cloexec(sock_ioctl); - // Prepare socket for routing / address control + /* Prepare socket for routing / address control */ sock_rtnl = create_socket(NETLINK_ROUTE, 0); if (!sock_rtnl) return -1; @@ -248,11 +267,11 @@ int system_init(void) if (!create_event_socket(&rtnl_event, NETLINK_ROUTE, cb_rtnl_event)) return -1; - if (!create_raw_event_socket(&hotplug_event, NETLINK_KOBJECT_UEVENT, 1, - handle_hotplug_event, 0)) + if (!create_hotplug_event_socket(&hotplug_event, NETLINK_KOBJECT_UEVENT, + handle_hotplug_event)) return -1; - // Receive network link events form kernel + /* Receive network link events form kernel */ nl_socket_add_membership(rtnl_event.sock, RTNLGRP_LINK); return 0; @@ -346,6 +365,11 @@ static void system_bridge_set_hairpin_mode(struct device *dev, const char *val) system_set_dev_sysctl("/sys/class/net/%s/brport/hairpin_mode", dev->ifname, val); } +static void system_bridge_set_isolated(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/class/net/%s/brport/isolated", dev->ifname, val); +} + static void system_bridge_set_multicast_router(struct device *dev, const char *val, bool bridge) { system_set_dev_sysctl(bridge ? "/sys/class/net/%s/bridge/multicast_router" : @@ -397,6 +421,36 @@ static void system_bridge_set_startup_query_interval(struct device *dev, const c dev->ifname, val); } +static void system_bridge_set_stp_state(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/stp_state", dev->ifname, val); +} + +static void system_bridge_set_forward_delay(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/forward_delay", dev->ifname, val); +} + +static void system_bridge_set_priority(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/priority", dev->ifname, val); +} + +static void system_bridge_set_ageing_time(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/ageing_time", dev->ifname, val); +} + +static void system_bridge_set_hello_time(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/hello_time", dev->ifname, val); +} + +static void system_bridge_set_max_age(struct device *dev, const char *val) +{ + system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/max_age", dev->ifname, val); +} + static void system_bridge_set_learning(struct device *dev, const char *val) { system_set_dev_sysctl("/sys/class/net/%s/brport/learning", dev->ifname, val); @@ -512,7 +566,7 @@ static int system_get_sendredirects(struct device *dev, char *buf, const size_t dev->ifname, buf, buf_sz); } -// Evaluate netlink messages +/* Evaluate netlink messages */ static int cb_rtnl_event(struct nl_msg *msg, void *arg) { struct nlmsghdr *nh = nlmsg_hdr(msg); @@ -543,16 +597,20 @@ out: static void handle_hotplug_msg(char *data, int size) { - const char *subsystem = NULL, *interface = NULL; + const char *subsystem = NULL, *interface = NULL, *interface_old = NULL; char *cur, *end, *sep; struct device *dev; int skip; - bool add; + bool add, move = false; if (!strncmp(data, "add@", 4)) add = true; else if (!strncmp(data, "remove@", 7)) add = false; + else if (!strncmp(data, "move@", 5)) { + add = true; + move = true; + } else return; @@ -573,12 +631,32 @@ handle_hotplug_msg(char *data, int size) subsystem = sep + 1; if (strcmp(subsystem, "net") != 0) return; + } else if (!strcmp(cur, "DEVPATH_OLD")) { + interface_old = strrchr(sep + 1, '/'); + if (interface_old) + interface_old++; } - if (subsystem && interface) + } + + if (subsystem && interface) { + if (move && interface_old) + goto move; + else goto found; } + return; +move: + dev = device_find(interface_old); + if (!dev) + goto found; + + if (dev->type != &simple_device_type) + goto found; + + device_set_present(dev, false); + found: dev = device_find(interface); if (!dev) @@ -600,13 +678,39 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events) struct sockaddr_nl nla; unsigned char *buf = NULL; int size; + int err; + socklen_t errlen = sizeof(err); + + if (!u->error) { + while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) { + if (nla.nl_pid == 0) + handle_hotplug_msg((char *) buf, size); - while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) { - if (nla.nl_pid == 0) - handle_hotplug_msg((char *) buf, size); + free(buf); + } + return; + } + + if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) + goto abort; + + switch(err) { + case ENOBUFS: + /* Increase rx buffer size on netlink socket */ + ev->bufsize *= 2; + if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) + goto abort; + break; - free(buf); + default: + goto abort; } + u->error = false; + return; + +abort: + uloop_fd_delete(&ev->uloop); + return; } static int system_rtnl_call(struct nl_msg *msg) @@ -636,7 +740,7 @@ static int system_bridge_if(const char *bridge, struct device *dev, int cmd, voi ifr.ifr_ifindex = dev->ifindex; else ifr.ifr_data = data; - strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, bridge, sizeof(ifr.ifr_name) - 1); return ioctl(sock_ioctl, cmd, &ifr); } @@ -708,7 +812,7 @@ int system_bridge_addif(struct device *bridge, struct device *dev) system_bridge_set_wireless(bridge, dev); if (dev->settings.flags & DEV_OPT_MULTICAST_ROUTER) { - snprintf(buf, sizeof(buf), "%i", dev->settings.multicast_router); + snprintf(buf, sizeof(buf), "%u", dev->settings.multicast_router); system_bridge_set_multicast_router(dev, buf, false); } @@ -724,6 +828,10 @@ int system_bridge_addif(struct device *bridge, struct device *dev) !dev->settings.unicast_flood) system_bridge_set_unicast_flood(dev, "0"); + if (dev->settings.flags & DEV_OPT_ISOLATE && + dev->settings.isolate) + system_bridge_set_isolated(dev, "1"); + return ret; } @@ -735,7 +843,7 @@ int system_bridge_delif(struct device *bridge, struct device *dev) int system_if_resolve(struct device *dev) { struct ifreq ifr; - strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); if (!ioctl(sock_ioctl, SIOCGIFINDEX, &ifr)) return ifr.ifr_ifindex; else @@ -747,8 +855,10 @@ static int system_if_flags(const char *ifname, unsigned add, unsigned rem) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr); + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + if (ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr) < 0) + return -1; + ifr.ifr_flags |= add; ifr.ifr_flags &= ~rem; return ioctl(sock_ioctl, SIOCSIFFLAGS, &ifr); @@ -796,7 +906,7 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) struct clear_data *clr = arg; struct nlmsghdr *hdr = nlmsg_hdr(msg); bool (*cb)(struct nlmsghdr *, int ifindex); - int type; + int type, ret; switch(clr->type) { case RTM_GETADDR: @@ -833,13 +943,23 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) D(SYSTEM, "Remove %s from device %s\n", type == RTM_DELADDR ? "an address" : "a route", clr->dev->ifname); + memcpy(nlmsg_hdr(clr->msg), hdr, hdr->nlmsg_len); hdr = nlmsg_hdr(clr->msg); hdr->nlmsg_type = type; hdr->nlmsg_flags = NLM_F_REQUEST; nl_socket_disable_auto_ack(sock_rtnl); - nl_send_auto_complete(sock_rtnl, clr->msg); + ret = nl_send_auto_complete(sock_rtnl, clr->msg); + if (ret < 0) { + if (type == RTM_DELRULE) + D(SYSTEM, "Error deleting a rule: %d\n", ret); + else + D(SYSTEM, "Error deleting %s from device '%s': %d\n", + type == RTM_DELADDR ? "an address" : "a route", + clr->dev->ifname, ret); + } + nl_socket_enable_auto_ack(sock_rtnl); return NL_SKIP; @@ -865,7 +985,7 @@ static void system_if_clear_entries(struct device *dev, int type, int af) { struct clear_data clr; - struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct nl_cb *cb; struct rtmsg rtm = { .rtm_family = af, .rtm_flags = RTM_F_CLONED, @@ -888,6 +1008,7 @@ system_if_clear_entries(struct device *dev, int type, int af) return; } + cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) return; @@ -900,10 +1021,13 @@ system_if_clear_entries(struct device *dev, int type, int af) nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_event, &pending); nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &pending); - nl_send_auto_complete(sock_rtnl, clr.msg); + if (nl_send_auto_complete(sock_rtnl, clr.msg) < 0) + goto free; + while (pending > 0) nl_recvmsgs(sock_rtnl, cb); +free: nlmsg_free(clr.msg); out: nl_cb_put(cb); @@ -916,8 +1040,8 @@ void system_if_clear_state(struct device *dev) { static char buf[256]; char *bridge; - device_set_ifindex(dev, system_if_resolve(dev)); + if (dev->external || !dev->ifindex) return; @@ -939,6 +1063,8 @@ void system_if_clear_state(struct device *dev) system_if_clear_entries(dev, RTM_GETADDR, AF_INET); system_if_clear_entries(dev, RTM_GETROUTE, AF_INET6); system_if_clear_entries(dev, RTM_GETADDR, AF_INET6); + system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET); + system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET6); system_set_disable_ipv6(dev, "0"); } @@ -995,7 +1121,7 @@ static void system_bridge_conf_multicast(struct device *bridge, bridge->ifname, buf); if (bridge->settings.flags & DEV_OPT_MULTICAST_ROUTER) { - snprintf(buf, buf_len, "%i", bridge->settings.multicast_router); + snprintf(buf, buf_len, "%u", bridge->settings.multicast_router); system_bridge_set_multicast_router(bridge, buf, true); } @@ -1025,41 +1151,33 @@ static void system_bridge_conf_multicast(struct device *bridge, int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg) { char buf[64]; - unsigned long args[4] = {}; if (ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname) < 0) return -1; - args[0] = BRCTL_SET_BRIDGE_STP_STATE; - args[1] = !!cfg->stp; - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + system_bridge_set_stp_state(bridge, cfg->stp ? "1" : "0"); - args[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; - args[1] = sec_to_jiffies(cfg->forward_delay); - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->forward_delay)); + system_bridge_set_forward_delay(bridge, buf); system_bridge_conf_multicast(bridge, cfg, buf, sizeof(buf)); - args[0] = BRCTL_SET_BRIDGE_PRIORITY; - args[1] = cfg->priority; - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + snprintf(buf, sizeof(buf), "%d", cfg->priority); + system_bridge_set_priority(bridge, buf); if (cfg->flags & BRIDGE_OPT_AGEING_TIME) { - args[0] = BRCTL_SET_AGEING_TIME; - args[1] = sec_to_jiffies(cfg->ageing_time); - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->ageing_time)); + system_bridge_set_ageing_time(bridge, buf); } if (cfg->flags & BRIDGE_OPT_HELLO_TIME) { - args[0] = BRCTL_SET_BRIDGE_HELLO_TIME; - args[1] = sec_to_jiffies(cfg->hello_time); - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->hello_time)); + system_bridge_set_hello_time(bridge, buf); } if (cfg->flags & BRIDGE_OPT_MAX_AGE) { - args[0] = BRCTL_SET_BRIDGE_MAX_AGE; - args[1] = sec_to_jiffies(cfg->max_age); - system_bridge_if(bridge->ifname, NULL, SIOCDEVPRIVATE, &args); + snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->max_age)); + system_bridge_set_max_age(bridge, buf); } return 0; @@ -1215,7 +1333,8 @@ static int system_vlan(struct device *dev, int id) .u.name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, }; - ioctl(sock_ioctl, SIOCSIFVLAN, &ifr); + if (ioctl(sock_ioctl, SIOCSIFVLAN, &ifr) < 0) + return -1; if (id < 0) { ifr.cmd = DEL_VLAN_CMD; @@ -1297,7 +1416,7 @@ system_if_get_settings(struct device *dev, struct device_settings *s) char buf[10]; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); if (ioctl(sock_ioctl, SIOCGIFMTU, &ifr) == 0) { s->mtu = ifr.ifr_mtu; @@ -1362,7 +1481,7 @@ system_if_get_settings(struct device *dev, struct device_settings *s) } if (!system_get_neigh4locktime(dev, buf, sizeof(buf))) { - s->neigh4locktime = strtoul(buf, NULL, 0); + s->neigh4locktime = strtol(buf, NULL, 0); s->flags |= DEV_OPT_NEIGHLOCKTIME; } @@ -1387,40 +1506,6 @@ system_if_get_settings(struct device *dev, struct device_settings *s) } } -static void -system_if_set_rps_xps_val(const char *path, int val) -{ - char val_buf[8]; - glob_t gl; - int i; - - if (glob(path, 0, NULL, &gl)) - return; - - snprintf(val_buf, sizeof(val_buf), "%x", val); - for (i = 0; i < gl.gl_pathc; i++) - system_set_sysctl(gl.gl_pathv[i], val_buf); - - globfree(&gl); -} - -static void -system_if_apply_rps_xps(struct device *dev, struct device_settings *s) -{ - long n_cpus = sysconf(_SC_NPROCESSORS_ONLN); - int val; - - if (n_cpus < 2) - return; - - val = (1 << n_cpus) - 1; - snprintf(dev_buf, sizeof(dev_buf), "/sys/class/net/%s/queues/*/rps_cpus", dev->ifname); - system_if_set_rps_xps_val(dev_buf, s->rps ? val : 0); - - snprintf(dev_buf, sizeof(dev_buf), "/sys/class/net/%s/queues/*/xps_cpus", dev->ifname); - system_if_set_rps_xps_val(dev_buf, s->xps ? val : 0); -} - void system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned int apply_mask) { @@ -1428,7 +1513,7 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned char buf[12]; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); if (s->flags & DEV_OPT_MTU & apply_mask) { ifr.ifr_mtu = s->mtu; if (ioctl(sock_ioctl, SIOCSIFMTU, &ifr) < 0) @@ -1456,23 +1541,23 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned s->flags &= ~DEV_OPT_PROMISC; } if (s->flags & DEV_OPT_RPFILTER & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->rpfilter); + snprintf(buf, sizeof(buf), "%u", s->rpfilter); system_set_rpfilter(dev, buf); } if (s->flags & DEV_OPT_ACCEPTLOCAL & apply_mask) system_set_acceptlocal(dev, s->acceptlocal ? "1" : "0"); if (s->flags & DEV_OPT_IGMPVERSION & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->igmpversion); + snprintf(buf, sizeof(buf), "%u", s->igmpversion); system_set_igmpversion(dev, buf); } if (s->flags & DEV_OPT_MLDVERSION & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->mldversion); + snprintf(buf, sizeof(buf), "%u", s->mldversion); system_set_mldversion(dev, buf); } if (s->flags & DEV_OPT_NEIGHREACHABLETIME & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->neigh4reachabletime); + snprintf(buf, sizeof(buf), "%u", s->neigh4reachabletime); system_set_neigh4reachabletime(dev, buf); - snprintf(buf, sizeof(buf), "%d", s->neigh6reachabletime); + snprintf(buf, sizeof(buf), "%u", s->neigh6reachabletime); system_set_neigh6reachabletime(dev, buf); } if (s->flags & DEV_OPT_NEIGHLOCKTIME & apply_mask) { @@ -1480,13 +1565,13 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned system_set_neigh4locktime(dev, buf); } if (s->flags & DEV_OPT_NEIGHGCSTALETIME & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->neigh4gcstaletime); + snprintf(buf, sizeof(buf), "%u", s->neigh4gcstaletime); system_set_neigh4gcstaletime(dev, buf); - snprintf(buf, sizeof(buf), "%d", s->neigh6gcstaletime); + snprintf(buf, sizeof(buf), "%u", s->neigh6gcstaletime); system_set_neigh6gcstaletime(dev, buf); } if (s->flags & DEV_OPT_DADTRANSMITS & apply_mask) { - snprintf(buf, sizeof(buf), "%d", s->dadtransmits); + snprintf(buf, sizeof(buf), "%u", s->dadtransmits); system_set_dadtransmits(dev, buf); } if (s->flags & DEV_OPT_MULTICAST & apply_mask) { @@ -1496,8 +1581,6 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned } if (s->flags & DEV_OPT_SENDREDIRECTS & apply_mask) system_set_sendredirects(dev, s->sendredirects ? "1" : "0"); - - system_if_apply_rps_xps(dev, s); } int system_if_up(struct device *dev) @@ -1574,6 +1657,9 @@ int system_if_check(struct device *dev) }; int ret = 1; + if (!cb) + return ret; + msg = nlmsg_alloc_simple(RTM_GETLINK, 0); if (!msg) goto out; @@ -1586,7 +1672,10 @@ int system_if_check(struct device *dev) nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_if_check_ack, &chk); nl_cb_err(cb, NL_CB_CUSTOM, cb_if_check_error, &chk); - nl_send_auto_complete(sock_rtnl, msg); + ret = nl_send_auto_complete(sock_rtnl, msg); + if (ret < 0) + goto free; + while (chk.pending > 0) nl_recvmsgs(sock_rtnl, cb); @@ -1679,12 +1768,29 @@ static const struct { uint32_t mask; const char *name; } ethtool_link_modes[] = { - { ADVERTISED_10baseT_Half, "10H" }, - { ADVERTISED_10baseT_Full, "10F" }, - { ADVERTISED_100baseT_Half, "100H" }, - { ADVERTISED_100baseT_Full, "100F" }, - { ADVERTISED_1000baseT_Half, "1000H" }, - { ADVERTISED_1000baseT_Full, "1000F" }, + { ADVERTISED_10baseT_Half, "10baseT-H" }, + { ADVERTISED_10baseT_Full, "10baseT-F" }, + { ADVERTISED_100baseT_Half, "100baseT-H" }, + { ADVERTISED_100baseT_Full, "100baseT-F" }, + { ADVERTISED_1000baseT_Half, "1000baseT-H" }, + { ADVERTISED_1000baseT_Full, "1000baseT-F" }, + { ADVERTISED_1000baseKX_Full, "1000baseKX-F" }, + { ADVERTISED_2500baseX_Full, "2500baseX-F" }, + { ADVERTISED_10000baseT_Full, "10000baseT-F" }, + { ADVERTISED_10000baseKX4_Full, "10000baseKX4-F" }, + { ADVERTISED_10000baseKR_Full, "10000baseKR-F" }, + { ADVERTISED_20000baseMLD2_Full, "20000baseMLD2-F" }, + { ADVERTISED_20000baseKR2_Full, "20000baseKR2-F" }, + { ADVERTISED_40000baseKR4_Full, "40000baseKR4-F" }, + { ADVERTISED_40000baseCR4_Full, "40000baseCR4-F" }, + { ADVERTISED_40000baseSR4_Full, "40000baseSR4-F" }, + { ADVERTISED_40000baseLR4_Full, "40000baseLR4-F" }, +#ifdef ADVERTISED_56000baseKR4_Full + { ADVERTISED_56000baseKR4_Full, "56000baseKR4-F" }, + { ADVERTISED_56000baseCR4_Full, "56000baseCR4-F" }, + { ADVERTISED_56000baseSR4_Full, "56000baseSR4-F" }, + { ADVERTISED_56000baseLR4_Full, "56000baseLR4-F" }, +#endif }; static void system_add_link_modes(struct blob_buf *b, __u32 mask) @@ -1711,16 +1817,12 @@ system_if_dump_info(struct device *dev, struct blob_buf *b) { struct ethtool_cmd ecmd; struct ifreq ifr; - char buf[64], *s; + char *s; void *c; - int dir_fd; - - snprintf(buf, sizeof(buf), "/sys/class/net/%s", dev->ifname); - dir_fd = open(buf, O_DIRECTORY); memset(&ecmd, 0, sizeof(ecmd)); memset(&ifr, 0, sizeof(ifr)); - strcpy(ifr.ifr_name, dev->ifname); + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); ifr.ifr_data = (caddr_t) &ecmd; ecmd.cmd = ETHTOOL_GSET; @@ -1729,6 +1831,10 @@ system_if_dump_info(struct device *dev, struct blob_buf *b) system_add_link_modes(b, ecmd.advertising); blobmsg_close_array(b, c); + c = blobmsg_open_array(b, "link-partner-advertising"); + system_add_link_modes(b, ecmd.lp_advertising); + blobmsg_close_array(b, c); + c = blobmsg_open_array(b, "link-supported"); system_add_link_modes(b, ecmd.supported); blobmsg_close_array(b, c); @@ -1737,9 +1843,10 @@ system_if_dump_info(struct device *dev, struct blob_buf *b) snprintf(s, 8, "%d%c", ethtool_cmd_speed(&ecmd), ecmd.duplex == DUPLEX_HALF ? 'H' : 'F'); blobmsg_add_string_buffer(b); + + blobmsg_add_u8(b, "autoneg", !!ecmd.autoneg); } - close(dir_fd); return 0; } @@ -1845,6 +1952,46 @@ int system_del_address(struct device *dev, struct device_addr *addr) return system_addr(dev, addr, RTM_DELADDR); } +static int system_neigh(struct device *dev, struct device_neighbor *neighbor, int cmd) +{ + int alen = ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16; + unsigned int flags = 0; + struct ndmsg ndm = { + .ndm_family = (alen == 4) ? AF_INET : AF_INET6, + .ndm_ifindex = dev->ifindex, + .ndm_state = NUD_PERMANENT, + .ndm_flags = (neighbor->proxy ? NTF_PROXY : 0) | (neighbor->router ? NTF_ROUTER : 0), + }; + struct nl_msg *msg; + + if (cmd == RTM_NEWNEIGH) + flags |= NLM_F_CREATE | NLM_F_REPLACE; + + msg = nlmsg_alloc_simple(cmd, flags); + + if (!msg) + return -1; + + nlmsg_append(msg, &ndm, sizeof(ndm), 0); + + nla_put(msg, NDA_DST, alen, &neighbor->addr); + if (neighbor->flags & DEVNEIGH_MAC) + nla_put(msg, NDA_LLADDR, sizeof(neighbor->macaddr), &neighbor->macaddr); + + + return system_rtnl_call(msg); +} + +int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor) +{ + return system_neigh(dev, neighbor, RTM_NEWNEIGH); +} + +int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor) +{ + return system_neigh(dev, neighbor, RTM_DELNEIGH); +} + static int system_rt(struct device *dev, struct device_route *route, int cmd) { int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16; @@ -1877,7 +2024,7 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) if (cmd == RTM_NEWROUTE) { flags |= NLM_F_CREATE | NLM_F_REPLACE; - if (!dev) { // Add null-route + if (!dev) { /* Add null-route */ rtm.rtm_scope = RT_SCOPE_UNIVERSE; rtm.rtm_type = RTN_UNREACHABLE; } @@ -2171,6 +2318,9 @@ static int system_iprule(struct iprule *rule, int cmd) nla_put_u32(msg, FRA_TABLE, rule->lookup); } + if (rule->flags & IPRULE_SUP_PREFIXLEN) + nla_put_u32(msg, FRA_SUPPRESS_PREFIXLEN, rule->sup_prefixlen); + if (rule->flags & IPRULE_GOTO) nla_put_u32(msg, FRA_GOTO, rule->gotoid); @@ -2254,11 +2404,175 @@ static int tunnel_ioctl(const char *name, int cmd, void *p) struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name) - 1); ifr.ifr_ifru.ifru_data = p; return ioctl(sock_ioctl, cmd, &ifr); } +#ifdef IFLA_IPTUN_MAX +static int system_add_ip6_tunnel(const char *name, const unsigned int link, + struct blob_attr **tb) +{ + struct nl_msg *nlm = nlmsg_alloc_simple(RTM_NEWLINK, + NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); + struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC }; + struct blob_attr *cur; + int ret = 0, ttl = 0; + + if (!nlm) + return -1; + + nlmsg_append(nlm, &ifi, sizeof(ifi), 0); + nla_put_string(nlm, IFLA_IFNAME, name); + + if (link) + nla_put_u32(nlm, IFLA_LINK, link); + + struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO); + if (!linkinfo) { + ret = -ENOMEM; + goto failure; + } + + nla_put_string(nlm, IFLA_INFO_KIND, "ip6tnl"); + struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA); + if (!infodata) { + ret = -ENOMEM; + goto failure; + } + + if (link) + nla_put_u32(nlm, IFLA_IPTUN_LINK, link); + + if ((cur = tb[TUNNEL_ATTR_TTL])) + ttl = blobmsg_get_u32(cur); + + nla_put_u8(nlm, IFLA_IPTUN_PROTO, IPPROTO_IPIP); + nla_put_u8(nlm, IFLA_IPTUN_TTL, (ttl) ? ttl : 64); + + struct in6_addr in6buf; + if ((cur = tb[TUNNEL_ATTR_LOCAL])) { + if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) { + ret = -EINVAL; + goto failure; + } + nla_put(nlm, IFLA_IPTUN_LOCAL, sizeof(in6buf), &in6buf); + } + + if ((cur = tb[TUNNEL_ATTR_REMOTE])) { + if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) { + ret = -EINVAL; + goto failure; + } + nla_put(nlm, IFLA_IPTUN_REMOTE, sizeof(in6buf), &in6buf); + } + + if ((cur = tb[TUNNEL_ATTR_DATA])) { + struct blob_attr *tb_data[__IPIP6_DATA_ATTR_MAX]; + uint32_t tun_flags = IP6_TNL_F_IGN_ENCAP_LIMIT; + + blobmsg_parse(ipip6_data_attr_list.params, __IPIP6_DATA_ATTR_MAX, tb_data, + blobmsg_data(cur), blobmsg_len(cur)); + + if ((cur = tb_data[IPIP6_DATA_ENCAPLIMIT])) { + char *str = blobmsg_get_string(cur); + + if (strcmp(str, "ignore")) { + char *e; + unsigned encap_limit = strtoul(str, &e, 0); + + if (e == str || *e || encap_limit > 255) { + ret = -EINVAL; + goto failure; + } + + nla_put_u8(nlm, IFLA_IPTUN_ENCAP_LIMIT, encap_limit); + tun_flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } + +#ifdef IFLA_IPTUN_FMR_MAX + if ((cur = tb_data[IPIP6_DATA_FMRS])) { + struct blob_attr *rcur; + unsigned rrem, fmrcnt = 0; + struct nlattr *fmrs = nla_nest_start(nlm, IFLA_IPTUN_FMRS); + + if (!fmrs) { + ret = -ENOMEM; + goto failure; + } + + blobmsg_for_each_attr(rcur, cur, rrem) { + struct blob_attr *tb_fmr[__FMR_DATA_ATTR_MAX], *tb_cur; + struct in6_addr ip6prefix; + struct in_addr ip4prefix; + unsigned ip4len, ip6len, ealen, offset; + + blobmsg_parse(fmr_data_attr_list.params, __FMR_DATA_ATTR_MAX, tb_fmr, + blobmsg_data(rcur), blobmsg_len(rcur)); + + if (!(tb_cur = tb_fmr[FMR_DATA_PREFIX6]) || + !parse_ip_and_netmask(AF_INET6, + blobmsg_data(tb_cur), &ip6prefix, + &ip6len)) { + ret = -EINVAL; + goto failure; + } + + if (!(tb_cur = tb_fmr[FMR_DATA_PREFIX4]) || + !parse_ip_and_netmask(AF_INET, + blobmsg_data(tb_cur), &ip4prefix, + &ip4len)) { + ret = -EINVAL; + goto failure; + } + + if (!(tb_cur = tb_fmr[FMR_DATA_EALEN])) { + ret = -EINVAL; + goto failure; + } + ealen = blobmsg_get_u32(tb_cur); + + if (!(tb_cur = tb_fmr[FMR_DATA_OFFSET])) { + ret = -EINVAL; + goto failure; + } + offset = blobmsg_get_u32(tb_cur); + + struct nlattr *rule = nla_nest_start(nlm, ++fmrcnt); + if (!rule) { + ret = -ENOMEM; + goto failure; + } + + nla_put(nlm, IFLA_IPTUN_FMR_IP6_PREFIX, sizeof(ip6prefix), &ip6prefix); + nla_put(nlm, IFLA_IPTUN_FMR_IP4_PREFIX, sizeof(ip4prefix), &ip4prefix); + nla_put_u8(nlm, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ip6len); + nla_put_u8(nlm, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ip4len); + nla_put_u8(nlm, IFLA_IPTUN_FMR_EA_LEN, ealen); + nla_put_u8(nlm, IFLA_IPTUN_FMR_OFFSET, offset); + + nla_nest_end(nlm, rule); + } + + nla_nest_end(nlm, fmrs); + } +#endif + if (tun_flags) + nla_put_u32(nlm, IFLA_IPTUN_FLAGS, tun_flags); + } + + nla_nest_end(nlm, infodata); + nla_nest_end(nlm, linkinfo); + + return system_rtnl_call(nlm); + +failure: + nlmsg_free(nlm); + return ret; +} +#endif + #ifdef IFLA_IPTUN_MAX #define IP6_FLOWINFO_TCLASS htonl(0x0FF00000) static int system_add_gre_tunnel(const char *name, const char *kind, @@ -2267,10 +2581,11 @@ static int system_add_gre_tunnel(const char *name, const char *kind, struct nl_msg *nlm; struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, }; struct blob_attr *cur; - uint32_t ikey = 0, okey = 0, flags = 0, flowinfo = 0; + uint32_t ikey = 0, okey = 0, flowinfo = 0, flags6 = IP6_TNL_F_IGN_ENCAP_LIMIT; uint16_t iflags = 0, oflags = 0; uint8_t tos = 0; int ret = 0, ttl = 0; + unsigned encap_limit = 0; nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); if (!nlm) @@ -2314,7 +2629,7 @@ static int system_add_gre_tunnel(const char *name, const char *kind, tos = uval; } else { if (v6) - flags |= IP6_TNL_F_USE_ORIG_TCLASS; + flags6 |= IP6_TNL_F_USE_ORIG_TCLASS; else tos = 1; } @@ -2351,10 +2666,27 @@ static int system_add_gre_tunnel(const char *name, const char *kind, iflags |= GRE_SEQ; } - if ((cur = tb[GRE_DATA_OSEQNO])) { + if ((cur = tb_data[GRE_DATA_OSEQNO])) { if (blobmsg_get_bool(cur)) oflags |= GRE_SEQ; } + + if ((cur = tb_data[GRE_DATA_ENCAPLIMIT])) { + char *str = blobmsg_get_string(cur); + + if (strcmp(str, "ignore")) { + char *e; + + encap_limit = strtoul(str, &e, 0); + + if (e == str || *e || encap_limit > 255) { + ret = -EINVAL; + goto failure; + } + + flags6 &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } } if (v6) { @@ -2374,13 +2706,15 @@ static int system_add_gre_tunnel(const char *name, const char *kind, } nla_put(nlm, IFLA_GRE_REMOTE, sizeof(in6buf), &in6buf); } - nla_put_u8(nlm, IFLA_GRE_ENCAP_LIMIT, 4); + + if (!(flags6 & IP6_TNL_F_IGN_ENCAP_LIMIT)) + nla_put_u8(nlm, IFLA_GRE_ENCAP_LIMIT, encap_limit); if (flowinfo) nla_put_u32(nlm, IFLA_GRE_FLOWINFO, flowinfo); - if (flags) - nla_put_u32(nlm, IFLA_GRE_FLAGS, flags); + if (flags6) + nla_put_u32(nlm, IFLA_GRE_FLAGS, flags6); if (!ttl) ttl = 64; @@ -2443,10 +2777,10 @@ static int system_add_gre_tunnel(const char *name, const char *kind, nla_put_u16(nlm, IFLA_GRE_IFLAGS, iflags); if (okey) - nla_put_u32(nlm, IFLA_GRE_OKEY, okey); + nla_put_u32(nlm, IFLA_GRE_OKEY, htonl(okey)); if (ikey) - nla_put_u32(nlm, IFLA_GRE_IKEY, ikey); + nla_put_u32(nlm, IFLA_GRE_IKEY, htonl(ikey)); nla_nest_end(nlm, infodata); nla_nest_end(nlm, linkinfo); @@ -2559,6 +2893,63 @@ failure: } #endif +#ifdef IFLA_XFRM_MAX +static int system_add_xfrm_tunnel(const char *name, const char *kind, + const unsigned int link, struct blob_attr **tb) +{ + struct nl_msg *nlm; + struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, }; + struct blob_attr *cur; + int ret = 0; + + nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); + if (!nlm) + return -1; + + nlmsg_append(nlm, &ifi, sizeof(ifi), 0); + nla_put_string(nlm, IFLA_IFNAME, name); + + struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO); + if (!linkinfo) { + ret = -ENOMEM; + goto failure; + } + + nla_put_string(nlm, IFLA_INFO_KIND, kind); + struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA); + if (!infodata) { + ret = -ENOMEM; + goto failure; + } + + if (link) + nla_put_u32(nlm, IFLA_XFRM_LINK, link); + + if ((cur = tb[TUNNEL_ATTR_DATA])) { + struct blob_attr *tb_data[__XFRM_DATA_ATTR_MAX]; + uint32_t if_id = 0; + + blobmsg_parse(xfrm_data_attr_list.params, __XFRM_DATA_ATTR_MAX, tb_data, + blobmsg_data(cur), blobmsg_len(cur)); + + if ((cur = tb_data[XFRM_DATA_IF_ID])) { + if ((if_id = blobmsg_get_u32(cur))) + nla_put_u32(nlm, IFLA_XFRM_IF_ID, if_id); + } + + } + + nla_nest_end(nlm, infodata); + nla_nest_end(nlm, linkinfo); + + return system_rtnl_call(nlm); + +failure: + nlmsg_free(nlm); + return ret; +} +#endif + #ifdef IFLA_VXLAN_MAX static int system_add_vxlan(const char *name, const unsigned int link, struct blob_attr **tb, bool v6) { @@ -2671,13 +3062,26 @@ static int system_add_vxlan(const char *name, const unsigned int link, struct bl } nla_put_u16(msg, IFLA_VXLAN_PORT, htons(port)); + if ((cur = tb_data[VXLAN_DATA_ATTR_RXCSUM])) { + bool rxcsum = blobmsg_get_bool(cur); + nla_put_u8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, !rxcsum); + } + + if ((cur = tb_data[VXLAN_DATA_ATTR_TXCSUM])) { + bool txcsum = blobmsg_get_bool(cur); + nla_put_u8(msg, IFLA_VXLAN_UDP_CSUM, txcsum); + nla_put_u8(msg, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, !txcsum); + } + if ((cur = tb[TUNNEL_ATTR_TOS])) { char *str = blobmsg_get_string(cur); unsigned tos = 1; if (strcmp(str, "inherit")) { - if (!system_tos_aton(str, &tos)) - return -EINVAL; + if (!system_tos_aton(str, &tos)) { + ret = -EINVAL; + goto failure; + } } nla_put_u8(msg, IFLA_VXLAN_TOS, tos); @@ -2737,7 +3141,7 @@ static int system_add_sit_tunnel(const char *name, const unsigned int link, stru p6.prefixlen = mask; } - if ((cur = tb[SIXRD_DATA_RELAY_PREFIX])) { + if ((cur = tb_data[SIXRD_DATA_RELAY_PREFIX])) { if (!parse_ip_and_netmask(AF_INET, blobmsg_data(cur), &p6.relay_prefix, &mask) || mask > 32) { ret = -EINVAL; @@ -2806,7 +3210,7 @@ static int system_add_proto_tunnel(const char *name, const uint8_t proto, const if (p.iph.ttl && p.iph.frag_off == 0) return -EINVAL; - strncpy(p.name, name, sizeof(p.name)); + strncpy(p.name, name, sizeof(p.name) - 1); switch (p.iph.protocol) { case IPPROTO_IPIP: @@ -2831,7 +3235,8 @@ static int __system_del_ip_tunnel(const char *name, struct blob_attr **tb) if (!strcmp(str, "greip") || !strcmp(str, "gretapip") || !strcmp(str, "greip6") || !strcmp(str, "gretapip6") || !strcmp(str, "vtiip") || !strcmp(str, "vtiip6") || - !strcmp(str, "vxlan") || !strcmp(str, "vxlan6")) + !strcmp(str, "vxlan") || !strcmp(str, "vxlan6") || + !strcmp(str, "xfrm")) return system_link_del(name); else return tunnel_ioctl(name, SIOCDELTUNNEL, NULL); @@ -2913,107 +3318,7 @@ int system_add_ip_tunnel(const char *name, struct blob_attr *attr) return system_add_sit_tunnel(name, link, tb); #ifdef IFLA_IPTUN_MAX else if (!strcmp(str, "ipip6")) { - struct nl_msg *nlm = nlmsg_alloc_simple(RTM_NEWLINK, - NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); - struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC }; - int ret = 0; - - if (!nlm) - return -1; - - nlmsg_append(nlm, &ifi, sizeof(ifi), 0); - nla_put_string(nlm, IFLA_IFNAME, name); - - if (link) - nla_put_u32(nlm, IFLA_LINK, link); - - struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO); - if (!linkinfo) { - ret = -ENOMEM; - goto failure; - } - nla_put_string(nlm, IFLA_INFO_KIND, "ip6tnl"); - struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA); - if (!infodata) { - ret = -ENOMEM; - goto failure; - } - - if (link) - nla_put_u32(nlm, IFLA_IPTUN_LINK, link); - - nla_put_u8(nlm, IFLA_IPTUN_PROTO, IPPROTO_IPIP); - nla_put_u8(nlm, IFLA_IPTUN_TTL, (ttl) ? ttl : 64); - nla_put_u8(nlm, IFLA_IPTUN_ENCAP_LIMIT, 4); - - struct in6_addr in6buf; - if ((cur = tb[TUNNEL_ATTR_LOCAL])) { - if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) { - ret = -EINVAL; - goto failure; - } - nla_put(nlm, IFLA_IPTUN_LOCAL, sizeof(in6buf), &in6buf); - } - - if ((cur = tb[TUNNEL_ATTR_REMOTE])) { - if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) { - ret = -EINVAL; - goto failure; - } - nla_put(nlm, IFLA_IPTUN_REMOTE, sizeof(in6buf), &in6buf); - } - -#ifdef IFLA_IPTUN_FMR_MAX - if ((cur = tb[TUNNEL_ATTR_FMRS])) { - struct nlattr *fmrs = nla_nest_start(nlm, IFLA_IPTUN_FMRS); - - struct blob_attr *fmr; - unsigned rem, fmrcnt = 0; - blobmsg_for_each_attr(fmr, cur, rem) { - if (blobmsg_type(fmr) != BLOBMSG_TYPE_STRING) - continue; - - unsigned ip4len, ip6len, ealen, offset = 6; - char ip6buf[48]; - char ip4buf[16]; - - if (sscanf(blobmsg_get_string(fmr), "%47[^/]/%u,%15[^/]/%u,%u,%u", - ip6buf, &ip6len, ip4buf, &ip4len, &ealen, &offset) < 5) { - ret = -EINVAL; - goto failure; - } - - struct in6_addr ip6prefix; - struct in_addr ip4prefix; - if (inet_pton(AF_INET6, ip6buf, &ip6prefix) != 1 || - inet_pton(AF_INET, ip4buf, &ip4prefix) != 1) { - ret = -EINVAL; - goto failure; - } - - struct nlattr *rule = nla_nest_start(nlm, ++fmrcnt); - - nla_put(nlm, IFLA_IPTUN_FMR_IP6_PREFIX, sizeof(ip6prefix), &ip6prefix); - nla_put(nlm, IFLA_IPTUN_FMR_IP4_PREFIX, sizeof(ip4prefix), &ip4prefix); - nla_put_u8(nlm, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ip6len); - nla_put_u8(nlm, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ip4len); - nla_put_u8(nlm, IFLA_IPTUN_FMR_EA_LEN, ealen); - nla_put_u8(nlm, IFLA_IPTUN_FMR_OFFSET, offset); - - nla_nest_end(nlm, rule); - } - - nla_nest_end(nlm, fmrs); - } -#endif - - nla_nest_end(nlm, infodata); - nla_nest_end(nlm, linkinfo); - - return system_rtnl_call(nlm); -failure: - nlmsg_free(nlm); - return ret; + return system_add_ip6_tunnel(name, link, tb); } else if (!strcmp(str, "greip")) { return system_add_gre_tunnel(name, "gre", link, tb, false); } else if (!strcmp(str, "gretapip")) { @@ -3028,6 +3333,10 @@ failure: } else if (!strcmp(str, "vtiip6")) { return system_add_vti_tunnel(name, "vti6", link, tb, true); #endif +#ifdef IFLA_XFRM_MAX + } else if (!strcmp(str, "xfrm")) { + return system_add_xfrm_tunnel(name, "xfrm", link, tb); +#endif #ifdef IFLA_VXLAN_MAX } else if(!strcmp(str, "vxlan")) { return system_add_vxlan(name, link, tb, false);