summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Axtens <dja@axtens.net>2018-01-30 22:15:33 -0500
committerDavid S. Miller <davem@davemloft.net>2018-02-01 09:36:03 -0500
commit2b16f048729bf35e6c28a40cbfad07239f9dcd90 (patch)
treefc9e536298bba6872e9f9b8f25359c7afa42c4ea
parent255442c93843f52b6891b21d0b485bf2c97f93c3 (diff)
net: create skb_gso_validate_mac_len()
If you take a GSO skb, and split it into packets, will the MAC length (L2 + L3 + L4 headers + payload) of those packets be small enough to fit within a given length? Move skb_gso_mac_seglen() to skbuff.h with other related functions like skb_gso_network_seglen() so we can use it, and then create skb_gso_validate_mac_len to do the full calculation. Signed-off-by: Daniel Axtens <dja@axtens.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/skbuff.h16
-rw-r--r--net/core/skbuff.c63
-rw-r--r--net/sched/sch_tbf.c10
3 files changed, 66 insertions, 23 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index ac89a93b7c83..5ebc0f869720 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3287,6 +3287,7 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
3287void skb_scrub_packet(struct sk_buff *skb, bool xnet); 3287void skb_scrub_packet(struct sk_buff *skb, bool xnet);
3288unsigned int skb_gso_transport_seglen(const struct sk_buff *skb); 3288unsigned int skb_gso_transport_seglen(const struct sk_buff *skb);
3289bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu); 3289bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu);
3290bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len);
3290struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); 3291struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
3291struct sk_buff *skb_vlan_untag(struct sk_buff *skb); 3292struct sk_buff *skb_vlan_untag(struct sk_buff *skb);
3292int skb_ensure_writable(struct sk_buff *skb, int write_len); 3293int skb_ensure_writable(struct sk_buff *skb, int write_len);
@@ -4120,6 +4121,21 @@ static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
4120 return hdr_len + skb_gso_transport_seglen(skb); 4121 return hdr_len + skb_gso_transport_seglen(skb);
4121} 4122}
4122 4123
4124/**
4125 * skb_gso_mac_seglen - Return length of individual segments of a gso packet
4126 *
4127 * @skb: GSO skb
4128 *
4129 * skb_gso_mac_seglen is used to determine the real size of the
4130 * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4
4131 * headers (TCP/UDP).
4132 */
4133static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
4134{
4135 unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
4136 return hdr_len + skb_gso_transport_seglen(skb);
4137}
4138
4123/* Local Checksum Offload. 4139/* Local Checksum Offload.
4124 * Compute outer checksum based on the assumption that the 4140 * Compute outer checksum based on the assumption that the
4125 * inner checksum will be offloaded later. 4141 * inner checksum will be offloaded later.
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 01e8285aea73..8c61c27c1b28 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4914,37 +4914,74 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
4914EXPORT_SYMBOL_GPL(skb_gso_transport_seglen); 4914EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
4915 4915
4916/** 4916/**
4917 * skb_gso_validate_mtu - Return in case such skb fits a given MTU 4917 * skb_gso_size_check - check the skb size, considering GSO_BY_FRAGS
4918 * 4918 *
4919 * @skb: GSO skb 4919 * There are a couple of instances where we have a GSO skb, and we
4920 * @mtu: MTU to validate against 4920 * want to determine what size it would be after it is segmented.
4921 * 4921 *
4922 * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU 4922 * We might want to check:
4923 * once split. 4923 * - L3+L4+payload size (e.g. IP forwarding)
4924 * - L2+L3+L4+payload size (e.g. sanity check before passing to driver)
4925 *
4926 * This is a helper to do that correctly considering GSO_BY_FRAGS.
4927 *
4928 * @seg_len: The segmented length (from skb_gso_*_seglen). In the
4929 * GSO_BY_FRAGS case this will be [header sizes + GSO_BY_FRAGS].
4930 *
4931 * @max_len: The maximum permissible length.
4932 *
4933 * Returns true if the segmented length <= max length.
4924 */ 4934 */
4925bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu) 4935static inline bool skb_gso_size_check(const struct sk_buff *skb,
4926{ 4936 unsigned int seg_len,
4937 unsigned int max_len) {
4927 const struct skb_shared_info *shinfo = skb_shinfo(skb); 4938 const struct skb_shared_info *shinfo = skb_shinfo(skb);
4928 const struct sk_buff *iter; 4939 const struct sk_buff *iter;
4929 unsigned int hlen;
4930
4931 hlen = skb_gso_network_seglen(skb);
4932 4940
4933 if (shinfo->gso_size != GSO_BY_FRAGS) 4941 if (shinfo->gso_size != GSO_BY_FRAGS)
4934 return hlen <= mtu; 4942 return seg_len <= max_len;
4935 4943
4936 /* Undo this so we can re-use header sizes */ 4944 /* Undo this so we can re-use header sizes */
4937 hlen -= GSO_BY_FRAGS; 4945 seg_len -= GSO_BY_FRAGS;
4938 4946
4939 skb_walk_frags(skb, iter) { 4947 skb_walk_frags(skb, iter) {
4940 if (hlen + skb_headlen(iter) > mtu) 4948 if (seg_len + skb_headlen(iter) > max_len)
4941 return false; 4949 return false;
4942 } 4950 }
4943 4951
4944 return true; 4952 return true;
4945} 4953}
4954
4955/**
4956 * skb_gso_validate_mtu - Return in case such skb fits a given MTU
4957 *
4958 * @skb: GSO skb
4959 * @mtu: MTU to validate against
4960 *
4961 * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
4962 * once split.
4963 */
4964bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
4965{
4966 return skb_gso_size_check(skb, skb_gso_network_seglen(skb), mtu);
4967}
4946EXPORT_SYMBOL_GPL(skb_gso_validate_mtu); 4968EXPORT_SYMBOL_GPL(skb_gso_validate_mtu);
4947 4969
4970/**
4971 * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
4972 *
4973 * @skb: GSO skb
4974 * @len: length to validate against
4975 *
4976 * skb_gso_validate_mac_len validates if a given skb will fit a wanted
4977 * length once split, including L2, L3 and L4 headers and the payload.
4978 */
4979bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len)
4980{
4981 return skb_gso_size_check(skb, skb_gso_mac_seglen(skb), len);
4982}
4983EXPORT_SYMBOL_GPL(skb_gso_validate_mac_len);
4984
4948static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb) 4985static struct sk_buff *skb_reorder_vlan_header(struct sk_buff *skb)
4949{ 4986{
4950 if (skb_cow(skb, skb_headroom(skb)) < 0) { 4987 if (skb_cow(skb, skb_headroom(skb)) < 0) {
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 83e76d046993..229172d509cc 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -142,16 +142,6 @@ static u64 psched_ns_t2l(const struct psched_ratecfg *r,
142 return len; 142 return len;
143} 143}
144 144
145/*
146 * Return length of individual segments of a gso packet,
147 * including all headers (MAC, IP, TCP/UDP)
148 */
149static unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
150{
151 unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
152 return hdr_len + skb_gso_transport_seglen(skb);
153}
154
155/* GSO packet is too big, segment it so that tbf can transmit 145/* GSO packet is too big, segment it so that tbf can transmit
156 * each segment in time 146 * each segment in time
157 */ 147 */