On Tue, Oct 27, 2015 at 12:59:20AM -0600, Kevin Reay wrote:
> I did add an additional check for "zero" ASNs to the 2-byte default,
> inspired by a quick glance at Wireshark's heuristics. I now flip
> through each segment's ASNs inside of bgp_attr_get_as_size(), looking
> for any zero-value 16bit ASNs that would indicate the set isn't valid
> for the assumed byte-length (ie. the first two bytes of a 4-byte ASN).
> I'd need to read through the RFCs in detail to ensure this is witout
> issue though.
I've attached the updated version of the previous tcpdump bgp AS_PATH
size discovery function.
It uses the original 2-byte size assumption with the addition of a
check for any zero-value ASNs. After some research, it appears this is
a valid approach according to RFC7607 (they aren't valid in an
AS_PATH).
The additional check appears to further improve the success in my test
cases as well as obviously correcting the original in-tree behaviour.
Any feedback would be great.
Index: print-bgp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-bgp.c,v
retrieving revision 1.18
diff -u -p -r1.18 print-bgp.c
--- print-bgp.c 20 Oct 2015 11:29:07 -0000 1.18
+++ print-bgp.c 29 Oct 2015 20:48:06 -0000
@@ -140,6 +140,9 @@ struct bgp_attr {
#define BGP_CONFED_AS_SEQUENCE 3 /* draft-ietf-idr-rfc3065bis-01 */
#define BGP_CONFED_AS_SET 4 /* draft-ietf-idr-rfc3065bis-01 */
+#define BGP_AS_SEG_TYPE_MIN BGP_AS_SET
+#define BGP_AS_SEG_TYPE_MAX BGP_CONFED_AS_SET
+
static struct tok bgp_as_path_segment_open_values[] = {
{ BGP_AS_SET, " {" },
{ BGP_AS_SEQUENCE, " " },
@@ -400,6 +403,64 @@ trunc:
}
#endif
+/*
+ * Try to determine the size of the ASs encoded in an AS-path. It is
+ * not obvious as both speaker types exchange AS-paths with the same
+ * path-attribute type.
+ */
+static int
+bgp_attr_get_as_size(u_int8_t bgpa_type, const u_char *dat, int len)
+{
+ const u_char *p;
+ int i;
+
+ p = dat;
+
+ /* AS4 path types are always encoded in 4-byte format */
+ if (bgpa_type == BGPTYPE_AS4_PATH)
+ return 4;
+
+ /*
+ * Start by assuming 2-byte ASs. Iterate through the path data using the
+ * segment length values. Switch to 4-bytes if we encounter an invalid
+ * field value/if the AS-Path length is invalid for the assumed size.
+ */
+ while (p < dat + len) {
+ TCHECK(p[0]);
+
+ /* check segment type: invalid value means wrong size */
+ if (p[0] < BGP_AS_SEG_TYPE_MIN || p[0] > BGP_AS_SEG_TYPE_MAX)
+ goto trunc;
+
+ TCHECK2(p[1], 1 + p[1] * 2);
+
+ /* check segment length: invalid indicates wrong size */
+ if (p[1] == 0)
+ goto trunc;
+
+ /*
+ * Look for zero-value ASs which are likely the first 2 bytes of
+ * a 4-byte AS. RFC7607 asserts a zero in an AS_PATH is invalid.
+ */
+ for (i = 0; i < p[1]; i++) {
+ if (EXTRACT_16BITS(&p[2 + i * 2]) == 0)
+ goto trunc;
+ }
+
+ p += 2 + p[1] * 2;
+ }
+
+ /* matching length: it's very likely the ASs were encoded as 2-bytes */
+ if (p == dat + len)
+ return 2;
+trunc:
+ /*
+ * Either there was not enough data or we tried to decode 4-byte ASs
+ * with an incorrect size of 2-bytes.
+ */
+ return 4;
+}
+
static int
bgp_attr_print(const struct bgp_attr *attr, const u_char *dat, int len)
{
@@ -425,17 +486,7 @@ bgp_attr_print(const struct bgp_attr *at
}
break;
case BGPTYPE_AS4_PATH:
- asn_bytes = 4;
- /* FALLTHROUGH */
case BGPTYPE_AS_PATH:
- /*
- * 2-byte speakers will receive AS4_PATH as well AS_PATH (2-byte).
- * 4-byte speakers will only receive AS_PATH but it will be 4-byte.
- * To identify which is the case, compare the length of the path
- * segment value in bytes, with the path segment length from the
- * message (counted in # of AS)
- */
-
if (len % 2) {
printf(" invalid len");
break;
@@ -445,15 +496,13 @@ bgp_attr_print(const struct bgp_attr *at
printf(" empty");
break;
}
+ asn_bytes = bgp_attr_get_as_size(attr->bgpa_type, dat, len);
while (p < dat + len) {
TCHECK2(p[0], 2);
- if (asn_bytes == 0) {
- if (p[1] == 0) {
- /* invalid: segment contains one or more AS */
- printf(" malformed");
- break;
- }
- asn_bytes = (len-2)/p[1];
+ if (p[1] == 0) {
+ /* invalid: segment contains no AS */
+ printf(" malformed");
+ break;
}
printf("%s",
tok2str(bgp_as_path_segment_open_values,