aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/skbuff.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/core/skbuff.c')
-rw-r--r--net/core/skbuff.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 3cfff2a3d651..1e4278a4dd7e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4030,6 +4030,93 @@ int skb_checksum_setup(struct sk_buff *skb, bool recalculate)
4030} 4030}
4031EXPORT_SYMBOL(skb_checksum_setup); 4031EXPORT_SYMBOL(skb_checksum_setup);
4032 4032
4033/**
4034 * skb_checksum_maybe_trim - maybe trims the given skb
4035 * @skb: the skb to check
4036 * @transport_len: the data length beyond the network header
4037 *
4038 * Checks whether the given skb has data beyond the given transport length.
4039 * If so, returns a cloned skb trimmed to this transport length.
4040 * Otherwise returns the provided skb. Returns NULL in error cases
4041 * (e.g. transport_len exceeds skb length or out-of-memory).
4042 *
4043 * Caller needs to set the skb transport header and release the returned skb.
4044 * Provided skb is consumed.
4045 */
4046static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb,
4047 unsigned int transport_len)
4048{
4049 struct sk_buff *skb_chk;
4050 unsigned int len = skb_transport_offset(skb) + transport_len;
4051 int ret;
4052
4053 if (skb->len < len) {
4054 kfree_skb(skb);
4055 return NULL;
4056 } else if (skb->len == len) {
4057 return skb;
4058 }
4059
4060 skb_chk = skb_clone(skb, GFP_ATOMIC);
4061 kfree_skb(skb);
4062
4063 if (!skb_chk)
4064 return NULL;
4065
4066 ret = pskb_trim_rcsum(skb_chk, len);
4067 if (ret) {
4068 kfree_skb(skb_chk);
4069 return NULL;
4070 }
4071
4072 return skb_chk;
4073}
4074
4075/**
4076 * skb_checksum_trimmed - validate checksum of an skb
4077 * @skb: the skb to check
4078 * @transport_len: the data length beyond the network header
4079 * @skb_chkf: checksum function to use
4080 *
4081 * Applies the given checksum function skb_chkf to the provided skb.
4082 * Returns a checked and maybe trimmed skb. Returns NULL on error.
4083 *
4084 * If the skb has data beyond the given transport length, then a
4085 * trimmed & cloned skb is checked and returned.
4086 *
4087 * Caller needs to set the skb transport header and release the returned skb.
4088 * Provided skb is consumed.
4089 */
4090struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
4091 unsigned int transport_len,
4092 __sum16(*skb_chkf)(struct sk_buff *skb))
4093{
4094 struct sk_buff *skb_chk;
4095 unsigned int offset = skb_transport_offset(skb);
4096 int ret;
4097
4098 skb_chk = skb_checksum_maybe_trim(skb, transport_len);
4099 if (!skb_chk)
4100 return NULL;
4101
4102 if (!pskb_may_pull(skb_chk, offset)) {
4103 kfree_skb(skb_chk);
4104 return NULL;
4105 }
4106
4107 __skb_pull(skb_chk, offset);
4108 ret = skb_chkf(skb_chk);
4109 __skb_push(skb_chk, offset);
4110
4111 if (ret) {
4112 kfree_skb(skb_chk);
4113 return NULL;
4114 }
4115
4116 return skb_chk;
4117}
4118EXPORT_SYMBOL(skb_checksum_trimmed);
4119
4033void __skb_warn_lro_forwarding(const struct sk_buff *skb) 4120void __skb_warn_lro_forwarding(const struct sk_buff *skb)
4034{ 4121{
4035 net_warn_ratelimited("%s: received packets cannot be forwarded while LRO is enabled\n", 4122 net_warn_ratelimited("%s: received packets cannot be forwarded while LRO is enabled\n",