diff options
author | Oliver Smith <osmith@sysmocom.de> | 2024-02-23 13:23:54 +0100 |
---|---|---|
committer | Oliver Smith <osmith@sysmocom.de> | 2024-02-23 16:30:56 +0100 |
commit | 2b0bc7c35eb87108af50527ffe8a3efca2eed9b5 (patch) | |
tree | 967a9149b2c71f77b38e65cff0bf1d5c59c9ad72 | |
parent | daddf2e13f1ffc1eaffea77a682a6490cbbf5f5d (diff) |
WIP: support IPv6 on outer layer (backwards compatible)osmith/wip
Change-Id: I4212d847b6e1ad5ce120451a2d8a5578c48b90fe
-rw-r--r-- | ggsn/ggsn.c | 11 | ||||
-rw-r--r-- | gtp/gsn.c | 141 | ||||
-rw-r--r-- | gtp/gsn_internal.h | 10 | ||||
-rw-r--r-- | gtp/gtp.c | 16 | ||||
-rw-r--r-- | include/osmocom/gtp/gsn.h | 15 |
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); @@ -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; +}; @@ -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); |