I want to ask about calculation of ipv6 network and host side.
For example, I have the IPv6 address 2001:470:1f15:1bcd:34::41
and prefix 96
.
Do you know a easy way to do bitwise and
between IPv6 address and prefix?
According to IPv4:
192.168.1.2 255.255.255.0 network : 192.168.1.0
So simple.
I want to do the same thing to IPv6 address. But IPv6 address is 16 bytes, so you can't use unsigned int
for that.
Is there any API to do this? Or should I use arrays?
The /prefix-length variable is a decimal value that indicates the number of contiguous, higher-order bits of the address that make up the network portion of the address. For example, 2001:DB8::/32 is an IPv6 address prefix, indicating that the first 32 bits make up the network portion of the address.
IPv6 doesn't have a subnet mask but instead calls it a Prefix Length, often shortened to “Prefix”. Prefix length and CIDR masking work similarly; The prefix length denotes how many bits of the address define the network in which it exists.
The example IPv6 address has the following subnet prefix. The subnet prefix always contains 64 bits. These bits include 48 bits for the site prefix, in addition to 16 bits for the subnet ID.
For information on CIDR format IP addresses, refer to Designing Your CIDR IPv4 Addressing Scheme. The prefix 2001:db8::/32 is a special IPv6 prefix that is used specifically for documentation examples. You can also specify a subnet prefix, which defines the internal topology of the network to a router.
OK, I did this in C rather than C++, but it should work. Also, it uses bswap_64
which is AFAIK a GNU extension so may not work on everything.
It seems to be very quick on amd64, and faster than the current solution Yasar has come up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <arpa/inet.h>
#if defined __GNUC__ && __GNUC__ >= 2
#include <byteswap.h>
#else
#error "Sorry, you need GNU for this"
#endif
struct split
{
uint64_t start;
uint64_t end;
};
void ipv6_prefix (unsigned char *masked, unsigned char *packed, int prefix)
{
struct split parts;
uint64_t mask = 0;
unsigned char *p = masked;
memset(masked, 0, sizeof(struct in6_addr));
memcpy(&parts, packed, sizeof(parts));
if (prefix <= 64)
{
mask = bswap_64(bswap_64(parts.start) & ((uint64_t) (~0) << (64 - prefix)));
memcpy(masked, &mask, sizeof(uint64_t));
return;
}
prefix -= 64;
memcpy(masked, &(parts.start), sizeof(uint64_t));
p += sizeof(uint64_t);
mask = bswap_64(bswap_64(parts.end) & (uint64_t) (~0) << (64 - prefix));
memcpy(p, &mask, sizeof(uint64_t));
}
int main (int argc, char **argv)
{
unsigned char packed[sizeof(struct in6_addr)];
unsigned char masked[sizeof(struct in6_addr)];
char buf[INET6_ADDRSTRLEN], *p;
int prefix = 56;
if (argc < 2)
return 1;
if ((p = strchr(argv[1], '/')))
{
*p++ = '\0';
prefix = atoi(p);
}
inet_pton(AF_INET6, argv[1], packed);
ipv6_prefix(masked, packed, prefix);
inet_ntop(AF_INET6, masked, buf, INET6_ADDRSTRLEN);
printf("prefix = %s/%d\n", buf, prefix);
return 0;
}
Calculate mask from prefix length:
struct sockaddr_in6 netmask;
for (long i = prefixLength, j = 0; i > 0; i -= 8, ++j)
netmask.sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff
: (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU );
Apply netmask to address, I derived this from inet_lnaof
.
bool
inet6_lnaof (
struct in6_addr* restrict dst,
const struct in6_addr* restrict src,
const struct in6_addr* restrict netmask
)
{
bool has_lna = FALSE;
assert (NULL != dst);
assert (NULL != src);
assert (NULL != netmask);
for (unsigned i = 0; i < 16; i++) {
dst->s6_addr[i] = src->s6_addr[i] & netmask->s6_addr[i];
has_lna |= (0 != (src->s6_addr[i] & !netmask->s6_addr[i]));
}
return has_lna;
}
You can convert the address to binary in network byte order with inet_pton. Then set/clear the bits one byte at a time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With