the-tcpdump-group/libpcap

expression rejects all packets for IPv6 upper-layer protocol

yiyuandao opened this issue ยท 20 comments

Hi,

I tried to run following command:

tcpdump -i em1 'ip6 and udp[10]&1 !=0' 

and got the following:

tcpdump: expression rejects all packets

I checked the document pcap-filter.
and in the section 'expr relop expr':

Note that tcp,
udp and other upper-layer protocol types only apply to IPv4, not
IPv6 (this will be fixed in the future)

I wonder is there any plan to add this filter for IPv6?

Thanks.

@yiyuandao wanted to follow up on your filtering question for IPv6; You could try using running your UDP filter wide open and then using grep to capture everything coming across as IP6.

tcpdump -i em1 udp and 'udp[10] & 1!=0' -vvv | grep -i 'IP6'

This issue still exists (in tcp/udp). Is there a reason the syntax ought not to work? Since something like ip6 and tcp port 80 works fine.

If we do want syntax like ip6 and tcp[1] = 0x5 to work, do we want only tcp[1] = 0x5 to look for IPv6 in addition to IPv4? Or leave it as only IPv4?
tcp port 80 Checks for both so there is a little inconsistency.

What is the desired behaviour? I was thinking of making a PR with these changes but I'm not sure what would be preferred.

As the pcap-filter(7) man page explains it:

To access data inside the packet, use the following syntax:
proto [ expr : size ]
Note that tcp, udp and other upper-layer protocol types only apply to IPv4, not IPv6 (this will be fixed in the future).

Closing as resolved (not a bug, but an improvement that remains to be made).

Should this issue be left open to track it as an enhancement request?

Alright, let's keep it reopened for a while longer.

jviki commented

Is somebody working on this issue? What is to be implemented? If the compiler can generate instructions to find TCP port after IPv6 header, it should work for anything else inside the TCP header, shouldn't it?

This is an old known problem, nobody is working on it now, as far as I am aware. Competent volunteers are welcome to contribute a solution. Ideally it would be best to discuss the solution in sufficient detail before spending time on the code.

jviki commented

I agree. If it is not too complex, I believe I can provide some development capacity from my team. We would really like this thing to work, however, we have no experience with libpcap internals.

What can be some next step?

That might turn out a deeper rabbit hole than it seems because this problem is not to extend an existing dual-stack mapping of another packet data accessor onto UDP, but to introduce dual-stack mapping of packet data accessors for the first time.

In any case, it should be helpful to study this and this documents, then you can have a look at scanner.l, grammar.y and gencode.c and try to think what a good solution would look like. After that it would be a good time to confirm your findings here or on the mailing list before committing into the implementation.

Hopefully that's good enough a starting point.

I think the Wireshark web sites are being reworked. Down link reported on Discord.

The keynote itself can still be found on YouTube though: https://www.youtube.com/watch?v=XHlqIqPvKw8

A copy of the PDF is here.

Thank you for raising this, www.tcpdump.org is now using the copy for the documentation reference.

A possible work-around until a more robust solution is available: ip6 and (ip6[6] = 17) and ((ip6[40 + 8 + 2] & 1) != 0). Of course this only works if there are no IPv6 extension headers present, and I didn't combine the offsets intentionally so it's more obvious that we're skipping the standard 40 bytes of the IPv6 header, the 8 bytes of the UDP header and 2 additional bytes.

I think the Wireshark web sites are being reworked.

They are indeed being reworked, and a recent change inadvertently broke some links. https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf should be working again.

jviki commented

I did a quick research about the topic and performed some very basic experiments.

Here is bytecode for IPv4/TCP with dst port 80...

$ tcpdump -d 'ip and tcp dst port 80'
Warning: assuming Ethernet
(000) ldh      [12]
(001) jeq      #0x800           jt 2	jf 10
(002) ldb      [23]
(003) jeq      #0x6             jt 4	jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10	jf 6
(006) ldxb     4*([14]&0xf)
(007) ldh      [x + 16]                              ; loading dst port
(008) jeq      #0x50            jt 9	jf 10        ; testing for TCP dst port 80
(009) ret      #262144
(010) ret      #0

Bytecode for IPv4/TCP flags (currently same result as with tcp[tcpflags] & 2 != 0):

$ tcpdump -d 'ip and tcp[tcpflags] & 2 != 0'
Warning: assuming Ethernet
(000) ldh      [12]
(001) jeq      #0x800           jt 2	jf 10
(002) ldb      [23]
(003) jeq      #0x6             jt 4	jf 10
(004) ldh      [20]
(005) jset     #0x1fff          jt 10	jf 6
(006) ldxb     4*([14]&0xf)
(007) ldb      [x + 27]                              ; loading TCP flags
(008) jset     #0x2             jt 9	jf 10        ; testing for TCP flags SYN
(009) ret      #262144
(010) ret      #0

Here is bytecode for IPv6/TCP with dst port 80... It ignores IPv6 extension headers.

$ tcpdump -d 'ip6 and tcp dst port 80'
Warning: assuming Ethernet
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2	jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 4	jf 7
(004) ldh      [56]                                 ; loading dst port
(005) jeq      #0x50            jt 6	jf 7        ; testing for TCP dst port 80
(006) ret      #262144
(007) ret      #0

And for IPv6/TCP flags, it something like fails:

$ tcpdump -d 'ip6 and tcp[tcpflags] & 2 != 0'
Warning: assuming Ethernet
tcpdump: expression rejects all packets

I do not see any reason why the last expression would not give something like this:

(000) ldh      [12]
(001) jeq      #0x86dd          jt 2	jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 4	jf 7
(004) ldh      [69]                                 ; loading TCP flags
(005) jset     #0x2             jt 6	jf 7        ; testing for TCP flags SYN
(006) ret      #262144
(007) ret      #0

And expression tcp[tcpflags] & 2 != 0 should take advantage of such improvement as well and consider both IPv4 and IPv6. I think, generating such code would be the goal of this issue. I believe that the logic to be updated is in this switch case: https://github.com/the-tcpdump-group/libpcap/blob/master/gencode.c#L7483.

After that, the much more difficult part would be to parse IPv6 extension headers... But I consider it to be another story.

jviki commented

Loading of ports is regardless of L3 layer is done via gen_load_a(), the IPv6 case is here https://github.com/the-tcpdump-group/libpcap/blob/master/gencode.c#L1865. Thus, I assume that this logic can be somehow refactored/merged or something.

When you study the bytecode, it helps to remember that by default you see the optimized version. You could use BPF Exam to get a slightly better view of what comes out of gencode.c and what how it changes in optimize.c.