aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Smith <osmith@sysmocom.de>2024-02-23 13:23:54 +0100
committerOliver Smith <osmith@sysmocom.de>2024-02-23 16:30:56 +0100
commit2b0bc7c35eb87108af50527ffe8a3efca2eed9b5 (patch)
tree967a9149b2c71f77b38e65cff0bf1d5c59c9ad72
parentdaddf2e13f1ffc1eaffea77a682a6490cbbf5f5d (diff)
WIP: support IPv6 on outer layer (backwards compatible)osmith/wip
-rw-r--r--ggsn/ggsn.c11
-rw-r--r--gtp/gsn.c141
-rw-r--r--gtp/gsn_internal.h10
-rw-r--r--gtp/gtp.c16
-rw-r--r--include/osmocom/gtp/gsn.h15
5 files changed, 154 insertions, 39 deletions
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 @@ int ggsn_start(struct ggsn_ctx *ggsn)
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 @@ int ggsn_start(struct ggsn_ctx *ggsn)
/* 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 @@ free_filename:
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 @@ int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
/* 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 @@ int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
(*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 @@ error:
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 f426e6d..5843dfe 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -1067,6 +1067,14 @@ int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
pdp->fd, pdp->seq, pdp->tid);
}
+static void in46a_to_gsna(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);
+}
+
/* Handle Create PDP Context Request */
int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
struct sockaddr_in *peer, int fd,
@@ -1083,6 +1091,7 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
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 +1309,11 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
}
/* Initialize our own IP addresses */
- in_addr2gsna(&pdp->gsnlc, &gsn->gsnc);
- in_addr2gsna(&pdp->gsnlu, &gsn->gsnu);
+ gtp_get_gsnc(gsn, &addr);
+ in46a_to_gsna(&pdp->gsnlc, &addr);
+
+ gtp_get_gsnu(gsn, &addr);
+ in46a_to_gsna(&pdp->gsnlu, &addr);
if (!gtp_pdp_getimsi(gsn, &pdp_old, pdp->imsi, pdp->nsapi)) {
/* Found old pdp with same tid. Now the voodoo begins! */
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 @@ struct gsn_t {
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 @@ struct gsn_t {
/* 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);