Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is there any code for bitwise and ipv6 address and network mask (prefix)?

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?

like image 375
iyasar Avatar asked Aug 23 '11 09:08

iyasar


People also ask

What is the prefix of an IPv6 address?

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.

Does IPv6 have a mask?

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.

How many bits are allocated for the prefix of the IPv6 address?

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.

What is the IPv6 prefix of the address 2001?

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.


3 Answers

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;
}
like image 189
benofbrown Avatar answered Nov 15 '22 06:11

benofbrown


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;
}
like image 26
Steve-o Avatar answered Nov 15 '22 04:11

Steve-o


You can convert the address to binary in network byte order with inet_pton. Then set/clear the bits one byte at a time.

like image 35
Sander Steffann Avatar answered Nov 15 '22 06:11

Sander Steffann