summaryrefslogtreecommitdiff
path: root/proto/bgp/packets.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/bgp/packets.c')
-rw-r--r--proto/bgp/packets.c41
1 files changed, 36 insertions, 5 deletions
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index c3a86731..bbb0865f 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -318,6 +318,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
#else /* IPv6 version */
+static inline int
+same_iface(struct bgp_proto *p, ip_addr *ip)
+{
+ neighbor *n = neigh_find(&p->p, ip, 0);
+ return n && p->neigh && n->iface == p->neigh->iface;
+}
+
static byte *
bgp_create_update(struct bgp_conn *conn, byte *buf)
{
@@ -329,7 +336,6 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
ip_addr *ipp, ip, ip_ll;
ea_list *ea;
eattr *nh;
- neighbor *n;
put_u16(buf, 0);
w = buf+4;
@@ -399,10 +405,13 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
* link local address seems to be a natural way to solve that
* problem, but it is contrary to RFC 2545 and Quagga does not
* accept such routes.
+ *
+ * There are two cases, either we have global IP, or
+ * IPA_NONE if the neighbor is link-local. For IPA_NONE,
+ * we suppose it is on the same iface, see bgp_update_attrs().
*/
- n = neigh_find(&p->p, &ip, 0);
- if (n && p->neigh && n->iface == p->neigh->iface)
+ if (ipa_zero(ip) || same_iface(p, &ip))
{
if (second && ipa_nonzero(ipp[1]))
ip_ll = ipp[1];
@@ -434,6 +443,9 @@ bgp_create_update(struct bgp_conn *conn, byte *buf)
*tmp++ = BGP_AF_IPV6;
*tmp++ = 1;
+ if (ipa_has_link_scope(ip))
+ ip = IPA_NONE;
+
if (ipa_nonzero(ip_ll))
{
*tmp++ = 32;
@@ -809,13 +821,27 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
#ifdef IPV6
int second = (nh->u.ptr->length == NEXT_HOP_LENGTH);
+
+ /* First address should not be link-local, but may be zero in direct mode */
+ if (ipa_has_link_scope(*nexthop))
+ *nexthop = IPA_NONE;
#else
int second = 0;
#endif
if (p->cf->gw_mode == GW_DIRECT)
{
- neighbor *ng = neigh_find(&p->p, nexthop, 0) ? : p->neigh;
+ neighbor *ng;
+
+ if (ipa_nonzero(*nexthop))
+ ng = neigh_find(&p->p, nexthop, 0);
+ else if (second) /* GW_DIRECT -> single_hop -> p->neigh != NULL */
+ ng = neigh_find2(&p->p, nexthop + 1, p->neigh->iface, 0);
+
+ /* Fallback */
+ if (!ng)
+ ng = p->neigh;
+
if (ng->scope == SCOPE_HOST)
return 0;
@@ -826,7 +852,12 @@ bgp_set_next_hop(struct bgp_proto *p, rta *a)
a->igp_metric = 0;
}
else /* GW_RECURSIVE */
- rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
+ {
+ if (ipa_zero(*nexthop))
+ return 0;
+
+ rta_set_recursive_next_hop(p->p.table, a, p->igp_table, nexthop, nexthop + second);
+ }
return 1;
}