From cddc20689a17a1b30d491cd2021f911a669f6dbc Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Fri, 18 Jul 2014 12:13:43 +0400 Subject: [PATCH] ipv6: add support for prefixes greater than 64 --- accel-pppd/extra/ipv6pool.c | 82 ++++++++++++++++++++++++++------- accel-pppd/ifcfg.c | 18 ++++---- accel-pppd/ipv6/nd.c | 2 +- accel-pppd/libnetlink/iputils.c | 36 +++++++++++++++ accel-pppd/libnetlink/iputils.h | 2 + 5 files changed, 115 insertions(+), 25 deletions(-) diff --git a/accel-pppd/extra/ipv6pool.c b/accel-pppd/extra/ipv6pool.c index 9db7b40..0ca270c 100644 --- a/accel-pppd/extra/ipv6pool.c +++ b/accel-pppd/extra/ipv6pool.c @@ -28,55 +28,105 @@ struct dppool_item_t struct ipv6db_prefix_t it; }; - static LIST_HEAD(ippool); static LIST_HEAD(dppool); static spinlock_t pool_lock; static struct ipdb_t ipdb; +static void in6_addr_add(struct in6_addr *res, const struct in6_addr *arg) +{ + uint16_t n = 0; + int i; + + for (i = 15; i >= 0; i--) { + n = (uint16_t)res->s6_addr[i] + arg->s6_addr[i] + (n >> 8); + res->s6_addr[i] = n & 0xff; + } +} + +static int in6_addr_cmp(const struct in6_addr *n1, const struct in6_addr *n2) +{ + int i; + + for (i = 0; i < 16; i++) { + if (n1->s6_addr[i] < n2->s6_addr[i]) + return -1; + if (n1->s6_addr[i] > n2->s6_addr[i]) + return 1; + } + + return 0; +} + static void generate_ippool(struct in6_addr *addr, int mask, int prefix_len) { struct ippool_item_t *it; - uint64_t ip, endip, step; struct ipv6db_addr_t *a; + struct in6_addr ip, end, step; - ip = be64toh(*(uint64_t *)addr->s6_addr); - endip = ip | ((1llu << (64 - mask)) - 1); - step = 1 << (64 - prefix_len); - - for (; ip <= endip; ip += step) { + memcpy(&ip, addr, sizeof(ip)); + + memcpy(&end, addr, sizeof(end)); + if (mask > 64) + *(uint64_t *)(end.s6_addr + 8) = htobe64(be64toh(*(uint64_t *)(end.s6_addr + 8)) | ((1llu << (128 - mask)) - 1)); + else { + memset(end.s6_addr + 8, 0xff, 8); + *(uint64_t *)end.s6_addr = htobe64(be64toh(*(uint64_t *)end.s6_addr) | ((1llu << (64 - mask)) - 1)); + } + + memset(&step, 0, sizeof(step)); + if (prefix_len > 64) + *(uint64_t *)(step.s6_addr + 8) = htobe64(1llu << (128 - prefix_len)); + else + *(uint64_t *)step.s6_addr = htobe64(1llu << (64 - prefix_len)); + + while (in6_addr_cmp(&ip, &end) <= 0) { it = malloc(sizeof(*it)); it->it.owner = &ipdb; INIT_LIST_HEAD(&it->it.addr_list); a = malloc(sizeof(*a)); memset(a, 0, sizeof(*a)); - *(uint64_t *)a->addr.s6_addr = htobe64(ip); + memcpy(&a->addr, &ip, sizeof(ip)); a->prefix_len = prefix_len; list_add_tail(&a->entry, &it->it.addr_list); list_add_tail(&it->entry, &ippool); + in6_addr_add(&ip, &step); } } static void generate_dppool(struct in6_addr *addr, int mask, int prefix_len) { struct dppool_item_t *it; - uint64_t ip, endip, step; + struct in6_addr ip, end, step; struct ipv6db_addr_t *a; - ip = be64toh(*(uint64_t *)addr->s6_addr); - endip = ip | ((1llu << (64 - mask)) - 1); - step = 1 << (64 - prefix_len); + memcpy(&ip, addr, sizeof(ip)); + + memcpy(&end, addr, sizeof(end)); + if (mask > 64) + *(uint64_t *)(end.s6_addr + 8) = htobe64(be64toh(*(uint64_t *)(end.s6_addr + 8)) | ((1llu << (128 - mask)) - 1)); + else { + memset(end.s6_addr + 8, 0xff, 8); + *(uint64_t *)end.s6_addr = htobe64(be64toh(*(uint64_t *)end.s6_addr) | ((1llu << (64 - mask)) - 1)); + } + + memset(&step, 0, sizeof(step)); + if (prefix_len > 64) + *(uint64_t *)(step.s6_addr + 8) = htobe64(1llu << (128 - prefix_len)); + else + *(uint64_t *)step.s6_addr = htobe64(1llu << (64 - prefix_len)); - for (; ip <= endip; ip += step) { + while (in6_addr_cmp(&ip, &end) <= 0) { it = malloc(sizeof(*it)); it->it.owner = &ipdb; INIT_LIST_HEAD(&it->it.prefix_list); a = malloc(sizeof(*a)); memset(a, 0, sizeof(*a)); - *(uint64_t *)a->addr.s6_addr = htobe64(ip); + memcpy(&a->addr, &ip, sizeof(ip)); a->prefix_len = prefix_len; list_add_tail(&a->entry, &it->it.prefix_list); list_add_tail(&it->entry, &dppool); + in6_addr_add(&ip, &step); } } @@ -107,13 +157,13 @@ static void add_prefix(int type, const char *_val) if (sscanf(ptr1 + 1, "%i", &mask) != 1) goto err; - if (mask < 7 || mask > 64) + if (mask < 7 || mask > 127) goto err; if (sscanf(ptr2 + 1, "%i", &prefix_len) != 1) goto err; - if (prefix_len > 64 || prefix_len < mask) + if (prefix_len > 128 || prefix_len < mask) goto err; if (type) diff --git a/accel-pppd/ifcfg.c b/accel-pppd/ifcfg.c index 3aff9eb..3190873 100644 --- a/accel-pppd/ifcfg.c +++ b/accel-pppd/ifcfg.c @@ -13,6 +13,7 @@ #include "linux_ppp.h" #include "triton.h" +#include "iputils.h" #include "events.h" #include "ppp.h" #include "ipdb.h" @@ -150,14 +151,15 @@ void ap_session_ifup(struct ap_session *ses) } list_for_each_entry(a, &ses->ipv6->addr_list, entry) { - if (a->prefix_len == 128) - continue; - - build_addr(a, ses->ipv6->intf_id, &ifr6.ifr6_addr); - ifr6.ifr6_prefixlen = a->prefix_len; - - if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) - log_ppp_error("failed to add IPv6 address: %s\n", strerror(errno)); + /*if (a->prefix_len < 128) { + build_addr(a, ses->ipv6->intf_id, &ifr6.ifr6_addr); + ifr6.ifr6_prefixlen = a->prefix_len; + + if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) + log_ppp_error("failed to add IPv6 address: %s\n", strerror(errno)); + } else*/ + if (ip6route_add(ses->ifindex, &a->addr, a->prefix_len, 0)) + log_ppp_error("failed to add IPv6 route: %s\n", strerror(errno)); } } diff --git a/accel-pppd/ipv6/nd.c b/accel-pppd/ipv6/nd.c index 02e372c..67a4a0f 100644 --- a/accel-pppd/ipv6/nd.c +++ b/accel-pppd/ipv6/nd.c @@ -127,7 +127,7 @@ static void ipv6_nd_send_ra(struct ipv6_nd_handler_t *h, struct sockaddr_in6 *ad pinfo = (struct nd_opt_prefix_info *)(adv + 1); list_for_each_entry(a, &h->ses->ipv6->addr_list, entry) { - if (a->prefix_len > 64) + if (a->prefix_len != 64) continue; memset(pinfo, 0, sizeof(*pinfo)); diff --git a/accel-pppd/libnetlink/iputils.c b/accel-pppd/libnetlink/iputils.c index 060fbab..7077098 100644 --- a/accel-pppd/libnetlink/iputils.c +++ b/accel-pppd/libnetlink/iputils.c @@ -377,6 +377,42 @@ int __export iproute_del(int ifindex, in_addr_t dst, int proto) return 0; } +int __export ip6route_add(int ifindex, struct in6_addr *dst, int pref_len, int proto) +{ + struct ipaddr_req { + struct nlmsghdr n; + struct rtmsg i; + char buf[4096]; + } req; + + if (!rth) + open_rth(); + + if (!rth) + return -1; + + memset(&req, 0, sizeof(req) - 4096); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; + req.n.nlmsg_type = RTM_NEWROUTE; + req.i.rtm_family = AF_INET6; + req.i.rtm_table = RT_TABLE_MAIN; + req.i.rtm_scope = RT_SCOPE_LINK; + req.i.rtm_protocol = proto; + req.i.rtm_type = RTN_UNICAST; + req.i.rtm_dst_len = pref_len; + + addattr_l(&req.n, sizeof(req), RTA_DST, dst, sizeof(*dst)); + addattr32(&req.n, sizeof(req), RTA_OIF, ifindex); + + if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0) + return -1; + + return 0; + +} + in_addr_t __export iproute_get(in_addr_t dst) { struct ipaddr_req { diff --git a/accel-pppd/libnetlink/iputils.h b/accel-pppd/libnetlink/iputils.h index 75dfd1a..0c88793 100644 --- a/accel-pppd/libnetlink/iputils.h +++ b/accel-pppd/libnetlink/iputils.h @@ -18,6 +18,8 @@ int iproute_add(int ifindex, in_addr_t src, in_addr_t dst, int proto); int iproute_del(int ifindex, in_addr_t dst, int proto); in_addr_t iproute_get(in_addr_t dst); +int ip6route_add(int ifindex, struct in6_addr *dst, int prefix_len, int proto); + int iprule_add(uint32_t addr, int table); int iprule_del(uint32_t addr, int table); #endif -- GitLab