X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=system-linux.c;h=4463a2a8282a1151cb99734d65dd57689a7fad52;hb=HEAD;hp=5ea95582cb006da0d80069038aa887b2810482fe;hpb=85f01c44a950be8518ce5a7d251b5bba219348cf;p=project%2Fnetifd.git diff --git a/system-linux.c b/system-linux.c index 5ea9558..4463a2a 100644 --- a/system-linux.c +++ b/system-linux.c @@ -48,6 +48,8 @@ #include +#include "ethtool-modes.h" + #ifndef RTN_FAILED_POLICY #define RTN_FAILED_POLICY 12 #endif @@ -89,27 +91,92 @@ static int cb_rtnl_event(struct nl_msg *msg, void *arg); static void handle_hotplug_event(struct uloop_fd *u, unsigned int events); static int system_add_proto_tunnel(const char *name, const uint8_t proto, const unsigned int link, struct blob_attr **tb); -static int __system_del_ip_tunnel(const char *name, struct blob_attr **tb); static char dev_buf[256]; +static const char *proc_path = "/proc"; +static const char *sysfs_path = "/sys"; + +struct netdev_type { + unsigned short id; + const char *name; +}; + +static const struct netdev_type netdev_types[] = { + {ARPHRD_NETROM, "netrom"}, + {ARPHRD_ETHER, "ethernet"}, + {ARPHRD_EETHER, "eethernet"}, + {ARPHRD_AX25, "ax25"}, + {ARPHRD_PRONET, "pronet"}, + {ARPHRD_CHAOS, "chaos"}, + {ARPHRD_IEEE802, "ieee802"}, + {ARPHRD_ARCNET, "arcnet"}, + {ARPHRD_APPLETLK, "appletlk"}, + {ARPHRD_DLCI, "dlci"}, + {ARPHRD_ATM, "atm"}, + {ARPHRD_METRICOM, "metricom"}, + {ARPHRD_IEEE1394, "ieee1394"}, + {ARPHRD_EUI64, "eui64"}, + {ARPHRD_INFINIBAND, "infiniband"}, + {ARPHRD_SLIP, "slip"}, + {ARPHRD_CSLIP, "cslip"}, + {ARPHRD_SLIP6, "slip6"}, + {ARPHRD_CSLIP6, "cslip6"}, + {ARPHRD_RSRVD, "rsrvd"}, + {ARPHRD_ADAPT, "adapt"}, + {ARPHRD_ROSE, "rose"}, + {ARPHRD_X25, "x25"}, + {ARPHRD_HWX25, "hwx25"}, + {ARPHRD_PPP, "ppp"}, + {ARPHRD_CISCO, "cisco"}, + {ARPHRD_LAPB, "lapb"}, + {ARPHRD_DDCMP, "ddcmp"}, + {ARPHRD_RAWHDLC, "rawhdlc"}, + {ARPHRD_TUNNEL, "tunnel"}, + {ARPHRD_TUNNEL6, "tunnel6"}, + {ARPHRD_FRAD, "frad"}, + {ARPHRD_SKIP, "skip"}, + {ARPHRD_LOOPBACK, "loopback"}, + {ARPHRD_LOCALTLK, "localtlk"}, + {ARPHRD_FDDI, "fddi"}, + {ARPHRD_BIF, "bif"}, + {ARPHRD_SIT, "sit"}, + {ARPHRD_IPDDP, "ipddp"}, + {ARPHRD_IPGRE, "ipgre"}, + {ARPHRD_PIMREG,"pimreg"}, + {ARPHRD_HIPPI, "hippi"}, + {ARPHRD_ASH, "ash"}, + {ARPHRD_ECONET, "econet"}, + {ARPHRD_IRDA, "irda"}, + {ARPHRD_FCPP, "fcpp"}, + {ARPHRD_FCAL, "fcal"}, + {ARPHRD_FCPL, "fcpl"}, + {ARPHRD_FCFABRIC, "fcfabric"}, + {ARPHRD_IEEE80211, "ieee80211"}, + {ARPHRD_IEEE80211_PRISM, "ie80211-prism"}, + {ARPHRD_IEEE80211_RADIOTAP, "ieee80211-radiotap"}, +#ifdef ARPHRD_PHONET + {ARPHRD_PHONET, "phonet"}, +#endif +#ifdef ARPHRD_PHONET_PIPE + {ARPHRD_PHONET_PIPE, "phonet-pipe"}, +#endif + {ARPHRD_IEEE802154, "ieee802154"}, + {ARPHRD_VOID, "void"}, + {ARPHRD_NONE, "none"} +}; static void handler_nl_event(struct uloop_fd *u, unsigned int events) { struct event_socket *ev = container_of(u, struct event_socket, uloop); - int err; - socklen_t errlen = sizeof(err); + int ret; - if (!u->error) { - nl_recvmsgs_default(ev->sock); + ret = nl_recvmsgs_default(ev->sock); + if (ret >= 0) return; - } - - if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) - goto abort; - switch(err) { - case ENOBUFS: + switch (-ret) { + case NLE_NOMEM: /* Increase rx buffer size on netlink socket */ ev->bufsize *= 2; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) @@ -123,7 +190,6 @@ handler_nl_event(struct uloop_fd *u, unsigned int events) default: goto abort; } - u->error = false; return; abort: @@ -131,6 +197,14 @@ abort: return; } +static void +nl_udebug_cb(void *priv, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + + udebug_netlink_msg(priv, nlmsg_get_proto(msg), nlh, nlh->nlmsg_len); +} + static struct nl_sock * create_socket(int protocol, int groups) { @@ -148,6 +222,9 @@ create_socket(int protocol, int groups) return NULL; } + nl_socket_set_tx_debug_cb(sock, nl_udebug_cb, &udb_nl); + nl_socket_set_rx_debug_cb(sock, nl_udebug_cb, &udb_nl); + return sock; } @@ -281,7 +358,7 @@ int system_init(void) return 0; } -static void system_set_sysctl(const char *path, const char *val) +static void write_file(const char *path, const char *val) { int fd; @@ -293,383 +370,369 @@ static void system_set_sysctl(const char *path, const char *val) close(fd); } -static void system_set_dev_sysctl(const char *path, const char *device, const char *val) +static int read_file(const char *path, char *buf, const size_t buf_sz) { - snprintf(dev_buf, sizeof(dev_buf), path, device); - system_set_sysctl(dev_buf, val); -} + int fd = -1, ret = -1; -static void system_set_disable_ipv6(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname, val); -} + fd = open(path, O_RDONLY); + if (fd < 0) + goto out; -static void system_set_ip6segmentrouting(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled", dev->ifname, val); -} + ssize_t len = read(fd, buf, buf_sz - 1); + if (len < 0) + goto out; -static void system_set_rpfilter(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter", dev->ifname, val); -} + ret = buf[len] = 0; -static void system_set_acceptlocal(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/accept_local", dev->ifname, val); -} +out: + if (fd >= 0) + close(fd); -static void system_set_igmpversion(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/force_igmp_version", dev->ifname, val); + return ret; } -static void system_set_mldversion(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/force_mld_version", dev->ifname, val); -} -static void system_set_neigh4reachabletime(struct device *dev, const char *val) +static const char * +dev_sysctl_path(const char *prefix, const char *ifname, const char *file) { - system_set_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/base_reachable_time_ms", dev->ifname, val); -} + snprintf(dev_buf, sizeof(dev_buf), "%s/sys/net/%s/%s/%s", proc_path, prefix, ifname, file); -static void system_set_neigh6reachabletime(struct device *dev, const char *val) -{ - system_set_dev_sysctl("/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", dev->ifname, val); + return dev_buf; } -static void system_set_neigh4gcstaletime(struct device *dev, const char *val) +static const char * +dev_sysfs_path(const char *ifname, const char *file) { - system_set_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/gc_stale_time", dev->ifname, val); + snprintf(dev_buf, sizeof(dev_buf), "%s/class/net/%s/%s", sysfs_path, ifname, file); + + return dev_buf; } -static void system_set_neigh6gcstaletime(struct device *dev, const char *val) +static void +system_set_dev_sysctl(const char *prefix, const char *file, const char *ifname, + const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv6/neigh/%s/gc_stale_time", dev->ifname, val); + write_file(dev_sysctl_path(prefix, ifname, file), val); } -static void system_set_neigh4locktime(struct device *dev, const char *val) +static int +system_get_dev_sysctl(const char *prefix, const char *file, const char *ifname, + char *buf, size_t buf_sz) { - system_set_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/locktime", dev->ifname, val); + return read_file(dev_sysctl_path(prefix, ifname, file), buf, buf_sz); } -static void system_set_dadtransmits(struct device *dev, const char *val) +static void +system_set_dev_sysfs(const char *file, const char *ifname, const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", dev->ifname, val); + if (!val) + return; + + write_file(dev_sysfs_path(ifname, file), val); } -static void system_set_sendredirects(struct device *dev, const char *val) +static void +system_set_dev_sysfs_int(const char *file, const char *ifname, int val) { - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/send_redirects", dev->ifname, val); + char buf[16]; + + snprintf(buf, sizeof(buf), "%d", val); + system_set_dev_sysfs(file, ifname, buf); } -static void system_set_drop_v4_unicast_in_l2_multicast(struct device *dev, const char *val) +static int +system_get_dev_sysfs(const char *file, const char *ifname, char *buf, size_t buf_sz) { - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val); + return read_file(dev_sysfs_path(ifname, file), buf, buf_sz); } -static void system_set_drop_v6_unicast_in_l2_multicast(struct device *dev, const char *val) +static void system_set_disable_ipv6(struct device *dev, const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "disable_ipv6", dev->ifname, val); } -static void system_set_drop_gratuitous_arp(struct device *dev, const char *val) +static void system_set_ip6segmentrouting(struct device *dev, const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "seg6_enabled", dev->ifname, val); } -static void system_set_drop_unsolicited_na(struct device *dev, const char *val) +static void system_set_rpfilter(struct device *dev, const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "rp_filter", dev->ifname, val); } -static void system_set_arp_accept(struct device *dev, const char *val) +static void system_set_acceptlocal(struct device *dev, const char *val) { - system_set_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept", dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "accept_local", dev->ifname, val); } -static void system_bridge_set_multicast_to_unicast(struct device *dev, const char *val) +static void system_set_igmpversion(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_to_unicast", dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "force_igmp_version", dev->ifname, val); } -static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val) +static void system_set_mldversion(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/multicast_fast_leave", dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "force_mld_version", dev->ifname, val); } -static void system_bridge_set_hairpin_mode(struct device *dev, const char *val) +static void system_set_neigh4reachabletime(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/hairpin_mode", dev->ifname, val); + system_set_dev_sysctl("ipv4/neigh", "base_reachable_time_ms", dev->ifname, val); } -static void system_bridge_set_isolated(struct device *dev, const char *val) +static void system_set_neigh6reachabletime(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/isolated", dev->ifname, val); + system_set_dev_sysctl("ipv6/neigh", "base_reachable_time_ms", dev->ifname, val); } -static void system_bridge_set_multicast_router(struct device *dev, const char *val, bool bridge) +static void system_set_neigh4gcstaletime(struct device *dev, const char *val) { - system_set_dev_sysctl(bridge ? "/sys/class/net/%s/bridge/multicast_router" : - "/sys/class/net/%s/brport/multicast_router", - dev->ifname, val); + system_set_dev_sysctl("ipv4/neigh", "gc_stale_time", dev->ifname, val); } -static void system_bridge_set_robustness(struct device *dev, const char *val) +static void system_set_neigh6gcstaletime(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_startup_query_count", - dev->ifname, val); - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_last_member_count", - dev->ifname, val); + system_set_dev_sysctl("ipv6/neigh", "gc_stale_time", dev->ifname, val); } -static void system_bridge_set_query_interval(struct device *dev, const char *val) +static void system_set_neigh4locktime(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_query_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv4/neigh", "locktime", dev->ifname, val); } -static void system_bridge_set_query_response_interval(struct device *dev, const char *val) +static void system_set_dadtransmits(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_query_response_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "dad_transmits", dev->ifname, val); } -static void system_bridge_set_last_member_interval(struct device *dev, const char *val) +static void system_set_sendredirects(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_last_member_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "send_redirects", dev->ifname, val); } -static void system_bridge_set_membership_interval(struct device *dev, const char *val) +static void system_set_drop_v4_unicast_in_l2_multicast(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_membership_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "drop_unicast_in_l2_multicast", dev->ifname, val); } -static void system_bridge_set_other_querier_timeout(struct device *dev, const char *val) +static void system_set_drop_v6_unicast_in_l2_multicast(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_querier_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "drop_unicast_in_l2_multicast", dev->ifname, val); } -static void system_bridge_set_startup_query_interval(struct device *dev, const char *val) +static void system_set_drop_gratuitous_arp(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_startup_query_interval", - dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "drop_gratuitous_arp", dev->ifname, val); } -static void system_bridge_set_stp_state(struct device *dev, const char *val) +static void system_set_drop_unsolicited_na(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/stp_state", dev->ifname, val); + system_set_dev_sysctl("ipv6/conf", "drop_unsolicited_na", dev->ifname, val); } -static void system_bridge_set_forward_delay(struct device *dev, const char *val) +static void system_set_arp_accept(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/forward_delay", dev->ifname, val); + system_set_dev_sysctl("ipv4/conf", "arp_accept", dev->ifname, val); } -static void system_bridge_set_priority(struct device *dev, const char *val) +static void system_bridge_set_multicast_to_unicast(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/priority", dev->ifname, val); + system_set_dev_sysfs("brport/multicast_to_unicast", dev->ifname, val); } -static void system_bridge_set_ageing_time(struct device *dev, const char *val) +static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/ageing_time", dev->ifname, val); + system_set_dev_sysfs("brport/multicast_fast_leave", dev->ifname, val); } -static void system_bridge_set_hello_time(struct device *dev, const char *val) +static void system_bridge_set_hairpin_mode(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/hello_time", dev->ifname, val); + system_set_dev_sysfs("brport/hairpin_mode", dev->ifname, val); } -static void system_bridge_set_max_age(struct device *dev, const char *val) +static void system_bridge_set_proxyarp_wifi(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/max_age", dev->ifname, val); + system_set_dev_sysfs("brport/proxyarp_wifi", dev->ifname, val); } -static void system_bridge_set_learning(struct device *dev, const char *val) +static void system_bridge_set_bpdu_filter(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/class/net/%s/brport/learning", dev->ifname, val); + system_set_dev_sysfs("brport/bpdu_filter", dev->ifname, val); } -static void system_bridge_set_unicast_flood(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/unicast_flood", dev->ifname, val); + system_set_dev_sysfs("brport/isolated", dev->ifname, val); } -static void system_bridge_set_vlan_filtering(struct device *dev, const char *val) +static void system_bridge_set_multicast_router(struct device *dev, const char *val) { - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/vlan_filtering", dev->ifname, val); + system_set_dev_sysfs("brport/multicast_router", dev->ifname, val); } -static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz) +void system_bridge_set_stp_state(struct device *dev, bool val) { - int fd = -1, ret = -1; - - fd = open(path, O_RDONLY); - if (fd < 0) - goto out; + const char *valstr = val ? "1" : "0"; - ssize_t len = read(fd, buf, buf_sz - 1); - if (len < 0) - goto out; - - ret = buf[len] = 0; - -out: - if (fd >= 0) - close(fd); + system_set_dev_sysfs("bridge/stp_state", dev->ifname, valstr); +} - return ret; +static void system_bridge_set_learning(struct device *dev, const char *val) +{ + system_set_dev_sysfs("brport/learning", dev->ifname, val); } -static int -system_get_dev_sysctl(const char *path, const char *device, char *buf, const size_t buf_sz) +static void system_bridge_set_unicast_flood(struct device *dev, const char *val) { - snprintf(dev_buf, sizeof(dev_buf), path, device); - return system_get_sysctl(dev_buf, buf, buf_sz); + system_set_dev_sysfs("brport/unicast_flood", dev->ifname, val); } static int system_get_disable_ipv6(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv6/conf", "disable_ipv6", + dev->ifname, buf, buf_sz); } static int system_get_ip6segmentrouting(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/seg6_enabled", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv6/conf", "seg6_enabled", + dev->ifname, buf, buf_sz); } static int system_get_rpfilter(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/rp_filter", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv4/conf", "rp_filter", + dev->ifname, buf, buf_sz); } static int system_get_acceptlocal(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/accept_local", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv4/conf", "accept_local", + dev->ifname, buf, buf_sz); } static int system_get_igmpversion(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/force_igmp_version", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv4/conf", "force_igmp_version", + dev->ifname, buf, buf_sz); } static int system_get_mldversion(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/force_mld_version", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv6/conf", "force_mld_version", + dev->ifname, buf, buf_sz); } static int system_get_neigh4reachabletime(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/base_reachable_time_ms", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv4/neigh", "base_reachable_time_ms", + dev->ifname, buf, buf_sz); } static int system_get_neigh6reachabletime(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv6/neigh", "base_reachable_time_ms", + dev->ifname, buf, buf_sz); } static int system_get_neigh4gcstaletime(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/gc_stale_time", - dev->ifname, buf, buf_sz); + return system_get_dev_sysctl("ipv4/neigh", "gc_stale_time", + dev->ifname, buf, buf_sz); } static int system_get_neigh6gcstaletime(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/neigh/%s/gc_stale_time", + return system_get_dev_sysctl("ipv6/neigh", "gc_stale_time", dev->ifname, buf, buf_sz); } static int system_get_neigh4locktime(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/neigh/%s/locktime", + return system_get_dev_sysctl("ipv4/neigh", "locktime", dev->ifname, buf, buf_sz); } static int system_get_dadtransmits(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/dad_transmits", + return system_get_dev_sysctl("ipv6/conf", "dad_transmits", dev->ifname, buf, buf_sz); } static int system_get_sendredirects(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/send_redirects", + return system_get_dev_sysctl("ipv4/conf", "send_redirects", dev->ifname, buf, buf_sz); } static int system_get_drop_v4_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast", + return system_get_dev_sysctl("ipv4/conf", "drop_unicast_in_l2_multicast", dev->ifname, buf, buf_sz); } static int system_get_drop_v6_unicast_in_l2_multicast(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast", + return system_get_dev_sysctl("ipv6/conf", "drop_unicast_in_l2_multicast", dev->ifname, buf, buf_sz); } static int system_get_drop_gratuitous_arp(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp", + return system_get_dev_sysctl("ipv4/conf", "drop_gratuitous_arp", dev->ifname, buf, buf_sz); } static int system_get_drop_unsolicited_na(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na", + return system_get_dev_sysctl("ipv6/conf", "drop_unsolicited_na", dev->ifname, buf, buf_sz); } static int system_get_arp_accept(struct device *dev, char *buf, const size_t buf_sz) { - return system_get_dev_sysctl("/proc/sys/net/ipv4/conf/%s/arp_accept", + return system_get_dev_sysctl("ipv4/conf", "arp_accept", dev->ifname, buf, buf_sz); } +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 +#endif + +static void +system_device_update_state(struct device *dev, unsigned int flags, unsigned int ifindex) +{ + if (dev->type == &simple_device_type) { + if (dev->external) + device_set_disabled(dev, !(flags & IFF_UP)); + + device_set_present(dev, ifindex > 0); + } + device_set_link(dev, flags & IFF_LOWER_UP ? true : false); +} + /* Evaluate netlink messages */ static int cb_rtnl_event(struct nl_msg *msg, void *arg) { struct nlmsghdr *nh = nlmsg_hdr(msg); + struct ifinfomsg *ifi = NLMSG_DATA(nh); struct nlattr *nla[__IFLA_MAX]; - int link_state = 0; - char buf[10]; + struct device *dev; if (nh->nlmsg_type != RTM_NEWLINK) - goto out; + return 0; nlmsg_parse(nh, sizeof(struct ifinfomsg), nla, __IFLA_MAX - 1, NULL); if (!nla[IFLA_IFNAME]) - goto out; + return 0; - struct device *dev = device_find(nla_data(nla[IFLA_IFNAME])); + dev = device_find(nla_data(nla[IFLA_IFNAME])); if (!dev) - goto out; - - if (!system_get_dev_sysctl("/sys/class/net/%s/carrier", dev->ifname, buf, sizeof(buf))) - link_state = strtoul(buf, NULL, 0); - - if (dev->type == &simple_device_type) - device_set_present(dev, true); - - device_set_link(dev, link_state ? true : false); + return 0; -out: + system_device_update_state(dev, ifi->ifi_flags, ifi->ifi_index); return 0; } @@ -728,24 +791,19 @@ 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; + free(buf); } - if (getsockopt(u->fd, SOL_SOCKET, SO_ERROR, (void *)&err, &errlen)) - goto abort; + switch (-size) { + case 0: + return; - switch(err) { - case ENOBUFS: + case NLE_NOMEM: /* Increase rx buffer size on netlink socket */ ev->bufsize *= 2; if (nl_socket_set_buffer_size(ev->sock, ev->bufsize, 0)) @@ -755,7 +813,6 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events) default: goto abort; } - u->error = false; return; abort: @@ -776,9 +833,44 @@ static int system_rtnl_call(struct nl_msg *msg) return nl_wait_for_ack(sock_rtnl); } +static struct nl_msg *__system_ifinfo_msg(int af, int index, const char *ifname, uint16_t type, uint16_t flags) +{ + struct nl_msg *msg; + struct ifinfomsg iim = { + .ifi_family = af, + .ifi_index = index, + }; + + msg = nlmsg_alloc_simple(type, flags | NLM_F_REQUEST); + if (!msg) + return NULL; + + nlmsg_append(msg, &iim, sizeof(iim), 0); + if (ifname) + nla_put_string(msg, IFLA_IFNAME, ifname); + + return msg; +} + +static struct nl_msg *system_ifinfo_msg(const char *ifname, uint16_t type, uint16_t flags) +{ + return __system_ifinfo_msg(AF_UNSPEC, 0, ifname, type, flags); +} + +static int system_link_del(const char *ifname) +{ + struct nl_msg *msg; + + msg = system_ifinfo_msg(ifname, RTM_DELLINK, 0); + if (!msg) + return -1; + + return system_rtnl_call(msg); +} + int system_bridge_delbr(struct device *bridge) { - return ioctl(sock_ioctl, SIOCBRDELBR, bridge->ifname); + return system_link_del(bridge->ifname); } static int system_bridge_if(const char *bridge, struct device *dev, int cmd, void *data) @@ -794,15 +886,11 @@ static int system_bridge_if(const char *bridge, struct device *dev, int cmd, voi return ioctl(sock_ioctl, cmd, &ifr); } -static bool system_is_bridge(const char *name, char *buf, int buflen) +static bool system_is_bridge(const char *name) { struct stat st; - snprintf(buf, buflen, "/sys/devices/virtual/net/%s/bridge", name); - if (stat(buf, &st) < 0) - return false; - - return true; + return stat(dev_sysfs_path(name, "bridge"), &st) >= 0; } static char *system_get_bridge(const char *name, char *buf, int buflen) @@ -811,7 +899,7 @@ static char *system_get_bridge(const char *name, char *buf, int buflen) ssize_t len = -1; glob_t gl; - snprintf(buf, buflen, "/sys/devices/virtual/net/*/brif/%s/bridge", name); + snprintf(buf, buflen, "%s/devices/virtual/net/*/brif/%s/bridge", sysfs_path, name); if (glob(buf, GLOB_NOSORT, NULL, &gl) < 0) return NULL; @@ -835,17 +923,21 @@ static void system_bridge_set_wireless(struct device *bridge, struct device *dev) { bool mcast_to_ucast = dev->wireless_ap; - bool hairpin = true; + bool hairpin; - if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && - !bridge->settings.multicast_to_unicast) + if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST) + mcast_to_ucast = dev->settings.multicast_to_unicast; + else if (bridge->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST && + !bridge->settings.multicast_to_unicast) mcast_to_ucast = false; - if (!mcast_to_ucast || dev->wireless_isolate) + hairpin = mcast_to_ucast || dev->wireless_proxyarp; + if (dev->wireless_isolate) hairpin = false; system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0"); system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0"); + system_bridge_set_proxyarp_wifi(dev, dev->wireless_proxyarp ? "1" : "0"); } int system_bridge_addif(struct device *bridge, struct device *dev) @@ -855,16 +947,19 @@ int system_bridge_addif(struct device *bridge, struct device *dev) int tries = 0; int ret; -retry: - ret = 0; - oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf)); - if (!oldbr || strcmp(oldbr, bridge->ifname) != 0) { + + for (tries = 0; tries < 3; tries++) { + ret = 0; + oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf)); + if (oldbr && !strcmp(oldbr, bridge->ifname)) + break; + ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL); - tries++; - D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s\n", + if (!ret) + break; + + D(SYSTEM, "Failed to add device '%s' to bridge '%s' (tries=%d): %s", dev->ifname, bridge->ifname, tries, strerror(errno)); - if (tries <= 3) - goto retry; } if (dev->wireless) @@ -872,7 +967,7 @@ retry: if (dev->settings.flags & DEV_OPT_MULTICAST_ROUTER) { snprintf(buf, sizeof(buf), "%u", dev->settings.multicast_router); - system_bridge_set_multicast_router(dev, buf, false); + system_bridge_set_multicast_router(dev, buf); } if (dev->settings.flags & DEV_OPT_MULTICAST_FAST_LEAVE && @@ -891,6 +986,9 @@ retry: dev->settings.isolate) system_bridge_set_isolated(dev, "1"); + if (dev->bpdu_filter) + system_bridge_set_bpdu_filter(dev, dev->bpdu_filter ? "1" : "0"); + return ret; } @@ -899,25 +997,23 @@ int system_bridge_delif(struct device *bridge, struct device *dev) return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL); } -int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int vflags) +int system_bridge_vlan(const char *iface, uint16_t vid, int16_t vid_end, bool add, unsigned int vflags) { - struct ifinfomsg ifi = { .ifi_family = PF_BRIDGE, }; struct bridge_vlan_info vinfo = { .vid = vid, }; unsigned short flags = 0; struct nlattr *afspec; struct nl_msg *nlm; + int index; int ret = 0; - ifi.ifi_index = if_nametoindex(iface); - if (!ifi.ifi_index) + index = if_nametoindex(iface); + if (!index) return -1; - nlm = nlmsg_alloc_simple(add ? RTM_SETLINK : RTM_DELLINK, NLM_F_REQUEST); + nlm = __system_ifinfo_msg(PF_BRIDGE, index, NULL, add ? RTM_SETLINK : RTM_DELLINK, 0); if (!nlm) return -1; - nlmsg_append(nlm, &ifi, sizeof(ifi), 0); - if (vflags & BRVLAN_F_SELF) flags |= BRIDGE_FLAGS_SELF; @@ -936,7 +1032,18 @@ int system_bridge_vlan(const char *iface, uint16_t vid, bool add, unsigned int v if (flags) nla_put_u16(nlm, IFLA_BRIDGE_FLAGS, flags); + if (vid_end > vid) + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; + nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + + if (vid_end > vid) { + vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; + vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; + vinfo.vid = vid_end; + nla_put(nlm, IFLA_BRIDGE_VLAN_INFO, sizeof(vinfo), &vinfo); + } + nla_nest_end(nlm, afspec); return system_rtnl_call(nlm); @@ -946,6 +1053,91 @@ failure: return ret; } +int system_bonding_set_device(struct device *dev, struct bonding_config *cfg) +{ + const char *ifname = dev->ifname; + struct blob_attr *cur; + char op = cfg ? '+' : '-'; + char buf[64]; + size_t rem; + + snprintf(dev_buf, sizeof(dev_buf), "%s/class/net/bonding_masters", sysfs_path); + snprintf(buf, sizeof(buf), "%c%s", op, ifname); + write_file(dev_buf, buf); + + if (!cfg) + return 0; + + system_set_dev_sysfs("bonding/mode", ifname, bonding_policy_str[cfg->policy]); + + system_set_dev_sysfs_int("bonding/all_ports_active", ifname, cfg->all_ports_active); + + if (cfg->policy == BONDING_MODE_BALANCE_XOR || + cfg->policy == BONDING_MODE_BALANCE_TLB || + cfg->policy == BONDING_MODE_8023AD) + system_set_dev_sysfs("bonding/xmit_hash_policy", ifname, cfg->xmit_hash_policy); + + if (cfg->policy == BONDING_MODE_8023AD) { + system_set_dev_sysfs("bonding/ad_actor_system", ifname, cfg->ad_actor_system); + system_set_dev_sysfs_int("bonding/ad_actor_sys_prio", ifname, cfg->ad_actor_sys_prio); + system_set_dev_sysfs("bonding/ad_select", ifname, cfg->ad_select); + system_set_dev_sysfs("bonding/lacp_rate", ifname, cfg->lacp_rate); + system_set_dev_sysfs_int("bonding/min_links", ifname, cfg->min_links); + } + + if (cfg->policy == BONDING_MODE_BALANCE_RR) + system_set_dev_sysfs_int("bonding/packets_per_slave", ifname, cfg->packets_per_port); + + if (cfg->policy == BONDING_MODE_BALANCE_TLB || + cfg->policy == BONDING_MODE_BALANCE_ALB) + system_set_dev_sysfs_int("bonding/lp_interval", ifname, cfg->lp_interval); + + if (cfg->policy == BONDING_MODE_BALANCE_TLB) + system_set_dev_sysfs_int("bonding/tlb_dynamic_lb", ifname, cfg->dynamic_lb); + system_set_dev_sysfs_int("bonding/resend_igmp", ifname, cfg->resend_igmp); + system_set_dev_sysfs_int("bonding/num_grat_arp", ifname, cfg->num_peer_notif); + system_set_dev_sysfs("bonding/primary_reselect", ifname, cfg->primary_reselect); + system_set_dev_sysfs("bonding/fail_over_mac", ifname, cfg->failover_mac); + + system_set_dev_sysfs_int((cfg->monitor_arp ? + "bonding/arp_interval" : + "bonding/miimon"), ifname, cfg->monitor_interval); + + blobmsg_for_each_attr(cur, cfg->arp_target, rem) { + snprintf(buf, sizeof(buf), "+%s", blobmsg_get_string(cur)); + system_set_dev_sysfs("bonding/arp_ip_target", ifname, buf); + } + + system_set_dev_sysfs_int("bonding/arp_all_targets", ifname, cfg->arp_all_targets); + if (cfg->policy < BONDING_MODE_8023AD) + system_set_dev_sysfs("bonding/arp_validate", ifname, cfg->arp_validate); + system_set_dev_sysfs_int("bonding/use_carrier", ifname, cfg->use_carrier); + if (!cfg->monitor_arp && cfg->monitor_interval) { + system_set_dev_sysfs_int("bonding/updelay", ifname, cfg->updelay); + system_set_dev_sysfs_int("bonding/downdelay", ifname, cfg->downdelay); + } + + return 0; +} + +int system_bonding_set_port(struct device *dev, struct device *port, bool add, bool primary) +{ + const char *port_name = port->ifname; + const char op_ch = add ? '+' : '-'; + char buf[IFNAMSIZ + 1]; + + snprintf(buf, sizeof(buf), "%c%s", op_ch, port_name); + system_if_down(port); + system_set_dev_sysfs("bonding/slaves", dev->ifname, buf); + system_if_up(port); + + if (primary) + system_set_dev_sysfs("bonding/primary", dev->ifname, + add ? port_name : ""); + + return 0; +} + int system_if_resolve(struct device *dev) { struct ifreq ifr; @@ -985,7 +1177,7 @@ static bool check_ifaddr(struct nlmsghdr *hdr, int ifindex) { struct ifaddrmsg *ifa = NLMSG_DATA(hdr); - return ifa->ifa_index == ifindex; + return (long)ifa->ifa_index == ifindex; } static bool check_route(struct nlmsghdr *hdr, int ifindex) @@ -1046,9 +1238,9 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) return NL_SKIP; if (type == RTM_DELRULE) - D(SYSTEM, "Remove a rule\n"); + D(SYSTEM, "Remove a rule"); else - D(SYSTEM, "Remove %s from device %s\n", + D(SYSTEM, "Remove %s from device %s", type == RTM_DELADDR ? "an address" : "a route", clr->dev->ifname); @@ -1061,9 +1253,9 @@ static int cb_clear_event(struct nl_msg *msg, void *arg) 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); + D(SYSTEM, "Error deleting a rule: %d", ret); else - D(SYSTEM, "Error deleting %s from device '%s': %d\n", + D(SYSTEM, "Error deleting %s from device '%s': %d", type == RTM_DELADDR ? "an address" : "a route", clr->dev->ifname, ret); } @@ -1155,15 +1347,15 @@ void system_if_clear_state(struct device *dev) system_if_flags(dev->ifname, 0, IFF_UP); - if (system_is_bridge(dev->ifname, buf, sizeof(buf))) { - D(SYSTEM, "Delete existing bridge named '%s'\n", dev->ifname); + if (system_is_bridge(dev->ifname)) { + D(SYSTEM, "Delete existing bridge named '%s'", dev->ifname); system_bridge_delbr(dev); return; } bridge = system_get_bridge(dev->ifname, buf, sizeof(buf)); if (bridge) { - D(SYSTEM, "Remove device '%s' from bridge '%s'\n", dev->ifname, bridge); + D(SYSTEM, "Remove device '%s' from bridge '%s'", dev->ifname, bridge); system_bridge_if(bridge, dev, SIOCBRDELIF, NULL); } @@ -1182,12 +1374,47 @@ sec_to_jiffies(int val) return (unsigned long) val * 100; } -static void system_bridge_conf_multicast_deps(struct device *bridge, - struct bridge_config *cfg, - char *buf, - int buf_len) +int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg) { - int val; + struct nlattr *linkinfo, *data; + struct nl_msg *msg; + uint64_t val; + int rv; + + msg = system_ifinfo_msg(bridge->ifname, RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL); + if (!msg) + return -1; + + if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + nla_put_string(msg, IFLA_INFO_KIND, "bridge"); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + nla_put_u32(msg, IFLA_BR_STP_STATE, cfg->stp); + nla_put_u32(msg, IFLA_BR_FORWARD_DELAY, sec_to_jiffies(cfg->forward_delay)); + nla_put_u8(msg, IFLA_BR_MCAST_SNOOPING, !!cfg->igmp_snoop); + nla_put_u8(msg, IFLA_BR_MCAST_QUERIER, !!cfg->multicast_querier); + nla_put_u32(msg, IFLA_BR_MCAST_HASH_MAX, cfg->hash_max); + + if (bridge->settings.flags & DEV_OPT_MULTICAST_ROUTER) + nla_put_u8(msg, IFLA_BR_MCAST_ROUTER, !!bridge->settings.multicast_router); + + if (cfg->flags & BRIDGE_OPT_ROBUSTNESS) { + nla_put_u32(msg, IFLA_BR_MCAST_STARTUP_QUERY_CNT, cfg->robustness); + nla_put_u32(msg, IFLA_BR_MCAST_LAST_MEMBER_CNT, cfg->robustness); + } + + if (cfg->flags & BRIDGE_OPT_QUERY_INTERVAL) + nla_put_u64(msg, IFLA_BR_MCAST_QUERY_INTVL, cfg->query_interval); + + if (cfg->flags & BRIDGE_OPT_QUERY_RESPONSE_INTERVAL) + nla_put_u64(msg, IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, cfg->query_response_interval); + + if (cfg->flags & BRIDGE_OPT_LAST_MEMBER_INTERVAL) + nla_put_u64(msg, IFLA_BR_MCAST_LAST_MEMBER_INTVL, cfg->last_member_interval); if (cfg->flags & BRIDGE_OPT_ROBUSTNESS || cfg->flags & BRIDGE_OPT_QUERY_INTERVAL || @@ -1195,109 +1422,47 @@ static void system_bridge_conf_multicast_deps(struct device *bridge, val = cfg->robustness * cfg->query_interval + cfg->query_response_interval; - snprintf(buf, buf_len, "%i", val); - system_bridge_set_membership_interval(bridge, buf); + nla_put_u64(msg, IFLA_BR_MCAST_MEMBERSHIP_INTVL, val); - val = cfg->robustness * cfg->query_interval + - cfg->query_response_interval / 2; + val -= cfg->query_response_interval / 2; - snprintf(buf, buf_len, "%i", val); - system_bridge_set_other_querier_timeout(bridge, buf); + nla_put_u64(msg, IFLA_BR_MCAST_QUERIER_INTVL, val); } if (cfg->flags & BRIDGE_OPT_QUERY_INTERVAL) { val = cfg->query_interval / 4; - snprintf(buf, buf_len, "%i", val); - system_bridge_set_startup_query_interval(bridge, buf); + nla_put_u64(msg, IFLA_BR_MCAST_STARTUP_QUERY_INTVL, val); } -} - -static void system_bridge_conf_multicast(struct device *bridge, - struct bridge_config *cfg, - char *buf, - int buf_len) -{ - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_snooping", - bridge->ifname, cfg->igmp_snoop ? "1" : "0"); - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/multicast_querier", - bridge->ifname, cfg->multicast_querier ? "1" : "0"); + nla_put_u8(msg, IFLA_BR_VLAN_FILTERING, !!cfg->vlan_filtering); + nla_put_u16(msg, IFLA_BR_PRIORITY, cfg->priority); + nla_put_u32(msg, IFLA_BR_HELLO_TIME, sec_to_jiffies(cfg->hello_time)); + nla_put_u32(msg, IFLA_BR_MAX_AGE, sec_to_jiffies(cfg->max_age)); - snprintf(buf, buf_len, "%i", cfg->hash_max); - system_set_dev_sysctl("/sys/devices/virtual/net/%s/bridge/hash_max", - bridge->ifname, buf); + if (cfg->flags & BRIDGE_OPT_AGEING_TIME) + nla_put_u32(msg, IFLA_BR_AGEING_TIME, sec_to_jiffies(cfg->ageing_time)); - if (bridge->settings.flags & DEV_OPT_MULTICAST_ROUTER) { - snprintf(buf, buf_len, "%u", bridge->settings.multicast_router); - system_bridge_set_multicast_router(bridge, buf, true); - } + nla_nest_end(msg, data); + nla_nest_end(msg, linkinfo); - if (cfg->flags & BRIDGE_OPT_ROBUSTNESS) { - snprintf(buf, buf_len, "%i", cfg->robustness); - system_bridge_set_robustness(bridge, buf); - } - - if (cfg->flags & BRIDGE_OPT_QUERY_INTERVAL) { - snprintf(buf, buf_len, "%i", cfg->query_interval); - system_bridge_set_query_interval(bridge, buf); - } - - if (cfg->flags & BRIDGE_OPT_QUERY_RESPONSE_INTERVAL) { - snprintf(buf, buf_len, "%i", cfg->query_response_interval); - system_bridge_set_query_response_interval(bridge, buf); - } - - if (cfg->flags & BRIDGE_OPT_LAST_MEMBER_INTERVAL) { - snprintf(buf, buf_len, "%i", cfg->last_member_interval); - system_bridge_set_last_member_interval(bridge, buf); - } - - system_bridge_conf_multicast_deps(bridge, cfg, buf, buf_len); -} - -int system_bridge_addbr(struct device *bridge, struct bridge_config *cfg) -{ - char buf[64]; - - if (ioctl(sock_ioctl, SIOCBRADDBR, bridge->ifname) < 0) - return -1; - - system_bridge_set_stp_state(bridge, cfg->stp ? "1" : "0"); - - 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)); - system_bridge_set_vlan_filtering(bridge, cfg->vlan_filtering ? "1" : "0"); - - snprintf(buf, sizeof(buf), "%d", cfg->priority); - system_bridge_set_priority(bridge, buf); - - if (cfg->flags & BRIDGE_OPT_AGEING_TIME) { - 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) { - snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->hello_time)); - system_bridge_set_hello_time(bridge, buf); - } + rv = system_rtnl_call(msg); + if (rv) + D(SYSTEM, "Error adding bridge '%s': %d", bridge->ifname, rv); - if (cfg->flags & BRIDGE_OPT_MAX_AGE) { - snprintf(buf, sizeof(buf), "%lu", sec_to_jiffies(cfg->max_age)); - system_bridge_set_max_age(bridge, buf); - } + return rv; - return 0; +nla_put_failure: + nlmsg_free(msg); + return -ENOMEM; } int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvlan_config *cfg) { struct nl_msg *msg; struct nlattr *linkinfo, *data; - struct ifinfomsg iim = { .ifi_family = AF_UNSPEC, }; - int i, rv; + size_t i; + int rv; static const struct { const char *name; enum macvlan_mode val; @@ -1308,16 +1473,12 @@ int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvla { "passthru", MACVLAN_MODE_PASSTHRU }, }; - msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); - + msg = system_ifinfo_msg(macvlan->ifname, RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL); if (!msg) return -1; - nlmsg_append(msg, &iim, sizeof(iim), 0); - if (cfg->flags & MACVLAN_OPT_MACADDR) nla_put(msg, IFLA_ADDRESS, sizeof(cfg->macaddr), cfg->macaddr); - nla_put_string(msg, IFLA_IFNAME, macvlan->ifname); nla_put_u32(msg, IFLA_LINK, dev->ifindex); if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) @@ -1343,7 +1504,7 @@ int system_macvlan_add(struct device *macvlan, struct device *dev, struct macvla rv = system_rtnl_call(msg); if (rv) - D(SYSTEM, "Error adding macvlan '%s' over '%s': %d\n", macvlan->ifname, dev->ifname, rv); + D(SYSTEM, "Error adding macvlan '%s' over '%s': %d", macvlan->ifname, dev->ifname, rv); return rv; @@ -1355,45 +1516,20 @@ nla_put_failure: 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, - }; + int index; if (!dev) return -1; - iim.ifi_index = system_if_resolve(dev); - msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST); - + index = system_if_resolve(dev); + msg = __system_ifinfo_msg(AF_UNSPEC, index, target_ifname, RTM_NEWLINK, 0); 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; - struct ifinfomsg iim = { - .ifi_family = AF_UNSPEC, - .ifi_index = 0, - }; - - msg = nlmsg_alloc_simple(RTM_DELLINK, NLM_F_REQUEST); - - if (!msg) - return -1; - - nlmsg_append(msg, &iim, sizeof(iim), 0); - nla_put_string(msg, IFLA_IFNAME, ifname); - return system_rtnl_call(msg); -} - int system_macvlan_del(struct device *macvlan) { return system_link_del(macvlan->ifname); @@ -1416,7 +1552,7 @@ int system_netns_set(int netns_fd) int system_veth_add(struct device *veth, struct veth_config *cfg) { struct nl_msg *msg; - struct ifinfomsg empty_iim = {}; + struct ifinfomsg empty_iim = {0,}; struct nlattr *linkinfo, *data, *veth_info; int rv; @@ -1456,9 +1592,9 @@ int system_veth_add(struct device *veth, struct veth_config *cfg) rv = system_rtnl_call(msg); if (rv) { if (cfg->flags & VETH_OPT_PEER_NAME) - D(SYSTEM, "Error adding veth '%s' with peer '%s': %d\n", veth->ifname, cfg->peer_name, rv); + D(SYSTEM, "Error adding veth '%s' with peer '%s': %d", veth->ifname, cfg->peer_name, rv); else - D(SYSTEM, "Error adding veth '%s': %d\n", veth->ifname, rv); + D(SYSTEM, "Error adding veth '%s': %d", veth->ifname, rv); } return rv; @@ -1506,76 +1642,459 @@ 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, *qos; - struct ifinfomsg iim = { .ifi_family = AF_UNSPEC }; - struct vlan_qos_mapping *elem; - struct ifla_vlan_qos_mapping nl_qos_map; - int rv; + struct nl_msg *msg; + 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); + + if (!msg) + return -1; + + nlmsg_append(msg, &iim, sizeof(iim), 0); + nla_put_string(msg, IFLA_IFNAME, vlandev->ifname); + nla_put_u32(msg, IFLA_LINK, dev->ifindex); + + if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) + goto nla_put_failure; + + nla_put_string(msg, IFLA_INFO_KIND, "vlan"); + + if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + nla_put_u16(msg, IFLA_VLAN_ID, cfg->vid); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + nla_put_u16(msg, IFLA_VLAN_PROTOCOL, htons(cfg->proto)); +#else + if(cfg->proto == VLAN_PROTO_8021AD) + 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); + + rv = system_rtnl_call(msg); + if (rv) + D(SYSTEM, "Error adding vlandev '%s' over '%s': %d", vlandev->ifname, dev->ifname, rv); + + return rv; + +nla_put_failure: + nlmsg_free(msg); + return -ENOMEM; +} + +int system_vlandev_del(struct device *vlandev) +{ + return system_link_del(vlandev->ifname); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) +struct if_get_master_data { + int ifindex; + int master_ifindex; + int pending; +}; + +static void if_get_master_dsa_linkinfo_attr(struct if_get_master_data *data, + struct rtattr *attr) +{ + struct rtattr *cur; + int rem = RTA_PAYLOAD(attr); + + for (cur = RTA_DATA(attr); RTA_OK(cur, rem); cur = RTA_NEXT(cur, rem)) { + if (cur->rta_type != IFLA_DSA_MASTER) + continue; + + data->master_ifindex = *(__u32 *)RTA_DATA(cur); + } +} + +static void if_get_master_linkinfo_attr(struct if_get_master_data *data, + struct rtattr *attr) +{ + struct rtattr *cur; + int rem = RTA_PAYLOAD(attr); + + for (cur = RTA_DATA(attr); RTA_OK(cur, rem); cur = RTA_NEXT(cur, rem)) { + if (cur->rta_type != IFLA_INFO_KIND && cur->rta_type != IFLA_INFO_DATA) + continue; + + if (cur->rta_type == IFLA_INFO_KIND && strcmp("dsa", (char *)RTA_DATA(cur))) + break; + + if (cur->rta_type == IFLA_INFO_DATA) + if_get_master_dsa_linkinfo_attr(data, cur); + } +} + +static int cb_if_get_master_valid(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nh = nlmsg_hdr(msg); + struct ifinfomsg *ifi = NLMSG_DATA(nh); + struct if_get_master_data *data = (struct if_get_master_data *)arg; + struct rtattr *attr; + int rem; + + if (nh->nlmsg_type != RTM_NEWLINK) + return NL_SKIP; + + if (ifi->ifi_family != AF_UNSPEC) + return NL_SKIP; + + if (ifi->ifi_index != data->ifindex) + return NL_SKIP; + + attr = IFLA_RTA(ifi); + rem = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); + + while (RTA_OK(attr, rem)) { + if (attr->rta_type == IFLA_LINKINFO) + if_get_master_linkinfo_attr(data, attr); + + attr = RTA_NEXT(attr, rem); + } + + return NL_OK; +} + +static int cb_if_get_master_ack(struct nl_msg *msg, void *arg) +{ + struct if_get_master_data *data = (struct if_get_master_data *)arg; + data->pending = 0; + return NL_STOP; +} + +static int cb_if_get_master_error(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +{ + struct if_get_master_data *data = (struct if_get_master_data *)arg; + data->pending = 0; + return NL_STOP; +} + +static int system_if_get_master_ifindex(struct device *dev) +{ + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_family = AF_UNSPEC, + .ifi_index = 0, + }; + struct if_get_master_data data = { + .ifindex = if_nametoindex(dev->ifname), + .master_ifindex = -1, + .pending = 1, + }; + int ret = -1; + + if (!cb) + return ret; + + msg = nlmsg_alloc_simple(RTM_GETLINK, NLM_F_REQUEST); + if (!msg) + goto out; + + if (nlmsg_append(msg, &ifi, sizeof(ifi), 0) || + nla_put_string(msg, IFLA_IFNAME, dev->ifname)) + goto free; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_if_get_master_valid, &data); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_if_get_master_ack, &data); + nl_cb_err(cb, NL_CB_CUSTOM, cb_if_get_master_error, &data); + + ret = nl_send_auto_complete(sock_rtnl, msg); + if (ret < 0) + goto free; + + while (data.pending > 0) + nl_recvmsgs(sock_rtnl, cb); + + if (data.master_ifindex >= 0) + ret = data.master_ifindex; + +free: + nlmsg_free(msg); +out: + nl_cb_put(cb); + return ret; +} + +static void system_refresh_orig_macaddr(struct device *dev, struct device_settings *s) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + + if (ioctl(sock_ioctl, SIOCGIFHWADDR, &ifr) == 0) + memcpy(s->macaddr, &ifr.ifr_hwaddr.sa_data, sizeof(s->macaddr)); +} + +static void system_set_master(struct device *dev, int master_ifindex) +{ + struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC, }; + struct nl_msg *nlm; + + nlm = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST); + if (!nlm) + return; + + nlmsg_append(nlm, &ifi, sizeof(ifi), 0); + nla_put_string(nlm, IFLA_IFNAME, dev->ifname); + + struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO); + if (!linkinfo) + goto failure; + + nla_put_string(nlm, IFLA_INFO_KIND, "dsa"); + struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA); + if (!infodata) + goto failure; + + nla_put_u32(nlm, IFLA_DSA_MASTER, master_ifindex); + + nla_nest_end(nlm, infodata); + nla_nest_end(nlm, linkinfo); + + system_rtnl_call(nlm); + + return; + +failure: + nlmsg_free(nlm); +} +#endif + +static void ethtool_link_mode_clear_bit(__s8 nwords, int nr, __u32 *mask) +{ + if (nr < 0) + return; + + if (nr >= (nwords * 32)) + return; + + mask[nr / 32] &= ~(1U << (nr % 32)); +} + +static bool ethtool_link_mode_test_bit(__s8 nwords, int nr, const __u32 *mask) +{ + if (nr < 0) + return false; + + if (nr >= (nwords * 32)) + return false; + + return !!(mask[nr / 32] & (1U << (nr % 32))); +} + +static int +system_get_ethtool_gro(struct device *dev) +{ + struct ethtool_value ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.cmd = ETHTOOL_GGRO; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr)) + return -1; + + return ecmd.data; +} + +static void +system_set_ethtool_gro(struct device *dev, struct device_settings *s) +{ + struct ethtool_value ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.cmd = ETHTOOL_SGRO; + ecmd.data = s->gro; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + + ioctl(sock_ioctl, SIOCETHTOOL, &ifr); +} + +static void +system_set_ethtool_pause(struct device *dev, struct device_settings *s) +{ + struct ethtool_pauseparam pp; + struct ifreq ifr = { + .ifr_data = (caddr_t)&pp, + }; + + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + memset(&pp, 0, sizeof(pp)); + pp.cmd = ETHTOOL_GPAUSEPARAM; + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr)) + return; + + if (s->flags & DEV_OPT_RXPAUSE || s->flags & DEV_OPT_TXPAUSE) { + pp.autoneg = AUTONEG_DISABLE; + + if (s->flags & DEV_OPT_PAUSE) { + if (s->flags & DEV_OPT_RXPAUSE) + pp.rx_pause = s->rxpause && s->pause; + else + pp.rx_pause = s->pause; + + if (s->flags & DEV_OPT_TXPAUSE) + pp.tx_pause = s->txpause && s->pause; + else + pp.tx_pause = s->pause; + } else { + if (s->flags & DEV_OPT_RXPAUSE) + pp.rx_pause = s->rxpause; + + if (s->flags & DEV_OPT_TXPAUSE) + pp.tx_pause = s->txpause; + } + + if (s->flags & DEV_OPT_ASYM_PAUSE && + !s->asym_pause && (pp.rx_pause != pp.tx_pause)) + pp.rx_pause = pp.tx_pause = false; + } else { + pp.autoneg = AUTONEG_ENABLE; + /* Pause and Asym_Pause advertising bits will be set via + * ETHTOOL_SLINKSETTINGS in system_set_ethtool_settings() + */ + } + + pp.cmd = ETHTOOL_SPAUSEPARAM; + ioctl(sock_ioctl, SIOCETHTOOL, &ifr); +} + +static void +system_set_ethtool_eee_settings(struct device *dev, struct device_settings *s) +{ + struct ethtool_eee eeecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&eeecmd, + }; - msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL); + memset(&eeecmd, 0, sizeof(eeecmd)); + eeecmd.cmd = ETHTOOL_SEEE; + eeecmd.eee_enabled = s->eee; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); - if (!msg) - return -1; + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0) + netifd_log_message(L_WARNING, "cannot set eee %d for device %s", s->eee, dev->ifname); +} - nlmsg_append(msg, &iim, sizeof(iim), 0); - nla_put_string(msg, IFLA_IFNAME, vlandev->ifname); - nla_put_u32(msg, IFLA_LINK, dev->ifindex); +static void +system_set_ethtool_settings(struct device *dev, struct device_settings *s) +{ + struct { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * 127]; + } ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + size_t i; + __s8 nwords; + __u32 *supported, *advertising; - if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO))) - goto nla_put_failure; + system_set_ethtool_pause(dev, s); - nla_put_string(msg, IFLA_INFO_KIND, "vlan"); + if (s->flags & DEV_OPT_EEE) + system_set_ethtool_eee_settings(dev, s); - if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) - goto nla_put_failure; + memset(&ecmd, 0, sizeof(ecmd)); + ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); - nla_put_u16(msg, IFLA_VLAN_ID, cfg->vid); + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) < 0 || + ecmd.req.link_mode_masks_nwords >= 0 || + ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) - nla_put_u16(msg, IFLA_VLAN_PROTOCOL, htons(cfg->proto)); -#else - if(cfg->proto == VLAN_PROTO_8021AD) - 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 + ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; - if (!(qos = nla_nest_start(msg, IFLA_VLAN_INGRESS_QOS))) - goto nla_put_failure; + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) < 0 || + ecmd.req.link_mode_masks_nwords <= 0 || + ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return; - 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); + nwords = ecmd.req.link_mode_masks_nwords; + supported = &ecmd.link_mode_data[0]; + advertising = &ecmd.link_mode_data[nwords]; + memcpy(advertising, supported, sizeof(__u32) * nwords); - if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) - goto nla_put_failure; + for (i = 0; i < ARRAY_SIZE(ethtool_modes); i++) { + if (s->flags & DEV_OPT_DUPLEX) { + if (s->duplex) + ethtool_link_mode_clear_bit(nwords, ethtool_modes[i].bit_half, advertising); + else + ethtool_link_mode_clear_bit(nwords, ethtool_modes[i].bit_full, advertising); + } + if (!(s->flags & DEV_OPT_SPEED) || + s->speed == ethtool_modes[i].speed) + continue; - 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); + ethtool_link_mode_clear_bit(nwords, ethtool_modes[i].bit_full, advertising); + ethtool_link_mode_clear_bit(nwords, ethtool_modes[i].bit_half, advertising); } - nla_nest_end(msg, qos); - nla_nest_end(msg, data); - nla_nest_end(msg, linkinfo); + if (s->flags & DEV_OPT_PAUSE) + if (!s->pause) + ethtool_link_mode_clear_bit(nwords, ETHTOOL_LINK_MODE_Pause_BIT, advertising); - rv = system_rtnl_call(msg); - if (rv) - D(SYSTEM, "Error adding vlandev '%s' over '%s': %d\n", vlandev->ifname, dev->ifname, rv); + if (s->flags & DEV_OPT_ASYM_PAUSE) + if (!s->asym_pause) + ethtool_link_mode_clear_bit(nwords, ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertising); - return rv; + if (s->flags & DEV_OPT_AUTONEG) { + ecmd.req.autoneg = s->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; + if (!s->autoneg) { + if (s->flags & DEV_OPT_SPEED) + ecmd.req.speed = s->speed; -nla_put_failure: - nlmsg_free(msg); - return -ENOMEM; + if (s->flags & DEV_OPT_DUPLEX) + ecmd.req.duplex = s->duplex ? DUPLEX_FULL : DUPLEX_HALF; + } + } + + ecmd.req.cmd = ETHTOOL_SLINKSETTINGS; + ioctl(sock_ioctl, SIOCETHTOOL, &ifr); } -int system_vlandev_del(struct device *vlandev) +static void +system_set_ethtool_settings_after_up(struct device *dev, struct device_settings *s) { - return system_link_del(vlandev->ifname); + if (s->flags & DEV_OPT_GRO) + system_set_ethtool_gro(dev, s); } void @@ -1583,6 +2102,7 @@ system_if_get_settings(struct device *dev, struct device_settings *s) { struct ifreq ifr; char buf[10]; + int ret; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); @@ -1703,16 +2223,40 @@ system_if_get_settings(struct device *dev, struct device_settings *s) s->arp_accept = strtoul(buf, NULL, 0); s->flags |= DEV_OPT_ARP_ACCEPT; } + + ret = system_get_ethtool_gro(dev); + if (ret >= 0) { + s->gro = ret; + s->flags |= DEV_OPT_GRO; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) + ret = system_if_get_master_ifindex(dev); + if (ret >= 0) { + s->master_ifindex = ret; + s->flags |= DEV_OPT_MASTER; + } +#endif } void -system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned int apply_mask) +system_if_apply_settings(struct device *dev, struct device_settings *s, uint64_t apply_mask) { struct ifreq ifr; char buf[12]; apply_mask &= s->flags; + if (apply_mask & DEV_OPT_MASTER) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) + system_set_master(dev, s->master_ifindex); + if (!(apply_mask & (DEV_OPT_MACADDR | DEV_OPT_DEFAULT_MACADDR)) || dev->external) + system_refresh_orig_macaddr(dev, &dev->orig_settings); +#else + netifd_log_message(L_WARNING, "%s Your kernel is older than linux 6.1.0, changing DSA port conduit is not supported!", dev->ifname); +#endif + } + memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); if (apply_mask & DEV_OPT_MTU) { @@ -1801,6 +2345,12 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned system_set_drop_unsolicited_na(dev, s->drop_unsolicited_na ? "1" : "0"); if (apply_mask & DEV_OPT_ARP_ACCEPT) system_set_arp_accept(dev, s->arp_accept ? "1" : "0"); + system_set_ethtool_settings(dev, s); +} + +void system_if_apply_settings_after_up(struct device *dev, struct device_settings *s) +{ + system_set_ethtool_settings_after_up(dev, s); } int system_if_up(struct device *dev) @@ -1819,10 +2369,6 @@ struct if_check_data { int ret; }; -#ifndef IFF_LOWER_UP -#define IFF_LOWER_UP 0x10000 -#endif - static int cb_if_check_valid(struct nl_msg *msg, void *arg) { struct nlmsghdr *nh = nlmsg_hdr(msg); @@ -1832,10 +2378,7 @@ static int cb_if_check_valid(struct nl_msg *msg, void *arg) if (nh->nlmsg_type != RTM_NEWLINK) return NL_SKIP; - if (chk->dev->type == &simple_device_type) - device_set_present(chk->dev, ifi->ifi_index > 0 ? true : false); - device_set_link(chk->dev, ifi->ifi_flags & IFF_LOWER_UP ? true : false); - + system_device_update_state(chk->dev, ifi->ifi_flags, ifi->ifi_index); return NL_OK; } @@ -2040,11 +2583,8 @@ int system_bridge_vlan_check(struct device *dev, char *ifname) } } - goto out; - free: nlmsg_free(msg); -out: nl_cb_put(cb); return data.ret; } @@ -2098,21 +2638,11 @@ struct device * system_if_get_parent(struct device *dev) { char buf[64], *devname; - int ifindex, iflink, len; - FILE *f; - - snprintf(buf, sizeof(buf), "/sys/class/net/%s/iflink", dev->ifname); - f = fopen(buf, "r"); - if (!f) - return NULL; - - len = fread(buf, 1, sizeof(buf) - 1, f); - fclose(f); + int ifindex, iflink; - if (len <= 0) + if (system_get_dev_sysfs("iflink", dev->ifname, buf, sizeof(buf)) < 0) return NULL; - buf[len] = 0; iflink = strtoul(buf, NULL, 0); ifindex = system_if_resolve(dev); if (!iflink || iflink == ifindex) @@ -2169,90 +2699,392 @@ read_uint64_file(int dir_fd, const char *file, uint64_t *val) return ret; } -/* Assume advertised flags == supported flags */ -static const struct { - uint32_t mask; - const char *name; -} ethtool_link_modes[] = { - { 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 -}; +bool +system_if_force_external(const char *ifname) +{ + struct stat s; + + return stat(dev_sysfs_path(ifname, "phy80211"), &s) == 0; +} -static void system_add_link_modes(struct blob_buf *b, __u32 mask) +static const char * +system_netdevtype_name(unsigned short dev_type) { - int i; - for (i = 0; i < ARRAY_SIZE(ethtool_link_modes); i++) { - if (mask & ethtool_link_modes[i].mask) - blobmsg_add_string(b, NULL, ethtool_link_modes[i].name); + size_t i; + + for (i = 0; i < ARRAY_SIZE(netdev_types); i++) { + if (netdev_types[i].id == dev_type) + return netdev_types[i].name; } + + /* the last key is used by default */ + i = ARRAY_SIZE(netdev_types) - 1; + + return netdev_types[i].name; } -bool -system_if_force_external(const char *ifname) +static void +system_add_devtype(struct blob_buf *b, const char *ifname) { - char buf[64]; - struct stat s; + char buf[100]; + bool found = false; + + if (!system_get_dev_sysfs("uevent", ifname, buf, sizeof(buf))) { + const char *info = "DEVTYPE="; + char *context = NULL; + const char *line = strtok_r(buf, "\r\n", &context); + + while (line != NULL) { + char *index = strstr(line, info); + + if (index != NULL) { + blobmsg_add_string(b, "devtype", index + strlen(info)); + found = true; + break; + } + + line = strtok_r(NULL, "\r\n", &context); + } + } + + if (!found) { + unsigned short number = 0; + const char *name = NULL; + + if (!system_get_dev_sysfs("type", ifname, buf, sizeof(buf))) { + number = strtoul(buf, NULL, 0); + name = system_netdevtype_name(number); + blobmsg_add_string(b, "devtype", name); + } + } +} + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +static int32_t +ethtool_feature_count(const char *ifname) +{ + struct { + struct ethtool_sset_info hdr; + uint32_t buf; + } req = { + .hdr = { + .cmd = ETHTOOL_GSSET_INFO, + .sset_mask = 1 << ETH_SS_FEATURES + } + }; + + struct ifreq ifr = { + .ifr_data = (void *)&req + }; + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0) + return -1; + + if (!req.hdr.sset_mask) + return 0; + + return req.buf; +} + +static int32_t +ethtool_feature_index(const char *ifname, const char *keyname) +{ + struct ethtool_gstrings *feature_names; + struct ifreq ifr = { 0 }; + int32_t n_features; + uint32_t i; + + n_features = ethtool_feature_count(ifname); + + if (n_features <= 0) + return -1; + + feature_names = calloc(1, sizeof(*feature_names) + n_features * ETH_GSTRING_LEN); + + if (!feature_names) + return -1; + + feature_names->cmd = ETHTOOL_GSTRINGS; + feature_names->string_set = ETH_SS_FEATURES; + feature_names->len = n_features; + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + ifr.ifr_data = (void *)feature_names; + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0) { + free(feature_names); + + return -1; + } + + for (i = 0; i < feature_names->len; i++) + if (!strcmp((char *)&feature_names->data[i * ETH_GSTRING_LEN], keyname)) + break; + + if (i >= feature_names->len) + i = -1; + + free(feature_names); + + return i; +} + +static bool +ethtool_feature_value(const char *ifname, const char *keyname) +{ + struct ethtool_get_features_block *feature_block; + struct ethtool_gfeatures *feature_values; + struct ifreq ifr = { 0 }; + int32_t feature_idx; + bool active; + + feature_idx = ethtool_feature_index(ifname, keyname); + + if (feature_idx < 0) + return false; + + feature_values = calloc(1, + sizeof(*feature_values) + + sizeof(feature_values->features[0]) * DIV_ROUND_UP(feature_idx, 32)); + + if (!feature_values) + return false; + + feature_values->cmd = ETHTOOL_GFEATURES; + feature_values->size = DIV_ROUND_UP(feature_idx, 32); + + strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); + ifr.ifr_data = (void *)feature_values; + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) != 0) { + free(feature_values); + + return false; + } + + feature_block = &feature_values->features[feature_idx / 32]; + active = feature_block->active & (1U << feature_idx % 32); + + free(feature_values); + + return active; +} + +static void +system_add_link_mode_name(struct blob_buf *b, int i, bool half) +{ + char *buf; + + /* allocate string buffer large enough for the mode name and a suffix + * "-F" or "-H" indicating full duplex or half duplex. + */ + buf = blobmsg_alloc_string_buffer(b, NULL, strlen(ethtool_modes[i].name) + 3); + if (!buf) + return; + + strcpy(buf, ethtool_modes[i].name); + if (half) + strcat(buf, "-H"); + else + strcat(buf, "-F"); + + blobmsg_add_string_buffer(b); +} + +static void +system_add_link_modes(__s8 nwords, struct blob_buf *b, __u32 *mask) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(ethtool_modes); i++) { + if (ethtool_link_mode_test_bit(nwords, ethtool_modes[i].bit_half, mask)) + system_add_link_mode_name(b, i, true); + + if (ethtool_link_mode_test_bit(nwords, ethtool_modes[i].bit_full, mask)) + system_add_link_mode_name(b, i, false); + } +} + +static void +system_add_pause_modes(__s8 nwords, struct blob_buf *b, __u32 *mask) +{ + if (ethtool_link_mode_test_bit(nwords, ETHTOOL_LINK_MODE_Pause_BIT, mask)) + blobmsg_add_string(b, NULL, "pause"); + + if (ethtool_link_mode_test_bit(nwords, ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask)) + blobmsg_add_string(b, NULL, "asym_pause"); +} + + +static void +system_add_ethtool_pause_an(struct blob_buf *b, __s8 nwords, + __u32 *advertising, __u32 *lp_advertising) +{ + bool an_rx = false, an_tx = false; + void *d; + + d = blobmsg_open_array(b, "negotiated"); + + /* Work out negotiated pause frame usage per + * IEEE 802.3-2005 table 28B-3. + */ + if (ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Pause_BIT, + advertising) && + ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Pause_BIT, + lp_advertising)) { + an_tx = true; + an_rx = true; + } else if (ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + advertising) && + ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + lp_advertising)) { + if (ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Pause_BIT, + advertising)) + an_rx = true; + else if (ethtool_link_mode_test_bit(nwords, + ETHTOOL_LINK_MODE_Pause_BIT, + lp_advertising)) + an_tx = true; + } + if (an_tx) + blobmsg_add_string(b, NULL, "rx"); + + if (an_rx) + blobmsg_add_string(b, NULL, "tx"); + + blobmsg_close_array(b, d); +} + +static void +system_get_ethtool_pause(struct device *dev, bool *rx_pause, bool *tx_pause, bool *pause_autoneg) +{ + struct ethtool_pauseparam pp; + struct ifreq ifr = { + .ifr_data = (caddr_t)&pp, + }; + + strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); + memset(&pp, 0, sizeof(pp)); + pp.cmd = ETHTOOL_GPAUSEPARAM; - snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211", ifname); - return stat(buf, &s) == 0; + /* may fail */ + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) == -1) { + *pause_autoneg = true; + return; + } + + *rx_pause = pp.rx_pause; + *tx_pause = pp.tx_pause; + *pause_autoneg = pp.autoneg; } int system_if_dump_info(struct device *dev, struct blob_buf *b) { - struct ethtool_cmd ecmd; - struct ifreq ifr; + __u32 *supported, *advertising, *lp_advertising; + bool rx_pause, tx_pause, pause_autoneg; + struct { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * 127]; + } ecmd; + struct ifreq ifr = { + .ifr_data = (caddr_t)&ecmd, + }; + __s8 nwords; + void *c, *d; char *s; - void *c; + + system_get_ethtool_pause(dev, &rx_pause, &tx_pause, &pause_autoneg); memset(&ecmd, 0, sizeof(ecmd)); - memset(&ifr, 0, sizeof(ifr)); + ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name) - 1); - ifr.ifr_data = (caddr_t) &ecmd; - ecmd.cmd = ETHTOOL_GSET; - if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) == 0) { - c = blobmsg_open_array(b, "link-advertising"); - system_add_link_modes(b, ecmd.advertising); - blobmsg_close_array(b, c); + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) < 0 || + ecmd.req.link_mode_masks_nwords >= 0 || + ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -EOPNOTSUPP; + + ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; + + if (ioctl(sock_ioctl, SIOCETHTOOL, &ifr) < 0 || + ecmd.req.link_mode_masks_nwords <= 0 || + ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) + return -EIO; + + nwords = ecmd.req.link_mode_masks_nwords; + supported = &ecmd.link_mode_data[0]; + advertising = &ecmd.link_mode_data[nwords]; + lp_advertising = &ecmd.link_mode_data[2 * nwords]; + + c = blobmsg_open_array(b, "link-advertising"); + system_add_link_modes(nwords, b, advertising); + blobmsg_close_array(b, c); + + c = blobmsg_open_array(b, "link-partner-advertising"); + system_add_link_modes(nwords, b, lp_advertising); + blobmsg_close_array(b, c); + + c = blobmsg_open_array(b, "link-supported"); + system_add_link_modes(nwords, b, supported); + blobmsg_close_array(b, c); + + if (ethtool_validate_speed(ecmd.req.speed) && + (ecmd.req.speed != (__u32)SPEED_UNKNOWN) && + (ecmd.req.speed != 0)) { + s = blobmsg_alloc_string_buffer(b, "speed", 10); + snprintf(s, 8, "%d%c", ecmd.req.speed, + ecmd.req.duplex == DUPLEX_HALF ? 'H' : 'F'); + blobmsg_add_string_buffer(b); + } + blobmsg_add_u8(b, "autoneg", !!ecmd.req.autoneg); + + c = blobmsg_open_table(b, "flow-control"); + blobmsg_add_u8(b, "autoneg", pause_autoneg); - c = blobmsg_open_array(b, "link-partner-advertising"); - system_add_link_modes(b, ecmd.lp_advertising); - blobmsg_close_array(b, c); + d = blobmsg_open_array(b, "supported"); + system_add_pause_modes(nwords, b, supported); + blobmsg_close_array(b, d); - c = blobmsg_open_array(b, "link-supported"); - system_add_link_modes(b, ecmd.supported); - blobmsg_close_array(b, c); + if (pause_autoneg) { + d = blobmsg_open_array(b, "link-advertising"); + system_add_pause_modes(nwords, b, advertising); + blobmsg_close_array(b, d); + } - s = blobmsg_alloc_string_buffer(b, "speed", 8); - snprintf(s, 8, "%d%c", ethtool_cmd_speed(&ecmd), - ecmd.duplex == DUPLEX_HALF ? 'H' : 'F'); - blobmsg_add_string_buffer(b); + d = blobmsg_open_array(b, "link-partner-advertising"); + system_add_pause_modes(nwords, b, lp_advertising); + blobmsg_close_array(b, d); - blobmsg_add_u8(b, "autoneg", !!ecmd.autoneg); + if (pause_autoneg) { + system_add_ethtool_pause_an(b, nwords, advertising, + lp_advertising); + } else { + d = blobmsg_open_array(b, "selected"); + if (rx_pause) + blobmsg_add_string(b, NULL, "rx"); + + if (tx_pause) + blobmsg_add_string(b, NULL, "tx"); + + blobmsg_close_array(b, d); } + blobmsg_close_table(b, c); + + blobmsg_add_u8(b, "hw-tc-offload", + ethtool_feature_value(dev->ifname, "hw-tc-offload")); + + system_add_devtype(b, dev->ifname); + return 0; } @@ -2269,13 +3101,11 @@ system_if_dump_stats(struct device *dev, struct blob_buf *b) "rx_errors", "tx_bytes", "tx_window_errors", "rx_fifo_errors", "tx_carrier_errors", }; - char buf[64]; int stats_dir; - int i; + size_t i; uint64_t val = 0; - snprintf(buf, sizeof(buf), "/sys/class/net/%s/statistics", dev->ifname); - stats_dir = open(buf, O_DIRECTORY); + stats_dir = open(dev_sysfs_path(dev->ifname, "statistics"), O_DIRECTORY); if (stats_dir < 0) return -1; @@ -2459,6 +3289,9 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) } } + if (route->flags & DEVROUTE_NODEV) + dev = NULL; + msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return -1; @@ -2517,14 +3350,13 @@ int system_del_route(struct device *dev, struct device_route *route) int system_flush_routes(void) { - const char *names[] = { - "/proc/sys/net/ipv4/route/flush", - "/proc/sys/net/ipv6/route/flush" - }; - int fd, i; + const char *names[] = { "ipv4", "ipv6" }; + size_t i; + int fd; for (i = 0; i < ARRAY_SIZE(names); i++) { - fd = open(names[i], O_WRONLY); + snprintf(dev_buf, sizeof(dev_buf), "%s/sys/net/%s/route/flush", proc_path, names[i]); + fd = open(dev_buf, O_WRONLY); if (fd < 0) continue; @@ -2727,6 +3559,15 @@ static int system_iprule(struct iprule *rule, int cmd) if (rule->flags & IPRULE_SUP_PREFIXLEN) nla_put_u32(msg, FRA_SUPPRESS_PREFIXLEN, rule->sup_prefixlen); + if (rule->flags & IPRULE_UIDRANGE) { + struct fib_rule_uid_range uidrange = { + .start = rule->uidrange_start, + .end = rule->uidrange_end + }; + + nla_put(msg, FRA_UID_RANGE, sizeof(uidrange), &uidrange); + } + if (rule->flags & IPRULE_GOTO) nla_put_u32(msg, FRA_GOTO, rule->gotoid); @@ -3558,7 +4399,7 @@ static int system_add_vxlan(const char *name, const unsigned int link, struct bl ret = system_rtnl_call(msg); if (ret) - D(SYSTEM, "Error adding vxlan '%s': %d\n", name, ret); + D(SYSTEM, "Error adding vxlan '%s': %d", name, ret); return ret; @@ -3617,7 +4458,7 @@ static int system_add_sit_tunnel(const char *name, const unsigned int link, stru return ret; failure: - __system_del_ip_tunnel(name, tb); + system_link_del(name); return ret; } @@ -3679,33 +4520,9 @@ static int system_add_proto_tunnel(const char *name, const uint8_t proto, const return -1; } -static int __system_del_ip_tunnel(const char *name, struct blob_attr **tb) -{ - struct blob_attr *cur; - const char *str; - - if (!(cur = tb[TUNNEL_ATTR_TYPE])) - return -EINVAL; - str = blobmsg_data(cur); - - 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, "xfrm")) - return system_link_del(name); - else - return tunnel_ioctl(name, SIOCDELTUNNEL, NULL); -} - -int system_del_ip_tunnel(const char *name, struct blob_attr *attr) +int system_del_ip_tunnel(const struct device *dev) { - struct blob_attr *tb[__TUNNEL_ATTR_MAX]; - - blobmsg_parse(tunnel_attr_list.params, __TUNNEL_ATTR_MAX, tb, - blob_data(attr), blob_len(attr)); - - return __system_del_ip_tunnel(name, tb); + return system_link_del(dev->ifname); } int system_update_ipv6_mtu(struct device *dev, int mtu) @@ -3714,10 +4531,7 @@ int system_update_ipv6_mtu(struct device *dev, int mtu) char buf[64]; int fd; - snprintf(buf, sizeof(buf), "/proc/sys/net/ipv6/conf/%s/mtu", - dev->ifname); - - fd = open(buf, O_RDWR); + fd = open(dev_sysctl_path("ipv6/conf", dev->ifname, "mtu"), O_RDWR); if (fd < 0) return ret; @@ -3738,7 +4552,7 @@ out: return ret; } -int system_add_ip_tunnel(const char *name, struct blob_attr *attr) +int system_add_ip_tunnel(const struct device *dev, struct blob_attr *attr) { struct blob_attr *tb[__TUNNEL_ATTR_MAX]; struct blob_attr *cur; @@ -3747,7 +4561,7 @@ int system_add_ip_tunnel(const char *name, struct blob_attr *attr) blobmsg_parse(tunnel_attr_list.params, __TUNNEL_ATTR_MAX, tb, blob_data(attr), blob_len(attr)); - __system_del_ip_tunnel(name, tb); + system_link_del(dev->ifname); if (!(cur = tb[TUNNEL_ATTR_TYPE])) return -EINVAL; @@ -3771,37 +4585,37 @@ int system_add_ip_tunnel(const char *name, struct blob_attr *attr) } if (!strcmp(str, "sit")) - return system_add_sit_tunnel(name, link, tb); + return system_add_sit_tunnel(dev->ifname, link, tb); #ifdef IFLA_IPTUN_MAX else if (!strcmp(str, "ipip6")) { - return system_add_ip6_tunnel(name, link, tb); + return system_add_ip6_tunnel(dev->ifname, link, tb); } else if (!strcmp(str, "greip")) { - return system_add_gre_tunnel(name, "gre", link, tb, false); + return system_add_gre_tunnel(dev->ifname, "gre", link, tb, false); } else if (!strcmp(str, "gretapip")) { - return system_add_gre_tunnel(name, "gretap", link, tb, false); + return system_add_gre_tunnel(dev->ifname, "gretap", link, tb, false); } else if (!strcmp(str, "greip6")) { - return system_add_gre_tunnel(name, "ip6gre", link, tb, true); + return system_add_gre_tunnel(dev->ifname, "ip6gre", link, tb, true); } else if (!strcmp(str, "gretapip6")) { - return system_add_gre_tunnel(name, "ip6gretap", link, tb, true); + return system_add_gre_tunnel(dev->ifname, "ip6gretap", link, tb, true); #ifdef IFLA_VTI_MAX } else if (!strcmp(str, "vtiip")) { - return system_add_vti_tunnel(name, "vti", link, tb, false); + return system_add_vti_tunnel(dev->ifname, "vti", link, tb, false); } else if (!strcmp(str, "vtiip6")) { - return system_add_vti_tunnel(name, "vti6", link, tb, true); + return system_add_vti_tunnel(dev->ifname, "vti6", link, tb, true); #endif #ifdef IFLA_XFRM_MAX } else if (!strcmp(str, "xfrm")) { - return system_add_xfrm_tunnel(name, "xfrm", link, tb); + return system_add_xfrm_tunnel(dev->ifname, "xfrm", link, tb); #endif #ifdef IFLA_VXLAN_MAX } else if(!strcmp(str, "vxlan")) { - return system_add_vxlan(name, link, tb, false); + return system_add_vxlan(dev->ifname, link, tb, false); } else if(!strcmp(str, "vxlan6")) { - return system_add_vxlan(name, link, tb, true); + return system_add_vxlan(dev->ifname, link, tb, true); #endif #endif } else if (!strcmp(str, "ipip")) { - return system_add_proto_tunnel(name, IPPROTO_IPIP, link, tb); + return system_add_proto_tunnel(dev->ifname, IPPROTO_IPIP, link, tb); } else return -EINVAL;