pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-uecups/+/42470?usp=email )
Change subject: Support configuring IPv4v6 tunnels ......................................................................
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,