From ced1cac7329ae41cd0c3d3e53ea905cc6c6e36af Mon Sep 17 00:00:00 2001 From: David Cronin Date: Mon, 18 Sep 2017 23:56:50 +0100 Subject: [PATCH] Added ability to decode bgp add-path for updated and withdrawn routes for ipv4 and ipv6. Since there is no flag to signal that a message contains add-path content, a heuristic approach is taken where we assume an add-path format if an add-path message is sensible and that standard bgp is not. In this way we will only display as add-path if displaying as regular bgp was in some way incorrect anyway. Also modified bgp_update_print to correctly decode withdrawn routes as IPv4 as IPv6 routes are always in MP_UNREACH_NLRI. --- print-bgp.c | 159 +++++++++++++++++++++++++++++++++++------ tests/TESTLIST | 1 + tests/bgp-addpath.out | 30 ++++++++ tests/bgp-addpath.pcap | Bin 0 -> 311 bytes 4 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 tests/bgp-addpath.out create mode 100644 tests/bgp-addpath.pcap diff --git a/print-bgp.c b/print-bgp.c index 66bbe65b..30270117 100644 --- a/print-bgp.c +++ b/print-bgp.c @@ -1362,6 +1362,62 @@ trunc: return 4; } +/* + * The only way to know that a BGP UPDATE message is using add path is + * by checking if the capability is in the OPEN message which we may have missed. + * So this function checks if it is possible that the update could contain add path + * and if so it checks that standard BGP doesn't make sense. + */ + +static int +check_add_path(const u_char *pptr, u_int length, u_int max_prefix_length) { + + u_int offset, prefix_length; + if (length < 5) { + return 0; + } + + /* check if it could be add path */ + for(offset = 0; offset < length;) { + offset += 4; + prefix_length = pptr[offset]; + /* + * Add 4 to cover the path id + * and check the prefix length isn't greater than 32/128. + */ + if (prefix_length > max_prefix_length) { + return 0; + } + /* Add 1 for the prefix_length byte and prefix_length to cover the address */ + offset += 1 + ((prefix_length + 7) / 8); + } + /* check we haven't gone past the end of the section */ + if (offset > length) { + return 0; + } + + /* check it's not standard BGP */ + for(offset = 0; offset < length; ) { + prefix_length = pptr[offset]; + /* + * If the prefix_length is zero (0.0.0.0/0) + * and since it's not the only address (length >= 5) + * then it is add-path + */ + if (prefix_length < 1 || prefix_length > max_prefix_length) { + return 1; + } + offset += 1 + ((prefix_length + 7) / 8); + } + if (offset > length) { + return 1; + } + + /* assume not add-path by default */ + return 0; +} + + static int bgp_attr_print(netdissect_options *ndo, u_int atype, const u_char *pptr, u_int len) @@ -1378,6 +1434,7 @@ bgp_attr_print(netdissect_options *ndo, const u_char *tptr; char buf[MAXHOSTNAMELEN + 100]; int as_size; + int add_path4, add_path6, path_id; tptr = pptr; tlen=len; @@ -1742,11 +1799,18 @@ bgp_attr_print(netdissect_options *ndo, ND_PRINT((ndo, ", no SNPA")); } + add_path4 = check_add_path(tptr, (len-(tptr - pptr)), 32); + add_path6 = check_add_path(tptr, (len-(tptr - pptr)), 128); + while (tptr < pptr + len) { switch (af<<8 | safi) { case (AFNUM_INET<<8 | SAFNUM_UNICAST): case (AFNUM_INET<<8 | SAFNUM_MULTICAST): case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST): + if (add_path4) { + path_id = EXTRACT_32BITS(tptr); + tptr += 4; + } advance = decode_prefix4(ndo, tptr, len, buf, sizeof(buf)); if (advance == -1) ND_PRINT((ndo, "\n\t (illegal prefix length)")); @@ -1756,6 +1820,9 @@ bgp_attr_print(netdissect_options *ndo, break; /* bytes left, but not enough */ else ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path4) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } break; case (AFNUM_INET<<8 | SAFNUM_LABUNICAST): advance = decode_labeled_prefix4(ndo, tptr, len, buf, sizeof(buf)); @@ -1811,6 +1878,10 @@ bgp_attr_print(netdissect_options *ndo, case (AFNUM_INET6<<8 | SAFNUM_UNICAST): case (AFNUM_INET6<<8 | SAFNUM_MULTICAST): case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST): + if (add_path6) { + path_id = EXTRACT_32BITS(tptr); + tptr += 4; + } advance = decode_prefix6(ndo, tptr, len, buf, sizeof(buf)); if (advance == -1) ND_PRINT((ndo, "\n\t (illegal prefix length)")); @@ -1820,6 +1891,9 @@ bgp_attr_print(netdissect_options *ndo, break; /* bytes left, but not enough */ else ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path6) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } break; case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST): advance = decode_labeled_prefix6(ndo, tptr, len, buf, sizeof(buf)); @@ -1910,11 +1984,18 @@ bgp_attr_print(netdissect_options *ndo, tptr += 3; + add_path4 = check_add_path(tptr, (len-(tptr - pptr)), 32); + add_path6 = check_add_path(tptr, (len-(tptr - pptr)), 128); + while (tptr < pptr + len) { switch (af<<8 | safi) { case (AFNUM_INET<<8 | SAFNUM_UNICAST): case (AFNUM_INET<<8 | SAFNUM_MULTICAST): case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST): + if (add_path4) { + path_id = EXTRACT_32BITS(tptr); + tptr += 4; + } advance = decode_prefix4(ndo, tptr, len, buf, sizeof(buf)); if (advance == -1) ND_PRINT((ndo, "\n\t (illegal prefix length)")); @@ -1924,6 +2005,9 @@ bgp_attr_print(netdissect_options *ndo, break; /* bytes left, but not enough */ else ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path4) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } break; case (AFNUM_INET<<8 | SAFNUM_LABUNICAST): advance = decode_labeled_prefix4(ndo, tptr, len, buf, sizeof(buf)); @@ -1950,6 +2034,10 @@ bgp_attr_print(netdissect_options *ndo, case (AFNUM_INET6<<8 | SAFNUM_UNICAST): case (AFNUM_INET6<<8 | SAFNUM_MULTICAST): case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST): + if (add_path6) { + path_id = EXTRACT_32BITS(tptr); + tptr += 4; + } advance = decode_prefix6(ndo, tptr, len, buf, sizeof(buf)); if (advance == -1) ND_PRINT((ndo, "\n\t (illegal prefix length)")); @@ -1959,6 +2047,9 @@ bgp_attr_print(netdissect_options *ndo, break; /* bytes left, but not enough */ else ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path6) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } break; case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST): advance = decode_labeled_prefix6(ndo, tptr, len, buf, sizeof(buf)); @@ -2498,6 +2589,8 @@ bgp_update_print(netdissect_options *ndo, struct bgp bgp; const u_char *p; int withdrawn_routes_len; + char buf[MAXHOSTNAMELEN + 100]; + int wpfx; int len; int i; @@ -2516,17 +2609,40 @@ bgp_update_print(netdissect_options *ndo, p += 2; length -= 2; if (withdrawn_routes_len) { - /* - * Without keeping state from the original NLRI message, - * it's not possible to tell if this a v4 or v6 route, - * so only try to decode it if we're not v6 enabled. - */ ND_TCHECK2(p[0], withdrawn_routes_len); if (length < withdrawn_routes_len) goto trunc; - ND_PRINT((ndo, "\n\t Withdrawn routes: %d bytes", withdrawn_routes_len)); - p += withdrawn_routes_len; - length -= withdrawn_routes_len; + if (withdrawn_routes_len < 2) + goto trunc; + + ND_PRINT((ndo, "\n\t Withdrawn routes:")); + int add_path, path_id; + add_path = check_add_path(p, withdrawn_routes_len, 32); + while(withdrawn_routes_len > 0) { + if (add_path) { + path_id = EXTRACT_32BITS(p); + p += 4; + length -= 4; + withdrawn_routes_len -= 4; + } + wpfx = decode_prefix4(ndo, p, withdrawn_routes_len, buf, sizeof(buf)); + if (wpfx == -1) { + ND_PRINT((ndo, "\n\t (illegal prefix length)")); + break; + } else if (wpfx == -2) + goto trunc; + else if (wpfx == -3) + goto trunc; /* bytes left, but not enough */ + else { + ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } + p += wpfx; + length -= wpfx; + withdrawn_routes_len -= wpfx; + } + } } ND_TCHECK2(p[0], 2); @@ -2597,17 +2713,15 @@ bgp_update_print(netdissect_options *ndo, } if (length) { - /* - * XXX - what if they're using the "Advertisement of - * Multiple Paths in BGP" feature: - * - * https://datatracker.ietf.org/doc/draft-ietf-idr-add-paths/ - * - * http://tools.ietf.org/html/draft-ietf-idr-add-paths-06 - */ + int add_path = check_add_path(p, length, 32); + int path_id; ND_PRINT((ndo, "\n\t Updated routes:")); - while (length) { - char buf[MAXHOSTNAMELEN + 100]; + while (length > 0) { + if (add_path) { + path_id = EXTRACT_32BITS(p); + p += 4; + length -= 4; + } i = decode_prefix4(ndo, p, length, buf, sizeof(buf)); if (i == -1) { ND_PRINT((ndo, "\n\t (illegal prefix length)")); @@ -2617,9 +2731,12 @@ bgp_update_print(netdissect_options *ndo, else if (i == -3) goto trunc; /* bytes left, but not enough */ else { - ND_PRINT((ndo, "\n\t %s", buf)); - p += i; - length -= i; + ND_PRINT((ndo, "\n\t %s", buf)); + if (add_path) { + ND_PRINT((ndo, " Path Id: %d", path_id)); + } + p += i; + length -= i; } } } diff --git a/tests/TESTLIST b/tests/TESTLIST index eb7b7607..95d4242a 100644 --- a/tests/TESTLIST +++ b/tests/TESTLIST @@ -26,6 +26,7 @@ bgp_infloop-v bgp-infinite-loop.pcap bgp_infloop-v.out -v bgp-aigp bgp-aigp.pcap bgp-aigp.out -v bgp-large-community bgp-large-community.pcap bgp-large-community.out -v bgp-shutdown-communication bgp-shutdown-communication.pcap bgp-shutdown-communication.out -v +bgp-addpath bgp-addpath.pcap bgp-addpath.out -v # EAP tests eapon1 eapon1.pcap eapon1.out diff --git a/tests/bgp-addpath.out b/tests/bgp-addpath.out new file mode 100644 index 00000000..89845d88 --- /dev/null +++ b/tests/bgp-addpath.out @@ -0,0 +1,30 @@ +IP truncated-ip - 38 bytes missing! (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto TCP (6), length 309) + 127.0.0.1.179 > 127.0.0.1.80: Flags [S], seq 0:269, win 8192, length 269: BGP + Update Message (2), length: 231 + Withdrawn routes: + 8.2.0.0/24 Path Id: 20 + 8.2.1.0/24 Path Id: 21 + Origin (1), length: 1, Flags [T]: EGP + Next Hop (3), length: 0, Flags [T]: invalid len + AS Path (2), length: 6, Flags [T]: 100 200 + Multi-Protocol Reach NLRI (14), length: 25, Flags [T]: + AFI: IPv4 (1), SAFI: Unicast (1) + nexthop: 1.2.3.4, nh-length: 4, no SNPA + 120.4.0.0/24 Path Id: 40 + 120.4.1.0/24 Path Id: 41 + Multi-Protocol Unreach NLRI (15), length: 19, Flags [T]: + AFI: IPv4 (1), SAFI: Unicast (1) + 32.45.0.0/24 Path Id: 700 + 32.45.1.0/24 Path Id: 701 + Multi-Protocol Reach NLRI (14), length: 61, Flags [T]: + AFI: IPv6 (2), SAFI: Unicast (1) + nexthop: 2000:0:0:40::1, nh-length: 16, no SNPA + 2002::1400:0/120 Path Id: 1 + 2002::1400:100/120 Path Id: 2 + Multi-Protocol Unreach NLRI (15), length: 43, Flags [T]: + AFI: IPv6 (2), SAFI: Unicast (1) + 2002::2800:0/120 Path Id: 100 + 2002::2800:100/120 Path Id: 101 + Updated routes: + 6.2.0.0/24 Path Id: 10 + 6.2.1.0/24 Path Id: 11 diff --git a/tests/bgp-addpath.pcap b/tests/bgp-addpath.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6f198e489a706b47259dd343441dd0215a723b2e GIT binary patch literal 311 zcmZ8bF$%&!5S-nFAPPyq2xoU?L@X_ZYwTlbpZp<^!VmZfU!Z8KAlO)GBYweO822uu zFavvYw=+ApuiJ6Q@X?~)0EIlZ-~Iai1R`o`37SLhCaA7rAzL^hYv(f7c&vcB1AG{< zm=e`v>m3Ob4u*(~2g3m~a@pi;ZPnqK0FJ3UFA%Dia#pOQLv(dk)VUj;68AWT0L=Pcd; literal 0 HcmV?d00001