From 2d6a69e69a4b4cb2b8cc63c4810dae44e5a4d8f6 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Wed, 6 Dec 2017 19:26:25 +0100 Subject: Add support for IPv4v6 End User Addresses Before this commit, when an MS requested an ipv4v6 context osmo-ggsn returned an error stating the type was unknown, and this text was printed in the log: Processing create PDP context request for APN 'ims' Cannot decode EUA from MS/SGSN: f1 8d This patch has been tested with an MS running the 3 types of addresses: - IPv4 and IPv6: no regressions observed, the context is activated and packets are sent to the ggsn. - IPv4v6: Wireshark correctly parses request and reponse, and then ICMPv6 traffic from both sides. Finally I see the MS using the IPv4 and IPv6 DNS addresses advertised and TCP traffic over IPv4 (because probably my IPv6 network setup is not correct). I also checked I can disable/enable data (pdp ctx delete and activate) several times without any issue. Change-Id: Ic820759167fd3bdf329cb11d4b942e903fe50af5 --- ggsn/ggsn.c | 95 +++++++++++++++++++++++++++++++++-------------------------- ggsn/icmpv6.c | 7 ++++- 2 files changed, 59 insertions(+), 43 deletions(-) (limited to 'ggsn') diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 1c1276f..fa3e20c 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -335,17 +335,20 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st static int delete_context(struct pdp_t *pdp) { struct gsn_t *gsn = pdp->gsn; - struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer; struct apn_ctx *apn = pdp->priv; + struct ippoolm_t *member; + int i; LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n"); - struct ippoolm_t *member = pdp->peer; - if (pdp->peer) { - send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */ - ippool_freeip(ipp->pool, ipp); - } else - LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n"); + for (i = 0; i < 2; i++) { + if (pdp->peer[i]) { + member = pdp->peer[i]; + send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */ + ippool_freeip(member->pool, member); + } else if(i == 0) + LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n"); + } if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) { LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n", @@ -512,10 +515,10 @@ int create_context_ind(struct pdp_t *pdp) static char name_buf[256]; struct gsn_t *gsn = pdp->gsn; struct ggsn_ctx *ggsn = gsn->priv; - struct in46_addr addr; - struct ippoolm_t *member; + struct in46_addr addr[2]; + struct ippoolm_t *member = NULL; struct apn_ctx *apn; - int rc; + int rc, num_addr, i; osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l); @@ -550,55 +553,63 @@ 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 (in46a_from_eua(&pdp->eua, &addr)) { + memset(addr, 0, sizeof(addr)); + if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) { LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n", osmo_hexdump(pdp->eua.v, pdp->eua.l)); gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP); return 0; } - if (addr.len == sizeof(struct in_addr)) { - /* does this APN actually have an IPv4 pool? */ - if (!apn_supports_ipv4(apn)) - goto err_wrong_af; + /* Allocate dynamic addresses from the pool */ + for (i = 0; i < num_addr; i++) { + if (addr[i].len == sizeof(struct in_addr)) { + /* does this APN actually have an IPv4 pool? */ + if (!apn_supports_ipv4(apn)) + goto err_wrong_af; + + rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0); + if (rc < 0) + goto err_pool_full; + /* copy back */ + memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4); + + } else if (addr[i].len == sizeof(struct in6_addr)) { + + /* does this APN actually have an IPv6 pool? */ + if (!apn_supports_ipv6(apn)) + goto err_wrong_af; + + rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0); + if (rc < 0) + goto err_pool_full; + + /* IPv6 doesn't really send the real/allocated address at this point, but just + * the link-identifier which the MS shall use for router solicitation */ + /* initialize upper 64 bits to prefix, they are discarded by MS anyway */ + memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8); + /* use allocated 64bit prefix as lower 64bit, used as link id by MS */ + memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8); + } else + OSMO_ASSERT(0); - rc = ippool_newip(apn->v4.pool, &member, &addr, 0); - if (rc < 0) - goto err_pool_full; - in46a_to_eua(&member->addr, &pdp->eua); + pdp->peer[i] = member; + member->peer = pdp; + } + + in46a_to_eua(addr, num_addr, &pdp->eua); + if (apn_supports_ipv4(apn)) { /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */ if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) { LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno)); gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL); return 0; } - } else if (addr.len == sizeof(struct in6_addr)) { - struct in46_addr tmp; - - /* does this APN actually have an IPv6 pool? */ - if (!apn_supports_ipv6(apn)) - goto err_wrong_af; - - rc = ippool_newip(apn->v6.pool, &member, &addr, 0); - if (rc < 0) - goto err_pool_full; - - /* IPv6 doesn't really send the real/allocated address at this point, but just - * the link-identifier which the MS shall use for router solicitation */ - tmp.len = addr.len; - /* initialize upper 64 bits to prefix, they are discarded by MS anyway */ - memcpy(tmp.v6.s6_addr, &member->addr.v6, 8); - /* use allocated 64bit prefix as lower 64bit, used as link id by MS */ - memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8); - in46a_to_eua(&tmp, &pdp->eua); - } else - OSMO_ASSERT(0); + } - pdp->peer = member; pdp->ipif = apn->tun.tun; /* TODO */ pdp->priv = apn; - member->peer = pdp; if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */ gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES); diff --git a/ggsn/icmpv6.c b/ggsn/icmpv6.c index 11ced24..6564a54 100644 --- a/ggsn/icmpv6.c +++ b/ggsn/icmpv6.c @@ -183,12 +183,17 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len) int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr, const uint8_t *pack, unsigned len) { - struct ippoolm_t *member = pdp->peer; + struct ippoolm_t *member; const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack; const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h)); struct msgb *msg; OSMO_ASSERT(pdp); + + member = pdp->peer[0]; + OSMO_ASSERT(member); + if (member->addr.len == sizeof(struct in_addr)) /* ipv4v6 context */ + member = pdp->peer[1]; OSMO_ASSERT(member); if (len < sizeof(*ip6h)) { -- cgit v1.2.3