aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/skbuff.h2
-rw-r--r--net/core/skbuff.c95
-rw-r--r--net/openvswitch/actions.c86
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);
2679struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); 2679struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
2680struct sk_buff *skb_vlan_untag(struct sk_buff *skb); 2680struct sk_buff *skb_vlan_untag(struct sk_buff *skb);
2681int skb_ensure_writable(struct sk_buff *skb, int write_len); 2681int skb_ensure_writable(struct sk_buff *skb, int write_len);
2682int skb_vlan_pop(struct sk_buff *skb);
2683int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci);
2682 2684
2683struct skb_checksum_ops { 2685struct 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}
4164EXPORT_SYMBOL(skb_ensure_writable); 4164EXPORT_SYMBOL(skb_ensure_writable);
4165 4165
4166/* remove VLAN header from packet and update csum accordingly. */
4167static 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);
4193pull:
4194 __skb_pull(skb, offset);
4195
4196 return err;
4197}
4198
4199int 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}
4231EXPORT_SYMBOL(skb_vlan_pop);
4232
4233int 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}
4259EXPORT_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. */
210static 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
238static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) 209static 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
270static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, 221static 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
298static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, 232static 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: