pespin submitted this change.
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(-)
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 change 42456. To unsubscribe, or for help writing mail filters, visit settings.