calipso_skbuff_setattr() adjusts payload_len by the length of the option it inserts or resizes: payload = ntohs(ip6_hdr->payload_len); ip6_hdr->payload_len = htons(payload + len_delta); GRO can build BIG TCP packets larger than IPV6_MAXPLEN, and stores payload_len == 0 for them; ipv6_payload_len() then falls back to skb->len. For such a packet forwarded to a destination mapped to CALIPSO, the code above writes len_delta into payload_len, and ipv6_payload_len() no longer falls back to skb->len. nf_tables, conntrack and sch_cake rely on it. Leave payload_len alone when it is 0, as calipso_skbuff_delattr() does. Tested on one kernel (arm64, QEMU) with SELinux permissive, a minimal mdp-generated policy (network_peer_controls=1), "netlabelctl calipso add pass doi:16" and a default NetLabel mapping of fd00:2::/64 to CALIPSO DOI 16: - calipso_skbuff_setattr() called through netlbl_skbuff_setattr() from a test-only debugfs hook (not part of this patch, see below) on a 70000 byte GSO TCPv6 skb with payload_len 0: before: payload_len 16, ipv6_payload_len() 16 after: payload_len 0, ipv6_payload_len() 70016 (== skb->len - 40) and on a 1000 byte skb: payload_len 1000 -> 1016 both before and after. - Forward path, client -> router -> server network namespaces, the client injects 5 unlabeled UDP datagrams with AF_PACKET (to avoid socket labeling): the router adds the CALIPSO option, the server sees nexthdr 0 with payload_len 31 (15 + 16) and delivers all 5 datagrams (Udp6InDatagrams 5, Ip6InTruncatedPkts 0), with and without this patch. Fixes: 81fbc812132c ("ipv6/gro: insert temporary HBH/jumbo header") Suggested-by: Eric Dumazet Signed-off-by: Joas Antonio dos Santos Assisted-by: Claude:claude-opus-5-5 --- This is the separate patch for calipso_skbuff_setattr() mentioned in the review of "calipso: update payload_len when removing the CALIPSO option". Test-only debugfs hook used above (built in with obj-y in net/netlabel/Makefile, triggered with "echo 1 > /sys/kernel/debug/calipso_selftest"): // SPDX-License-Identifier: GPL-2.0 /* TEST ONLY, not for submission: drive calipso_skbuff_setattr() through * netlbl_skbuff_setattr() with a normal and a BIG TCP sized skb. * Needs a NetLabel default mapping to CALIPSO (netlabelctl). * Trigger: echo 1 > /sys/kernel/debug/calipso_selftest */ #include #include #include #include #include #include static void run_one(const char *name, unsigned int payload, __be16 plen_field, bool gso) { struct netlbl_lsm_secattr secattr; struct ipv6hdr *ip6; struct sk_buff *skb; int rc; skb = alloc_skb(LL_MAX_HEADER + 512 + sizeof(*ip6) + payload, GFP_KERNEL); if (!skb) return; skb_reserve(skb, LL_MAX_HEADER + 512); skb_reset_network_header(skb); ip6 = skb_put_zero(skb, sizeof(*ip6) + payload); ip6->version = 6; ip6->nexthdr = IPPROTO_TCP; ip6->hop_limit = 64; ip6->payload_len = plen_field; ipv6_addr_set(&ip6->saddr, htonl(0xfd000001), 0, 0, htonl(2)); ipv6_addr_set(&ip6->daddr, htonl(0xfd000002), 0, 0, htonl(2)); skb->protocol = htons(ETH_P_IPV6); if (gso) { /* what GRO builds for a BIG TCP aggregate */ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; skb_shinfo(skb)->gso_size = 1400; } netlbl_secattr_init(&secattr); secattr.flags = NETLBL_SECATTR_MLS_LVL; secattr.attr.mls.lvl = 0; pr_info("CALSELFTEST %s: before skb->len=%u payload_len=%u ipv6_payload_len=%u\n", name, skb->len, ntohs(ipv6_hdr(skb)->payload_len), ipv6_payload_len(skb, ipv6_hdr(skb))); rc = netlbl_skbuff_setattr(skb, AF_INET6, &secattr); pr_info("CALSELFTEST %s: after rc=%d skb->len=%u nexthdr=%u payload_len=%u ipv6_payload_len=%u expected=%u\n", name, rc, skb->len, ipv6_hdr(skb)->nexthdr, ntohs(ipv6_hdr(skb)->payload_len), ipv6_payload_len(skb, ipv6_hdr(skb)), skb->len - (unsigned int)sizeof(struct ipv6hdr)); netlbl_secattr_destroy(&secattr); kfree_skb(skb); } static ssize_t trigger(struct file *f, const char __user *buf, size_t len, loff_t *pos) { run_one("normal", 1000, htons(1000), false); run_one("bigtcp", 70000, 0, true); return len; } static const struct file_operations fops = { .write = trigger }; static int __init calipso_selftest_init(void) { debugfs_create_file("calipso_selftest", 0200, NULL, NULL, &fops); return 0; } late_initcall(calipso_selftest_init); net/ipv6/calipso.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index c6a34334e..dfea7ed65 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -1318,7 +1318,7 @@ static int calipso_skbuff_setattr(struct sk_buff *skb, struct ipv6hdr *ip6_hdr; struct ipv6_opt_hdr *hop; unsigned char buf[CALIPSO_MAX_BUFFER]; - int len_delta, new_end, pad, payload; + int len_delta, new_end, pad; unsigned int start, end; ip6_hdr = ipv6_hdr(skb); @@ -1357,8 +1357,9 @@ static int calipso_skbuff_setattr(struct sk_buff *skb, sizeof(*ip6_hdr) + start); skb_reset_network_header(skb); ip6_hdr = ipv6_hdr(skb); - payload = ntohs(ip6_hdr->payload_len); - ip6_hdr->payload_len = htons(payload + len_delta); + /* BIG TCP packets have payload_len == 0 */ + if (ip6_hdr->payload_len) + be16_add_cpu(&ip6_hdr->payload_len, len_delta); } hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); -- 2.43.0