pespin has uploaded this change for review.

View Change

Support configuring IPv4v6 tunnels

Change-Id: Ic2da7a761a8df7e006fc02ca6557a48f371e4151
---
M daemon/cups_client.c
M daemon/daemon_vty.c
M daemon/gtp_endpoint.c
M daemon/gtp_tunnel.c
M daemon/internal.h
M ttcn3/UECUPS_Types.ttcn
6 files changed, 138 insertions(+), 51 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-uecups refs/changes/70/42470/1
diff --git a/daemon/cups_client.c b/daemon/cups_client.c
index f7c6f1a..a96861f 100644
--- a/daemon/cups_client.c
+++ b/daemon/cups_client.c
@@ -198,9 +198,9 @@
json_object_set_new(jslaac_ind, "ipv6_prefix", json_string(ipv6_str));

osmo_hexdump_buf(ipv6_str, sizeof(ipv6_str),
- (const unsigned char *)&t->user_addr.u.sin6.sin6_addr,
- sizeof(t->user_addr.u.sin6.sin6_addr),
- "", true);
+ (const unsigned char *)&t->user_addr_ipv6_global.u.sin6.sin6_addr,
+ sizeof(t->user_addr_ipv6_global.u.sin6.sin6_addr),
+ "", true);
json_object_set_new(jslaac_ind, "ipv6_user_addr", json_string(ipv6_str));

json_str = json_dumps(jtx, JSON_SORT_KEYS);
@@ -265,21 +265,26 @@
return 0;
}

-static int parse_eua(struct osmo_sockaddr *out, json_t *jip, json_t *jaddr_type)
+static int parse_eua(enum gtp1u_eua_type *user_addr_type, struct osmo_sockaddr *out4,
+ struct osmo_sockaddr *out6, json_t *jip, json_t *jaddr_type)
{
const char *addr_type, *ip;
- uint8_t buf[16];
-
+ uint8_t buf[20];
+ struct sockaddr_in *sin = &out4->u.sin;
+ struct sockaddr_in6 *sin6 = &out6->u.sin6;
if (!json_is_string(jip) || !json_is_string(jaddr_type))
return -EINVAL;

addr_type = json_string_value(jaddr_type);
ip = json_string_value(jip);

- memset(out, 0, sizeof(*out));
+ memset(out4, 0, sizeof(*out4));
+ out4->u.sa.sa_family = AF_UNSPEC;
+ memset(out6, 0, sizeof(*out6));
+ out6->u.sa.sa_family = AF_UNSPEC;

if (!strcmp(addr_type, "IPV4")) {
- struct sockaddr_in *sin = &out->u.sin;
+ *user_addr_type = GTP1U_EUA_TYPE_IPv4;
if (osmo_hexparse(ip, buf, sizeof(buf)) != 4) {
LOGP(DUECUPS, LOGL_NOTICE, "Failed parsing EUA %s type %s\n", ip, addr_type);
return -EINVAL;
@@ -287,13 +292,23 @@
memcpy(&sin->sin_addr, buf, 4);
sin->sin_family = AF_INET;
} else if (!strcmp(addr_type, "IPV6")) {
- struct sockaddr_in6 *sin6 = &out->u.sin6;
+ *user_addr_type = GTP1U_EUA_TYPE_IPv6;
if (osmo_hexparse(ip, buf, sizeof(buf)) != 16) {
LOGP(DUECUPS, LOGL_NOTICE, "Failed parsing EUA %s type %s\n", ip, addr_type);
return -EINVAL;
}
memcpy(&sin6->sin6_addr, buf, 16);
sin6->sin6_family = AF_INET6;
+ } else if (!strcmp(addr_type, "IPV4V6")) {
+ *user_addr_type = GTP1U_EUA_TYPE_IPv4v6;
+ if (osmo_hexparse(ip, buf, sizeof(buf)) != 20) {
+ LOGP(DUECUPS, LOGL_NOTICE, "Failed parsing EUA %s type %s\n", ip, addr_type);
+ return -EINVAL;
+ }
+ memcpy(&sin->sin_addr, buf, 4);
+ sin->sin_family = AF_INET;
+ memcpy(&sin6->sin6_addr, buf + 4, 16);
+ sin6->sin6_family = AF_INET6;
} else {
LOGP(DUECUPS, LOGL_NOTICE, "Unknown EUA type %s\n", addr_type);
return -EINVAL;
@@ -395,7 +410,8 @@
rc = parse_ep(&out->remote_udp, jremote_gtp_ep);
if (rc < 0)
return rc;
- rc = parse_eua(&out->user_addr, juser_addr, juser_addr_type);
+ rc = parse_eua(&out->user_addr_type, &out->user_addr_ipv4,
+ &out->user_addr_ipv6, juser_addr, juser_addr_type);
if (rc < 0)
return rc;
out->rx_teid = json_integer_value(jrx_teid);
diff --git a/daemon/daemon_vty.c b/daemon/daemon_vty.c
index 16f23b8..e2562c9 100644
--- a/daemon/daemon_vty.c
+++ b/daemon/daemon_vty.c
@@ -230,20 +230,34 @@

static void show_one_tunnel(struct vty *vty, const struct gtp_tunnel *t)
{
- char remote_ip[64], remote_port[16], user_addr[64];
+ char remote_ip[64], remote_port[16], user_addr_v4[64], user_addr_v6[64];

getnameinfo(&t->remote_udp.u.sa, sizeof(t->remote_udp.u.sas),
remote_ip, sizeof(remote_ip), remote_port, sizeof(remote_port),
NI_NUMERICHOST|NI_NUMERICSERV);

- getnameinfo(&t->user_addr.u.sa, sizeof(t->user_addr.u.sas),
- user_addr, sizeof(user_addr), NULL, 0,
- NI_NUMERICHOST|NI_NUMERICSERV);
+ if (t->user_addr_type == GTP1U_EUA_TYPE_IPv4 || t->user_addr_type == GTP1U_EUA_TYPE_IPv4v6) {
+ getnameinfo(&t->user_addr_ipv4.u.sa, sizeof(t->user_addr_ipv4.u.sas),
+ user_addr_v4, sizeof(user_addr_v4), NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ } else {
+ user_addr_v4[0] = '\0';
+ }
+ if ((t->user_addr_type == GTP1U_EUA_TYPE_IPv6 || t->user_addr_type == GTP1U_EUA_TYPE_IPv4v6) &&
+ t->user_addr_ipv6_global.u.sa.sa_family == AF_INET6) {
+ getnameinfo(&t->user_addr_ipv6_global.u.sa, sizeof(t->user_addr_ipv6_global.u.sas),
+ user_addr_v6, sizeof(user_addr_v6), NULL, 0,
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ } else {
+ user_addr_v6[0] = '\0';
+ }

-
- vty_out(vty, "%s/%08X - %s:%s/%08X %s(%s) %s%s",
- t->gtp_ep->name, t->rx_teid, remote_ip, remote_port, t->tx_teid,
- t->tun_dev->devname, t->tun_dev->netns_name, user_addr, VTY_NEWLINE);
+ vty_out(vty, "%s/%08X - %s:%s/%08X %s(%s) %s%s%s%s",
+ t->gtp_ep->name, t->rx_teid,
+ remote_ip, remote_port, t->tx_teid,
+ t->tun_dev->devname, t->tun_dev->netns_name, user_addr_v4,
+ (user_addr_v4[0] != '\0' && user_addr_v6[0] != '\0') ? " " : "",
+ user_addr_v6, VTY_NEWLINE);
}

DEFUN(show_tunnel, show_tunnel_cmd,
diff --git a/daemon/gtp_endpoint.c b/daemon/gtp_endpoint.c
index 0332f91..524226c 100644
--- a/daemon/gtp_endpoint.c
+++ b/daemon/gtp_endpoint.c
@@ -72,19 +72,20 @@
0, 16 - prefix_len_bytes);

/* Pick second address in the prefix: */
- memcpy(&t->user_addr.u.sin6.sin6_addr,
+ t->user_addr_ipv6_global.u.sa.sa_family = AF_INET6;
+ memcpy(&t->user_addr_ipv6_global.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;
+ ((uint8_t *)&t->user_addr_ipv6_global.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])));
+ inet_ntop(AF_INET6, &t->user_addr_ipv6_global.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) {
+ if ((rc = osmo_netdev_add_addr(t->tun_dev->netdev, &t->user_addr_ipv6_global, 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])),
+ inet_ntop(AF_INET6, &t->user_addr_ipv6_global.u.sin6.sin6_addr, &ip6strbuf[1][0], sizeof(ip6strbuf[1])),
strerror(-rc));
}

@@ -174,12 +175,12 @@
struct osmo_icmpv6_radv_hdr *ra;
switch (iph->version) {
case 4:
- if (t->user_addr.u.sa.sa_family != AF_INET) {
+ if (t->user_addr_ipv4.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) {
+ if (memcmp(&iph->daddr, &t->user_addr_ipv4.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;
@@ -187,12 +188,12 @@
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 (t->user_addr_ipv6_ll.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 (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)));
@@ -203,11 +204,18 @@
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;
+ } else {
+ /* Match by global IPv6 /64 prefix allocated through SLAAC: */
+ if (t->user_addr_ipv6_global.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 (memcmp(&ip6h->ip6_dst, &t->user_addr_ipv6_prefix.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:
diff --git a/daemon/gtp_tunnel.c b/daemon/gtp_tunnel.c
index 2765488..fc99e4f 100644
--- a/daemon/gtp_tunnel.c
+++ b/daemon/gtp_tunnel.c
@@ -57,15 +57,28 @@
t->rx_teid = cpars->rx_teid;
t->tx_teid = cpars->tx_teid;
memcpy(&t->exthdr, &cpars->exthdr, sizeof(t->exthdr));
- 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) {
- LOGT(t, LOGL_ERROR, "Cannot add user addr to tun device: %s\n",
- strerror(-rc));
+ if (cpars->user_addr_type == GTP1U_EUA_TYPE_IPv4 || cpars->user_addr_type == GTP1U_EUA_TYPE_IPv4v6) {
+ memcpy(&t->user_addr_ipv4, &cpars->user_addr_ipv4, sizeof(t->user_addr_ipv4));
+ if ((rc = osmo_netdev_add_addr(t->tun_dev->netdev, &t->user_addr_ipv4, 32)) < 0) {
+ LOGT(t, LOGL_ERROR, "Cannot add user addr to tun device: %s\n",
+ strerror(-rc));
+ }
+ } else {
+ t->user_addr_ipv4.u.sa.sa_family = AF_UNSPEC;
}

+ t->user_addr_type = cpars->user_addr_type;
+ if (cpars->user_addr_type == GTP1U_EUA_TYPE_IPv6 || cpars->user_addr_type == GTP1U_EUA_TYPE_IPv4v6)
+ memcpy(&t->user_addr_ipv6_ll, &cpars->user_addr_ipv6, sizeof(t->user_addr_ipv6_ll));
+ else
+ t->user_addr_ipv6_ll.u.sa.sa_family = AF_UNSPEC;
+
+ /* user_addr_ipv6_global will be set later on during IPv6 SLAAC procedure: */
+ t->user_addr_ipv6_global.u.sa.sa_family = AF_UNSPEC;
+
+ memcpy(&t->remote_udp, &cpars->remote_udp, sizeof(t->remote_udp));
+
/* TODO: hash table? */
llist_add_tail(&t->list, &d->gtp_tunnels);
pthread_rwlock_unlock(&d->rwlock);
@@ -125,9 +138,23 @@

llist_for_each_entry(t, &d->gtp_tunnels, list) {
/* TODO: Find best matching filter */
- if (t->tun_dev == tun &&
- osmo_sockaddr_cmp(osa, &t->user_addr) == 0)
+ if (t->tun_dev != tun)
+ continue;
+ switch (osa->u.sa.sa_family) {
+ case AF_INET:
+ if (t->user_addr_type == GTP1U_EUA_TYPE_IPv6)
+ continue;
+ if (osmo_sockaddr_cmp(osa, &t->user_addr_ipv4) != 0)
+ continue;
return t;
+ case AF_INET6:
+ if (t->user_addr_type == GTP1U_EUA_TYPE_IPv4)
+ continue;
+ if (osmo_sockaddr_cmp(osa, &t->user_addr_ipv6_ll) != 0 &&
+ osmo_sockaddr_cmp(osa, &t->user_addr_ipv6_global) != 0)
+ continue;
+ return t;
+ }
}
return NULL;
}
@@ -141,8 +168,18 @@
/* talloc is not thread safe, all alloc/free must come from main thread */
ASSERT_MAIN_THREAD(t->d);

- if ((rc = osmo_netdev_del_addr(t->tun_dev->netdev, &t->user_addr, 32)) < 0)
- LOGT(t, LOGL_ERROR, "Cannot remove user address: %s\n", strerror(-rc));
+ if (t->user_addr_type == GTP1U_EUA_TYPE_IPv4 || t->user_addr_type == GTP1U_EUA_TYPE_IPv4v6) {
+ if ((rc = osmo_netdev_del_addr(t->tun_dev->netdev, &t->user_addr_ipv4, 32)) < 0)
+ LOGT(t, LOGL_ERROR, "Cannot remove IPv4 user address: %s\n", strerror(-rc));
+ }
+ if (t->user_addr_type == GTP1U_EUA_TYPE_IPv6 || t->user_addr_type == GTP1U_EUA_TYPE_IPv4v6) {
+ if ((rc = osmo_netdev_del_addr(t->tun_dev->netdev, &t->user_addr_ipv6_ll, 32)) < 0)
+ LOGT(t, LOGL_ERROR, "Cannot remove IPv6 link-local user address: %s\n", strerror(-rc));
+ if (t->user_addr_ipv6_global.u.sa.sa_family != AF_UNSPEC) {
+ if ((rc = osmo_netdev_del_addr(t->tun_dev->netdev, &t->user_addr_ipv6_global, 32)) < 0)
+ LOGT(t, LOGL_ERROR, "Cannot remove IPv6 global user address: %s\n", strerror(-rc));
+ }
+ }

llist_del(&t->list);

@@ -180,9 +217,10 @@
struct msgb *msg;
int rc;

- OSMO_ASSERT(t->user_addr.u.sa.sa_family == AF_INET6);
+ OSMO_ASSERT(t->user_addr_type == GTP1U_EUA_TYPE_IPv6 ||
+ t->user_addr_type == GTP1U_EUA_TYPE_IPv4v6);

- msg = osmo_icmpv6_construct_rs(&t->user_addr.u.sin6.sin6_addr);
+ msg = osmo_icmpv6_construct_rs(&t->user_addr_ipv6_ll.u.sin6.sin6_addr);

pthread_rwlock_rdlock(&t->d->rwlock);
rc = tx_gtp1u_pkt(t, msg->head, msgb_data(msg), msgb_length(msg));
diff --git a/daemon/internal.h b/daemon/internal.h
index 5d4b832..d9e040c 100644
--- a/daemon/internal.h
+++ b/daemon/internal.h
@@ -172,6 +172,12 @@
* this is what happens when IP arrives on the tun device
*/

+enum gtp1u_eua_type {
+ GTP1U_EUA_TYPE_IPv4,
+ GTP1U_EUA_TYPE_IPv6,
+ GTP1U_EUA_TYPE_IPv4v6,
+};
+
struct gtp1u_exthdr_pdu_sess_container {
bool enabled;
uint8_t pdu_type; /* GTP1_EXTHDR_PDU_TYPE_* */
@@ -203,9 +209,11 @@
uint32_t rx_teid;

/* End user Address (inner IP) */
+ enum gtp1u_eua_type user_addr_type;
+ struct osmo_sockaddr user_addr_ipv4;
struct osmo_sockaddr user_addr_ipv6_ll;
struct osmo_sockaddr user_addr_ipv6_prefix;
- struct osmo_sockaddr user_addr;
+ struct osmo_sockaddr user_addr_ipv6_global;

/* Remote UDP IP/Port*/
struct osmo_sockaddr remote_udp;
@@ -231,7 +239,9 @@
struct gtp1u_exthdrs exthdr;

/* end user address */
- struct osmo_sockaddr user_addr;
+ enum gtp1u_eua_type user_addr_type;
+ struct osmo_sockaddr user_addr_ipv4;
+ struct osmo_sockaddr user_addr_ipv6;

/* remote GTP/UDP IP+Port */
struct osmo_sockaddr remote_udp;
diff --git a/ttcn3/UECUPS_Types.ttcn b/ttcn3/UECUPS_Types.ttcn
index 45466c8..ba4e7d4 100644
--- a/ttcn3/UECUPS_Types.ttcn
+++ b/ttcn3/UECUPS_Types.ttcn
@@ -7,7 +7,8 @@

type enumerated UECUPS_AddrType {
IPV4 (1),
- IPV6 (2)
+ IPV6 (2),
+ IPV4V6 (3)
};

type enumerated UECUPS_Result {
@@ -43,7 +44,7 @@

/* user address (allocated inside the tunnel) */
UECUPS_AddrType user_addr_type,
- OCT4_16n user_addr,
+ OCT4_20n user_addr,

/* GTP endpoint (UDP IP/Port tuples) */
UECUPS_SockAddr local_gtp_ep,

To view, visit change 42470. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-uecups
Gerrit-Branch: master
Gerrit-Change-Id: Ic2da7a761a8df7e006fc02ca6557a48f371e4151
Gerrit-Change-Number: 42470
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>