From b17fe7bfe955fafc1f34ee64e420d5a5bf41cfce Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Tue, 18 Jul 2023 16:47:08 +0200 Subject: [PATCH] kernel-gtp: support IPv6 on inner layer Related: OS#6096 Change-Id: I3df47b6c209f1e2f8254ba139581d6e622c6b35f --- TODO-RELEASE | 1 + ggsn/ggsn.c | 8 +--- lib/gtp-kernel.c | 100 +++++++++++++++++++++++++++++++---------------- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/TODO-RELEASE b/TODO-RELEASE index d8cc3af..22cfd1d 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -8,3 +8,4 @@ # If any interfaces have been removed or changed since the last public release: c:r:0. #library what description / commit summary line libgtp REMOVE remove GTP cause defines of reserved values +libgtpnl > 1.2.5 gtp_tunnel_set_family() diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index cec043d..db906ea 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -202,11 +202,6 @@ int apn_start(struct apn_ctx *apn) break; case APN_GTPU_MODE_KERNEL_GTP: LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name); - if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) { - LOGPAPN(LOGL_ERROR, apn, "Kernel GTP currently supports only IPv4\n"); - apn_stop(apn); - return -1; - } if (gsn == NULL) { /* skip bringing up the APN now if the GSN is not initialized yet. * This happens during initial load of the config file, as the @@ -536,8 +531,7 @@ int create_context_ind(struct pdp_t *pdp) in46a_to_eua(addr, num_addr, &pdp->eua); - if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP && apn_supports_ipv4(apn)) { - /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */ + if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) { 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); diff --git a/lib/gtp-kernel.c b/lib/gtp-kernel.c index 2a731e6..9bfb380 100644 --- a/lib/gtp-kernel.c +++ b/lib/gtp-kernel.c @@ -104,61 +104,93 @@ void gtp_kernel_stop(const char *devname) int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname) { - struct in_addr ms, sgsn; + int ms_addr_count; + struct in46_addr ms[2]; + struct in46_addr sgsn; struct gtp_tunnel *t; int ret; pdp_debug(__func__, devname, pdp); - t = gtp_tunnel_alloc(); - if (t == NULL) - return -1; + in46a_from_gsna(&pdp->gsnrc, &sgsn); - memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr)); - memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr)); + ms_addr_count = in46a_from_eua(&pdp->eua, ms); - gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); - gtp_tunnel_set_version(t, pdp->version); - gtp_tunnel_set_ms_ip4(t, &ms); - gtp_tunnel_set_sgsn_ip4(t, &sgsn); - if (pdp->version == 0) { - gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); - gtp_tunnel_set_flowid(t, pdp->flru); - } else { - gtp_tunnel_set_i_tei(t, pdp->teid_own); - /* use the TEI advertised by SGSN when sending packets - * towards the SGSN */ - gtp_tunnel_set_o_tei(t, pdp->teid_gn); + for (int i = 0; i < ms_addr_count; i++) { + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); + gtp_tunnel_set_version(t, pdp->version); + + if (in46a_to_af(&ms[i]) == AF_INET) + gtp_tunnel_set_ms_ip4(t, &ms[i].v4); + else { + /* In IPv6, EUA doesn't contain the actual IP + * addr/prefix. Set higher bits to 0 to get the 64 bit + * netmask. */ + memset(((void *)&ms[i].v6) + 8, 0, 8); + gtp_tunnel_set_ms_ip6(t, &ms[i].v6); + } + + if (in46a_to_af(&sgsn) == AF_INET) + gtp_tunnel_set_sgsn_ip4(t, &sgsn.v4); + else + gtp_tunnel_set_sgsn_ip6(t, &sgsn.v6); + + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_i_tei(t, pdp->teid_own); + /* use the TEI advertised by SGSN when sending packets + * towards the SGSN */ + gtp_tunnel_set_o_tei(t, pdp->teid_gn); + } + + ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + if (ret != 0) + break; } - ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); - gtp_tunnel_free(t); - return ret; } int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname) { + int ms_addr_count; + struct in46_addr ms[2]; struct gtp_tunnel *t; int ret; pdp_debug(__func__, devname, pdp); - t = gtp_tunnel_alloc(); - if (t == NULL) - return -1; + ms_addr_count = in46a_from_eua(&pdp->eua, ms); - gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); - gtp_tunnel_set_version(t, pdp->version); - if (pdp->version == 0) { - gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); - gtp_tunnel_set_flowid(t, pdp->flru); - } else { - gtp_tunnel_set_i_tei(t, pdp->teid_own); + for (int i = 0; i < ms_addr_count; i++) { + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + gtp_tunnel_set_ifidx(t, if_nametoindex(devname)); + gtp_tunnel_set_family(t, in46a_to_af(&ms[i])); + gtp_tunnel_set_version(t, pdp->version); + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_i_tei(t, pdp->teid_own); + } + + ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + if (ret != 0) + break; } - ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); - gtp_tunnel_free(t); - return ret; }