X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fnetifd.git;a=blobdiff_plain;f=system-linux.c;h=c7b8b8f66c8c45bdcdf3dbdd7f57de9376d8402b;hp=34d7cae30d4592ff5fbc1e3bb98029565211ba7e;hb=19ab568942e2699a414bd7bf3d53823c9b707abb;hpb=1466967ffa3e408b709d5e84932616787b2853ac diff --git a/system-linux.c b/system-linux.c index 34d7cae..c7b8b8f 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, val); + system_set_sysctl(dev_buf, val); } static void system_set_disable_ipv6(struct device *dev, const char *val) @@ -235,10 +256,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) @@ -257,18 +283,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 +319,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; @@ -588,7 +620,7 @@ 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; } } @@ -602,15 +634,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); + 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 +661,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; } @@ -804,7 +841,8 @@ 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; struct ifaddrmsg ifa = { .ifa_family = (alen == 4) ? AF_INET : AF_INET6, .ifa_prefixlen = addr->mask, @@ -813,20 +851,19 @@ static int system_addr(struct device *dev, struct device_addr *addr, int cmd) struct nl_msg *msg; - dev = addr->device; - if (dev) { - if (!dev->ifindex) - return -1; - - ifa.ifa_index = dev->ifindex; - } - msg = nlmsg_alloc_simple(cmd, 0); 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); + } + return system_rtnl_call(msg); } @@ -862,7 +899,7 @@ 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_BOOT, .rtm_scope = scope, .rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST, }; @@ -871,14 +908,6 @@ static int system_rt(struct device *dev, struct device_route *route, int cmd) if (cmd == RTM_NEWROUTE) flags |= NLM_F_CREATE | NLM_F_REPLACE; - dev = route->device; - if (dev) { - if (!dev->ifindex) - return -1; - - ifindex = dev->ifindex; - } - msg = nlmsg_alloc_simple(cmd, flags); if (!msg) return -1; @@ -888,11 +917,13 @@ 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); + nla_put_u32(msg, RTA_OIF, ifindex); return system_rtnl_call(msg); } @@ -939,3 +970,87 @@ 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); +} + +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; + int cmd = SIOCADDTUNNEL; + + 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)); + + cur = tb[TUNNEL_ATTR_TYPE]; + if (!cur) + return -EINVAL; + + str = blobmsg_data(cur); + if (!strcmp(str, "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)); + return tunnel_ioctl(base, cmd, &p); +}