X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=system-linux.c;h=8345e5d206e710e1f9bd4eeafba87edd375b0f2b;hp=4fa0ead507ded0046cace9412f51c761abe810a8;hb=486aa750a164d41905beb61afec89268e3eb7f48;hpb=677f9754526a7923417fb70f9a8a38adaeb251a6 diff --git a/system-linux.c b/system-linux.c index 4fa0ead..8345e5d 100644 --- a/system-linux.c +++ b/system-linux.c @@ -1,3 +1,16 @@ +/* + * netifd - network interface daemon + * Copyright (C) 2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ #define _GNU_SOURCE #include @@ -5,10 +18,18 @@ #include #include +#include +#include + +#include +#include + #include #include +#include #include #include +#include #include #include @@ -38,6 +59,8 @@ static struct nl_sock *sock_rtnl = NULL; static int cb_rtnl_event(struct nl_msg *msg, void *arg); static void handle_hotplug_event(struct uloop_fd *u, unsigned int events); +static char dev_buf[256]; + static void handler_nl_event(struct uloop_fd *u, unsigned int events) { @@ -131,10 +154,8 @@ static void system_set_sysctl(const char *path, const char *val) static void system_set_dev_sysctl(const char *path, const char *device, const char *val) { - static char buf[256]; - - snprintf(buf, sizeof(buf), path, val); - system_set_sysctl(buf, val); + snprintf(dev_buf, sizeof(dev_buf), path, device); + system_set_sysctl(dev_buf, val); } static void system_set_disable_ipv6(struct device *dev, const char *val) @@ -214,6 +235,9 @@ found: if (dev->type != &simple_device_type) return; + if (add && system_if_force_external(dev->ifname)) + return; + device_set_present(dev, add); } @@ -235,10 +259,15 @@ handle_hotplug_event(struct uloop_fd *u, unsigned int events) static int system_rtnl_call(struct nl_msg *msg) { - int s = -(nl_send_auto_complete(sock_rtnl, msg) - || nl_wait_for_ack(sock_rtnl)); + int ret; + + ret = nl_send_auto_complete(sock_rtnl, msg); nlmsg_free(msg); - return s; + + if (ret < 0) + return ret; + + return nl_wait_for_ack(sock_rtnl); } int system_bridge_delbr(struct device *bridge) @@ -249,6 +278,8 @@ int system_bridge_delbr(struct device *bridge) static int system_bridge_if(const char *bridge, struct device *dev, int cmd, void *data) { struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); if (dev) ifr.ifr_ifindex = dev->ifindex; else @@ -257,18 +288,6 @@ static int system_bridge_if(const char *bridge, struct device *dev, int cmd, voi return ioctl(sock_ioctl, cmd, &ifr); } -int system_bridge_addif(struct device *bridge, struct device *dev) -{ - system_set_disable_ipv6(dev, "1"); - return system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL); -} - -int system_bridge_delif(struct device *bridge, struct device *dev) -{ - system_set_disable_ipv6(dev, "0"); - return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL); -} - static bool system_is_bridge(const char *name, char *buf, int buflen) { struct stat st; @@ -305,6 +324,24 @@ static char *system_get_bridge(const char *name, char *buf, int buflen) return path + 1; } +int system_bridge_addif(struct device *bridge, struct device *dev) +{ + char *oldbr; + + system_set_disable_ipv6(dev, "1"); + oldbr = system_get_bridge(dev->ifname, dev_buf, sizeof(dev_buf)); + if (oldbr && !strcmp(oldbr, bridge->ifname)) + return 0; + + return system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL); +} + +int system_bridge_delif(struct device *bridge, struct device *dev) +{ + system_set_disable_ipv6(dev, "0"); + return system_bridge_if(bridge->ifname, dev, SIOCBRDELIF, NULL); +} + static int system_if_resolve(struct device *dev) { struct ifreq ifr; @@ -318,6 +355,8 @@ static int system_if_resolve(struct device *dev) static int system_if_flags(const char *ifname, unsigned add, unsigned rem) { struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); ioctl(sock_ioctl, SIOCGIFFLAGS, &ifr); ifr.ifr_flags |= add; @@ -343,8 +382,13 @@ static bool check_ifaddr(struct nlmsghdr *hdr, int ifindex) static bool check_route(struct nlmsghdr *hdr, int ifindex) { + struct rtmsg *r = NLMSG_DATA(hdr); struct nlattr *tb[__RTA_MAX]; + if (r->rtm_protocol == RTPROT_KERNEL && + r->rtm_family == AF_INET6) + return false; + nlmsg_parse(hdr, sizeof(struct rtmsg), tb, __RTA_MAX - 1, NULL); if (!tb[RTA_OIF]) return false; @@ -588,12 +632,12 @@ system_if_get_settings(struct device *dev, struct device_settings *s) } if (ioctl(sock_ioctl, SIOCGIFHWADDR, &ifr) == 0) { - memcpy(s->macaddr, &ifr.ifr_hwaddr, sizeof(s->macaddr)); + memcpy(s->macaddr, &ifr.ifr_hwaddr.sa_data, sizeof(s->macaddr)); s->flags |= DEV_OPT_MACADDR; } } -static void +void system_if_apply_settings(struct device *dev, struct device_settings *s) { struct ifreq ifr; @@ -602,15 +646,19 @@ system_if_apply_settings(struct device *dev, struct device_settings *s) strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name)); if (s->flags & DEV_OPT_MTU) { ifr.ifr_mtu = s->mtu; - ioctl(sock_ioctl, SIOCSIFMTU, &ifr); + if (ioctl(sock_ioctl, SIOCSIFMTU, &ifr) < 0) + s->flags &= ~DEV_OPT_MTU; } if (s->flags & DEV_OPT_TXQUEUELEN) { ifr.ifr_qlen = s->txqueuelen; - ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr); + if (ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr) < 0) + s->flags &= ~DEV_OPT_TXQUEUELEN; } - if (s->flags & DEV_OPT_MACADDR) { - memcpy(&ifr.ifr_hwaddr, s->macaddr, sizeof(s->macaddr)); - ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr); + if ((s->flags & DEV_OPT_MACADDR) && !dev->external) { + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(&ifr.ifr_hwaddr.sa_data, s->macaddr, sizeof(s->macaddr)); + if (ioctl(sock_ioctl, SIOCSIFHWADDR, &ifr) < 0) + s->flags &= ~DEV_OPT_MACADDR; } } @@ -625,6 +673,7 @@ int system_if_up(struct device *dev) int system_if_down(struct device *dev) { int ret = system_if_flags(dev->ifname, 0, IFF_UP); + dev->orig_settings.flags &= dev->settings.flags; system_if_apply_settings(dev, &dev->orig_settings); return ret; } @@ -698,14 +747,14 @@ retry: } static bool -read_int_file(int dir_fd, const char *file, int *val) +read_uint64_file(int dir_fd, const char *file, uint64_t *val) { char buf[64]; bool ret = false; ret = read_string_file(dir_fd, file, buf, sizeof(buf)); if (ret) - *val = strtoul(buf, NULL, 0); + *val = strtoull(buf, NULL, 0); return ret; } @@ -732,6 +781,16 @@ static void system_add_link_modes(struct blob_buf *b, __u32 mask) } } +bool +system_if_force_external(const char *ifname) +{ + char buf[64]; + struct stat s; + + snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211", ifname); + return stat(buf, &s) == 0; +} + int system_if_dump_info(struct device *dev, struct blob_buf *b) { @@ -739,15 +798,14 @@ system_if_dump_info(struct device *dev, struct blob_buf *b) struct ifreq ifr; char buf[64], *s; void *c; - int dir_fd, val = 0; + int dir_fd; + uint64_t val = 0; snprintf(buf, sizeof(buf), "/sys/class/net/%s", dev->ifname); dir_fd = open(buf, O_DIRECTORY); - if (read_int_file(dir_fd, "carrier", &val)) + if (read_uint64_file(dir_fd, "carrier", &val)) blobmsg_add_u8(b, "link", !!val); - if (read_string_file(dir_fd, "address", buf, sizeof(buf))) - blobmsg_add_string(b, "macaddr", buf); memset(&ecmd, 0, sizeof(ecmd)); memset(&ifr, 0, sizeof(ifr)); @@ -789,7 +847,8 @@ system_if_dump_stats(struct device *dev, struct blob_buf *b) }; char buf[64]; int stats_dir; - int i, val = 0; + int i; + uint64_t val = 0; snprintf(buf, sizeof(buf), "/sys/class/net/%s/statistics", dev->ifname); stats_dir = open(buf, O_DIRECTORY); @@ -797,8 +856,8 @@ system_if_dump_stats(struct device *dev, struct blob_buf *b) return -1; for (i = 0; i < ARRAY_SIZE(counters); i++) - if (read_int_file(stats_dir, counters[i], &val)) - blobmsg_add_u32(b, counters[i], val); + if (read_uint64_file(stats_dir, counters[i], &val)) + blobmsg_add_u64(b, counters[i], val); close(stats_dir); return 0; @@ -806,7 +865,9 @@ system_if_dump_stats(struct device *dev, struct blob_buf *b) static int system_addr(struct device *dev, struct device_addr *addr, int cmd) { - int alen = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16; + bool v4 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET4); + int alen = v4 ? 4 : 16; + unsigned int flags = 0; struct ifaddrmsg ifa = { .ifa_family = (alen == 4) ? AF_INET : AF_INET6, .ifa_prefixlen = addr->mask, @@ -814,21 +875,43 @@ static int system_addr(struct device *dev, struct device_addr *addr, int cmd) }; struct nl_msg *msg; + if (cmd == RTM_NEWADDR) + flags |= NLM_F_CREATE | NLM_F_REPLACE; - dev = addr->device; - if (dev) { - if (!dev->ifindex) - return -1; - - ifa.ifa_index = dev->ifindex; - } - - msg = nlmsg_alloc_simple(cmd, 0); + msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return -1; nlmsg_append(msg, &ifa, sizeof(ifa), 0); nla_put(msg, IFA_LOCAL, alen, &addr->addr); + if (v4) { + if (addr->broadcast) + nla_put_u32(msg, IFA_BROADCAST, addr->broadcast); + if (addr->point_to_point) + nla_put_u32(msg, IFA_ADDRESS, addr->point_to_point); + } else { + time_t now = system_get_rtime(); + struct ifa_cacheinfo cinfo = {0xffffffffU, 0xffffffffU, 0, 0}; + + if (addr->preferred_until) { + int preferred = addr->preferred_until - now; + if (preferred < 0) + preferred = 0; + + cinfo.ifa_prefered = preferred; + } + + if (addr->valid_until) { + int valid = addr->valid_until - now; + if (valid <= 0) + return -1; + + cinfo.ifa_valid = valid; + } + + nla_put(msg, IFA_CACHEINFO, sizeof(cinfo), &cinfo); + } + return system_rtnl_call(msg); } @@ -847,7 +930,6 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16; bool have_gw; unsigned int flags = 0; - int ifindex = dev->ifindex; if (alen == 4) have_gw = !!route->nexthop.in.s_addr; @@ -864,21 +946,19 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) .rtm_family = (alen == 4) ? AF_INET : AF_INET6, .rtm_dst_len = route->mask, .rtm_table = RT_TABLE_MAIN, - .rtm_protocol = RTPROT_BOOT, + .rtm_protocol = (route->flags & DEVADDR_KERNEL) ? RTPROT_KERNEL : RTPROT_STATIC, .rtm_scope = scope, .rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST, }; struct nl_msg *msg; - if (cmd == RTM_NEWROUTE) + if (cmd == RTM_NEWROUTE) { flags |= NLM_F_CREATE | NLM_F_REPLACE; - dev = route->device; - if (dev) { - if (!dev->ifindex) - return -1; - - ifindex = dev->ifindex; + if (!dev) { // Add null-route + rtm.rtm_scope = RT_SCOPE_UNIVERSE; + rtm.rtm_type = RTN_UNREACHABLE; + } } msg = nlmsg_alloc_simple(cmd, flags); @@ -890,11 +970,14 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) if (route->mask) nla_put(msg, RTA_DST, alen, &route->addr); + if (route->metric > 0) + nla_put_u32(msg, RTA_PRIORITY, route->metric); + if (have_gw) nla_put(msg, RTA_GATEWAY, alen, &route->nexthop); - if (route->flags & DEVADDR_DEVICE) - nla_put_u32(msg, RTA_OIF, ifindex); + if (dev) + nla_put_u32(msg, RTA_OIF, dev->ifindex); return system_rtnl_call(msg); } @@ -941,3 +1024,144 @@ time_t system_get_rtime(void) return 0; } + +#ifndef IP_DF +#define IP_DF 0x4000 +#endif + +static void tunnel_parm_init(struct ip_tunnel_parm *p) +{ + memset(p, 0, sizeof(*p)); + p->iph.version = 4; + p->iph.ihl = 5; + p->iph.frag_off = htons(IP_DF); +} + +static int tunnel_ioctl(const char *name, int cmd, void *p) +{ + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_ifru.ifru_data = p; + return ioctl(sock_ioctl, cmd, &ifr); +} + +int system_del_ip_tunnel(const char *name) +{ + struct ip_tunnel_parm p; + + tunnel_parm_init(&p); + return tunnel_ioctl(name, SIOCDELTUNNEL, &p); +} + +int system_update_ipv6_mtu(struct device *dev, int mtu) +{ + int ret = -1; + char buf[64]; + snprintf(buf, sizeof(buf), "/proc/sys/net/ipv6/conf/%s/mtu", + dev->ifname); + + int fd = open(buf, O_RDWR); + ssize_t len = read(fd, buf, sizeof(buf) - 1); + if (len < 0) + goto out; + + buf[len] = 0; + ret = atoi(buf); + + if (!mtu || ret <= mtu) + goto out; + + lseek(fd, 0, SEEK_SET); + if (write(fd, buf, snprintf(buf, sizeof(buf), "%i", mtu)) <= 0) + ret = -1; + +out: + close(fd); + return ret; +} + +static int parse_ipaddr(struct blob_attr *attr, __be32 *addr) +{ + if (!attr) + return 1; + + return inet_pton(AF_INET, blobmsg_data(attr), (void *) addr); +} + + +int system_add_ip_tunnel(const char *name, struct blob_attr *attr) +{ + struct blob_attr *tb[__TUNNEL_ATTR_MAX]; + struct blob_attr *cur; + struct ip_tunnel_parm p; + const char *base, *str; + bool is_sit; + + system_del_ip_tunnel(name); + + tunnel_parm_init(&p); + + blobmsg_parse(tunnel_attr_list.params, __TUNNEL_ATTR_MAX, tb, + blob_data(attr), blob_len(attr)); + + if (!(cur = tb[TUNNEL_ATTR_TYPE])) + return -EINVAL; + str = blobmsg_data(cur); + is_sit = !strcmp(str, "sit"); + + if (is_sit) { + p.iph.protocol = IPPROTO_IPV6; + base = "sit0"; + } else + return -EINVAL; + + if (!parse_ipaddr(tb[TUNNEL_ATTR_LOCAL], &p.iph.saddr)) + return -EINVAL; + + if (!parse_ipaddr(tb[TUNNEL_ATTR_REMOTE], &p.iph.daddr)) + return -EINVAL; + + if ((cur = tb[TUNNEL_ATTR_TTL])) { + unsigned int val = blobmsg_get_u32(cur); + + if (val > 255) + return -EINVAL; + + p.iph.ttl = val; + } + + strncpy(p.name, name, sizeof(p.name)); + if (tunnel_ioctl(base, SIOCADDTUNNEL, &p) < 0) + return -1; + +#ifdef SIOCADD6RD + cur = tb[TUNNEL_ATTR_6RD_PREFIX]; + if (cur && is_sit) { + unsigned int mask; + struct ip_tunnel_6rd p6; + + memset(&p6, 0, sizeof(p6)); + + if (!parse_ip_and_netmask(AF_INET6, blobmsg_data(cur), + &p6.prefix, &mask) || mask > 128) + return -EINVAL; + p6.prefixlen = mask; + + if ((cur = tb[TUNNEL_ATTR_6RD_RELAY_PREFIX])) { + if (!parse_ip_and_netmask(AF_INET, blobmsg_data(cur), + &p6.relay_prefix, &mask) || mask > 32) + return -EINVAL; + p6.relay_prefixlen = mask; + } + + if (tunnel_ioctl(name, SIOCADD6RD, &p6) < 0) { + system_del_ip_tunnel(name); + return -1; + } + } +#endif + + return 0; +}