nat: recompute IPv4 header checksum after pf rewrote the addresses (OpenBSD ip_output does this)

This commit is contained in:
lgcosta committed 2026-11-11 20:20:52 -03:00
1 parent d520172753
commit 917d3e3fe0
2 files changed
+42 -3

No files matched your search

+9
View File
@@ -6254,8 +6254,17 @@ pf_icmp_state_lookup(struct pf_pdesc *pd, struct pf_state_key_cmp *key,
&key->addr[0], &key->addr[1], 0, 0);
action = pf_find_state(pd, key, stp);
pr_info("pfk: ICMPDBG dir=%d sidx=%d icmp_dir=%d key0=%08x key1=%08x p0=%u p1=%u act=%d\n",
pd->dir, pd->sidx, icmp_dir, key->addr[0].v4.s_addr,
key->addr[1].v4.s_addr, key->port[0], key->port[1], action);
if (action != PF_MATCH)
return (action);
pr_info("pfk: ICMPDBG st wire0=%08x wire1=%08x wp0=%u wp1=%u stack0=%08x stack1=%08x\n",
(*stp)->key[PF_SK_WIRE]->addr[0].v4.s_addr,
(*stp)->key[PF_SK_WIRE]->addr[1].v4.s_addr,
(*stp)->key[PF_SK_WIRE]->port[0], (*stp)->key[PF_SK_WIRE]->port[1],
(*stp)->key[PF_SK_STACK]->addr[0].v4.s_addr,
(*stp)->key[PF_SK_STACK]->addr[1].v4.s_addr);
if ((*stp)->state_flags & PFSTATE_SLOPPY)
return (-1);
+33 -3
View File
@@ -92,6 +92,8 @@ pfk_reroute(struct sk_buff *skb)
fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
rt = ip_route_output_key(&init_net, &fl4);
pr_info("pfk: RERDBG src=%08x dst=%08x rt=%px dev=%s\n", fl4.saddr, fl4.daddr,
rt, (rt && !IS_ERR(rt) && rt->dst.dev) ? rt->dst.dev->name : "?");
if (IS_ERR(rt) || rt == NULL) {
kfree_skb(skb);
return;
@@ -102,18 +104,38 @@ pfk_reroute(struct sk_buff *skb)
rt->dst.output(&init_net, skb->sk, skb);
}
/*
* PFLINUX: pf rewrote the IPv4 addresses of the packet (NAT). OpenBSD's
* ip_output() recomputes the IP header checksum on every packet it sends;
* Linux does not, so fix it up here after pf touched the header.
*/
static void
pfk_fix_ip_csum(struct sk_buff *skb)
{
struct iphdr *iph;
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
return;
iph = ip_hdr(skb);
iph->check = 0;
iph->check = csum_fold(csum_partial(skb->data, iph->ihl * 4, 0));
/* the transport checksum was already fixed by pf */
skb->ip_summed = CHECKSUM_NONE;
}
static void
pfk_pkt_work(struct work_struct *w)
{
struct pfk_pkt *pk = container_of(w, struct pfk_pkt, work);
struct mbuf *m, *m0;
struct sk_buff *orig;
__be32 orig_dst = 0;
__be32 orig_dst = 0, orig_src = 0;
int action;
if (pk->af == AF_INET && pk->dir == PF_IN &&
pskb_may_pull(pk->skb, sizeof(struct iphdr)))
if (pk->af == AF_INET && pskb_may_pull(pk->skb, sizeof(struct iphdr))) {
orig_dst = ip_hdr(pk->skb)->daddr;
orig_src = ip_hdr(pk->skb)->saddr;
}
m = pfk_m_wrap_skb(pk->skb, pk->af);
if (!m) {
@@ -129,6 +151,12 @@ pfk_pkt_work(struct work_struct *w)
* and packet walk run concurrently from here on — documented. */
action = pf_test(pk->af, pk->dir, pk->ifp, &m);
if (pk->af == AF_INET && orig_src != 0 &&
pskb_may_pull(pk->skb, sizeof(struct iphdr)) &&
(ip_hdr(pk->skb)->saddr != orig_src ||
ip_hdr(pk->skb)->daddr != orig_dst))
pfk_fix_ip_csum(pk->skb);
if (m == m0) {
/* same buffer, possibly rewritten in place: our wrapper
@@ -138,6 +166,8 @@ pfk_pkt_work(struct work_struct *w)
pk->af == AF_INET && pk->dir == PF_IN &&
pskb_may_pull(pk->skb, sizeof(struct iphdr)) &&
ip_hdr(pk->skb)->daddr != orig_dst) {
pr_info("pfk: RERDBG trigger old=%08x new=%08x\n", orig_dst,
ip_hdr(pk->skb)->daddr);
/* pf translated the destination (reverse NAT):
* re-route instead of delivering locally */
pfk_reroute(pk->skb);