Mailing List Archive

[PATCH 4/6] bgpd: default originate for VPNv4 and ENCAP address family
This commit introduces default route facility for VPNv4 and ENCAP
subsequent address family.
When user sets a default route with a VPNv4 or ENCAP address family
to any neighbor, Route Distinguisher must be taken into account to fill
the MP_REACH_NLRI attribute of UPDATE message accordingly.
Reversely, when user withdraw a default route with a VPNv4 address family
to any neighbor, Route Distinguisher must be taken into account to fill
the MP_UNREACH_NLRI attribute of UPDATE message accordingly.
Since the default route is no stored in RIB, it must be stored in struct
peer to be able to send default route to peers that restart with support
of VPNv4 address family. When sending routes to a restarting BGP speaker,
there are as many UPDATE messages sent as the number of default route
for all RD configured for that peer.

Signed-off-by: Julien Courtat <julien.courtat@6wind.com>
---
bgpd/bgp_fsm.c | 6 +++
bgpd/bgp_packet.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
bgpd/bgp_packet.h | 5 ++
bgpd/bgp_route.c | 103 +++++++++++++++++++++++++++++++++++--
bgpd/bgp_route.h | 4 ++
bgpd/bgpd.c | 111 ++++++++++++++++++++++++++++++++++++++++
bgpd/bgpd.h | 29 +++++++----
7 files changed, 395 insertions(+), 12 deletions(-)

diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 2e07d93..e9ca0f7 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -584,6 +584,12 @@ bgp_stop (struct peer *peer)
peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
#endif /* 0 */

+ /* Delete all nodes of VPNv4 default routes list */
+ if (peer->def_route_rd)
+ {
+ list_delete_all_node (peer->def_route_rd);
+ }
+
return 0;
}

diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 8e315d1..f0f5273 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -534,6 +534,83 @@ bgp_default_update_send (struct peer *peer, struct attr *attr,
}

void
+bgp_default_update_vpnv4_send(struct peer *peer, struct prefix_rd *rd,
+ struct attr *attr, afi_t afi,
+ size_t nlabels, uint32_t *labels)
+{
+ struct bgp *bgp;
+ struct stream *s;
+ struct prefix p;
+ unsigned long pos_total_path_attr_len;
+ bgp_size_t total_attr_len;
+ size_t mpattrlen_pos = 0;
+ size_t pos_end = 0;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ if (afi == AFI_IP)
+ str2prefix ("0.0.0.0/0", &p);
+#ifdef HAVE_IPV6
+ else
+ str2prefix ("::/0", &p);
+#endif /* HAVE_IPV6 */
+
+ /* Logging the attribute. */
+ if (BGP_DEBUG (update, UPDATE_OUT))
+ {
+ char attrstr[BUFSIZ];
+ char buf[INET6_BUFSIZ];
+ attrstr[0] = '\0';
+
+ bgp_dump_attr (peer, attr, attrstr, BUFSIZ);
+ zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s",
+ peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ),
+ p.prefixlen, attrstr);
+ }
+
+ bgp = peer->bgp;
+
+ s = stream_new (BGP_MAX_PACKET_SIZE);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length. */
+ stream_putw (s, 0);
+
+ /* Make place for total attribute length. */
+ pos_total_path_attr_len = stream_get_endp (s);
+ stream_putw (s, 0);
+ total_attr_len = bgp_packet_attribute (NULL, peer, s,
+ attr, &p, afi, SAFI_MPLS_VPN,
+ bgp->peer_self, NULL, NULL, 0);
+ pos_end = stream_get_endp (s);
+
+ /* Encode the prefix in MP_REACH_NLRI attribute */
+ mpattrlen_pos = bgp_packet_mpattr_start(s, afi, SAFI_MPLS_VPN, attr);
+
+ bgp_packet_mpattr_prefix(s, afi, SAFI_MPLS_VPN, &p, rd, labels, nlabels);
+
+ /* set size of mp attributes at good position in UPDATE message */
+ bgp_packet_mpattr_end(s, mpattrlen_pos);
+
+ /* update total attribute length */
+ total_attr_len += (stream_get_endp (s) - pos_end);
+
+ /* Set Total Path Attribute Length. */
+ stream_putw_at (s, pos_total_path_attr_len, total_attr_len);
+
+ /* Set size. */
+ bgp_packet_set_size (s);
+
+ /* Add packet to the peer. */
+ bgp_packet_add (peer, s);
+
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
+void
bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
{
struct stream *s;
@@ -610,6 +687,78 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi)
BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
}

+void
+bgp_default_withdraw_vpnv4_send (struct peer *peer, afi_t afi, struct prefix_rd *rd)
+{
+ struct stream *s;
+ struct prefix p;
+ unsigned long attrlen_pos = 0;
+ bgp_size_t total_attr_len;
+ size_t mpattrlen_pos = 0;
+ size_t pos_end = 0;
+ uint32_t *labels = NULL;
+ size_t nlabels = 0;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ if (afi == AFI_IP)
+ str2prefix ("0.0.0.0/0", &p);
+#ifdef HAVE_IPV6
+ else
+ str2prefix ("::/0", &p);
+#endif /* HAVE_IPV6 */
+
+ total_attr_len = 0;
+
+ if (BGP_DEBUG (update, UPDATE_OUT))
+ {
+ char buf[INET6_BUFSIZ];
+
+ zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable",
+ peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ),
+ p.prefixlen);
+ }
+
+ /* Withdrawn Routes. */
+ /* Create UPDATE message needed based on the Route Distinguisher */
+ s = stream_new (BGP_MAX_PACKET_SIZE);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker (s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length. */
+ stream_putw (s, 0);
+
+ /* Make place for total attribute length. */
+ attrlen_pos = stream_get_endp (s);
+ stream_putw (s, 0);
+ pos_end = stream_get_endp (s);
+
+ /* start to build MP_REACH_NLRI attribute */
+ mpattrlen_pos = bgp_packet_mpunreach_start(s, afi, SAFI_MPLS_VPN);
+
+ /* Encode the prefix in MP_UNREACH_NLRI attribute */
+ bgp_packet_mpunreach_prefix(s, &p, afi, SAFI_MPLS_VPN, rd, labels, nlabels);
+
+ /* set size of mp attributes at good position in UPDATE message */
+ bgp_packet_mpunreach_end(s, mpattrlen_pos);
+
+ /* update total attribute length */
+ total_attr_len = (stream_get_endp (s) - pos_end);
+
+ /* Set Total Path Attribute Length. */
+ stream_putw_at (s, attrlen_pos, total_attr_len);
+
+ /* Set size. */
+ bgp_packet_set_size (s);
+
+ /* Add packet to the peer. */
+ bgp_packet_add (peer, s);
+
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+}
+
/* Get next packet to be written. */
static struct stream *
bgp_write_packet (struct peer *peer)
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 6b0b7f4..d68336f 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -51,8 +51,13 @@ extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int);
extern void bgp_default_update_send (struct peer *, struct attr *,
afi_t, safi_t, struct peer *);
extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t);
+extern void bgp_default_withdraw_vpnv4_send (struct peer *, afi_t,
+ struct prefix_rd *rd);

extern int bgp_capability_receive (struct peer *, bgp_size_t);
+extern void bgp_default_update_vpnv4_send (struct peer *peer, struct prefix_rd *rd,
+ struct attr *attr, afi_t afi,
+ size_t nlabels, uint32_t *labels);

extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *);

diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 7024590..cd96815 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -2857,6 +2857,53 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw)
aspath_unintern (&aspath);
}

+void
+bgp_default_originate_rd (struct peer *peer, afi_t afi, struct prefix_rd *rd,
+ struct bgp_nexthop *nh, size_t nlabels,
+ uint32_t *labels, int withdraw)
+{
+ if (withdraw)
+ {
+ int empty;
+
+ if (CHECK_FLAG (peer->af_sflags[afi][SAFI_MPLS_VPN], PEER_STATUS_DEFAULT_ORIGINATE))
+ bgp_default_withdraw_vpnv4_send (peer, afi, rd);
+
+ empty = (NULL == listnode_head(peer->def_route_rd));
+ if (empty)
+ UNSET_FLAG (peer->af_sflags[afi][SAFI_MPLS_VPN], PEER_STATUS_DEFAULT_ORIGINATE);
+ }
+ else
+ {
+ struct attr attr;
+ struct aspath *aspath;
+ struct attr_extra *ae;
+ struct bgp *bgp = peer->bgp;
+
+ bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+ aspath = attr.aspath;
+ attr.local_pref = bgp->default_local_pref;
+ ae = attr.extra;
+
+ if (nh)
+ {
+ memcpy (&attr.nexthop, &nh->v4, IPV4_MAX_BYTELEN);
+ ae->mp_nexthop_global_in = nh->v4;
+ }
+ else
+ ae->mp_nexthop_global_in = bgp->router_id;
+
+ if (! CHECK_FLAG (peer->af_sflags[afi][SAFI_MPLS_VPN], PEER_STATUS_DEFAULT_ORIGINATE))
+ {
+ SET_FLAG (peer->af_sflags[afi][SAFI_MPLS_VPN], PEER_STATUS_DEFAULT_ORIGINATE);
+ }
+ bgp_default_update_vpnv4_send(peer, rd, &attr, afi, nlabels, labels);
+
+ bgp_attr_extra_free (&attr);
+ aspath_unintern (&aspath);
+ }
+}
+
static void
bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
struct bgp_table *table, int rsclient)
@@ -2871,10 +2918,60 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi,
if (! table)
table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi];

- if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
- && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
- bgp_default_originate (peer, afi, safi, 0);
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
+ {
+ if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP))
+ bgp_default_originate (peer, afi, safi, 0);
+ else
+ {
+ /* Create as many UPDATE message needed for each Route Distinguisher */
+ for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn; rn = bgp_route_next (rn))
+ {
+ struct bgp_node *rm;
+ struct bgp_info *ri;

+ /* look for neighbor in tables */
+ if ((table = rn->info) != NULL)
+ {
+ int rd_header = 1;
+
+ for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm))
+ for (ri = rm->info; ri; ri = ri->next)
+ {
+ if (rd_header)
+ {
+ size_t nlabels = 0;
+ struct listnode *node;
+ struct bgp_vrf *vrf;
+ u_int32_t *labels = NULL;
+
+ /* get labels */
+ if (ri->extra)
+ {
+ labels = ri->extra->labels;
+ nlabels = ri->extra->nlabels;
+ }
+
+ rd_header = 0;
+
+ /* find nh in VRF list */
+ for (ALL_LIST_ELEMENTS_RO(peer->bgp->vrfs, node, vrf))
+ {
+ if (!prefix_rd_cmp((struct prefix_rd*)&rn->p,
+ &vrf->outbound_rd))
+ {
+ bgp_default_originate_rd (peer, afi,
+ (struct prefix_rd*)&rn->p,
+ &vrf->nh, nlabels, labels, 0);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
/* It's initialized in bgp_announce_[check|check_rsclient]() */
attr.extra = &extra;

diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 281785c..11931b9 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -210,6 +210,10 @@ extern void bgp_cleanup_routes (void);
extern void bgp_announce_route (struct peer *, afi_t, safi_t);
extern void bgp_announce_route_all (struct peer *);
extern void bgp_default_originate (struct peer *, afi_t, safi_t, int);
+extern void bgp_default_originate_rd (struct peer *peer, afi_t afi,
+ struct prefix_rd *rd, struct bgp_nexthop *nh,
+ size_t nlabels, uint32_t *labels,
+ int withdraw);
extern void bgp_soft_reconfig_in (struct peer *, afi_t, safi_t);
extern void bgp_soft_reconfig_rsclient (struct peer *, afi_t, safi_t);
extern void bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 5725730..64edefc 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -937,6 +937,9 @@ peer_create (union sockunion *su, struct bgp *bgp, as_t local_as,
if (! active && peer_active (peer))
bgp_timer_set (peer);

+ /* Initialize list of VPNv4 default routes */
+ peer->def_route_rd = list_new();
+
return peer;
}

@@ -1433,6 +1436,11 @@ peer_delete (struct peer *peer)
if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETING))
bgp_peer_clear_node_queue_drain_immediate(peer);

+ /* Delete list of VPNv4 default routes */
+ if (peer->def_route_rd)
+ {
+ list_delete (peer->def_route_rd);
+ }
peer_unlock (peer); /* initial reference */

return 0;
@@ -3478,6 +3486,109 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi)
}

int
+peer_default_originate_set_rd (struct peer *peer, struct prefix_rd *rd, afi_t afi,
+ struct bgp_nexthop *nh, size_t nlabels, uint32_t *labels)
+{
+ char rdstr[RD_ADDRSTRLEN];
+ char labelstr[RD_ADDRSTRLEN];
+ struct listnode *node;
+ struct bgp_vrf *vrf, *found = NULL;
+ struct prefix_rd* d;
+
+ prefix_rd2str(rd, rdstr, RD_ADDRSTRLEN);
+
+ /* Adress family must be activated. */
+ if (! peer->afc[afi][SAFI_MPLS_VPN])
+ return BGP_ERR_PEER_INACTIVE;
+
+ /* Default originate can't be used for peer group member. */
+ if (peer_is_group_member (peer, afi, SAFI_MPLS_VPN))
+ return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+ /* Check RD has been recorded for the peer */
+ for (ALL_LIST_ELEMENTS_RO(peer->bgp->vrfs, node, vrf))
+ {
+ if (0 == prefix_rd_cmp(rd, &vrf->outbound_rd))
+ {
+ found = vrf;
+ break;
+ }
+ }
+
+ if (!found)
+ return 1;
+
+ labels2str(labelstr, RD_ADDRSTRLEN, labels, nlabels);
+ zlog_info("%s: rd=%s, afi=%d, nh=%s, nlabels=%zu, labels=%s", __func__,
+ rdstr, afi, inet_ntoa(nh->v4), nlabels, nlabels? labelstr:"");
+
+ /* add this VRF in peer list of VPNv4 default route if not already present */
+ d = (struct prefix_rd*) listnode_lookup(peer->def_route_rd, found);
+ if (!d)
+ {
+ memcpy (&found->nh, nh, sizeof(*nh));
+ listnode_add(peer->def_route_rd, found);
+ }
+
+ if (!CHECK_FLAG (peer->af_flags[afi][SAFI_MPLS_VPN], PEER_FLAG_DEFAULT_ORIGINATE))
+ SET_FLAG (peer->af_flags[afi][SAFI_MPLS_VPN], PEER_FLAG_DEFAULT_ORIGINATE);
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ if (peer->status == Established && peer->afc_nego[afi][SAFI_MPLS_VPN])
+ bgp_default_originate_rd(peer, afi, rd, nh, nlabels, labels, 0);
+
+ return 0;
+}
+
+int
+peer_default_originate_unset_rd (struct peer *peer, afi_t afi, struct prefix_rd *rd)
+{
+ char rdstr[RD_ADDRSTRLEN];
+ struct listnode *node;
+ struct bgp_vrf *vrf, *found = NULL;
+ int empty;
+
+ /* Adress family must be activated. */
+ if (! peer->afc[afi][SAFI_MPLS_VPN])
+ return BGP_ERR_PEER_INACTIVE;
+
+ /* Default originate can't be used for peer group member. */
+ if (peer_is_group_member (peer, afi, SAFI_MPLS_VPN))
+ return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER;
+
+ /* Check RD has been recorded for the peer */
+ for (ALL_LIST_ELEMENTS_RO(peer->bgp->vrfs, node, vrf))
+ {
+ if (!prefix_rd_cmp(rd, &vrf->outbound_rd))
+ found = vrf;
+ }
+
+ if (!found)
+ return 1;
+
+ /* remove this RD in peer list of VPNv4 default route */
+ listnode_delete(peer->def_route_rd, found);
+ empty = (NULL == listnode_head(peer->def_route_rd));
+
+ /* if there is no more entry in list, unset flag PEER_FLAG_DEFAULT_ORIGINATE */
+ if (empty && CHECK_FLAG (peer->af_flags[afi][SAFI_MPLS_VPN], PEER_FLAG_DEFAULT_ORIGINATE))
+ {
+ UNSET_FLAG (peer->af_flags[afi][SAFI_MPLS_VPN], PEER_FLAG_DEFAULT_ORIGINATE);
+ zlog_debug("%s: Unset peer's flag PEER_FLAG_DEFAULT_ORIGINATE for afi=%d safi=%d",
+ __func__, afi, SAFI_MPLS_VPN);
+ }
+
+ prefix_rd2str(rd, rdstr, RD_ADDRSTRLEN);
+ zlog_info("%s: rd=%s, afi=%d", __func__, rdstr, afi);
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
+ if (peer->status == Established && peer->afc_nego[afi][SAFI_MPLS_VPN])
+ bgp_default_originate_rd (peer, afi, rd, NULL, 0, NULL, 1);
+
+ return 0;
+}
+
+int
peer_port_set (struct peer *peer, u_int16_t port)
{
peer->port = port;
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index b3ad648..1a7ea7a 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -208,6 +208,15 @@ struct bgp_rt_sub
struct list *vrfs;
};

+/* Next hop self address. */
+struct bgp_nexthop
+{
+ struct interface *ifp;
+ struct in_addr v4;
+ struct in6_addr v6_global;
+ struct in6_addr v6_local;
+};
+
struct bgp_vrf
{
struct bgp *bgp;
@@ -225,6 +234,9 @@ struct bgp_vrf
/* Static route configuration. */
struct bgp_table *route[AFI_MAX];

+ /* default route */
+ struct bgp_nexthop nh;
+
};

struct bgp_api_route
@@ -259,15 +271,6 @@ struct bgp_notify
bgp_size_t length;
};

-/* Next hop self address. */
-struct bgp_nexthop
-{
- struct interface *ifp;
- struct in_addr v4;
- struct in6_addr v6_global;
- struct in6_addr v6_local;
-};
-
#define RMAP_IN 0
#define RMAP_OUT 1
#define RMAP_IMPORT 2
@@ -471,6 +474,9 @@ struct peer
#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */
#define PEER_FLAG_NEXTHOP_SELF_ALL (1 << 17) /* next-hop-self all */

+ /* list of VPNv4 default route configured (bgp_vrf*) */
+ struct list *def_route_rd;
+
/* MD5 password */
char *password;

@@ -990,6 +996,11 @@ extern int peer_update_source_unset (struct peer *);

extern int peer_default_originate_set (struct peer *, afi_t, safi_t, const char *);
extern int peer_default_originate_unset (struct peer *, afi_t, safi_t);
+extern int peer_default_originate_set_rd (struct peer *peer, struct prefix_rd *rd,
+ afi_t afi, struct bgp_nexthop *nh,
+ size_t nlabels, uint32_t *labels);
+extern int peer_default_originate_unset_rd (struct peer *peer, afi_t afi,
+ struct prefix_rd *rd);

extern int peer_port_set (struct peer *, u_int16_t);
extern int peer_port_unset (struct peer *);
--
2.1.4


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev