summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/infiniband/core/lag.c2
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.c4
-rw-r--r--drivers/net/amt.c6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_txrx.c2
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_txrx.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.c2
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_txrx.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c2
-rw-r--r--drivers/net/ethernet/sfc/falcon/selftest.c4
-rw-r--r--drivers/net/ethernet/sfc/selftest.c4
-rw-r--r--drivers/net/ethernet/sfc/siena/selftest.c4
-rw-r--r--drivers/net/ethernet/sfc/tc_encap_actions.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c4
-rw-r--r--drivers/net/geneve.c4
-rw-r--r--drivers/net/netconsole.c2
-rw-r--r--drivers/net/netdevsim/dev.c2
-rw-r--r--drivers/net/netdevsim/psample.c2
-rw-r--r--drivers/net/netdevsim/psp.c8
-rw-r--r--drivers/net/vxlan/vxlan_core.c2
-rw-r--r--drivers/net/wireguard/receive.c2
-rw-r--r--include/linux/udp.h27
-rw-r--r--include/trace/events/icmp.h2
-rw-r--r--lib/tests/blackhole_dev_kunit.c2
-rw-r--r--net/6lowpan/nhc_udp.c10
-rw-r--r--net/core/pktgen.c4
-rw-r--r--net/core/selftests.c4
-rw-r--r--net/core/skbuff.c10
-rw-r--r--net/core/tso.c3
-rw-r--r--net/ipv4/esp4.c2
-rw-r--r--net/ipv4/fou_core.c2
-rw-r--r--net/ipv4/ipconfig.c6
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic_main.c4
-rw-r--r--net/ipv4/route.c2
-rw-r--r--net/ipv4/udp.c7
-rw-r--r--net/ipv4/udp_offload.c49
-rw-r--r--net/ipv4/udp_tunnel_core.c2
-rw-r--r--net/ipv6/esp6.c5
-rw-r--r--net/ipv6/fou6.c2
-rw-r--r--net/ipv6/ip6_udp_tunnel.c2
-rw-r--r--net/ipv6/udp.c7
-rw-r--r--net/ipv6/udp_offload.c2
-rw-r--r--net/l2tp/l2tp_core.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c2
-rw-r--r--net/netfilter/nf_conntrack_proto_udp.c15
-rw-r--r--net/netfilter/nf_log_syslog.c2
-rw-r--r--net/netfilter/nf_nat_helper.c2
-rw-r--r--net/psp/psp_main.c2
-rw-r--r--net/sched/act_csum.c4
-rw-r--r--net/xfrm/xfrm_nat_keepalive.c2
-rw-r--r--tools/testing/selftests/net/Makefile1
-rwxr-xr-xtools/testing/selftests/net/big_tcp_tunnels.sh188
53 files changed, 340 insertions, 102 deletions
diff --git a/drivers/infiniband/core/lag.c b/drivers/infiniband/core/lag.c
index 8fd80adfe833..00fe241737ff 100644
--- a/drivers/infiniband/core/lag.c
+++ b/drivers/infiniband/core/lag.c
@@ -36,7 +36,7 @@ static struct sk_buff *rdma_build_skb(struct net_device *netdev,
uh->source =
htons(rdma_flow_label_to_udp_sport(ah_attr->grh.flow_label));
uh->dest = htons(ROCE_V2_UDP_DPORT);
- uh->len = htons(sizeof(struct udphdr));
+ udp_set_len_short(uh, sizeof(struct udphdr));
if (is_ipv4) {
skb_push(skb, sizeof(struct iphdr));
diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c
index 3741b2c4b0bb..53daaf4c1eb2 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.c
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@ -242,7 +242,7 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
pkt->port_num = 1;
pkt->hdr = (u8 *)(udph + 1);
pkt->mask = RXE_GRH_MASK;
- pkt->paylen = be16_to_cpu(udph->len) - sizeof(*udph);
+ pkt->paylen = udp_get_len_short(udph) - sizeof(*udph);
/* remove udp header */
skb_pull(skb, sizeof(struct udphdr));
@@ -305,7 +305,7 @@ static void prepare_udp_hdr(struct sk_buff *skb, __be16 src_port,
udph->dest = dst_port;
udph->source = src_port;
- udph->len = htons(skb->len);
+ udp_set_len_short(udph, skb->len);
udph->check = 0;
}
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index 02168862f676..f8169c5512a5 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -667,7 +667,7 @@ static void amt_send_discovery(struct amt_dev *amt)
udph = udp_hdr(skb);
udph->source = amt->gw_port;
udph->dest = amt->relay_port;
- udph->len = htons(sizeof(*udph) + sizeof(*amtd));
+ udp_set_len_short(udph, sizeof(*udph) + sizeof(*amtd));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
@@ -760,7 +760,7 @@ static void amt_send_request(struct amt_dev *amt, bool v6)
udph = udp_hdr(skb);
udph->source = amt->gw_port;
udph->dest = amt->relay_port;
- udph->len = htons(sizeof(*amtrh) + sizeof(*udph));
+ udp_set_len_short(udph, sizeof(*amtrh) + sizeof(*udph));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
@@ -2611,7 +2611,7 @@ static void amt_send_advertisement(struct amt_dev *amt, __be32 nonce,
udph = udp_hdr(skb);
udph->source = amt->relay_port;
udph->dest = dport;
- udph->len = htons(sizeof(*amta) + sizeof(*udph));
+ udp_set_len_short(udph, sizeof(*amta) + sizeof(*udph));
udph->check = 0;
offset = skb_transport_offset(skb);
skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
index 894f2d06d39d..ef5e657816f0 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c
@@ -3129,7 +3129,7 @@ static int i40e_tso(struct i40e_tx_buffer *first, u8 *hdr_len,
SKB_GSO_UDP_TUNNEL_CSUM)) {
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) {
- l4.udp->len = 0;
+ udp_set_len_short(l4.udp, 0);
/* determine offset of outer transport header */
l4_offset = l4.hdr - skb->data;
diff --git a/drivers/net/ethernet/intel/iavf/iavf_txrx.c b/drivers/net/ethernet/intel/iavf/iavf_txrx.c
index 363c42bf3dcf..c30abf17cf5d 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_txrx.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_txrx.c
@@ -1774,7 +1774,7 @@ static int iavf_tso(struct iavf_tx_buffer *first, u8 *hdr_len,
SKB_GSO_UDP_TUNNEL_CSUM)) {
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) {
- l4.udp->len = 0;
+ udp_set_len_short(l4.udp, 0);
/* determine offset of outer transport header */
l4_offset = l4.hdr - skb->data;
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index 4ca1a0602307..fdea2758adf1 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -1893,7 +1893,7 @@ int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
SKB_GSO_UDP_TUNNEL_CSUM)) {
if (!(skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL) &&
(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM)) {
- l4.udp->len = 0;
+ udp_set_len_short(l4.udp, 0);
/* determine offset of outer transport header */
l4_start = (u8)(l4.hdr - skb->data);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 7f9056404f64..566b08ca3a6c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -2871,7 +2871,7 @@ int idpf_tso(struct sk_buff *skb, struct idpf_tx_offload_params *off)
(__force __wsum)htonl(paylen));
/* compute length of segmentation header */
off->tso_hdr_len = sizeof(struct udphdr) + l4_start;
- l4.udp->len = htons(shinfo->gso_size + sizeof(struct udphdr));
+ udp_set_len_short(l4.udp, shinfo->gso_size + sizeof(struct udphdr));
break;
default:
return -EINVAL;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
index 625bb5a05344..8d2d607bc92f 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c
@@ -750,7 +750,7 @@ static void otx2_sqe_add_ext(struct otx2_nic *pfvf, struct otx2_snd_queue *sq,
ext->lso_format = pfvf->hw.lso_udpv6_idx;
}
- udph->len = htons(sizeof(struct udphdr));
+ udp_set_len_short(udph, sizeof(struct udphdr));
}
} else if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
ext->tstmp = 1;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 6fbc0441c4b8..04af54b704d8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -1081,7 +1081,7 @@ static void mlx5e_shampo_update_ipv4_udp_hdr(struct mlx5e_rq *rq, struct iphdr *
struct udphdr *uh;
uh = (struct udphdr *)(skb->data + udp_off);
- uh->len = htons(skb->len - udp_off);
+ udp_set_len_short(uh, skb->len - udp_off);
if (uh->check)
uh->check = ~udp_v4_check(skb->len - udp_off, ipv4->saddr,
@@ -1100,7 +1100,7 @@ static void mlx5e_shampo_update_ipv6_udp_hdr(struct mlx5e_rq *rq, struct ipv6hdr
struct udphdr *uh;
uh = (struct udphdr *)(skb->data + udp_off);
- uh->len = htons(skb->len - udp_off);
+ udp_set_len_short(uh, skb->len - udp_off);
if (uh->check)
uh->check = ~udp_v6_check(skb->len - udp_off, &ipv6->saddr,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
index accc26d1a872..1dcdb86690bb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
@@ -113,7 +113,7 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv)
/* Fill UDP header */
udph->source = htons(9);
udph->dest = htons(9); /* Discard Protocol */
- udph->len = htons(sizeof(struct mlx5ehdr) + sizeof(struct udphdr));
+ udp_set_len_short(udph, sizeof(struct mlx5ehdr) + sizeof(struct udphdr));
udph->check = 0;
/* Fill IP header */
diff --git a/drivers/net/ethernet/sfc/falcon/selftest.c b/drivers/net/ethernet/sfc/falcon/selftest.c
index db4dd7fb77f5..4d29e0baf2eb 100644
--- a/drivers/net/ethernet/sfc/falcon/selftest.c
+++ b/drivers/net/ethernet/sfc/falcon/selftest.c
@@ -401,8 +401,8 @@ static void ef4_iterate_state(struct ef4_nic *efx)
/* Initialise udp header */
payload->udp.source = 0;
- payload->udp.len = htons(sizeof(*payload) -
- offsetof(struct ef4_loopback_payload, udp));
+ udp_set_len_short(&payload->udp, sizeof(*payload) -
+ offsetof(struct ef4_loopback_payload, udp));
payload->udp.check = 0; /* checksum ignored */
/* Fill out payload */
diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c
index 8ec76329237a..dc716feb79cb 100644
--- a/drivers/net/ethernet/sfc/selftest.c
+++ b/drivers/net/ethernet/sfc/selftest.c
@@ -398,8 +398,8 @@ static void efx_iterate_state(struct efx_nic *efx)
/* Initialise udp header */
payload->udp.source = 0;
- payload->udp.len = htons(sizeof(*payload) -
- offsetof(struct efx_loopback_payload, udp));
+ udp_set_len_short(&payload->udp, sizeof(*payload) -
+ offsetof(struct efx_loopback_payload, udp));
payload->udp.check = 0; /* checksum ignored */
/* Fill out payload */
diff --git a/drivers/net/ethernet/sfc/siena/selftest.c b/drivers/net/ethernet/sfc/siena/selftest.c
index 930643612df5..c74cf5131364 100644
--- a/drivers/net/ethernet/sfc/siena/selftest.c
+++ b/drivers/net/ethernet/sfc/siena/selftest.c
@@ -399,8 +399,8 @@ static void efx_iterate_state(struct efx_nic *efx)
/* Initialise udp header */
payload->udp.source = 0;
- payload->udp.len = htons(sizeof(*payload) -
- offsetof(struct efx_loopback_payload, udp));
+ udp_set_len_short(&payload->udp, sizeof(*payload) -
+ offsetof(struct efx_loopback_payload, udp));
payload->udp.check = 0; /* checksum ignored */
/* Fill out payload */
diff --git a/drivers/net/ethernet/sfc/tc_encap_actions.c b/drivers/net/ethernet/sfc/tc_encap_actions.c
index db222abef53b..c2ad3a358d20 100644
--- a/drivers/net/ethernet/sfc/tc_encap_actions.c
+++ b/drivers/net/ethernet/sfc/tc_encap_actions.c
@@ -311,7 +311,7 @@ static void efx_gen_tun_header_udp(struct efx_tc_encap_action *encap, u8 len)
encap->encap_hdr_len += sizeof(*udp);
udp->dest = key->tp_dst;
- udp->len = cpu_to_be16(sizeof(*udp) + len);
+ udp_set_len_short(udp, sizeof(*udp) + len);
}
static void efx_gen_tun_header_vxlan(struct efx_tc_encap_action *encap)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index a0c75886587c..29e824bd90ca 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -154,9 +154,9 @@ static struct sk_buff *stmmac_test_get_udp_skb(struct stmmac_priv *priv,
} else {
uhdr->source = htons(attr->sport);
uhdr->dest = htons(attr->dport);
- uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
+ udp_set_len_short(uhdr, sizeof(*shdr) + sizeof(*uhdr) + attr->size);
if (attr->max_size)
- uhdr->len = htons(attr->max_size -
+ udp_set_len_short(uhdr, attr->max_size -
(sizeof(*ihdr) + sizeof(*ehdr)));
uhdr->check = 0;
}
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 17bd3543d587..359d6da7348c 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -630,7 +630,7 @@ static int geneve_post_decap_hint(const struct sock *sk, struct sk_buff *skb,
/* Adjust the nested UDP header len and checksum. */
uh = udp_hdr(skb);
- uh->len = htons(skb->len - gro_hint->nested_tp_offset);
+ udp_set_len_short(uh, skb->len - gro_hint->nested_tp_offset);
if (uh->check) {
len = skb->len - gro_hint->nested_tp_offset;
skb_shinfo(skb)->gso_type |= SKB_GSO_UDP_TUNNEL_CSUM;
@@ -1808,6 +1808,8 @@ static void geneve_setup(struct net_device *dev)
dev->max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len;
netif_keep_dst(dev);
+ netif_set_tso_max_size(dev, GSO_MAX_SIZE);
+
dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->priv_flags |= IFF_LIVE_ADDR_CHANGE | IFF_NO_QUEUE;
dev->lltx = true;
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 49b2243a20b2..7f8851a734fc 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -1881,7 +1881,7 @@ static void push_udp(struct netconsole_target *nt, struct sk_buff *skb, int len)
udph = udp_hdr(skb);
udph->source = htons(nt->local_port);
udph->dest = htons(nt->remote_port);
- udph->len = htons(udp_len);
+ udp_set_len_short(udph, udp_len);
netpoll_udp_checksum(np, skb, len);
}
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index aed9ad5f1b43..f65b4cf4ea39 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -845,7 +845,7 @@ static struct sk_buff *nsim_dev_trap_skb_build(void)
udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
get_random_bytes(&udph->source, sizeof(u16));
get_random_bytes(&udph->dest, sizeof(u16));
- udph->len = htons(sizeof(struct udphdr) + data_len);
+ udp_set_len_short(udph, sizeof(struct udphdr) + data_len);
return skb;
}
diff --git a/drivers/net/netdevsim/psample.c b/drivers/net/netdevsim/psample.c
index 717d157c3ae2..1e71c3da4def 100644
--- a/drivers/net/netdevsim/psample.c
+++ b/drivers/net/netdevsim/psample.c
@@ -73,7 +73,7 @@ static struct sk_buff *nsim_dev_psample_skb_build(void)
udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
get_random_bytes(&udph->source, sizeof(u16));
get_random_bytes(&udph->dest, sizeof(u16));
- udph->len = htons(sizeof(struct udphdr) + data_len);
+ udp_set_len_short(udph, sizeof(struct udphdr) + data_len);
return skb;
}
diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
index 59c990fdc79e..6b3532b5e360 100644
--- a/drivers/net/netdevsim/psp.c
+++ b/drivers/net/netdevsim/psp.c
@@ -84,6 +84,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
struct iphdr *iph;
struct udphdr *uh;
__wsum csum;
+ int udplen;
/* Do not decapsulate. Receive the skb with the udp and psp
* headers still there as if this is a normal udp packet.
@@ -91,19 +92,20 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
* provide a valid checksum here, so the skb isn't dropped.
*/
uh = udp_hdr(skb);
+ udplen = udp_get_len(skb, uh, skb_transport_offset(skb));
csum = skb_checksum(skb, skb_transport_offset(skb),
- ntohs(uh->len), 0);
+ udplen, 0);
switch (skb->protocol) {
case htons(ETH_P_IP):
iph = ip_hdr(skb);
- uh->check = udp_v4_check(ntohs(uh->len), iph->saddr,
+ uh->check = udp_v4_check(udplen, iph->saddr,
iph->daddr, csum);
break;
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6):
ip6h = ipv6_hdr(skb);
- uh->check = udp_v6_check(ntohs(uh->len), &ip6h->saddr,
+ uh->check = udp_v6_check(udplen, &ip6h->saddr,
&ip6h->daddr, csum);
break;
#endif
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 67c367cc5662..3a32f067e701 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -3370,6 +3370,8 @@ static void vxlan_setup(struct net_device *dev)
dev->mangleid_features = NETIF_F_GSO_PARTIAL;
netif_keep_dst(dev);
+ netif_set_tso_max_size(dev, GSO_MAX_SIZE);
+
dev->priv_flags |= IFF_NO_QUEUE;
dev->change_proto_down = true;
dev->lltx = true;
diff --git a/drivers/net/wireguard/receive.c b/drivers/net/wireguard/receive.c
index eb8851113654..824bbefce61c 100644
--- a/drivers/net/wireguard/receive.c
+++ b/drivers/net/wireguard/receive.c
@@ -62,7 +62,7 @@ static int prepare_skb_header(struct sk_buff *skb, struct wg_device *wg)
* to have UDP fields.
*/
return -EINVAL;
- data_len = ntohs(udp->len);
+ data_len = udp_get_len_short(udp);
if (unlikely(data_len < sizeof(struct udphdr) ||
data_len > skb->len - data_offset))
/* UDP packet is reporting too small of a size or lying about
diff --git a/include/linux/udp.h b/include/linux/udp.h
index ce56ebcee5cb..998906ec3b32 100644
--- a/include/linux/udp.h
+++ b/include/linux/udp.h
@@ -23,6 +23,33 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
return (struct udphdr *)skb_transport_header(skb);
}
+static inline unsigned int udp_get_len(const struct sk_buff *skb,
+ const struct udphdr *uh,
+ unsigned int dataoff)
+{
+ if (uh->len)
+ return ntohs(uh->len);
+ if (skb_is_gso(skb)) /* BIG TCP */
+ return skb->len - dataoff;
+ return 0;
+}
+
+static inline unsigned int udp_get_len_short(const struct udphdr *uh)
+{
+ return ntohs(uh->len);
+}
+
+static inline void udp_set_len(struct udphdr *uh, unsigned int len)
+{
+ uh->len = len < GRO_LEGACY_MAX_SIZE ? htons(len) : 0;
+}
+
+static inline void udp_set_len_short(struct udphdr *uh, unsigned int len)
+{
+ DEBUG_NET_WARN_ON_ONCE(len >= GRO_LEGACY_MAX_SIZE);
+ uh->len = htons(len);
+}
+
#define UDP_HTABLE_SIZE_MIN_PERNET 128
#define UDP_HTABLE_SIZE_MIN (IS_ENABLED(CONFIG_BASE_SMALL) ? 128 : 256)
#define UDP_HTABLE_SIZE_MAX 65536
diff --git a/include/trace/events/icmp.h b/include/trace/events/icmp.h
index 31559796949a..09ae115099df 100644
--- a/include/trace/events/icmp.h
+++ b/include/trace/events/icmp.h
@@ -44,7 +44,7 @@ TRACE_EVENT(icmp_send,
} else {
__entry->sport = ntohs(uh->source);
__entry->dport = ntohs(uh->dest);
- __entry->ulen = ntohs(uh->len);
+ __entry->ulen = udp_get_len_short(uh);
}
p32 = (__be32 *) __entry->saddr;
diff --git a/lib/tests/blackhole_dev_kunit.c b/lib/tests/blackhole_dev_kunit.c
index 06834ab35f43..fa3e0533038d 100644
--- a/lib/tests/blackhole_dev_kunit.c
+++ b/lib/tests/blackhole_dev_kunit.c
@@ -46,7 +46,7 @@ static void test_blackholedev(struct kunit *test)
uh = (struct udphdr *)skb_push(skb, sizeof(struct udphdr));
skb_set_transport_header(skb, 0);
uh->source = uh->dest = htons(UDP_PORT);
- uh->len = htons(data_len);
+ udp_set_len_short(uh, data_len);
uh->check = 0;
/* (Network) IPv6 */
ip6h = (struct ipv6hdr *)skb_push(skb, sizeof(struct ipv6hdr));
diff --git a/net/6lowpan/nhc_udp.c b/net/6lowpan/nhc_udp.c
index 0a506c77283d..ed4227e6db74 100644
--- a/net/6lowpan/nhc_udp.c
+++ b/net/6lowpan/nhc_udp.c
@@ -88,16 +88,16 @@ static int udp_uncompress(struct sk_buff *skb, size_t needed)
switch (lowpan_dev(skb->dev)->lltype) {
case LOWPAN_LLTYPE_IEEE802154:
if (lowpan_802154_cb(skb)->d_size)
- uh.len = htons(lowpan_802154_cb(skb)->d_size -
- sizeof(struct ipv6hdr));
+ udp_set_len_short(&uh, lowpan_802154_cb(skb)->d_size -
+ sizeof(struct ipv6hdr));
else
- uh.len = htons(skb->len + sizeof(struct udphdr));
+ udp_set_len_short(&uh, skb->len + sizeof(struct udphdr));
break;
default:
- uh.len = htons(skb->len + sizeof(struct udphdr));
+ udp_set_len_short(&uh, skb->len + sizeof(struct udphdr));
break;
}
- pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
+ pr_debug("uncompressed UDP length: src = %d", udp_get_len_short(&uh));
/* replace the compressed UDP head by the uncompressed UDP
* header
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 8e185b318288..5b4dd04d6124 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3005,7 +3005,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(datalen + 8); /* DATA + udphdr */
+ udp_set_len_short(udph, datalen + 8); /* DATA + udphdr */
udph->check = 0;
iph->ihl = 5;
@@ -3138,7 +3138,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
udplen = datalen + sizeof(struct udphdr);
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(udplen);
+ udp_set_len_short(udph, udplen);
udph->check = 0;
*(__be32 *) iph = htonl(0x60000000); /* Version + flow */
diff --git a/net/core/selftests.c b/net/core/selftests.c
index 0a203d3fb9dc..36b949ae520b 100644
--- a/net/core/selftests.c
+++ b/net/core/selftests.c
@@ -72,9 +72,9 @@ struct sk_buff *net_test_get_skb(struct net_device *ndev, u8 id,
} else {
uhdr->source = htons(attr->sport);
uhdr->dest = htons(attr->dport);
- uhdr->len = htons(sizeof(*shdr) + sizeof(*uhdr) + attr->size);
+ udp_set_len_short(uhdr, sizeof(*shdr) + sizeof(*uhdr) + attr->size);
if (attr->max_size)
- uhdr->len = htons(attr->max_size -
+ udp_set_len_short(uhdr, attr->max_size -
(sizeof(*ihdr) + sizeof(*ehdr)));
uhdr->check = 0;
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d798fbdc3da7..d7d53a7c58f6 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4775,6 +4775,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
struct sk_buff *tail = NULL;
struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list;
unsigned int mss = skb_shinfo(head_skb)->gso_size;
+ bool gso_by_frags = mss == GSO_BY_FRAGS;
unsigned int doffset = head_skb->data - skb_mac_header(head_skb);
unsigned int offset = doffset;
unsigned int tnl_hlen = skb_tnl_header_len(head_skb);
@@ -4790,7 +4791,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
int nfrags, pos;
if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) &&
- mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) {
+ !gso_by_frags && mss != skb_headlen(head_skb)) {
struct sk_buff *check_skb;
for (check_skb = list_skb; check_skb; check_skb = check_skb->next) {
@@ -4818,7 +4819,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
sg = !!(features & NETIF_F_SG);
csum = !!can_checksum_protocol(features, proto);
- if (sg && csum && (mss != GSO_BY_FRAGS)) {
+ if (sg && csum && !gso_by_frags) {
if (!(features & NETIF_F_GSO_PARTIAL)) {
struct sk_buff *iter;
unsigned int frag_len;
@@ -4852,9 +4853,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
/* GSO partial only requires that we trim off any excess that
* doesn't fit into an MSS sized block, so take care of that
* now.
- * Cap len to not accidentally hit GSO_BY_FRAGS.
*/
- partial_segs = min(len, GSO_BY_FRAGS - 1) / mss;
+ partial_segs = len / mss;
if (partial_segs > 1)
mss *= partial_segs;
else
@@ -4878,7 +4878,7 @@ normal:
int hsize;
int size;
- if (unlikely(mss == GSO_BY_FRAGS)) {
+ if (unlikely(gso_by_frags)) {
len = list_skb->len;
} else {
len = head_skb->len - offset;
diff --git a/net/core/tso.c b/net/core/tso.c
index 347b3856ddb9..d2934bcfa795 100644
--- a/net/core/tso.c
+++ b/net/core/tso.c
@@ -39,7 +39,8 @@ void tso_build_hdr(const struct sk_buff *skb, char *hdr, struct tso_t *tso,
} else {
struct udphdr *uh = (struct udphdr *)hdr;
- uh->len = htons(sizeof(*uh) + size);
+ /* size is after segmentation. */
+ udp_set_len_short(uh, sizeof(*uh) + size);
}
}
EXPORT_SYMBOL(tso_build_hdr);
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index dfc81ee969ae..a6c18aea7498 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -323,7 +323,7 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
uh = (struct udphdr *)esp->esph;
uh->source = sport;
uh->dest = dport;
- uh->len = htons(len);
+ udp_set_len_short(uh, len);
uh->check = 0;
/* For IPv4 ESP with UDP encapsulation, if xo is not null, the skb is in the crypto offload
diff --git a/net/ipv4/fou_core.c b/net/ipv4/fou_core.c
index 865bd7205122..aef3ce1dec7a 100644
--- a/net/ipv4/fou_core.c
+++ b/net/ipv4/fou_core.c
@@ -1040,7 +1040,7 @@ static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
uh->dest = e->dport;
uh->source = sport;
- uh->len = htons(skb->len);
+ udp_set_len(uh, skb->len);
udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
fl4->saddr, fl4->daddr, skb->len);
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index a35ffedacc7c..155db067eaec 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -847,7 +847,7 @@ static void __init ic_bootp_send_if(struct ic_device *d, unsigned long jiffies_d
/* Construct UDP header */
b->udph.source = htons(68);
b->udph.dest = htons(67);
- b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr));
+ udp_set_len_short(&b->udph, sizeof(struct bootp_pkt) - sizeof(struct iphdr));
/* UDP checksum not calculated -- explicitly allowed in BOOTP RFC */
/* Construct DHCP/BOOTP header */
@@ -1025,10 +1025,10 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
if (b->udph.source != htons(67) || b->udph.dest != htons(68))
goto drop;
- if (ntohs(h->tot_len) < ntohs(b->udph.len) + sizeof(struct iphdr))
+ if (ntohs(h->tot_len) < udp_get_len_short(&b->udph) + sizeof(struct iphdr))
goto drop;
- len = ntohs(b->udph.len) - sizeof(struct udphdr);
+ len = udp_get_len_short(&b->udph) - sizeof(struct udphdr);
ext_len = len - (sizeof(*b) -
sizeof(struct iphdr) -
sizeof(struct udphdr) -
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
index e540b86bd15b..4492bc548a66 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c
@@ -127,7 +127,7 @@ static int snmp_translate(struct nf_conn *ct, int dir, struct sk_buff *skb)
{
struct iphdr *iph = ip_hdr(skb);
struct udphdr *udph = (struct udphdr *)((__be32 *)iph + iph->ihl);
- u16 datalen = ntohs(udph->len) - sizeof(struct udphdr);
+ u16 datalen = udp_get_len_short(udph) - sizeof(struct udphdr);
char *data = (unsigned char *)udph + sizeof(struct udphdr);
struct snmp_ctx ctx;
int ret;
@@ -181,7 +181,7 @@ static int help(struct sk_buff *skb, unsigned int protoff,
* enough room for a UDP header. Just verify the UDP length field so we
* can mess around with the payload.
*/
- if (ntohs(udph->len) != skb->len - (iph->ihl << 2)) {
+ if (udp_get_len_short(udph) != skb->len - (iph->ihl << 2)) {
nf_ct_helper_log(skb, ct, "dropping malformed packet\n");
return NF_DROP;
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 4fed07cae7da..ca4c3faa0b64 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -3192,7 +3192,7 @@ static struct sk_buff *inet_rtm_getroute_build_skb(__be32 src, __be32 dst,
udph = skb_put_zero(skb, sizeof(struct udphdr));
udph->source = sport;
udph->dest = dport;
- udph->len = htons(sizeof(struct udphdr));
+ udp_set_len_short(udph, sizeof(struct udphdr));
udph->check = 0;
break;
}
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index d3ddcbfc8477..af9603217444 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1108,7 +1108,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
uh = udp_hdr(skb);
uh->source = inet_sk(sk)->inet_sport;
uh->dest = fl4->fl4_dport;
- uh->len = htons(len);
+ /* Datagram length checked in udp_sendmsg. */
+ udp_set_len_short(uh, len);
uh->check = 0;
if (cork->gso_size) {
@@ -2593,8 +2594,8 @@ int udp_rcv(struct sk_buff *skb)
struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(skb->dev);
struct sock *sk = NULL;
- unsigned short ulen;
__be32 saddr, daddr;
+ unsigned int ulen;
struct udphdr *uh;
bool refcounted;
int drop_reason;
@@ -2608,7 +2609,7 @@ int udp_rcv(struct sk_buff *skb)
goto drop; /* No space for header. */
uh = udp_hdr(skb);
- ulen = ntohs(uh->len);
+ ulen = udp_get_len(skb, uh, 0);
saddr = ip_hdr(skb)->saddr;
daddr = ip_hdr(skb)->daddr;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 29651b1a0bc7..8f77c8788f6d 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -279,11 +279,11 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
* segment instead of the entire frame.
*/
if (gso_partial && skb_is_gso(skb)) {
- uh->len = htons(skb_shinfo(skb)->gso_size +
- SKB_GSO_CB(skb)->data_offset +
- skb->head - (unsigned char *)uh);
+ udp_set_len_short(uh, skb_shinfo(skb)->gso_size +
+ SKB_GSO_CB(skb)->data_offset +
+ skb->head - (unsigned char *)uh);
} else {
- uh->len = htons(len);
+ udp_set_len_short(uh, len);
}
if (!need_csum)
@@ -468,7 +468,7 @@ static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
if (IS_ERR(skb))
return skb;
- udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
+ udp_set_len_short(udp_hdr(skb), sizeof(struct udphdr) + mss);
if (is_ipv6)
return __udpv6_gso_segment_list_csum(skb);
@@ -486,8 +486,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
unsigned int mss;
bool copy_dtor;
__sum16 check;
- __be16 newlen;
int ret = 0;
+ u16 newlen;
mss = skb_shinfo(gso_skb)->gso_size;
if (gso_skb->len <= sizeof(*uh) + mss)
@@ -564,8 +564,8 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
(skb_shinfo(gso_skb)->tx_flags & SKBTX_ANY_TSTAMP);
/* compute checksum adjustment based on old length versus new */
- newlen = htons(sizeof(*uh) + mss);
- check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
+ newlen = sizeof(*uh) + mss;
+ check = csum16_add(csum16_sub(uh->check, uh->len), htons(newlen));
for (;;) {
if (copy_dtor) {
@@ -577,7 +577,7 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
if (!seg->next)
break;
- uh->len = newlen;
+ udp_set_len_short(uh, newlen);
uh->check = check;
if (seg->ip_summed == CHECKSUM_PARTIAL)
@@ -594,11 +594,10 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
* segment may not be full MSS, account for that in the checksum
*/
if (!skb_is_gso(seg))
- newlen = htons(skb_tail_pointer(seg) -
- skb_transport_header(seg) + seg->data_len);
- check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
+ newlen = skb_tail_pointer(seg) - skb_transport_header(seg) + seg->data_len;
+ check = csum16_add(csum16_sub(uh->check, uh->len), htons(newlen));
- uh->len = newlen;
+ udp_set_len_short(uh, newlen);
uh->check = check;
if (seg->ip_summed == CHECKSUM_PARTIAL)
@@ -708,12 +707,8 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
return NULL;
}
- /* Do not deal with padded or malicious packets, sorry ! */
- ulen = ntohs(uh->len);
- if (ulen <= sizeof(*uh) || ulen != skb_gro_len(skb)) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ ulen = udp_get_len_short(uh);
+
/* pull encapsulating udp header */
skb_gro_pull(skb, sizeof(struct udphdr));
@@ -742,7 +737,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
* On len mismatch merge the first packet shorter than gso_size,
* otherwise complete the GRO packet.
*/
- if (ulen > ntohs(uh2->len) || flush) {
+ if (ulen > udp_get_len_short(uh2) || flush) {
pp = p;
} else {
if (NAPI_GRO_CB(skb)->is_flist) {
@@ -765,7 +760,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
}
}
- if (ret || ulen != ntohs(uh2->len) ||
+ if (ret || ulen != udp_get_len_short(uh2) ||
NAPI_GRO_CB(p)->count >= UDP_GRO_CNT_MAX)
pp = p;
@@ -783,8 +778,14 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
struct sk_buff *p;
struct udphdr *uh2;
unsigned int off = skb_gro_offset(skb);
+ unsigned int ulen;
int flush = 1;
+ /* Do not deal with padded or malicious packets, sorry! */
+ ulen = udp_get_len_short(uh);
+ if (ulen <= sizeof(*uh) || ulen != skb_gro_len(skb))
+ goto out;
+
/* We can do L4 aggregation only if the packet can't land in a tunnel
* otherwise we could corrupt the inner stream. Detecting such packets
* cannot be foolproof and the aggregation might still happen in some
@@ -915,12 +916,12 @@ static int udp_gro_complete_segment(struct sk_buff *skb)
int udp_gro_complete(struct sk_buff *skb, int nhoff,
udp_lookup_t lookup)
{
- __be16 newlen = htons(skb->len - nhoff);
struct udphdr *uh = (struct udphdr *)(skb->data + nhoff);
+ unsigned int newlen = skb->len - nhoff;
struct sock *sk;
int err;
- uh->len = newlen;
+ udp_set_len(uh, newlen);
sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
udp4_lib_lookup_skb, skb, uh->source, uh->dest);
@@ -957,7 +958,7 @@ INDIRECT_CALLABLE_SCOPE int udp4_gro_complete(struct sk_buff *skb, int nhoff)
/* do fraglist only if there is no outer UDP encap (or we already processed it) */
if (NAPI_GRO_CB(skb)->is_flist && !NAPI_GRO_CB(skb)->encap_mark) {
- uh->len = htons(skb->len - nhoff);
+ udp_set_len(uh, skb->len - nhoff);
skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
index 9ab3728f9630..a128fe85620d 100644
--- a/net/ipv4/udp_tunnel_core.c
+++ b/net/ipv4/udp_tunnel_core.c
@@ -178,7 +178,7 @@ void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb
uh->dest = dst_port;
uh->source = src_port;
- uh->len = htons(skb->len);
+ udp_set_len(uh, skb->len);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 296b57926abb..72ec0d7d1120 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -230,7 +230,8 @@ static void esp_output_encap_csum(struct sk_buff *skb)
if (*skb_mac_header(skb) == IPPROTO_UDP) {
struct udphdr *uh = udp_hdr(skb);
struct ipv6hdr *ip6h = ipv6_hdr(skb);
- int len = ntohs(uh->len);
+ /* esp6_output_udp_encap limits len to U16_MAX. */
+ int len = udp_get_len_short(uh);
unsigned int offset = skb_transport_offset(skb);
__wsum csum = skb_checksum(skb, offset, skb->len - offset, 0);
@@ -358,7 +359,7 @@ static struct ip_esp_hdr *esp6_output_udp_encap(struct sk_buff *skb,
uh = (struct udphdr *)esp->esph;
uh->source = sport;
uh->dest = dport;
- uh->len = htons(len);
+ udp_set_len_short(uh, len);
uh->check = 0;
*skb_mac_header(skb) = IPPROTO_UDP;
diff --git a/net/ipv6/fou6.c b/net/ipv6/fou6.c
index 157765259e2f..4b659ca60ba9 100644
--- a/net/ipv6/fou6.c
+++ b/net/ipv6/fou6.c
@@ -30,7 +30,7 @@ static void fou6_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
uh->dest = e->dport;
uh->source = sport;
- uh->len = htons(skb->len);
+ udp_set_len(uh, skb->len);
udp6_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM6), skb,
&fl6->saddr, &fl6->daddr, skb->len);
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index 9adb5775487f..32525a051a6f 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -93,7 +93,7 @@ void udp_tunnel6_xmit_skb(struct dst_entry *dst, struct sock *sk,
uh->dest = dst_port;
uh->source = src_port;
- uh->len = htons(skb->len);
+ udp_set_len(uh, skb->len);
skb_dst_set(skb, dst);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 0910cc171776..fd875908ac0c 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1083,12 +1083,12 @@ INDIRECT_CALLABLE_SCOPE int udpv6_rcv(struct sk_buff *skb)
daddr = &ipv6_hdr(skb)->daddr;
uh = udp_hdr(skb);
- ulen = ntohs(uh->len);
+ ulen = udp_get_len(skb, uh, 0);
if (ulen > skb->len)
goto short_packet;
/* Check for jumbo payload */
- if (ulen == 0)
+ if (ulen == 0 && inet6_is_jumbogram(skb))
ulen = skb->len;
if (ulen < sizeof(*uh))
@@ -1371,7 +1371,8 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
uh = udp_hdr(skb);
uh->source = fl6->fl6_sport;
uh->dest = fl6->fl6_dport;
- uh->len = htons(len);
+ /* Datagram length checked in udpv6_sendmsg. */
+ udp_set_len_short(uh, len);
uh->check = 0;
if (cork->gso_size) {
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 778afc7453ce..7370bcb80332 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -171,7 +171,7 @@ int udp6_gro_complete(struct sk_buff *skb, int nhoff)
/* do fraglist only if there is no outer UDP encap (or we already processed it) */
if (NAPI_GRO_CB(skb)->is_flist && !NAPI_GRO_CB(skb)->encap_mark) {
- uh->len = htons(skb->len - nhoff);
+ udp_set_len(uh, skb->len - nhoff);
skb_shinfo(skb)->gso_type |= (SKB_GSO_FRAGLIST|SKB_GSO_UDP_L4);
skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index f940914959b1..4712cc41881a 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1296,7 +1296,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, uns
ret = NET_XMIT_DROP;
goto out_unlock;
}
- uh->len = htons(udp_len);
+ udp_set_len_short(uh, udp_len);
/* Calculate UDP checksum if configured to do so */
#if IS_ENABLED(CONFIG_IPV6)
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 9fef4335da13..0ab161e7f8b2 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -1098,7 +1098,7 @@ ipvs_gue_encap(struct net *net, struct sk_buff *skb,
dport = cp->dest->tun_port;
udph->dest = dport;
udph->source = sport;
- udph->len = htons(skb->len);
+ udp_set_len(udph, skb->len);
udph->check = 0;
*next_protocol = IPPROTO_UDP;
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index cc9b7e5e1935..a7edfefd6fd1 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -41,11 +41,22 @@ static void udp_error_log(const struct sk_buff *skb,
nf_l4proto_log_invalid(skb, state, IPPROTO_UDP, "%s", msg);
}
+static bool udp_validate_len(struct sk_buff *skb,
+ const struct udphdr *hdr,
+ unsigned int dataoff)
+{
+ unsigned int udplen = udp_get_len(skb, hdr, dataoff);
+ unsigned int skblen = skb->len - dataoff;
+
+ if (udplen > skblen || udplen < sizeof(*hdr))
+ return false;
+ return true;
+}
+
static bool udp_error(struct sk_buff *skb,
unsigned int dataoff,
const struct nf_hook_state *state)
{
- unsigned int udplen = skb->len - dataoff;
const struct udphdr *hdr;
struct udphdr _hdr;
@@ -57,7 +68,7 @@ static bool udp_error(struct sk_buff *skb,
}
/* Truncated/malformed packets */
- if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
+ if (!udp_validate_len(skb, hdr, dataoff)) {
udp_error_log(skb, state, "truncated/malformed packet");
return true;
}
diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c
index e37b09b3203b..71dff0eb672c 100644
--- a/net/netfilter/nf_log_syslog.c
+++ b/net/netfilter/nf_log_syslog.c
@@ -301,7 +301,7 @@ nf_log_dump_udp_header(struct nf_log_buf *m,
/* Max length: 20 "SPT=65535 DPT=65535 " */
nf_log_buf_add(m, "SPT=%u DPT=%u LEN=%u ",
- ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len));
+ ntohs(uh->source), ntohs(uh->dest), udp_get_len_short(uh));
out:
return 0;
diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c
index bf591e6af005..3853f41db499 100644
--- a/net/netfilter/nf_nat_helper.c
+++ b/net/netfilter/nf_nat_helper.c
@@ -161,7 +161,7 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb,
/* update the length of the UDP packet */
datalen = skb->len - protoff;
- udph->len = htons(datalen);
+ udp_set_len_short(udph, datalen);
/* fix udp checksum if udp checksum was previously calculated */
if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL)
diff --git a/net/psp/psp_main.c b/net/psp/psp_main.c
index 8b2f178e317c..c9c1a8826b7f 100644
--- a/net/psp/psp_main.c
+++ b/net/psp/psp_main.c
@@ -231,7 +231,7 @@ static void psp_write_headers(struct net *net, struct sk_buff *skb, __be32 spi,
uh->source = udp_flow_src_port(net, skb, 0, 0, false);
}
uh->check = 0;
- uh->len = htons(udp_len);
+ udp_set_len(uh, udp_len);
psph->nexthdr = IPPROTO_TCP;
psph->hdrlen = PSP_HDRLEN_NOOPT;
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 078d3a27130b..5fff52a8ca90 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -276,7 +276,7 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl,
return 0;
iph = ip_hdr(skb);
- ul = ntohs(udph->len);
+ ul = udp_get_len_short(udph);
if (udplite || udph->check) {
@@ -334,7 +334,7 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl,
return 0;
ip6h = ipv6_hdr(skb);
- ul = ntohs(udph->len);
+ ul = udp_get_len_short(udph);
udph->check = 0;
diff --git a/net/xfrm/xfrm_nat_keepalive.c b/net/xfrm/xfrm_nat_keepalive.c
index eb1b6f67739e..0e21ab72f0fe 100644
--- a/net/xfrm/xfrm_nat_keepalive.c
+++ b/net/xfrm/xfrm_nat_keepalive.c
@@ -135,7 +135,7 @@ static void nat_keepalive_send(struct nat_keepalive *ka)
uh = skb_push(skb, sizeof(*uh));
uh->source = ka->encap_sport;
uh->dest = ka->encap_dport;
- uh->len = htons(skb->len);
+ udp_set_len_short(uh, skb->len);
uh->check = 0;
skb->mark = ka->smark;
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 7e9ae937cffa..b43ddf192ecc 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -13,6 +13,7 @@ TEST_PROGS := \
arp_ndisc_untracked_subnets.sh \
bareudp.sh \
big_tcp.sh \
+ big_tcp_tunnels.sh \
bind_bhash.sh \
bpf_offload.py \
bridge_stp_mode.sh \
diff --git a/tools/testing/selftests/net/big_tcp_tunnels.sh b/tools/testing/selftests/net/big_tcp_tunnels.sh
new file mode 100755
index 000000000000..d6513ed8d4e8
--- /dev/null
+++ b/tools/testing/selftests/net/big_tcp_tunnels.sh
@@ -0,0 +1,188 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Testing for IPv4 and IPv6 BIG TCP over VXLAN and GENEVE tunnels.
+
+SERVER_NS=$(mktemp -u server-XXXXXXXX)
+SERVER_IP4="192.168.1.1"
+SERVER_IP6="2001:db8::1:1"
+SERVER_IP4_TUN="192.168.2.1"
+SERVER_IP6_TUN="2001:db8::2:1"
+
+CLIENT_NS=$(mktemp -u client-XXXXXXXX)
+CLIENT_IP4="192.168.1.2"
+CLIENT_IP6="2001:db8::1:2"
+CLIENT_IP4_TUN="192.168.2.2"
+CLIENT_IP6_TUN="2001:db8::2:2"
+
+: "${PACKETS_THRESHOLD:=1000}"
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+setup() {
+ ip netns add "$SERVER_NS"
+ ip netns add "$CLIENT_NS"
+ ip -netns "$SERVER_NS" link add link1 type veth peer name link0 netns "$CLIENT_NS"
+
+ ip -netns "$CLIENT_NS" link set link0 up
+ ip -netns "$CLIENT_NS" addr replace "$CLIENT_IP4/24" dev link0
+ ip -netns "$CLIENT_NS" addr replace "$CLIENT_IP6/112" dev link0 nodad
+ ip -netns "$CLIENT_NS" link set link0 \
+ gso_max_size 196608 gso_ipv4_max_size 196608 \
+ gro_max_size 196608 gro_ipv4_max_size 196608
+ ip -netns "$SERVER_NS" link set link1 up
+ ip -netns "$SERVER_NS" addr replace "$SERVER_IP4/24" dev link1
+ ip -netns "$SERVER_NS" addr replace "$SERVER_IP6/112" dev link1 nodad
+ ip -netns "$SERVER_NS" link set link1 \
+ gso_max_size 196608 gso_ipv4_max_size 196608 \
+ gro_max_size 196608 gro_ipv4_max_size 196608
+
+ ip netns exec "$SERVER_NS" netserver >/dev/null
+}
+
+setup_tunnel() {
+ if [ "$2" = 4 ]; then
+ SERVER_IP="$SERVER_IP4"
+ CLIENT_IP="$CLIENT_IP4"
+ echo "Setting up ${1^^} over IPv4, veth tx csum offload $3"
+ else
+ SERVER_IP="$SERVER_IP6"
+ CLIENT_IP="$CLIENT_IP6"
+ echo "Setting up ${1^^} over IPv6, veth tx csum offload $3"
+ fi
+
+ if [ "$1" = vxlan ]; then
+ ip -netns "$CLIENT_NS" link add tun0 type vxlan \
+ id 5001 remote "$SERVER_IP" local "$CLIENT_IP" dev link0 dstport 4789
+ else
+ ip -netns "$CLIENT_NS" link add tun0 type geneve \
+ id 5001 remote "$SERVER_IP"
+ fi
+ ip -netns "$CLIENT_NS" link set tun0 up
+ ip -netns "$CLIENT_NS" addr replace "$CLIENT_IP4_TUN/24" dev tun0
+ ip -netns "$CLIENT_NS" addr replace "$CLIENT_IP6_TUN/112" dev tun0 nodad
+ ip -netns "$CLIENT_NS" link set tun0 \
+ gso_max_size 196608 gso_ipv4_max_size 196608 \
+ gro_max_size 196608 gro_ipv4_max_size 196608
+ if [ "$1" = vxlan ]; then
+ ip -netns "$SERVER_NS" link add tun1 type vxlan \
+ id 5001 remote "$CLIENT_IP" local "$SERVER_IP" dev link1 dstport 4789
+ else
+ ip -netns "$SERVER_NS" link add tun1 type geneve \
+ id 5001 remote "$CLIENT_IP"
+ fi
+ ip -netns "$SERVER_NS" link set tun1 up
+ ip -netns "$SERVER_NS" addr replace "$SERVER_IP4_TUN/24" dev tun1
+ ip -netns "$SERVER_NS" addr replace "$SERVER_IP6_TUN/112" dev tun1 nodad
+ ip -netns "$SERVER_NS" link set tun1 \
+ gso_max_size 196608 gso_ipv4_max_size 196608 \
+ gro_max_size 196608 gro_ipv4_max_size 196608
+
+ ip netns exec "$CLIENT_NS" ethtool -K link0 tx-checksumming "$3" > /dev/null
+ ip netns exec "$SERVER_NS" ethtool -K link1 tx-checksumming "$3" > /dev/null
+}
+
+cleanup_tunnel() {
+ ip -netns "$CLIENT_NS" link del tun0
+ ip -netns "$SERVER_NS" link del tun1
+}
+
+cleanup() {
+ ip netns pids "$SERVER_NS" | xargs -r kill
+ ip netns pids "$CLIENT_NS" | xargs -r kill
+ ip netns del "$SERVER_NS"
+ ip netns del "$CLIENT_NS"
+ rm -rf "$WORKDIR"
+}
+
+do_test() {
+ # When tx csum offload is off, software GSO is performed before passing the
+ # packet to veth. Check BIG TCP packets inside the VXLAN tunnel to verify
+ # the software checksum path: if the checksum code is broken, these packets
+ # will be dropped.
+ if [ "$3" = on ]; then
+ CAPTURE_IFACE='link'
+ if [ "$1" = 4 ]; then
+ IPTABLES=iptables
+ else
+ IPTABLES=ip6tables
+ fi
+ else
+ CAPTURE_IFACE='tun'
+ if [ "$2" = 4 ]; then
+ IPTABLES=iptables
+ else
+ IPTABLES=ip6tables
+ fi
+ fi
+ if [ "$2" = 4 ]; then
+ IPTABLES_SACK=iptables
+ else
+ IPTABLES_SACK=ip6tables
+ fi
+
+ ip netns exec "$SERVER_NS" "$IPTABLES" -w -t raw -I PREROUTING -i "${CAPTURE_IFACE}1" -m length ! --length 0:65535 -m comment --comment "bigtcp"
+ ip netns exec "$CLIENT_NS" "$IPTABLES" -w -t raw -I OUTPUT -o "${CAPTURE_IFACE}0" -m length ! --length 0:65535 -m comment --comment "bigtcp"
+ ip netns exec "$SERVER_NS" "$IPTABLES_SACK" -w -t raw -I OUTPUT -o "tun1" -p tcp -m tcp --tcp-flags ACK ACK --tcp-option 5 -m comment --comment "sack"
+
+ if [ "$2" = 4 ]; then
+ SERVER_IP="$SERVER_IP4_TUN"
+ echo "Running IPv4 traffic in the tunnel"
+ else
+ SERVER_IP="$SERVER_IP6_TUN"
+ echo "Running IPv6 traffic in the tunnel"
+ fi
+
+ ip netns exec "$CLIENT_NS" netperf -t TCP_STREAM -l 5 -H "$SERVER_IP" -- \
+ -m 80000 > /dev/null
+
+ PACKETS_SERVER=$(ip netns exec "$SERVER_NS" "$IPTABLES-save" -c -t raw | sed -rn '/ --comment bigtcp/{s/^\[([0-9]+):.*/\1/p;q}')
+ PACKETS_CLIENT=$(ip netns exec "$CLIENT_NS" "$IPTABLES-save" -c -t raw | sed -rn '/ --comment bigtcp/{s/^\[([0-9]+):.*/\1/p;q}')
+ PACKETS_SACK=$(ip netns exec "$SERVER_NS" "$IPTABLES_SACK-save" -c -t raw | sed -rn '/ --comment sack/{s/^\[([0-9]+):.*/\1/p;q}')
+ ip netns exec "$SERVER_NS" "$IPTABLES" -w -t raw -D PREROUTING -i "${CAPTURE_IFACE}1" -m length ! --length 0:65535 -m comment --comment "bigtcp"
+ ip netns exec "$CLIENT_NS" "$IPTABLES" -w -t raw -D OUTPUT -o "${CAPTURE_IFACE}0" -m length ! --length 0:65535 -m comment --comment "bigtcp"
+ ip netns exec "$SERVER_NS" "$IPTABLES_SACK" -w -t raw -D OUTPUT -o "tun1" -p tcp -m tcp --tcp-flags ACK ACK --tcp-option 5 -m comment --comment "sack"
+
+ echo "Captured BIG TCP RX packets: $PACKETS_SERVER"
+ echo "Captured BIG TCP TX packets: $PACKETS_CLIENT"
+ echo "Captured TCP SACK packets: $PACKETS_SACK"
+ [ "$PACKETS_SERVER" -gt "$PACKETS_THRESHOLD" ] || return 1
+ [ "$PACKETS_CLIENT" -gt "$PACKETS_THRESHOLD" ] || return 1
+ [ "$PACKETS_SACK" -lt "$(( PACKETS_CLIENT / 2 ))" ] || return 1
+}
+
+if ! netperf -V &> /dev/null; then
+ echo "SKIP: Could not run test without netperf tool"
+ exit "$ksft_skip"
+fi
+
+if ! iptables --version &> /dev/null; then
+ echo "SKIP: Could not run test without iptables tool"
+ exit "$ksft_skip"
+fi
+
+if ! ethtool --version &> /dev/null; then
+ echo "SKIP: Could not run test without ethtool tool"
+ exit "$ksft_skip"
+fi
+
+if ! ip link help 2>&1 | grep gso_ipv4_max_size &> /dev/null; then
+ echo "SKIP: Could not run test without gso/gro_ipv4_max_size supported in ip-link"
+ exit "$ksft_skip"
+fi
+
+WORKDIR=$(mktemp -d)
+trap cleanup EXIT
+setup
+for tunnel in vxlan geneve; do
+ for tun_family in 4 6; do
+ for traffic_family in 4 6; do
+ for csum_offload in on off; do
+ setup_tunnel "$tunnel" "$tun_family" "$csum_offload" || exit "$?"
+ do_test "$tun_family" "$traffic_family" "$csum_offload" || exit "$?"
+ cleanup_tunnel
+ done
+ done
+ done
+done