Fix a segfault when printing a malformed BGP AS_PATH update due to ASN
extraction.
Better AS size extraction from AS paths: better heuristics (see
bgp_attr_get_as_size).
Also fixes output support for 4-byte ASNs. For example;
(AS_PATH[T] {500.500 513.65211})
becomes:
(AS_PATH[T] {500 500} 65211)
Collapses bgp_attr_print switch 2-byte and 4-byte path cases.
bgp_attr_get_as_size function copied from tcpdump Git repo master.
This brings behavior in-line with newer versions on Linux.
Finally, minor output tweak: add a space after BGP update attribute
ORIGINATOR_ID's flags to make consistent with other attributes.
Index: print-bgp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-bgp.c,v
retrieving revision 1.17
diff -u -p -r1.17 print-bgp.c
--- print-bgp.c 16 Jan 2015 06:40:21 -0000 1.17
+++ print-bgp.c 14 Oct 2015 00:15:13 -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,63 @@ trunc:
}
#endif
+/*
+ * bgp_attr_get_as_size
+ *
+ * Try to find the size of the ASs encoded in an as-path. It is not obvious, as
+ * both Old speakers that do not support 4 byte AS, and the new speakers that
do
+ * support, exchange AS-Path with the same path-attribute type value 0x02.
+ */
+static int
+bgp_attr_get_as_size (u_int8_t bgpa_type, const u_char *pptr, int len)
+{
+ const u_char *tptr = pptr;
+
+ /*
+ * If the path attribute is the optional AS4 path type, then we already
+ * know, that ASs must be encoded in 4 byte format.
+ */
+ if (bgpa_type == BGPTYPE_AS4_PATH) {
+ return 4;
+ }
+
+ /*
+ * Let us assume that ASs are of 2 bytes in size, and check if the AS-Path
+ * TLV is good. If not, ask the caller to try with AS encoded as 4 bytes
+ * each.
+ */
+ while (tptr < pptr + len) {
+ TCHECK(tptr[0]);
+
+ /*
+ * If we do not find a valid segment type, our guess might be wrong.
+ */
+ if (tptr[0] < BGP_AS_SEG_TYPE_MIN || tptr[0] > BGP_AS_SEG_TYPE_MAX) {
+ goto trunc;
+ }
+ TCHECK(tptr[1]);
+ tptr += 2 + tptr[1] * 2;
+ }
+
+ /*
+ * If we correctly reached end of the AS path attribute data content,
+ * then most likely ASs were indeed encoded as 2 bytes.
+ */
+ if (tptr == pptr + len) {
+ return 2;
+ }
+
+trunc:
+
+ /*
+ * We can come here, either we did not have enough data, or if we
+ * try to decode 4 byte ASs in 2 byte format. Either way, return 4,
+ * so that calller can try to decode each AS as of 4 bytes. If indeed
+ * there was not enough data, it will crib and end the parse anyways.
+ */
+ return 4;
+}
+
static int
bgp_attr_print(const struct bgp_attr *attr, const u_char *dat, int len)
{
@@ -413,7 +473,6 @@ bgp_attr_print(const struct bgp_attr *at
p = dat;
tlen = len;
- asn_bytes = 0;
switch (attr->bgpa_type) {
case BGPTYPE_ORIGIN:
@@ -425,17 +484,8 @@ 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;
@@ -444,11 +494,19 @@ bgp_attr_print(const struct bgp_attr *at
printf(" empty");
break;
}
+
+ /*
+ * BGP updates exchanged between New speakers that support 4
+ * byte AS, ASs are always encoded in 4 bytes. There is no
+ * definitive way to find this, just by the packet's
+ * contents. So, check for packet's TLV's sanity assuming
+ * 2 bytes first, and it does not pass, assume that ASs are
+ * encoded in 4 bytes format and move on.
+ */
+ asn_bytes = bgp_attr_get_as_size(attr->bgpa_type, dat, len);
+
while (p < dat + len) {
TCHECK(p[0]);
- if (asn_bytes == 0) {
- asn_bytes = (len-2)/p[1];
- }
printf("%s",
tok2str(bgp_as_path_segment_open_values,
"?", p[0]));
@@ -456,13 +514,10 @@ bgp_attr_print(const struct bgp_attr *at
for (i = 0; i < p[1] * asn_bytes; i += asn_bytes) {
TCHECK2(p[2 + i], asn_bytes);
printf("%s", i == 0 ? "" : " ");
- if (asn_bytes == 2 || EXTRACT_16BITS(&p[2 + i]))
- printf("%u%s",
- EXTRACT_16BITS(&p[2 + i]),
- asn_bytes == 4 ? "." : "");
- if (asn_bytes == 4)
- printf("%u",
- EXTRACT_16BITS(&p[2 + i + 2]));
+ printf("%u",
+ asn_bytes == 2 ?
+ EXTRACT_16BITS(&p[2 + i]) :
+ EXTRACT_32BITS(&p[2 + i]));
}
TCHECK(p[0]);
printf("%s",
@@ -549,7 +604,7 @@ bgp_attr_print(const struct bgp_attr *at
break;
}
TCHECK2(p[0], 4);
- printf("%s",getname(p));
+ printf(" %s", getname(p));
break;
case BGPTYPE_CLUSTER_LIST:
if (len % 4) {