|
@@ -187,6 +187,54 @@ out_unlock:
|
|
|
}
|
|
|
EXPORT_SYMBOL(skb_udp_tunnel_segment);
|
|
|
|
|
|
+struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
|
|
|
+ netdev_features_t features,
|
|
|
+ unsigned int mss, __sum16 check)
|
|
|
+{
|
|
|
+ struct sk_buff *segs, *seg;
|
|
|
+ unsigned int hdrlen;
|
|
|
+ struct udphdr *uh;
|
|
|
+
|
|
|
+ if (gso_skb->len <= sizeof(*uh) + mss)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ hdrlen = gso_skb->data - skb_mac_header(gso_skb);
|
|
|
+ skb_pull(gso_skb, sizeof(*uh));
|
|
|
+
|
|
|
+ segs = skb_segment(gso_skb, features);
|
|
|
+ if (unlikely(IS_ERR_OR_NULL(segs)))
|
|
|
+ return segs;
|
|
|
+
|
|
|
+ for (seg = segs; seg; seg = seg->next) {
|
|
|
+ uh = udp_hdr(seg);
|
|
|
+ uh->len = htons(seg->len - hdrlen);
|
|
|
+ uh->check = check;
|
|
|
+
|
|
|
+ /* last packet can be partial gso_size */
|
|
|
+ if (!seg->next)
|
|
|
+ csum_replace2(&uh->check, htons(mss),
|
|
|
+ htons(seg->len - hdrlen - sizeof(*uh)));
|
|
|
+ }
|
|
|
+
|
|
|
+ return segs;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__udp_gso_segment);
|
|
|
+
|
|
|
+static struct sk_buff *__udp4_gso_segment(struct sk_buff *gso_skb,
|
|
|
+ netdev_features_t features)
|
|
|
+{
|
|
|
+ const struct iphdr *iph = ip_hdr(gso_skb);
|
|
|
+ unsigned int mss = skb_shinfo(gso_skb)->gso_size;
|
|
|
+
|
|
|
+ if (!can_checksum_protocol(features, htons(ETH_P_IP)))
|
|
|
+ return ERR_PTR(-EIO);
|
|
|
+
|
|
|
+ return __udp_gso_segment(gso_skb, features, mss,
|
|
|
+ udp_v4_check(sizeof(struct udphdr) + mss,
|
|
|
+ iph->saddr, iph->daddr, 0));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(__udp4_gso_segment);
|
|
|
+
|
|
|
static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
|
|
|
netdev_features_t features)
|
|
|
{
|
|
@@ -203,12 +251,15 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
- if (!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP))
|
|
|
+ if (!(skb_shinfo(skb)->gso_type & (SKB_GSO_UDP | SKB_GSO_UDP_L4)))
|
|
|
goto out;
|
|
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
|
|
|
goto out;
|
|
|
|
|
|
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
|
|
|
+ return __udp4_gso_segment(skb, features);
|
|
|
+
|
|
|
mss = skb_shinfo(skb)->gso_size;
|
|
|
if (unlikely(skb->len <= mss))
|
|
|
goto out;
|