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;