pespin has submitted this change. ( 
https://gerrit.osmocom.org/c/osmo-uecups/+/42456?usp=email )

Change subject: Implement logic to perform IPv6 SLAAC in GTPU tunnel
......................................................................

Implement logic to perform IPv6 SLAAC in GTPU tunnel

The IPv6 SLAAC prcoedure is not yet triggered in this patch; it will be
triggered by cups_client's new command in follow-up patch.

Depends: libosmo-netif.git Change-Id I78ec7270c717af0a1b8ffd9398cd69ea7a0dbee2
Change-Id: I837a4d7ec7c134412ab4a2e09909670e81ecbeea
---
M TODO-RELEASE
M daemon/gtp_endpoint.c
M daemon/gtp_tunnel.c
M daemon/internal.h
M daemon/main.c
M daemon/tun_device.c
6 files changed, 162 insertions(+), 6 deletions(-)

Approvals:
  Jenkins Builder: Verified
  pespin: Looks good to me, approved




diff --git a/TODO-RELEASE b/TODO-RELEASE
index 8eda384..6149922 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
 libosmocore      >1.13.1          osmo_netdev_del_addr()
+libosmo-netif    >1.7.0           osmo_icmpv6_*()
diff --git a/daemon/gtp_endpoint.c b/daemon/gtp_endpoint.c
index 3340aee..5d14e84 100644
--- a/daemon/gtp_endpoint.c
+++ b/daemon/gtp_endpoint.c
@@ -13,6 +13,9 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>

 #include <pthread.h>

@@ -21,6 +24,8 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/logging.h>

+#include <osmocom/netif/icmpv6.h>
+
 #include "gtp.h"
 #include "internal.h"

@@ -41,6 +46,51 @@
  * GTP Endpoint (UDP socket)
  ***********************************************************************/

+static void handle_router_adv(struct gtp_tunnel *t, struct ip6_hdr *ip6h, 
struct osmo_icmpv6_radv_hdr *ra, size_t ra_len)
+{
+       struct osmo_icmpv6_opt_hdr *opt_hdr;
+       struct osmo_icmpv6_opt_prefix *opt_prefix;
+       int rc;
+       struct in6_addr rm;
+       char ip6strbuf[2][INET6_ADDRSTRLEN];
+       memset(&rm, 0, sizeof(rm));
+
+       LOGT(t, LOGL_INFO, "Received ICMPv6 Router Advertisement\n");
+
+       foreach_icmpv6_opt(ra, ra_len, opt_hdr) {
+               if (opt_hdr->type == ICMPv6_OPT_TYPE_PREFIX_INFO) {
+                       opt_prefix = (struct osmo_icmpv6_opt_prefix *)opt_hdr;
+                       size_t prefix_len_bytes = (opt_prefix->prefix_len + 
7)/8;
+                       LOGT(t, LOGL_DEBUG, "Parsing OPT Prefix info 
(prefix_len=%u): %s\n",
+                            opt_prefix->prefix_len,
+                            osmo_hexdump((const unsigned char 
*)opt_prefix->prefix, prefix_len_bytes));
+
+                       memcpy(&t->user_addr_ipv6_prefix.u.sin6.sin6_addr,
+                              opt_prefix->prefix,
+                              prefix_len_bytes);
+                       memset(&((uint8_t 
*)&t->user_addr_ipv6_prefix.u.sin6.sin6_addr)[prefix_len_bytes],
+                            0, 16 - prefix_len_bytes);
+
+                       /* Pick second address in the prefix: */
+                       memcpy(&t->user_addr.u.sin6.sin6_addr,
+                              &t->user_addr_ipv6_prefix.u.sin6.sin6_addr,
+                              
sizeof(t->user_addr_ipv6_prefix.u.sin6.sin6_addr));
+                       ((uint8_t *)&t->user_addr.u.sin6.sin6_addr)[15] = 2;
+
+                       LOGT(t, LOGL_INFO, "Adding global IPv6 prefix %s/%u 
address %s\n",
+                            inet_ntop(AF_INET6, 
&t->user_addr_ipv6_prefix.u.sin6.sin6_addr, &ip6strbuf[0][0], 
sizeof(ip6strbuf[0])),
+                            opt_prefix->prefix_len,
+                            inet_ntop(AF_INET6, 
&t->user_addr.u.sin6.sin6_addr, &ip6strbuf[1][0], sizeof(ip6strbuf[1])));
+
+                       if ((rc = osmo_netdev_add_addr(t->tun_dev->netdev, 
&t->user_addr, 64)) < 0) {
+                               LOGT(t, LOGL_ERROR, "Cannot add global IPv6 
user addr %s to tun device: %s\n",
+                                    inet_ntop(AF_INET6, 
&t->user_addr.u.sin6.sin6_addr, &ip6strbuf[1][0], sizeof(ip6strbuf[1])),
+                                    strerror(-rc));
+                       }
+               }
+       }
+}
+
 static void handle_gtp1u(struct gtp_endpoint *ep, const uint8_t *buffer, 
unsigned int nread)
 {
        struct gtp_daemon *d = ep->d;
@@ -50,6 +100,7 @@
        int rc, outfd;
        uint32_t teid;
        uint16_t gtp_len;
+       char ip6strbuf[200];

        if (nread < sizeof(*gtph)) {
                LOGEP_NC(ep, LOGL_NOTICE, "Short read: %u < %lu\n", nread, 
sizeof(*gtph));
@@ -114,6 +165,54 @@
                return;
        }
        outfd = t->tun_dev->fd;
+
+       struct iphdr *iph = (struct iphdr *)payload;
+       struct ip6_hdr *ip6h;
+       struct osmo_icmpv6_radv_hdr *ra;
+       switch (iph->version) {
+       case 4:
+               if (t->user_addr.u.sa.sa_family != AF_INET) {
+                       LOGT(t, LOGL_NOTICE, "Rx GTPU payload for unexpected 
IPv4 %s in non-IPv4 PDP Context\n",
+                               inet_ntop(AF_INET, &iph->daddr, ip6strbuf, 
sizeof(ip6strbuf)));
+                       goto unlock_ret;
+               }
+               if (memcmp(&iph->daddr, &t->user_addr.u.sin.sin_addr, 4) != 0) {
+                       LOGT(t, LOGL_NOTICE, "Rx GTPU payload for unknown dst 
IP addr %s\n",
+                               inet_ntop(AF_INET, &iph->daddr, ip6strbuf, 
sizeof(ip6strbuf)));
+                       goto unlock_ret;
+               }
+               break;
+       case 6:
+               ip6h = (struct ip6_hdr *)payload;
+               if (t->user_addr.u.sa.sa_family != AF_INET6) {
+                       LOGT(t, LOGL_NOTICE, "Rx GTPU payload for unexpected 
IPv6 %s in non-IPv6 PDP Context\n",
+                               inet_ntop(AF_INET6, &ip6h->ip6_dst, ip6strbuf, 
sizeof(ip6strbuf)));
+                       goto unlock_ret;
+               }
+               if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) {
+                       if (memcmp(&ip6h->ip6_dst, 
&t->user_addr_ipv6_ll.u.sin6.sin6_addr, 16) != 0) {
+                               LOGT(t, LOGL_NOTICE, "Rx GTPU payload for 
unknown link-local dst IP addr %s\n",
+                                    inet_ntop(AF_INET6, &ip6h->ip6_dst, 
ip6strbuf, sizeof(ip6strbuf)));
+                               goto unlock_ret;
+                       }
+                       if ((ra = osmo_icmpv6_validate_router_adv(payload, 
gtp_len))) {
+                               size_t ra_len = (uint8_t *)ra - (uint8_t 
*)payload;
+                               handle_router_adv(t, (struct ip6_hdr *)payload, 
ra, ra_len);
+                               goto unlock_ret;
+                       }
+               /* Match by global IPv6 /64 prefix allocated through SLAAC: */
+               } else if (memcmp(&ip6h->ip6_dst, 
&t->user_addr.u.sin6.sin6_addr, 8) != 0) {
+                       LOGT(t, LOGL_NOTICE, "Rx GTPU payload for unknown 
global dst IP addr %s\n",
+                            inet_ntop(AF_INET6, &ip6h->ip6_dst, ip6strbuf, 
sizeof(ip6strbuf)));
+                       goto unlock_ret;
+               }
+               break;
+       default:
+               LOGT(t, LOGL_NOTICE, "Rx GTPU payload with unknown IP version 
%d\n", iph->version);
+               goto unlock_ret;
+       }
+
+       outfd = t->tun_dev->fd;
        pthread_rwlock_unlock(&d->rwlock);

        /* 3) write to TUN device */
@@ -122,6 +221,11 @@
                LOGEP_NC(ep, LOGL_FATAL, "Error writing to tun device %s\n", 
strerror(errno));
                exit(1);
        }
+       return;
+
+unlock_ret:
+       pthread_rwlock_unlock(&d->rwlock);
+       return;
 }

 /* One thread for reading from each GTP/UDP socket (GTP decapsulation -> tun)
diff --git a/daemon/gtp_tunnel.c b/daemon/gtp_tunnel.c
index 4c6b2c5..2765488 100644
--- a/daemon/gtp_tunnel.c
+++ b/daemon/gtp_tunnel.c
@@ -14,10 +14,9 @@
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/logging.h>

-#include "internal.h"
+#include <osmocom/netif/icmpv6.h>

-#define LOGT(t, lvl, fmt, args ...) \
-       LOGP(DGT, lvl, "%s: " fmt, (t)->name, ## args)
+#include "internal.h"

 /***********************************************************************
  * GTP Tunnel
@@ -58,7 +57,8 @@
        t->rx_teid = cpars->rx_teid;
        t->tx_teid = cpars->tx_teid;
        memcpy(&t->exthdr, &cpars->exthdr, sizeof(t->exthdr));
-       memcpy(&t->user_addr, &cpars->user_addr, sizeof(t->user_addr));
+       memcpy(&t->user_addr_ipv6_ll, &cpars->user_addr, sizeof(t->user_addr));
+       memcpy(&t->user_addr, &t->user_addr_ipv6_ll, 
sizeof(t->user_addr_ipv6_ll));
        memcpy(&t->remote_udp, &cpars->remote_udp, sizeof(t->remote_udp));

        if ((rc = osmo_netdev_add_addr(t->tun_dev->netdev, &t->user_addr, 32)) 
< 0) {
@@ -173,3 +173,40 @@

        return rc;
 }
+
+/* Called with d->rwlock locked, tx_gtp1u_pk() will unlock. */
+static int _gtp_tunnel_tx_icmpv6_rs(struct gtp_tunnel *t)
+{
+       struct msgb *msg;
+       int rc;
+
+       OSMO_ASSERT(t->user_addr.u.sa.sa_family == AF_INET6);
+
+       msg = osmo_icmpv6_construct_rs(&t->user_addr.u.sin6.sin6_addr);
+
+       pthread_rwlock_rdlock(&t->d->rwlock);
+       rc = tx_gtp1u_pkt(t, msg->head, msgb_data(msg), msgb_length(msg));
+       /* pthread_rwlock_unlock() was called inside tx_gtp1u_pkt(). */
+       if (rc < 0)
+               LOGT(t, LOGL_FATAL, "Error Writing to UDP socket: %s\n", 
strerror(errno));
+       msgb_free(msg);
+       return 0;
+}
+
+int gtp_tunnel_tx_icmpv6_rs(struct gtp_daemon *d, const struct osmo_sockaddr 
*bind_addr, uint32_t rx_teid)
+{
+       struct gtp_endpoint *ep;
+
+       pthread_rwlock_wrlock(&d->rwlock);
+       ep = _gtp_endpoint_find(d, bind_addr);
+       if (ep) {
+               /* find tunnel for rx TEID within endpoint */
+               struct gtp_tunnel *t = _gtp_tunnel_find_r(d, rx_teid, ep);
+               if (t) {
+                       return _gtp_tunnel_tx_icmpv6_rs(t);
+                       /* pthread_rwlock_unlock() was called inside 
_gtp_tunnel_tx_icmpv6_rs()->tx_gtp1u_pkt(). */
+               }
+       }
+       pthread_rwlock_unlock(&d->rwlock);
+       return -ENOENT;
+}
diff --git a/daemon/internal.h b/daemon/internal.h
index ae2356b..2394e2c 100644
--- a/daemon/internal.h
+++ b/daemon/internal.h
@@ -14,6 +14,7 @@
 #include <osmocom/core/utils.h>
 #include <osmocom/core/socket.h>
 #include <osmocom/core/netdev.h>
+#include <osmocom/core/logging.h>

 #include <osmocom/netif/stream.h>

@@ -35,6 +36,7 @@
        DEP,
        DGT,
        DUECUPS,
+       DICMP6,
 };


@@ -43,6 +45,7 @@
  ***********************************************************************/

 struct gtp_daemon;
+struct gtp_tunnel;

 /* local UDP socket for GTP communication */
 struct gtp_endpoint {
@@ -199,6 +202,8 @@
        uint32_t rx_teid;

        /* End user Address (inner IP) */
+       struct osmo_sockaddr user_addr_ipv6_ll;
+       struct osmo_sockaddr user_addr_ipv6_prefix;
        struct osmo_sockaddr user_addr;

        /* Remote UDP IP/Port*/
@@ -241,7 +246,12 @@

 void _gtp_tunnel_destroy(struct gtp_tunnel *t);
 bool gtp_tunnel_destroy(struct gtp_daemon *d, const struct osmo_sockaddr 
*bind_addr, uint32_t rx_teid);
+int gtp_tunnel_tx_icmpv6_rs(struct gtp_daemon *d, const struct osmo_sockaddr 
*bind_addr, uint32_t rx_teid);

+int tx_gtp1u_pkt(struct gtp_tunnel *t, uint8_t *base_buffer, const uint8_t 
*payload, unsigned int payload_len);
+
+#define LOGT(t, lvl, fmt, args ...) \
+       LOGP(DGT, lvl, "%s: " fmt, (t)->name, ## args)

 /***********************************************************************
  * GTP Daemon
diff --git a/daemon/main.c b/daemon/main.c
index b00e5d3..c604f02 100644
--- a/daemon/main.c
+++ b/daemon/main.c
@@ -100,7 +100,11 @@
                .description = "UE Control User Plane Separation",
                .enabled = 1, .loglevel = LOGL_DEBUG,
        },
-
+       [DICMP6] = {
+               .name = "DICMP6",
+               .description = "ICMPv6 (SLAAC) PDU Session setup",
+               .enabled = 1, .loglevel = LOGL_DEBUG,
+       },
 };

 static const struct log_info log_info = {
diff --git a/daemon/tun_device.c b/daemon/tun_device.c
index c12c694..1178c7a 100644
--- a/daemon/tun_device.c
+++ b/daemon/tun_device.c
@@ -139,7 +139,7 @@
 }

 /* Note: This function is called with d->rwlock locked, and it's responsible 
of unlocking it before returning. */
-static int tx_gtp1u_pkt(struct gtp_tunnel *t, uint8_t *base_buffer, const 
uint8_t *payload, unsigned int payload_len)
+int tx_gtp1u_pkt(struct gtp_tunnel *t, uint8_t *base_buffer, const uint8_t 
*payload, unsigned int payload_len)
 {
        struct gtp1_header *gtph;
        unsigned int head_len = payload - base_buffer;

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

Gerrit-MessageType: merged
Gerrit-Project: osmo-uecups
Gerrit-Branch: master
Gerrit-Change-Id: I837a4d7ec7c134412ab4a2e09909670e81ecbeea
Gerrit-Change-Number: 42456
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>

Reply via email to