[PATCH] osmo-ggsn[master]: Add support for IPv4v6 User End Addresses

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Pau Espin Pedrol gerrit-no-reply at lists.osmocom.org
Wed Dec 6 19:19:10 UTC 2017


Review at  https://gerrit.osmocom.org/5216

Add support for IPv4v6 User End Addresses

Before this commit, when an MS requested an ipv4v6 context osmo-ggsn
returned an error stating the type was unknown, and this text was
printed in the log:
Processing create PDP context request for APN 'ims'
Cannot decode EUA from MS/SGSN: f1 8d

This patch has been tested with an MS running the 3 types of addresses:
- IPv4 and IPv6: no regressions observed, the context is activated and
packets are sent to the ggsn.
- IPv4v6: Wireshark correctly parses request and reponse, and then
ICMPv6 traffic from both sides. Finally I see the MS using the IPv4 and
IPv6 DNS addresses advertised and TCP traffic over IPv4 (because
probably my IPv6 network setup is not correct). I also checked I can
disable/enable data (pdp ctx delete and activate) several times without
any issue.

Change-Id: Ic820759167fd3bdf329cb11d4b942e903fe50af5
---
M ggsn/ggsn.c
M ggsn/icmpv6.c
M gtp/pdp.h
M lib/in46_addr.c
M lib/in46_addr.h
M sgsnemu/sgsnemu.c
M tests/lib/in46a_test.c
M tests/lib/in46a_v6_test.ok
8 files changed, 218 insertions(+), 78 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/16/5216/1

diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index 1c1276f..eba7c2a 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -335,17 +335,23 @@
 static int delete_context(struct pdp_t *pdp)
 {
 	struct gsn_t *gsn = pdp->gsn;
-	struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
 	struct apn_ctx *apn = pdp->priv;
+	struct ippoolm_t *member;
 
 	LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
-	struct ippoolm_t *member = pdp->peer;
 
-	if (pdp->peer) {
+	if (pdp->peer[0]) {
+		member = pdp->peer[0];
 		send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
-		ippool_freeip(ipp->pool, ipp);
+		ippool_freeip(member->pool, member);
 	} else
 		LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
+
+	if (pdp->peer[1]) { /* ipv4v6 */
+		member = pdp->peer[1];
+		send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
+		ippool_freeip(member->pool, member);
+	}
 
 	if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
 		LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
@@ -512,10 +518,10 @@
 	static char name_buf[256];
 	struct gsn_t *gsn = pdp->gsn;
 	struct ggsn_ctx *ggsn = gsn->priv;
-	struct in46_addr addr;
-	struct ippoolm_t *member;
+	struct in46_addr addr[2];
+	struct ippoolm_t *member = NULL;
 	struct apn_ctx *apn;
-	int rc;
+	int rc, num_addr, i;
 
 	osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
 
@@ -550,55 +556,63 @@
 	memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l);	/* TODO */
 	pdp->qos_neg.l = pdp->qos_req.l;
 
-	if (in46a_from_eua(&pdp->eua, &addr)) {
+	memset(addr, 0, sizeof(addr[0])*2);
+	if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
 		LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
 			osmo_hexdump(pdp->eua.v, pdp->eua.l));
 		gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
 		return 0;
 	}
 
-	if (addr.len == sizeof(struct in_addr)) {
-		/* does this APN actually have an IPv4 pool? */
-		if (!apn_supports_ipv4(apn))
-			goto err_wrong_af;
+	/* Allocate dynamic addresses from the pool */
+	for (i = 0; i < num_addr; i++) {
+		if (addr[i].len == sizeof(struct in_addr)) {
+			/* does this APN actually have an IPv4 pool? */
+			if (!apn_supports_ipv4(apn))
+				goto err_wrong_af;
 
-		rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
-		if (rc < 0)
-			goto err_pool_full;
-		in46a_to_eua(&member->addr, &pdp->eua);
+			rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
+			if (rc < 0)
+				goto err_pool_full;
+			/* copy back */
+			memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
 
+		} else if (addr[i].len == sizeof(struct in6_addr)) {
+
+			/* does this APN actually have an IPv6 pool? */
+			if (!apn_supports_ipv6(apn))
+				goto err_wrong_af;
+
+			rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
+			if (rc < 0)
+				goto err_pool_full;
+
+			/* IPv6 doesn't really send the real/allocated address at this point, but just
+			 * the link-identifier which the MS shall use for router solicitation */
+			/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
+			memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
+			/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
+			memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
+		} else
+			OSMO_ASSERT(0);
+
+		pdp->peer[i] = member;
+		member->peer = pdp;
+	}
+
+	in46a_to_eua(addr, num_addr, &pdp->eua);
+
+	if (apn_supports_ipv4(apn)) {
 		/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
 		if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
 			LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
 			gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
 			return 0;
 		}
-	} else if (addr.len == sizeof(struct in6_addr)) {
-		struct in46_addr tmp;
+	}
 
-		/* does this APN actually have an IPv6 pool? */
-		if (!apn_supports_ipv6(apn))
-			goto err_wrong_af;
-
-		rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
-		if (rc < 0)
-			goto err_pool_full;
-
-		/* IPv6 doesn't really send the real/allocated address at this point, but just
-		 * the link-identifier which the MS shall use for router solicitation */
-		tmp.len = addr.len;
-		/* initialize upper 64 bits to prefix, they are discarded by MS anyway */
-		memcpy(tmp.v6.s6_addr, &member->addr.v6, 8);
-		/* use allocated 64bit prefix as lower 64bit, used as link id by MS */
-		memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
-		in46a_to_eua(&tmp, &pdp->eua);
-	} else
-		OSMO_ASSERT(0);
-
-	pdp->peer = member;
 	pdp->ipif = apn->tun.tun;	/* TODO */
 	pdp->priv = apn;
-	member->peer = pdp;
 
 	if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
 		gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
diff --git a/ggsn/icmpv6.c b/ggsn/icmpv6.c
index 11ced24..6564a54 100644
--- a/ggsn/icmpv6.c
+++ b/ggsn/icmpv6.c
@@ -183,12 +183,17 @@
 int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
 			const uint8_t *pack, unsigned len)
 {
-	struct ippoolm_t *member = pdp->peer;
+	struct ippoolm_t *member;
 	const struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
 	const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
 	struct msgb *msg;
 
 	OSMO_ASSERT(pdp);
+
+	member = pdp->peer[0];
+	OSMO_ASSERT(member);
+	if (member->addr.len == sizeof(struct in_addr)) /* ipv4v6 context */
+		member = pdp->peer[1];
 	OSMO_ASSERT(member);
 
 	if (len < sizeof(*ip6h)) {
diff --git a/gtp/pdp.h b/gtp/pdp.h
index f1d8ad6..b581952 100644
--- a/gtp/pdp.h
+++ b/gtp/pdp.h
@@ -26,6 +26,7 @@
 #define PDP_EUA_ORG_IETF	0xF1
 #define PDP_EUA_TYPE_v4		0x21
 #define PDP_EUA_TYPE_v6		0x57
+#define PDP_EUA_TYPE_v4v6	0x8D
 
 /* GTP Information elements from 29.060 v3.9.0 7.7 Information Elements */
 /* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
@@ -121,7 +122,7 @@
 	/* Parameters shared by all PDP context belonging to the same MS */
 
 	void *ipif;		/* IP network interface */
-	void *peer;		/* Pointer to peer protocol */
+	void *peer[2];		/* Pointer to peer protocol */
 	void *asap;		/* Application specific service access point */
 
 	uint64_t imsi;		/* International Mobile Subscriber Identity. */
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
index 36ad6af..49431c2 100644
--- a/lib/in46_addr.c
+++ b/lib/in46_addr.c
@@ -253,33 +253,54 @@
 	}
 }
 
-/*! Convert given PDP End User Address to in46_addr
+/*! Convert given array of in46_addr to PDP End User Address
+ *  \param[in] dst Array containing 1 or 2 in46_addr
+ *  \param[out] eua End User Address structure to fill
  *  \returns 0 on success; negative on error */
-int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
+int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua)
 {
-	switch (src->len) {
-	case 4:
-		eua->l = 6;
-		eua->v[0] = PDP_EUA_ORG_IETF;
-		eua->v[1] = PDP_EUA_TYPE_v4;
-		memcpy(&eua->v[2], &src->v4, 4);	/* Copy a 4 byte address */
-		break;
-	case 8:
-	case 16:
-		eua->l = 18;
-		eua->v[0] = PDP_EUA_ORG_IETF;
-		eua->v[1] = PDP_EUA_TYPE_v6;
-		memcpy(&eua->v[2], &src->v6, 16);	/* Copy a 16 byte address */
-		break;
-	default:
-		OSMO_ASSERT(0);
-		return -1;
+	const struct in46_addr *src_v4, *src_v6;
+	if (size == 1) {
+		switch (src->len) {
+		case 4:
+			eua->l = 6;
+			eua->v[0] = PDP_EUA_ORG_IETF;
+			eua->v[1] = PDP_EUA_TYPE_v4;
+			memcpy(&eua->v[2], &src->v4, 4);	/* Copy a 4 byte address */
+			break;
+		case 8:
+		case 16:
+			eua->l = 18;
+			eua->v[0] = PDP_EUA_ORG_IETF;
+			eua->v[1] = PDP_EUA_TYPE_v6;
+			memcpy(&eua->v[2], &src->v6, 16);	/* Copy a 16 byte address */
+			break;
+		default:
+			OSMO_ASSERT(0);
+			return -1;
+		}
+		return 0;
 	}
+
+	if (src[0].len == src[1].len)
+		return -1; /* we should have a v4 and a v6 address */
+
+	src_v4 = (src[0].len == 4) ? &src[0] : &src[1];
+	src_v6 = (src[0].len == 4) ? &src[1] : &src[0];
+
+	eua->l = 22;
+	eua->v[0] = PDP_EUA_ORG_IETF;
+	eua->v[1] = PDP_EUA_TYPE_v4v6;
+	memcpy(&eua->v[2], &src_v4->v4, 4);
+	memcpy(&eua->v[6], &src_v6->v6, 16);
+
 	return 0;
 }
 
-/*! Convert given in46_addr to PDP End User Address
- *  \returns 0 on success; negative on error */
+/*! Convert given PDP End User Address to an array of in46_addr
+ *  \param[in] eua End User Address structure to parse
+ *  \param[out] dst Array containing 2 in46_addr
+ *  \returns number of parsed addresses on success; negative on error */
 int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
 {
 	if (eua->l < 2)
@@ -295,22 +316,46 @@
 			memcpy(&dst->v4, &eua->v[2], 4);	/* Copy a 4 byte address */
 		else
 			dst->v4.s_addr = 0;
-		break;
+		return 1;
 	case PDP_EUA_TYPE_v6:
 		dst->len = 16;
 		if (eua->l >= 18)
 			memcpy(&dst->v6, &eua->v[2], 16);	/* Copy a 16 byte address */
 		else
 			memset(&dst->v6, 0, 16);
-		break;
+		return 1;
+	case PDP_EUA_TYPE_v4v6:
+		/* 3GPP TS 29 00, section 7.7.27 */
+		switch (eua->l) {
+			case 2: /* v4 & v6 dynamic */
+				dst[0].v4.s_addr = 0;
+				memset(&dst[1].v6, 0, 16);
+				break;
+			case 6: /* v4 static, v6 dynamic */
+				memcpy(&dst[0].v4, &eua->v[2], 4);
+				memset(&dst[1].v6, 0, 16);
+				break;
+			case 18: /* v4 dynamic, v6 static */
+				dst[0].v4.s_addr = 0;
+				memcpy(&dst[1].v6, &eua->v[2], 16);
+				break;
+			case 22:  /* v4 & v6 static */
+				memcpy(&dst[0].v4, &eua->v[2], 4);
+				memcpy(&dst[1].v6, &eua->v[6], 16);
+				break;
+			default:
+				return -1;
+		}
+		dst[0].len = 4;
+		dst[1].len = 16;
+		return 2;
 	default:
 		return -1;
 	}
-	return 0;
 
 default_to_dyn_v4:
 	/* assume dynamic IPv4 by default */
 	dst->len = 4;
 	dst->v4.s_addr = 0;
-	return 0;
+	return 1;
 }
diff --git a/lib/in46_addr.h b/lib/in46_addr.h
index ff26521..e4654cc 100644
--- a/lib/in46_addr.h
+++ b/lib/in46_addr.h
@@ -29,5 +29,5 @@
 extern int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen);
 unsigned int in46a_netmasklen(const struct in46_addr *netmask);
 
-int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua);
+int in46a_to_eua(const struct in46_addr *src, unsigned int size, struct ul66_t *eua);
 int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index c31f875..300183b 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -1617,7 +1617,7 @@
 		/* Otherwise it is deallocated by gtplib */
 		pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
 
-		pdp->peer = &iparr[n];
+		pdp->peer[0] = &iparr[n]; /* FIXME: support v4v6, have 2 peers */
 		pdp->ipif = tun;	/* TODO */
 		iparr[n].pdp = pdp;
 
diff --git a/tests/lib/in46a_test.c b/tests/lib/in46a_test.c
index c0bb670..b22da16 100644
--- a/tests/lib/in46a_test.c
+++ b/tests/lib/in46a_test.c
@@ -137,7 +137,7 @@
 #endif
 
 	/* IPv4 address */
-	OSMO_ASSERT(in46a_to_eua(&g_ia4, &eua) == 0);
+	OSMO_ASSERT(in46a_to_eua(&g_ia4, 1, &eua) == 0);
 	OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
 	OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
 	OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
@@ -154,7 +154,7 @@
 	printf("Testing in46a_from_eua() with IPv4 addresses\n");
 
 	/* default: v4 unspec */
-	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
 	OSMO_ASSERT(ia.len == 4);
 	OSMO_ASSERT(ia.v4.s_addr == 0);
 
@@ -173,14 +173,14 @@
 	/* unspecified V4 */
 	memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
 	eua.l = sizeof(v4_unspec);
-	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
 	OSMO_ASSERT(ia.len == 4);
 	OSMO_ASSERT(ia.v4.s_addr == 0);
 
 	/* specified V4 */
 	memcpy(eua.v, v4_spec, sizeof(v4_spec));
 	eua.l = sizeof(v4_spec);
-	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
 	OSMO_ASSERT(ia.len == 4);
 	OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
 }
@@ -278,19 +278,41 @@
 	};
 	struct ul66_t eua;
 
-	printf("testing in46a_to_eua() with IPv6 addresses\n");
+	printf("Testing in46a_to_eua() with IPv6 addresses\n");
 
 	/* IPv6 address */
-	OSMO_ASSERT(in46a_to_eua(&g_ia6, &eua) == 0);
+	OSMO_ASSERT(in46a_to_eua(&g_ia6, 1, &eua) == 0);
 	OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
 	OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
 	OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
 
 	/* IPv6 address with prefix / length 8 */
-	OSMO_ASSERT(in46a_to_eua(&ia_v6_8, &eua) == 0);
+	OSMO_ASSERT(in46a_to_eua(&ia_v6_8, 1, &eua) == 0);
 	OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
 	OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
 	OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
+}
+
+static void test_in46a_to_eua_v4v6() {
+	const struct in46_addr ia_v4v6[2] = {
+		{
+		.len = 16,
+		.v6.s6_addr = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 },
+		},
+		{
+		.len = 4,
+		.v4.s_addr = 0x0d0c0b0a,
+		}
+	};
+	struct ul66_t eua;
+	printf("Testing in46a_to_eua() with IPv4v6 addresses\n");
+
+	/* IPv4 address */
+	OSMO_ASSERT(in46a_to_eua(ia_v4v6, 2, &eua) == 0);
+	OSMO_ASSERT(eua.v[0] == PDP_EUA_ORG_IETF);
+	OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4v6);
+	OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
+	OSMO_ASSERT(!memcmp(&eua.v[6], &g_ia6.v6, 16));
 }
 
 static void test_in46a_from_eua_v6(void)
@@ -308,16 +330,65 @@
 	/* unspecified V6 */
 	memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
 	eua.l = sizeof(v6_unspec);
-	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
 	OSMO_ASSERT(ia.len == 16);
 	OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
 
 	/* specified V6 */
 	memcpy(eua.v, v6_spec, sizeof(v6_spec));
 	eua.l = sizeof(v6_spec);
-	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 0);
+	OSMO_ASSERT(in46a_from_eua(&eua, &ia) == 1);
 	OSMO_ASSERT(ia.len == 16);
 	OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
+}
+
+static void test_in46a_from_eua_v4v6(void) {
+	struct in46_addr ia[2];
+	struct ul66_t eua;
+	const uint8_t v4_unspec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6 };
+	const uint8_t v4_spec_v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4 };
+	const uint8_t v4_unspec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
+	const uint8_t v4_spec_v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4v6, 1,2,3,4, 1,2,3,4,5,6,7,8,9,0xa,0xb,0xc,0xd,0xe,0xf,0x10 };
+
+	memset(&eua, 0, sizeof(eua));
+
+	printf("Testing in46a_from_eua() with IPv4v6 addresses\n");
+
+	/* unspecified V4 & V6 */
+	memcpy(eua.v, v4_unspec_v6_unspec, sizeof(v4_unspec_v6_unspec));
+	eua.l = sizeof(v4_unspec_v6_unspec);
+	OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+	OSMO_ASSERT(ia[0].len == 4);
+	OSMO_ASSERT(ia[1].len == 16);
+	OSMO_ASSERT(ia[0].v4.s_addr == 0);
+	OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
+
+	/* specified V4, unspecified V6 */
+	memcpy(eua.v, v4_spec_v6_unspec, sizeof(v4_spec_v6_unspec));
+	eua.l = sizeof(v4_spec_v6_unspec);
+	OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+	OSMO_ASSERT(ia[0].len == 4);
+	OSMO_ASSERT(ia[1].len == 16);
+	OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
+	OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[1].v6));
+
+	/* unspecified V4, specified V6 */
+	memcpy(eua.v, v4_unspec_v6_spec, sizeof(v4_unspec_v6_spec));
+	eua.l = sizeof(v4_unspec_v6_spec);
+	OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+	OSMO_ASSERT(ia[0].len == 4);
+	OSMO_ASSERT(ia[1].len == 16);
+	OSMO_ASSERT(ia[0].v4.s_addr == 0);
+	OSMO_ASSERT(!memcmp(&ia[1].v6, v4_unspec_v6_spec+2, ia[1].len));
+
+	/* specified V4, specified V6 */
+	memcpy(eua.v, v4_spec_v6_spec, sizeof(v4_spec_v6_spec));
+	eua.l = sizeof(v4_spec_v6_spec);
+	OSMO_ASSERT(in46a_from_eua(&eua, ia) == 2);
+	OSMO_ASSERT(ia[0].len == 4);
+	OSMO_ASSERT(ia[1].len == 16);
+	OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
+	OSMO_ASSERT(!memcmp(&ia[1].v6, v4_spec_v6_spec+6, ia[1].len));
 }
 
 static void test_in46a_netmasklen_v6(void)
@@ -378,6 +449,8 @@
 		test_in46a_equal_v6();
 		test_in46a_to_eua_v6();
 		test_in46a_from_eua_v6();
+		test_in46a_to_eua_v4v6();
+		test_in46a_from_eua_v4v6();
 		test_in46a_netmasklen_v6();
 	}
 	return 0;
diff --git a/tests/lib/in46a_v6_test.ok b/tests/lib/in46a_v6_test.ok
index d092591..10dc7f4 100644
--- a/tests/lib/in46a_v6_test.ok
+++ b/tests/lib/in46a_v6_test.ok
@@ -3,6 +3,8 @@
 Testing in46a_ntop() with IPv6 addresses
 res = 102:304:506:708:90a:b0c:d0e:f10
 Testing in46a_equal() with IPv6 addresses
-testing in46a_to_eua() with IPv6 addresses
+Testing in46a_to_eua() with IPv6 addresses
 Testing in46a_from_eua() with IPv6 addresses
+Testing in46a_to_eua() with IPv4v6 addresses
+Testing in46a_from_eua() with IPv4v6 addresses
 Testing in46a_netmasklen() with IPv6 addresses

-- 
To view, visit https://gerrit.osmocom.org/5216
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic820759167fd3bdf329cb11d4b942e903fe50af5
Gerrit-PatchSet: 1
Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Owner: Pau Espin Pedrol <pespin at sysmocom.de>



More information about the gerrit-log mailing list