| Summary: | ipf packet munging bug using "to" option in rule | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Base System | Reporter: | Louis Mamakos <louie> | ||||
| Component: | kern | Assignee: | Darern Reed <darrenr> | ||||
| Status: | Closed FIXED | ||||||
| Severity: | Affects Only Me | CC: | net | ||||
| Priority: | Normal | ||||||
| Version: | 4.3-STABLE | ||||||
| Hardware: | Any | ||||||
| OS: | Any | ||||||
| Attachments: |
|
||||||
Responsible Changed From-To: freebsd-bugs->darrenr Darren is Mr. Ipfilter FWIW i've had this problem too, and the patch on this PR fixed it. -- Internet Vision Internet Consultancy Tel: 020 7589 4500 60 Albert Court & Web development Fax: 020 7589 4522 Prince Consort Road vision@ivision.co.uk London SW7 2BE http://www.ivision.co.uk/ State Changed From-To: open->closed this has been imported into -current and -stable |
I'm using ipf on a machine to perform some stupid packet tricks, including the dubious operation of fowarding traffic out an alternative network interface based on the source address of the packet. The reasons why are beyond the scope of this PR. I have a rule that looks something like this: pass out quick on de0 from any to any head 11 block out quick on de0 to tun0 from 144.202.0.0/16 to !144.202.0.0/16 group 11 ... The intent is that when packets from 144.202.0.0/16 arrive on this host, that they get forcebly shoved out the tun0 network interface, rather than taking the expect forwarding path via the de0 interface. This works MOST OF THE TIME. However, sending packets larger than a certain size results in the packet being mangled. It turns out this happens when the packet is large enough that it can no longer be contained within a single mbuf, and there's an mbuf cluster attached. There is code in sys/netinet/ip_fil.c:ipfr_fastroute() which does something like this (paraphrased):n ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); if (!ip->ip_sum) ip->ip_sum = in_cksum(m, hlen); error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); if (i) { ip->ip_id = ntohs(ip->ip_id); ip->ip_len = ntohs(ip->ip_len); ip->ip_off = ntohs(ip->ip_off); } The problem is that if the outbound network interface saves a pointer to the cluster, it will pick up up the version of the packet after it's been modified by the ntohs() calls after the if_output routine is called. In my case, this happens on a tunnel interface but other network interfaces which can DMA directly from the mbufs rather than copying the data will suffer from the same problem. Fix: I did a brutal hack in ipfr_fastroute() to make a copy of the mbuf if it's not M_WRITABLE(), and then arranged for the if (i) case in the code above to never get run to corrupt the data in the mbuf. I don't understand the global structure of the ipf code well enough to know if that's a "proper" way to solve the problem or not. After applying the workaround, traffic is now working where previously I was getting nasty checksum errors and the like due to some of the fields being byte-swapped. I would imagine that big-endian systems and those with network interfaces that require the transmit data to be copied wouldn't have seen this problem previously. How-To-Repeat: as described, sorta..