tcpdump crashes in print-cdp.c

2016-03-23 Thread Can Erkin Acar
Here is a new version of the diff that fixes the CDP printer in tcpdump.
(https://www.sccs.swarthmore.edu/users/16/mmcconv1/dump/tcpdump-crashes/)

With this change the code does not access outside the packet anymore
but it would be nice if it could be tested on a network with real CDP
(Cisco Discovery Protocol) packets to see if there is any change in the
output. Error checking may have become too aggressive.

Thanks,

Can

Index: print-cdp.c
===
RCS file: /cvs/src/usr.sbin/tcpdump/print-cdp.c,v
retrieving revision 1.5
diff -u -p -u -p -r1.5 print-cdp.c
--- print-cdp.c 16 Jan 2015 06:40:21 -  1.5
+++ print-cdp.c 23 Mar 2016 06:09:31 -
@@ -39,7 +39,7 @@
 #include "addrtoname.h"
 #include "extract.h"   /* must come after interface.h */

-void cdp_print_addr(const u_char * p, int l);
+int cdp_print_addr(const u_char * p, int l);
 void cdp_print_prefixes(const u_char * p, int l);

 /*
@@ -65,7 +65,7 @@ cdp_print(const u_char *p, u_int length,

while (i < length) {
if (i + 4 > caplen) {
-   printf("[!cdp]");
+   printf("[|cdp]");
return;
}

@@ -75,8 +75,11 @@ cdp_print(const u_char *p, u_int length,
if (vflag)
printf(" %02x/%02x", type, len);

+   if (len < 4)
+   goto error;
+
if (i+len > caplen) {
-   printf("[!cdp]");
+   printf("[|cdp]");
return;
}

@@ -86,12 +89,15 @@ cdp_print(const u_char *p, u_int length,
break;
case 0x02:
printf(" Addr");
-   cdp_print_addr(p + i + 4, len - 4);
+   if (cdp_print_addr(p + i + 4, len - 4))
+   goto error;
break;
case 0x03:
printf(" PortID '%.*s'", len - 4, p + i + 4);
break;
case 0x04:
+   if (len < 8)
+   goto error;
printf(" CAP 0x%02x", (unsigned) p[i+7]);
break;
case 0x05:
@@ -110,9 +116,13 @@ cdp_print(const u_char *p, u_int length,
printf(" VTP-Management-Domain '%.*s'", len-4, p+i+4 );
break;
case 0x0a:  /* guess - not documented */
+   if (len < 6)
+   goto error;
printf(" Native-VLAN-ID %d", (p[i+4]<<8) + p[i+4+1] - 1 
);
break;
case 0x0b:  /* guess - not documented */
+   if (len < 5)
+   goto error;
printf(" Duplex %s", p[i+4] ? "full": "half" );
break;
default:
@@ -124,42 +134,59 @@ cdp_print(const u_char *p, u_int length,
break;
i += len;
}
+error:
+   printf("[!cdp]");
 }

-void
+#define CDP_CHECK_ACCESS(p, s) if ((endp - (p)) < (s)) return 1
+
+int
 cdp_print_addr(const u_char * p, int l)
 {
-   int pl, al, num;
+   int pl, pt, al, num;
const u_char * endp = p+l;

+   CDP_CHECK_ACCESS(p, 4);
num = (p[0] << 24) + (p[1]<<16) + (p[2]<<8)+ p[3];
p+=4;

printf(" (%d): ", num);

while(p < endp && num >= 0) {
+   CDP_CHECK_ACCESS(p, 2);
+   pt=*p;
pl=*(p+1);
p+=2;

+   CDP_CHECK_ACCESS(p, 3);
/* special case: IPv4, protocol type=0xcc, addr. length=4 */
if (pl == 1 && *p == 0xcc && p[1] == 0 && p[2] == 4) {
p+=3;

+   CDP_CHECK_ACCESS(p, 4);
printf("IPv4 %d.%d.%d.%d ", p[0], p[1], p[2], p[3]);
p+=4;
} else {/* generic case: just print raw data */
-   printf("pt=0x%02x, pl=%d, pb=", *(p-2), pl);
+   printf("pt=0x%02x, pl=%d, pb=", pt, pl);
+
+   CDP_CHECK_ACCESS(p, pl);
while(pl-- > 0)
printf(" %02x", *p++);
+
+   CDP_CHECK_ACCESS(p, 2);
al=(*p << 8) + *(p+1);
printf(", al=%d, a=", al);
p+=2;
+
+   CDP_CHECK_ACCESS(p, al);
while(al-- > 0)
printf(" %02x", *p++);
}
printf("  ");
num--;
}
+
+   return 0;
 }


@@ -169,7 +196,8 @@ cdp_print_prefixes(const u_char * p, int
printf(" IPv4 Prefixes 

Test Request: variable-size DLT headers in libpcap

2010-07-05 Thread Can Erkin Acar
Please test the attached diff with tcpdump and pflogd
filter expressions you commonly use to make sure it
does not break anything.

This is needed for Henning's work on pflog.


Instructions:

1. Apply the patch under /usr/src/lib/libpcap,
2. re-build libpcap and install it.
3. restart pflogd

The patch changes the way BPF code is generated from
filter expressions for the pflog interface, to allow
for variable-sized pflog headers.


Things to test for:

1. Run your favorite filter expressions on both
pflog and non-pflog interfaces, and make sure
the packets are filtered correctly.

2. Make sure pflogd still generates correct logs,
especially if you are using a filter expression.

3. Try complex filter expressions on the pflog interface
and see if they still fit the memory (there is an
upper bound to the number of instructions, and this
diff increases the size of the generated code).

4. dump the result of some tcpdump filter code before
and after applying this patch for *non-pflog*
interfaces. There should be *no change*.

You can use '-d' switch to tcpdump to dump the filter code.
You can use '-O' to disable optimization.

   Example: tcpdump -i em0 -d host 1.2.3.4 and port 80


Please let me know about your test results,
what you tested, and the architecture you tested on.



Background:

Years ago, when designing the new pflog header,
we assumed that the header would have to be
extended in the future. We did not want to switch
DLT numbers again, so we added a 'size' field
which also doubled as a 'version' field.

This would allow us to add fields to the pflog header
in the future and still be backwards compatible.

Unfortunately, our plans were foiled by the
mess that was libpcap. The bpf filtering language
and filter code generator in libpcap was designed
with the assumption that the link layer has a
fixed size (this is also the reason why there are
two 802_11 header types for WiFi).

Now that Henning has some real need to add to the
pflog header, he asked me to revisit that code and
try to get bpf filtering work on variable sized
pflog headers.



Technical details:

BPF uses a very simple language for testing packets
against a filter language. The filter expression
you specify in tcpdump is parsed and assembled into
a bytecode that the kernel can use to match packets.

Each interface has a specific link-type (DLT_EN10MB
for ethernet, DLT_PFLOG for pflog etc.) and each
link type has a header infront of the packet itself.

The code generation itself is done in libpcap(4)
for simplicity, the size of the link-header for
each link-type is hardcoded into the code generator.

The diff modifies the code generator to handle
variable-sized link-headers. For pflog, which has
the header length in the header itself, the generator
generates additional BPF code to parse the pflog
link-header to determine the size of the link header
instead of assuming a fixed size.

The two important offsets based on the link header
are 1) the start of the link payload (eg. IP header)
and 2) the start of the protocol header (eg. TCP header)
these values require a number of BPF instructions to
compute, and used extensively by most filter constructs.
To prevent the code size from increasing too much, the
offsets are computed once, at the start, and the results
are placed in 'registers'.

Let me know if you have any questions.

Thanks,

Can
Index: gencode.c
===
RCS file: /cvs/src/lib/libpcap/gencode.c,v
retrieving revision 1.33
diff -u -p -r1.33 gencode.c
--- gencode.c   26 Jun 2010 16:47:07 -  1.33
+++ gencode.c   1 Jul 2010 04:51:46 -
@@ -99,6 +99,13 @@ static void free_reg(int);
 
 static struct block *root;
 
+/* initialization code used for variable link header */
+static struct slist *init_code = NULL;
+
+/* Flags and registers for variable link type handling */
+static int variable_nl;
+static int nl_reg, iphl_reg;
+
 /*
  * We divy out chunks of memory rather than call malloc each time so
  * we don't have to worry about leaking memory.  It's probably
@@ -126,7 +133,9 @@ static void backpatch(struct block *, st
 static void merge(struct block *, struct block *);
 static struct block *gen_cmp(u_int, u_int, bpf_int32);
 static struct block *gen_cmp_gt(u_int, u_int, bpf_int32);
+static struct block *gen_cmp_nl(u_int, u_int, bpf_int32);
 static struct block *gen_mcmp(u_int, u_int, bpf_int32, bpf_u_int32);
+static struct block *gen_mcmp_nl(u_int, u_int, bpf_int32, bpf_u_int32);
 static struct block *gen_bcmp(u_int, u_int, const u_char *);
 static struct block *gen_uncond(int);
 static __inline struct block *gen_true(void);
@@ -296,6 +305,12 @@ pcap_compile(pcap_t *p, struct bpf_progr
if (root == NULL)
root = gen_retblk(snaplen);
 
+   /* prepend initialization code to root */
+   if (init_code != NULL  root != NULL) {
+   sappend(init_code, root-stmts);
+   root-stmts =