bgpd: Regularise BGP NLRI sanity checks a bit
* bgp_route.h: (bgp_nlri_sanity_check) The bulk of the args are equivalent
to a (struct bgp_nlri), consolidate.
* bgp_route.c: (bgp_nlri_sanity_check) Make this a frontend for all afi/safis.
Including SAFI_MPLS_LABELED_VPN.
(bgp_nlri_sanity_check_ip) Regular IP NLRI sanity check based on the
existing code, and adjusted for (struct bgp_nlri *) arg.
* bgp_attr.c: (bgp_mp_reach_parse) Adjust for passing (struct bgp_nlri *)
to bgp_nlri_sanity_check.
Get rid of special-casing to not sanity check VPN.
(bgp_mp_unreach_parse) Ditto.
* bgp_mplsvpn.c: Use the same VPN parsing code for both the sanity
check and the actual parse.
(bgp_nlri_parse_vpn) renamed to bgp_nlri_parse_vpn_body and made
internal.
(bgp_nlri_parse_vpn_body) Added (bool) argument to control whether it
is sanity checking or whether it should update routing state for each
NLRI. Send a NOTIFY and reset the session, if there's a parsing
error, as bgp_nlri_sanity_check_ip does, and as is required by the
RFC.
(bgp_nlri_parse_vpn) now a wrapper to call _body with update.
(bgp_nlri_sanity_check_vpn) wrapper to call parser without
updating.
* bgp_mplsvpn.h: (bgp_nlri_sanity_check_vpn) export for
bgp_nlri_sanity_check.
* bgp_packet.c: (bgp_update_receive) Adjust for bgp_nlri_sanity_check
argument changes.
* test/bgp_mp_attr_test.c: Extend to also test the NLRI parsing functions,
if the initial MP-attr parsing has succeeded. Fix the NLRI in the
VPN cases. Add further VPN tests.
* tests/bgpd.tests/testbgpmpattr.exp: Add the new test cases.
This commit a joint effort of:
Lou Berger <lberger@labn.net>
Donald Sharp <sharpd@cumulusnetworks.com>
Paul Jakma <paul.jakma@hpe.com> / <paul@jakma.org>
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 888f11a..98571da 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -1725,23 +1725,20 @@
__func__, peer->host);
return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
}
-
- if (safi != SAFI_MPLS_LABELED_VPN)
- {
- ret = bgp_nlri_sanity_check (peer, afi, safi, stream_pnt (s), nlri_len);
- if (ret < 0)
- {
- zlog_info ("%s: (%s) NLRI doesn't pass sanity check",
- __func__, peer->host);
- return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
- }
- }
-
+
mp_update->afi = afi;
mp_update->safi = safi;
mp_update->nlri = stream_pnt (s);
mp_update->length = nlri_len;
+ ret = bgp_nlri_sanity_check (peer, mp_update);
+ if (ret < 0)
+ {
+ zlog_info ("%s: (%s) NLRI doesn't pass sanity check",
+ __func__, peer->host);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
stream_forward_getp (s, nlri_len);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_REACH_NLRI);
@@ -1759,7 +1756,6 @@
afi_t afi;
safi_t safi;
u_int16_t withdraw_len;
- int ret;
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
@@ -1775,18 +1771,14 @@
withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
- if (safi != SAFI_MPLS_LABELED_VPN)
- {
- ret = bgp_nlri_sanity_check (peer, afi, safi, stream_pnt (s), withdraw_len);
- if (ret < 0)
- return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
- }
-
mp_withdraw->afi = afi;
mp_withdraw->safi = safi;
mp_withdraw->nlri = stream_pnt (s);
mp_withdraw->length = withdraw_len;
+ if (bgp_nlri_sanity_check (peer, mp_withdraw) < 0)
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+
stream_forward_getp (s, withdraw_len);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MP_UNREACH_NLRI);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index f8b43df..900bc48 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -32,6 +32,7 @@
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_packet.h"
static u_int16_t
decode_rd_type (u_char *pnt)
@@ -91,9 +92,9 @@
rd_ip->val |= (u_int16_t) *pnt;
}
-int
-bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
- struct bgp_nlri *packet)
+static int
+bgp_nlri_parse_vpn_body (struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool update)
{
u_char *pnt;
u_char *lim;
@@ -129,30 +130,53 @@
psize = PSIZE (prefixlen);
/* sanity check against packet data */
- if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8 || (pnt + psize) > lim)
+ if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8)
{
- zlog_err ("prefix length (%d) is less than 88"
- " or larger than received (%u)",
+ plog_err (peer->log,
+ "%s [Error] Update packet error / VPNv4"
+ " (prefix length %d less than VPNv4 min length)",
+ peer->host, prefixlen);
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
+ return -1;
+ }
+ if ((pnt + psize) > lim)
+ {
+ plog_err (peer->log,
+ "%s [Error] Update packet error / VPNv4"
+ " (psize %u exceeds packet size (%u)",
+ peer->host,
prefixlen, (uint)(lim-pnt));
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
return -1;
}
/* sanity check against storage for the IP address portion */
if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u))
{
- zlog_err ("prefix length (%d) exceeds prefix storage (%zu)",
+ plog_err (peer->log,
+ "%s [Error] Update packet error / VPNv4"
+ " (psize %u exceeds storage size (%zu)",
+ peer->host,
prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, sizeof(p.u));
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
return -1;
}
/* Sanity check against max bitlen of the address family */
if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen (&p))
{
- zlog_err ("prefix length (%d) exceeds family (%u) max byte length (%u)",
+ plog_err (peer->log,
+ "%s [Error] Update packet error / VPNv4"
+ " (psize %u exceeds family (%u) max byte len %u)",
+ peer->host,
prefixlen - VPN_PREFIXLEN_MIN_BYTES*8,
p.family, prefix_blen (&p));
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
return -1;
-
}
/* Copyr label to prefix. */
@@ -187,22 +211,46 @@
memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES,
psize - VPN_PREFIXLEN_MIN_BYTES);
- if (attr)
- bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
- ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
- else
- bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
- ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+ if (update)
+ {
+ if (attr)
+ bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0);
+ else
+ bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt);
+ }
}
/* Packet length consistency check. */
if (pnt != lim)
- return -1;
+ {
+ plog_err (peer->log,
+ "%s [Error] Update packet error / VPNv4"
+ " (%zu data remaining after parsing)",
+ peer->host, lim - pnt);
+ bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
+ return -1;
+ }
return 0;
#undef VPN_PREFIXLEN_MIN_BYTES
}
int
+bgp_nlri_sanity_check_vpn (struct peer *peer, struct bgp_nlri *nlri)
+{
+ return bgp_nlri_parse_vpn_body (peer, NULL, nlri, false);
+}
+
+int
+bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet)
+{
+ return bgp_nlri_parse_vpn_body (peer, attr, packet, true);
+}
+
+int
str2prefix_rd (const char *str, struct prefix_rd *prd)
{
int ret; /* ret of called functions */
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 3299b9c..b89b6d8 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -43,6 +43,7 @@
extern void bgp_mplsvpn_init (void);
extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *);
+extern int bgp_nlri_sanity_check_vpn (struct peer *, struct bgp_nlri *);
extern u_int32_t decode_label (u_char *);
extern int str2prefix_rd (const char *, struct prefix_rd *);
extern int str2tag (const char *, u_char *);
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index b8a38fa..d40c24c 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1660,17 +1660,17 @@
/* Unfeasible Route packet format check. */
if (withdraw_len > 0)
{
- ret = bgp_nlri_sanity_check (peer, AFI_IP, SAFI_UNICAST, stream_pnt (s), withdraw_len);
+ withdraw.afi = AFI_IP;
+ withdraw.safi = SAFI_UNICAST;
+ withdraw.nlri = stream_pnt (s);
+ withdraw.length = withdraw_len;
+ ret = bgp_nlri_sanity_check (peer, &withdraw);
if (ret < 0)
return -1;
if (BGP_DEBUG (packet, PACKET_RECV))
zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host);
- withdraw.afi = AFI_IP;
- withdraw.safi = SAFI_UNICAST;
- withdraw.nlri = stream_pnt (s);
- withdraw.length = withdraw_len;
stream_forward_getp (s, withdraw_len);
}
@@ -1751,8 +1751,14 @@
if (update_len)
{
+ /* Set NLRI portion to structure. */
+ update.afi = AFI_IP;
+ update.safi = SAFI_UNICAST;
+ update.nlri = stream_pnt (s);
+ update.length = update_len;
+
/* Check NLRI packet format and prefix length. */
- ret = bgp_nlri_sanity_check (peer, AFI_IP, SAFI_UNICAST, stream_pnt (s), update_len);
+ ret = bgp_nlri_sanity_check (peer, &update);
if (ret < 0)
{
bgp_attr_unintern_sub (&attr);
@@ -1760,11 +1766,6 @@
return -1;
}
- /* Set NLRI portion to structure. */
- update.afi = AFI_IP;
- update.safi = SAFI_UNICAST;
- update.nlri = stream_pnt (s);
- update.length = update_len;
stream_forward_getp (s, update_len);
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 0dcae61..c1e5e59 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -3322,16 +3322,16 @@
return 0;
}
-/* NLRI encode syntax check routine. */
-int
-bgp_nlri_sanity_check (struct peer *peer, int afi, safi_t safi,
- u_char *pnt, bgp_size_t length)
+static int
+bgp_nlri_sanity_check_ip (struct peer *peer, struct bgp_nlri *nlri)
{
u_char *end;
u_char prefixlen;
int psize;
-
- end = pnt + length;
+ u_char *pnt = nlri->nlri;
+ afi_t afi = nlri->afi;
+ safi_t safi = nlri->safi;
+ end = pnt + nlri->length;
/* RFC1771 6.3 The NLRI field in the UPDATE message is checked for
syntactic validity. If the field is syntactically incorrect,
@@ -3395,6 +3395,20 @@
return 0;
}
+int
+bgp_nlri_sanity_check (struct peer *peer, struct bgp_nlri *nlri)
+{
+ switch (nlri->safi)
+ {
+ case SAFI_MPLS_LABELED_VPN:
+ return bgp_nlri_sanity_check_vpn (peer, nlri);
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ return bgp_nlri_sanity_check_ip (peer, nlri);
+ default: return -1;
+ }
+}
+
static struct bgp_static *
bgp_static_new (void)
{
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 8e9bcbd..c16eca7 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -196,7 +196,7 @@
extern void bgp_info_set_flag (struct bgp_node *, struct bgp_info *, u_int32_t);
extern void bgp_info_unset_flag (struct bgp_node *, struct bgp_info *, u_int32_t);
-extern int bgp_nlri_sanity_check (struct peer *, int, safi_t, u_char *, bgp_size_t);
+extern int bgp_nlri_sanity_check (struct peer *, struct bgp_nlri *);
extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *);
extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int);