void udp_tunnel_update_gro_rcv(struct sock *sk, bool add)
{ struct udp_tunnel_type_entry *cur = NULL; struct udp_sock *up = udp_sk(sk); int i, old_gro_type_nr;
if (!UDP_MAX_TUNNEL_TYPES || !up->gro_receive) return;
mutex_lock(&udp_tunnel_gro_type_lock);
/* Check if the static call is permanently disabled. */ if (udp_tunnel_gro_type_nr > UDP_MAX_TUNNEL_TYPES) goto out;
for (i = 0; i < udp_tunnel_gro_type_nr; i++) if (udp_tunnel_gro_types[i].gro_receive == up->gro_receive)
cur = &udp_tunnel_gro_types[i];
old_gro_type_nr = udp_tunnel_gro_type_nr; if (add) { /* * Update the matching entry, if found, or add a new one * if needed
*/ if (cur) {
refcount_inc(&cur->count); goto out;
}
if (unlikely(udp_tunnel_gro_type_nr == UDP_MAX_TUNNEL_TYPES)) {
pr_err_once("Too many UDP tunnel types, please increase UDP_MAX_TUNNEL_TYPES\n"); /* Ensure static call will never be enabled */
udp_tunnel_gro_type_nr = UDP_MAX_TUNNEL_TYPES + 1;
} else {
cur = &udp_tunnel_gro_types[udp_tunnel_gro_type_nr++];
refcount_set(&cur->count, 1);
cur->gro_receive = up->gro_receive;
}
} else { /* * The stack cleanups only successfully added tunnel, the * lookup on removal should never fail.
*/ if (WARN_ON_ONCE(!cur)) goto out;
if (!refcount_dec_and_test(&cur->count)) goto out;
/* Avoid gaps, so that the enable tunnel has always id 0 */
*cur = udp_tunnel_gro_types[--udp_tunnel_gro_type_nr];
}
if (unlikely(!pskb_may_pull(skb, tnl_hlen))) goto out;
/* Adjust partial header checksum to negate old length. * We cannot rely on the value contained in uh->len as it is * possible that the actual value exceeds the boundaries of the * 16 bit length field due to the header being added outside of an * IP or IPv6 frame that was already limited to 64K - 1.
*/ if (skb_shinfo(skb)->gso_type & SKB_GSO_PARTIAL)
partial = (__force __wsum)uh->len; else
partial = (__force __wsum)htonl(skb->len);
partial = csum_sub(csum_unfold(uh->check), partial);
features &= skb->dev->hw_enc_features; if (need_csum)
features &= ~NETIF_F_SCTP_CRC;
/* The only checksum offload we care about from here on out is the * outer one so strip the existing checksum feature flags and * instead set the flag based on our outer checksum offload value.
*/ if (remcsum) {
features &= ~NETIF_F_CSUM_MASK; if (!need_csum || offload_csum)
features |= NETIF_F_HW_CSUM;
}
/* Set up inner headers if we are offloading inner checksum */ if (skb->ip_summed == CHECKSUM_PARTIAL) {
skb_reset_inner_headers(skb);
skb->encapsulation = 1;
}
/* If we are only performing partial GSO the inner header * will be using a length value equal to only one MSS sized * 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 - (unsignedchar *)uh);
} else {
uh->len = htons(len);
}
if (unlikely(skb_checksum_start(gso_skb) !=
skb_transport_header(gso_skb) &&
!(skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST))) return ERR_PTR(-EINVAL);
/* We don't know if egress device can segment and checksum the packet * when IPv6 extension headers are present. Fall back to software GSO.
*/ if (gso_skb->ip_summed != CHECKSUM_PARTIAL)
features &= ~(NETIF_F_GSO_UDP_L4 | NETIF_F_CSUM_MASK);
if (skb_gso_ok(gso_skb, features | NETIF_F_GSO_ROBUST)) { /* Packet is from an untrusted source, reset gso_segs. */
skb_shinfo(gso_skb)->gso_segs = DIV_ROUND_UP(gso_skb->len - sizeof(*uh),
mss); return NULL;
}
if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) { /* Detect modified geometry and pass those to skb_segment. */ if (skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size) return __udp_gso_segment_list(gso_skb, features, is_ipv6);
ret = __skb_linearize(gso_skb); if (ret) return ERR_PTR(ret);
/* Setup csum, as fraglist skips this in udp4_gro_receive. */
gso_skb->csum_start = skb_transport_header(gso_skb) - gso_skb->head;
gso_skb->csum_offset = offsetof(struct udphdr, check);
gso_skb->ip_summed = CHECKSUM_PARTIAL;
/* clear destructor to avoid skb_segment assigning it to tail */
copy_dtor = gso_skb->destructor == sock_wfree; if (copy_dtor) {
gso_skb->destructor = NULL;
gso_skb->sk = NULL;
}
segs = skb_segment(gso_skb, features); if (IS_ERR_OR_NULL(segs)) { if (copy_dtor) {
gso_skb->destructor = sock_wfree;
gso_skb->sk = sk;
} return segs;
}
/* GSO partial and frag_list segmentation only requires splitting * the frame into an MSS multiple and possibly a remainder, both * cases return a GSO skb. So update the mss now.
*/ if (skb_is_gso(segs))
mss *= skb_shinfo(segs)->gso_segs;
seg = segs;
uh = udp_hdr(seg);
/* preserve TX timestamp flags and TS key for first segment */
skb_shinfo(seg)->tskey = skb_shinfo(gso_skb)->tskey;
skb_shinfo(seg)->tx_flags |=
(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);
for (;;) { if (copy_dtor) {
seg->destructor = sock_wfree;
seg->sk = sk;
sum_truesize += seg->truesize;
}
/* last packet can be partial gso_size, account for that in checksum */
newlen = htons(skb_tail_pointer(seg) - skb_transport_header(seg) +
seg->data_len);
check = csum16_add(csum16_sub(uh->check, uh->len), newlen);
/* On the TX path, CHECKSUM_NONE and CHECKSUM_UNNECESSARY have the same * meaning. However, check for bad offloads in the GSO stack expects the * latter, if the checksum was calculated in software. To vouch for the * segment skbs we actually need to set it on the gso_skb.
*/ if (gso_skb->ip_summed == CHECKSUM_NONE)
gso_skb->ip_summed = CHECKSUM_UNNECESSARY;
/* update refcount for the packet */ if (copy_dtor) { int delta = sum_truesize - gso_skb->truesize;
/* In some pathological cases, delta can be negative. * We need to either use refcount_add() or refcount_sub_and_test()
*/ if (likely(delta >= 0))
refcount_add(delta, &sk->sk_wmem_alloc); else
WARN_ON_ONCE(refcount_sub_and_test(-delta, &sk->sk_wmem_alloc));
} return segs;
}
EXPORT_SYMBOL_GPL(__udp_gso_segment);
/* If there is no outer header we can fake a checksum offload * due to the fact that we have already done the checksum in * software prior to segmenting the frame.
*/ if (!skb->encap_hdr_csum)
features |= NETIF_F_HW_CSUM;
/* Fragment the skb. IP headers of the fragments are updated in * inet_gso_segment()
*/
segs = skb_segment(skb, features);
out: return segs;
}
/* Terminate the flow on len mismatch or if it grow "too much". * Under small packet flood GRO count could elsewhere grow a lot * leading to excessive truesize values. * On len mismatch merge the first packet shorter than gso_size, * otherwise complete the GRO packet.
*/ if (ulen > ntohs(uh2->len) || flush) {
pp = p;
} else { if (NAPI_GRO_CB(skb)->is_flist) { if (!pskb_may_pull(skb, skb_gro_offset(skb))) {
NAPI_GRO_CB(skb)->flush = 1; return NULL;
} if ((skb->ip_summed != p->ip_summed) ||
(skb->csum_level != p->csum_level)) {
NAPI_GRO_CB(skb)->flush = 1; return NULL;
}
skb_set_network_header(skb, skb_gro_receive_network_offset(skb));
ret = skb_gro_receive_list(p, skb);
} else {
skb_gro_postpull_rcsum(skb, uh, sizeof(struct udphdr));
/* 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 * cases. Such packets should be caught in udp_unexpected_gso later.
*/
NAPI_GRO_CB(skb)->is_flist = 0; if (!sk || !udp_sk(sk)->gro_receive) { /* If the packet was locally encapsulated in a UDP tunnel that * wasn't detected above, do not GRO.
*/ if (skb->encapsulation) goto out;
if (skb->dev->features & NETIF_F_GRO_FRAGLIST)
NAPI_GRO_CB(skb)->is_flist = sk ? !udp_test_bit(GRO_ENABLED, sk) : 1;
/* mark that this skb passed once through the tunnel gro layer */
NAPI_GRO_CB(skb)->encap_mark = 1;
flush = 0;
list_for_each_entry(p, head, list) { if (!NAPI_GRO_CB(p)->same_flow) continue;
uh2 = (struct udphdr *)(p->data + off);
/* Match ports and either checksums are either both zero * or nonzero.
*/ if ((*(u32 *)&uh->source != *(u32 *)&uh2->source) ||
(!uh->check ^ !uh2->check)) {
NAPI_GRO_CB(p)->same_flow = 0; continue;
}
}
if (skb->encapsulation)
skb->inner_transport_header = skb->transport_header;
return 0;
}
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); struct sock *sk; int err;
uh->len = newlen;
sk = INDIRECT_CALL_INET(lookup, udp6_lib_lookup_skb,
udp4_lib_lookup_skb, skb, uh->source, uh->dest); if (sk && udp_sk(sk)->gro_complete) {
skb_shinfo(skb)->gso_type = uh->check ? SKB_GSO_UDP_TUNNEL_CSUM
: SKB_GSO_UDP_TUNNEL;
/* clear the encap mark, so that inner frag_list gro_complete * can take place
*/
NAPI_GRO_CB(skb)->encap_mark = 0;
/* Set encapsulation before calling into inner gro_complete() * functions to make them set up the inner offsets.
*/
skb->encapsulation = 1;
err = udp_sk(sk)->gro_complete(sk, skb,
nhoff + sizeof(struct udphdr));
} else {
err = udp_gro_complete_segment(skb);
}
if (skb->remcsum_offload)
skb_shinfo(skb)->gso_type |= SKB_GSO_TUNNEL_REMCSUM;
/* 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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.