iyasar
iyasar

Reputation: 1271

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?

Upvotes: 8

Views: 11811

Answers (7)

iyasar
iyasar

Reputation: 1271

this is quick and dirty solution, the source code is below if you want, use it. Warning ! the function assumes the IPv6 address is valid.

typedef uint16_t ip6_addr[8];


void ipv6_apply_mask(const char *ip6addr, unsigned int mask, ip6_addr ip6){

    ip6_addr in_ip6;
    inet_pton(PF_INET6, ip6addr, ip6);


    for(int i = 0; i < 8; i++){
        in_ip6[i] = ntohs(ip6[i]);
    }

    int index = (int) (mask / 16);
    int residue = mask % 16;

     if(residue == 0 && index == 8)
      return;

     switch(residue){
        case 0:in_ip6[index++] = 0; break;
        case 1:in_ip6[index++]&=0x8000; break;
        case 2:in_ip6[index++]&=0xc000; break;
        case 3:in_ip6[index++]&=0xe000; break;
        case 4:in_ip6[index++]&=0xf000; break;

        case 5:in_ip6[index++]&=0xf800; break;
        case 6:in_ip6[index++]&=0xfc00; break;
        case 7:in_ip6[index++]&=0xfe00; break;
        case 8:in_ip6[index++]&=0xff00; break;

        case  9:in_ip6[index++]&=0xff80; break;
        case 10:in_ip6[index++]&=0xffc0; break;
        case 11:in_ip6[index++]&=0xffe0; break;
        case 12:in_ip6[index++]&=0xfff0; break;

        case 13:in_ip6[index++]&=0xfff8; break;
        case 14:in_ip6[index++]&=0xfffc; break;
        case 15:in_ip6[index++]&=0xfffe; break;
    }

    for (int i = index; i < 8; i++){
       in_ip6[i] = 0;
    }

    for(int i = 0; i < 8; i++){
       ip6[i] = htons(in_ip6[i]);
    } 

 return;
}

Upvotes: 0

Damien
Damien

Reputation: 1538

See the functions in6_addr_mask and in6_addr_start from in6calc.c.

Example usage:

struct in6_addr addr, mask, start;
char addr_buffer[INET6_ADDRSTRLEN] = "2001:470:1f15:1bcd:34::41";
uint8_t bits = 96; 

inet_pton(AF_INET6, addr_buffer, &addr);

in6_addr_mask(&mask, bits); // Mask
in6_addr_start(&start, &addr, &mask); // Bitwise &

inet_ntop(AF_INET6, &start, addr_buffer, INET6_ADDRSTRLEN);
printf("Network:    %s\n", addr_buffer);

Yields:

Network:    2001:470:1f15:1bcd:34::

Upvotes: 0

lgekman
lgekman

Reputation: 6932

I searched for this function but the above seem to complex so I wrote my own;

// Clear all bits above the prefix
void mask_ipv6(struct in6_addr* a, unsigned prefix)
{
    assert(prefix <= 128);
    unsigned bits_to_clear = 128 - prefix;
    unsigned i = 3;
    while (bits_to_clear >= 32) {
        a->s6_addr32[i] = 0;
        bits_to_clear -= 32;
        i--;
    }
    if (bits_to_clear == 0)
        return;
    uint32_t mask = htonl(~((1 << bits_to_clear) - 1));
    a->s6_addr32[i] &= mask;
}

You may of course use a function name more of your liking.

Upvotes: 0

Steve-o
Steve-o

Reputation: 12866

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;
}

Upvotes: 2

benofbrown
benofbrown

Reputation: 71

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;
}

Upvotes: 2

Sander Steffann
Sander Steffann

Reputation: 9978

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

Upvotes: 1

Djole
Djole

Reputation: 1145

Threat the IP lik a 16 bytes array, skip the masked/8 bytes in the next byte mask the higher masked%8 bits, set the other ones to 0

int offset=masked/8;
char remmask=0;
int rem = masked%8;
while(rem)
{
   rem--;
   remmask|= 0x80>>rem; //0x80 is the highest bit in a byte set

}
offset++;
(((char*)ipv6)+offset) &= remmask;
while(offset<16)
{
   (((char*)ipv6)+offset=0;
   offset++;
}

Wrote the code right here, so it hasn't been tested but I am thinking you could use something like this

Upvotes: 0

Related Questions