ipfw firewall has `reass' action that does IP fragments reassembly and then pass the result for further processing.
Currently it works only for IPv4 and it just breaks IPv6 fragments. (See https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=170604)
For IPv4 the reass action uses ip_reass() function for reassembly. It produces the large packet, that can be refragmented again in case when firewall acts as router, because IPv4 router typically can do IPv4 fragmentation.
For IPv6 similar approach is not applicable. IPv6 router does not perform IP fragmentation. Thus if reassembled packet is not for our host, it will be dropped by ip6_forward() function.
The solution implemented in this patch is make IPv6 fragmentation for reassembled packets that is going to be forwarded.
I added ip6_reass() function, that is mostly the copy of frag6_input(), but it returns mbuf pointer, instead of next protocol. Also, it saves some information to help make fragmentation later if it is needed. frag6_input() now uses ip6_reass() function.
In ipfw(4) several new functions added:
- ipfw_reass() does IPv4/IPv6 reassembly if needed. This function is also called from ipfw_divert().
- ipfw_setnexthop() is used to setup mbuf's fwd_tag.
- ipfw_ip6_fragment() is used to make IPv6 fragmentation of reassembled packet.
How it is supposed to work:
ipfw reass rule consumes fragments until the last fragment is not received. Then it returns IP_FW_REASS return code to ipfw_check_packet(). ipfw_check_packet() remembers the fact that IPv6 reassembly was done and passes the reassembled packet to the next rule. When next matched rule will be found, we will make the decision about need of refragmentation.
In case of IP_FW_DENY or IP_FW_DIVERT we leave reassembled packet as is.
In case of IP_FW_PASS we check, that there is no fwd_tag. If fwd_tag is present and next hop is our own address, then fragmentation is not needed. When there is no fwd_tag, we check the destination address. For other cases we do IPv6 fragmentation using the maximum fragment size stored in the mbuf's non-persistent storage. Then all fragments are marked with M_SKIP_FIREWALL flag and queued via netisr queue.