aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch/actions.c
diff options
context:
space:
mode:
authorSimon Horman <horms@verge.net.au>2014-10-06 08:05:13 -0400
committerPravin B Shelar <pshelar@nicira.com>2014-11-06 02:52:33 -0500
commit25cd9ba0abc0749e5cb78e6493c6f6b3311ec6c5 (patch)
treefbfb953b27fbdcc27d0a50a6e3444532f51f5ffa /net/openvswitch/actions.c
parent59b93b41e7fa71138734a911b11b044340dd16bd (diff)
openvswitch: Add basic MPLS support to kernel
Allow datapath to recognize and extract MPLS labels into flow keys and execute actions which push, pop, and set labels on packets. Based heavily on work by Leo Alterman, Ravi K, Isaku Yamahata and Joe Stringer. Cc: Ravi K <rkerur@gmail.com> Cc: Leo Alterman <lalterman@nicira.com> Cc: Isaku Yamahata <yamahata@valinux.co.jp> Cc: Joe Stringer <joe@wand.net.nz> Signed-off-by: Simon Horman <horms@verge.net.au> Signed-off-by: Jesse Gross <jesse@nicira.com> Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
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. */