aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2017-12-06 19:26:25 +0100
committerPau Espin Pedrol <pespin@sysmocom.de>2017-12-11 11:39:18 +0100
commit2d6a69e69a4b4cb2b8cc63c4810dae44e5a4d8f6 (patch)
treeb0484aa531ce9f9973f467c3720be4406d4d8518 /lib
parent4f0343233b83337afa1e1dfb4bcf9d076ecd4be2 (diff)
Add support for IPv4v6 End User 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
Diffstat (limited to 'lib')
-rw-r--r--lib/in46_addr.c109
-rw-r--r--lib/in46_addr.h2
2 files changed, 84 insertions, 27 deletions
diff --git a/lib/in46_addr.c b/lib/in46_addr.c
index 36ad6af..f4bb8a2 100644
--- a/lib/in46_addr.c
+++ b/lib/in46_addr.c
@@ -253,33 +253,66 @@ unsigned int in46a_netmasklen(const struct in46_addr *netmask)
}
}
-/*! Convert given PDP End User Address to in46_addr
- * \returns 0 on success; negative on error */
-int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
+/*! Convert given array of in46_addr to PDP End User Address
+ * \param[in] src Array containing 1 or 2 in46_addr
+ * \param[out] eua End User Address structure to fill
+ * \returns 0 on success; negative on error
+ *
+ * In case size is 2, this function expects to find exactly one IPv4 and one
+ * IPv6 addresses in src. */
+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 (1 or 2) on success; negative on error
+ *
+ * This function expects to receive an End User Address struct together with an
+ * array of 2 zeroed in46_addr structs. The in46_addr structs are filled in
+ * order, hence if the function returns 1 the parsed address will be stored in
+ * the first struct and the second one will be left intact. If 2 is returned, it
+ * is guaranteed that one of them is an IPv4 and the other one is an IPv6, but
+ * the order in which they are presented is not specified and must be
+ * discovered for instance by checking the len field of each address.
+ */
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
{
if (eua->l < 2)
@@ -295,22 +328,46 @@ int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
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.060, 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_prefix_equal(const struct in46_addr *a, const struct in46_addr
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);