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);


--
To view, visit https://gerrit.osmocom.org/c/osmo-ggsn/+/36072?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: I4212d847b6e1ad5ce120451a2d8a5578c48b90fe
Gerrit-Change-Number: 36072
Gerrit-PatchSet: 1
Gerrit-Owner: osmith <[email protected]>
Gerrit-MessageType: newchange

Reply via email to