Now that ICMP traffic is fragmenting across the network, it's time to change and start using TCP traffic.
This is a follow on from MTU / Fragmentation / ACL Tests….Part 1 and I am aiming to find out what happens to fragmented packets when matched to static extended access lists.
I'm going to use a program called Ostinato for this. It's a freeware tool for generating packets, allowing you to control every field within the headers. This will allow me to generate specific size packets.
The plan is to generate a 200 byte packet (no fragments), a 600 byte packet (2 fragments) and a 1400 byte packet (3 fragments). The reason I've stuck below 1500 for the largest is to stop Windows getting involved, and make sure we aren't adding jumbo frames into the mix - it will just cloud what I am trying to achieve.
So the settings in Ostinato are:
Notice all of the black lines are called TCP Retransmission. This is just because I didn't change the TCP Sequence Number when I sent the packets, so they all had the same one.
I've been involved in some conversations recently regarding fragmentation of IP packets containing TCP and their effect on TCP Sequence numbers. However, if you look through this capture you can see that the TCP sequence number is unaffected by fragmentation. This is to be expected really, as TCP is a Layer 4 protocol, and the packet is fragmented and reassembled at Layer 3 - which should be a process that is transparent to the upper layers. The second fragment of the third packet (the larger 1400 byte one), only contains the 4 byte HDLC header, the 20 byte IP header, and 480 bytes of DEADFACE repeated, which is the data that is padding out the TCP packet. There is NO TCP information (source ports etc.) in the packet at all.
This then, is why access lists will have difficulty matching - if you are matching traffic that is port 80, and there is no port information in the fragment, then how will it react?
There are three scenarios which I want to try. I'll state them here, and guess the answers, then I'll go and try them.
I want to create an access list that matches ONLY traffic headed for 10.100.0.2 (R2) port 80, from 10.0.0.2 and denies all else. I think that that should allow the TCP packets, the same as above, to match...including the fragments.
Using the same access list, I want to send traffic to port 81. The access list should deny that. But I think that it should deny the initial packet, but allow the fragments, as the IP's still match - we'll see.
I then want to send traffic to 10.100.0.2 port 80, from a different IP address. I think this will behave as expected and deny all traffic.
I'll verify this using wireshark - assuming that if it doesn't arrive then it was filtered. Unfortunately I can't check the ACL counters, as the packet is checked against the ACL before it is fragmented.
So on R1:
ip access-list extended PORT80 10 permit tcp host 10.0.0.2 host 10.100.0.2 eq 80 log 20 deny ip any any log interface Serial 0/0 ip access-group PORT80 out
I've explicitly added the deny, although it would be there anyway, just so that I can see the counters increment should I decide to look at them.
And......it doesn't work. As expected, a packet headed to port 80 gets through. It comes through exactly as before, with three fragments. However, a packet headed to port 81 is blocked by the ACL and no fragments make it to the end router.
I have a theory for this - because the ACL is checked before transmission, the packet destined for port 81 matches the deny statement and gets binned, before it gets far enough down the protocol stack to be fragmented.
The next step then, is to add an R3, between R1 and R2, with both of it's interfaces having an MTU of 500. The packets will be fragmented at R1, and reach R3 - where an access list will check their exit before passing them to R2. I should then see this more clearly.
!!! Missing image
So there's the new lab. All of the serial interfaces have an MTU of 500, and there is EIGRP running between R1 and R3 just because I'm too lazy for static routes. With no access lists whatsoever, I am back to where I was - a packet of 1400 bytes appears at S0/0 on R2 as three fragments.
Now I'm going to apply the access list I used earlier to R3 S0/0 outbound. Hopefully, I should now be able to use wireshark, and also the access list counters in IOS to see the matches.
I'm now going to generate 3 packets:
- src 10.0.0.2:80 dst 10.100.0.2:80
- src 10.0.0.2:80 dst 10.100.0.2:81
- src 10.0.0.5:80 dst 10.100.0.2:80
And (drum roll.....)......excitingly.......here's the results:
R3#sh ip access-list PORT80 Extended IP access list PORT80 10 permit tcp host 10.0.0.2 host 10.100.0.2 eq www log (5 matches) 20 deny ip any any log (4 matches)
Perfect! 5 matches for the permit statement - thats three fragments from the first packet and the two non-initial fragments from the second packet. And 4 matches for the deny statement - thats the initial fragment from the second packet, and all three from the third.
That is exactly what I expected to see. The logic is along the lines of:
"I know it's a fragment, and it has no TCP information to match my access list. However, the source and destination match, so it may be a part of a stream I should permit. I'll allow it anyway, because I know that I will accurately match the packet containing the TCP header as it passes through me - if I should deny it, then the end host will never receive the TCP header and never be able to re-assemble the packet, so I'm only permitting junk anyway."
Just for completeness, here is the wireshark output from this:
!!! Missing image
So in summary then:
For non-fragmented packets, or initial fragments of a fragmented sequence, a static access-list will behave exactly as expected. For non-initial fragments containing no TCP header, the packet will be matched on layer 3 information (source and destination IP) only, and if it matches will be permitted. This does mean that an access list will allow non-initial fragments destined to the wrong port, but they will be dropped by the end host anyway as the initial fragment will never arrive.
All of this can be backed up by a Cisco Whitepaper, found at: Access Control Lists and IP Fragments. This is just my practical verification.
I hope somebody finds this useful. As usual, if anyone finds any errors, has any questions, or wants to discuss it, then please get in touch.