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;