// vim:sw=4:ts=4:et: /* * Copyright (C) 2021 Andrei Belov (@defanator on github) * */ #include #include #include #include #include #include #include #include #include /* 16 octets in binary form + dots between octets + trailing zero */ #define INET6_ADDRBINSTRLEN ((8 * 16) + (1 * 15) + 1UL) #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") #endif char * sprintb(char *dst, void const * const ptr, size_t const size) { int i, j; char *dp = dst; unsigned char byte; unsigned char *b = (unsigned char*) ptr; for (i = size - 1; i >= 0; i--) { for (j = 7; j >= 0; j--) { byte = (b[i] >> j) & 1; *dp++ = byte ? '1' : '0'; } } *dp = 0x0; return dst; } char * sprintb_addr6(char *dst, struct in6_addr *in6) { char *dp = dst; u_char *p = in6->s6_addr; int i; for (i = 0; i < 16; i++) { sprintb(dp, &p[i], sizeof(u_char)); dp += 8; if (i < 15) *dp++ = '.'; } return dst; } struct in6_addr ipv6_rand(char *net,uint8_t prefixlen) { int i, j, s; uint8_t bits, shift; u_char *addr, *mask; struct in6_addr addr6, mask6,rand6; //char straddr6[INET6_ADDRSTRLEN]; //char binaddr6[INET6_ADDRBINSTRLEN]; char *ip6net = net; if (inet_pton(AF_INET6, ip6net, &addr6) < 1) { printf("incorrect IPv6 address/net: \"%s\"\n", ip6net); exit(1); } addr = addr6.s6_addr; mask = mask6.s6_addr; shift = prefixlen; bits = 128 - shift; for (i = 0; i < 16; i++) { s = (shift > 8) ? 8 : shift; shift -= s; mask[i] = (u_char) (0xffu << (8 - s)); if (addr[i] != (addr[i] & mask[i])) { addr[i] &= mask[i]; } } //inet_ntop(AF_INET6, &addr6, straddr6, INET6_ADDRSTRLEN); //printf("network: %s/%d\n", straddr6, prefixlen); //inet_ntop(AF_INET6, &mask6, straddr6, INET6_ADDRSTRLEN); //printf("netmask: %s (free bits=%d)\n", straddr6, bits); //printf("%s\n", sprintb_addr6(binaddr6, &addr6)); //printf("%s\n", sprintb_addr6(binaddr6, &mask6)); //printf("----\n"); srand(((unsigned) getpid() << 16) ^ time(NULL)); uint32_t rv = rand(); int k = 0; shift = bits; rand6 = addr6; addr = rand6.s6_addr; for (j = 15; j >= 0; j--) { s = (shift > 8) ? 8 : shift; shift -= s; addr[j] = addr[j] ^ ((addr[j] ^ rv) & ~mask[j]); if (shift == 0) break; rv >>= 8; /* * Note that the MSB of the first octet in random value rv * will always be 0 as RAND_MAX=0x7FFFFFFF, i.e. if we refresh * rv after using all the 4 octets from uint32_t, the leading * bit in octets 4, 8, 12, 16 in generated IPv6 address * will _always_ be 0. * * While it seems legit for e.g. ::ffff:0:0/96 (IPv4-mapped * addresses), there may be a better way of handling this * (e.g. refresh rv after using 3 of 4 octets or reverse * bits in first octet before applying). * */ //if (++k > 2) { if (++k > 3) { rv = rand(); k = 0; } } //inet_ntop(AF_INET6, &rand6, straddr6, INET6_ADDRSTRLEN); //printf("%s [%s]\n", sprintb_addr6(binaddr6, &rand6), straddr6); return rand6; }