ongrep

A cleaned up fork of ngrep for OpenBSD
git clone git://git.sgregoratto.me/ongrep
Log | Files | Refs | README | LICENSE

commit ce64df46ddbc4d059b7c59a958f4a65da543b303
parent 13dd4f236c6289e0920c8cbbc93ad1a5c148bb77
Author: Jordan Ritter <jpr5@darkridge.com>
Date:   Wed,  6 Sep 2017 13:44:42 -0700

Fix pcap datalink bug introducd with VLAN changes

Context: a bug report came in while ago on ngrep for not "seeing" TCP
frames in a pcap dump, when there obviously were TCP frames present.
That ended up being due to the fact that the PCAP data had been captured
off a VLAN, and thus had VLAN headers embedded in the frames.  Ended up
not being ngrep's fault per se; all the user had to do was specify
`vlan` in their BPF filter and PCAP would deliver the frames to ngrep.
But for a netops person moving across lots of different interfaces and
pcap files quickly, it's hard to keep track and you kind of just want it
to work.  Favoring convenience at the time, I thought it would be both
helpful and harmless to include VLAN frames automatically in the BPF
filter.

Turns out it wasn't harmless.  I found newer bug reports for "no VLAN
support for data link type 113", which is the PCAP error message you get
when you combine `vlan` in your BPF filter with frames that come from a
Linux "cooked" socket (what you get with `-d any`).

I'm still working out what to do in the long run, around Just Working
when VLANs are in the mix.  In the meantime, this reworks how the
filters are constructed, introduces the `include_vlan` flag, switches it
off when `-d any` is specified, and moves BPF filter construction to
later in the initialization so there's more opportunity to conditionally
switch it off (e.g. when reading from a `DLT_LINUX_SLL` device).

Diffstat:
Mngrep.c | 156+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mngrep.h | 10++++------
2 files changed, 89 insertions(+), 77 deletions(-)

diff --git a/ngrep.c b/ngrep.c @@ -158,6 +158,7 @@ char *filter = NULL, *filter_file = NULL; char pc_err[PCAP_ERRBUF_SIZE]; uint8_t link_offset; uint8_t radiotap_present = 0; +uint8_t include_vlan = 1; pcap_t *pd = NULL, *pd_dumppcap = NULL; pcap_dumper_t *pd_dump = NULL; @@ -261,6 +262,9 @@ int main(int argc, char **argv) { clean_exit(-1); case 'd': usedev = optarg; + /* Linux: any = DLT_LINUX_SLL, pcap says incompatible with VLAN */ + if (!strncasecmp(usedev, "any", 3)) + include_vlan = 0; break; #endif case 'c': @@ -360,6 +364,8 @@ int main(int argc, char **argv) { tcpkill_init(); #endif + /* Setup PCAP input */ + if (read_file) { if (!(pd = pcap_open_offline(read_file, pc_err))) { @@ -405,6 +411,73 @@ int main(int argc, char **argv) { } } + /* Setup link header offset */ + + switch(pcap_datalink(pd)) { + case DLT_EN10MB: + link_offset = ETHHDR_SIZE; + break; + + case DLT_IEEE802: + link_offset = TOKENRING_SIZE; + break; + + case DLT_FDDI: + link_offset = FDDIHDR_SIZE; + break; + + case DLT_SLIP: + link_offset = SLIPHDR_SIZE; + break; + + case DLT_PPP: + link_offset = PPPHDR_SIZE; + break; + +#if HAVE_DLT_LOOP + case DLT_LOOP: +#endif + case DLT_NULL: + link_offset = LOOPHDR_SIZE; + break; + +#if HAVE_DLT_RAW + case DLT_RAW: + link_offset = RAWHDR_SIZE; + break; +#endif + +#if HAVE_DLT_LINUX_SLL + case DLT_LINUX_SLL: + link_offset = ISDNHDR_SIZE; + include_vlan = 0; + break; +#endif + +#if HAVE_DLT_IEEE802_11_RADIO + case DLT_IEEE802_11_RADIO: + radiotap_present = 1; +#endif + +#if HAVE_DLT_IEEE802_11 + case DLT_IEEE802_11: + link_offset = IEEE80211HDR_SIZE; + break; +#endif + +#if HAVE_DLT_PFLOG + case DLT_PFLOG: + link_offset = PFLOGHDR_SIZE; + break; +#endif + + default: + fprintf(stderr, "fatal: unsupported interface type %u\n", pcap_datalink(pd)); + clean_exit(-1); + } + + /* Setup BPF filter */ + if (filter_file) { char buf[1024] = {0}; FILE *f = fopen(filter_file, "r"); @@ -440,7 +513,7 @@ int main(int argc, char **argv) { } } else { - filter = strdup(BPF_FILTER_IP); + filter = include_vlan ? strdup(BPF_TEMPLATE_IP_VLAN) : strdup(BPF_TEMPLATE_IP); if (pcap_compile(pd, &pcapfilter, filter, 0, mask.s_addr)) { pcap_perror(pd, "pcap compile"); @@ -456,6 +529,8 @@ int main(int argc, char **argv) { clean_exit(-1); } + /* Setup matcher */ + if (match_data) { if (bin_match) { uint32_t i = 0, n; @@ -568,67 +643,6 @@ int main(int argc, char **argv) { if (filter) free(filter); if (re_match_word) free(match_data); - switch(pcap_datalink(pd)) { - case DLT_EN10MB: - link_offset = ETHHDR_SIZE; - break; - - case DLT_IEEE802: - link_offset = TOKENRING_SIZE; - break; - - case DLT_FDDI: - link_offset = FDDIHDR_SIZE; - break; - - case DLT_SLIP: - link_offset = SLIPHDR_SIZE; - break; - - case DLT_PPP: - link_offset = PPPHDR_SIZE; - break; - -#if HAVE_DLT_LOOP - case DLT_LOOP: -#endif - case DLT_NULL: - link_offset = LOOPHDR_SIZE; - break; - -#if HAVE_DLT_RAW - case DLT_RAW: - link_offset = RAWHDR_SIZE; - break; -#endif - -#if HAVE_DLT_LINUX_SLL - case DLT_LINUX_SLL: - link_offset = ISDNHDR_SIZE; - break; -#endif - -#if HAVE_DLT_IEEE802_11_RADIO - case DLT_IEEE802_11_RADIO: - radiotap_present = 1; -#endif - -#if HAVE_DLT_IEEE802_11 - case DLT_IEEE802_11: - link_offset = IEEE80211HDR_SIZE; - break; -#endif - -#if HAVE_DLT_PFLOG - case DLT_PFLOG: - link_offset = PFLOGHDR_SIZE; - break; -#endif - - default: - fprintf(stderr, "fatal: unsupported interface type %u\n", pcap_datalink(pd)); - clean_exit(-1); - } if (dump_file) { pd_dump = pcap_dump_open(pd, dump_file); @@ -673,7 +687,7 @@ static inline uint8_t vlan_frame_count(u_char *p, uint16_t limit) { } void process(u_char *d, struct pcap_pkthdr *h, u_char *p) { - uint8_t vlan_offset = vlan_frame_count(p, h->caplen) * VLANHDR_SIZE; + uint8_t vlan_offset = include_vlan ? vlan_frame_count(p, h->caplen) * VLANHDR_SIZE : 0; struct ip *ip4_pkt = (struct ip *) (p + link_offset + vlan_offset); #if USE_IPv6 @@ -1123,6 +1137,7 @@ void dump_formatted(unsigned char *data, uint32_t len, uint16_t mindex, uint16_t } char *get_filter_from_string(char *str) { + const char *template = include_vlan ? BPF_TEMPLATE_USERSPEC_IP_VLAN : BPF_TEMPLATE_USERSPEC_IP; char *mine, *s; uint32_t len; @@ -1135,17 +1150,18 @@ char *get_filter_from_string(char *str) { if (*s == '\r' || *s == '\n') *s = ' '; - if (!(mine = (char*)malloc(len + sizeof(BPF_MAIN_FILTER)))) + if (!(mine = (char*)malloc(len + strlen(template) + 1))) return NULL; - memset(mine, 0, len + sizeof(BPF_MAIN_FILTER)); + memset(mine, 0, len + strlen(template) + 1); - sprintf(mine, BPF_MAIN_FILTER, str); + sprintf(mine, template, str); return mine; } char *get_filter_from_argv(char **argv) { + const char *template = include_vlan ? BPF_TEMPLATE_USERSPEC_IP_VLAN : BPF_TEMPLATE_USERSPEC_IP; char **arg = argv, *theirs, *mine; char *from, *to; uint32_t len = 0; @@ -1157,11 +1173,11 @@ char *get_filter_from_argv(char **argv) { len += (uint32_t)strlen(*arg++) + 1; if (!(theirs = (char*)malloc(len + 1)) || - !(mine = (char*)malloc(len + sizeof(BPF_MAIN_FILTER)))) + !(mine = (char*)malloc(len + strlen(template) + 1))) return NULL; memset(theirs, 0, len + 1); - memset(mine, 0, len + sizeof(BPF_MAIN_FILTER)); + memset(mine, 0, len + strlen(template) + 1); arg = argv; to = theirs; @@ -1171,7 +1187,7 @@ char *get_filter_from_argv(char **argv) { *(to-1) = ' '; } - sprintf(mine, BPF_MAIN_FILTER, theirs); + sprintf(mine, template, theirs); free(theirs); return mine; @@ -1560,5 +1576,3 @@ char *win32_choosedevice(void) { return dev; } #endif - - diff --git a/ngrep.h b/ngrep.h @@ -66,10 +66,10 @@ #define BPF_FILTER_IP_TYPE "(ip)" #endif -#define BPF_FILTER_IP "(" BPF_FILTER_IP_TYPE " || (vlan && " BPF_FILTER_IP_TYPE "))" - -#define BPF_FILTER_OTHER "( %s) and " -#define BPF_MAIN_FILTER BPF_FILTER_OTHER BPF_FILTER_IP +#define BPF_TEMPLATE_IP BPF_FILTER_IP_TYPE +#define BPF_TEMPLATE_IP_VLAN "(" BPF_FILTER_IP_TYPE " || (vlan && " BPF_FILTER_IP_TYPE "))" +#define BPF_TEMPLATE_USERSPEC_IP "( %s) and " BPF_TEMPLATE_IP +#define BPF_TEMPLATE_USERSPEC_IP_VLAN "( %s) and " BPF_TEMPLATE_IP_VLAN #define WORD_REGEX "((^%s\\W)|(\\W%s$)|(\\W%s\\W))" @@ -167,5 +167,3 @@ const char ANSI_bold[] = "\33[01m"; const char *ANSI_hilite = ANSI_red; const char ANSI_off[] = "\33[00m"; - -