Handle multiprotocol UPDATE.

Add IPv6 routes advertisement handling, with MP path attributes heading
prepared on initialization.
BTW, fix a bug in attribute size calculation (for extended attr).

Signed-off-by: Benjamin Cama <benoar@dolka.fr>
This commit is contained in:
Benjamin Cama 2011-07-20 14:45:09 +02:00
parent 59411b8799
commit db276bcc80
2 changed files with 222 additions and 1 deletions

201
bgp.c
View file

@ -250,6 +250,51 @@ int bgp_start(struct bgp_peer *peer, char *name, int as, int keepalive,
memcpy(peer->path_attrs, path_attrs, peer->path_attr_len); memcpy(peer->path_attrs, path_attrs, peer->path_attr_len);
/* multiprotocol attributes initialization */
if (config->ipv6_prefix.s6_addr[0])
{
struct bgp_attr_mp_reach_nlri_partial mp_reach_nlri_partial;
struct bgp_attr_mp_unreach_nlri_partial mp_unreach_nlri_partial;
a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL;
a.code = BGP_PATH_ATTR_CODE_MP_REACH_NLRI;
a.data.s.len = 0; /* will be set on UPDATE */
mp_reach_nlri_partial.afi = htons(AF_INET6);
mp_reach_nlri_partial.safi = BGP_MP_SAFI_UNICAST;
mp_reach_nlri_partial.reserved = 0;
mp_reach_nlri_partial.next_hop_len = 16;
/* use the defined nexthop6, or our address in ipv6_prefix */
if (config->nexthop6_address.s6_addr[0])
memcpy(&mp_reach_nlri_partial.next_hop,
&config->nexthop6_address.s6_addr, 16);
else
{
/* our address is ipv6prefix::1 */
memcpy(&mp_reach_nlri_partial.next_hop,
&config->ipv6_prefix.s6_addr, 16);
mp_reach_nlri_partial.next_hop[15] = 1;
}
memcpy(&a.data.s.value, &mp_reach_nlri_partial,
sizeof(struct bgp_attr_mp_reach_nlri_partial));
memcpy(&peer->mp_reach_nlri_partial, &a,
BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE);
a.flags = BGP_PATH_ATTR_FLAG_OPTIONAL | BGP_PATH_ATTR_FLAG_EXTLEN;
a.code = BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI;
a.data.e.len = 0; /* will be set on UPDATE */
mp_unreach_nlri_partial.afi = htons(AF_INET6);
mp_unreach_nlri_partial.safi = BGP_MP_SAFI_UNICAST;
memcpy(&a.data.e.value, &mp_unreach_nlri_partial,
sizeof(struct bgp_attr_mp_unreach_nlri_partial));
memcpy(&peer->mp_unreach_nlri_partial, &a,
BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE);
}
LOG(4, 0, 0, "Initiating BGP connection to %s (routing %s)\n", LOG(4, 0, 0, "Initiating BGP connection to %s (routing %s)\n",
name, enable ? "enabled" : "suspended"); name, enable ? "enabled" : "suspended");
@ -1492,6 +1537,162 @@ static int bgp_send_update(struct bgp_peer *peer)
/* send/buffer UPDATE message for IPv6 routes */ /* send/buffer UPDATE message for IPv6 routes */
static int bgp_send_update6(struct bgp_peer *peer) static int bgp_send_update6(struct bgp_peer *peer)
{ {
uint16_t unf_len = 0;
uint16_t attr_len;
char *unreach_len;
uint8_t reach_len;
uint16_t len = sizeof(peer->outbuf->packet.header);
struct bgp_route6_list *have = peer->routes6;
struct bgp_route6_list *want = peer->routing ? bgp_routes6 : 0;
struct bgp_route6_list *e = 0;
struct bgp_route6_list *add = 0;
int s;
char ipv6addr[INET6_ADDRSTRLEN];
char *data = (char *) &peer->outbuf->packet.data;
/* need leave room for attr_len, bgp_path_attrs and one prefix */
char *max = (char *) &peer->outbuf->packet.data
+ sizeof(peer->outbuf->packet.data)
- sizeof(attr_len) - peer->path_attr_len_without_nexthop
- BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE - sizeof(struct bgp_ip6_prefix);
memset(peer->outbuf->packet.header.marker, 0xff,
sizeof(peer->outbuf->packet.header.marker));
peer->outbuf->packet.header.type = BGP_MSG_UPDATE;
/* insert non-MP unf_len */
memcpy(data, &unf_len, sizeof(unf_len));
/* skip over attr_len too; will be filled when known */
data += sizeof(unf_len) + sizeof(attr_len);
len += sizeof(unf_len) + sizeof(attr_len);
/* copy usual attributes */
memcpy(data, peer->path_attrs, peer->path_attr_len_without_nexthop);
data += peer->path_attr_len_without_nexthop;
len += peer->path_attr_len_without_nexthop;
/* copy MP unreachable NLRI heading */
memcpy(data, peer->mp_unreach_nlri_partial,
BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE);
/* remember where to update this attr len */
unreach_len = data + 2;
data += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
len += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
peer->update_routes6 = 0; /* tentatively clear */
/* find differences */
while ((have || want) && data < (max - sizeof(struct bgp_ip6_prefix)))
{
if (have)
s = want
? memcmp(&have->dest, &want->dest, sizeof(have->dest))
: -1;
else
s = 1;
if (s < 0) /* found one to delete */
{
struct bgp_route6_list *tmp = have;
have = have->next;
s = BGP_IP_PREFIX_SIZE(tmp->dest);
memcpy(data, &tmp->dest, s);
data += s;
unf_len += s;
len += s;
LOG(5, 0, 0, "Withdrawing route %s/%d from BGP peer %s\n",
inet_ntop(AF_INET6, &tmp->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
tmp->dest.len, peer->name);
free(tmp);
if (e)
e->next = have;
else
peer->routes6 = have;
}
else
{
if (!s) /* same */
{
e = have; /* stash the last found to relink above */
have = have->next;
want = want->next;
}
else if (s > 0) /* addition reqd. */
{
if (add)
{
peer->update_routes6 = 1; /* only one add per packet */
if (!have)
break;
}
else
add = want;
if (want)
want = want->next;
}
}
}
if (have || want)
peer->update_routes6 = 1; /* more to do */
/* anything changed? */
if (!(unf_len || add))
return 1;
/* go back and insert MP unf_len */
unf_len += sizeof(struct bgp_attr_mp_unreach_nlri_partial);
unf_len = htons(unf_len);
memcpy(&unreach_len, &unf_len, sizeof(unf_len));
if (add)
{
if (!(e = malloc(sizeof(*e))))
{
LOG(0, 0, 0, "Can't allocate route for %s/%d (%s)\n",
inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
add->dest.len, strerror(errno));
return 0;
}
memcpy(e, add, sizeof(*e));
e->next = 0;
peer->routes6 = bgp_insert_route6(peer->routes6, e);
/* copy MP reachable NLRI heading */
memcpy(data, peer->mp_reach_nlri_partial,
BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE);
/* with proper len */
reach_len = BGP_IP_PREFIX_SIZE(add->dest);
data[2] = reach_len;
data += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
len += BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE;
memcpy(data, &add->dest, reach_len);
data += reach_len;
len += reach_len;
LOG(5, 0, 0, "Advertising route %s/%d to BGP peer %s\n",
inet_ntop(AF_INET6, &add->dest.prefix, ipv6addr, INET6_ADDRSTRLEN),
add->dest.len, peer->name);
}
/* go back and insert attr_len */
attr_len = htons(len - 4);
memcpy(&peer->outbuf->packet.data + 2, &attr_len, sizeof(attr_len));
peer->outbuf->packet.header.len = htons(len);
peer->outbuf->done = 0;
return bgp_write(peer);
} }
/* send/buffer NOTIFICATION message */ /* send/buffer NOTIFICATION message */

22
bgp.h
View file

@ -101,6 +101,22 @@ struct bgp_path_attr {
} data; /* variable */ } data; /* variable */
} __attribute__ ((packed)); } __attribute__ ((packed));
struct bgp_attr_mp_reach_nlri_partial {
uint16_t afi; /* sa_family_t */
uint8_t safi;
uint8_t next_hop_len;
uint8_t next_hop[16];
uint8_t reserved;
} __attribute__ ((packed));
#define BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE (3 + sizeof(struct bgp_attr_mp_reach_nlri_partial))
struct bgp_attr_mp_unreach_nlri_partial {
uint16_t afi; /* sa_family_t */
uint8_t safi;
} __attribute__ ((packed));
/* we use it as an extended attribute */
#define BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE (4 + sizeof(struct bgp_attr_mp_unreach_nlri_partial))
/* bgp_path_attr.flags (bitfields) */ /* bgp_path_attr.flags (bitfields) */
#define BGP_PATH_ATTR_FLAG_OPTIONAL (1 << 7) #define BGP_PATH_ATTR_FLAG_OPTIONAL (1 << 7)
#define BGP_PATH_ATTR_FLAG_TRANS (1 << 6) #define BGP_PATH_ATTR_FLAG_TRANS (1 << 6)
@ -121,9 +137,11 @@ struct bgp_path_attr {
#define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE 6 /* well-known, discretionary */ #define BGP_PATH_ATTR_CODE_ATOMIC_AGGREGATE 6 /* well-known, discretionary */
#define BGP_PATH_ATTR_CODE_AGGREGATOR 7 /* optional, transitive */ #define BGP_PATH_ATTR_CODE_AGGREGATOR 7 /* optional, transitive */
#define BGP_PATH_ATTR_CODE_COMMUNITIES 8 /* optional, transitive (RFC1997) */ #define BGP_PATH_ATTR_CODE_COMMUNITIES 8 /* optional, transitive (RFC1997) */
#define BGP_PATH_ATTR_CODE_MP_REACH_NLRI 14 /* optional, non-transitive (RFC4760) */
#define BGP_PATH_ATTR_CODE_MP_UNREACH_NLRI 15 /* optional, non-transitive (RFC4760) */
#define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \ #define BGP_PATH_ATTR_SIZE(p) ((((p).flags & BGP_PATH_ATTR_FLAG_EXTLEN) \
? ((p).data.e.len + 1) : (p).data.s.len) + 3) ? ((p).data.e.len + 4) : (p).data.s.len) + 3)
/* well known COMMUNITIES */ /* well known COMMUNITIES */
#define BGP_COMMUNITY_NO_EXPORT 0xffffff01 /* don't advertise outside confederation */ #define BGP_COMMUNITY_NO_EXPORT 0xffffff01 /* don't advertise outside confederation */
@ -222,6 +240,8 @@ struct bgp_peer {
int handle_ipv6_routes; /* can handle IPv6 routes advertisements */ int handle_ipv6_routes; /* can handle IPv6 routes advertisements */
int update_routes6; /* UPDATE required for IPv6 routes */ int update_routes6; /* UPDATE required for IPv6 routes */
struct bgp_route6_list *routes6; /* IPv6 routes known by this peer */ struct bgp_route6_list *routes6; /* IPv6 routes known by this peer */
char mp_reach_nlri_partial[BGP_PATH_ATTR_MP_REACH_NLRI_PARTIAL_SIZE];
char mp_unreach_nlri_partial[BGP_PATH_ATTR_MP_UNREACH_NLRI_PARTIAL_SIZE];
}; };
/* bgp_peer.cli_flag */ /* bgp_peer.cli_flag */