diff options
-rw-r--r-- | include/linux/skbuff.h | 2 | ||||
-rw-r--r-- | net/core/skbuff.c | 95 | ||||
-rw-r--r-- | net/openvswitch/actions.c | 86 |
3 files changed, 106 insertions, 77 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e045516891a9..78c299f40bac 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -2679,6 +2679,8 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb); | |||
2679 | struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); | 2679 | struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); |
2680 | struct sk_buff *skb_vlan_untag(struct sk_buff *skb); | 2680 | struct sk_buff *skb_vlan_untag(struct sk_buff *skb); |
2681 | int skb_ensure_writable(struct sk_buff *skb, int write_len); | 2681 | int skb_ensure_writable(struct sk_buff *skb, int write_len); |
2682 | int skb_vlan_pop(struct sk_buff *skb); | ||
2683 | int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci); | ||
2682 | 2684 | ||
2683 | struct skb_checksum_ops { | 2685 | struct skb_checksum_ops { |
2684 | __wsum (*update)(const void *mem, int len, __wsum wsum); | 2686 | __wsum (*update)(const void *mem, int len, __wsum wsum); |
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d11bbe0da355..c906c5f4bf69 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -4163,6 +4163,101 @@ int skb_ensure_writable(struct sk_buff *skb, int write_len) | |||
4163 | } | 4163 | } |
4164 | EXPORT_SYMBOL(skb_ensure_writable); | 4164 | EXPORT_SYMBOL(skb_ensure_writable); |
4165 | 4165 | ||
4166 | /* remove VLAN header from packet and update csum accordingly. */ | ||
4167 | static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci) | ||
4168 | { | ||
4169 | struct vlan_hdr *vhdr; | ||
4170 | unsigned int offset = skb->data - skb_mac_header(skb); | ||
4171 | int err; | ||
4172 | |||
4173 | __skb_push(skb, offset); | ||
4174 | err = skb_ensure_writable(skb, VLAN_ETH_HLEN); | ||
4175 | if (unlikely(err)) | ||
4176 | goto pull; | ||
4177 | |||
4178 | skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN); | ||
4179 | |||
4180 | vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); | ||
4181 | *vlan_tci = ntohs(vhdr->h_vlan_TCI); | ||
4182 | |||
4183 | memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); | ||
4184 | __skb_pull(skb, VLAN_HLEN); | ||
4185 | |||
4186 | vlan_set_encap_proto(skb, vhdr); | ||
4187 | skb->mac_header += VLAN_HLEN; | ||
4188 | |||
4189 | if (skb_network_offset(skb) < ETH_HLEN) | ||
4190 | skb_set_network_header(skb, ETH_HLEN); | ||
4191 | |||
4192 | skb_reset_mac_len(skb); | ||
4193 | pull: | ||
4194 | __skb_pull(skb, offset); | ||
4195 | |||
4196 | return err; | ||
4197 | } | ||
4198 | |||
4199 | int skb_vlan_pop(struct sk_buff *skb) | ||
4200 | { | ||
4201 | u16 vlan_tci; | ||
4202 | __be16 vlan_proto; | ||
4203 | int err; | ||
4204 | |||
4205 | if (likely(vlan_tx_tag_present(skb))) { | ||
4206 | skb->vlan_tci = 0; | ||
4207 | } else { | ||
4208 | if (unlikely((skb->protocol != htons(ETH_P_8021Q) && | ||
4209 | skb->protocol != htons(ETH_P_8021AD)) || | ||
4210 | skb->len < VLAN_ETH_HLEN)) | ||
4211 | return 0; | ||
4212 | |||
4213 | err = __skb_vlan_pop(skb, &vlan_tci); | ||
4214 | if (err) | ||
4215 | return err; | ||
4216 | } | ||
4217 | /* move next vlan tag to hw accel tag */ | ||
4218 | if (likely((skb->protocol != htons(ETH_P_8021Q) && | ||
4219 | skb->protocol != htons(ETH_P_8021AD)) || | ||
4220 | skb->len < VLAN_ETH_HLEN)) | ||
4221 | return 0; | ||
4222 | |||
4223 | vlan_proto = skb->protocol; | ||
4224 | err = __skb_vlan_pop(skb, &vlan_tci); | ||
4225 | if (unlikely(err)) | ||
4226 | return err; | ||
4227 | |||
4228 | __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); | ||
4229 | return 0; | ||
4230 | } | ||
4231 | EXPORT_SYMBOL(skb_vlan_pop); | ||
4232 | |||
4233 | int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci) | ||
4234 | { | ||
4235 | if (vlan_tx_tag_present(skb)) { | ||
4236 | unsigned int offset = skb->data - skb_mac_header(skb); | ||
4237 | int err; | ||
4238 | |||
4239 | /* __vlan_insert_tag expect skb->data pointing to mac header. | ||
4240 | * So change skb->data before calling it and change back to | ||
4241 | * original position later | ||
4242 | */ | ||
4243 | __skb_push(skb, offset); | ||
4244 | err = __vlan_insert_tag(skb, skb->vlan_proto, | ||
4245 | vlan_tx_tag_get(skb)); | ||
4246 | if (err) | ||
4247 | return err; | ||
4248 | skb->protocol = skb->vlan_proto; | ||
4249 | skb->mac_len += VLAN_HLEN; | ||
4250 | __skb_pull(skb, offset); | ||
4251 | |||
4252 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
4253 | skb->csum = csum_add(skb->csum, csum_partial(skb->data | ||
4254 | + (2 * ETH_ALEN), VLAN_HLEN, 0)); | ||
4255 | } | ||
4256 | __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci); | ||
4257 | return 0; | ||
4258 | } | ||
4259 | EXPORT_SYMBOL(skb_vlan_push); | ||
4260 | |||
4166 | /** | 4261 | /** |
4167 | * alloc_skb_with_frags - allocate skb with page frags | 4262 | * alloc_skb_with_frags - allocate skb with page frags |
4168 | * | 4263 | * |
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 7ffa37716265..4e05ea1c2d11 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
@@ -206,93 +206,27 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key, | |||
206 | return 0; | 206 | return 0; |
207 | } | 207 | } |
208 | 208 | ||
209 | /* remove VLAN header from packet and update csum accordingly. */ | ||
210 | static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) | ||
211 | { | ||
212 | struct vlan_hdr *vhdr; | ||
213 | int err; | ||
214 | |||
215 | err = skb_ensure_writable(skb, VLAN_ETH_HLEN); | ||
216 | if (unlikely(err)) | ||
217 | return err; | ||
218 | |||
219 | skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN); | ||
220 | |||
221 | vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN); | ||
222 | *current_tci = vhdr->h_vlan_TCI; | ||
223 | |||
224 | memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN); | ||
225 | __skb_pull(skb, VLAN_HLEN); | ||
226 | |||
227 | vlan_set_encap_proto(skb, vhdr); | ||
228 | skb->mac_header += VLAN_HLEN; | ||
229 | |||
230 | if (skb_network_offset(skb) < ETH_HLEN) | ||
231 | skb_set_network_header(skb, ETH_HLEN); | ||
232 | |||
233 | /* Update mac_len for subsequent MPLS actions */ | ||
234 | skb_reset_mac_len(skb); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) | 209 | static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) |
239 | { | 210 | { |
240 | __be16 tci; | ||
241 | int err; | 211 | int err; |
242 | 212 | ||
243 | if (likely(vlan_tx_tag_present(skb))) { | 213 | err = skb_vlan_pop(skb); |
244 | skb->vlan_tci = 0; | 214 | if (vlan_tx_tag_present(skb)) |
245 | } else { | 215 | invalidate_flow_key(key); |
246 | if (unlikely(skb->protocol != htons(ETH_P_8021Q) || | 216 | else |
247 | skb->len < VLAN_ETH_HLEN)) | ||
248 | return 0; | ||
249 | |||
250 | err = __pop_vlan_tci(skb, &tci); | ||
251 | if (err) | ||
252 | return err; | ||
253 | } | ||
254 | /* move next vlan tag to hw accel tag */ | ||
255 | if (likely(skb->protocol != htons(ETH_P_8021Q) || | ||
256 | skb->len < VLAN_ETH_HLEN)) { | ||
257 | key->eth.tci = 0; | 217 | key->eth.tci = 0; |
258 | return 0; | 218 | return err; |
259 | } | ||
260 | |||
261 | invalidate_flow_key(key); | ||
262 | err = __pop_vlan_tci(skb, &tci); | ||
263 | if (unlikely(err)) | ||
264 | return err; | ||
265 | |||
266 | __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci)); | ||
267 | return 0; | ||
268 | } | 219 | } |
269 | 220 | ||
270 | static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, | 221 | static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, |
271 | const struct ovs_action_push_vlan *vlan) | 222 | const struct ovs_action_push_vlan *vlan) |
272 | { | 223 | { |
273 | if (unlikely(vlan_tx_tag_present(skb))) { | 224 | if (vlan_tx_tag_present(skb)) |
274 | u16 current_tag; | ||
275 | |||
276 | /* push down current VLAN tag */ | ||
277 | current_tag = vlan_tx_tag_get(skb); | ||
278 | |||
279 | skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto, | ||
280 | current_tag); | ||
281 | if (!skb) | ||
282 | return -ENOMEM; | ||
283 | /* Update mac_len for subsequent MPLS actions */ | ||
284 | skb->mac_len += VLAN_HLEN; | ||
285 | |||
286 | if (skb->ip_summed == CHECKSUM_COMPLETE) | ||
287 | skb->csum = csum_add(skb->csum, csum_partial(skb->data | ||
288 | + (2 * ETH_ALEN), VLAN_HLEN, 0)); | ||
289 | |||
290 | invalidate_flow_key(key); | 225 | invalidate_flow_key(key); |
291 | } else { | 226 | else |
292 | key->eth.tci = vlan->vlan_tci; | 227 | key->eth.tci = vlan->vlan_tci; |
293 | } | 228 | return skb_vlan_push(skb, vlan->vlan_tpid, |
294 | __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); | 229 | ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); |
295 | return 0; | ||
296 | } | 230 | } |
297 | 231 | ||
298 | static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, | 232 | static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, |
@@ -858,8 +792,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
858 | 792 | ||
859 | case OVS_ACTION_ATTR_PUSH_VLAN: | 793 | case OVS_ACTION_ATTR_PUSH_VLAN: |
860 | err = push_vlan(skb, key, nla_data(a)); | 794 | err = push_vlan(skb, key, nla_data(a)); |
861 | if (unlikely(err)) /* skb already freed. */ | ||
862 | return err; | ||
863 | break; | 795 | break; |
864 | 796 | ||
865 | case OVS_ACTION_ATTR_POP_VLAN: | 797 | case OVS_ACTION_ATTR_POP_VLAN: |