]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Distributed under the terms of the GNU GPL version 2. | |
00ba5f3b | 3 | * Copyright (c) 2007, 2008, 2009, 2010 Nicira Networks. |
a14bc59f BP |
4 | * |
5 | * Significant portions of this file may be copied from parts of the Linux | |
6 | * kernel, by Linus Torvalds and others. | |
064af421 BP |
7 | */ |
8 | ||
9 | /* Functions for executing flow actions. */ | |
10 | ||
11 | #include <linux/skbuff.h> | |
12 | #include <linux/in.h> | |
13 | #include <linux/ip.h> | |
14 | #include <linux/tcp.h> | |
15 | #include <linux/udp.h> | |
16 | #include <linux/in6.h> | |
17 | #include <linux/if_vlan.h> | |
f1193301 | 18 | #include <net/inet_ecn.h> |
064af421 BP |
19 | #include <net/ip.h> |
20 | #include <net/checksum.h> | |
f2459fe7 | 21 | |
064af421 | 22 | #include "actions.h" |
f2459fe7 | 23 | #include "datapath.h" |
064af421 | 24 | #include "openvswitch/datapath-protocol.h" |
f2459fe7 | 25 | #include "vport.h" |
064af421 | 26 | |
0cd8a05e JG |
27 | static struct sk_buff * |
28 | make_writable(struct sk_buff *skb, unsigned min_headroom, gfp_t gfp) | |
064af421 BP |
29 | { |
30 | if (skb_shared(skb) || skb_cloned(skb)) { | |
0cd8a05e JG |
31 | struct sk_buff *nskb; |
32 | unsigned headroom = max(min_headroom, skb_headroom(skb)); | |
33 | ||
34 | nskb = skb_copy_expand(skb, headroom, skb_tailroom(skb), gfp); | |
064af421 | 35 | if (nskb) { |
ff6402a9 | 36 | set_skb_csum_bits(skb, nskb); |
064af421 BP |
37 | kfree_skb(skb); |
38 | return nskb; | |
39 | } | |
40 | } else { | |
41 | unsigned int hdr_len = (skb_transport_offset(skb) | |
42 | + sizeof(struct tcphdr)); | |
43 | if (pskb_may_pull(skb, min(hdr_len, skb->len))) | |
44 | return skb; | |
45 | } | |
46 | kfree_skb(skb); | |
47 | return NULL; | |
48 | } | |
49 | ||
659586ef JG |
50 | static void set_tunnel(struct sk_buff *skb, struct odp_flow_key *key, |
51 | __be32 tun_id) | |
52 | { | |
53 | OVS_CB(skb)->tun_id = key->tun_id = tun_id; | |
54 | } | |
064af421 BP |
55 | |
56 | static struct sk_buff * | |
57 | vlan_pull_tag(struct sk_buff *skb) | |
58 | { | |
59 | struct vlan_ethhdr *vh = vlan_eth_hdr(skb); | |
60 | struct ethhdr *eh; | |
61 | ||
064af421 BP |
62 | /* Verify we were given a vlan packet */ |
63 | if (vh->h_vlan_proto != htons(ETH_P_8021Q)) | |
64 | return skb; | |
65 | ||
635c9298 JG |
66 | if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) |
67 | skb->csum = csum_sub(skb->csum, csum_partial(skb->data | |
68 | + ETH_HLEN, VLAN_HLEN, 0)); | |
69 | ||
064af421 BP |
70 | memmove(skb->data + VLAN_HLEN, skb->data, 2 * VLAN_ETH_ALEN); |
71 | ||
72 | eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN); | |
73 | ||
74 | skb->protocol = eh->h_proto; | |
75 | skb->mac_header += VLAN_HLEN; | |
76 | ||
77 | return skb; | |
78 | } | |
79 | ||
80 | ||
81 | static struct sk_buff * | |
82 | modify_vlan_tci(struct datapath *dp, struct sk_buff *skb, | |
83 | struct odp_flow_key *key, const union odp_action *a, | |
84 | int n_actions, gfp_t gfp) | |
85 | { | |
86 | u16 tci, mask; | |
87 | ||
88 | if (a->type == ODPAT_SET_VLAN_VID) { | |
89 | tci = ntohs(a->vlan_vid.vlan_vid); | |
90 | mask = VLAN_VID_MASK; | |
11cdf5e6 | 91 | key->dl_vlan = a->vlan_vid.vlan_vid; |
064af421 | 92 | } else { |
d42c4f8d | 93 | tci = a->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT; |
064af421 | 94 | mask = VLAN_PCP_MASK; |
11cdf5e6 | 95 | key->dl_vlan_pcp = a->vlan_pcp.vlan_pcp; |
064af421 BP |
96 | } |
97 | ||
0cd8a05e | 98 | skb = make_writable(skb, VLAN_HLEN, gfp); |
064af421 BP |
99 | if (!skb) |
100 | return ERR_PTR(-ENOMEM); | |
101 | ||
102 | if (skb->protocol == htons(ETH_P_8021Q)) { | |
103 | /* Modify vlan id, but maintain other TCI values */ | |
104 | struct vlan_ethhdr *vh = vlan_eth_hdr(skb); | |
635c9298 JG |
105 | __be16 old_tci = vh->h_vlan_TCI; |
106 | ||
064af421 | 107 | vh->h_vlan_TCI = htons((ntohs(vh->h_vlan_TCI) & ~mask) | tci); |
635c9298 JG |
108 | |
109 | if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) { | |
110 | __be16 diff[] = { ~old_tci, vh->h_vlan_TCI }; | |
111 | ||
112 | skb->csum = ~csum_partial((char *)diff, sizeof(diff), | |
113 | ~skb->csum); | |
114 | } | |
064af421 | 115 | } else { |
b28c72ba JG |
116 | int err; |
117 | ||
064af421 BP |
118 | /* Add vlan header */ |
119 | ||
120 | /* Set up checksumming pointers for checksum-deferred packets | |
121 | * on Xen. Otherwise, dev_queue_xmit() will try to do this | |
122 | * when we send the packet out on the wire, and it will fail at | |
123 | * that point because skb_checksum_setup() will not look inside | |
124 | * an 802.1Q header. */ | |
b28c72ba JG |
125 | err = vswitch_skb_checksum_setup(skb); |
126 | if (unlikely(err)) { | |
127 | kfree_skb(skb); | |
128 | return ERR_PTR(err); | |
129 | } | |
064af421 BP |
130 | |
131 | /* GSO is not implemented for packets with an 802.1Q header, so | |
132 | * we have to do segmentation before we add that header. | |
133 | * | |
134 | * GSO does work with hardware-accelerated VLAN tagging, but we | |
135 | * can't use hardware-accelerated VLAN tagging since it | |
136 | * requires the device to have a VLAN group configured (with | |
137 | * e.g. vconfig(8)) and we don't do that. | |
138 | * | |
139 | * Having to do this here may be a performance loss, since we | |
140 | * can't take advantage of TSO hardware support, although it | |
141 | * does not make a measurable network performance difference | |
142 | * for 1G Ethernet. Fixing that would require patching the | |
143 | * kernel (either to add GSO support to the VLAN protocol or to | |
144 | * support hardware-accelerated VLAN tagging without VLAN | |
145 | * groups configured). */ | |
146 | if (skb_is_gso(skb)) { | |
147 | struct sk_buff *segs; | |
148 | ||
149 | segs = skb_gso_segment(skb, 0); | |
150 | kfree_skb(skb); | |
151 | if (unlikely(IS_ERR(segs))) | |
152 | return ERR_CAST(segs); | |
153 | ||
154 | do { | |
155 | struct sk_buff *nskb = segs->next; | |
156 | int err; | |
157 | ||
158 | segs->next = NULL; | |
159 | ||
635c9298 JG |
160 | /* GSO can change the checksum type so update.*/ |
161 | compute_ip_summed(segs, true); | |
162 | ||
064af421 BP |
163 | segs = __vlan_put_tag(segs, tci); |
164 | err = -ENOMEM; | |
165 | if (segs) { | |
166 | struct odp_flow_key segkey = *key; | |
167 | err = execute_actions(dp, segs, | |
168 | &segkey, a + 1, | |
169 | n_actions - 1, | |
170 | gfp); | |
171 | } | |
172 | ||
173 | if (unlikely(err)) { | |
174 | while ((segs = nskb)) { | |
175 | nskb = segs->next; | |
176 | segs->next = NULL; | |
177 | kfree_skb(segs); | |
178 | } | |
179 | return ERR_PTR(err); | |
180 | } | |
181 | ||
182 | segs = nskb; | |
183 | } while (segs->next); | |
184 | ||
185 | skb = segs; | |
635c9298 | 186 | compute_ip_summed(skb, true); |
064af421 BP |
187 | } |
188 | ||
189 | /* The hardware-accelerated version of vlan_put_tag() works | |
190 | * only for a device that has a VLAN group configured (with | |
191 | * e.g. vconfig(8)), so call the software-only version | |
192 | * __vlan_put_tag() directly instead. | |
193 | */ | |
194 | skb = __vlan_put_tag(skb, tci); | |
195 | if (!skb) | |
196 | return ERR_PTR(-ENOMEM); | |
635c9298 JG |
197 | |
198 | /* GSO doesn't fix up the hardware computed checksum so this | |
199 | * will only be hit in the non-GSO case. */ | |
200 | if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) | |
201 | skb->csum = csum_add(skb->csum, csum_partial(skb->data | |
202 | + ETH_HLEN, VLAN_HLEN, 0)); | |
064af421 BP |
203 | } |
204 | ||
205 | return skb; | |
206 | } | |
207 | ||
208 | static struct sk_buff *strip_vlan(struct sk_buff *skb, | |
209 | struct odp_flow_key *key, gfp_t gfp) | |
210 | { | |
0cd8a05e | 211 | skb = make_writable(skb, 0, gfp); |
064af421 BP |
212 | if (skb) { |
213 | vlan_pull_tag(skb); | |
214 | key->dl_vlan = htons(ODP_VLAN_NONE); | |
215 | } | |
216 | return skb; | |
217 | } | |
218 | ||
219 | static struct sk_buff *set_dl_addr(struct sk_buff *skb, | |
11cdf5e6 | 220 | struct odp_flow_key *key, |
064af421 BP |
221 | const struct odp_action_dl_addr *a, |
222 | gfp_t gfp) | |
223 | { | |
0cd8a05e | 224 | skb = make_writable(skb, 0, gfp); |
064af421 BP |
225 | if (skb) { |
226 | struct ethhdr *eh = eth_hdr(skb); | |
11cdf5e6 JG |
227 | if (a->type == ODPAT_SET_DL_SRC) { |
228 | memcpy(eh->h_source, a->dl_addr, ETH_ALEN); | |
229 | memcpy(key->dl_src, a->dl_addr, ETH_ALEN); | |
230 | } else { | |
231 | memcpy(eh->h_dest, a->dl_addr, ETH_ALEN); | |
232 | memcpy(key->dl_dst, a->dl_addr, ETH_ALEN); | |
233 | } | |
064af421 BP |
234 | } |
235 | return skb; | |
236 | } | |
237 | ||
238 | /* Updates 'sum', which is a field in 'skb''s data, given that a 4-byte field | |
239 | * covered by the sum has been changed from 'from' to 'to'. If set, | |
240 | * 'pseudohdr' indicates that the field is in the TCP or UDP pseudo-header. | |
241 | * Based on nf_proto_csum_replace4. */ | |
242 | static void update_csum(__sum16 *sum, struct sk_buff *skb, | |
243 | __be32 from, __be32 to, int pseudohdr) | |
244 | { | |
245 | __be32 diff[] = { ~from, to }; | |
1cdb82b9 | 246 | |
635c9298 | 247 | if (OVS_CB(skb)->ip_summed != OVS_CSUM_PARTIAL) { |
064af421 BP |
248 | *sum = csum_fold(csum_partial((char *)diff, sizeof(diff), |
249 | ~csum_unfold(*sum))); | |
635c9298 | 250 | if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE && pseudohdr) |
064af421 BP |
251 | skb->csum = ~csum_partial((char *)diff, sizeof(diff), |
252 | ~skb->csum); | |
253 | } else if (pseudohdr) | |
254 | *sum = ~csum_fold(csum_partial((char *)diff, sizeof(diff), | |
255 | csum_unfold(*sum))); | |
256 | } | |
257 | ||
258 | static struct sk_buff *set_nw_addr(struct sk_buff *skb, | |
259 | struct odp_flow_key *key, | |
260 | const struct odp_action_nw_addr *a, | |
261 | gfp_t gfp) | |
262 | { | |
263 | if (key->dl_type != htons(ETH_P_IP)) | |
264 | return skb; | |
265 | ||
0cd8a05e | 266 | skb = make_writable(skb, 0, gfp); |
064af421 BP |
267 | if (skb) { |
268 | struct iphdr *nh = ip_hdr(skb); | |
269 | u32 *f = a->type == ODPAT_SET_NW_SRC ? &nh->saddr : &nh->daddr; | |
270 | u32 old = *f; | |
271 | u32 new = a->nw_addr; | |
272 | ||
273 | if (key->nw_proto == IPPROTO_TCP) { | |
274 | struct tcphdr *th = tcp_hdr(skb); | |
275 | update_csum(&th->check, skb, old, new, 1); | |
276 | } else if (key->nw_proto == IPPROTO_UDP) { | |
277 | struct udphdr *th = udp_hdr(skb); | |
278 | update_csum(&th->check, skb, old, new, 1); | |
279 | } | |
280 | update_csum(&nh->check, skb, old, new, 0); | |
281 | *f = new; | |
11cdf5e6 JG |
282 | |
283 | if (a->type == ODPAT_SET_NW_SRC) | |
284 | key->nw_src = a->nw_addr; | |
285 | else | |
286 | key->nw_dst = a->nw_addr; | |
064af421 BP |
287 | } |
288 | return skb; | |
289 | } | |
290 | ||
959a2ecd JP |
291 | static struct sk_buff *set_nw_tos(struct sk_buff *skb, |
292 | struct odp_flow_key *key, | |
293 | const struct odp_action_nw_tos *a, | |
294 | gfp_t gfp) | |
295 | { | |
296 | if (key->dl_type != htons(ETH_P_IP)) | |
297 | return skb; | |
298 | ||
299 | skb = make_writable(skb, 0, gfp); | |
300 | if (skb) { | |
301 | struct iphdr *nh = ip_hdr(skb); | |
302 | u8 *f = &nh->tos; | |
303 | u8 old = *f; | |
f1193301 | 304 | u8 new; |
959a2ecd | 305 | |
f1193301 | 306 | /* Set the DSCP bits and preserve the ECN bits. */ |
3c5f6de3 | 307 | new = a->nw_tos | (nh->tos & INET_ECN_MASK); |
959a2ecd JP |
308 | update_csum(&nh->check, skb, htons((uint16_t)old), |
309 | htons((uint16_t)new), 0); | |
310 | *f = new; | |
11cdf5e6 | 311 | key->nw_tos = a->nw_tos; |
959a2ecd JP |
312 | } |
313 | return skb; | |
314 | } | |
315 | ||
064af421 BP |
316 | static struct sk_buff * |
317 | set_tp_port(struct sk_buff *skb, struct odp_flow_key *key, | |
318 | const struct odp_action_tp_port *a, | |
319 | gfp_t gfp) | |
320 | { | |
321 | int check_ofs; | |
322 | ||
323 | if (key->dl_type != htons(ETH_P_IP)) | |
324 | return skb; | |
325 | ||
326 | if (key->nw_proto == IPPROTO_TCP) | |
327 | check_ofs = offsetof(struct tcphdr, check); | |
328 | else if (key->nw_proto == IPPROTO_UDP) | |
329 | check_ofs = offsetof(struct udphdr, check); | |
330 | else | |
331 | return skb; | |
332 | ||
0cd8a05e | 333 | skb = make_writable(skb, 0, gfp); |
064af421 BP |
334 | if (skb) { |
335 | struct udphdr *th = udp_hdr(skb); | |
336 | u16 *f = a->type == ODPAT_SET_TP_SRC ? &th->source : &th->dest; | |
337 | u16 old = *f; | |
338 | u16 new = a->tp_port; | |
985224ac | 339 | update_csum((u16*)(skb_transport_header(skb) + check_ofs), |
00ba5f3b | 340 | skb, old, new, 0); |
064af421 | 341 | *f = new; |
11cdf5e6 JG |
342 | if (a->type == ODPAT_SET_TP_SRC) |
343 | key->tp_src = a->tp_port; | |
344 | else | |
345 | key->tp_dst = a->tp_port; | |
064af421 BP |
346 | } |
347 | return skb; | |
348 | } | |
349 | ||
350 | static inline unsigned packet_length(const struct sk_buff *skb) | |
351 | { | |
352 | unsigned length = skb->len - ETH_HLEN; | |
353 | if (skb->protocol == htons(ETH_P_8021Q)) | |
354 | length -= VLAN_HLEN; | |
355 | return length; | |
356 | } | |
357 | ||
064af421 BP |
358 | static void |
359 | do_output(struct datapath *dp, struct sk_buff *skb, int out_port) | |
360 | { | |
f2459fe7 JG |
361 | struct dp_port *p; |
362 | int mtu; | |
064af421 BP |
363 | |
364 | if (!skb) | |
365 | goto error; | |
366 | ||
f2459fe7 | 367 | p = rcu_dereference(dp->ports[out_port]); |
064af421 BP |
368 | if (!p) |
369 | goto error; | |
370 | ||
f2459fe7 JG |
371 | mtu = vport_get_mtu(p->vport); |
372 | if (packet_length(skb) > mtu && !skb_is_gso(skb)) { | |
373 | printk(KERN_WARNING "%s: dropped over-mtu packet: %d > %d\n", | |
374 | dp_name(dp), packet_length(skb), mtu); | |
375 | goto error; | |
376 | } | |
377 | ||
378 | vport_send(p->vport, skb); | |
064af421 BP |
379 | return; |
380 | ||
381 | error: | |
382 | kfree_skb(skb); | |
383 | } | |
384 | ||
385 | /* Never consumes 'skb'. Returns a port that 'skb' should be sent to, -1 if | |
386 | * none. */ | |
387 | static int output_group(struct datapath *dp, __u16 group, | |
388 | struct sk_buff *skb, gfp_t gfp) | |
389 | { | |
390 | struct dp_port_group *g = rcu_dereference(dp->groups[group]); | |
391 | int prev_port = -1; | |
392 | int i; | |
393 | ||
394 | if (!g) | |
395 | return -1; | |
396 | for (i = 0; i < g->n_ports; i++) { | |
f2459fe7 JG |
397 | struct dp_port *p = rcu_dereference(dp->ports[g->ports[i]]); |
398 | if (!p || OVS_CB(skb)->dp_port == p) | |
064af421 BP |
399 | continue; |
400 | if (prev_port != -1) { | |
401 | struct sk_buff *clone = skb_clone(skb, gfp); | |
402 | if (!clone) | |
403 | return -1; | |
404 | do_output(dp, clone, prev_port); | |
405 | } | |
406 | prev_port = p->port_no; | |
407 | } | |
408 | return prev_port; | |
409 | } | |
410 | ||
411 | static int | |
412 | output_control(struct datapath *dp, struct sk_buff *skb, u32 arg, gfp_t gfp) | |
413 | { | |
414 | skb = skb_clone(skb, gfp); | |
415 | if (!skb) | |
416 | return -ENOMEM; | |
417 | return dp_output_control(dp, skb, _ODPL_ACTION_NR, arg); | |
418 | } | |
419 | ||
72b06300 BP |
420 | /* Send a copy of this packet up to the sFlow agent, along with extra |
421 | * information about what happened to it. */ | |
422 | static void sflow_sample(struct datapath *dp, struct sk_buff *skb, | |
56fd8edf | 423 | const union odp_action *a, int n_actions, |
f2459fe7 | 424 | gfp_t gfp, struct dp_port *dp_port) |
72b06300 BP |
425 | { |
426 | struct odp_sflow_sample_header *hdr; | |
427 | unsigned int actlen = n_actions * sizeof(union odp_action); | |
428 | unsigned int hdrlen = sizeof(struct odp_sflow_sample_header); | |
429 | struct sk_buff *nskb; | |
72b06300 BP |
430 | |
431 | nskb = skb_copy_expand(skb, actlen + hdrlen, 0, gfp); | |
432 | if (!nskb) | |
433 | return; | |
434 | ||
435 | memcpy(__skb_push(nskb, actlen), a, actlen); | |
436 | hdr = (struct odp_sflow_sample_header*)__skb_push(nskb, hdrlen); | |
437 | hdr->n_actions = n_actions; | |
f2459fe7 | 438 | hdr->sample_pool = atomic_read(&dp_port->sflow_pool); |
72b06300 BP |
439 | dp_output_control(dp, nskb, _ODPL_SFLOW_NR, 0); |
440 | } | |
441 | ||
064af421 BP |
442 | /* Execute a list of actions against 'skb'. */ |
443 | int execute_actions(struct datapath *dp, struct sk_buff *skb, | |
444 | struct odp_flow_key *key, | |
445 | const union odp_action *a, int n_actions, | |
446 | gfp_t gfp) | |
447 | { | |
448 | /* Every output action needs a separate clone of 'skb', but the common | |
449 | * case is just a single output action, so that doing a clone and | |
450 | * then freeing the original skbuff is wasteful. So the following code | |
451 | * is slightly obscure just to avoid that. */ | |
452 | int prev_port = -1; | |
c1c9c9c4 | 453 | u32 priority = skb->priority; |
a5225dd6 | 454 | int err; |
72b06300 BP |
455 | |
456 | if (dp->sflow_probability) { | |
f2459fe7 | 457 | struct dp_port *p = OVS_CB(skb)->dp_port; |
56fd8edf BP |
458 | if (p) { |
459 | atomic_inc(&p->sflow_pool); | |
460 | if (dp->sflow_probability == UINT_MAX || | |
461 | net_random() < dp->sflow_probability) | |
462 | sflow_sample(dp, skb, a, n_actions, gfp, p); | |
463 | } | |
72b06300 BP |
464 | } |
465 | ||
659586ef JG |
466 | OVS_CB(skb)->tun_id = 0; |
467 | ||
064af421 BP |
468 | for (; n_actions > 0; a++, n_actions--) { |
469 | WARN_ON_ONCE(skb_shared(skb)); | |
470 | if (prev_port != -1) { | |
471 | do_output(dp, skb_clone(skb, gfp), prev_port); | |
472 | prev_port = -1; | |
473 | } | |
474 | ||
475 | switch (a->type) { | |
476 | case ODPAT_OUTPUT: | |
477 | prev_port = a->output.port; | |
478 | break; | |
479 | ||
480 | case ODPAT_OUTPUT_GROUP: | |
481 | prev_port = output_group(dp, a->output_group.group, | |
482 | skb, gfp); | |
483 | break; | |
484 | ||
485 | case ODPAT_CONTROLLER: | |
486 | err = output_control(dp, skb, a->controller.arg, gfp); | |
487 | if (err) { | |
488 | kfree_skb(skb); | |
489 | return err; | |
490 | } | |
491 | break; | |
492 | ||
659586ef JG |
493 | case ODPAT_SET_TUNNEL: |
494 | set_tunnel(skb, key, a->tunnel.tun_id); | |
495 | break; | |
496 | ||
064af421 BP |
497 | case ODPAT_SET_VLAN_VID: |
498 | case ODPAT_SET_VLAN_PCP: | |
499 | skb = modify_vlan_tci(dp, skb, key, a, n_actions, gfp); | |
500 | if (IS_ERR(skb)) | |
501 | return PTR_ERR(skb); | |
502 | break; | |
503 | ||
504 | case ODPAT_STRIP_VLAN: | |
505 | skb = strip_vlan(skb, key, gfp); | |
506 | break; | |
507 | ||
508 | case ODPAT_SET_DL_SRC: | |
509 | case ODPAT_SET_DL_DST: | |
11cdf5e6 | 510 | skb = set_dl_addr(skb, key, &a->dl_addr, gfp); |
064af421 BP |
511 | break; |
512 | ||
513 | case ODPAT_SET_NW_SRC: | |
514 | case ODPAT_SET_NW_DST: | |
515 | skb = set_nw_addr(skb, key, &a->nw_addr, gfp); | |
516 | break; | |
517 | ||
959a2ecd JP |
518 | case ODPAT_SET_NW_TOS: |
519 | skb = set_nw_tos(skb, key, &a->nw_tos, gfp); | |
520 | break; | |
521 | ||
064af421 BP |
522 | case ODPAT_SET_TP_SRC: |
523 | case ODPAT_SET_TP_DST: | |
524 | skb = set_tp_port(skb, key, &a->tp_port, gfp); | |
525 | break; | |
c1c9c9c4 BP |
526 | |
527 | case ODPAT_SET_PRIORITY: | |
528 | skb->priority = a->priority.priority; | |
529 | break; | |
530 | ||
531 | case ODPAT_POP_PRIORITY: | |
532 | skb->priority = priority; | |
533 | break; | |
064af421 BP |
534 | } |
535 | if (!skb) | |
536 | return -ENOMEM; | |
537 | } | |
538 | if (prev_port != -1) | |
539 | do_output(dp, skb, prev_port); | |
540 | else | |
541 | kfree_skb(skb); | |
a5225dd6 | 542 | return 0; |
064af421 | 543 | } |