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
This commit is contained in:
parent
4f0343233b
commit
2d6a69e69a
95
ggsn/ggsn.c
95
ggsn/ggsn.c
|
@ -335,17 +335,20 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
|
||||||
static int delete_context(struct pdp_t *pdp)
|
static int delete_context(struct pdp_t *pdp)
|
||||||
{
|
{
|
||||||
struct gsn_t *gsn = pdp->gsn;
|
struct gsn_t *gsn = pdp->gsn;
|
||||||
struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
|
|
||||||
struct apn_ctx *apn = pdp->priv;
|
struct apn_ctx *apn = pdp->priv;
|
||||||
|
struct ippoolm_t *member;
|
||||||
|
int i;
|
||||||
|
|
||||||
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
|
LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
|
||||||
struct ippoolm_t *member = pdp->peer;
|
|
||||||
|
|
||||||
if (pdp->peer) {
|
for (i = 0; i < 2; i++) {
|
||||||
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
|
if (pdp->peer[i]) {
|
||||||
ippool_freeip(ipp->pool, ipp);
|
member = pdp->peer[i];
|
||||||
} else
|
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
ippool_freeip(member->pool, member);
|
||||||
|
} else if(i == 0)
|
||||||
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
|
if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
|
||||||
|
@ -512,10 +515,10 @@ int create_context_ind(struct pdp_t *pdp)
|
||||||
static char name_buf[256];
|
static char name_buf[256];
|
||||||
struct gsn_t *gsn = pdp->gsn;
|
struct gsn_t *gsn = pdp->gsn;
|
||||||
struct ggsn_ctx *ggsn = gsn->priv;
|
struct ggsn_ctx *ggsn = gsn->priv;
|
||||||
struct in46_addr addr;
|
struct in46_addr addr[2];
|
||||||
struct ippoolm_t *member;
|
struct ippoolm_t *member = NULL;
|
||||||
struct apn_ctx *apn;
|
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);
|
osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
|
||||||
|
|
||||||
|
@ -550,55 +553,63 @@ int create_context_ind(struct pdp_t *pdp)
|
||||||
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
|
memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
|
||||||
pdp->qos_neg.l = pdp->qos_req.l;
|
pdp->qos_neg.l = pdp->qos_req.l;
|
||||||
|
|
||||||
if (in46a_from_eua(&pdp->eua, &addr)) {
|
memset(addr, 0, sizeof(addr));
|
||||||
|
if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
|
||||||
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
|
||||||
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
osmo_hexdump(pdp->eua.v, pdp->eua.l));
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr.len == sizeof(struct in_addr)) {
|
/* Allocate dynamic addresses from the pool */
|
||||||
/* does this APN actually have an IPv4 pool? */
|
for (i = 0; i < num_addr; i++) {
|
||||||
if (!apn_supports_ipv4(apn))
|
if (addr[i].len == sizeof(struct in_addr)) {
|
||||||
goto err_wrong_af;
|
/* 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);
|
rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
goto err_pool_full;
|
goto err_pool_full;
|
||||||
in46a_to_eua(&member->addr, &pdp->eua);
|
/* 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! */
|
/* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
|
||||||
if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
|
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));
|
LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
|
||||||
return 0;
|
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->ipif = apn->tun.tun; /* TODO */
|
||||||
pdp->priv = apn;
|
pdp->priv = apn;
|
||||||
member->peer = pdp;
|
|
||||||
|
|
||||||
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
|
||||||
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
|
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
|
||||||
|
|
|
@ -183,12 +183,17 @@ static bool icmpv6_validate_router_solicit(const uint8_t *pack, unsigned len)
|
||||||
int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp, const struct in6_addr *own_ll_addr,
|
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)
|
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 ip6_hdr *ip6h = (struct ip6_hdr *)pack;
|
||||||
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
const struct icmpv6_hdr *ic6h = (struct icmpv6_hdr *) (pack + sizeof(*ip6h));
|
||||||
struct msgb *msg;
|
struct msgb *msg;
|
||||||
|
|
||||||
OSMO_ASSERT(pdp);
|
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);
|
OSMO_ASSERT(member);
|
||||||
|
|
||||||
if (len < sizeof(*ip6h)) {
|
if (len < sizeof(*ip6h)) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ struct gsn_t;
|
||||||
#define PDP_EUA_ORG_IETF 0xF1
|
#define PDP_EUA_ORG_IETF 0xF1
|
||||||
#define PDP_EUA_TYPE_v4 0x21
|
#define PDP_EUA_TYPE_v4 0x21
|
||||||
#define PDP_EUA_TYPE_v6 0x57
|
#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 */
|
/* 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 *
|
/* Also covers version 0. Note that version 0 6: QOS Profile was superceded *
|
||||||
|
@ -121,7 +122,7 @@ struct pdp_t {
|
||||||
/* Parameters shared by all PDP context belonging to the same MS */
|
/* Parameters shared by all PDP context belonging to the same MS */
|
||||||
|
|
||||||
void *ipif; /* IP network interface */
|
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 */
|
void *asap; /* Application specific service access point */
|
||||||
|
|
||||||
uint64_t imsi; /* International Mobile Subscriber Identity. */
|
uint64_t imsi; /* International Mobile Subscriber Identity. */
|
||||||
|
|
109
lib/in46_addr.c
109
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
|
/*! Convert given array of in46_addr to PDP End User Address
|
||||||
* \returns 0 on success; negative on error */
|
* \param[in] src Array containing 1 or 2 in46_addr
|
||||||
int in46a_to_eua(const struct in46_addr *src, struct ul66_t *eua)
|
* \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) {
|
const struct in46_addr *src_v4, *src_v6;
|
||||||
case 4:
|
if (size == 1) {
|
||||||
eua->l = 6;
|
switch (src->len) {
|
||||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
case 4:
|
||||||
eua->v[1] = PDP_EUA_TYPE_v4;
|
eua->l = 6;
|
||||||
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||||
break;
|
eua->v[1] = PDP_EUA_TYPE_v4;
|
||||||
case 8:
|
memcpy(&eua->v[2], &src->v4, 4); /* Copy a 4 byte address */
|
||||||
case 16:
|
break;
|
||||||
eua->l = 18;
|
case 8:
|
||||||
eua->v[0] = PDP_EUA_ORG_IETF;
|
case 16:
|
||||||
eua->v[1] = PDP_EUA_TYPE_v6;
|
eua->l = 18;
|
||||||
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
eua->v[0] = PDP_EUA_ORG_IETF;
|
||||||
break;
|
eua->v[1] = PDP_EUA_TYPE_v6;
|
||||||
default:
|
memcpy(&eua->v[2], &src->v6, 16); /* Copy a 16 byte address */
|
||||||
OSMO_ASSERT(0);
|
break;
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Convert given in46_addr to PDP End User Address
|
/*! Convert given PDP End User Address to an array of in46_addr
|
||||||
* \returns 0 on success; negative on error */
|
* \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)
|
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst)
|
||||||
{
|
{
|
||||||
if (eua->l < 2)
|
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 */
|
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
|
||||||
else
|
else
|
||||||
dst->v4.s_addr = 0;
|
dst->v4.s_addr = 0;
|
||||||
break;
|
return 1;
|
||||||
case PDP_EUA_TYPE_v6:
|
case PDP_EUA_TYPE_v6:
|
||||||
dst->len = 16;
|
dst->len = 16;
|
||||||
if (eua->l >= 18)
|
if (eua->l >= 18)
|
||||||
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
|
memcpy(&dst->v6, &eua->v[2], 16); /* Copy a 16 byte address */
|
||||||
else
|
else
|
||||||
memset(&dst->v6, 0, 16);
|
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:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
|
||||||
default_to_dyn_v4:
|
default_to_dyn_v4:
|
||||||
/* assume dynamic IPv4 by default */
|
/* assume dynamic IPv4 by default */
|
||||||
dst->len = 4;
|
dst->len = 4;
|
||||||
dst->v4.s_addr = 0;
|
dst->v4.s_addr = 0;
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
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);
|
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);
|
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
|
||||||
|
|
|
@ -1617,7 +1617,7 @@ int main(int argc, char **argv)
|
||||||
/* Otherwise it is deallocated by gtplib */
|
/* Otherwise it is deallocated by gtplib */
|
||||||
pdp_newpdp(&pdp, myimsi, options.nsapi, NULL);
|
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 */
|
pdp->ipif = tun; /* TODO */
|
||||||
iparr[n].pdp = pdp;
|
iparr[n].pdp = pdp;
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ static void test_in46a_to_eua(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* IPv4 address */
|
/* 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[0] == PDP_EUA_ORG_IETF);
|
||||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
|
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v4);
|
||||||
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
|
OSMO_ASSERT(osmo_load32le(&eua.v[2]) == g_ia4.v4.s_addr);
|
||||||
|
@ -154,7 +154,7 @@ static void test_in46a_from_eua(void)
|
||||||
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
printf("Testing in46a_from_eua() with IPv4 addresses\n");
|
||||||
|
|
||||||
/* default: v4 unspec */
|
/* 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.len == 4);
|
||||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||||
|
|
||||||
|
@ -173,14 +173,14 @@ static void test_in46a_from_eua(void)
|
||||||
/* unspecified V4 */
|
/* unspecified V4 */
|
||||||
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
|
||||||
eua.l = 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.len == 4);
|
||||||
OSMO_ASSERT(ia.v4.s_addr == 0);
|
OSMO_ASSERT(ia.v4.s_addr == 0);
|
||||||
|
|
||||||
/* specified V4 */
|
/* specified V4 */
|
||||||
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
memcpy(eua.v, v4_spec, sizeof(v4_spec));
|
||||||
eua.l = 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.len == 4);
|
||||||
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
OSMO_ASSERT(ia.v4.s_addr == htonl(0x01020304));
|
||||||
}
|
}
|
||||||
|
@ -278,21 +278,43 @@ static void test_in46a_to_eua_v6(void)
|
||||||
};
|
};
|
||||||
struct ul66_t eua;
|
struct ul66_t eua;
|
||||||
|
|
||||||
printf("testing in46a_to_eua() with IPv6 addresses\n");
|
printf("Testing in46a_to_eua() with IPv6 addresses\n");
|
||||||
|
|
||||||
/* IPv6 address */
|
/* 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[0] == PDP_EUA_ORG_IETF);
|
||||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||||
OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
|
OSMO_ASSERT(!memcmp(&eua.v[2], &g_ia6.v6, 16));
|
||||||
|
|
||||||
/* IPv6 address with prefix / length 8 */
|
/* 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[0] == PDP_EUA_ORG_IETF);
|
||||||
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
OSMO_ASSERT(eua.v[1] == PDP_EUA_TYPE_v6);
|
||||||
OSMO_ASSERT(!memcmp(&eua.v[2], &ia_v6_8.v6, 16));
|
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)
|
static void test_in46a_from_eua_v6(void)
|
||||||
{
|
{
|
||||||
struct in46_addr ia;
|
struct in46_addr ia;
|
||||||
|
@ -308,18 +330,67 @@ static void test_in46a_from_eua_v6(void)
|
||||||
/* unspecified V6 */
|
/* unspecified V6 */
|
||||||
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
|
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
|
||||||
eua.l = 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(ia.len == 16);
|
||||||
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
|
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia.v6));
|
||||||
|
|
||||||
/* specified V6 */
|
/* specified V6 */
|
||||||
memcpy(eua.v, v6_spec, sizeof(v6_spec));
|
memcpy(eua.v, v6_spec, sizeof(v6_spec));
|
||||||
eua.l = 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(ia.len == 16);
|
||||||
OSMO_ASSERT(!memcmp(&ia.v6, v6_spec+2, ia.len));
|
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)
|
static void test_in46a_netmasklen_v6(void)
|
||||||
{
|
{
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
@ -378,6 +449,8 @@ int main(int argc, char **argv)
|
||||||
test_in46a_equal_v6();
|
test_in46a_equal_v6();
|
||||||
test_in46a_to_eua_v6();
|
test_in46a_to_eua_v6();
|
||||||
test_in46a_from_eua_v6();
|
test_in46a_from_eua_v6();
|
||||||
|
test_in46a_to_eua_v4v6();
|
||||||
|
test_in46a_from_eua_v4v6();
|
||||||
test_in46a_netmasklen_v6();
|
test_in46a_netmasklen_v6();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -3,6 +3,8 @@ Testing in46a_to_sas() with IPv6 addresses
|
||||||
Testing in46a_ntop() with IPv6 addresses
|
Testing in46a_ntop() with IPv6 addresses
|
||||||
res = 102:304:506:708:90a:b0c:d0e:f10
|
res = 102:304:506:708:90a:b0c:d0e:f10
|
||||||
Testing in46a_equal() with IPv6 addresses
|
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_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
|
Testing in46a_netmasklen() with IPv6 addresses
|
||||||
|
|
Loading…
Reference in New Issue