From: Antonio Quartulli antonio@open-mesh.com
OGMs and unicast TVLV packets carry TVLV containers as payload. With this patch such containers are now parsed and the relevant information is printed to screen.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com ---
changes since v4: - avoid warning in LEN_CHECK macro
changes since v3: - undo unneeded style change in tcpdump.c - remove unneeded sentence about style change sin commit message
changes since v2: - extended doc in README and manpage
changes since v1: - remove all the style changes - make tvlv_len of type ssize_t in dump_batman_iv_ogm()
README | 1 + main.h | 7 +++ man/batctl.8 | 3 ++ tcpdump.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- tcpdump.h | 1 + 5 files changed, 177 insertions(+), 4 deletions(-)
diff --git a/README b/README index b5fd259..f87c551 100644 --- a/README +++ b/README @@ -161,6 +161,7 @@ packet types: 2 - batman icmp packets 4 - batman unicast packets 8 - batman broadcast packets + 16 - batman unicast tvlv packets 32 - batman fragmented packets 64 - batman tt / roaming packets 128 - non batman packets diff --git a/main.h b/main.h index 839c1c7..b16afb6 100644 --- a/main.h +++ b/main.h @@ -49,4 +49,11 @@
extern char module_ver_path[];
+#ifndef VLAN_VID_MASK +#define VLAN_VID_MASK 0xfff +#endif + +#define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \ + (int)(vid & VLAN_VID_MASK) : -1) + #endif diff --git a/man/batctl.8 b/man/batctl.8 index 110020e..8d2de19 100644 --- a/man/batctl.8 +++ b/man/batctl.8 @@ -288,6 +288,9 @@ except specified). The following packet types are available: 8 - batman broadcast packets .RE .RS 16 +16 - batman unicast tvlv packets +.RE +.RS 16 32 - batman fragmented packets .RE .RS 16 diff --git a/tcpdump.c b/tcpdump.c index e84617e..8aea979 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -63,7 +63,7 @@ if ((size_t)(buff_len) < (check_len)) { \ }
static unsigned short dump_level_all = DUMP_TYPE_BATOGM | DUMP_TYPE_BATICMP | DUMP_TYPE_BATUCAST | - DUMP_TYPE_BATBCAST | DUMP_TYPE_BATFRAG | DUMP_TYPE_NONBAT; + DUMP_TYPE_BATBCAST | DUMP_TYPE_BATUTVLV | DUMP_TYPE_BATFRAG | DUMP_TYPE_NONBAT; static unsigned short dump_level;
static void parse_eth_hdr(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed); @@ -83,6 +83,7 @@ static void tcpdump_usage(void) fprintf(stderr, " \t\t%3d - batman unicast packets\n", DUMP_TYPE_BATUCAST); fprintf(stderr, " \t\t%3d - batman broadcast packets\n", DUMP_TYPE_BATBCAST); fprintf(stderr, " \t\t%3d - batman fragmented packets\n", DUMP_TYPE_BATFRAG); + fprintf(stderr, " \t\t%3d - batman unicast tvlv packets\n", DUMP_TYPE_BATUTVLV); fprintf(stderr, " \t\t%3d - non batman packets\n", DUMP_TYPE_NONBAT); fprintf(stderr, " \t\t%3d - batman ogm & non batman packets\n", DUMP_TYPE_BATOGM | DUMP_TYPE_NONBAT); } @@ -99,6 +100,138 @@ static int print_time(void) return 1; }
+static void batctl_tvlv_parse_gw_v1(void *buff, + ssize_t (buff_len)__attribute__((unused))) +{ + struct batadv_tvlv_gateway_data *tvlv = buff; + uint32_t down, up; + + down = ntohl(tvlv->bandwidth_down); + up = ntohl(tvlv->bandwidth_up); + + printf("\tTVLV GWv1: down %d.%.1dMbps, up %d.%1dMbps\n", + down / 10, down % 10, up / 10, up % 10); +} + +static void batctl_tvlv_parse_dat_v1(void (*buff)__attribute__((unused)), + ssize_t (buff_len)__attribute__((unused))) +{ + printf("\tTVLV DATv1: enabled\n"); +} + +static void batctl_tvlv_parse_nc_v1(void (*buff)__attribute__((unused)), + ssize_t (buff_len)__attribute__((unused))) +{ + printf("\tTVLV NCv1: enabled\n"); +} + +static void batctl_tvlv_parse_tt_v1(void *buff, + ssize_t (buff_len)__attribute__((unused))) +{ + struct batadv_tvlv_tt_data *tvlv = buff; + struct batadv_tvlv_tt_vlan_data *vlan; + int i, num_vlan, num_entry; + const char *type; + + if (tvlv->flags & BATADV_TT_OGM_DIFF) + type = "OGM DIFF"; + else if (tvlv->flags & BATADV_TT_REQUEST) + type = "TT REQUEST"; + else if (tvlv->flags & BATADV_TT_RESPONSE) + type = "TT RESPONSE"; + else + type = "UNKNOWN"; + + num_vlan = ntohs(tvlv->num_vlan); + buff_len -= sizeof(*tvlv) + sizeof(*vlan) * num_vlan; + num_entry = buff_len / sizeof(struct batadv_tvlv_tt_change); + + printf("\tTVLV TTv1: %s [%c] ttvn=%hhu vlan_num=%hu entry_num=%hu\n", + type, tvlv->flags & BATADV_TT_FULL_TABLE ? 'F' : '.', + tvlv->ttvn, num_vlan, num_entry); + + vlan = (struct batadv_tvlv_tt_vlan_data *)(tvlv + 1); + for (i = 0; i < num_vlan; i++) { + printf("\t\tVLAN ID %hd, crc %#.8x\n", + BATADV_PRINT_VID(ntohs(vlan->vid)), + ntohl(vlan->crc)); + vlan++; + } +} + +static void batctl_tvlv_parse_roam_v1(void *buff, + ssize_t (buff_len)__attribute__((unused))) +{ + struct batadv_tvlv_roam_adv *tvlv = buff; + + printf("\tTVLV ROAMv1: client %s, VLAN ID %d\n", + get_name_by_macaddr((struct ether_addr *)tvlv->client, NO_FLAGS), + BATADV_PRINT_VID(ntohs(tvlv->vid))); +} + +typedef void (*batctl_tvlv_parser_t)(void *buff, ssize_t buff_len); + +/* location [i][j] contains the parsing function for TVLV of type 'i' and + * version 'j + 1' + */ +batctl_tvlv_parser_t tvlv_parsers[][1] = { + [BATADV_TVLV_GW][0] = batctl_tvlv_parse_gw_v1, + [BATADV_TVLV_DAT][0] = batctl_tvlv_parse_dat_v1, + [BATADV_TVLV_NC][0] = batctl_tvlv_parse_nc_v1, + [BATADV_TVLV_TT][0] = batctl_tvlv_parse_tt_v1, + [BATADV_TVLV_ROAM][0] = batctl_tvlv_parse_roam_v1, +}; + +static void dump_batman_ucast_tvlv(unsigned char *packet_buff, ssize_t buff_len, + int read_opt, int time_printed) +{ + struct batadv_unicast_tvlv_packet *tvlv_packet; + struct batadv_tvlv_hdr *tvlv_hdr; + struct ether_header *ether_header; + struct ether_addr *src, *dst; + batctl_tvlv_parser_t parser; + ssize_t check_len, tvlv_len, len; + uint8_t *ptr; + + check_len = (size_t)buff_len - sizeof(struct ether_header); + + LEN_CHECK(check_len, sizeof(*tvlv_packet), "BAT TVLV"); + check_len -= sizeof(*tvlv_packet); + + ether_header = (struct ether_header *)packet_buff; + tvlv_packet = (struct batadv_unicast_tvlv_packet *)(ether_header + 1); + + LEN_CHECK(check_len, (size_t)ntohs(tvlv_packet->tvlv_len), + "BAT TVLV (containers)"); + + if (!time_printed) + time_printed = print_time(); + + src = (struct ether_addr *)tvlv_packet->src; + printf("BAT %s > ", get_name_by_macaddr(src, read_opt)); + + dst = (struct ether_addr *)tvlv_packet->dst; + tvlv_len = ntohs(tvlv_packet->tvlv_len); + printf("%s: TVLV, len %zu, tvlv_len %zu, ttl %hhu\n", + get_name_by_macaddr(dst, read_opt), + buff_len - sizeof(struct ether_header), tvlv_len, + tvlv_packet->ttl); + + ptr = (uint8_t *)(tvlv_packet + 1); + + while (tvlv_len > 0) { + tvlv_hdr = (struct batadv_tvlv_hdr *)ptr; + len = ntohs(tvlv_hdr->len); + + parser = tvlv_parsers[tvlv_hdr->type][tvlv_hdr->version - 1]; + parser(tvlv_hdr + 1, len); + + /* go to the next container */ + ptr = (uint8_t *)(tvlv_hdr + 1) + len; + tvlv_len -= sizeof(*tvlv_hdr) + len; + } +} + static int dump_bla2_claim(struct ether_header *eth_hdr, struct ether_arp *arphdr, int read_opt) { @@ -482,8 +615,13 @@ static void dump_batman_iv_ogm(unsigned char *packet_buff, ssize_t buff_len, int { struct ether_header *ether_header; struct batadv_ogm_packet *batman_ogm_packet; + struct batadv_tvlv_hdr *tvlv_hdr; + ssize_t tvlv_len, len, check_len; + batctl_tvlv_parser_t parser; + uint8_t *ptr;
- LEN_CHECK((size_t)buff_len - sizeof(struct ether_header), sizeof(struct batadv_ogm_packet), "BAT IV OGM"); + check_len = (size_t)buff_len - sizeof(struct ether_header); + LEN_CHECK(check_len, sizeof(struct batadv_ogm_packet), "BAT IV OGM");
ether_header = (struct ether_header *)packet_buff; batman_ogm_packet = (struct batadv_ogm_packet *)(packet_buff + sizeof(struct ether_header)); @@ -494,14 +632,32 @@ static void dump_batman_iv_ogm(unsigned char *packet_buff, ssize_t buff_len, int printf("BAT %s: ", get_name_by_macaddr((struct ether_addr *)batman_ogm_packet->orig, read_opt));
- printf("OGM IV via neigh %s, seq %u, tq %3d, ttl %2d, v %d, flags [%c%c%c], length %zu\n", + tvlv_len = ntohs(batman_ogm_packet->tvlv_len); + printf("OGM IV via neigh %s, seq %u, tq %3d, ttl %2d, v %d, flags [%c%c%c], length %zu, tvlv_len %zu\n", get_name_by_macaddr((struct ether_addr *)ether_header->ether_shost, read_opt), ntohl(batman_ogm_packet->seqno), batman_ogm_packet->tq, batman_ogm_packet->ttl, batman_ogm_packet->version, (batman_ogm_packet->flags & BATADV_NOT_BEST_NEXT_HOP ? 'N' : '.'), (batman_ogm_packet->flags & BATADV_DIRECTLINK ? 'D' : '.'), (batman_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP ? 'F' : '.'), - (size_t)buff_len - sizeof(struct ether_header)); + check_len, tvlv_len); + + check_len -= sizeof(struct batadv_ogm_packet); + LEN_CHECK(check_len, (size_t)tvlv_len, "BAT OGM TVLV (containers)"); + + ptr = (uint8_t *)(batman_ogm_packet + 1); + + while (tvlv_len > 0) { + tvlv_hdr = (struct batadv_tvlv_hdr *)ptr; + len = ntohs(tvlv_hdr->len); + + parser = tvlv_parsers[tvlv_hdr->type][tvlv_hdr->version - 1]; + parser(tvlv_hdr + 1, len); + + /* go to the next container */ + ptr = (uint8_t *)(tvlv_hdr + 1) + len; + tvlv_len -= sizeof(*tvlv_hdr) + len; + } }
static void dump_batman_icmp(unsigned char *packet_buff, ssize_t buff_len, int read_opt, int time_printed) @@ -683,6 +839,11 @@ static void parse_eth_hdr(unsigned char *packet_buff, ssize_t buff_len, int read case BATADV_UNICAST_4ADDR: if (dump_level & DUMP_TYPE_BATUCAST) dump_batman_4addr(packet_buff, buff_len, read_opt, time_printed); + case BATADV_UNICAST_TVLV: + if ((dump_level & DUMP_TYPE_BATUCAST) || + (dump_level & DUMP_TYPE_BATUTVLV)) + dump_batman_ucast_tvlv(packet_buff, buff_len, + read_opt, time_printed); break; default: fprintf(stderr, "Warning - packet contains unknown batman packet type: 0x%02x\n", batman_ogm_packet->packet_type); diff --git a/tcpdump.h b/tcpdump.h index cfb1c69..5d936f2 100644 --- a/tcpdump.h +++ b/tcpdump.h @@ -41,6 +41,7 @@ #define DUMP_TYPE_BATICMP 2 #define DUMP_TYPE_BATUCAST 4 #define DUMP_TYPE_BATBCAST 8 +#define DUMP_TYPE_BATUTVLV 16 #define DUMP_TYPE_BATFRAG 32 #define DUMP_TYPE_NONBAT 128
On Sunday 02 November 2014 09:38:04 Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
OGMs and unicast TVLV packets carry TVLV containers as payload. With this patch such containers are now parsed and the relevant information is printed to screen.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
Applied in revision 4c39fb8.
Thanks, Marek
+typedef void (*batctl_tvlv_parser_t)(void *buff, ssize_t buff_len);
+/* location [i][j] contains the parsing function for TVLV of type 'i' and
- version 'j + 1'
- */
+batctl_tvlv_parser_t tvlv_parsers[][1] = {
[BATADV_TVLV_GW][0] = batctl_tvlv_parse_gw_v1,
[BATADV_TVLV_DAT][0] = batctl_tvlv_parse_dat_v1,
[BATADV_TVLV_NC][0] = batctl_tvlv_parse_nc_v1,
[BATADV_TVLV_TT][0] = batctl_tvlv_parse_tt_v1,
[BATADV_TVLV_ROAM][0] = batctl_tvlv_parse_roam_v1,
+};
[....]
while (tvlv_len > 0) {
tvlv_hdr = (struct batadv_tvlv_hdr *)ptr;
len = ntohs(tvlv_hdr->len);
parser = tvlv_parsers[tvlv_hdr->type][tvlv_hdr->version - 1];
parser(tvlv_hdr + 1, len);
/* go to the next container */
ptr = (uint8_t *)(tvlv_hdr + 1) + len;
tvlv_len -= sizeof(*tvlv_hdr) + len;
}
+}
[....]
while (tvlv_len > 0) {
tvlv_hdr = (struct batadv_tvlv_hdr *)ptr;
len = ntohs(tvlv_hdr->len);
parser = tvlv_parsers[tvlv_hdr->type][tvlv_hdr->version - 1];
parser(tvlv_hdr + 1, len);
/* go to the next container */
ptr = (uint8_t *)(tvlv_hdr + 1) + len;
tvlv_len -= sizeof(*tvlv_hdr) + len;
}
}
I've already explained this to Antonio but here again in public:
* neither type nor version are validated * the type can point in tvlv_parsers to an invalid (non-existing) entry * the version of this entry can also point to an invalid parser * this is a big problem because I can crash batctl td with data packets send from other people (even valid data packets with mcast tvlv) * I've already experienced this problem when using nodes from a company which ships this patch since a while (> 4 months)
Maybe it can be redone with some switch statements or (more space consuming) full tables which can handle all input data. Of course a check of the returned parser is still necessary inside the loops.
Kind regards, Sven
On Tuesday 11 November 2014 23:56:03 Sven Eckelmann wrote: [...]
I've already explained this to Antonio but here again in public:
- neither type nor version are validated
- the type can point in tvlv_parsers to an invalid (non-existing) entry
- the version of this entry can also point to an invalid parser
- this is a big problem because I can crash batctl td with data packets send from other people (even valid data packets with mcast tvlv)
- I've already experienced this problem when using nodes from a company which ships this patch since a while (> 4 months)
Maybe it can be redone with some switch statements or (more space consuming) full tables which can handle all input data. Of course a check of the returned parser is still necessary inside the loops.
Kind regards, Sven
For people who require a direct experience of the crash with normal traffic send by batman-adv:
1. compile batctl: CFLAGS="-g3 -fsanitize=address -fsanitize=undefined -fsanitize=leak" make 2. Start it on some interfaceL sudo ./batctl td eth0 3. run on another console tcpreplay on the same interface: sudo tcpreplay -i eth0 v15_iv_ogm.pcapng 4. experience the crash:
10:17:28.724487 BAT 00:21:cc:b4:82:33: OGM IV via neigh 00:21:cc:b4:82:33, seq 3093200194, tq 255, ttl 50, v 15, flags [..F], length 68, tvlv_len 44 TVLV TTv1: OGM DIFF [.] ttvn=4 vlan_num=1 entry_num=0 VLAN ID -1, crc 0xb8d4beb4 TVLV GWv1: down 10.0Mbps, up 2.0Mbps ================================================================= ==4213==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000639410 at pc 0x415f75 bp 0x7ffff4a02c70 sp 0x7ffff4a02c68 READ of size 8 at 0x000000639410 thread T0 #0 0x415f74 in dump_batman_iv_ogm batctl/tcpdump.c:654 #1 0x41710e in parse_eth_hdr batctl/tcpdump.c:825 #2 0x4195c8 in tcpdump batctl/tcpdump.c:1118 #3 0x403b7a in main batctl/main.c:146 #4 0x7fd0f52dbb44 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b44) #5 0x4029d8 (batctl+0x4029d8)
0x000000639410 is located 0 bytes to the right of global variable 'tvlv_parsers' from 'tcpdump.c' (0x6393e0) of size 48 0x000000639410 is located 48 bytes to the left of global variable '*.Lubsan_type7' from 'tcpdump.c' (0x639440) of size 4 SUMMARY: AddressSanitizer: global-buffer-overflow batctl/tcpdump.c:654 dump_batman_iv_ogm Shadow bytes around the buggy address: 0x0000800bf230: 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 00 f9 f9 f9 0x0000800bf240: f9 f9 f9 f9 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 0x0000800bf250: 00 00 00 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 0x0000800bf260: 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 0x0000800bf270: 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 =>0x0000800bf280: 00 00[f9]f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 0x0000800bf290: 00 00 00 00 00 00 00 00 00 00 00 00 00 f9 f9 f9 0x0000800bf2a0: f9 f9 f9 f9 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 0x0000800bf2b0: 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00 0x0000800bf2c0: 00 f9 f9 f9 f9 f9 f9 f9 04 f9 f9 f9 f9 f9 f9 f9 0x0000800bf2d0: 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Contiguous container OOB:fc ASan internal: fe ==4213==ABORTING
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org