osmith has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/36072?usp=email )
Change subject: kernel-gtp: support IPv6 on outer layer (backwards compatible) ......................................................................
kernel-gtp: support IPv6 on outer layer (backwards compatible)
Implement I257fff1dcd9d030a7f9ea936b2693a3f13208230 again, but this time make it backwards compatible. The IPv6 are stored in a new internal struct, and setters and getters are added.
Related: OS#1953, OS#6096 Change-Id: I4212d847b6e1ad5ce120451a2d8a5578c48b90fe --- M ggsn/ggsn.c M gtp/gsn.c M gtp/gsn_internal.h M gtp/gtp.c M gtp/gtp_internal.h M include/osmocom/gtp/gsn.h 6 files changed, 169 insertions(+), 39 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/72/36072/1
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index de3f614..76ed1d2 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -818,7 +818,7 @@ LOGPGGSN(LOGL_INFO, ggsn, "Starting GGSN\n");
/* Start libgtp listener */ - if (gtp_new(&ggsn->gsn, ggsn->cfg.state_dir, &ggsn->cfg.listen_addr.v4, GTP_MODE_GGSN)) { + if (gtp_new2(&ggsn->gsn, ggsn->cfg.state_dir, &ggsn->cfg.listen_addr, GTP_MODE_GGSN)) { LOGPGGSN(LOGL_ERROR, ggsn, "Failed to create GTP: %s\n", strerror(errno)); return -1; } @@ -826,11 +826,10 @@
/* patch in different addresses to use (in case we're behind NAT, the listen * address is different from what we advertise externally) */ - if (ggsn->cfg.gtpc_addr.v4.s_addr) - ggsn->gsn->gsnc = ggsn->cfg.gtpc_addr.v4; - - if (ggsn->cfg.gtpu_addr.v4.s_addr) - ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4; + if (ggsn->cfg.gtpc_addr.len) + gtp_set_gsnc(ggsn->gsn, &ggsn->cfg.gtpc_addr); + if (ggsn->cfg.gtpu_addr.len) + gtp_set_gsnu(ggsn->gsn, &ggsn->cfg.gtpu_addr);
/* Register File Descriptors */ osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, OSMO_FD_READ, ggsn_gtp_fd_cb, ggsn, 0); diff --git a/gtp/gsn.c b/gtp/gsn.c index 8cb1a47..3cdb61e 100644 --- a/gtp/gsn.c +++ b/gtp/gsn.c @@ -431,49 +431,65 @@ talloc_free(filename); }
-static int create_and_bind_socket(const char *name, struct gsn_t *gsn, int *fd, int domain, - const struct in_addr *listen, int port) +static int create_and_bind_socket(const char *name, struct gsn_t *gsn, int *fd, const struct in46_addr *listen, + int port) { - struct sockaddr_in addr; + int family = in46a_to_af(listen); int type = SOCK_DGRAM; int protocol = 0; - - *fd = socket(domain, type, protocol); - + struct sockaddr addr = {0}; + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; + *fd = socket(family, type, protocol); if (*fd < 0) { rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SOCKET); LOGP(DLGTP, LOGL_ERROR, "%s socket(domain=%d, type=%d, protocol=%d) failed: Error = %s\n", - name, domain, type, protocol, strerror(errno)); + name, family, type, protocol, strerror(errno)); return -errno; } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = domain; - addr.sin_addr = *listen; - addr.sin_port = htons(port); + switch (family) { + case AF_INET: + addr4->sin_family = AF_INET; + addr4->sin_addr = listen->v4; + addr4->sin_port = htons(port); #if defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr4->sin_len = sizeof(struct addr); #endif - - if (bind(*fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + break; + case AF_INET6: + addr6->sin6_family = AF_INET6; + addr6->sin6_addr = listen->v6; + addr6->sin6_port = htons(port); +#if defined(__FreeBSD__) || defined(__APPLE__) + addr6->sin6_len = sizeof(struct addr); +#endif + break; + default: + OSMO_ASSERT(false); + break; + } + if (bind(*fd, &addr, sizeof(addr)) < 0) { rate_ctr_inc2(gsn->ctrg, GSN_CTR_ERR_SOCKET); - LOGP_WITH_ADDR(DLGTP, LOGL_ERROR, addr, - "%s bind(fd=%d) failed: Error = %s\n", - name, *fd, strerror(errno)); + LOGP(DLGTP, LOGL_ERROR, + "%s bind(fd=%d, addr=(%s:%d)) failed: Error = %s\n", + name, *fd, in46a_ntoa(listen), port, strerror(errno)); return -errno; } - return 0; }
-int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, - int mode) +int gtp_new2(struct gsn_t **gsn, char *statedir, struct in46_addr *listen, int mode) { - LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", inet_ntoa(*listen)); + struct gsn_internal *internal; + + LOGP(DLGTP, LOGL_NOTICE, "GTP: gtp_newgsn() started at %s\n", in46a_ntoa(listen));
*gsn = talloc_zero(gsn_ctx, struct gsn_t);
+ internal = talloc_zero(*gsn, struct gsn_internal); + (*gsn)->internal = internal; + (*gsn)->statedir = statedir; log_restart(*gsn);
@@ -510,8 +526,8 @@
/* Store function parameters */ /* Same IP for user traffic and signalling */ - (*gsn)->gsnc = *listen; - (*gsn)->gsnu = *listen; + gtp_set_gsnc(*gsn, listen); + gtp_set_gsnu(*gsn, listen); (*gsn)->mode = mode;
(*gsn)->fd0 = -1; @@ -519,15 +535,13 @@ (*gsn)->fd1u = -1;
/* Create GTP version 0 socket */ - if (create_and_bind_socket("GTPv0", *gsn, &(*gsn)->fd0, AF_INET, listen, GTP0_PORT) < 0) + if (create_and_bind_socket("GTPv0", *gsn, &(*gsn)->fd0, listen, GTP0_PORT) < 0) goto error; - /* Create GTP version 1 control plane socket */ - if (create_and_bind_socket("GTPv1 control plane", *gsn, &(*gsn)->fd1c, AF_INET, listen, GTP1C_PORT) < 0) + if (create_and_bind_socket("GTPv1 control plane", *gsn, &(*gsn)->fd1c, listen, GTP1C_PORT) < 0) goto error; - /* Create GTP version 1 user plane socket */ - if (create_and_bind_socket("GTPv1 user plane", *gsn, &(*gsn)->fd1u, AF_INET, listen, GTP1U_PORT) < 0) + if (create_and_bind_socket("GTPv1 user plane", *gsn, &(*gsn)->fd1u, listen, GTP1U_PORT) < 0) goto error;
/* Start internal queue timer */ @@ -540,6 +554,75 @@ return -1; }
+/* IPv4 only, use gtp_new2 to support IPv6 too. */ +int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, int mode) +{ + struct in46_addr i46a = {0}; + + i46a.len = sizeof(struct in_addr); + i46a.v4 = *listen; + + return gtp_new2(gsn, statedir, &i46a, mode); +} + +void gtp_get_gsnc(const struct gsn_t *gsn, struct in46_addr *dest) +{ + struct gsn_internal *internal = gsn->internal; + + if (internal->gsnc_is_v6) { + dest->len = sizeof(struct in6_addr); + dest->v6 = internal->gsnc6; + } else { + dest->len = sizeof(struct in_addr); + dest->v4 = gsn->gsnc; + } +} + +void gtp_get_gsnu(const struct gsn_t *gsn, struct in46_addr *dest) +{ + struct gsn_internal *internal = gsn->internal; + + if (internal->gsnu_is_v6) { + dest->len = sizeof(struct in6_addr); + dest->v6 = internal->gsnu6; + } else { + dest->len = sizeof(struct in_addr); + dest->v4 = gsn->gsnu; + } +} + +void gtp_set_gsnc(struct gsn_t *gsn, const struct in46_addr *addr) +{ + struct gsn_internal *internal = gsn->internal; + + if (in46a_to_af(addr) == AF_INET6) { + gsn->gsnc = (struct in_addr){0}; + internal->gsnc6 = addr->v6; + internal->gsnc_is_v6 = true; + + } else { + gsn->gsnc = addr->v4; + internal->gsnc6 = (struct in6_addr){0}; + internal->gsnc_is_v6 = false; + } +} + +void gtp_set_gsnu(struct gsn_t *gsn, const struct in46_addr *addr) +{ + struct gsn_internal *internal = gsn->internal; + + if (in46a_to_af(addr) == AF_INET6) { + gsn->gsnu = (struct in_addr){0}; + internal->gsnu6 = addr->v6; + internal->gsnu_is_v6 = true; + + } else { + gsn->gsnu = addr->v4; + internal->gsnu6 = (struct in6_addr){0}; + internal->gsnu_is_v6 = false; + } +} + int gtp_free(struct gsn_t *gsn) {
diff --git a/gtp/gsn_internal.h b/gtp/gsn_internal.h index 732cb17..dbf8991 100644 --- a/gtp/gsn_internal.h +++ b/gtp/gsn_internal.h @@ -2,3 +2,13 @@ #include <osmocom/core/in46_addr.h>
void gtp_queue_timer_start(struct gsn_t *gsn); + +struct gsn_internal { + /* IP address of this gsn for signalling */ + bool gsnc_is_v6; + struct in6_addr gsnc6; + + /* IP address of this gsn for user traffic */ + bool gsnu_is_v6; + struct in6_addr gsnu6; +}; diff --git a/gtp/gtp.c b/gtp/gtp.c index 68d8003..9708928 100644 --- a/gtp/gtp.c +++ b/gtp/gtp.c @@ -1083,6 +1083,7 @@ int hlen = get_hlen(pack); uint8_t linked_nsapi = 0; struct pdp_t *linked_pdp = NULL; + struct in46_addr addr;
if (!gtp_duplicate(gsn, version, peer, seq)) return 0; @@ -1300,8 +1301,11 @@ }
/* Initialize our own IP addresses */ - in_addr2gsna(&pdp->gsnlc, &gsn->gsnc); - in_addr2gsna(&pdp->gsnlu, &gsn->gsnu); + gtp_get_gsnc(gsn, &addr); + in46a2gsna(&pdp->gsnlc, &addr); + + gtp_get_gsnu(gsn, &addr); + in46a2gsna(&pdp->gsnlu, &addr);
if (!gtp_pdp_getimsi(gsn, &pdp_old, pdp->imsi, pdp->nsapi)) { /* Found old pdp with same tid. Now the voodoo begins! */ @@ -3115,6 +3119,14 @@ memcpy(&dst->v6, gsna->v, gsna->l); }
+void in46a2gsna(struct ul16_t *gsna, const struct in46_addr *src) +{ + memset(gsna, 0, sizeof(struct ul16_t)); + gsna->l = src->len; + OSMO_ASSERT(gsna->l <= sizeof(gsna->v)); + memcpy(gsna->v, &src->v6, gsna->l); +} +
/* TS 29.060 has yet again a different encoding for IMSIs than * what we have in other places, so we cannot use the gsm48 diff --git a/gtp/gtp_internal.h b/gtp/gtp_internal.h index 7ee47cc..3e47026 100644 --- a/gtp/gtp_internal.h +++ b/gtp/gtp_internal.h @@ -50,4 +50,5 @@ int eua2ipv4(struct in_addr *dst, struct ul66_t *eua); int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src); void gsna2in46a(const struct ul16_t *gsna, struct in46_addr *dst); +void in46a2gsna(struct ul16_t *gsna, const struct in46_addr *src); uint64_t gtp_imsi_str2gtp(const char *str); diff --git a/include/osmocom/gtp/gsn.h b/include/osmocom/gtp/gsn.h index 936db9b..2db4fd4 100644 --- a/include/osmocom/gtp/gsn.h +++ b/include/osmocom/gtp/gsn.h @@ -17,6 +17,7 @@ #include <osmocom/core/timer.h> #include <osmocom/core/tdef.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/core/in46_addr.h>
#include "pdp.h"
@@ -77,6 +78,9 @@ int fd1c; /* GTP1 control plane file descriptor */ int fd1u; /* GTP0 user plane file descriptor */ int mode; /* Mode of operation: GGSN or SGSN */ + + /* If you access gsnc and gsnu directly, only IPv4 is supported. Use + * gtp_new2(), gtp_get_gsnc(), gtp_get_gsnu() to support IPv6 too. */ struct in_addr gsnc; /* IP address of this gsn for signalling */ struct in_addr gsnu; /* IP address of this gsn for user traffic */
@@ -113,12 +117,19 @@
/* Timers: */ struct osmo_tdef *tdef; + + void *internal; };
/* External API functions */
-extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, - int mode); +extern int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, int mode); +extern int gtp_new2(struct gsn_t **gsn, char *statedir, struct in46_addr *listen, int mode); + +extern void gtp_get_gsnc(const struct gsn_t *gsn, struct in46_addr *dest); +extern void gtp_get_gsnu(const struct gsn_t *gsn, struct in46_addr *dest); +extern void gtp_set_gsnu(struct gsn_t *gsn, const struct in46_addr *addr); +extern void gtp_set_gsnc(struct gsn_t *gsn, const struct in46_addr *addr);
extern int gtp_free(struct gsn_t *gsn);