summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@diac24.net>2012-04-04 00:14:36 +0200
committerDavid Lamparter <equinox@diac24.net>2012-04-04 00:25:51 +0200
commite96b312150d8e376c1ef463793d1929eca3618d5 (patch)
tree33bdbba11475be746d7ebf684fd1441b9db4b929
parenta3537862f3c00b60fc52a67c1cc447c2a65f97bd (diff)
lib: pretty ip_masklen and masklen2ip
nonwithstanding any desire for optimisation, these versions are shorter and more concise. reading the comments, they might even be easier to understand. I've tested them on i686 and x86_64, and checked that correct assembler code is emitted for ARM, MIPS and PowerPC. IPv6 is left as an exercise for another day, none of the ideas I had led to a "yes, this is the one to go with" solution. Signed-off-by: David Lamparter <equinox@diac24.net>
-rw-r--r--lib/prefix.c128
1 files changed, 19 insertions, 109 deletions
diff --git a/lib/prefix.c b/lib/prefix.c
index 60e573a6..a3b1adf8 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -31,79 +31,6 @@
/* Maskbit. */
static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
0xf8, 0xfc, 0xfe, 0xff};
-static const u_int32_t maskbytes_big_endian[] =
-{
- 0x00000000, /* /0 0.0.0.0 */
- 0x80000000, /* /1 128.0.0.0 */
- 0xc0000000, /* /2 192.0.0.0 */
- 0xe0000000, /* /3 224.0.0.0 */
- 0xf0000000, /* /4 240.0.0.0 */
- 0xf8000000, /* /5 248.0.0.0 */
- 0xfc000000, /* /6 252.0.0.0 */
- 0xfe000000, /* /7 254.0.0.0 */
- 0xff000000, /* /8 255.0.0.0 */
- 0xff800000, /* /9 255.128.0.0 */
- 0xffc00000, /* /10 255.192.0.0 */
- 0xffe00000, /* /11 255.224.0.0 */
- 0xfff00000, /* /12 255.240.0.0 */
- 0xfff80000, /* /13 255.248.0.0 */
- 0xfffc0000, /* /14 255.252.0.0 */
- 0xfffe0000, /* /15 255.254.0.0 */
- 0xffff0000, /* /16 255.255.0.0 */
- 0xffff8000, /* /17 255.255.128.0 */
- 0xffffc000, /* /18 255.255.192.0 */
- 0xffffe000, /* /19 255.255.224.0 */
- 0xfffff000, /* /20 255.255.240.0 */
- 0xfffff800, /* /21 255.255.248.0 */
- 0xfffffc00, /* /22 255.255.252.0 */
- 0xfffffe00, /* /23 255.255.254.0 */
- 0xffffff00, /* /24 255.255.255.0 */
- 0xffffff80, /* /25 255.255.255.128 */
- 0xffffffc0, /* /26 255.255.255.192 */
- 0xffffffe0, /* /27 255.255.255.224 */
- 0xfffffff0, /* /28 255.255.255.240 */
- 0xfffffff8, /* /29 255.255.255.248 */
- 0xfffffffc, /* /30 255.255.255.252 */
- 0xfffffffe, /* /31 255.255.255.254 */
- 0xffffffff /* /32 255.255.255.255 */
-};
-
-static const u_int32_t maskbytes_little_endian[] =
-{
- 0x00000000, /* /0 0.0.0.0 */
- 0x00000080, /* /1 128.0.0.0 */
- 0x000000c0, /* /2 192.0.0.0 */
- 0x000000e0, /* /3 224.0.0.0 */
- 0x000000f0, /* /4 240.0.0.0 */
- 0x000000f8, /* /5 248.0.0.0 */
- 0x000000fc, /* /6 252.0.0.0 */
- 0x000000fe, /* /7 254.0.0.0 */
- 0x000000ff, /* /8 255.0.0.0 */
- 0x000080ff, /* /9 255.128.0.0 */
- 0x0000c0ff, /* /10 255.192.0.0 */
- 0x0000e0ff, /* /11 255.224.0.0 */
- 0x0000f0ff, /* /12 255.240.0.0 */
- 0x0000f8ff, /* /13 255.248.0.0 */
- 0x0000fcff, /* /14 255.252.0.0 */
- 0x0000feff, /* /15 255.254.0.0 */
- 0x0000ffff, /* /16 255.255.0.0 */
- 0x0080ffff, /* /17 255.255.128.0 */
- 0x00c0ffff, /* /18 255.255.192.0 */
- 0x00e0ffff, /* /19 255.255.224.0 */
- 0x00f0ffff, /* /20 255.255.240.0 */
- 0x00f8ffff, /* /21 255.255.248.0 */
- 0x00fcffff, /* /22 255.255.252.0 */
- 0x00feffff, /* /23 255.255.254.0 */
- 0x00ffffff, /* /24 255.255.255.0 */
- 0x80ffffff, /* /25 255.255.255.128 */
- 0xc0ffffff, /* /26 255.255.255.192 */
- 0xe0ffffff, /* /27 255.255.255.224 */
- 0xf0ffffff, /* /28 255.255.255.240 */
- 0xf8ffffff, /* /29 255.255.255.248 */
- 0xfcffffff, /* /30 255.255.255.252 */
- 0xfeffffff, /* /31 255.255.255.254 */
- 0xffffffff /* /32 255.255.255.255 */
-};
static const struct in6_addr maskbytes6[] =
{
@@ -527,11 +454,15 @@ void
masklen2ip (const int masklen, struct in_addr *netmask)
{
assert (masklen >= 0 && masklen <= IPV4_MAX_BITLEN);
-#if (BYTE_ORDER == LITTLE_ENDIAN)
- netmask->s_addr = maskbytes_little_endian[masklen];
-#elif (BYTE_ORDER == BIG_ENDIAN)
- netmask->s_addr = maskbytes_big_endian[masklen];
-#endif
+
+ /* left shift is only defined for less than the size of the type.
+ * we unconditionally use long long in case the target platform
+ * has defined behaviour for << 32 (or has a 64-bit left shift) */
+
+ if (sizeof(unsigned long long) > 4)
+ netmask->s_addr = htonl(0xffffffffULL << (32 - masklen));
+ else
+ netmask->s_addr = htonl(masklen ? 0xffffffffU << (32 - masklen) : 0);
}
/* Convert IP address's netmask into integer. We assume netmask is
@@ -539,43 +470,22 @@ masklen2ip (const int masklen, struct in_addr *netmask)
u_char
ip_masklen (struct in_addr netmask)
{
- u_char len;
- u_char *pnt;
- u_char *end;
- u_char val;
-
- len = 0;
- pnt = (u_char *) &netmask;
- end = pnt + 4;
-
- while ((pnt < end) && (*pnt == 0xff))
- {
- len+= 8;
- pnt++;
- }
-
- if (pnt < end)
- {
- val = *pnt;
- while (val)
- {
- len++;
- val <<= 1;
- }
- }
- return len;
+ uint32_t tmp = ~ntohl(netmask.s_addr);
+ if (tmp)
+ /* clz: count leading zeroes. sadly, the behaviour of this builtin
+ * is undefined for a 0 argument, even though most CPUs give 32 */
+ return __builtin_clz(tmp);
+ else
+ return 32;
}
/* Apply mask to IPv4 prefix (network byte order). */
void
apply_mask_ipv4 (struct prefix_ipv4 *p)
{
- assert (p->prefixlen >= 0 && p->prefixlen <= IPV4_MAX_BITLEN);
-#if (BYTE_ORDER == LITTLE_ENDIAN)
- p->prefix.s_addr &= maskbytes_little_endian[p->prefixlen];
-#elif (BYTE_ORDER == BIG_ENDIAN)
- p->prefix.s_addr &= maskbytes_big_endian[p->prefixlen];
-#endif
+ struct in_addr mask;
+ masklen2ip(p->prefixlen, &mask);
+ p->prefix.s_addr &= mask.s_addr;
}
/* If prefix is 0.0.0.0/0 then return 1 else return 0. */