X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=system-linux.c;h=c225175b7f4ee10151df4cbf8b445f7caafdeb79;hb=74e0222eeb9e62f4d5073a5b3d9208678782a198;hp=1f7c29b18d6153370947a762b378053fb8d0a473;hpb=c1f6a82c21d6fdb30af178f3b5604a202ab1b26c;p=project%2Fnetifd.git diff --git a/system-linux.c b/system-linux.c index 1f7c29b..c225175 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 @@ -25,11 +26,13 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -43,6 +46,8 @@ #include #include +#include + #ifndef RTN_FAILED_POLICY #define RTN_FAILED_POLICY 12 #endif @@ -69,6 +74,7 @@ #include "netifd.h" #include "device.h" #include "system.h" +#include "utils.h" struct event_socket { struct uloop_fd uloop; @@ -104,12 +110,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 +143,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 +174,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 +263,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 +271,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,9 +369,9 @@ 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_isolate_mode(struct device *dev, const char *val) +static void system_bridge_set_isolated(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/isolate_mode", dev->ifname, 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) @@ -402,6 +425,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); @@ -517,7 +570,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); @@ -629,13 +682,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); + + free(buf); + } + return; + } + + if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) + goto abort; - while ((size = nl_recv(ev->sock, &nla, &buf, NULL)) > 0) { - if (nla.nl_pid == 0) - handle_hotplug_msg((char *) buf, size); + 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) @@ -755,7 +834,7 @@ int system_bridge_addif(struct device *bridge, struct device *dev) if (dev->settings.flags & DEV_OPT_ISOLATE && dev->settings.isolate) - system_bridge_set_isolate_mode(dev, "1"); + system_bridge_set_isolated(dev, "1"); return ret; } @@ -831,7 +910,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: @@ -868,13 +947,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; @@ -900,7 +989,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, @@ -923,6 +1012,7 @@ system_if_clear_entries(struct device *dev, int type, int af) return; } + cb = nl_cb_alloc(NL_CB_DEFAULT); if (!cb) return; @@ -935,10 +1025,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); @@ -951,8 +1044,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; @@ -974,6 +1067,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"); } @@ -1060,41 +1155,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; @@ -1160,6 +1247,30 @@ nla_put_failure: return -ENOMEM; } +int system_link_netns_move(struct device *dev, int netns_fd, const char *target_ifname) +{ + struct nl_msg *msg; + struct ifinfomsg iim = { + .ifi_family = AF_UNSPEC, + }; + + if (!dev) + return -1; + + iim.ifi_index = system_if_resolve(dev); + msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST); + + if (!msg) + return -1; + + nlmsg_append(msg, &iim, sizeof(iim), 0); + if (target_ifname) + nla_put_string(msg, IFLA_IFNAME, target_ifname); + + nla_put_u32(msg, IFLA_NET_NS_FD, netns_fd); + return system_rtnl_call(msg); +} + static int system_link_del(const char *ifname) { struct nl_msg *msg; @@ -1183,6 +1294,20 @@ int system_macvlan_del(struct device *macvlan) return system_link_del(macvlan->ifname); } +int system_netns_open(const pid_t target_ns) +{ + char pid_net_path[PATH_MAX]; + + snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%u/ns/net", target_ns); + + return open(pid_net_path, O_RDONLY); +} + +int system_netns_set(int netns_fd) +{ + return setns(netns_fd, CLONE_NEWNET); +} + int system_veth_add(struct device *veth, struct veth_config *cfg) { struct nl_msg *msg; @@ -1277,8 +1402,10 @@ int system_vlan_del(struct device *dev) int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg) { struct nl_msg *msg; - struct nlattr *linkinfo, *data; + struct nlattr *linkinfo, *data, *qos; struct ifinfomsg iim = { .ifi_family = AF_UNSPEC }; + struct vlan_qos_mapping *elem; + struct ifla_vlan_qos_mapping nl_qos_map; int rv; msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); @@ -1307,6 +1434,26 @@ int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlande netifd_log_message(L_WARNING, "%s Your kernel is older than linux 3.10.0, 802.1ad is not supported defaulting to 802.1q", vlandev->type->name); #endif + if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + + vlist_simple_for_each_element(&cfg->ingress_qos_mapping_list, elem, node) { + nl_qos_map.from = elem->from; + nl_qos_map.to = elem->to; + nla_put(msg, IFLA_VLAN_QOS_MAPPING, sizeof(nl_qos_map), &nl_qos_map); + } + nla_nest_end(msg, qos); + + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + + vlist_simple_for_each_element(&cfg->egress_qos_mapping_list, elem, node) { + nl_qos_map.from = elem->from; + nl_qos_map.to = elem->to; + nla_put(msg, IFLA_VLAN_QOS_MAPPING, sizeof(nl_qos_map), &nl_qos_map); + } + nla_nest_end(msg, qos); + nla_nest_end(msg, data); nla_nest_end(msg, linkinfo); @@ -1589,7 +1736,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); @@ -1682,12 +1832,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) @@ -1849,6 +2016,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; @@ -1881,7 +2088,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; } @@ -2243,7 +2450,7 @@ time_t system_get_rtime(void) struct timespec ts; struct timeval tv; - if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) return ts.tv_sec; if (gettimeofday(&tv, NULL) == 0) @@ -2326,6 +2533,7 @@ static int system_add_ip6_tunnel(const char *name, const unsigned int link, 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)); @@ -2343,8 +2551,8 @@ static int system_add_ip6_tunnel(const char *name, const unsigned int link, } nla_put_u8(nlm, IFLA_IPTUN_ENCAP_LIMIT, encap_limit); - } else - nla_put_u32(nlm, IFLA_IPTUN_FLAGS, IP6_TNL_F_IGN_ENCAP_LIMIT); + tun_flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } } #ifdef IFLA_IPTUN_FMR_MAX @@ -2414,6 +2622,8 @@ static int system_add_ip6_tunnel(const char *name, const unsigned int link, nla_nest_end(nlm, fmrs); } #endif + if (tun_flags) + nla_put_u32(nlm, IFLA_IPTUN_FLAGS, tun_flags); } nla_nest_end(nlm, infodata); @@ -2435,10 +2645,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) @@ -2482,7 +2693,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; } @@ -2519,10 +2730,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) { @@ -2542,13 +2770,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; @@ -2727,6 +2957,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) { @@ -3012,7 +3299,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); @@ -3109,6 +3397,10 @@ int system_add_ip_tunnel(const char *name, struct blob_attr *attr) } 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);