diff options
Diffstat (limited to 'lib/prefix.c')
-rw-r--r-- | lib/prefix.c | 241 |
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 */ + |