diff options
-rw-r--r-- | bgpd/bgp_network.c | 63 | ||||
-rw-r--r-- | lib/prefix.c | 54 | ||||
-rw-r--r-- | lib/prefix.h | 2 |
3 files changed, 82 insertions, 37 deletions
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 1a8587e9..be01bbd6 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -233,46 +233,36 @@ bgp_bind (struct peer *peer) } static int -bgp_bind_address (int sock, struct in_addr *addr) +bgp_update_address (struct interface *ifp, const union sockunion *dst, + union sockunion *addr) { - int ret; - struct sockaddr_in local; - - memset (&local, 0, sizeof (struct sockaddr_in)); - local.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - local.sin_len = sizeof(struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - memcpy (&local.sin_addr, addr, sizeof (struct in_addr)); - - if ( bgpd_privs.change (ZPRIVS_RAISE) ) - zlog_err ("bgp_bind_address: could not raise privs"); - - ret = bind (sock, (struct sockaddr *)&local, sizeof (struct sockaddr_in)); - if (ret < 0) - ; - - if (bgpd_privs.change (ZPRIVS_LOWER) ) - zlog_err ("bgp_bind_address: could not lower privs"); - - return 0; -} - -static struct in_addr * -bgp_update_address (struct interface *ifp) -{ - struct prefix_ipv4 *p; + struct prefix *p, *sel, *d; struct connected *connected; struct listnode *node; + int common; + + d = sockunion2hostprefix (dst); + sel = NULL; + common = -1; for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) { - p = (struct prefix_ipv4 *) connected->address; - - if (p->family == AF_INET) - return &p->prefix; + p = connected->address; + if (p->family != d->family) + continue; + if (prefix_common_bits (p, d) > common) + { + sel = p; + common = prefix_common_bits (sel, d); + } } - return NULL; + + prefix_free (d); + if (!sel) + return 1; + + prefix2sockunion (sel, addr); + return 0; } /* Update source selection. */ @@ -280,7 +270,7 @@ static void bgp_update_source (struct peer *peer) { struct interface *ifp; - struct in_addr *addr; + union sockunion addr; /* Source is specified with interface name. */ if (peer->update_if) @@ -289,11 +279,10 @@ bgp_update_source (struct peer *peer) if (! ifp) return; - addr = bgp_update_address (ifp); - if (! addr) + if (bgp_update_address (ifp, &peer->su, &addr)) return; - bgp_bind_address (peer->fd, addr); + sockunion_bind (peer->fd, &addr, 0, &addr); } /* Source is specified with IP address. */ diff --git a/lib/prefix.c b/lib/prefix.c index c85e6594..f5de4bde 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -180,6 +180,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) @@ -569,6 +609,20 @@ 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) { diff --git a/lib/prefix.h b/lib/prefix.h index a7598b7e..f6259dee 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -156,12 +156,14 @@ extern int prefix2str (const struct prefix *, char *, int); extern int prefix_match (const struct prefix *, const struct prefix *); extern int prefix_same (const struct prefix *, const struct prefix *); extern int prefix_cmp (const struct prefix *, const struct prefix *); +extern int prefix_common_bits (const struct prefix *, const struct prefix *); extern void prefix_copy (struct prefix *dest, const struct prefix *src); extern void apply_mask (struct prefix *); extern struct prefix *sockunion2prefix (const union sockunion *dest, const union sockunion *mask); extern struct prefix *sockunion2hostprefix (const union sockunion *); +extern void prefix2sockunion (const struct prefix *, union sockunion *); extern struct prefix_ipv4 *prefix_ipv4_new (void); extern void prefix_ipv4_free (struct prefix_ipv4 *); |