From a0d281db1cd2f122fb8f0adfd9e4a82e60efbd2f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 2 Aug 2017 21:48:16 +0200 Subject: IPv6 support for user IP This patch enables the use of IPv6 PDP contexts. The phone will have to request an IPv6 End-user-Address, and the GGSN will have to be configured for an IPv6 pool. The outer transport-layer IP between SGSN and GGSN must still be IPv4, it is not modified by this patch Change-Id: I22c3bf32a98e5daf99d6eaeac8c9f95cc7574774 --- ggsn/ggsn.c | 11 +++++++--- lib/in46_addr.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/in46_addr.h | 5 +++++ sgsnemu/sgsnemu.c | 2 +- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 4fb2066..8e7d1e3 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -190,7 +191,7 @@ int create_context_ind(struct pdp_t *pdp) memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */ pdp->qos_neg.l = pdp->qos_req.l; - if (pdp_euaton(&pdp->eua, &addr.v4)) { + if (in46a_from_eua(&pdp->eua, &addr)) { addr.v4.s_addr = 0; /* Request dynamic */ } @@ -199,7 +200,7 @@ int create_context_ind(struct pdp_t *pdp) return 0; /* Allready in use, or no more available */ } - pdp_ntoeua(&member->addr.v4, &pdp->eua); + in46a_to_eua(&member->addr, &pdp->eua); pdp->peer = member; pdp->ipif = tun; /* TODO */ member->peer = pdp; @@ -224,14 +225,18 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) struct ippoolm_t *ipm; struct in46_addr dst; struct iphdr *iph = (struct iphdr *)pack; + struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; if (iph->version == 4) { if (len < sizeof(*iph) || len < 4*iph->ihl) return -1; dst.len = 4; dst.v4.s_addr = iph->daddr; + } else if (iph->version == 6) { + dst.len = 16; + dst.v6 = ip6h->ip6_dst; } else { - LOGP(DGGSN, LOGL_NOTICE, "non-IPv4 packet received from tun\n"); + LOGP(DGGSN, LOGL_NOTICE, "non-IPv packet received from tun\n"); return -1; } diff --git a/lib/in46_addr.c b/lib/in46_addr.c index 903ceec..844e318 100644 --- a/lib/in46_addr.c +++ b/lib/in46_addr.c @@ -146,3 +146,63 @@ int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, return 0; } } + +/*! Convert given PDP End User Address to in46_addr + * \returns 0 on success; negative on error */ +int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua) +{ + switch (src->len) { + case 4: + eua->l = 6; + eua->v[0] = 0xf1; /* IETF */ + eua->v[1] = 0x21; /* IPv4 */ + memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */ + break; + case 16: + eua->l = 18; + eua->v[0] = 0xf1; /* IETF */ + eua->v[1] = 0x57; /* IPv6 */ + memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */ + break; + default: + return -1; + } + return 0; +} + +/*! Convert given in46_addr to PDP End User Address + * \returns 0 on success; negative on error */ +int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst) +{ + if (eua->l < 2) + goto default_to_dyn_v4; + + if (eua->v[0] != 0xf1) + return -1; + + switch (eua->v[1]) { + case 0x21: + dst->len = 4; + if (eua->l >= 6) + memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */ + else + dst->v4.s_addr = 0; + break; + case 0x57: + dst->len = 16; + if (eua->l >= 18) + memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */ + else + memset(&dst->v6, 0, 16); + break; + default: + return -1; + } + return 0; + +default_to_dyn_v4: + /* assume dynamic IPv4 by default */ + dst->len = 4; + dst->v4.s_addr = 0; + return 0; +} diff --git a/lib/in46_addr.h b/lib/in46_addr.h index f28fd8e..a0ad6e1 100644 --- a/lib/in46_addr.h +++ b/lib/in46_addr.h @@ -2,6 +2,8 @@ #include #include +#include "../gtp/pdp.h" + /* a simple wrapper around an in6_addr to also contain the length of the address, * thereby implicitly indicating the address family of the address */ struct in46_addr { @@ -17,3 +19,6 @@ extern int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in extern const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size); extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b); extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen); + +int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua); +int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst); diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c index af8f9fa..8c9cfd3 100644 --- a/sgsnemu/sgsnemu.c +++ b/sgsnemu/sgsnemu.c @@ -1329,7 +1329,7 @@ int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) return EOF; /* Not what we expected */ } - if (pdp_euaton(&pdp->eua, &addr.v4)) { + if (in46a_from_eua(&pdp->eua, &addr)) { printf ("Received create PDP context response. Cause value: %d\n", cause); -- cgit v1.2.3