700 lines
19 KiB
C
700 lines
19 KiB
C
/* LWIP service - addr.c - socket address verification and conversion */
|
|
|
|
#include "lwip.h"
|
|
|
|
/*
|
|
* Return TRUE if the given socket address is of type AF_UNSPEC, or FALSE
|
|
* otherwise.
|
|
*/
|
|
int
|
|
addr_is_unspec(const struct sockaddr * addr, socklen_t addr_len)
|
|
{
|
|
|
|
return (addr_len >= offsetof(struct sockaddr, sa_data) &&
|
|
addr->sa_family == AF_UNSPEC);
|
|
}
|
|
|
|
/*
|
|
* Check whether the given multicast address is generally valid. This check
|
|
* should not be moved into addr_get_inet(), as we do not want to forbid
|
|
* creating routes for such addresses, for example. We do however apply the
|
|
* restrictions here to all provided source and destination addresses. Return
|
|
* TRUE if the address is an acceptable multicast address, or FALSE otherwise.
|
|
*/
|
|
int
|
|
addr_is_valid_multicast(const ip_addr_t * ipaddr)
|
|
{
|
|
uint8_t scope;
|
|
|
|
assert(ip_addr_ismulticast(ipaddr));
|
|
|
|
/* We apply restrictions to IPv6 multicast addresses only. */
|
|
if (IP_IS_V6(ipaddr)) {
|
|
scope = ip6_addr_multicast_scope(ip_2_ip6(ipaddr));
|
|
|
|
if (scope == IP6_MULTICAST_SCOPE_RESERVED0 ||
|
|
scope == IP6_MULTICAST_SCOPE_RESERVEDF)
|
|
return FALSE;
|
|
|
|
/*
|
|
* We do not impose restrictions on the three defined embedded
|
|
* flags, even though we put no effort into supporting them,
|
|
* especially in terms of automatically creating routes for
|
|
* all cases. We do force the fourth flag to be zero.
|
|
* Unfortunately there is no lwIP macro to check for this flag.
|
|
*/
|
|
if (ip_2_ip6(ipaddr)->addr[0] & PP_HTONL(0x00800000UL))
|
|
return FALSE;
|
|
|
|
/* Prevent KAME-embedded zone IDs from entering the system. */
|
|
if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN) &&
|
|
(ip_2_ip6(ipaddr)->addr[0] & PP_HTONL(0x0000ffffUL)))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Load a sockaddr structure, as copied from userland, as a lwIP-style IP
|
|
* address and (optionally) a port number. The expected type of IP address is
|
|
* given as 'type', which must be one of IPADDR_TYPE_{V4,ANY,V6}. If it is
|
|
* IPADDR_TYPE_V4, 'addr' is expected to point to a sockaddr_in structure. If
|
|
* it is IPADDR_TYPE_{ANY,V6}, 'addr' is expected to point to a sockaddr_in6
|
|
* structure. For the _ANY case, the result will be an _ANY address only if it
|
|
* is the unspecified (all-zeroes) address and a _V6 address in all other
|
|
* cases. For the _V6 case, the result will always be a _V6 address. The
|
|
* length of the structure pointed to by 'addr' is given as 'addr_len'. If the
|
|
* boolean 'kame' flag is set, addresses will be interpreted to be KAME style,
|
|
* meaning that for scoped IPv6 addresses, the zone is embedded in the address
|
|
* rather than given in sin6_scope_id. On success, store the resulting IP
|
|
* address in 'ipaddr'. If 'port' is not NULL, store the port number in it;
|
|
* otherwise, ignore the port number. On any parsing failure, return an
|
|
* appropriate negative error code.
|
|
*/
|
|
int
|
|
addr_get_inet(const struct sockaddr * addr, socklen_t addr_len, uint8_t type,
|
|
ip_addr_t * ipaddr, int kame, uint16_t * port)
|
|
{
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
ip6_addr_t *ip6addr;
|
|
uint32_t ifindex;
|
|
|
|
switch (type) {
|
|
case IPADDR_TYPE_V4:
|
|
if (addr_len != sizeof(sin))
|
|
return EINVAL;
|
|
|
|
/*
|
|
* Getting around strict aliasing problems. Oh, the irony of
|
|
* doing an extra memcpy so that the compiler can do a better
|
|
* job at optimizing..
|
|
*/
|
|
memcpy(&sin, addr, sizeof(sin));
|
|
|
|
if (sin.sin_family != AF_INET)
|
|
return EAFNOSUPPORT;
|
|
|
|
ip_addr_set_ip4_u32(ipaddr, sin.sin_addr.s_addr);
|
|
|
|
if (port != NULL)
|
|
*port = ntohs(sin.sin_port);
|
|
|
|
return OK;
|
|
|
|
case IPADDR_TYPE_ANY:
|
|
case IPADDR_TYPE_V6:
|
|
if (addr_len != sizeof(sin6))
|
|
return EINVAL;
|
|
|
|
/* Again, strict aliasing.. */
|
|
memcpy(&sin6, addr, sizeof(sin6));
|
|
|
|
if (sin6.sin6_family != AF_INET6)
|
|
return EAFNOSUPPORT;
|
|
|
|
memset(ipaddr, 0, sizeof(*ipaddr));
|
|
|
|
/*
|
|
* This is a bit ugly, but NetBSD does not expose s6_addr32 and
|
|
* s6_addr is a series of bytes, which is a mismatch for lwIP.
|
|
* The alternative would be another memcpy..
|
|
*/
|
|
ip6addr = ip_2_ip6(ipaddr);
|
|
assert(sizeof(ip6addr->addr) == sizeof(sin6.sin6_addr));
|
|
memcpy(ip6addr->addr, &sin6.sin6_addr, sizeof(ip6addr->addr));
|
|
|
|
/*
|
|
* If the address may have a scope, extract the zone ID.
|
|
* Where the zone ID is depends on the 'kame' parameter: KAME-
|
|
* style addresses have it embedded within the address, whereas
|
|
* non-KAME addresses use the (misnamed) sin6_scope_id field.
|
|
*/
|
|
if (ip6_addr_has_scope(ip6addr, IP6_UNKNOWN)) {
|
|
if (kame) {
|
|
ifindex =
|
|
ntohl(ip6addr->addr[0]) & 0x0000ffffUL;
|
|
|
|
ip6addr->addr[0] &= PP_HTONL(0xffff0000UL);
|
|
} else {
|
|
/*
|
|
* Reject KAME-style addresses for normal
|
|
* socket calls, to save ourselves the trouble
|
|
* of mixed address styles elsewhere.
|
|
*/
|
|
if (ip6addr->addr[0] & PP_HTONL(0x0000ffffUL))
|
|
return EINVAL;
|
|
|
|
ifindex = sin6.sin6_scope_id;
|
|
}
|
|
|
|
/*
|
|
* Reject invalid zone IDs. This also enforces that
|
|
* no zone IDs wider than eight bits enter the system.
|
|
* As a side effect, it is not possible to add routes
|
|
* for invalid zones, but that should be no problem.
|
|
*/
|
|
if (ifindex != 0 &&
|
|
ifdev_get_by_index(ifindex) == NULL)
|
|
return ENXIO;
|
|
|
|
ip6_addr_set_zone(ip6addr, ifindex);
|
|
} else
|
|
ip6_addr_clear_zone(ip6addr);
|
|
|
|
/*
|
|
* Set the type to ANY if it was ANY and the address itself is
|
|
* ANY as well. Otherwise, we are binding to a specific IPv6
|
|
* address, so IPV6_V6ONLY stops being relevant and we should
|
|
* leave the address set to V6. Destination addresses for ANY
|
|
* are set to V6 elsewhere.
|
|
*/
|
|
if (type == IPADDR_TYPE_ANY && ip6_addr_isany(ip6addr))
|
|
IP_SET_TYPE(ipaddr, type);
|
|
else
|
|
IP_SET_TYPE(ipaddr, IPADDR_TYPE_V6);
|
|
|
|
if (port != NULL)
|
|
*port = ntohs(sin6.sin6_port);
|
|
|
|
return OK;
|
|
|
|
default:
|
|
return EAFNOSUPPORT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store an lwIP-style IP address and port number as a sockaddr structure
|
|
* (sockaddr_in or sockaddr_in6, depending on the given IP address) to be
|
|
* copied to userland. The result is stored in the buffer pointed to by
|
|
* 'addr'. Before the call, 'addr_len' must be set to the size of this buffer.
|
|
* This is an internal check to prevent buffer overflows, and must not be used
|
|
* to validate input, since a mismatch will trigger a panic. After the call,
|
|
* 'addr_len' will be set to the size of the resulting structure. The lwIP-
|
|
* style address is given as 'ipaddr'. If the boolean 'kame' flag is set, the
|
|
* address will be stored KAME-style, meaning that for scoped IPv6 addresses,
|
|
* the address zone will be stored embedded in the address rather than in
|
|
* sin6_scope_id. If relevant, 'port' contains the port number in host-byte
|
|
* order; otherwise it should be set to zone.
|
|
*/
|
|
void
|
|
addr_put_inet(struct sockaddr * addr, socklen_t * addr_len,
|
|
const ip_addr_t * ipaddr, int kame, uint16_t port)
|
|
{
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
const ip6_addr_t *ip6addr;
|
|
uint32_t zone;
|
|
|
|
switch (IP_GET_TYPE(ipaddr)) {
|
|
case IPADDR_TYPE_V4:
|
|
if (*addr_len < sizeof(sin))
|
|
panic("provided address buffer too small");
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
|
|
sin.sin_len = sizeof(sin);
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(port);
|
|
sin.sin_addr.s_addr = ip_addr_get_ip4_u32(ipaddr);
|
|
|
|
memcpy(addr, &sin, sizeof(sin));
|
|
*addr_len = sizeof(sin);
|
|
|
|
break;
|
|
|
|
case IPADDR_TYPE_ANY:
|
|
case IPADDR_TYPE_V6:
|
|
if (*addr_len < sizeof(sin6))
|
|
panic("provided address buffer too small");
|
|
|
|
ip6addr = ip_2_ip6(ipaddr);
|
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
|
|
|
sin6.sin6_len = sizeof(sin6);
|
|
sin6.sin6_family = AF_INET6;
|
|
sin6.sin6_port = htons(port);
|
|
memcpy(&sin6.sin6_addr, ip6addr->addr, sizeof(sin6.sin6_addr));
|
|
|
|
/*
|
|
* If the IPv6 address has a zone set, it must be scoped, and
|
|
* we put the zone in the result. It may occur that a scoped
|
|
* IPv6 address does not have a zone here though, for example
|
|
* if packet routing fails for sendto() with a zoneless address
|
|
* on an unbound socket, resulting in an RTM_MISS message. In
|
|
* such cases, simply leave the zone index blank in the result.
|
|
*/
|
|
if (ip6_addr_has_zone(ip6addr)) {
|
|
assert(ip6_addr_has_scope(ip6addr, IP6_UNKNOWN));
|
|
|
|
zone = ip6_addr_zone(ip6addr);
|
|
assert(zone <= UINT8_MAX);
|
|
|
|
if (kame)
|
|
sin6.sin6_addr.s6_addr[3] = zone;
|
|
else
|
|
sin6.sin6_scope_id = zone;
|
|
}
|
|
|
|
memcpy(addr, &sin6, sizeof(sin6));
|
|
*addr_len = sizeof(sin6);
|
|
|
|
break;
|
|
|
|
default:
|
|
panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Load a link-layer sockaddr structure (sockaddr_dl), as copied from userland,
|
|
* and return the contained name and/or hardware address. The address is
|
|
* provided as 'addr', with length 'addr_len'. On success, return OK. If
|
|
* 'name' is not NULL, it must be of size 'name_max', and will be used to store
|
|
* the (null-terminated) interface name in the given structure if present, or
|
|
* the empty string if not. If 'hwaddr' is not NULL, it will be used to store
|
|
* the hardware address in the given structure, which must in that case be
|
|
* present and exactly 'hwaddr_len' bytes long. On any parsing failure, return
|
|
* an appropriate negative error code.
|
|
*/
|
|
int
|
|
addr_get_link(const struct sockaddr * addr, socklen_t addr_len, char * name,
|
|
size_t name_max, uint8_t * hwaddr, size_t hwaddr_len)
|
|
{
|
|
struct sockaddr_dlx sdlx;
|
|
size_t nlen, alen;
|
|
|
|
if (addr_len < offsetof(struct sockaddr_dlx, sdlx_data))
|
|
return EINVAL;
|
|
|
|
/*
|
|
* We cannot prevent callers from passing in massively oversized
|
|
* sockaddr_dl structure. However, we insist that all the actual data
|
|
* be contained within the size of our sockaddr_dlx version.
|
|
*/
|
|
if (addr_len > sizeof(sdlx))
|
|
addr_len = sizeof(sdlx);
|
|
|
|
memcpy(&sdlx, addr, addr_len);
|
|
|
|
if (sdlx.sdlx_family != AF_LINK)
|
|
return EAFNOSUPPORT;
|
|
|
|
/* Address selectors are not currently supported. */
|
|
if (sdlx.sdlx_slen != 0)
|
|
return EINVAL;
|
|
|
|
nlen = (size_t)sdlx.sdlx_nlen;
|
|
alen = (size_t)sdlx.sdlx_alen;
|
|
|
|
/* The nlen and alen fields are 8-bit, so no risks of overflow here. */
|
|
if (addr_len < offsetof(struct sockaddr_dlx, sdlx_data) + nlen + alen)
|
|
return EINVAL;
|
|
|
|
/*
|
|
* Copy out the name, truncating it if needed. The name in the
|
|
* sockaddr is not null terminated, so we have to do that. If the
|
|
* sockaddr has no name, copy out an empty name.
|
|
*/
|
|
if (name != NULL) {
|
|
assert(name_max > 0);
|
|
|
|
if (name_max > nlen + 1)
|
|
name_max = nlen + 1;
|
|
|
|
memcpy(name, sdlx.sdlx_data, name_max - 1);
|
|
name[name_max - 1] = '\0';
|
|
}
|
|
|
|
/*
|
|
* Copy over the hardware address. For simplicity, we require that the
|
|
* caller specify the exact hardware address length.
|
|
*/
|
|
if (hwaddr != NULL) {
|
|
if (alen != hwaddr_len)
|
|
return EINVAL;
|
|
|
|
memcpy(hwaddr, sdlx.sdlx_data + nlen, hwaddr_len);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*
|
|
* Store a link-layer sockaddr structure (sockaddr_dl), to be copied to
|
|
* userland. The result is stored in the buffer pointed to by 'addr'. Before
|
|
* the call, 'addr_len' must be set to the size of this buffer. This is an
|
|
* internal check to prevent buffer overflows, and must not be used to validate
|
|
* input, since a mismatch will trigger a panic. After the call, 'addr_len'
|
|
* will be set to the size of the resulting structure. The given interface
|
|
* index 'ifindex' and (IFT_) interface type 'type' will always be stored in
|
|
* the resulting structure. If 'name' is not NULL, it must be a null-
|
|
* terminated interface name string which will be included in the structure.
|
|
* If 'hwaddr' is not NULL, it must be a hardware address of length
|
|
* 'hwaddr_len', which will also be included in the structure.
|
|
*/
|
|
void
|
|
addr_put_link(struct sockaddr * addr, socklen_t * addr_len, uint32_t ifindex,
|
|
uint32_t type, const char * name, const uint8_t * hwaddr,
|
|
size_t hwaddr_len)
|
|
{
|
|
struct sockaddr_dlx sdlx;
|
|
size_t name_len;
|
|
socklen_t len;
|
|
|
|
name_len = (name != NULL) ? strlen(name) : 0;
|
|
|
|
if (hwaddr == NULL)
|
|
hwaddr_len = 0;
|
|
|
|
assert(name_len < IFNAMSIZ);
|
|
assert(hwaddr_len <= NETIF_MAX_HWADDR_LEN);
|
|
|
|
len = offsetof(struct sockaddr_dlx, sdlx_data) + name_len + hwaddr_len;
|
|
|
|
if (*addr_len < len)
|
|
panic("provided address buffer too small");
|
|
|
|
memset(&sdlx, 0, sizeof(sdlx));
|
|
sdlx.sdlx_len = len;
|
|
sdlx.sdlx_family = AF_LINK;
|
|
sdlx.sdlx_index = ifindex;
|
|
sdlx.sdlx_type = type;
|
|
sdlx.sdlx_nlen = name_len;
|
|
sdlx.sdlx_alen = hwaddr_len;
|
|
if (name_len > 0)
|
|
memcpy(sdlx.sdlx_data, name, name_len);
|
|
if (hwaddr_len > 0)
|
|
memcpy(sdlx.sdlx_data + name_len, hwaddr, hwaddr_len);
|
|
|
|
memcpy(addr, &sdlx, len);
|
|
*addr_len = len;
|
|
}
|
|
|
|
/*
|
|
* Convert an IPv4 or IPv6 netmask, given as sockaddr structure 'addr', to a
|
|
* prefix length. The length of the sockaddr structure is given as 'addr_len'.
|
|
* For consistency with addr_get_inet(), the expected address type is given as
|
|
* 'type', and must be either IPADDR_TYPE_V4 or IPADDR_TYPE_V6. On success,
|
|
* return OK with the number of set prefix bits returned in 'prefix', and
|
|
* optionally with a lwIP representation of the netmask stored in 'ipaddr' (if
|
|
* not NULL). On failure, return an appropriate negative error code. Note
|
|
* that this function does not support compressed IPv4 network masks; such
|
|
* addresses must be expanded before a call to this function.
|
|
*/
|
|
int
|
|
addr_get_netmask(const struct sockaddr * addr, socklen_t addr_len,
|
|
uint8_t type, unsigned int * prefix, ip_addr_t * ipaddr)
|
|
{
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
unsigned int byte, bit;
|
|
uint32_t val;
|
|
|
|
switch (type) {
|
|
case IPADDR_TYPE_V4:
|
|
if (addr_len != sizeof(sin))
|
|
return EINVAL;
|
|
|
|
memcpy(&sin, addr, sizeof(sin));
|
|
|
|
if (sin.sin_family != AF_INET)
|
|
return EAFNOSUPPORT;
|
|
|
|
val = ntohl(sin.sin_addr.s_addr);
|
|
|
|
/* Find the first zero bit. */
|
|
for (bit = 0; bit < IP4_BITS; bit++)
|
|
if (!(val & (1 << (IP4_BITS - bit - 1))))
|
|
break;
|
|
|
|
*prefix = bit;
|
|
|
|
/* All bits after the first zero bit must also be zero. */
|
|
if (bit < IP4_BITS &&
|
|
(val & ((1 << (IP4_BITS - bit - 1)) - 1)))
|
|
return EINVAL;
|
|
|
|
if (ipaddr != NULL)
|
|
ip_addr_set_ip4_u32(ipaddr, sin.sin_addr.s_addr);
|
|
|
|
return OK;
|
|
|
|
case IPADDR_TYPE_V6:
|
|
if (addr_len != sizeof(sin6))
|
|
return EINVAL;
|
|
|
|
memcpy(&sin6, addr, sizeof(sin6));
|
|
|
|
if (sin6.sin6_family != AF_INET6)
|
|
return EAFNOSUPPORT;
|
|
|
|
/* Find the first zero bit. */
|
|
for (byte = 0; byte < __arraycount(sin6.sin6_addr.s6_addr);
|
|
byte++)
|
|
if (sin6.sin6_addr.s6_addr[byte] != 0xff)
|
|
break;
|
|
|
|
/* If all bits are set, there is nothing more to do. */
|
|
if (byte == __arraycount(sin6.sin6_addr.s6_addr)) {
|
|
*prefix = __arraycount(sin6.sin6_addr.s6_addr) * NBBY;
|
|
|
|
return OK;
|
|
}
|
|
|
|
for (bit = 0; bit < NBBY; bit++)
|
|
if (!(sin6.sin6_addr.s6_addr[byte] &
|
|
(1 << (NBBY - bit - 1))))
|
|
break;
|
|
|
|
*prefix = byte * NBBY + bit;
|
|
|
|
/* All bits after the first zero bit must also be zero. */
|
|
if (bit < NBBY && (sin6.sin6_addr.s6_addr[byte] &
|
|
((1 << (NBBY - bit - 1)) - 1)))
|
|
return EINVAL;
|
|
|
|
for (byte++; byte < __arraycount(sin6.sin6_addr.s6_addr);
|
|
byte++)
|
|
if (sin6.sin6_addr.s6_addr[byte] != 0)
|
|
return EINVAL;
|
|
|
|
if (ipaddr != NULL) {
|
|
ip_addr_set_zero_ip6(ipaddr);
|
|
|
|
memcpy(ip_2_ip6(ipaddr)->addr, &sin6.sin6_addr,
|
|
sizeof(ip_2_ip6(ipaddr)->addr));
|
|
}
|
|
|
|
return OK;
|
|
|
|
default:
|
|
panic("unknown IP address type: %u", type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate a raw network mask based on the given prefix length.
|
|
*/
|
|
void
|
|
addr_make_netmask(uint8_t * addr, socklen_t addr_len, unsigned int prefix)
|
|
{
|
|
unsigned int byte, bit;
|
|
|
|
byte = prefix / NBBY;
|
|
bit = prefix % NBBY;
|
|
|
|
assert(byte + !!bit <= addr_len);
|
|
|
|
if (byte > 0)
|
|
memset(addr, 0xff, byte);
|
|
if (bit != 0)
|
|
addr[byte++] = (uint8_t)(0xff << (NBBY - bit));
|
|
if (byte < addr_len)
|
|
memset(&addr[byte], 0, addr_len - byte);
|
|
}
|
|
|
|
/*
|
|
* Store a network mask as a sockaddr structure, in 'addr'. Before the call,
|
|
* 'addr_len' must be set to the memory size of 'addr'. The address type is
|
|
* given as 'type', and must be either IPADDR_TYPE_V4 or IPADDR_TYPE_V6. The
|
|
* prefix length from which to generate the network mask is given as 'prefix'.
|
|
* Upon return, 'addr_len' is set to the size of the resulting sockaddr
|
|
* structure.
|
|
*/
|
|
void
|
|
addr_put_netmask(struct sockaddr * addr, socklen_t * addr_len, uint8_t type,
|
|
unsigned int prefix)
|
|
{
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
|
|
switch (type) {
|
|
case IPADDR_TYPE_V4:
|
|
if (*addr_len < sizeof(sin))
|
|
panic("provided address buffer too small");
|
|
|
|
assert(prefix <= IP4_BITS);
|
|
|
|
memset(&sin, 0, sizeof(sin));
|
|
sin.sin_len = sizeof(sin);
|
|
sin.sin_family = AF_INET;
|
|
|
|
addr_make_netmask((uint8_t *)&sin.sin_addr.s_addr,
|
|
sizeof(sin.sin_addr.s_addr), prefix);
|
|
|
|
memcpy(addr, &sin, sizeof(sin));
|
|
*addr_len = sizeof(sin);
|
|
|
|
break;
|
|
|
|
case IPADDR_TYPE_V6:
|
|
if (*addr_len < sizeof(sin6))
|
|
panic("provided address buffer too small");
|
|
|
|
assert(prefix <= IP6_BITS);
|
|
|
|
memset(&sin6, 0, sizeof(sin6));
|
|
sin6.sin6_len = sizeof(sin6);
|
|
sin6.sin6_family = AF_INET6;
|
|
|
|
addr_make_netmask(sin6.sin6_addr.s6_addr,
|
|
sizeof(sin6.sin6_addr.s6_addr), prefix);
|
|
|
|
memcpy(addr, &sin6, sizeof(sin6));
|
|
*addr_len = sizeof(sin6);
|
|
|
|
break;
|
|
|
|
default:
|
|
panic("unknown IP address type: %u", type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Normalize the given address in 'src' to the given number of prefix bits,
|
|
* setting all other bits to zero. Return the result in 'dst'.
|
|
*/
|
|
void
|
|
addr_normalize(ip_addr_t * dst, const ip_addr_t * src, unsigned int prefix)
|
|
{
|
|
#if !defined(NDEBUG)
|
|
unsigned int addr_len;
|
|
#endif /* !defined(NDEBUG) */
|
|
unsigned int byte, bit;
|
|
const uint8_t *srcaddr;
|
|
uint8_t type, *dstaddr;
|
|
|
|
type = IP_GET_TYPE(src);
|
|
|
|
memset(dst, 0, sizeof(*dst));
|
|
IP_SET_TYPE(dst, type);
|
|
|
|
switch (type) {
|
|
case IPADDR_TYPE_V4:
|
|
srcaddr = (const uint8_t *)&ip_2_ip4(src)->addr;
|
|
dstaddr = (uint8_t *)&ip_2_ip4(dst)->addr;
|
|
#if !defined(NDEBUG)
|
|
addr_len = sizeof(ip_2_ip4(src)->addr);
|
|
#endif /* !defined(NDEBUG) */
|
|
|
|
break;
|
|
|
|
case IPADDR_TYPE_V6:
|
|
ip6_addr_set_zone(ip_2_ip6(dst), ip6_addr_zone(ip_2_ip6(src)));
|
|
|
|
srcaddr = (const uint8_t *)&ip_2_ip6(src)->addr;
|
|
dstaddr = (uint8_t *)&ip_2_ip6(dst)->addr;
|
|
#if !defined(NDEBUG)
|
|
addr_len = sizeof(ip_2_ip6(src)->addr);
|
|
#endif /* !defined(NDEBUG) */
|
|
|
|
break;
|
|
|
|
default:
|
|
panic("unknown IP address type: %u", type);
|
|
}
|
|
|
|
byte = prefix / NBBY;
|
|
bit = prefix % NBBY;
|
|
|
|
assert(byte + !!bit <= addr_len);
|
|
|
|
if (byte > 0)
|
|
memcpy(dstaddr, srcaddr, byte);
|
|
if (bit != 0) {
|
|
dstaddr[byte] =
|
|
srcaddr[byte] & (uint8_t)(0xff << (NBBY - bit));
|
|
byte++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the number of common bits between the given two addresses, up to the
|
|
* given maximum. Thus, return a value between 0 and 'max' inclusive.
|
|
*/
|
|
unsigned int
|
|
addr_get_common_bits(const ip_addr_t * ipaddr1, const ip_addr_t * ipaddr2,
|
|
unsigned int max)
|
|
{
|
|
unsigned int addr_len, prefix, bit;
|
|
const uint8_t *addr1, *addr2;
|
|
uint8_t byte;
|
|
|
|
switch (IP_GET_TYPE(ipaddr1)) {
|
|
case IPADDR_TYPE_V4:
|
|
assert(IP_IS_V4(ipaddr2));
|
|
|
|
addr1 = (const uint8_t *)&ip_2_ip4(ipaddr1)->addr;
|
|
addr2 = (const uint8_t *)&ip_2_ip4(ipaddr2)->addr;
|
|
addr_len = sizeof(ip_2_ip4(ipaddr1)->addr);
|
|
|
|
break;
|
|
|
|
case IPADDR_TYPE_V6:
|
|
assert(IP_IS_V6(ipaddr2));
|
|
|
|
addr1 = (const uint8_t *)&ip_2_ip6(ipaddr1)->addr;
|
|
addr2 = (const uint8_t *)&ip_2_ip6(ipaddr2)->addr;
|
|
addr_len = sizeof(ip_2_ip6(ipaddr1)->addr);
|
|
|
|
break;
|
|
|
|
default:
|
|
panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr1));
|
|
}
|
|
|
|
if (addr_len > max * NBBY)
|
|
addr_len = max * NBBY;
|
|
|
|
prefix = 0;
|
|
|
|
for (prefix = 0; addr_len > 0; addr1++, addr2++, prefix += NBBY) {
|
|
if ((byte = (*addr1 ^ *addr2)) != 0) {
|
|
/* TODO: see if we want a lookup table for this. */
|
|
for (bit = 0; bit < NBBY; bit++, prefix++)
|
|
if (byte & (1 << (NBBY - bit - 1)))
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prefix > max)
|
|
prefix = max;
|
|
|
|
return prefix;
|
|
}
|
|
|
|
/*
|
|
* Convert the given IPv4 address to an IPv4-mapped IPv6 address.
|
|
*/
|
|
void
|
|
addr_make_v4mapped_v6(ip_addr_t * dst, const ip4_addr_t * src)
|
|
{
|
|
|
|
IP_ADDR6(dst, 0, 0, PP_HTONL(0x0000ffffUL), ip4_addr_get_u32(src));
|
|
}
|