| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/23 07:55 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"IP_VS",
"IP_VS_IPV6",
"IP_VS_PROTO_TCP",
"IP_VS_PROTO_UDP",
"IP_VS_PROTO_SCTP"
],
"FocusSymbols": [
"ip_vs_nat_icmp",
"ip_vs_nat_icmp_v6",
"handle_response_icmp",
"ip_vs_out_icmp",
"ip_vs_out_icmp_v6",
"ip_vs_in_icmp",
"ip_vs_in_icmp_v6",
"ip_vs_forward_icmp",
"sctp_csum_check",
"tcp_csum_check",
"udp_csum_check",
"ip_vs_icmp_xmit",
"ip_vs_icmp_xmit_v6"
],
"Reasoning": "The patch modifies ICMP NAT handling and checksum validation logic in IPVS. It changes how ICMP packets are processed, NATed, and transmitted, and updates checksum validation for TCP, UDP, and SCTP protocols within IPVS. Fuzzing should focus on these modified ICMP handling and checksum validation functions.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/23 07:55 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 0f31a5866b1c8003ae852d87efca8e885d3ffe59\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Jul 23 07:55:15 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/net/ip_vs.h b/include/net/ip_vs.h\nindex 417ff51f62fc8e..9ae9aad65541d3 100644\n--- a/include/net/ip_vs.h\n+++ b/include/net/ip_vs.h\n@@ -25,7 +25,9 @@\n #include \u003clinux/netfilter.h\u003e\t\t/* for union nf_inet_addr */\n #include \u003clinux/ip.h\u003e\n #include \u003clinux/ipv6.h\u003e\t\t\t/* for struct ipv6hdr */\n+#include \u003cnet/route.h\u003e\n #include \u003cnet/ipv6.h\u003e\n+#include \u003cnet/ip6_fib.h\u003e\n #if IS_ENABLED(CONFIG_NF_CONNTRACK)\n #include \u003cnet/netfilter/nf_conntrack.h\u003e\n #endif\n@@ -1972,8 +1974,8 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\t struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);\n int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n-\t\t struct ip_vs_protocol *pp, int offset,\n-\t\t unsigned int hooknum, struct ip_vs_iphdr *iph);\n+\t\t struct ip_vs_protocol *pp, unsigned int toff,\n+\t\t unsigned int hooknum, struct ip_vs_iphdr *ciph);\n void ip_vs_dest_dst_rcu_free(struct rcu_head *head);\n \n #ifdef CONFIG_IP_VS_IPV6\n@@ -1986,8 +1988,8 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\t struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);\n int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n-\t\t struct ip_vs_protocol *pp, int offset,\n-\t\t unsigned int hooknum, struct ip_vs_iphdr *iph);\n+\t\t struct ip_vs_protocol *pp, unsigned int toff,\n+\t\t unsigned int hooknum, struct ip_vs_iphdr *ciph);\n #endif\n \n #ifdef CONFIG_SYSCTL\n@@ -2059,15 +2061,15 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)\n }\n \n void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\t struct ip_vs_conn *cp, int dir);\n+\t\t struct ip_vs_conn *cp, int dir, unsigned int toff,\n+\t\t bool has_ports);\n \n #ifdef CONFIG_IP_VS_IPV6\n void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\t struct ip_vs_conn *cp, int dir);\n+\t\t struct ip_vs_conn *cp, int dir, unsigned int toff,\n+\t\t bool has_ports, struct ip_vs_iphdr *ciph);\n #endif\n \n-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);\n-\n static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)\n {\n \t__be32 diff[2] = { ~old, new };\n@@ -2093,6 +2095,36 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)\n \treturn csum_partial(diff, sizeof(diff), oldsum);\n }\n \n+static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)\n+{\n+\t/* Checksum unnecessary or already validated? */\n+\tif (skb_csum_unnecessary(skb))\n+\t\treturn false;\n+\t/* LOCAL_OUT ? */\n+\tif (!skb-\u003edev || skb-\u003edev-\u003eflags \u0026 IFF_LOOPBACK)\n+\t\treturn false;\n+\t/* !LOCAL_IN (FORWARD) ? */\n+\tif (af == AF_INET6) {\n+\t\tif (!(dst_rt6_info(skb_dst(skb))-\u003ert6i_flags \u0026 RTF_LOCAL))\n+\t\t\treturn false;\n+\t} else {\n+\t\tif (!(skb_rtable(skb)-\u003ert_flags \u0026 RTCF_LOCAL))\n+\t\t\treturn false;\n+\t}\n+\treturn true;\n+}\n+\n+/**\n+ * ip_vs_checksum_common_check - validate checksum for TCP/UDP/ICMP\n+ */\n+static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,\n+\t\t\t\t\t int offset, int proto, int af)\n+{\n+\tif (!ip_vs_checksum_needed(skb, af))\n+\t\treturn true;\n+\treturn !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);\n+}\n+\n /* Forget current conntrack (unconfirmed) and attach notrack entry */\n static inline void ip_vs_notrack(struct sk_buff *skb)\n {\ndiff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c\nindex b0e00be85cb12c..11cbdbaf561daf 100644\n--- a/net/netfilter/ipvs/ip_vs_app.c\n+++ b/net/netfilter/ipvs/ip_vs_app.c\n@@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,\n \tif (skb_ensure_writable(skb, ipvsh-\u003elen + sizeof(*th)))\n \t\treturn 0;\n \n-\tth = (struct tcphdr *)(skb_network_header(skb) + ipvsh-\u003elen);\n+\tth = (struct tcphdr *)(skb-\u003edata + ipvsh-\u003elen);\n \n \t/*\n \t *\tRemember seq number in case this pkt gets resized\n@@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,\n \tif (skb_ensure_writable(skb, ipvsh-\u003elen + sizeof(*th)))\n \t\treturn 0;\n \n-\tth = (struct tcphdr *)(skb_network_header(skb) + ipvsh-\u003elen);\n+\tth = (struct tcphdr *)(skb-\u003edata + ipvsh-\u003elen);\n \n \t/*\n \t *\tRemember seq number in case this pkt gets resized\ndiff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c\nindex bafab93451d037..7efa209a517b1c 100644\n--- a/net/netfilter/ipvs/ip_vs_core.c\n+++ b/net/netfilter/ipvs/ip_vs_core.c\n@@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }\n \n #endif\n \n-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)\n+static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)\n {\n \treturn csum_fold(skb_checksum(skb, offset, skb-\u003elen - offset, 0));\n }\n@@ -924,13 +924,13 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,\n * - inout: 1=in-\u003eout, 0=out-\u003ein\n */\n void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\t struct ip_vs_conn *cp, int inout)\n+\t\t struct ip_vs_conn *cp, int inout, unsigned int toff,\n+\t\t bool has_ports)\n {\n \tstruct iphdr *iph\t = ip_hdr(skb);\n-\tunsigned int icmp_offset = iph-\u003eihl*4;\n-\tstruct icmphdr *icmph\t = (struct icmphdr *)(skb_network_header(skb) +\n-\t\t\t\t\t\t icmp_offset);\n+\tstruct icmphdr *icmph\t = (struct icmphdr *)(skb-\u003edata + toff);\n \tstruct iphdr *ciph\t = (struct iphdr *)(icmph + 1);\n+\tunsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);\n \n \tif (inout) {\n \t\tiph-\u003esaddr = cp-\u003evaddr.ip;\n@@ -945,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t}\n \n \t/* the TCP/UDP/SCTP port */\n-\tif (IPPROTO_TCP == ciph-\u003eprotocol || IPPROTO_UDP == ciph-\u003eprotocol ||\n-\t IPPROTO_SCTP == ciph-\u003eprotocol) {\n+\tif (has_ports) {\n \t\t__be16 *ports = (void *)ciph + ciph-\u003eihl*4;\n \n \t\tif (inout)\n@@ -957,48 +956,40 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \n \t/* And finally the ICMP checksum */\n \ticmph-\u003echecksum = 0;\n-\ticmph-\u003echecksum = ip_vs_checksum_complete(skb, icmp_offset);\n+\ticmph-\u003echecksum = ip_vs_checksum_complete(skb, toff);\n \tskb-\u003eip_summed = CHECKSUM_UNNECESSARY;\n \n \tif (inout)\n-\t\tIP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,\n-\t\t\t\"Forwarding altered outgoing ICMP\");\n+\t\tIP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,\n+\t\t\t \"Forwarding altered outgoing ICMP\");\n \telse\n-\t\tIP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,\n-\t\t\t\"Forwarding altered incoming ICMP\");\n+\t\tIP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,\n+\t\t\t \"Forwarding altered incoming ICMP\");\n }\n \n #ifdef CONFIG_IP_VS_IPV6\n void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\t struct ip_vs_conn *cp, int inout)\n+\t\t struct ip_vs_conn *cp, int inout, unsigned int toff,\n+\t\t bool has_ports, struct ip_vs_iphdr *ciph)\n {\n \tstruct ipv6hdr *iph\t = ipv6_hdr(skb);\n-\tunsigned int icmp_offset = 0;\n-\tunsigned int offs\t = 0; /* header offset*/\n-\tint protocol;\n \tstruct icmp6hdr *icmph;\n-\tstruct ipv6hdr *ciph;\n-\tunsigned short fragoffs;\n+\tstruct ipv6hdr *cih;\n \n-\tipv6_find_hdr(skb, \u0026icmp_offset, IPPROTO_ICMPV6, \u0026fragoffs, NULL);\n-\ticmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);\n-\toffs = icmp_offset + sizeof(struct icmp6hdr);\n-\tciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);\n-\n-\tprotocol = ipv6_find_hdr(skb, \u0026offs, -1, \u0026fragoffs, NULL);\n+\ticmph = (struct icmp6hdr *)(skb-\u003edata + toff);\n+\tcih = (struct ipv6hdr *)(skb-\u003edata + ciph-\u003eoff);\n \n \tif (inout) {\n \t\tiph-\u003esaddr = cp-\u003evaddr.in6;\n-\t\tciph-\u003edaddr = cp-\u003evaddr.in6;\n+\t\tcih-\u003edaddr = cp-\u003evaddr.in6;\n \t} else {\n \t\tiph-\u003edaddr = cp-\u003edaddr.in6;\n-\t\tciph-\u003esaddr = cp-\u003edaddr.in6;\n+\t\tcih-\u003esaddr = cp-\u003edaddr.in6;\n \t}\n \n \t/* the TCP/UDP/SCTP port */\n-\tif (!fragoffs \u0026\u0026 (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||\n-\t\t\t IPPROTO_SCTP == protocol)) {\n-\t\t__be16 *ports = (void *)(skb_network_header(skb) + offs);\n+\tif (has_ports) {\n+\t\t__be16 *ports = (void *)(skb-\u003edata + ciph-\u003elen);\n \n \t\tIP_VS_DBG(11, \"%s() changed port %d to %d\\n\", __func__,\n \t\t\t ntohs(inout ? ports[1] : ports[0]),\n@@ -1011,19 +1002,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \n \t/* And finally the ICMP checksum */\n \ticmph-\u003eicmp6_cksum = ~csum_ipv6_magic(\u0026iph-\u003esaddr, \u0026iph-\u003edaddr,\n-\t\t\t\t\t skb-\u003elen - icmp_offset,\n+\t\t\t\t\t skb-\u003elen - toff,\n \t\t\t\t\t IPPROTO_ICMPV6, 0);\n-\tskb-\u003ecsum_start = skb_network_header(skb) - skb-\u003ehead + icmp_offset;\n+\tskb-\u003ecsum_start = skb_headroom(skb) + toff;\n \tskb-\u003ecsum_offset = offsetof(struct icmp6hdr, icmp6_cksum);\n \tskb-\u003eip_summed = CHECKSUM_PARTIAL;\n \n \tif (inout)\n-\t\tIP_VS_DBG_PKT(11, AF_INET6, pp, skb,\n-\t\t\t (void *)ciph - (void *)iph,\n+\t\tIP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph-\u003eoff,\n \t\t\t \"Forwarding altered outgoing ICMPv6\");\n \telse\n-\t\tIP_VS_DBG_PKT(11, AF_INET6, pp, skb,\n-\t\t\t (void *)ciph - (void *)iph,\n+\t\tIP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph-\u003eoff,\n \t\t\t \"Forwarding altered incoming ICMPv6\");\n }\n #endif\n@@ -1033,36 +1022,41 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,\n */\n static int handle_response_icmp(int af, struct sk_buff *skb,\n \t\t\t\tunion nf_inet_addr *snet,\n-\t\t\t\t__u8 protocol, struct ip_vs_conn *cp,\n+\t\t\t\tstruct ip_vs_conn *cp,\n \t\t\t\tstruct ip_vs_protocol *pp,\n-\t\t\t\tunsigned int offset, unsigned int ihl,\n-\t\t\t\tunsigned int hooknum)\n+\t\t\t\tstruct ip_vs_iphdr *ciph,\n+\t\t\t\tunsigned int toff, unsigned int hooknum)\n {\n+\tint iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;\n \tunsigned int verdict = NF_DROP;\n+\tunsigned int ctoff = ciph-\u003elen;\n+\tbool has_ports = false;\n \n \tif (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)\n \t\tgoto after_nat;\n \n \t/* Ensure the checksum is correct */\n-\tif (!skb_csum_unnecessary(skb) \u0026\u0026 ip_vs_checksum_complete(skb, ihl)) {\n+\tif (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {\n \t\t/* Failed checksum! */\n \t\tIP_VS_DBG_BUF(1, \"Forward ICMP: failed checksum from %s!\\n\",\n \t\t\t IP_VS_DBG_ADDR(af, snet));\n \t\tgoto out;\n \t}\n \n-\tif (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||\n-\t IPPROTO_SCTP == protocol)\n-\t\toffset += 2 * sizeof(__u16);\n-\tif (skb_ensure_writable(skb, offset))\n+\tif (ciph-\u003eprotocol == IPPROTO_TCP || ciph-\u003eprotocol == IPPROTO_UDP ||\n+\t ciph-\u003eprotocol == IPPROTO_SCTP) {\n+\t\tctoff += 2 * sizeof(__u16);\n+\t\thas_ports = true;\n+\t}\n+\tif (skb_ensure_writable(skb, ctoff))\n \t\tgoto out;\n \n #ifdef CONFIG_IP_VS_IPV6\n \tif (af == AF_INET6)\n-\t\tip_vs_nat_icmp_v6(skb, pp, cp, 1);\n+\t\tip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);\n \telse\n #endif\n-\t\tip_vs_nat_icmp(skb, pp, cp, 1);\n+\t\tip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);\n \n \tif (ip_vs_route_me_harder(cp-\u003eipvs, af, skb, hooknum))\n \t\tgoto out;\n@@ -1090,9 +1084,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,\n *\tCurrently handles error types - unreachable, quench, ttl exceeded.\n */\n static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,\n-\t\t\t int *related, unsigned int hooknum)\n+\t\t\t int *related, unsigned int hooknum,\n+\t\t\t struct ip_vs_iphdr *ipvsh)\n {\n-\tstruct iphdr *iph;\n \tstruct icmphdr\t_icmph, *ic;\n \tstruct iphdr\t_ciph, *cih;\t/* The ip header contained within the ICMP */\n \tstruct ip_vs_iphdr ciph;\n@@ -1107,17 +1101,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \tif (ip_is_fragment(ip_hdr(skb))) {\n \t\tif (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))\n \t\t\treturn NF_STOLEN;\n+\t\tif (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))\n+\t\t\treturn NF_ACCEPT;\n \t}\n \n-\tiph = ip_hdr(skb);\n-\toffset = ihl = iph-\u003eihl * 4;\n+\tihl = ipvsh-\u003elen;\n+\toffset = ipvsh-\u003elen;\n \tic = skb_header_pointer(skb, offset, sizeof(_icmph), \u0026_icmph);\n \tif (ic == NULL)\n \t\treturn NF_DROP;\n \n \tIP_VS_DBG(12, \"Outgoing ICMP (%d,%d) %pI4-\u003e%pI4\\n\",\n \t\t ic-\u003etype, ntohs(icmp_id(ic)),\n-\t\t \u0026iph-\u003esaddr, \u0026iph-\u003edaddr);\n+\t\t \u0026ipvsh-\u003esaddr.ip, \u0026ipvsh-\u003edaddr.ip);\n \n \t/*\n \t * Work through seeing if this is for us.\n@@ -1136,7 +1132,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \t/* Now find the contained IP header */\n \toffset += sizeof(_icmph);\n \tcih = skb_header_pointer(skb, offset, sizeof(_ciph), \u0026_ciph);\n-\tif (cih == NULL)\n+\tif (!(cih \u0026\u0026 cih-\u003eversion == 4 \u0026\u0026 cih-\u003eihl \u003e= 5))\n \t\treturn NF_ACCEPT; /* The packet looks wrong, ignore */\n \n \tpp = ip_vs_proto_get(cih-\u003eprotocol);\n@@ -1144,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \t\treturn NF_ACCEPT;\n \n \t/* Is the embedded protocol header present? */\n-\tif (unlikely(cih-\u003efrag_off \u0026 htons(IP_OFFSET) \u0026\u0026\n-\t\t pp-\u003edont_defrag))\n+\tif (unlikely(cih-\u003efrag_off \u0026 htons(IP_OFFSET) \u0026\u0026 !pp-\u003edont_defrag))\n \t\treturn NF_ACCEPT;\n \n \tIP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,\n@@ -1159,9 +1154,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \tif (!cp)\n \t\treturn NF_ACCEPT;\n \n-\tsnet.ip = iph-\u003esaddr;\n-\treturn handle_response_icmp(AF_INET, skb, \u0026snet, cih-\u003eprotocol, cp,\n-\t\t\t\t pp, ciph.len, ihl, hooknum);\n+\tsnet.ip = ipvsh-\u003esaddr.ip;\n+\treturn handle_response_icmp(AF_INET, skb, \u0026snet, cp, pp, \u0026ciph, ihl,\n+\t\t\t\t hooknum);\n }\n \n #ifdef CONFIG_IP_VS_IPV6\n@@ -1174,7 +1169,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \tstruct ip_vs_conn *cp;\n \tstruct ip_vs_protocol *pp;\n \tunion nf_inet_addr snet;\n-\tunsigned int offset;\n \n \t*related = 1;\n \tic = frag_safe_skb_hp(skb, ipvsh-\u003elen, sizeof(_icmph), \u0026_icmph);\n@@ -1210,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \tif (!pp)\n \t\treturn NF_ACCEPT;\n \n+\t/* Is the embedded protocol header present? */\n+\tif (unlikely(ciph.fragoffs \u0026\u0026 !pp-\u003edont_defrag))\n+\t\treturn NF_ACCEPT;\n+\n \t/* The embedded headers contain source and dest in reverse order */\n \tcp = INDIRECT_CALL_1(pp-\u003econn_out_get, ip_vs_conn_out_get_proto,\n \t\t\t ipvs, AF_INET6, skb, \u0026ciph);\n@@ -1217,9 +1215,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \t\treturn NF_ACCEPT;\n \n \tsnet.in6 = ciph.saddr.in6;\n-\toffset = ciph.len;\n-\treturn handle_response_icmp(AF_INET6, skb, \u0026snet, ciph.protocol, cp,\n-\t\t\t\t pp, offset, ipvsh-\u003elen, hooknum);\n+\treturn handle_response_icmp(AF_INET6, skb, \u0026snet, cp, pp, \u0026ciph,\n+\t\t\t\t ipvsh-\u003elen, hooknum);\n }\n #endif\n \n@@ -1545,7 +1542,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat\n #endif\n \t\tif (unlikely(iph.protocol == IPPROTO_ICMP)) {\n \t\t\tint related;\n-\t\t\tint verdict = ip_vs_out_icmp(ipvs, skb, \u0026related, hooknum);\n+\t\t\tint verdict = ip_vs_out_icmp(ipvs, skb, \u0026related,\n+\t\t\t\t\t\t hooknum, \u0026iph);\n \n \t\t\tif (related)\n \t\t\t\treturn verdict;\n@@ -1753,9 +1751,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,\n */\n static int\n ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n-\t unsigned int hooknum)\n+\t unsigned int hooknum, struct ip_vs_iphdr *iph)\n {\n-\tstruct iphdr *iph;\n \tstruct icmphdr\t_icmph, *ic;\n \tstruct iphdr\t_ciph, *cih;\t/* The ip header contained within the ICMP */\n \tstruct ip_vs_iphdr ciph;\n@@ -1765,7 +1762,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \tunsigned int offset, offset2, ihl, verdict;\n \tbool tunnel, new_cp = false;\n \tunion nf_inet_addr *raddr;\n-\tchar *outer_proto = \"IPIP\";\n+\tchar *outer_proto __maybe_unused = \"IPIP\";\n \tunsigned int hlen_ipip;\n \tint ulen = 0;\n \n@@ -1775,17 +1772,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \tif (ip_is_fragment(ip_hdr(skb))) {\n \t\tif (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))\n \t\t\treturn NF_STOLEN;\n+\t\tif (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))\n+\t\t\treturn NF_ACCEPT;\n \t}\n \n-\tiph = ip_hdr(skb);\n-\toffset = ihl = iph-\u003eihl * 4;\n+\tihl = iph-\u003elen;\n+\toffset = iph-\u003elen;\n \tic = skb_header_pointer(skb, offset, sizeof(_icmph), \u0026_icmph);\n \tif (ic == NULL)\n \t\treturn NF_DROP;\n \n \tIP_VS_DBG(12, \"Incoming ICMP (%d,%d) %pI4-\u003e%pI4\\n\",\n \t\t ic-\u003etype, ntohs(icmp_id(ic)),\n-\t\t \u0026iph-\u003esaddr, \u0026iph-\u003edaddr);\n+\t\t \u0026iph-\u003esaddr.ip, \u0026iph-\u003edaddr.ip);\n \n \t/*\n \t * Work through seeing if this is for us.\n@@ -1867,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \tpp = pd-\u003epp;\n \n \t/* Is the embedded protocol header present? */\n-\tif (unlikely(cih-\u003efrag_off \u0026 htons(IP_OFFSET) \u0026\u0026\n-\t\t pp-\u003edont_defrag))\n+\tif (unlikely(cih-\u003efrag_off \u0026 htons(IP_OFFSET) \u0026\u0026 !pp-\u003edont_defrag))\n \t\treturn NF_ACCEPT;\n \n \tIP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,\n@@ -1876,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \n \toffset2 = offset;\n \tip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, \u0026ciph);\n-\toffset = ciph.len;\n \n \t/* The embedded headers contain source and dest in reverse order.\n \t * For IPIP/UDP/GRE tunnel this is error for request, not for reply.\n@@ -1898,10 +1895,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \tverdict = NF_DROP;\n \n \t/* Ensure the checksum is correct */\n-\tif (!skb_csum_unnecessary(skb) \u0026\u0026 ip_vs_checksum_complete(skb, ihl)) {\n+\tif ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) \u0026\u0026\n+\t !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {\n \t\t/* Failed checksum! */\n \t\tIP_VS_DBG(1, \"Incoming ICMP: failed checksum from %pI4!\\n\",\n-\t\t\t \u0026iph-\u003esaddr);\n+\t\t\t \u0026iph-\u003esaddr.ip);\n \t\tgoto out;\n \t}\n \n@@ -1969,10 +1967,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,\n \n \t/* do the statistics and put it back */\n \tip_vs_in_stats(cp, skb);\n-\tif (IPPROTO_TCP == cih-\u003eprotocol || IPPROTO_UDP == cih-\u003eprotocol ||\n-\t IPPROTO_SCTP == cih-\u003eprotocol)\n-\t\toffset += 2 * sizeof(__u16);\n-\tverdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, \u0026ciph);\n+\tverdict = ip_vs_icmp_xmit(skb, cp, pp, iph-\u003elen, hooknum, \u0026ciph);\n \n out:\n \tif (likely(!new_cp))\n@@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \t\treturn NF_ACCEPT;\n \tpp = pd-\u003epp;\n \n-\t/* Cannot handle fragmented embedded protocol */\n-\tif (ciph.fragoffs)\n+\t/* Is the embedded protocol header present? */\n+\tif (ciph.fragoffs \u0026\u0026 !pp-\u003edont_defrag)\n \t\treturn NF_ACCEPT;\n \n \tIP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,\n@@ -2057,23 +2052,22 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,\n \t\tnew_cp = true;\n \t}\n \n-\t/* VS/TUN, VS/DR and LOCALNODE just let it go */\n-\tif ((hooknum == NF_INET_LOCAL_OUT) \u0026\u0026\n-\t (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {\n-\t\tverdict = NF_ACCEPT;\n+\tverdict = NF_DROP;\n+\n+\t/* Ensure the checksum is correct */\n+\tif (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ \u0026\u0026\n+\t !ip_vs_checksum_common_check(skb, iph-\u003elen, IPPROTO_ICMPV6,\n+\t\t\t\t\t AF_INET6)) {\n+\t\t/* Failed checksum! */\n+\t\tIP_VS_DBG(1, \"Incoming ICMPv6: failed checksum from %pI6c!\\n\",\n+\t\t\t \u0026iph-\u003esaddr);\n \t\tgoto out;\n \t}\n \n \t/* do the statistics and put it back */\n \tip_vs_in_stats(cp, skb);\n \n-\t/* Need to mangle contained IPv6 header in ICMPv6 packet */\n-\toffset = ciph.len;\n-\tif (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||\n-\t IPPROTO_SCTP == ciph.protocol)\n-\t\toffset += 2 * sizeof(__u16); /* Also mangle ports */\n-\n-\tverdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, \u0026ciph);\n+\tverdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph-\u003elen, hooknum, \u0026ciph);\n \n out:\n \tif (likely(!new_cp))\n@@ -2152,7 +2146,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state\n \t\tif (unlikely(iph.protocol == IPPROTO_ICMP)) {\n \t\t\tint related;\n \t\t\tint verdict = ip_vs_in_icmp(ipvs, skb, \u0026related,\n-\t\t\t\t\t\t hooknum);\n+\t\t\t\t\t\t hooknum, \u0026iph);\n \n \t\t\tif (related)\n \t\t\t\treturn verdict;\n@@ -2288,6 +2282,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,\n \t\t const struct nf_hook_state *state)\n {\n \tstruct netns_ipvs *ipvs = net_ipvs(state-\u003enet);\n+\tstruct ip_vs_iphdr iphdr;\n \tint r;\n \n \t/* ipvs enabled in this netns ? */\n@@ -2297,10 +2292,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,\n \tif (state-\u003epf == NFPROTO_IPV4) {\n \t\tif (ip_hdr(skb)-\u003eprotocol != IPPROTO_ICMP)\n \t\t\treturn NF_ACCEPT;\n+\t\tip_vs_fill_iph_skb(AF_INET, skb, false, \u0026iphdr);\n #ifdef CONFIG_IP_VS_IPV6\n \t} else {\n-\t\tstruct ip_vs_iphdr iphdr;\n-\n \t\tip_vs_fill_iph_skb(AF_INET6, skb, false, \u0026iphdr);\n \n \t\tif (iphdr.protocol != IPPROTO_ICMPV6)\n@@ -2310,7 +2304,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,\n #endif\n \t}\n \n-\treturn ip_vs_in_icmp(ipvs, skb, \u0026r, state-\u003ehook);\n+\treturn ip_vs_in_icmp(ipvs, skb, \u0026r, state-\u003ehook, \u0026iphdr);\n }\n \n static const struct nf_hook_ops ip_vs_ops4[] = {\ndiff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c\nindex c67317be17dfaf..3dbd3096e1637b 100644\n--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c\n+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c\n@@ -11,7 +11,7 @@\n \n static int\n sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\tunsigned int sctphoff);\n+\t\tstruct ip_vs_iphdr *iph);\n \n static int\n sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,\n@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!sctp_csum_check(cp-\u003eaf, skb, pp, sctphoff))\n+\t\tif (!sctp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/* Call application helper if needed */\n@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\tsctph = (void *) skb_network_header(skb) + sctphoff;\n+\tsctph = (void *)skb-\u003edata + sctphoff;\n \n \t/* Only update csum if we really have to */\n \tif (sctph-\u003esource != cp-\u003evport || payload_csum ||\n@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!sctp_csum_check(cp-\u003eaf, skb, pp, sctphoff))\n+\t\tif (!sctp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/* Call application helper if needed */\n@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\tsctph = (void *) skb_network_header(skb) + sctphoff;\n+\tsctph = (void *)skb-\u003edata + sctphoff;\n \n \t/* Only update csum if we really have to */\n \tif (sctph-\u003edest != cp-\u003edport || payload_csum ||\n@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \n static int\n sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t\tunsigned int sctphoff)\n+\t\tstruct ip_vs_iphdr *iph)\n {\n+\tunsigned int sctphoff = iph-\u003elen;\n \tstruct sctphdr *sh;\n \t__le32 cmp, val;\n \n+\tif (!ip_vs_checksum_needed(skb, af))\n+\t\treturn 1;\n \tsh = (struct sctphdr *)(skb-\u003edata + sctphoff);\n \tcmp = sh-\u003echecksum;\n \tval = sctp_compute_cksum(skb, sctphoff);\n \n \tif (val != cmp) {\n \t\t/* CRC failure, dump it. */\n-\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, 0,\n-\t\t\t\t\"Failed checksum for\");\n+\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, iph-\u003eoff,\n+\t\t\t\t \"Failed checksum for\");\n \t\treturn 0;\n \t}\n \treturn 1;\ndiff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c\nindex f86b763efcc4dc..99a286fdc90c65 100644\n--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c\n+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c\n@@ -29,7 +29,7 @@\n \n static int\n tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t unsigned int tcphoff);\n+\t struct ip_vs_iphdr *iph);\n \n static int\n tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,\n@@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!tcp_csum_check(cp-\u003eaf, skb, pp, tcphoff))\n+\t\tif (!tcp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/* Call application helper if needed */\n@@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\ttcph = (void *)skb_network_header(skb) + tcphoff;\n+\ttcph = (void *)skb-\u003edata + tcphoff;\n \ttcph-\u003esource = cp-\u003evport;\n \n \t/* Adjust TCP checksums */\n@@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!tcp_csum_check(cp-\u003eaf, skb, pp, tcphoff))\n+\t\tif (!tcp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/*\n@@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\ttcph = (void *)skb_network_header(skb) + tcphoff;\n+\ttcph = (void *)skb-\u003edata + tcphoff;\n \ttcph-\u003edest = cp-\u003edport;\n \n \t/*\n@@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \n static int\n tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t unsigned int tcphoff)\n+\t struct ip_vs_iphdr *iph)\n {\n-\tswitch (skb-\u003eip_summed) {\n-\tcase CHECKSUM_NONE:\n-\t\tskb-\u003ecsum = skb_checksum(skb, tcphoff, skb-\u003elen - tcphoff, 0);\n-\t\tfallthrough;\n-\tcase CHECKSUM_COMPLETE:\n-#ifdef CONFIG_IP_VS_IPV6\n-\t\tif (af == AF_INET6) {\n-\t\t\tif (csum_ipv6_magic(\u0026ipv6_hdr(skb)-\u003esaddr,\n-\t\t\t\t\t \u0026ipv6_hdr(skb)-\u003edaddr,\n-\t\t\t\t\t skb-\u003elen - tcphoff,\n-\t\t\t\t\t IPPROTO_TCP,\n-\t\t\t\t\t skb-\u003ecsum)) {\n-\t\t\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, 0,\n-\t\t\t\t\t\t \"Failed checksum for\");\n-\t\t\t\treturn 0;\n-\t\t\t}\n-\t\t} else\n-#endif\n-\t\t\tif (csum_tcpudp_magic(ip_hdr(skb)-\u003esaddr,\n-\t\t\t\t\t ip_hdr(skb)-\u003edaddr,\n-\t\t\t\t\t skb-\u003elen - tcphoff,\n-\t\t\t\t\t ip_hdr(skb)-\u003eprotocol,\n-\t\t\t\t\t skb-\u003ecsum)) {\n-\t\t\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, 0,\n-\t\t\t\t\t\t \"Failed checksum for\");\n-\t\t\t\treturn 0;\n-\t\t\t}\n-\t\tbreak;\n-\tdefault:\n-\t\t/* No need to checksum. */\n-\t\tbreak;\n+\tif (!ip_vs_checksum_common_check(skb, iph-\u003elen, IPPROTO_TCP, af)) {\n+\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, iph-\u003eoff,\n+\t\t\t\t \"Failed checksum for\");\n+\t\treturn 0;\n \t}\n-\n \treturn 1;\n }\n \ndiff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c\nindex 58f9e255927e2e..f32785682402dd 100644\n--- a/net/netfilter/ipvs/ip_vs_proto_udp.c\n+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c\n@@ -25,7 +25,7 @@\n \n static int\n udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t unsigned int udphoff);\n+\t struct ip_vs_iphdr *iph);\n \n static int\n udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,\n@@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!udp_csum_check(cp-\u003eaf, skb, pp, udphoff))\n+\t\tif (!udp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/*\n@@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\tudph = (void *)skb_network_header(skb) + udphoff;\n+\tudph = (void *)skb-\u003edata + udphoff;\n \tudph-\u003esource = cp-\u003evport;\n \n \t/*\n@@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\tint ret;\n \n \t\t/* Some checks before mangling */\n-\t\tif (!udp_csum_check(cp-\u003eaf, skb, pp, udphoff))\n+\t\tif (!udp_csum_check(cp-\u003eaf, skb, pp, iph))\n \t\t\treturn 0;\n \n \t\t/*\n@@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \t\t\tpayload_csum = true;\n \t}\n \n-\tudph = (void *)skb_network_header(skb) + udphoff;\n+\tudph = (void *)skb-\u003edata + udphoff;\n \tudph-\u003edest = cp-\u003edport;\n \n \t/*\n@@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,\n \n static int\n udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,\n-\t unsigned int udphoff)\n+\t struct ip_vs_iphdr *iph)\n {\n \tstruct udphdr _udph, *uh;\n \n-\tuh = skb_header_pointer(skb, udphoff, sizeof(_udph), \u0026_udph);\n+\tuh = skb_header_pointer(skb, iph-\u003elen, sizeof(_udph), \u0026_udph);\n \tif (uh == NULL)\n \t\treturn 0;\n \n-\tif (uh-\u003echeck != 0) {\n-\t\tswitch (skb-\u003eip_summed) {\n-\t\tcase CHECKSUM_NONE:\n-\t\t\tskb-\u003ecsum = skb_checksum(skb, udphoff,\n-\t\t\t\t\t\t skb-\u003elen - udphoff, 0);\n-\t\t\tfallthrough;\n-\t\tcase CHECKSUM_COMPLETE:\n-#ifdef CONFIG_IP_VS_IPV6\n-\t\t\tif (af == AF_INET6) {\n-\t\t\t\tif (csum_ipv6_magic(\u0026ipv6_hdr(skb)-\u003esaddr,\n-\t\t\t\t\t\t \u0026ipv6_hdr(skb)-\u003edaddr,\n-\t\t\t\t\t\t skb-\u003elen - udphoff,\n-\t\t\t\t\t\t IPPROTO_UDP,\n-\t\t\t\t\t\t skb-\u003ecsum)) {\n-\t\t\t\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, 0,\n-\t\t\t\t\t\t\t \"Failed checksum for\");\n-\t\t\t\t\treturn 0;\n-\t\t\t\t}\n-\t\t\t} else\n-#endif\n-\t\t\t\tif (csum_tcpudp_magic(ip_hdr(skb)-\u003esaddr,\n-\t\t\t\t\t\t ip_hdr(skb)-\u003edaddr,\n-\t\t\t\t\t\t skb-\u003elen - udphoff,\n-\t\t\t\t\t\t ip_hdr(skb)-\u003eprotocol,\n-\t\t\t\t\t\t skb-\u003ecsum)) {\n-\t\t\t\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, 0,\n-\t\t\t\t\t\t\t \"Failed checksum for\");\n-\t\t\t\t\treturn 0;\n-\t\t\t\t}\n-\t\t\tbreak;\n-\t\tdefault:\n-\t\t\t/* No need to checksum. */\n-\t\t\tbreak;\n-\t\t}\n+\tif (!uh-\u003echeck)\n+\t\treturn 1;\n+\tif (!ip_vs_checksum_common_check(skb, iph-\u003elen, IPPROTO_UDP, af)) {\n+\t\tIP_VS_DBG_RL_PKT(0, af, pp, skb, iph-\u003eoff,\n+\t\t\t\t \"Failed checksum for\");\n+\t\treturn 0;\n \t}\n \treturn 1;\n }\ndiff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c\nindex 9fef4335da13fd..0b0c5304993a91 100644\n--- a/net/netfilter/ipvs/ip_vs_xmit.c\n+++ b/net/netfilter/ipvs/ip_vs_xmit.c\n@@ -1502,20 +1502,22 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n */\n int\n ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n-\t\tstruct ip_vs_protocol *pp, int offset, unsigned int hooknum,\n-\t\tstruct ip_vs_iphdr *iph)\n+\t\tstruct ip_vs_protocol *pp, unsigned int toff,\n+\t\tunsigned int hooknum, struct ip_vs_iphdr *ciph)\n {\n \tstruct rtable\t*rt;\t/* Route to the other host */\n \tint rc;\n \tint local;\n \tint rt_mode, was_input;\n+\tbool has_ports = false;\n+\tunsigned int wlen;\n \n \t/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be\n \t forwarded directly here, because there is no need to\n \t translate address/port back */\n \tif (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {\n \t\tif (cp-\u003epacket_xmit)\n-\t\t\trc = cp-\u003epacket_xmit(skb, cp, pp, iph);\n+\t\t\trc = cp-\u003epacket_xmit(skb, cp, pp, ciph);\n \t\telse\n \t\t\trc = NF_ACCEPT;\n \t\t/* do not touch skb anymore */\n@@ -1533,7 +1535,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\t IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |\n \t\t IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;\n \tlocal = __ip_vs_get_out_rt(cp-\u003eipvs, cp-\u003eaf, skb, cp-\u003edest, cp-\u003edaddr.ip, rt_mode,\n-\t\t\t\t NULL, iph);\n+\t\t\t\t NULL, ciph);\n \tif (local \u003c 0)\n \t\tgoto tx_error;\n \trt = skb_rtable(skb);\n@@ -1564,14 +1566,21 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\tgoto tx_error;\n \t}\n \n+\twlen = ciph-\u003elen;\n+\tif (ciph-\u003eprotocol == IPPROTO_TCP || ciph-\u003eprotocol == IPPROTO_UDP ||\n+\t ciph-\u003eprotocol == IPPROTO_SCTP) {\n+\t\twlen += 2 * sizeof(__u16); /* Also mangle ports */\n+\t\thas_ports = true;\n+\t}\n+\n \t/* copy-on-write the packet before mangling it */\n-\tif (skb_ensure_writable(skb, offset))\n+\tif (skb_ensure_writable(skb, wlen))\n \t\tgoto tx_error;\n \n \tif (skb_cow(skb, rt-\u003edst.dev-\u003ehard_header_len))\n \t\tgoto tx_error;\n \n-\tip_vs_nat_icmp(skb, pp, cp, 0);\n+\tip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);\n \n \t/* Another hack: avoid icmp_send in ip_fragment */\n \tskb-\u003eignore_df = 1;\n@@ -1587,10 +1596,12 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,\n #ifdef CONFIG_IP_VS_IPV6\n int\n ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n-\t\tstruct ip_vs_protocol *pp, int offset, unsigned int hooknum,\n-\t\tstruct ip_vs_iphdr *ipvsh)\n+\t\tstruct ip_vs_protocol *pp, unsigned int toff,\n+\t\tunsigned int hooknum, struct ip_vs_iphdr *ciph)\n {\n+\tbool has_ports = false;\n \tstruct rt6_info\t*rt;\t/* Route to the other host */\n+\tunsigned int wlen;\n \tint rc;\n \tint local;\n \tint rt_mode;\n@@ -1600,7 +1611,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t translate address/port back */\n \tif (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {\n \t\tif (cp-\u003epacket_xmit)\n-\t\t\trc = cp-\u003epacket_xmit(skb, cp, pp, ipvsh);\n+\t\t\trc = cp-\u003epacket_xmit(skb, cp, pp, ciph);\n \t\telse\n \t\t\trc = NF_ACCEPT;\n \t\t/* do not touch skb anymore */\n@@ -1617,7 +1628,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\t IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |\n \t\t IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;\n \tlocal = __ip_vs_get_out_rt_v6(cp-\u003eipvs, cp-\u003eaf, skb, cp-\u003edest,\n-\t\t\t\t \u0026cp-\u003edaddr.in6, NULL, ipvsh, 0, rt_mode);\n+\t\t\t\t \u0026cp-\u003edaddr.in6, NULL, ciph, 0, rt_mode);\n \tif (local \u003c 0)\n \t\tgoto tx_error;\n \trt = dst_rt6_info(skb_dst(skb));\n@@ -1648,14 +1659,21 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,\n \t\tgoto tx_error;\n \t}\n \n+\twlen = ciph-\u003elen;\n+\tif (ciph-\u003eprotocol == IPPROTO_TCP || ciph-\u003eprotocol == IPPROTO_UDP ||\n+\t ciph-\u003eprotocol == IPPROTO_SCTP) {\n+\t\twlen += 2 * sizeof(__u16); /* Also mangle ports */\n+\t\thas_ports = true;\n+\t}\n+\n \t/* copy-on-write the packet before mangling it */\n-\tif (skb_ensure_writable(skb, offset))\n+\tif (skb_ensure_writable(skb, wlen))\n \t\tgoto tx_error;\n \n \tif (skb_cow(skb, rt-\u003edst.dev-\u003ehard_header_len))\n \t\tgoto tx_error;\n \n-\tip_vs_nat_icmp_v6(skb, pp, cp, 0);\n+\tip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);\n \n \t/* Another hack: avoid icmp_send in ip_fragment */\n \tskb-\u003eignore_df = 1;\n"
} |
| 2/1 | 2026/07/23 07:55 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"IP_VS",
"IP_VS_IPV6",
"IP_VS_PROTO_TCP",
"IP_VS_PROTO_UDP",
"IP_VS_PROTO_SCTP"
],
"FocusSymbols": [
"ip_vs_nat_icmp",
"ip_vs_nat_icmp_v6",
"handle_response_icmp",
"ip_vs_out_icmp",
"ip_vs_out_icmp_v6",
"ip_vs_in_icmp",
"ip_vs_in_icmp_v6",
"ip_vs_forward_icmp",
"sctp_csum_check",
"tcp_csum_check",
"udp_csum_check",
"ip_vs_icmp_xmit",
"ip_vs_icmp_xmit_v6"
],
"Reasoning": "The patch modifies ICMP NAT handling and checksum validation logic in IPVS. It changes how ICMP packets are processed, NATed, and transmitted, and updates checksum validation for TCP, UDP, and SCTP protocols within IPVS. Fuzzing should focus on these modified ICMP handling and checksum validation functions.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 0f31a5866b1c8003ae852d87efca8e885d3ffe59
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Jul 23 07:55:15 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h
index 417ff51f62fc8e..9ae9aad65541d3 100644
--- a/include/net/ip_vs.h
+++ b/include/net/ip_vs.h
@@ -25,7 +25,9 @@
#include <linux/netfilter.h> /* for union nf_inet_addr */
#include <linux/ip.h>
#include <linux/ipv6.h> /* for struct ipv6hdr */
+#include <net/route.h>
#include <net/ipv6.h>
+#include <net/ip6_fib.h>
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#endif
@@ -1972,8 +1974,8 @@ int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset,
- unsigned int hooknum, struct ip_vs_iphdr *iph);
+ struct ip_vs_protocol *pp, unsigned int toff,
+ unsigned int hooknum, struct ip_vs_iphdr *ciph);
void ip_vs_dest_dst_rcu_free(struct rcu_head *head);
#ifdef CONFIG_IP_VS_IPV6
@@ -1986,8 +1988,8 @@ int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph);
int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset,
- unsigned int hooknum, struct ip_vs_iphdr *iph);
+ struct ip_vs_protocol *pp, unsigned int toff,
+ unsigned int hooknum, struct ip_vs_iphdr *ciph);
#endif
#ifdef CONFIG_SYSCTL
@@ -2059,15 +2061,15 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp)
}
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
- struct ip_vs_conn *cp, int dir);
+ struct ip_vs_conn *cp, int dir, unsigned int toff,
+ bool has_ports);
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
- struct ip_vs_conn *cp, int dir);
+ struct ip_vs_conn *cp, int dir, unsigned int toff,
+ bool has_ports, struct ip_vs_iphdr *ciph);
#endif
-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset);
-
static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum)
{
__be32 diff[2] = { ~old, new };
@@ -2093,6 +2095,36 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum)
return csum_partial(diff, sizeof(diff), oldsum);
}
+static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af)
+{
+ /* Checksum unnecessary or already validated? */
+ if (skb_csum_unnecessary(skb))
+ return false;
+ /* LOCAL_OUT ? */
+ if (!skb->dev || skb->dev->flags & IFF_LOOPBACK)
+ return false;
+ /* !LOCAL_IN (FORWARD) ? */
+ if (af == AF_INET6) {
+ if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL))
+ return false;
+ } else {
+ if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL))
+ return false;
+ }
+ return true;
+}
+
+/**
+ * ip_vs_checksum_common_check - validate checksum for TCP/UDP/ICMP
+ */
+static inline bool ip_vs_checksum_common_check(struct sk_buff *skb,
+ int offset, int proto, int af)
+{
+ if (!ip_vs_checksum_needed(skb, af))
+ return true;
+ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af);
+}
+
/* Forget current conntrack (unconfirmed) and attach notrack entry */
static inline void ip_vs_notrack(struct sk_buff *skb)
{
diff --git a/net/netfilter/ipvs/ip_vs_app.c b/net/netfilter/ipvs/ip_vs_app.c
index b0e00be85cb12c..11cbdbaf561daf 100644
--- a/net/netfilter/ipvs/ip_vs_app.c
+++ b/net/netfilter/ipvs/ip_vs_app.c
@@ -367,7 +367,7 @@ static inline int app_tcp_pkt_out(struct ip_vs_conn *cp, struct sk_buff *skb,
if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
+ th = (struct tcphdr *)(skb->data + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
@@ -443,7 +443,7 @@ static inline int app_tcp_pkt_in(struct ip_vs_conn *cp, struct sk_buff *skb,
if (skb_ensure_writable(skb, ipvsh->len + sizeof(*th)))
return 0;
- th = (struct tcphdr *)(skb_network_header(skb) + ipvsh->len);
+ th = (struct tcphdr *)(skb->data + ipvsh->len);
/*
* Remember seq number in case this pkt gets resized
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index bafab93451d037..7efa209a517b1c 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -867,7 +867,7 @@ static int sysctl_nat_icmp_send(struct netns_ipvs *ipvs) { return 0; }
#endif
-__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
+static __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset)
{
return csum_fold(skb_checksum(skb, offset, skb->len - offset, 0));
}
@@ -924,13 +924,13 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af,
* - inout: 1=in->out, 0=out->in
*/
void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
- struct ip_vs_conn *cp, int inout)
+ struct ip_vs_conn *cp, int inout, unsigned int toff,
+ bool has_ports)
{
struct iphdr *iph = ip_hdr(skb);
- unsigned int icmp_offset = iph->ihl*4;
- struct icmphdr *icmph = (struct icmphdr *)(skb_network_header(skb) +
- icmp_offset);
+ struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff);
struct iphdr *ciph = (struct iphdr *)(icmph + 1);
+ unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr);
if (inout) {
iph->saddr = cp->vaddr.ip;
@@ -945,8 +945,7 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
}
/* the TCP/UDP/SCTP port */
- if (IPPROTO_TCP == ciph->protocol || IPPROTO_UDP == ciph->protocol ||
- IPPROTO_SCTP == ciph->protocol) {
+ if (has_ports) {
__be16 *ports = (void *)ciph + ciph->ihl*4;
if (inout)
@@ -957,48 +956,40 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* And finally the ICMP checksum */
icmph->checksum = 0;
- icmph->checksum = ip_vs_checksum_complete(skb, icmp_offset);
+ icmph->checksum = ip_vs_checksum_complete(skb, toff);
skb->ip_summed = CHECKSUM_UNNECESSARY;
if (inout)
- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
- "Forwarding altered outgoing ICMP");
+ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
+ "Forwarding altered outgoing ICMP");
else
- IP_VS_DBG_PKT(11, AF_INET, pp, skb, (void *)ciph - (void *)iph,
- "Forwarding altered incoming ICMP");
+ IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff,
+ "Forwarding altered incoming ICMP");
}
#ifdef CONFIG_IP_VS_IPV6
void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
- struct ip_vs_conn *cp, int inout)
+ struct ip_vs_conn *cp, int inout, unsigned int toff,
+ bool has_ports, struct ip_vs_iphdr *ciph)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
- unsigned int icmp_offset = 0;
- unsigned int offs = 0; /* header offset*/
- int protocol;
struct icmp6hdr *icmph;
- struct ipv6hdr *ciph;
- unsigned short fragoffs;
+ struct ipv6hdr *cih;
- ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
- icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
- offs = icmp_offset + sizeof(struct icmp6hdr);
- ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
-
- protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
+ icmph = (struct icmp6hdr *)(skb->data + toff);
+ cih = (struct ipv6hdr *)(skb->data + ciph->off);
if (inout) {
iph->saddr = cp->vaddr.in6;
- ciph->daddr = cp->vaddr.in6;
+ cih->daddr = cp->vaddr.in6;
} else {
iph->daddr = cp->daddr.in6;
- ciph->saddr = cp->daddr.in6;
+ cih->saddr = cp->daddr.in6;
}
/* the TCP/UDP/SCTP port */
- if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
- IPPROTO_SCTP == protocol)) {
- __be16 *ports = (void *)(skb_network_header(skb) + offs);
+ if (has_ports) {
+ __be16 *ports = (void *)(skb->data + ciph->len);
IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
ntohs(inout ? ports[1] : ports[0]),
@@ -1011,19 +1002,17 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
/* And finally the ICMP checksum */
icmph->icmp6_cksum = ~csum_ipv6_magic(&iph->saddr, &iph->daddr,
- skb->len - icmp_offset,
+ skb->len - toff,
IPPROTO_ICMPV6, 0);
- skb->csum_start = skb_network_header(skb) - skb->head + icmp_offset;
+ skb->csum_start = skb_headroom(skb) + toff;
skb->csum_offset = offsetof(struct icmp6hdr, icmp6_cksum);
skb->ip_summed = CHECKSUM_PARTIAL;
if (inout)
- IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
- (void *)ciph - (void *)iph,
+ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
"Forwarding altered outgoing ICMPv6");
else
- IP_VS_DBG_PKT(11, AF_INET6, pp, skb,
- (void *)ciph - (void *)iph,
+ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, ciph->off,
"Forwarding altered incoming ICMPv6");
}
#endif
@@ -1033,36 +1022,41 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
*/
static int handle_response_icmp(int af, struct sk_buff *skb,
union nf_inet_addr *snet,
- __u8 protocol, struct ip_vs_conn *cp,
+ struct ip_vs_conn *cp,
struct ip_vs_protocol *pp,
- unsigned int offset, unsigned int ihl,
- unsigned int hooknum)
+ struct ip_vs_iphdr *ciph,
+ unsigned int toff, unsigned int hooknum)
{
+ int iproto = af == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP;
unsigned int verdict = NF_DROP;
+ unsigned int ctoff = ciph->len;
+ bool has_ports = false;
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)
goto after_nat;
/* Ensure the checksum is correct */
- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
+ if (!ip_vs_checksum_common_check(skb, toff, iproto, af)) {
/* Failed checksum! */
IP_VS_DBG_BUF(1, "Forward ICMP: failed checksum from %s!\n",
IP_VS_DBG_ADDR(af, snet));
goto out;
}
- if (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
- IPPROTO_SCTP == protocol)
- offset += 2 * sizeof(__u16);
- if (skb_ensure_writable(skb, offset))
+ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+ ciph->protocol == IPPROTO_SCTP) {
+ ctoff += 2 * sizeof(__u16);
+ has_ports = true;
+ }
+ if (skb_ensure_writable(skb, ctoff))
goto out;
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6)
- ip_vs_nat_icmp_v6(skb, pp, cp, 1);
+ ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph);
else
#endif
- ip_vs_nat_icmp(skb, pp, cp, 1);
+ ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports);
if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum))
goto out;
@@ -1090,9 +1084,9 @@ static int handle_response_icmp(int af, struct sk_buff *skb,
* Currently handles error types - unreachable, quench, ttl exceeded.
*/
static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
- int *related, unsigned int hooknum)
+ int *related, unsigned int hooknum,
+ struct ip_vs_iphdr *ipvsh)
{
- struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
@@ -1107,17 +1101,19 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
+ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, ipvsh))
+ return NF_ACCEPT;
}
- iph = ip_hdr(skb);
- offset = ihl = iph->ihl * 4;
+ ihl = ipvsh->len;
+ offset = ipvsh->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Outgoing ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
- &iph->saddr, &iph->daddr);
+ &ipvsh->saddr.ip, &ipvsh->daddr.ip);
/*
* Work through seeing if this is for us.
@@ -1136,7 +1132,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
/* Now find the contained IP header */
offset += sizeof(_icmph);
cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (cih == NULL)
+ if (!(cih && cih->version == 4 && cih->ihl >= 5))
return NF_ACCEPT; /* The packet looks wrong, ignore */
pp = ip_vs_proto_get(cih->protocol);
@@ -1144,8 +1140,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
/* Is the embedded protocol header present? */
- if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
- pp->dont_defrag))
+ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
@@ -1159,9 +1154,9 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (!cp)
return NF_ACCEPT;
- snet.ip = iph->saddr;
- return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
- pp, ciph.len, ihl, hooknum);
+ snet.ip = ipvsh->saddr.ip;
+ return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl,
+ hooknum);
}
#ifdef CONFIG_IP_VS_IPV6
@@ -1174,7 +1169,6 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
union nf_inet_addr snet;
- unsigned int offset;
*related = 1;
ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph);
@@ -1210,6 +1204,10 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
if (!pp)
return NF_ACCEPT;
+ /* Is the embedded protocol header present? */
+ if (unlikely(ciph.fragoffs && !pp->dont_defrag))
+ return NF_ACCEPT;
+
/* The embedded headers contain source and dest in reverse order */
cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto,
ipvs, AF_INET6, skb, &ciph);
@@ -1217,9 +1215,8 @@ static int ip_vs_out_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
snet.in6 = ciph.saddr.in6;
- offset = ciph.len;
- return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
- pp, offset, ipvsh->len, hooknum);
+ return handle_response_icmp(AF_INET6, skb, &snet, cp, pp, &ciph,
+ ipvsh->len, hooknum);
}
#endif
@@ -1545,7 +1542,8 @@ ip_vs_out_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *stat
#endif
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related;
- int verdict = ip_vs_out_icmp(ipvs, skb, &related, hooknum);
+ int verdict = ip_vs_out_icmp(ipvs, skb, &related,
+ hooknum, &iph);
if (related)
return verdict;
@@ -1753,9 +1751,8 @@ static int ipvs_gre_decap(struct netns_ipvs *ipvs, struct sk_buff *skb,
*/
static int
ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
- unsigned int hooknum)
+ unsigned int hooknum, struct ip_vs_iphdr *iph)
{
- struct iphdr *iph;
struct icmphdr _icmph, *ic;
struct iphdr _ciph, *cih; /* The ip header contained within the ICMP */
struct ip_vs_iphdr ciph;
@@ -1765,7 +1762,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
unsigned int offset, offset2, ihl, verdict;
bool tunnel, new_cp = false;
union nf_inet_addr *raddr;
- char *outer_proto = "IPIP";
+ char *outer_proto __maybe_unused = "IPIP";
unsigned int hlen_ipip;
int ulen = 0;
@@ -1775,17 +1772,19 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
if (ip_is_fragment(ip_hdr(skb))) {
if (ip_vs_gather_frags(ipvs, skb, ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
+ if (!ip_vs_fill_iph_skb(AF_INET, skb, false, iph))
+ return NF_ACCEPT;
}
- iph = ip_hdr(skb);
- offset = ihl = iph->ihl * 4;
+ ihl = iph->len;
+ offset = iph->len;
ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
if (ic == NULL)
return NF_DROP;
IP_VS_DBG(12, "Incoming ICMP (%d,%d) %pI4->%pI4\n",
ic->type, ntohs(icmp_id(ic)),
- &iph->saddr, &iph->daddr);
+ &iph->saddr.ip, &iph->daddr.ip);
/*
* Work through seeing if this is for us.
@@ -1867,8 +1866,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
pp = pd->pp;
/* Is the embedded protocol header present? */
- if (unlikely(cih->frag_off & htons(IP_OFFSET) &&
- pp->dont_defrag))
+ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag))
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
@@ -1876,7 +1874,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
offset2 = offset;
ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph);
- offset = ciph.len;
/* The embedded headers contain source and dest in reverse order.
* For IPIP/UDP/GRE tunnel this is error for request, not for reply.
@@ -1898,10 +1895,11 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
verdict = NF_DROP;
/* Ensure the checksum is correct */
- if (!skb_csum_unnecessary(skb) && ip_vs_checksum_complete(skb, ihl)) {
+ if ((IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ || tunnel) &&
+ !ip_vs_checksum_common_check(skb, ihl, IPPROTO_ICMP, AF_INET)) {
/* Failed checksum! */
IP_VS_DBG(1, "Incoming ICMP: failed checksum from %pI4!\n",
- &iph->saddr);
+ &iph->saddr.ip);
goto out;
}
@@ -1969,10 +1967,7 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related,
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
- if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol ||
- IPPROTO_SCTP == cih->protocol)
- offset += 2 * sizeof(__u16);
- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
+ verdict = ip_vs_icmp_xmit(skb, cp, pp, iph->len, hooknum, &ciph);
out:
if (likely(!new_cp))
@@ -2032,8 +2027,8 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
return NF_ACCEPT;
pp = pd->pp;
- /* Cannot handle fragmented embedded protocol */
- if (ciph.fragoffs)
+ /* Is the embedded protocol header present? */
+ if (ciph.fragoffs && !pp->dont_defrag)
return NF_ACCEPT;
IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
@@ -2057,23 +2052,22 @@ static int ip_vs_in_icmp_v6(struct netns_ipvs *ipvs, struct sk_buff *skb,
new_cp = true;
}
- /* VS/TUN, VS/DR and LOCALNODE just let it go */
- if ((hooknum == NF_INET_LOCAL_OUT) &&
- (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
- verdict = NF_ACCEPT;
+ verdict = NF_DROP;
+
+ /* Ensure the checksum is correct */
+ if (IP_VS_FWD_METHOD(cp) == IP_VS_CONN_F_MASQ &&
+ !ip_vs_checksum_common_check(skb, iph->len, IPPROTO_ICMPV6,
+ AF_INET6)) {
+ /* Failed checksum! */
+ IP_VS_DBG(1, "Incoming ICMPv6: failed checksum from %pI6c!\n",
+ &iph->saddr);
goto out;
}
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
- /* Need to mangle contained IPv6 header in ICMPv6 packet */
- offset = ciph.len;
- if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
- IPPROTO_SCTP == ciph.protocol)
- offset += 2 * sizeof(__u16); /* Also mangle ports */
-
- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum, &ciph);
+ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, iph->len, hooknum, &ciph);
out:
if (likely(!new_cp))
@@ -2152,7 +2146,7 @@ ip_vs_in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state
if (unlikely(iph.protocol == IPPROTO_ICMP)) {
int related;
int verdict = ip_vs_in_icmp(ipvs, skb, &related,
- hooknum);
+ hooknum, &iph);
if (related)
return verdict;
@@ -2288,6 +2282,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct netns_ipvs *ipvs = net_ipvs(state->net);
+ struct ip_vs_iphdr iphdr;
int r;
/* ipvs enabled in this netns ? */
@@ -2297,10 +2292,9 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
if (state->pf == NFPROTO_IPV4) {
if (ip_hdr(skb)->protocol != IPPROTO_ICMP)
return NF_ACCEPT;
+ ip_vs_fill_iph_skb(AF_INET, skb, false, &iphdr);
#ifdef CONFIG_IP_VS_IPV6
} else {
- struct ip_vs_iphdr iphdr;
-
ip_vs_fill_iph_skb(AF_INET6, skb, false, &iphdr);
if (iphdr.protocol != IPPROTO_ICMPV6)
@@ -2310,7 +2304,7 @@ ip_vs_forward_icmp(void *priv, struct sk_buff *skb,
#endif
}
- return ip_vs_in_icmp(ipvs, skb, &r, state->hook);
+ return ip_vs_in_icmp(ipvs, skb, &r, state->hook, &iphdr);
}
static const struct nf_hook_ops ip_vs_ops4[] = {
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index c67317be17dfaf..3dbd3096e1637b 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -11,7 +11,7 @@
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int sctphoff);
+ struct ip_vs_iphdr *iph);
static int
sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -109,7 +109,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
+ if (!sctp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@@ -121,7 +121,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- sctph = (void *) skb_network_header(skb) + sctphoff;
+ sctph = (void *)skb->data + sctphoff;
/* Only update csum if we really have to */
if (sctph->source != cp->vport || payload_csum ||
@@ -157,7 +157,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!sctp_csum_check(cp->af, skb, pp, sctphoff))
+ if (!sctp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@@ -169,7 +169,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- sctph = (void *) skb_network_header(skb) + sctphoff;
+ sctph = (void *)skb->data + sctphoff;
/* Only update csum if we really have to */
if (sctph->dest != cp->dport || payload_csum ||
@@ -187,19 +187,22 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int sctphoff)
+ struct ip_vs_iphdr *iph)
{
+ unsigned int sctphoff = iph->len;
struct sctphdr *sh;
__le32 cmp, val;
+ if (!ip_vs_checksum_needed(skb, af))
+ return 1;
sh = (struct sctphdr *)(skb->data + sctphoff);
cmp = sh->checksum;
val = sctp_compute_cksum(skb, sctphoff);
if (val != cmp) {
/* CRC failure, dump it. */
- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
- "Failed checksum for");
+ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
+ "Failed checksum for");
return 0;
}
return 1;
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index f86b763efcc4dc..99a286fdc90c65 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -29,7 +29,7 @@
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int tcphoff);
+ struct ip_vs_iphdr *iph);
static int
tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -166,7 +166,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
+ if (!tcp_csum_check(cp->af, skb, pp, iph))
return 0;
/* Call application helper if needed */
@@ -179,7 +179,7 @@ tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- tcph = (void *)skb_network_header(skb) + tcphoff;
+ tcph = (void *)skb->data + tcphoff;
tcph->source = cp->vport;
/* Adjust TCP checksums */
@@ -244,7 +244,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!tcp_csum_check(cp->af, skb, pp, tcphoff))
+ if (!tcp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@@ -260,7 +260,7 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- tcph = (void *)skb_network_header(skb) + tcphoff;
+ tcph = (void *)skb->data + tcphoff;
tcph->dest = cp->dport;
/*
@@ -302,41 +302,13 @@ tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
tcp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int tcphoff)
+ struct ip_vs_iphdr *iph)
{
- switch (skb->ip_summed) {
- case CHECKSUM_NONE:
- skb->csum = skb_checksum(skb, tcphoff, skb->len - tcphoff, 0);
- fallthrough;
- case CHECKSUM_COMPLETE:
-#ifdef CONFIG_IP_VS_IPV6
- if (af == AF_INET6) {
- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- skb->len - tcphoff,
- IPPROTO_TCP,
- skb->csum)) {
- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
- "Failed checksum for");
- return 0;
- }
- } else
-#endif
- if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
- ip_hdr(skb)->daddr,
- skb->len - tcphoff,
- ip_hdr(skb)->protocol,
- skb->csum)) {
- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
- "Failed checksum for");
- return 0;
- }
- break;
- default:
- /* No need to checksum. */
- break;
+ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_TCP, af)) {
+ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
+ "Failed checksum for");
+ return 0;
}
-
return 1;
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 58f9e255927e2e..f32785682402dd 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -25,7 +25,7 @@
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int udphoff);
+ struct ip_vs_iphdr *iph);
static int
udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb,
@@ -155,7 +155,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!udp_csum_check(cp->af, skb, pp, udphoff))
+ if (!udp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@@ -170,7 +170,7 @@ udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- udph = (void *)skb_network_header(skb) + udphoff;
+ udph = (void *)skb->data + udphoff;
udph->source = cp->vport;
/*
@@ -238,7 +238,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
int ret;
/* Some checks before mangling */
- if (!udp_csum_check(cp->af, skb, pp, udphoff))
+ if (!udp_csum_check(cp->af, skb, pp, iph))
return 0;
/*
@@ -254,7 +254,7 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
payload_csum = true;
}
- udph = (void *)skb_network_header(skb) + udphoff;
+ udph = (void *)skb->data + udphoff;
udph->dest = cp->dport;
/*
@@ -298,48 +298,20 @@ udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
static int
udp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp,
- unsigned int udphoff)
+ struct ip_vs_iphdr *iph)
{
struct udphdr _udph, *uh;
- uh = skb_header_pointer(skb, udphoff, sizeof(_udph), &_udph);
+ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
if (uh == NULL)
return 0;
- if (uh->check != 0) {
- switch (skb->ip_summed) {
- case CHECKSUM_NONE:
- skb->csum = skb_checksum(skb, udphoff,
- skb->len - udphoff, 0);
- fallthrough;
- case CHECKSUM_COMPLETE:
-#ifdef CONFIG_IP_VS_IPV6
- if (af == AF_INET6) {
- if (csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr,
- skb->len - udphoff,
- IPPROTO_UDP,
- skb->csum)) {
- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
- "Failed checksum for");
- return 0;
- }
- } else
-#endif
- if (csum_tcpudp_magic(ip_hdr(skb)->saddr,
- ip_hdr(skb)->daddr,
- skb->len - udphoff,
- ip_hdr(skb)->protocol,
- skb->csum)) {
- IP_VS_DBG_RL_PKT(0, af, pp, skb, 0,
- "Failed checksum for");
- return 0;
- }
- break;
- default:
- /* No need to checksum. */
- break;
- }
+ if (!uh->check)
+ return 1;
+ if (!ip_vs_checksum_common_check(skb, iph->len, IPPROTO_UDP, af)) {
+ IP_VS_DBG_RL_PKT(0, af, pp, skb, iph->off,
+ "Failed checksum for");
+ return 0;
}
return 1;
}
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 9fef4335da13fd..0b0c5304993a91 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -1502,20 +1502,22 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/
int
ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
- struct ip_vs_iphdr *iph)
+ struct ip_vs_protocol *pp, unsigned int toff,
+ unsigned int hooknum, struct ip_vs_iphdr *ciph)
{
struct rtable *rt; /* Route to the other host */
int rc;
int local;
int rt_mode, was_input;
+ bool has_ports = false;
+ unsigned int wlen;
/* The ICMP packet for VS/TUN, VS/DR and LOCALNODE will be
forwarded directly here, because there is no need to
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
- rc = cp->packet_xmit(skb, cp, pp, iph);
+ rc = cp->packet_xmit(skb, cp, pp, ciph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@@ -1533,7 +1535,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode,
- NULL, iph);
+ NULL, ciph);
if (local < 0)
goto tx_error;
rt = skb_rtable(skb);
@@ -1564,14 +1566,21 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error;
}
+ wlen = ciph->len;
+ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+ ciph->protocol == IPPROTO_SCTP) {
+ wlen += 2 * sizeof(__u16); /* Also mangle ports */
+ has_ports = true;
+ }
+
/* copy-on-write the packet before mangling it */
- if (skb_ensure_writable(skb, offset))
+ if (skb_ensure_writable(skb, wlen))
goto tx_error;
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
- ip_vs_nat_icmp(skb, pp, cp, 0);
+ ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
@@ -1587,10 +1596,12 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
- struct ip_vs_iphdr *ipvsh)
+ struct ip_vs_protocol *pp, unsigned int toff,
+ unsigned int hooknum, struct ip_vs_iphdr *ciph)
{
+ bool has_ports = false;
struct rt6_info *rt; /* Route to the other host */
+ unsigned int wlen;
int rc;
int local;
int rt_mode;
@@ -1600,7 +1611,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
- rc = cp->packet_xmit(skb, cp, pp, ipvsh);
+ rc = cp->packet_xmit(skb, cp, pp, ciph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@@ -1617,7 +1628,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL |
IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL;
local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest,
- &cp->daddr.in6, NULL, ipvsh, 0, rt_mode);
+ &cp->daddr.in6, NULL, ciph, 0, rt_mode);
if (local < 0)
goto tx_error;
rt = dst_rt6_info(skb_dst(skb));
@@ -1648,14 +1659,21 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error;
}
+ wlen = ciph->len;
+ if (ciph->protocol == IPPROTO_TCP || ciph->protocol == IPPROTO_UDP ||
+ ciph->protocol == IPPROTO_SCTP) {
+ wlen += 2 * sizeof(__u16); /* Also mangle ports */
+ has_ports = true;
+ }
+
/* copy-on-write the packet before mangling it */
- if (skb_ensure_writable(skb, offset))
+ if (skb_ensure_writable(skb, wlen))
goto tx_error;
if (skb_cow(skb, rt->dst.dev->hard_header_len))
goto tx_error;
- ip_vs_nat_icmp_v6(skb, pp, cp, 0);
+ ip_vs_nat_icmp_v6(skb, pp, cp, 0, toff, has_ports, ciph);
/* Another hack: avoid icmp_send in ip_fragment */
skb->ignore_df = 1;
|
| 3/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16306 output: 34 thoughts: 445 Thoughts: **Analyzing the IPVS Patch Series** Okay, so I'm looking at this patch series aimed at modifying the IPVS code within `net/netfilter/ipvs/`. My immediate focus is on the changes related to ICMP NAT handling and checksum validation. It looks like they've touched quite a few functions, including `ip_vs_nat_icmp`, `ip_vs_nat_icmp_v6`, `ip_vs_in_icmp`, `ip_vs_out_icmp`, `ip_vs_icmp_xmit`, and their IPv6 counterparts. They've also refactored checksum checking within the protocol handlers (`ip_vs_proto_tcp.c`, `ip_vs_proto_udp.c`, `ip_vs_proto_sctp.c`) using this new helper, `ip_vs_checksum_common_check`, which is interesting. Before I dig too deep, I need to confirm that IPVS is even enabled and accessible, which I can confirm is the case, so good, good. Now, let's nail down precisely which functions are being modified, because those are the prime candidates for fuzzing and targeted analysis. I want to highlight the areas that are likely to be the most impacted. I've got a list of the modified functions: `ip_vs_nat_icmp`, `ip_vs_nat_icmp_v6`, `handle_response_icmp`, `ip_vs_out_icmp`, `ip_vs_out_icmp_v6`, `ip_vs_in_icmp`, `ip_vs_in_icmp_v6`, `ip_vs_forward_icmp`, `sctp_csum_check`, `tcp_csum_check`, `udp_csum_check`, `ip_vs_icmp_xmit`, and `ip_vs_icmp_xmit_v6`. This gives me a much more focused view of where to apply my testing. Next step: double-checking the Kconfig options. I need to ensure that the necessary configuration options, like `CONFIG_IP_VS`, `CONFIG_IP_VS_IPV6`, `CONFIG_IP_VS_PROTO_TCP`, `CONFIG_IP_VS_PROTO_UDP`, and `CONFIG_IP_VS_PROTO_SCTP` are all set correctly and haven't introduced any unexpected dependencies or conflicts. Then it's time to verify the configuration and proceed. |
| 4/2 | 2026/07/23 07:55 | tool | grepper |
0mArgs:{
"Expression": "config IP_VS",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig-4-#\nnet/netfilter/ipvs/Kconfig:5:menuconfig IP_VS\nnet/netfilter/ipvs/Kconfig-6-\ttristate \"IP virtual server support\"\n--\nnet/netfilter/ipvs/Kconfig=286=comment 'IPVS SH scheduler'\nnet/netfilter/ipvs/Kconfig-287-\nnet/netfilter/ipvs/Kconfig:288:config IP_VS_SH_TAB_BITS\nnet/netfilter/ipvs/Kconfig-289-\tint \"IPVS source hashing table size (the Nth power of 2)\"\n--\nnet/netfilter/ipvs/Kconfig=301=comment 'IPVS MH scheduler'\nnet/netfilter/ipvs/Kconfig-302-\nnet/netfilter/ipvs/Kconfig:303:config IP_VS_MH_TAB_INDEX\nnet/netfilter/ipvs/Kconfig-304-\tint \"IPVS maglev hashing table index of size (the prime numbers)\"\n"
} |
| 5/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17580 output: 37 thoughts: 28 Thoughts: Here's my thought process, as if I were considering this myself: **Examining IPVS Configuration Options** Okay, so I need to verify a couple of IPVS (IP Virtual Server) configurations here. Let's see... First, I want to confirm the status of `CONFIG_IP_VS_IPV6`. Makes sense – we need to ensure IPv6 support is enabled, especially considering the direction of network infrastructure. IPv6 is increasingly important, and without this, the IPVS setup will be severely limited. I'm thinking, this is probably critical for modern deployments. Then, I need to check the status of `CONFIG_IP_VS_PROTO_SCTP`. SCTP is a bit less common than TCP and UDP, but it’s still important for certain applications. We should verify that SCTP support is properly integrated and enabled if the specific use case demands it. Basically, these config options determine the fundamental capabilities of IPVS, so they are the starting point for everything. Need to make sure both of these are set correctly. |
| 6/2 | 2026/07/23 07:55 | tool | grepper |
0mError:no matches Args: {
"Expression": "config IP_VS_IPV6",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17879 output: 34 thoughts: 56 Thoughts: Okay, let's see... **Investigating a Kernel Configuration Option** Hmm, I suspect the relevant configuration option for this might be `CONFIG_IP_VS_IPV6`. I'm pretty sure I saw it in `net/netfilter/ipvs/Kconfig` before, but it's always worth double-checking. Time to take a look at either that file or maybe the general `net/netfilter/Kconfig` file, just to be thorough and ensure I haven't missed something. Gotta track down exactly where that thing is defined and how it interacts with the rest of the network stack. |
| 8/2 | 2026/07/23 07:55 | tool | grepper |
0mArgs:{
"Expression": "config IP_VS",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig-4-#\nnet/netfilter/ipvs/Kconfig:5:menuconfig IP_VS\nnet/netfilter/ipvs/Kconfig-6-\ttristate \"IP virtual server support\"\n--\nnet/netfilter/ipvs/Kconfig=286=comment 'IPVS SH scheduler'\nnet/netfilter/ipvs/Kconfig-287-\nnet/netfilter/ipvs/Kconfig:288:config IP_VS_SH_TAB_BITS\nnet/netfilter/ipvs/Kconfig-289-\tint \"IPVS source hashing table size (the Nth power of 2)\"\n--\nnet/netfilter/ipvs/Kconfig=301=comment 'IPVS MH scheduler'\nnet/netfilter/ipvs/Kconfig-302-\nnet/netfilter/ipvs/Kconfig:303:config IP_VS_MH_TAB_INDEX\nnet/netfilter/ipvs/Kconfig-304-\tint \"IPVS maglev hashing table index of size (the prime numbers)\"\n"
} |
| 9/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18370 output: 36 thoughts: 13 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Diving into IPv6 and IPVS** Okay, so I'm thinking... gotta get a handle on the IPv6 implementation with IPVS, right? Let's start with a solid `grep` to quickly identify relevant code sections. That `IP_VS_IPV6` symbol feels like the best starting point. It’s the usual go-to identifier for any IPv6 specific elements in the kernel module, and will help me pinpoint the parts of the code base. Grabbing all the instances of `IP_VS_IPV6` should give me a good lay of the land, especially if I have to debug some connectivity issues. That should cover the basics. |
| 10/2 | 2026/07/23 07:55 | tool | grepper |
0mArgs:{
"Expression": "IP_VS_IPV6",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig=27=if IP_VS\nnet/netfilter/ipvs/Kconfig-28-\nnet/netfilter/ipvs/Kconfig:29:config\tIP_VS_IPV6\nnet/netfilter/ipvs/Kconfig-30-\tbool \"IPv6 support for IPVS\"\n"
} |
| 11/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18670 output: 39 thoughts: 13 Thoughts: Okay, here's my summary, tailored for an expert audience, as if it's a direct representation of my thought process: **SCTP Consideration** Right, so we're looking at network protocol considerations. We've got the usual suspects to think about, but wait a second... what about `IP_VS_PROTO_SCTP`? My immediate thought is, "Have we fully considered the implications of Stream Control Transmission Protocol (SCTP) within this context?" I need to quickly recall any specific optimizations or quirks related to using SCTP. Specifically, how will this protocol affect the Virtual Server (VS) implementation? Does SCTP introduce unique challenges or opportunities we need to account for in the setup? Are there nuances to the SCTP that we must be aware of for optimum performance? We might need to refresh the documentation to make sure we have fully accounted for its use. I should make a note to circle back and fully think through its implications. |
| 12/2 | 2026/07/23 07:55 | tool | grepper |
0mArgs:{
"Expression": "IP_VS_PROTO_SCTP",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig=100=config\tIP_VS_PROTO_AH\n--\nnet/netfilter/ipvs/Kconfig-105-\nnet/netfilter/ipvs/Kconfig:106:config IP_VS_PROTO_SCTP\nnet/netfilter/ipvs/Kconfig-107-\tbool \"SCTP load balancing support\"\n"
} |
| 13/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19032 output: 37 thoughts: 11 Thoughts: Okay, here's my summary, tailored as if I, an expert, am processing these thoughts: **Analyzing TCP Protocol Interaction** Alright, let's see... what about the relationship between IP and TCP, specifically that `IP_VS_PROTO_TCP` thing? That's a key element, isn't it? I need to think carefully about how that affects traffic management, especially in virtual server setups. It's not just a standalone protocol; it's the interplay, the specific implementation details, the interaction between these layers that really matters for performance and security. I should also consider how this specific protocol interaction influences load balancing techniques. Thinking about it now I can see that this is a question that requires a deeper dive and I should do that. |
| 14/2 | 2026/07/23 07:55 | tool | grepper |
0mArgs:{
"Expression": "IP_VS_PROTO_TCP",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig=77=comment \"IPVS transport protocol load balancing support\"\nnet/netfilter/ipvs/Kconfig-78-\nnet/netfilter/ipvs/Kconfig:79:config\tIP_VS_PROTO_TCP\nnet/netfilter/ipvs/Kconfig-80-\tbool \"TCP load balancing support\"\n--\nnet/netfilter/ipvs/Kconfig=321=config\tIP_VS_FTP\nnet/netfilter/ipvs/Kconfig-322-\ttristate \"FTP protocol helper\"\nnet/netfilter/ipvs/Kconfig:323:\tdepends on IP_VS_PROTO_TCP \u0026\u0026 NF_CONNTRACK \u0026\u0026 NF_NAT \u0026\u0026 \\\nnet/netfilter/ipvs/Kconfig-324-\t\tNF_CONNTRACK_FTP\n"
} |
| 15/2 | 2026/07/23 07:55 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19448 output: 37 thoughts: 11 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue: **UDP Considerations in IPVS** Right, let's see... we're talking about IP Virtual Server (IPVS) and its protocol handling. I'm deep in the weeds here, considering the nuances. Okay, so we're setting up a load balancing system using IPVS. That's a solid choice, considering its performance advantages, especially with UDP. And I know it's capable of handling various types of protocols, which gets me thinking... what about the specifics of handling UDP with IPVS, since there are some differences. The question on my mind is: What about the `IP_VS_PROTO_UDP` option in particular? Are there any specific considerations that I should be making for UDP connections using this IPVS protocol option, so that I don't overlook a critical detail. |
| 16/2 | 2026/07/23 07:56 | tool | grepper |
0mArgs:{
"Expression": "IP_VS_PROTO_UDP",
"PathPrefix": "net/netfilter/ipvs/Kconfig"
}Results: {
"Output": "net/netfilter/ipvs/Kconfig=79=config\tIP_VS_PROTO_TCP\n--\nnet/netfilter/ipvs/Kconfig-84-\nnet/netfilter/ipvs/Kconfig:85:config\tIP_VS_PROTO_UDP\nnet/netfilter/ipvs/Kconfig-86-\tbool \"UDP load balancing support\"\n--\nnet/netfilter/ipvs/Kconfig=345=config\tIP_VS_PE_SIP\nnet/netfilter/ipvs/Kconfig-346-\ttristate \"SIP persistence engine\"\nnet/netfilter/ipvs/Kconfig:347:\tdepends on IP_VS_PROTO_UDP\nnet/netfilter/ipvs/Kconfig-348-\tdepends on NF_CONNTRACK_SIP\n"
} |
| 17/2 | 2026/07/23 07:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19897 output: 280 thoughts: 431 Thoughts: **Kernel Configuration & Function Analysis for IPVS ICMP Handling** Okay, so I'm looking at enabling specific configurations related to IP Virtual Server (IPVS) functionality. I need to make sure the following are enabled in the kernel: `IP_VS`, `IP_VS_IPV6`, `IP_VS_PROTO_TCP`, `IP_VS_PROTO_UDP`, and `IP_VS_PROTO_SCTP`. These are the basics for the different protocol support. Now, I'm focusing on some key symbols, primarily related to ICMP and checksum handling within IPVS. I'm thinking about: * ICMP NAT functions: `ip_vs_nat_icmp`, `ip_vs_nat_icmp_v6` * ICMP response handling: `handle_response_icmp` * ICMP transmission and reception: `ip_vs_out_icmp`, `ip_vs_out_icmp_v6`, `ip_vs_in_icmp`, `ip_vs_in_icmp_v6`, `ip_vs_forward_icmp` * Checksum checks: `sctp_csum_check`, `tcp_csum_check`, `udp_csum_check` * ICMP transmit functions: `ip_vs_icmp_xmit`, `ip_vs_icmp_xmit_v6` I need to confirm whether those checksum check functions are static, and where the others are defined. So I've checked... `sctp_csum_check`, `tcp_csum_check`, and `udp_csum_check` are static functions within their respective protocol files: `ip_vs_proto_sctp.c`, `ip_vs_proto_tcp.c`, and `ip_vs_proto_udp.c`. Good, that's expected. I found that `ip_vs_nat_icmp`, `handle_response_icmp`, `ip_vs_out_icmp`, `ip_vs_in_icmp`, `ip_vs_forward_icmp` all reside and are static within `ip_vs_core.c`. And finally, `ip_vs_icmp_xmit` is found within `ip_vs_xmit.c`. Okay, I think I have what I need, now. Let's use `set-results` to get this information in a usable format. |
| 18/2 | 2026/07/23 07:56 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"IP_VS",
"IP_VS_IPV6",
"IP_VS_PROTO_TCP",
"IP_VS_PROTO_UDP",
"IP_VS_PROTO_SCTP"
],
"FocusSymbols": [
"ip_vs_nat_icmp",
"ip_vs_nat_icmp_v6",
"handle_response_icmp",
"ip_vs_out_icmp",
"ip_vs_out_icmp_v6",
"ip_vs_in_icmp",
"ip_vs_in_icmp_v6",
"ip_vs_forward_icmp",
"sctp_csum_check",
"tcp_csum_check",
"udp_csum_check",
"ip_vs_icmp_xmit",
"ip_vs_icmp_xmit_v6"
],
"Reasoning": "The patch modifies ICMP NAT handling and checksum validation logic in IPVS. It changes how ICMP packets are processed, NATed, and transmitted, and updates checksum validation for TCP, UDP, and SCTP protocols within IPVS. Fuzzing should focus on these modified ICMP handling and checksum validation functions.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"IP_VS",
"IP_VS_IPV6",
"IP_VS_PROTO_TCP",
"IP_VS_PROTO_UDP",
"IP_VS_PROTO_SCTP"
],
"FocusSymbols": [
"ip_vs_nat_icmp",
"ip_vs_nat_icmp_v6",
"handle_response_icmp",
"ip_vs_out_icmp",
"ip_vs_out_icmp_v6",
"ip_vs_in_icmp",
"ip_vs_in_icmp_v6",
"ip_vs_forward_icmp",
"sctp_csum_check",
"tcp_csum_check",
"udp_csum_check",
"ip_vs_icmp_xmit",
"ip_vs_icmp_xmit_v6"
],
"Reasoning": "The patch modifies ICMP NAT handling and checksum validation logic in IPVS. It changes how ICMP packets are processed, NATed, and transmitted, and updates checksum validation for TCP, UDP, and SCTP protocols within IPVS. Fuzzing should focus on these modified ICMP handling and checksum validation functions.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|