Import in46_addr from OsmoGGSN

Import ip_addr and tests from I54c8a4b1d3a02b71d5983badcd923fa39ce7dd84.
Apply minor fixes so it passes the linter.

I'm in the process of extending the public API of OsmoGGSN's libgtp, so
a GSN can be created with either IPv4 or IPv6. The in46_addr code from
osmo-ggsn.git provides a good abstraction for that. I've considered
making it public in libgtp, but given that it is generic code that could
be used outside of libgtp in other Osmocom projects, put it in
libosmocore instead.

Related: OS#1953, OS#6096
Related: osmo-ggsn I8f4d9d78689909a149683583cfe36bcdc3f7cbc7
Change-Id: I31078f130ec42aeaa5ead1dde82fdd1eb44d992b
This commit is contained in:
Oliver Smith 2024-02-23 14:01:06 +01:00
parent 9d73503bd0
commit a32bebd806
10 changed files with 948 additions and 0 deletions

View File

@ -25,6 +25,7 @@ osmocore_HEADERS = \
gsmtap_util.h \
hash.h \
hashtable.h \
in46_addr.h \
isdnhdlc.h \
it_q.h \
linuxlist.h \

View File

@ -0,0 +1,43 @@
#pragma once
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/gtp/pdp.h>
/* a simple wrapper around an in6_addr to also contain the length of the address,
* thereby implicitly indicating the address family of the address */
struct in46_addr {
uint8_t len;
union {
struct in_addr v4;
struct in6_addr v6;
};
};
struct in46_prefix {
struct in46_addr addr;
uint8_t prefixlen;
};
extern int in46a_to_af(const struct in46_addr *in);
extern int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in);
extern const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size);
extern const char *in46a_ntoa(const struct in46_addr *in46);
extern const char *in46p_ntoa(const struct in46_prefix *in46p);
extern int in46a_equal(const struct in46_addr *a, const struct in46_addr *b);
extern int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b);
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, unsigned int size, struct ul66_t *eua);
int in46a_from_eua(const struct ul66_t *eua, struct in46_addr *dst);
static inline bool in46a_is_v6(const struct in46_addr *addr)
{
return addr->len == 8 || addr->len == 16;
}
static inline bool in46a_is_v4(const struct in46_addr *addr)
{
return addr->len == sizeof(struct in_addr);
}

View File

@ -41,6 +41,7 @@ libosmocore_la_SOURCES = \
exec.c \
fsm.c \
gsmtap_util.c \
in46_addr.c \
isdnhdlc.c \
it_q.c \
logging.c \

377
src/core/in46_addr.c Normal file
View File

@ -0,0 +1,377 @@
/*! \file in46_addr.c
* IPv4/v6 address functions.
*
* Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* SPDX-License-Identifier: GPL-2.0+
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
* notice and this permission notice is included in all copies or
* substantial portions of the software.
*
*/
#include <osmocom/core/utils.h>
#include <osmocom/core/in46_addr.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
/*! Return the address family of given \reff in46_addr argument */
int in46a_to_af(const struct in46_addr *in)
{
switch (in->len) {
case 4:
return AF_INET;
case 8:
case 16:
return AF_INET6;
default:
OSMO_ASSERT(0);
return -1;
}
}
/*! Convert \ref in46_addr to sockaddr_storage */
int in46a_to_sas(struct sockaddr_storage *out, const struct in46_addr *in)
{
struct sockaddr_in *sin = (struct sockaddr_in *)out;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)out;
switch (in->len) {
case 4:
sin->sin_family = AF_INET;
sin->sin_addr = in->v4;
break;
case 16:
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = in->v6;
break;
default:
OSMO_ASSERT(0);
return -1;
}
return 0;
}
/*! Convenience wrapper around inet_ntop() for in46_addr.
* \param[in] in the in46_addr to print
* \param[out] dst destination buffer where string representation of the address is stored
* \param[out] dst_size size dst. Usually it should be at least INET6_ADDRSTRLEN.
* \return address of dst on success, NULL on error */
const char *in46a_ntop(const struct in46_addr *in, char *dst, socklen_t dst_size)
{
int af;
if (!in || in->len == 0) {
osmo_strlcpy(dst, "UNDEFINED", dst_size);
return dst;
}
af = in46a_to_af(in);
if (af < 0)
return NULL;
return inet_ntop(af, (const void *) &in->v4, dst, dst_size);
}
/* like inet_ntoa() */
const char *in46a_ntoa(const struct in46_addr *in46)
{
static char addrstr_buf[256];
if (in46a_ntop(in46, addrstr_buf, sizeof(addrstr_buf)) < 0)
return "INVALID";
else
return addrstr_buf;
}
const char *in46p_ntoa(const struct in46_prefix *in46p)
{
static char addrstr_buf[256];
snprintf(addrstr_buf, sizeof(addrstr_buf), "%s/%u", in46a_ntoa(&in46p->addr), in46p->prefixlen);
return addrstr_buf;
}
/*! Determine if two in46_addr are equal or not
* \returns 1 in case they are equal; 0 otherwise */
int in46a_equal(const struct in46_addr *a, const struct in46_addr *b)
{
if (a->len == b->len && !memcmp(&a->v6, &b->v6, a->len))
return 1;
else
return 0;
}
/*! Determine if two in46_addr prefix are equal or not
* The prefix length is determined by the shortest of the prefixes of a and b
* \returns 1 in case the common prefix are equal; 0 otherwise */
int in46a_prefix_equal(const struct in46_addr *a, const struct in46_addr *b)
{
unsigned int len;
if (a->len > b->len)
len = b->len;
else
len = a->len;
if (!memcmp(&a->v6, &b->v6, len))
return 1;
else
return 0;
}
/*! Match if IPv6 addr1 + addr2 are within same \a mask */
static int ipv6_within_mask(const struct in6_addr *addr1, const struct in6_addr *addr2,
const struct in6_addr *mask)
{
struct in6_addr masked = *addr2;
#if defined(__linux__)
masked.s6_addr32[0] &= mask->s6_addr32[0];
masked.s6_addr32[1] &= mask->s6_addr32[1];
masked.s6_addr32[2] &= mask->s6_addr32[2];
masked.s6_addr32[3] &= mask->s6_addr32[3];
#else
masked.__u6_addr.__u6_addr32[0] &= mask->__u6_addr.__u6_addr32[0];
masked.__u6_addr.__u6_addr32[1] &= mask->__u6_addr.__u6_addr32[1];
masked.__u6_addr.__u6_addr32[2] &= mask->__u6_addr.__u6_addr32[2];
masked.__u6_addr.__u6_addr32[3] &= mask->__u6_addr.__u6_addr32[3];
#endif
if (!memcmp(addr1, &masked, sizeof(struct in6_addr)))
return 1;
else
return 0;
}
/*! Create an IPv6 netmask from the given prefix length */
static void create_ipv6_netmask(struct in6_addr *netmask, int prefixlen)
{
uint32_t *p_netmask;
memset(netmask, 0, sizeof(struct in6_addr));
if (prefixlen < 0)
prefixlen = 0;
else if (128 < prefixlen)
prefixlen = 128;
#if defined(__linux__)
p_netmask = &netmask->s6_addr32[0];
#else
p_netmask = &netmask->__u6_addr.__u6_addr32[0];
#endif
while (32 < prefixlen) {
*p_netmask = 0xffffffff;
p_netmask++;
prefixlen -= 32;
}
if (prefixlen != 0)
*p_netmask = htonl(0xFFFFFFFF << (32 - prefixlen));
}
/*! Determine if given \a addr is within given \a net + \a prefixlen
* Builds the netmask from \a net + \a prefixlen and matches it to \a addr
* \returns 1 in case of a match, 0 otherwise */
int in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net, size_t prefixlen)
{
struct in_addr netmask;
struct in6_addr netmask6;
if (addr->len != net->len)
return 0;
switch (addr->len) {
case 4:
netmask.s_addr = htonl(0xFFFFFFFF << (32 - prefixlen));
if ((addr->v4.s_addr & netmask.s_addr) == net->v4.s_addr)
return 1;
else
return 0;
case 16:
create_ipv6_netmask(&netmask6, prefixlen);
return ipv6_within_mask(&addr->v6, &net->v6, &netmask6);
default:
OSMO_ASSERT(0);
return 0;
}
}
static unsigned int ipv4_netmasklen(const struct in_addr *netmask)
{
uint32_t bits = netmask->s_addr;
uint8_t *b = (uint8_t *) &bits;
unsigned int i, prefix = 0;
for (i = 0; i < 4; i++) {
while (b[i] & 0x80) {
prefix++;
b[i] = b[i] << 1;
}
}
return prefix;
}
static unsigned int ipv6_netmasklen(const struct in6_addr *netmask)
{
#if defined(__linux__)
#define ADDRFIELD(i) s6_addr32[i]
#else
#define ADDRFIELD(i) __u6_addr.__u6_addr32[i]
#endif
unsigned int i, j, prefix = 0;
for (j = 0; j < 4; j++) {
uint32_t bits = netmask->ADDRFIELD(j);
uint8_t *b = (uint8_t *) &bits;
for (i = 0; i < 4; i++) {
while (b[i] & 0x80) {
prefix++;
b[i] = b[i] << 1;
}
}
}
#undef ADDRFIELD
return prefix;
}
/*! Convert netmask to prefix length representation
* \param[in] netmask in46_addr containing a netmask (consecutive list of 1-bit followed by consecutive list of 0-bit)
* \returns prefix length representation of the netmask (count of 1-bit from the start of the netmask)
*/
unsigned int in46a_netmasklen(const struct in46_addr *netmask)
{
switch (netmask->len) {
case 4:
return ipv4_netmasklen(&netmask->v4);
case 16:
return ipv6_netmasklen(&netmask->v6);
default:
OSMO_ASSERT(0);
return 0;
}
}
/*! 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)
{
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 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)
goto default_to_dyn_v4;
if (eua->v[0] != 0xf1)
return -1;
switch (eua->v[1]) {
case PDP_EUA_TYPE_v4:
dst->len = 4;
if (eua->l >= 6)
memcpy(&dst->v4, &eua->v[2], 4); /* Copy a 4 byte address */
else
dst->v4.s_addr = 0;
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);
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;
}
default_to_dyn_v4:
/* assume dynamic IPv4 by default */
dst->len = 4;
dst->v4.s_addr = 0;
return 1;
}

View File

@ -55,6 +55,17 @@ gsmtap_source_init2;
gsmtap_source_init_fd;
gsmtap_source_init_fd2;
gsmtap_type_names;
in46a_equal;
in46a_from_eua;
in46a_netmasklen;
in46a_ntoa;
in46a_ntop;
in46a_prefix_equal;
in46a_to_af;
in46a_to_eua;
in46a_to_sas;
in46a_within_mask;
in46p_ntoa;
log_add_target;
log_category_name;
log_check_level;

View File

@ -59,6 +59,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
osmo_io/osmo_io_test \
soft_uart/soft_uart_test \
rlp/rlp_test \
in46_addr/in46a_test \
$(NULL)
if ENABLE_MSGFILE
@ -505,6 +506,8 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
soft_uart/soft_uart_test.ok \
rlp/rlp_test.ok \
socket/socket_sctp_test.ok socket/socket_sctp_test.err \
in46_addr/in46a_test.ok \
in46_addr/in46a_v6_test.ok \
$(NULL)
if ENABLE_LIBSCTP

View File

@ -0,0 +1,473 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/bits.h>
#include <osmocom/core/in46_addr.h>
static struct log_info log_info = {};
static const struct in46_addr g_ia4 = {
.len = 4,
.v4.s_addr = 0x0d0c0b0a,
};
static void test_in46a_to_af(void)
{
printf("Testing in46a_to_af() with IPv4 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia4) == AF_INET);
}
static void test_in46a_to_sas(void)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
printf("Testing in46a_to_sas() with IPv4 addresses\n");
memset(&ss, 0, sizeof(ss));
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia4) == 0);
OSMO_ASSERT(sin->sin_family == AF_INET);
OSMO_ASSERT(sin->sin_addr.s_addr == g_ia4.v4.s_addr);
}
static void test_in46a_ntop(void)
{
struct in46_addr ia;
char buf[256];
const char *res;
printf("Testing in46a_ntop() with IPv4 addresses\n");
res = in46a_ntop(NULL, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
printf("res = %s\n", res);
ia.len = 0;
res = in46a_ntop(&ia, buf, sizeof(buf));
printf("res = %s\n", res);
OSMO_ASSERT(res && !strcmp(res, "UNDEFINED"));
ia.len = 4;
ia.v4.s_addr = htonl(0x01020304);
res = in46a_ntop(&ia, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "1.2.3.4"));
printf("res = %s\n", res);
}
static void test_in46p_ntoa(void)
{
const struct in46_prefix ip46 = {
.prefixlen = 24,
.addr = {
.len = 4,
.v4.s_addr = htonl(0x10203000),
},
};
printf("in46p_ntoa() returns %s\n", in46p_ntoa(&ip46));
}
static void test_in46a_equal(void)
{
struct in46_addr b;
printf("Testing in46a_equal() with IPv4 addresses\n");
memset(&b, 0xff, sizeof(b));
b.len = g_ia4.len;
b.v4.s_addr = g_ia4.v4.s_addr;
OSMO_ASSERT(in46a_equal(&g_ia4, &b));
}
static int log_in46a_within_mask(const struct in46_addr *addr, const struct in46_addr *net,
size_t prefixlen)
{
int rc;
printf("in46a_within_mask(%s, ", in46a_ntoa(addr));
printf("%s, %lu) = ", in46a_ntoa(net), prefixlen);
rc = in46a_within_mask(addr, net, prefixlen);
printf("%d\n", rc);
return rc;
}
static void test_in46a_within_mask(void)
{
struct in46_addr addr, mask;
printf("Testing in46a_within_mask() with IPv4 addresses\n");
addr = g_ia4;
mask = g_ia4;
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 32));
mask.v4.s_addr = htonl(ntohl(mask.v4.s_addr) & 0xfffffffC);
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
mask.v4.s_addr = htonl(ntohl(mask.v4.s_addr) & 0xfff80000);
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 13));
addr.v4.s_addr = htonl(ntohl(addr.v4.s_addr) + 1);
mask = g_ia4;
OSMO_ASSERT(!log_in46a_within_mask(&addr, &mask, 32));
mask.v4.s_addr = htonl(ntohl(mask.v4.s_addr) & 0xfffffffC);
OSMO_ASSERT(log_in46a_within_mask(&addr, &mask, 30));
}
static void test_in46a_to_eua(void)
{
struct ul66_t eua;
printf("testing in46a_to_eua() with IPv4 addresses\n");
#if 0 /* triggers assert in current implementation */
const struct in46_addr ia_invalid = { .len = 3, };
OSMO_ASSERT(in46a_to_eua(&ia_invalid, &eua) < 0);
#endif
/* IPv4 address */
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);
}
static void test_in46a_from_eua(void)
{
struct in46_addr ia[2];
struct ul66_t eua;
const uint8_t v4_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4 };
const uint8_t v4_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v4, 1, 2, 3, 4 };
memset(&eua, 0, sizeof(eua));
printf("Testing in46a_from_eua() with IPv4 addresses\n");
/* default: v4 unspec */
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[0].v4.s_addr == 0);
/* invalid */
eua.v[0] = 0x23;
eua.v[1] = PDP_EUA_TYPE_v4;
eua.l = 6;
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
/* invalid */
eua.v[0] = PDP_EUA_ORG_IETF;
eua.v[1] = 0x23;
eua.l = 6;
OSMO_ASSERT(in46a_from_eua(&eua, ia) < 0);
/* unspecified V4 */
memcpy(eua.v, v4_unspec, sizeof(v4_unspec));
eua.l = sizeof(v4_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[0].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) == 1);
OSMO_ASSERT(ia[0].len == 4);
OSMO_ASSERT(ia[0].v4.s_addr == htonl(0x01020304));
}
static void test_in46a_netmasklen(void)
{
struct in46_addr netmask;
unsigned int len;
printf("Testing in46a_netmasklen() with IPv4 addresses\n");
netmask.len = 4;
netmask.v4.s_addr = 0xffffffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 32);
netmask.v4.s_addr = 0x00ffffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 24);
netmask.v4.s_addr = 0x00f0ffff;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 20);
netmask.v4.s_addr = 0x000000fe;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 7);
netmask.v4.s_addr = 0x00000000;
len = in46a_netmasklen(&netmask);
OSMO_ASSERT(len == 0);
}
/* IPv6 specific tests */
static const struct in46_addr g_ia6 = {
.len = 16,
.v6.s6_addr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
};
static void test_in46a_to_af_v6(void)
{
struct in46_addr ia;
printf("Testing in46a_to_af() with IPv6 addresses\n");
OSMO_ASSERT(in46a_to_af(&g_ia6) == AF_INET6);
ia.len = 8;
OSMO_ASSERT(in46a_to_af(&ia) == AF_INET6);
}
static void test_in46a_to_sas_v6(void)
{
struct sockaddr_storage ss;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;
printf("Testing in46a_to_sas() with IPv6 addresses\n");
memset(&ss, 0, sizeof(ss));
OSMO_ASSERT(in46a_to_sas(&ss, &g_ia6) == 0);
OSMO_ASSERT(sin6->sin6_family == AF_INET6);
OSMO_ASSERT(!memcmp(&sin6->sin6_addr, &g_ia6.v6, sizeof(sin6->sin6_addr)));
}
static void test_in46a_ntop_v6(void)
{
char buf[256];
const char *res;
printf("Testing in46a_ntop() with IPv6 addresses\n");
res = in46a_ntop(&g_ia6, buf, sizeof(buf));
OSMO_ASSERT(res && !strcmp(res, "102:304:506:708:90a:b0c:d0e:f10"));
printf("res = %s\n", res);
}
static void test_in46a_equal_v6(void)
{
struct in46_addr b;
printf("Testing in46a_equal() with IPv6 addresses\n");
memset(&b, 0xff, sizeof(b));
b.len = g_ia6.len;
b.v6 = g_ia6.v6;
OSMO_ASSERT(in46a_equal(&g_ia6, &b));
}
static void test_in46a_to_eua_v6(void)
{
const struct in46_addr ia_v6_8 = {
.len = 8,
.v6.s6_addr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 },
};
struct ul66_t eua;
printf("Testing in46a_to_eua() with IPv6 addresses\n");
/* IPv6 address */
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, 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(void)
{
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)
{
struct in46_addr ia[2];
struct ul66_t eua;
const uint8_t v6_unspec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6 };
const uint8_t v6_spec[] = { PDP_EUA_ORG_IETF, PDP_EUA_TYPE_v6,
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 IPv6 addresses\n");
/* unspecified V6 */
memcpy(eua.v, v6_unspec, sizeof(v6_unspec));
eua.l = sizeof(v6_unspec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
OSMO_ASSERT(ia[0].len == 16);
OSMO_ASSERT(IN6_IS_ADDR_UNSPECIFIED(&ia[0].v6));
/* specified V6 */
memcpy(eua.v, v6_spec, sizeof(v6_spec));
eua.l = sizeof(v6_spec);
OSMO_ASSERT(in46a_from_eua(&eua, ia) == 1);
OSMO_ASSERT(ia[0].len == 16);
OSMO_ASSERT(!memcmp(&ia[0].v6, v6_spec+2, ia[0].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)
{
unsigned int len;
printf("Testing in46a_netmasklen() with IPv6 addresses\n");
const struct in46_addr netmaskA = {
.len = 16,
.v6.s6_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
};
len = in46a_netmasklen(&netmaskA);
OSMO_ASSERT(len == 128);
const struct in46_addr netmaskB = {
.len = 16,
.v6.s6_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00},
};
len = in46a_netmasklen(&netmaskB);
OSMO_ASSERT(len == 104);
const struct in46_addr netmaskC = {
.len = 16,
.v6.s6_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00},
};
len = in46a_netmasklen(&netmaskC);
OSMO_ASSERT(len == 103);
const struct in46_addr netmaskD = {
.len = 16,
.v6.s6_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};
len = in46a_netmasklen(&netmaskD);
OSMO_ASSERT(len == 0);
}
int main(int argc, char **argv)
{
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
srand(time(NULL));
if (argc < 2 || strcmp(argv[1], "-v6")) {
test_in46a_to_af();
test_in46a_to_sas();
test_in46a_ntop();
test_in46p_ntoa();
test_in46a_equal();
test_in46a_within_mask();
test_in46a_to_eua();
test_in46a_from_eua();
test_in46a_netmasklen();
} else {
test_in46a_to_af_v6();
test_in46a_to_sas_v6();
test_in46a_ntop_v6();
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;
}

View File

@ -0,0 +1,17 @@
Testing in46a_to_af() with IPv4 addresses
Testing in46a_to_sas() with IPv4 addresses
Testing in46a_ntop() with IPv4 addresses
res = UNDEFINED
res = UNDEFINED
res = 1.2.3.4
in46p_ntoa() returns 16.32.48.0/24
Testing in46a_equal() with IPv4 addresses
Testing in46a_within_mask() with IPv4 addresses
in46a_within_mask(10.11.12.13, 10.11.12.13, 32) = 1
in46a_within_mask(10.11.12.13, 10.11.12.12, 30) = 1
in46a_within_mask(10.11.12.13, 10.8.0.0, 13) = 1
in46a_within_mask(10.11.12.14, 10.11.12.13, 32) = 0
in46a_within_mask(10.11.12.14, 10.11.12.12, 30) = 1
testing in46a_to_eua() with IPv4 addresses
Testing in46a_from_eua() with IPv4 addresses
Testing in46a_netmasklen() with IPv4 addresses

View File

@ -0,0 +1,10 @@
Testing in46a_to_af() with IPv6 addresses
Testing in46a_to_sas() with IPv6 addresses
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_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

View File

@ -541,3 +541,15 @@ AT_KEYWORDS([rlp])
cat $abs_srcdir/rlp/rlp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/rlp/rlp_test], [0], [expout], [ignore])
AT_CLEANUP
AT_SETUP([in46a])
AT_KEYWORDS([in46a])
cat $abs_srcdir/in46_addr/in46a_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/in46_addr/in46a_test], [], [expout], [])
AT_CLEANUP
AT_SETUP([in46a_v6])
AT_KEYWORDS([in46a_v6])
cat $abs_srcdir/in46_addr/in46a_v6_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/in46_addr/in46a_test -v6], [], [expout], [])
AT_CLEANUP