|
@@ -4914,37 +4914,74 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
|
|
EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
|
|
EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
|
|
|
|
|
|
/**
|
|
/**
|
|
- * skb_gso_validate_mtu - Return in case such skb fits a given MTU
|
|
|
|
|
|
+ * skb_gso_size_check - check the skb size, considering GSO_BY_FRAGS
|
|
*
|
|
*
|
|
- * @skb: GSO skb
|
|
|
|
- * @mtu: MTU to validate against
|
|
|
|
|
|
+ * There are a couple of instances where we have a GSO skb, and we
|
|
|
|
+ * want to determine what size it would be after it is segmented.
|
|
*
|
|
*
|
|
- * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
|
|
|
|
- * once split.
|
|
|
|
|
|
+ * We might want to check:
|
|
|
|
+ * - L3+L4+payload size (e.g. IP forwarding)
|
|
|
|
+ * - L2+L3+L4+payload size (e.g. sanity check before passing to driver)
|
|
|
|
+ *
|
|
|
|
+ * This is a helper to do that correctly considering GSO_BY_FRAGS.
|
|
|
|
+ *
|
|
|
|
+ * @seg_len: The segmented length (from skb_gso_*_seglen). In the
|
|
|
|
+ * GSO_BY_FRAGS case this will be [header sizes + GSO_BY_FRAGS].
|
|
|
|
+ *
|
|
|
|
+ * @max_len: The maximum permissible length.
|
|
|
|
+ *
|
|
|
|
+ * Returns true if the segmented length <= max length.
|
|
*/
|
|
*/
|
|
-bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
|
|
|
|
-{
|
|
|
|
|
|
+static inline bool skb_gso_size_check(const struct sk_buff *skb,
|
|
|
|
+ unsigned int seg_len,
|
|
|
|
+ unsigned int max_len) {
|
|
const struct skb_shared_info *shinfo = skb_shinfo(skb);
|
|
const struct skb_shared_info *shinfo = skb_shinfo(skb);
|
|
const struct sk_buff *iter;
|
|
const struct sk_buff *iter;
|
|
- unsigned int hlen;
|
|
|
|
-
|
|
|
|
- hlen = skb_gso_network_seglen(skb);
|
|
|
|
|
|
|
|
if (shinfo->gso_size != GSO_BY_FRAGS)
|
|
if (shinfo->gso_size != GSO_BY_FRAGS)
|
|
- return hlen <= mtu;
|
|
|
|
|
|
+ return seg_len <= max_len;
|
|
|
|
|
|
/* Undo this so we can re-use header sizes */
|
|
/* Undo this so we can re-use header sizes */
|
|
- hlen -= GSO_BY_FRAGS;
|
|
|
|
|
|
+ seg_len -= GSO_BY_FRAGS;
|
|
|
|
|
|
skb_walk_frags(skb, iter) {
|
|
skb_walk_frags(skb, iter) {
|
|
- if (hlen + skb_headlen(iter) > mtu)
|
|
|
|
|
|
+ if (seg_len + skb_headlen(iter) > max_len)
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * skb_gso_validate_mtu - Return in case such skb fits a given MTU
|
|
|
|
+ *
|
|
|
|
+ * @skb: GSO skb
|
|
|
|
+ * @mtu: MTU to validate against
|
|
|
|
+ *
|
|
|
|
+ * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
|
|
|
|
+ * once split.
|
|
|
|
+ */
|
|
|
|
+bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
|
|
|
|
+{
|
|
|
|
+ return skb_gso_size_check(skb, skb_gso_network_seglen(skb), mtu);
|
|
|
|
+}
|
|
EXPORT_SYMBOL_GPL(skb_gso_validate_mtu);
|
|
EXPORT_SYMBOL_GPL(skb_gso_validate_mtu);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
|
|
|
|
+ *
|
|
|
|
+ * @skb: GSO skb
|
|
|
|
+ * @len: length to validate against
|
|
|
|
+ *
|
|
|
|
+ * skb_gso_validate_mac_len validates if a given skb will fit a wanted
|
|
|
|
+ * length once split, including L2, L3 and L4 headers and the payload.
|
|
|
|
+ */
|
|
|
|
+bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len)
|
|
|
|
+{
|
|
|
|
+ return skb_gso_size_check(skb, skb_gso_mac_seglen(skb), len);
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);
|
|
|
|
+
|
|
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
|
|
static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
|
|
{
|
|
{
|
|
if (skb_cow(skb, skb_headroom(skb)) < 0) {
|
|
if (skb_cow(skb, skb_headroom(skb)) < 0) {
|