From d4d6e09fd29e23e28960959ca488e1481339571e Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Tue, 8 Aug 2017 18:10:43 +0200 Subject: ippool: Extend pool to work with /64 prefixes In IPv6 GPRS, we actually don't want to allocate an individual v6 address (like in IPv4), but we want to allocate a prefix. The standard prefix lengh is 8 bytes, i.e. a /64 prefix. This patch extends the pool to be able to work with such v6 prefixes. Change-Id: I0cf700b6baf195a2e5fbea000531f801acaaa443 --- ggsn/ggsn.c | 6 +++++- lib/in46_addr.c | 2 ++ lib/ippool.c | 17 ++++++++++------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 0629f9e..9b11884 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -241,7 +241,11 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) dst.len = 4; dst.v4.s_addr = iph->daddr; } else if (iph->version == 6) { - dst.len = 16; + /* Due to the fact that 3GPP requires an allocation of a + * /64 prefix to each MS, we must instruct + * ippool_getip() below to match only the leading /64 + * prefix, i.e. the first 8 bytes of the address */ + dst.len = 8; dst.v6 = ip6h->ip6_dst; } else { LOGP(DGGSN, LOGL_NOTICE, "non-IPv packet received from tun\n"); diff --git a/lib/in46_addr.c b/lib/in46_addr.c index a220583..1785377 100644 --- a/lib/in46_addr.c +++ b/lib/in46_addr.c @@ -25,6 +25,7 @@ int in46a_to_af(const struct in46_addr *in) switch (in->len) { case 4: return AF_INET; + case 8: case 16: return AF_INET6; default: @@ -175,6 +176,7 @@ int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua) eua->v[1] = 0x21; /* IPv4 */ memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */ break; + case 8: case 16: eua->l = 18; eua->v[0] = 0xf1; /* IETF */ diff --git a/lib/ippool.c b/lib/ippool.c index 007dc50..1729ec7 100644 --- a/lib/ippool.c +++ b/lib/ippool.c @@ -96,11 +96,10 @@ static unsigned long int ippool_hash4(struct in_addr *addr) return lookup((unsigned char *)&addr->s_addr, sizeof(addr->s_addr), 0); } -static unsigned long int ippool_hash6(struct in6_addr *addr) +static unsigned long int ippool_hash6(struct in6_addr *addr, unsigned int len) { /* TODO: Review hash spread for IPv6 */ - return lookup((unsigned char *)addr->s6_addr, sizeof(addr->s6_addr), - 0); + return lookup((unsigned char *)addr->s6_addr, len, 0); } unsigned long int ippool_hash(struct in46_addr *addr) @@ -108,7 +107,7 @@ unsigned long int ippool_hash(struct in46_addr *addr) if (addr->len == 4) return ippool_hash4(&addr->v4); else - return ippool_hash6(&addr->v6); + return ippool_hash6(&addr->v6, addr->len); } /* Get IP address and mask */ @@ -209,6 +208,10 @@ int ippool_new(struct ippool_t **this, const char *dyn, const char *stat, "Failed to parse dynamic pool"); return -1; } + /* we want to work with /64 prefixes, i.e. allocate /64 prefixes rather + * than /128 (single IPv6 addresses) */ + if (addr.len == sizeof(struct in6_addr)) + addr.len = 64/8; /* Set IPPOOL_NONETWORK if IPPOOL_NOGATEWAY is set */ if (flags & IPPOOL_NOGATEWAY) { @@ -348,7 +351,7 @@ int ippool_getip(struct ippool_t *this, struct ippoolm_t **member, /* Find in hash table */ hash = ippool_hash(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { - if (in46a_equal(&p->addr, addr)) { + if (in46a_prefix_equal(&p->addr, addr)) { if (member) *member = p; return 0; @@ -421,7 +424,7 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member, /* Find in hash table */ hash = ippool_hash(addr) & this->hashmask; for (p = this->hash[hash]; p; p = p->nexthash) { - if (in46a_equal(&p->addr, addr)) { + if (in46a_prefix_equal(&p->addr, addr)) { p2 = p; break; } @@ -450,7 +453,7 @@ int ippool_newip(struct ippool_t *this, struct ippoolm_t **member, return -GTPCAUSE_SYS_FAIL; /* Allready in use / Should not happen */ } - if (p2->addr.len != addr->len) { + if (p2->addr.len != addr->len && !(addr->len == 16 && p2->addr.len == 8)) { SYS_ERR(DIP, LOGL_ERROR, 0, "MS requested unsupported PDP context type"); return -GTPCAUSE_UNKNOWN_PDP; } -- cgit v1.2.3