* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
* Copyright (C) 2013 Steven Barth <steven@midlink.org>
+ * Copyright (C) 2014 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
#include <linux/ip6_tunnel.h>
#include <linux/ethtool.h>
#include <linux/fib_rules.h>
+#include <linux/version.h>
#ifndef RTN_FAILED_POLICY
#define RTN_FAILED_POLICY 12
return true;
}
+static bool
+system_rtn_aton(const char *src, unsigned int *dst)
+{
+ char *e;
+ unsigned int n;
+
+ if (!strcmp(src, "local"))
+ n = RTN_LOCAL;
+ else if (!strcmp(src, "nat"))
+ n = RTN_NAT;
+ else if (!strcmp(src, "broadcast"))
+ n = RTN_BROADCAST;
+ else if (!strcmp(src, "anycast"))
+ n = RTN_ANYCAST;
+ else if (!strcmp(src, "multicast"))
+ n = RTN_MULTICAST;
+ else if (!strcmp(src, "prohibit"))
+ n = RTN_PROHIBIT;
+ else if (!strcmp(src, "unreachable"))
+ n = RTN_UNREACHABLE;
+ else if (!strcmp(src, "blackhole"))
+ n = RTN_BLACKHOLE;
+ else if (!strcmp(src, "xresolve"))
+ n = RTN_XRESOLVE;
+ else if (!strcmp(src, "unicast"))
+ n = RTN_UNICAST;
+ else if (!strcmp(src, "throw"))
+ n = RTN_THROW;
+ else if (!strcmp(src, "failed_policy"))
+ n = RTN_FAILED_POLICY;
+ else {
+ n = strtoul(src, &e, 0);
+ if (!e || *e || e == src || n > 255)
+ return false;
+ }
+
+ *dst = n;
+ return true;
+}
+
int system_init(void)
{
static struct event_socket rtnl_event;
system_set_dev_sysctl("/proc/sys/net/ipv6/conf/%s/disable_ipv6", dev->ifname, val);
}
+static int system_get_sysctl(const char *path, char *buf, const size_t buf_sz)
+{
+ int fd = -1, ret = -1;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ ssize_t len = read(fd, buf, buf_sz - 1);
+ if (len < 0)
+ goto out;
+
+ ret = buf[len] = 0;
+
+out:
+ if (fd >= 0)
+ close(fd);
+
+ return ret;
+}
+
+static int
+system_get_dev_sysctl(const char *path, const char *device, char *buf, const size_t buf_sz)
+{
+ snprintf(dev_buf, sizeof(dev_buf), path, device);
+ return system_get_sysctl(dev_buf, buf, buf_sz);
+}
+
+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);
+}
+
#ifndef IFF_LOWER_UP
#define IFF_LOWER_UP 0x10000
#endif
return path + 1;
}
+static void system_bridge_set_wireless(const char *bridge, const char *dev)
+{
+ snprintf(dev_buf, sizeof(dev_buf),
+ "/sys/devices/virtual/net/%s/brif/%s/multicast_to_unicast",
+ bridge, dev);
+ system_set_sysctl(dev_buf, "1");
+}
+
int system_bridge_addif(struct device *bridge, struct device *dev)
{
char *oldbr;
+ int ret = 0;
- 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;
+ if (!oldbr || strcmp(oldbr, bridge->ifname) != 0)
+ ret = system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
+
+ if (dev->wireless)
+ system_bridge_set_wireless(bridge->ifname, dev->ifname);
- return system_bridge_if(bridge->ifname, dev, SIOCBRADDIF, NULL);
+ return ret;
}
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 char buf[256];
char *bridge;
- if (dev->external)
- return;
-
device_set_ifindex(dev, system_if_resolve(dev));
- if (!dev->ifindex)
+ if (dev->external || !dev->ifindex)
return;
system_if_flags(dev->ifname, 0, IFF_UP);
{
struct nl_msg *msg;
struct nlattr *linkinfo, *data;
- struct ifinfomsg iim = { .ifi_family = AF_INET };
+ struct ifinfomsg iim = { .ifi_family = AF_UNSPEC, };
int ifindex = system_if_resolve(dev);
int i, rv;
static const struct {
if (cfg->flags & MACVLAN_OPT_MACADDR)
nla_put(msg, IFLA_ADDRESS, sizeof(cfg->macaddr), cfg->macaddr);
- nla_put(msg, IFLA_IFNAME, IFNAMSIZ, macvlan->ifname);
+ nla_put_string(msg, IFLA_IFNAME, macvlan->ifname);
nla_put_u32(msg, IFLA_LINK, ifindex);
if (!(linkinfo = nla_nest_start(msg, IFLA_LINKINFO)))
goto nla_put_failure;
- nla_put(msg, IFLA_INFO_KIND, strlen("macvlan"), "macvlan");
+ nla_put_string(msg, IFLA_INFO_KIND, "macvlan");
if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
goto nla_put_failure;
return -ENOMEM;
}
-int system_macvlan_del(struct device *macvlan)
+static int system_link_del(struct device *dev)
{
struct nl_msg *msg;
- struct ifinfomsg iim;
-
- iim.ifi_family = AF_INET;
- iim.ifi_index = 0;
+ struct ifinfomsg iim = {
+ .ifi_family = AF_UNSPEC,
+ .ifi_index = 0,
+ };
- msg = nlmsg_alloc_simple(RTM_DELLINK, 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, dev->ifname);
+ return system_rtnl_call(msg);
+}
- nla_put(msg, IFLA_INFO_KIND, strlen("macvlan"), "macvlan");
- nla_put(msg, IFLA_IFNAME, sizeof(macvlan->ifname), macvlan->ifname);
-
- system_rtnl_call(msg);
-
- return 0;
+int system_macvlan_del(struct device *macvlan)
+{
+ return system_link_del(macvlan);
}
static int system_vlan(struct device *dev, int id)
return system_vlan(dev, -1);
}
+int system_vlandev_add(struct device *vlandev, struct device *dev, struct vlandev_config *cfg)
+{
+ struct nl_msg *msg;
+ struct nlattr *linkinfo, *data;
+ struct ifinfomsg iim = { .ifi_family = AF_UNSPEC };
+ int ifindex = system_if_resolve(dev);
+ int rv;
+
+ if (ifindex == 0)
+ return -ENOENT;
+
+ 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, 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
+
+ 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\n", 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);
+}
+
static void
system_if_get_settings(struct device *dev, struct device_settings *s)
{
struct ifreq ifr;
+ char buf[10];
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name));
memcpy(s->macaddr, &ifr.ifr_hwaddr.sa_data, sizeof(s->macaddr));
s->flags |= DEV_OPT_MACADDR;
}
+
+ if (!system_get_disable_ipv6(dev, buf, sizeof(buf))) {
+ s->ipv6 = !strtoul(buf, NULL, 0);
+ s->flags |= DEV_OPT_IPV6;
+ }
}
void
-system_if_apply_settings(struct device *dev, struct device_settings *s)
+system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned int apply_mask)
{
struct ifreq ifr;
+ if (!apply_mask)
+ return;
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, dev->ifname, sizeof(ifr.ifr_name));
- if (s->flags & DEV_OPT_MTU) {
+ if (s->flags & DEV_OPT_MTU & apply_mask) {
ifr.ifr_mtu = s->mtu;
if (ioctl(sock_ioctl, SIOCSIFMTU, &ifr) < 0)
s->flags &= ~DEV_OPT_MTU;
}
- if (s->flags & DEV_OPT_TXQUEUELEN) {
+ if (s->flags & DEV_OPT_TXQUEUELEN & apply_mask) {
ifr.ifr_qlen = s->txqueuelen;
if (ioctl(sock_ioctl, SIOCSIFTXQLEN, &ifr) < 0)
s->flags &= ~DEV_OPT_TXQUEUELEN;
}
- if ((s->flags & DEV_OPT_MACADDR) && !dev->external) {
+ if ((s->flags & DEV_OPT_MACADDR & apply_mask) && !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;
}
+ if (s->flags & DEV_OPT_IPV6 & apply_mask)
+ system_set_disable_ipv6(dev, s->ipv6 ? "0" : "1");
}
int system_if_up(struct device *dev)
{
system_if_get_settings(dev, &dev->orig_settings);
- system_if_apply_settings(dev, &dev->settings);
+ system_if_apply_settings(dev, &dev->settings, dev->settings.flags);
device_set_ifindex(dev, system_if_resolve(dev));
return system_if_flags(dev->ifname, IFF_UP, 0);
}
{
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);
+ system_if_apply_settings(dev, &dev->orig_settings, dev->orig_settings.flags);
return ret;
}
route->nexthop.in6.s6_addr32[2] ||
route->nexthop.in6.s6_addr32[3];
- unsigned char scope = (cmd == RTM_DELROUTE) ? RT_SCOPE_NOWHERE :
- (have_gw) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
-
unsigned int table = (route->flags & (DEVROUTE_TABLE | DEVROUTE_SRCTABLE))
? route->table : RT_TABLE_MAIN;
.rtm_src_len = route->sourcemask,
.rtm_table = (table < 256) ? table : RT_TABLE_UNSPEC,
.rtm_protocol = (route->flags & DEVADDR_KERNEL) ? RTPROT_KERNEL : RTPROT_STATIC,
- .rtm_scope = scope,
+ .rtm_scope = RT_SCOPE_NOWHERE,
.rtm_type = (cmd == RTM_DELROUTE) ? 0: RTN_UNICAST,
+ .rtm_flags = (route->flags & DEVROUTE_ONLINK) ? RTNH_F_ONLINK : 0,
};
struct nl_msg *msg;
rtm.rtm_scope = RT_SCOPE_UNIVERSE;
rtm.rtm_type = RTN_UNREACHABLE;
}
+ else
+ rtm.rtm_scope = (have_gw) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
+ }
+
+ if (route->flags & DEVROUTE_TYPE) {
+ rtm.rtm_type = route->type;
+ if (!(route->flags & (DEVROUTE_TABLE | DEVROUTE_SRCTABLE))) {
+ if (rtm.rtm_type == RTN_LOCAL || rtm.rtm_type == RTN_BROADCAST ||
+ rtm.rtm_type == RTN_NAT || rtm.rtm_type == RTN_ANYCAST)
+ rtm.rtm_table = RT_TABLE_LOCAL;
+ }
+
+ if (rtm.rtm_type == RTN_LOCAL || rtm.rtm_type == RTN_NAT)
+ rtm.rtm_scope = RT_SCOPE_HOST;
+ else if (rtm.rtm_type == RTN_BROADCAST || rtm.rtm_type == RTN_MULTICAST ||
+ rtm.rtm_type == RTN_ANYCAST)
+ rtm.rtm_scope = RT_SCOPE_LINK;
}
msg = nlmsg_alloc_simple(cmd, flags);
if (table >= 256)
nla_put_u32(msg, RTA_TABLE, table);
+ if (route->flags & DEVROUTE_MTU) {
+ struct nlattr *metrics;
+
+ if (!(metrics = nla_nest_start(msg, RTA_METRICS)))
+ goto nla_put_failure;
+
+ nla_put_u32(msg, RTAX_MTU, route->mtu);
+
+ nla_nest_end(msg, metrics);
+ }
+
return system_rtnl_call(msg);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOMEM;
}
int system_add_route(struct device *dev, struct device_route *route)
return 0;
}
+bool system_resolve_rt_type(const char *type, unsigned int *id)
+{
+ return system_rtn_aton(type, id);
+}
+
bool system_resolve_rt_table(const char *name, unsigned int *id)
{
FILE *f;
if (table == RT_TABLE_UNSPEC)
return false;
- /* do not consider main table special */
- if (table == RT_TABLE_MAIN)
- table = RT_TABLE_UNSPEC;
-
*id = table;
return true;
}
+bool system_is_default_rt_table(unsigned int id)
+{
+ return (id == RT_TABLE_MAIN);
+}
+
static int system_iprule(struct iprule *rule, int cmd)
{
int alen = ((rule->flags & IPRULE_FAMILY) == IPRULE_INET4) ? 4 : 16;
bool system_resolve_iprule_action(const char *action, unsigned int *id)
{
- char *e;
- unsigned int n;
-
- if (!strcmp(action, "local"))
- n = RTN_LOCAL;
- else if (!strcmp(action, "nat"))
- n = RTN_NAT;
- else if (!strcmp(action, "broadcast"))
- n = RTN_BROADCAST;
- else if (!strcmp(action, "anycast"))
- n = RTN_ANYCAST;
- else if (!strcmp(action, "multicast"))
- n = RTN_MULTICAST;
- else if (!strcmp(action, "prohibit"))
- n = RTN_PROHIBIT;
- else if (!strcmp(action, "unreachable"))
- n = RTN_UNREACHABLE;
- else if (!strcmp(action, "blackhole"))
- n = RTN_BLACKHOLE;
- else if (!strcmp(action, "xresolve"))
- n = RTN_XRESOLVE;
- else if (!strcmp(action, "unicast"))
- n = RTN_UNICAST;
- else if (!strcmp(action, "throw"))
- n = RTN_THROW;
- else if (!strcmp(action, "failed_policy"))
- n = RTN_FAILED_POLICY;
- else {
- n = strtoul(action, &e, 0);
- if (!e || *e || e == action || n > 255)
- return false;
- }
-
- *id = n;
- return true;
+ return system_rtn_aton(action, id);
}
time_t system_get_rtime(void)
}
#endif
} else if (!strcmp(str, "ipip6")) {
- struct ip6_tnl_parm p = {
- .link = link,
- .proto = IPPROTO_IPIP,
- .hop_limit = (ttl) ? ttl : 64,
- .encap_limit = 4,
- };
+ struct nl_msg *nlm = nlmsg_alloc_simple(RTM_NEWLINK,
+ NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE);
+ struct ifinfomsg ifi = { .ifi_family = AF_UNSPEC };
+ int ret = 0;
- if ((cur = tb[TUNNEL_ATTR_LOCAL]) &&
- inet_pton(AF_INET6, blobmsg_data(cur), &p.laddr) < 1)
- return -EINVAL;
+ if (!nlm)
+ return -1;
- if ((cur = tb[TUNNEL_ATTR_REMOTE]) &&
- inet_pton(AF_INET6, blobmsg_data(cur), &p.raddr) < 1)
- return -EINVAL;
+ nlmsg_append(nlm, &ifi, sizeof(ifi), 0);
+ nla_put_string(nlm, IFLA_IFNAME, name);
- strncpy(p.name, name, sizeof(p.name));
- if (tunnel_ioctl("ip6tnl0", SIOCADDTUNNEL, &p) < 0)
- return -1;
- } else
+ if (link)
+ nla_put_u32(nlm, IFLA_LINK, link);
+
+ struct nlattr *linkinfo = nla_nest_start(nlm, IFLA_LINKINFO);
+ if (!linkinfo) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+ nla_put_string(nlm, IFLA_INFO_KIND, "ip6tnl");
+ struct nlattr *infodata = nla_nest_start(nlm, IFLA_INFO_DATA);
+ if (!infodata) {
+ ret = -ENOMEM;
+ goto failure;
+ }
+
+ if (link)
+ nla_put_u32(nlm, IFLA_IPTUN_LINK, link);
+
+ nla_put_u8(nlm, IFLA_IPTUN_PROTO, IPPROTO_IPIP);
+ nla_put_u8(nlm, IFLA_IPTUN_TTL, (ttl) ? ttl : 64);
+ nla_put_u8(nlm, IFLA_IPTUN_ENCAP_LIMIT, 4);
+
+ struct in6_addr in6buf;
+ if ((cur = tb[TUNNEL_ATTR_LOCAL])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_IPTUN_LOCAL, sizeof(in6buf), &in6buf);
+ }
+
+ if ((cur = tb[TUNNEL_ATTR_REMOTE])) {
+ if (inet_pton(AF_INET6, blobmsg_data(cur), &in6buf) < 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+ nla_put(nlm, IFLA_IPTUN_REMOTE, sizeof(in6buf), &in6buf);
+ }
+
+#ifdef IFLA_IPTUN_FMR_MAX
+ if ((cur = tb[TUNNEL_ATTR_FMRS])) {
+ struct nlattr *fmrs = nla_nest_start(nlm, IFLA_IPTUN_FMRS);
+
+ struct blob_attr *fmr;
+ unsigned rem, fmrcnt = 0;
+ blobmsg_for_each_attr(fmr, cur, rem) {
+ if (blobmsg_type(fmr) != BLOBMSG_TYPE_STRING)
+ continue;
+
+ unsigned ip4len, ip6len, ealen, offset = 6;
+ char ip6buf[48];
+ char ip4buf[16];
+
+ if (sscanf(blobmsg_get_string(fmr), "%47[^/]/%u,%15[^/]/%u,%u,%u",
+ ip6buf, &ip6len, ip4buf, &ip4len, &ealen, &offset) < 5) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ struct in6_addr ip6prefix;
+ struct in_addr ip4prefix;
+ if (inet_pton(AF_INET6, ip6buf, &ip6prefix) != 1 ||
+ inet_pton(AF_INET, ip4buf, &ip4prefix) != 1) {
+ ret = -EINVAL;
+ goto failure;
+ }
+
+ struct nlattr *rule = nla_nest_start(nlm, ++fmrcnt);
+
+ nla_put(nlm, IFLA_IPTUN_FMR_IP6_PREFIX, sizeof(ip6prefix), &ip6prefix);
+ nla_put(nlm, IFLA_IPTUN_FMR_IP4_PREFIX, sizeof(ip4prefix), &ip4prefix);
+ nla_put_u8(nlm, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ip6len);
+ nla_put_u8(nlm, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ip4len);
+ nla_put_u8(nlm, IFLA_IPTUN_FMR_EA_LEN, ealen);
+ nla_put_u8(nlm, IFLA_IPTUN_FMR_OFFSET, offset);
+
+ nla_nest_end(nlm, rule);
+ }
+
+ nla_nest_end(nlm, fmrs);
+ }
+#endif
+
+ nla_nest_end(nlm, infodata);
+ nla_nest_end(nlm, linkinfo);
+
+ return system_rtnl_call(nlm);
+failure:
+ nlmsg_free(nlm);
+ return ret;
+ }
+ else
return -EINVAL;
return 0;