tcpdump 'ip6' filter doesn't work on wg0 (wireguard)

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

tcpdump 'ip6' filter doesn't work on wg0 (wireguard)

Matthieu Herrb-3
Hi,

Trying to look at IPv6 traffic on my wireguard VPN with

tcpdump -n -i wg0 ip6

also shows all IPv4 traffic. Other interfacees seem to filter the v6
protocol correctly.

Any suggestion before I try to dig into the kernel code (which I'm not
really familiar with) ?

--
Matthieu Herrb

Reply | Threaded
Open this post in threaded view
|

Re: tcpdump 'ip6' filter doesn't work on wg0 (wireguard)

Klemens Nanni-2
On Sun, Jul 19, 2020 at 02:24:36PM +0200, Matthieu Herrb wrote:
> Trying to look at IPv6 traffic on my wireguard VPN with
>
> tcpdump -n -i wg0 ip6
>
> also shows all IPv4 traffic. Other interfacees seem to filter the v6
> protocol correctly.
This happens for all interfaces without link-layer, e.g. lo(4) as well;
see `tcpdump -c1 -ilo0 ip & ping6 -qc1 ::1'.

> Any suggestion before I try to dig into the kernel code (which I'm not
> really familiar with) ?
Not yet, but I'm curiously looking at this.

Reply | Threaded
Open this post in threaded view
|

Re: tcpdump 'ip6' filter doesn't work on wg0 (wireguard)

David Gwynne-5
On Mon, Jul 20, 2020 at 05:45:52PM +0200, Klemens Nanni wrote:

> On Sun, Jul 19, 2020 at 02:24:36PM +0200, Matthieu Herrb wrote:
> > Trying to look at IPv6 traffic on my wireguard VPN with
> >
> > tcpdump -n -i wg0 ip6
> >
> > also shows all IPv4 traffic. Other interfacees seem to filter the v6
> > protocol correctly.
> This happens for all interfaces without link-layer, e.g. lo(4) as well;
> see `tcpdump -c1 -ilo0 ip & ping6 -qc1 ::1'.
>
> > Any suggestion before I try to dig into the kernel code (which I'm not
> > really familiar with) ?
> Not yet, but I'm curiously looking at this.

kn@ pointed me at this, and we came up with the following. firstly, we
narrowed the problem down to pcap not actually looking at the header to
decide if a packet was ipv4 or ipv6:

$ sudo tcpdump -i wg0 -d ip
(000) ret      #116
$ sudo tcpdump -i wg0 -d ip6
(000) ret      #116
$ sudo tcpdump -i gre0 -d ip
(000) ret      #116
$ sudo tcpdump -i gre0 -d ip6
(000) ret      #116

our tunnel interfaces pretty much all use DLT_LOOP as their link type,
so this behaviour is consistent across all of them.

why the filter unconditionally matches these packets is because of
this stuff in src/lib/libpcap/gencode.c. im including bits for DLT_NULL
for comparison:

static void
init_linktype(type)
        int type;
{
[snip]
        switch (type) {
[snip]
        case DLT_NULL:
                off_linktype = 0;
                off_nl = 4;
                return;
[snip]
        case DLT_LOOP:
                off_linktype = -1;
                off_nl = 4;
                return;
[snip]
}

the actual filter is generated in gen_linktype:

static struct block *
gen_linktype(proto)
        int proto;
{
        struct block *b0, *b1;

        /* If we're not using encapsulation and checking for IP, we're done */
        if ((off_linktype == -1 || mpls_stack > 0) && proto == ETHERTYPE_IP)
                return gen_true();
#ifdef INET6
        /* this isn't the right thing to do, but sometimes necessary */
        if ((off_linktype == -1 || mpls_stack > 0) && proto == ETHERTYPE_IPV6)
                return gen_true();
#endif

        switch (linktype) {
[snip]
        case DLT_LOOP:
        case DLT_ENC:
        case DLT_NULL:
                /* XXX */
                if (proto == ETHERTYPE_IP)
                        return (gen_cmp(0, BPF_W, (bpf_int32)htonl(AF_INET)));
#ifdef INET6
                else if (proto == ETHERTYPE_IPV6)
                        return (gen_cmp(0, BPF_W, (bpf_int32)htonl(AF_INET6)));
#endif /* INET6 */
                else
                        return gen_false();
                break;

cos init_linktype sets off_linktype to -1, gen_linktype thinks that
DLT_LOOP has no linktype header, and just assumes everything is both
ipv4 or ipv6.

DLT_LOOP does have a link type header though, so we should fix
init_linktypes. this is backed up by
https://www.tcpdump.org/linktypes.html.

this diff seems to work ok:

$ sudo tcpdump -i gre0 -d ip              
(000) ld       [0]
(001) jeq      #0x2000000       jt 2 jf 3
(002) ret      #116
(003) ret      #0
$ sudo tcpdump -i gre0 -d ip6
(000) ld       [0]
(001) jeq      #0x18000000      jt 2 jf 3
(002) ret      #116
(003) ret      #0

ok?

Index: gencode.c
===================================================================
RCS file: /cvs/src/lib/libpcap/gencode.c,v
retrieving revision 1.52
diff -u -p -r1.52 gencode.c
--- gencode.c 9 Dec 2018 15:07:06 -0000 1.52
+++ gencode.c 20 Jul 2020 23:59:44 -0000
@@ -770,7 +770,7 @@ init_linktype(type)
  return;
 
  case DLT_LOOP:
- off_linktype = -1;
+ off_linktype = 0;
  off_nl = 4;
  return;
 

Reply | Threaded
Open this post in threaded view
|

Re: tcpdump 'ip6' filter doesn't work on wg0 (wireguard)

Klemens Nanni-2
On Tue, Jul 21, 2020 at 10:02:55AM +1000, David Gwynne wrote:
> kn@ pointed me at this, and we came up with the following. firstly, we
> narrowed the problem down to pcap not actually looking at the header to
> decide if a packet was ipv4 or ipv6:
dlg did the hard work, really - without him I'd probably have spent
hours reading through code.

> our tunnel interfaces pretty much all use DLT_LOOP as their link type,
> so this behaviour is consistent across all of them.
>
> why the filter unconditionally matches these packets is because of
> this stuff in src/lib/libpcap/gencode.c. im including bits for DLT_NULL
> for comparison:
 
> DLT_LOOP does have a link type header though, so we should fix
> init_linktypes. this is backed up by
> https://www.tcpdump.org/linktypes.html.
tl;dr: DLT_NULL is for loopback interfaces but host byte order, so
sharing pcap files across different endian machines is problematic.

DLT_LOOP is network byte order and does not suffer from this, but seems
OpenBSD specific.

> this diff seems to work ok:
You can compare behaviour more easily like this:

        $ cd /usr/src/lib/libpcap
        $ make
        # LD_LIBRARY_PATH=./obj tcpdump -i lo0 -d ip
        # tcpdump -i lo0 -d ip

OK kn