Tunnels

Hi,

in NetSessions::DispatchPacket() Bro has some code to decapsulate some tunnels.

The first one uses encap_hdr_size (and udp_tunnel_port). It appears that it expects an IP header and if it finds one(*) and if encap_hdr_size>0 then it will add skip encap_hdr_size and assume that the "interesting" part of the packet starts there. I was wondering why the code checks whether there is an IP header before skipping the fixed encap_hdr_bytes? Note that there is also the option to only skip if the packet is UDP and has a particular destination port (udp_tunnel_port). For this we do need to look for the IP header....
I think there might be a misplaced "else" here (i.e., the else belongs to a different if)

(*) it assumes there is one and just checks whether the caplen is long enough to contain the full IP header according to IP header length.

The other way it does tunnel decapsulation is done when parse_udp_tunnels is set. If the packet contains an IP header + UDP header the code checks whether the UDP payload looks like an IP packet and if so, then it will skip over these initial IP+UDP header. (Thus this code decapsulates IP-over-UDP). If udp_tunnel_port is specified then this code will limit its decapsulation to UDP packet with that destination port.

Some problems/concerns/questions:

* What was the intention of this code?
* All this happens before IP fragmentation. Shouldn't this be after
   defragmentation (and then re-inject the decapsulated packets before
   the de-fragmentation in the case that the tunneled IP is fragmented
   as well)
* The whole code is hard-code for IPv4 and doesn't check whether the
   initial IP header is actually a valid IP header (it assumes it is and
   just used IP header length). For IPv6 or ARP packets this code would
   obviously be incorrect (and possibly the code would incorrectly
   decapsulate something it shouldn't)
* While the code checks that there is enough packet data for the IP
   header, it does not check whether there is enough data for the UDP
   header before accessing uh_len and uh_dport.
   In the 2nd case it also doesn't check whether there's enough captured
   data to contain the encapsulated packet (Bro does look at this data
   for its heuristic that checks whether there is an IP header here)
* What should we do with this? Fix? Remove? Modify?

Note, the reason I was looking at this code is that I want to write something that can decapsulate tunneled IPv6 packets (6in4, 6to4, Teredo).

cu
Gregor

* What was the intention of this code?

There is a lot of guessing here as the code was written before I started working with Bro. The only way I have seen encap_hdr_size used is to remove VLAN headers. That is done properly now so that use of the encap_hdr_size variable is gone.

I have no clue what UDP tunnels were being decapsulated though. In my opinion it was probably something that should have been done with an analyzer anyway and is probably ok to remove.

* All this happens before IP fragmentation.

Like above, I think it's because it's to remove lower level encapsulation.

* What should we do with this? Fix? Remove? Modify?

I say remove. I was tempted to do it when I fixing the VLAN and MPLS handling but I had the same questions as you.

Note, the reason I was looking at this code is that I want to write
something that can decapsulate tunneled IPv6 packets (6in4, 6to4, Teredo).

I have code to do this written for Click! and it's fortunately very easy (I have AYIYA decapsulation code too). The problem is that you have two IP layer headers and only the one with your address space makes any sense. It's really confusing to see two non-local networks show up in your conn.log because it was a local host using tunneled address space.

I think we need the ability in the connection record to specify a parent connection or something. I suppose even just logging the presence of a tunnel may be enough. There are a lot of questions that will need answered once we start decapsulating tunnels.

I say go for it! :slight_smile:

  .Seth

[was used for VLAN]

Hmm. Interesting. In this case I'm surprised it worked because the way I read the code, it always assumes that there is an IP header. It cast the pkt pointer into a struct ip* and checks whether the caplen is large enough to the IP header length from this struct ip* (which should work often enough because the max header length is 64 bytes) but some short packets might have been missed by that code.

Note, the reason I was looking at this code is that I want to write
something that can decapsulate tunneled IPv6 packets (6in4, 6to4, Teredo).

I have code to do this written for Click! and it's fortunately very easy (I have AYIYA decapsulation code too).

Cool. I've actually never used Click, but yeah, the actual decapsulation is really quite easy. Deciding what to do with the parent connection (and how to implement that) is the hard part....

The problem is that you have two IP layer headers and only the one with your address space makes any sense. It's really confusing to see two non-local networks show up in your conn.log because it was a local host using tunneled address space.

The right way to do that is to pull IP analysis into the analyzer tree and then do tunnel de-capsulation using the analyzer tree. Then you could even decapsulate IP-over-HTTP-over-whatever. But that's going to be a lot of work. My plan was to do a simpler work-around though:

I think we need the ability in the connection record to specify a parent connection or something. I suppose even just logging the presence of a tunnel may be enough. There are a lot of questions that will need answered once we start decapsulating tunnels.

Yeah. I haven't looked into that too much yet. But I was thinking of either (or possibly both configureable)
   * removing the parent headers completely but generate an event to
     associate the child connection with the parent connection (1)
   * create a copy for the packet data of the child . Thus Bro would see
     both the parent connection and the decapsulated connection. One
     would still want an event to link the two and you'll have two
     packets with the same timestamp and the total byte counts will be
     inflated.....

(1) not that it's not necessarily a connection. 6to4 and 6in4 are directly on top of IP (using proto 41). So Bro wouldn't even see these packets because it only handles TCP/UDP/ICMP (you would get a weird though)

cu
Gregor

> [was used for VLAN]

Hmm. Interesting. In this case I'm surprised it worked because the way I
read the code, it always assumes that there is an IP header.

Quick comment: this was an earlier hack to deal with IP-in-UDP tunneling
used for our honeynet setup.

    Vern

* removing the parent headers completely but generate an event to
   associate the child connection with the parent connection (1)

That would probably work well once we figured out how to properly deal with (1). :slight_smile:

(1) not that it's not necessarily a connection. 6to4 and 6in4 are directly on top of IP (using proto 41). So Bro wouldn't even see these packets because it only handles TCP/UDP/ICMP (you would get a weird though)

Almost makes me wonder if eventually we'd want to have fake IP connections similarly to the fake udp connections?

  .Seth

Almost makes me wonder if eventually we'd want to have fake IP connections similarly to the fake udp connections?

I'm not following this. Seems we'd instead want (1) a one-time event
that identifies the presence of a tunnel, (2) regular processing (via
an analyzer chain) of the traffic inside the tunnel, and (3) a way to
tell that a give connection record (or other network event) ultimately
stems from tunneled traffic.

    Vern

Yep, your way's much simpler. :slight_smile:

  .Seth