aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Smith <osmith@sysmocom.de>2023-07-18 16:47:08 +0200
committerOliver Smith <osmith@sysmocom.de>2024-02-21 16:22:24 +0100
commitb17fe7bfe955fafc1f34ee64e420d5a5bf41cfce (patch)
treeb5cf032539f08f098c9fe695ff757075451bba27
parent0917ce4e2271c0d71dfb8c86eb2d99821a374781 (diff)
kernel-gtp: support IPv6 on inner layer
-rw-r--r--TODO-RELEASE1
-rw-r--r--ggsn/ggsn.c8
-rw-r--r--lib/gtp-kernel.c104
3 files changed, 70 insertions, 43 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;
-
- memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
- memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
-
- 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);
+ in46a_from_gsna(&pdp->gsnrc, &sgsn);
+
+ ms_addr_count = in46a_from_eua(&pdp->eua, ms);
+
+ 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);
+ ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
+ gtp_tunnel_free(t);
+
+ if (ret != 0)
+ break;
+ }
return ret;
}