summaryrefslogtreecommitdiffstats
path: root/lib/prefix.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/prefix.c')
-rw-r--r--lib/prefix.c241
1 files changed, 162 insertions, 79 deletions
diff --git a/lib/prefix.c b/lib/prefix.c
index 61a278ca..867c86a3 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
+ * 02111-1307, USA.
*/
#include <zebra.h>
@@ -27,10 +27,10 @@
#include "sockunion.h"
#include "memory.h"
#include "log.h"
-
+
/* Maskbit. */
-static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
- 0xf8, 0xfc, 0xfe, 0xff};
+static const u_char maskbit[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xff };
/* Number of bits in prefix type. */
#ifndef PNBBY
@@ -86,7 +86,7 @@ prefix_match (const struct prefix *n, const struct prefix *p)
if (shift)
if (maskbit[shift] & (np[offset] ^ pp[offset]))
return 0;
-
+
while (offset--)
if (np[offset] != pp[offset])
return 0;
@@ -119,7 +119,7 @@ prefix_copy (struct prefix *dest, const struct prefix *src)
}
}
-/*
+/*
* Return 1 if the address/netmask contained in the prefix structure
* is the same, and else return 0. For this routine, 'same' requires
* that not only the prefix length and the network part be the same,
@@ -181,6 +181,46 @@ prefix_cmp (const struct prefix *p1, const struct prefix *p2)
return 0;
}
+/*
+ * Count the number of common bits in 2 prefixes. The prefix length is
+ * ignored for this function; the whole prefix is compared. If the prefix
+ * address families don't match, return -1; otherwise the return value is
+ * in range 0 ... maximum prefix length for the address family.
+ */
+int
+prefix_common_bits (const struct prefix *p1, const struct prefix *p2)
+{
+ int pos, bit;
+ int length = 0;
+ u_char xor;
+
+ /* Set both prefix's head pointer. */
+ const u_char *pp1 = (const u_char *)&p1->u.prefix;
+ const u_char *pp2 = (const u_char *)&p2->u.prefix;
+
+ if (p1->family == AF_INET)
+ length = IPV4_MAX_BYTELEN;
+#ifdef HAVE_IPV6
+ if (p1->family == AF_INET6)
+ length = IPV6_MAX_BYTELEN;
+#endif
+ if (p1->family != p2->family || !length)
+ return -1;
+
+ for (pos = 0; pos < length; pos++)
+ if (pp1[pos] != pp2[pos])
+ break;
+ if (pos == length)
+ return pos * 8;
+
+ xor = pp1[pos] ^ pp2[pos];
+ for (bit = 0; bit < 8; bit++)
+ if (xor & (1 << (7 - bit)))
+ break;
+
+ return pos * 8 + bit;
+}
+
/* Return prefix family type string. */
const char *
prefix_family_str (const struct prefix *p)
@@ -215,50 +255,58 @@ prefix_ipv4_free (struct prefix_ipv4 *p)
prefix_free((struct prefix *)p);
}
-/* When string format is invalid return 0. */
+/* When string format is valid return 1 otherwise return 0.
+ *
+ * inet_aton() returns 1 <=> valid, 0 <=> invalid.
+ * inet_pton() returns 1 <=> valid, 0 <=> invalid, -1 <=> error
+ * where error => unknown address family argument
+ *
+ * Callers of this function vary in how they test the return:
+ *
+ * 1) some treat non-0 as OK and 0 as invalid -- consistent with inet_aton().
+ *
+ * 2) some treat > 0 as OK and <= 0 as invalid -- consistent with inet_pton().
+ *
+ * Since this function returns 1 <=> valid and 0 <=> invalid, both the above
+ * work.
+ */
int
str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p)
{
- int ret;
- int plen;
- char *pnt;
- char *cp;
+ char* pnt ;
+ char* cp ;
+ int ret ;
+ unsigned plen ;
- /* Find slash inside string. */
pnt = strchr (str, '/');
- /* String doesn't contail slash. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
- /* Convert string to prefix. */
+ /* No / => simple address */
+ plen = IPV4_MAX_BITLEN;
ret = inet_aton (str, &p->prefix);
- if (ret == 0)
- return 0;
-
- /* If address doesn't contain slash we assume it host address. */
- p->family = AF_INET;
- p->prefixlen = IPV4_MAX_BITLEN;
-
- return ret;
}
else
{
+ /* With / => prefix */
+ plen = (unsigned)atoi (pnt + 1) ;
+ if (plen > IPV4_MAX_PREFIXLEN)
+ return 0;
+
cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
strncpy (cp, str, pnt - str);
*(cp + (pnt - str)) = '\0';
ret = inet_aton (cp, &p->prefix);
XFREE (MTYPE_TMP, cp);
+ }
- /* Get prefix length. */
- plen = (u_char) atoi (++pnt);
- if (plen > IPV4_MAX_PREFIXLEN)
- return 0;
+ if (ret <= 0) /* should not return < 0, but it would not be valid ! */
+ return 0;
- p->family = AF_INET;
- p->prefixlen = plen;
- }
+ p->family = AF_INET;
+ p->prefixlen = plen;
- return ret;
+ return 1 ;
}
/* Convert masklen into IP address's netmask. */
@@ -274,7 +322,7 @@ masklen2ip (int masklen, struct in_addr *netmask)
offset = masklen / 8;
bit = masklen % 8;
-
+
while (offset--)
*pnt++ = 0xff;
@@ -300,7 +348,7 @@ ip_masklen (struct in_addr netmask)
{
len+= 8;
pnt++;
- }
+ }
if (pnt < end)
{
@@ -343,7 +391,7 @@ prefix_ipv4_any (const struct prefix_ipv4 *p)
{
return (p->prefix.s_addr == 0 && p->prefixlen == 0);
}
-
+
#ifdef HAVE_IPV6
/* Allocate a new ip version 6 route */
@@ -366,43 +414,61 @@ prefix_ipv6_free (struct prefix_ipv6 *p)
prefix_free((struct prefix *)p);
}
-/* If given string is valid return pin6 else return NULL */
+/* If given string is valid IPv6 address or prefix return 1 else return 0
+ *
+ * inet_aton() returns 1 <=> valid, 0 <=> invalid.
+ * inet_pton() returns 1 <=> valid, 0 <=> invalid, -1 <=> error
+ * where error => unknown address family argument
+ *
+ * Any error returned by inet_pton() is reported as an invalid address or
+ * prefix. So best not to call this if IPv6 is not supported.
+ *
+ * Callers of this function vary in how they test the return:
+ *
+ * 1) some treat non-0 as OK and 0 as invalid -- consistent with inet_aton().
+ *
+ * 2) some treat > 0 as OK and <= 0 as invalid -- consistent with inet_pton().
+ *
+ * Since this function returns 1 <=> valid and 0 <=> invalid, both the above
+ * work.
+ */
int
str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p)
{
- char *pnt;
- char *cp;
- int ret;
+ char* pnt ;
+ char* cp ;
+ int ret ;
+ unsigned plen ;
pnt = strchr (str, '/');
- /* If string doesn't contain `/' treat it as host route. */
- if (pnt == NULL)
+ if (pnt == NULL)
{
- ret = inet_pton (AF_INET6, str, &p->prefix);
- if (ret == 0)
- return 0;
- p->prefixlen = IPV6_MAX_BITLEN;
+ /* No / => simple address */
+ plen = IPV6_MAX_BITLEN;
+ ret = inet_pton (AF_INET6, str, &p->prefix);
}
- else
+ else
{
- int plen;
+ /* With / => prefix */
+ plen = (unsigned) atoi (pnt + 1) ;
+ if (plen > IPV6_MAX_PREFIXLEN)
+ return 0 ;
- cp = XMALLOC (0, (pnt - str) + 1);
+ cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1);
strncpy (cp, str, pnt - str);
*(cp + (pnt - str)) = '\0';
ret = inet_pton (AF_INET6, cp, &p->prefix);
- free (cp);
- if (ret == 0)
- return 0;
- plen = (u_char) atoi (++pnt);
- if (plen > 128)
- return 0;
- p->prefixlen = plen;
+ XFREE (MTYPE_TMP, cp);
}
- p->family = AF_INET6;
- return ret;
+ if (ret <= 0)
+ return 0 ;
+
+ p->family = AF_INET6;
+ p->prefixlen = plen;
+
+ return 1 ;
}
/* Convert struct in6_addr netmask into integer.
@@ -413,19 +479,19 @@ ip6_masklen (struct in6_addr netmask)
int len = 0;
unsigned char val;
unsigned char *pnt;
-
+
pnt = (unsigned char *) & netmask;
- while ((*pnt == 0xff) && len < 128)
+ while ((*pnt == 0xff) && len < 128)
{
len += 8;
pnt++;
- }
-
- if (len < 128)
+ }
+
+ if (len < 128)
{
val = *pnt;
- while (val)
+ while (val)
{
len++;
val <<= 1;
@@ -570,10 +636,23 @@ sockunion2hostprefix (const union sockunion *su)
return NULL;
}
+void
+prefix2sockunion (const struct prefix *p, union sockunion *su) {
+ memset (su, 0, sizeof (*su));
+
+ su->sa.sa_family = p->family;
+ if (p->family == AF_INET)
+ su->sin.sin_addr = p->u.prefix4;
+#ifdef HAVE_IPV6
+ if (p->family == AF_INET6)
+ memcpy (&su->sin6.sin6_addr, &p->u.prefix6, sizeof (struct in6_addr));
+#endif /* HAVE_IPV6 */
+}
+
int
prefix_blen (const struct prefix *p)
{
- switch (p->family)
+ switch (p->family)
{
case AF_INET:
return IPV4_MAX_BYTELEN;
@@ -587,25 +666,28 @@ prefix_blen (const struct prefix *p)
return 0;
}
-/* Generic function for conversion string to struct prefix. */
+/* Generic function for conversion string to struct prefix.
+ *
+ * Accepts addresses without '/' and prefixes with.
+ *
+ * Returns 1 <=> valid IPv4 or (if HAVE_IPV6) IPv6 address or prefix.
+ * 0 <=> not a a valid address or prefix
+ */
int
str2prefix (const char *str, struct prefix *p)
{
int ret;
- /* First we try to convert string to struct prefix_ipv4. */
+ /* First we try to convert string to struct prefix_ipv4. */
ret = str2prefix_ipv4 (str, (struct prefix_ipv4 *) p);
- if (ret)
- return ret;
#ifdef HAVE_IPV6
- /* Next we try to convert string to struct prefix_ipv6. */
- ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
- if (ret)
- return ret;
+ /* If not IPv4, try to convert to struct prefix_ipv6. */
+ if (ret == 0)
+ ret = str2prefix_ipv6 (str, (struct prefix_ipv6 *) p);
#endif /* HAVE_IPV6 */
- return 0;
+ return ret;
}
int
@@ -651,22 +733,22 @@ void apply_classful_mask_ipv4 (struct prefix_ipv4 *p)
{
u_int32_t destination;
-
+
destination = ntohl (p->prefix.s_addr);
-
+
if (p->prefixlen == IPV4_MAX_PREFIXLEN);
/* do nothing for host routes */
- else if (IN_CLASSC (destination))
+ else if (IN_CLASSC (destination))
{
p->prefixlen=24;
apply_mask_ipv4(p);
}
- else if (IN_CLASSB(destination))
+ else if (IN_CLASSB(destination))
{
p->prefixlen=16;
apply_mask_ipv4(p);
}
- else
+ else
{
p->prefixlen=8;
apply_mask_ipv4(p);
@@ -695,7 +777,7 @@ ipv4_broadcast_addr (in_addr_t hostaddr, int masklen)
(hostaddr ^ ~mask.s_addr);
}
-/* Utility function to convert ipv4 netmask to prefixes
+/* Utility function to convert ipv4 netmask to prefixes
ex.) "1.1.0.0" "255.255.0.0" => "1.1.0.0/16"
ex.) "1.0.0.0" NULL => "1.0.0.0/8" */
int
@@ -720,7 +802,7 @@ netmask_str2prefix_str (const char *net_str, const char *mask_str,
prefixlen = ip_masklen (mask);
}
- else
+ else
{
destination = ntohl (network.s_addr);
@@ -752,3 +834,4 @@ inet6_ntoa (struct in6_addr addr)
return buf;
}
#endif /* HAVE_IPV6 */
+