From 98ca67469da70accd710b14c28e77be2126170a8 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Mon, 7 Apr 2014 13:30:52 +0200 Subject: [PATCH] netifd: Add interface config support to enable/disable IPv6 in the kernel per device Main use case is being able to disable IPv6 on (a) WAN interface(s) when only IPv4 connectivity is offered or 6rd is used. Signed-off-by: Hans Dedecker --- bridge.c | 6 ++++++ device.c | 9 +++++++++ device.h | 3 +++ system-linux.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/bridge.c b/bridge.c index 6baef29..3edfeaa 100644 --- a/bridge.c +++ b/bridge.c @@ -154,6 +154,12 @@ bridge_enable_member(struct bridge_member *bm) if (!bm->present) return 0; + /* Disable IPv6 for bridge members */ + if (!(bm->dev.dev->settings.flags & DEV_OPT_IPV6)) { + bm->dev.dev->settings.ipv6 = 0; + bm->dev.dev->settings.flags |= DEV_OPT_IPV6; + } + ret = device_claim(&bm->dev); if (ret < 0) goto error; diff --git a/device.c b/device.c index 2fb68d7..f484c28 100644 --- a/device.c +++ b/device.c @@ -37,6 +37,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [DEV_ATTR_MACADDR] = { .name = "macaddr", .type = BLOBMSG_TYPE_STRING }, [DEV_ATTR_TXQUEUELEN] = { .name = "txqueuelen", .type = BLOBMSG_TYPE_INT32 }, [DEV_ATTR_ENABLED] = { .name = "enabled", .type = BLOBMSG_TYPE_BOOL }, + [DEV_ATTR_IPV6] = { .name = "ipv6", .type = BLOBMSG_TYPE_BOOL }, }; const struct uci_blob_param_list device_attr_list = { @@ -139,6 +140,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) memcpy(n->macaddr, (s->flags & DEV_OPT_MACADDR ? s->macaddr : os->macaddr), sizeof(n->macaddr)); + n->ipv6 = s->flags & DEV_OPT_IPV6 ? s->ipv6 : os->ipv6; n->flags = s->flags | os->flags; } @@ -172,6 +174,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) } } + if ((cur = tb[DEV_ATTR_IPV6])) { + s->ipv6 = blobmsg_get_bool(cur); + s->flags |= DEV_OPT_IPV6; + } + device_set_disabled(dev, disabled); } @@ -705,6 +712,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) blobmsg_add_string(b, "macaddr", format_macaddr(st.macaddr)); if (st.flags & DEV_OPT_TXQUEUELEN) blobmsg_add_u32(b, "txqueuelen", st.txqueuelen); + if (st.flags & DEV_OPT_IPV6) + blobmsg_add_u8(b, "ipv6", st.ipv6); } s = blobmsg_open_table(b, "statistics"); diff --git a/device.h b/device.h index e71027e..6fceaa1 100644 --- a/device.h +++ b/device.h @@ -31,6 +31,7 @@ enum { DEV_ATTR_MACADDR, DEV_ATTR_TXQUEUELEN, DEV_ATTR_ENABLED, + DEV_ATTR_IPV6, __DEV_ATTR_MAX, }; @@ -60,6 +61,7 @@ enum { DEV_OPT_MTU = (1 << 0), DEV_OPT_MACADDR = (1 << 1), DEV_OPT_TXQUEUELEN = (1 << 2), + DEV_OPT_IPV6 = (1 << 3), }; /* events broadcasted to all users of a device */ @@ -105,6 +107,7 @@ struct device_settings { unsigned int mtu; unsigned int txqueuelen; uint8_t macaddr[6]; + bool ipv6; }; /* diff --git a/system-linux.c b/system-linux.c index 7cec649..8f46705 100644 --- a/system-linux.c +++ b/system-linux.c @@ -211,6 +211,40 @@ 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); } +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 @@ -380,7 +414,6 @@ 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; @@ -390,7 +423,6 @@ int system_bridge_addif(struct device *bridge, struct device *dev) 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); } @@ -777,6 +809,7 @@ 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)); @@ -795,6 +828,11 @@ system_if_get_settings(struct device *dev, struct device_settings *s) 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 @@ -823,6 +861,8 @@ system_if_apply_settings(struct device *dev, struct device_settings *s, unsigned 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) -- 2.11.0