diff options
Diffstat (limited to 'net/core/skbuff.c')
-rw-r--r-- | net/core/skbuff.c | 87 |
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 | } |
4031 | EXPORT_SYMBOL(skb_checksum_setup); | 4031 | EXPORT_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 | */ | ||
4046 | static 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 | */ | ||
4090 | struct 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 | } | ||
4118 | EXPORT_SYMBOL(skb_checksum_trimmed); | ||
4119 | |||
4033 | void __skb_warn_lro_forwarding(const struct sk_buff *skb) | 4120 | void __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", |