aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch/actions.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/openvswitch/actions.c')
-rw-r--r--net/openvswitch/actions.c106
1 files changed, 105 insertions, 1 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 922c133b1933..930b1b6e4cef 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -28,10 +28,12 @@
28#include <linux/in6.h> 28#include <linux/in6.h>
29#include <linux/if_arp.h> 29#include <linux/if_arp.h>
30#include <linux/if_vlan.h> 30#include <linux/if_vlan.h>
31
31#include <net/ip.h> 32#include <net/ip.h>
32#include <net/ipv6.h> 33#include <net/ipv6.h>
33#include <net/checksum.h> 34#include <net/checksum.h>
34#include <net/dsfield.h> 35#include <net/dsfield.h>
36#include <net/mpls.h>
35#include <net/sctp/checksum.h> 37#include <net/sctp/checksum.h>
36 38
37#include "datapath.h" 39#include "datapath.h"
@@ -118,6 +120,92 @@ static int make_writable(struct sk_buff *skb, int write_len)
118 return pskb_expand_head(skb, 0, 0, GFP_ATOMIC); 120 return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
119} 121}
120 122
123static int push_mpls(struct sk_buff *skb,
124 const struct ovs_action_push_mpls *mpls)
125{
126 __be32 *new_mpls_lse;
127 struct ethhdr *hdr;
128
129 /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
130 if (skb->encapsulation)
131 return -ENOTSUPP;
132
133 if (skb_cow_head(skb, MPLS_HLEN) < 0)
134 return -ENOMEM;
135
136 skb_push(skb, MPLS_HLEN);
137 memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb),
138 skb->mac_len);
139 skb_reset_mac_header(skb);
140
141 new_mpls_lse = (__be32 *)skb_mpls_header(skb);
142 *new_mpls_lse = mpls->mpls_lse;
143
144 if (skb->ip_summed == CHECKSUM_COMPLETE)
145 skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse,
146 MPLS_HLEN, 0));
147
148 hdr = eth_hdr(skb);
149 hdr->h_proto = mpls->mpls_ethertype;
150
151 skb_set_inner_protocol(skb, skb->protocol);
152 skb->protocol = mpls->mpls_ethertype;
153
154 return 0;
155}
156
157static int pop_mpls(struct sk_buff *skb, const __be16 ethertype)
158{
159 struct ethhdr *hdr;
160 int err;
161
162 err = make_writable(skb, skb->mac_len + MPLS_HLEN);
163 if (unlikely(err))
164 return err;
165
166 if (skb->ip_summed == CHECKSUM_COMPLETE)
167 skb->csum = csum_sub(skb->csum,
168 csum_partial(skb_mpls_header(skb),
169 MPLS_HLEN, 0));
170
171 memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
172 skb->mac_len);
173
174 __skb_pull(skb, MPLS_HLEN);
175 skb_reset_mac_header(skb);
176
177 /* skb_mpls_header() is used to locate the ethertype
178 * field correctly in the presence of VLAN tags.
179 */
180 hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
181 hdr->h_proto = ethertype;
182 if (eth_p_mpls(skb->protocol))
183 skb->protocol = ethertype;
184 return 0;
185}
186
187static int set_mpls(struct sk_buff *skb, const __be32 *mpls_lse)
188{
189 __be32 *stack;
190 int err;
191
192 err = make_writable(skb, skb->mac_len + MPLS_HLEN);
193 if (unlikely(err))
194 return err;
195
196 stack = (__be32 *)skb_mpls_header(skb);
197 if (skb->ip_summed == CHECKSUM_COMPLETE) {
198 __be32 diff[] = { ~(*stack), *mpls_lse };
199
200 skb->csum = ~csum_partial((char *)diff, sizeof(diff),
201 ~skb->csum);
202 }
203
204 *stack = *mpls_lse;
205
206 return 0;
207}
208
121/* remove VLAN header from packet and update csum accordingly. */ 209/* remove VLAN header from packet and update csum accordingly. */
122static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) 210static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
123{ 211{
@@ -140,10 +228,12 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
140 228
141 vlan_set_encap_proto(skb, vhdr); 229 vlan_set_encap_proto(skb, vhdr);
142 skb->mac_header += VLAN_HLEN; 230 skb->mac_header += VLAN_HLEN;
231
143 if (skb_network_offset(skb) < ETH_HLEN) 232 if (skb_network_offset(skb) < ETH_HLEN)
144 skb_set_network_header(skb, ETH_HLEN); 233 skb_set_network_header(skb, ETH_HLEN);
145 skb_reset_mac_len(skb);
146 234
235 /* Update mac_len for subsequent MPLS actions */
236 skb_reset_mac_len(skb);
147 return 0; 237 return 0;
148} 238}
149 239
@@ -186,6 +276,8 @@ static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vla
186 276
187 if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag)) 277 if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
188 return -ENOMEM; 278 return -ENOMEM;
279 /* Update mac_len for subsequent MPLS actions */
280 skb->mac_len += VLAN_HLEN;
189 281
190 if (skb->ip_summed == CHECKSUM_COMPLETE) 282 if (skb->ip_summed == CHECKSUM_COMPLETE)
191 skb->csum = csum_add(skb->csum, csum_partial(skb->data 283 skb->csum = csum_add(skb->csum, csum_partial(skb->data
@@ -612,6 +704,10 @@ static int execute_set_action(struct sk_buff *skb,
612 case OVS_KEY_ATTR_SCTP: 704 case OVS_KEY_ATTR_SCTP:
613 err = set_sctp(skb, nla_data(nested_attr)); 705 err = set_sctp(skb, nla_data(nested_attr));
614 break; 706 break;
707
708 case OVS_KEY_ATTR_MPLS:
709 err = set_mpls(skb, nla_data(nested_attr));
710 break;
615 } 711 }
616 712
617 return err; 713 return err;
@@ -690,6 +786,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
690 execute_hash(skb, key, a); 786 execute_hash(skb, key, a);
691 break; 787 break;
692 788
789 case OVS_ACTION_ATTR_PUSH_MPLS:
790 err = push_mpls(skb, nla_data(a));
791 break;
792
793 case OVS_ACTION_ATTR_POP_MPLS:
794 err = pop_mpls(skb, nla_get_be16(a));
795 break;
796
693 case OVS_ACTION_ATTR_PUSH_VLAN: 797 case OVS_ACTION_ATTR_PUSH_VLAN:
694 err = push_vlan(skb, nla_data(a)); 798 err = push_vlan(skb, nla_data(a));
695 if (unlikely(err)) /* skb already freed. */ 799 if (unlikely(err)) /* skb already freed. */