aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarno Rajahalme <jrajahalme@nicira.com>2015-02-05 16:40:49 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-08 01:40:17 -0500
commit83d2b9ba1abca241df44a502b6da950a25856b5b (patch)
tree16b6618df6b9cdbbf955457352cb71acd8cca324
parent2150f984258e7909327cb90115c8ff41b4e9acb5 (diff)
net: openvswitch: Support masked set actions.
OVS userspace already probes the openvswitch kernel module for OVS_ACTION_ATTR_SET_MASKED support. This patch adds the kernel module implementation of masked set actions. The existing set action sets many fields at once. When only a subset of the IP header fields, for example, should be modified, all the IP fields need to be exact matched so that the other field values can be copied to the set action. A masked set action allows modification of an arbitrary subset of the supported header bits without requiring the rest to be matched. Masked set action is now supported for all writeable key types, except for the tunnel key. The set tunnel action is an exception as any input tunnel info is cleared before action processing starts, so there is no tunnel info to mask. The kernel module converts all (non-tunnel) set actions to masked set actions. This makes action processing more uniform, and results in less branching and duplicating the action processing code. When returning actions to userspace, the fully masked set actions are converted back to normal set actions. We use a kernel internal action code to be able to tell the userspace provided and converted masked set actions apart. Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com> Acked-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/uapi/linux/openvswitch.h22
-rw-r--r--net/openvswitch/actions.c373
-rw-r--r--net/openvswitch/flow_netlink.c161
3 files changed, 383 insertions, 173 deletions
diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h
index 7a8785a99243..bbd49a0c46c7 100644
--- a/include/uapi/linux/openvswitch.h
+++ b/include/uapi/linux/openvswitch.h
@@ -599,6 +599,12 @@ struct ovs_action_hash {
599 * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The 599 * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
600 * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its 600 * single nested %OVS_KEY_ATTR_* attribute specifies a header to modify and its
601 * value. 601 * value.
602 * @OVS_ACTION_ATTR_SET_MASKED: Replaces the contents of an existing header. A
603 * nested %OVS_KEY_ATTR_* attribute specifies a header to modify, its value,
604 * and a mask. For every bit set in the mask, the corresponding bit value
605 * is copied from the value to the packet header field, rest of the bits are
606 * left unchanged. The non-masked value bits must be passed in as zeroes.
607 * Masking is not supported for the %OVS_KEY_ATTR_TUNNEL attribute.
602 * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the 608 * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
603 * packet. 609 * packet.
604 * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet. 610 * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
@@ -617,6 +623,9 @@ struct ovs_action_hash {
617 * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all 623 * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all
618 * fields within a header are modifiable, e.g. the IPv4 protocol and fragment 624 * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
619 * type may not be changed. 625 * type may not be changed.
626 *
627 * @OVS_ACTION_ATTR_SET_TO_MASKED: Kernel internal masked set action translated
628 * from the @OVS_ACTION_ATTR_SET.
620 */ 629 */
621 630
622enum ovs_action_attr { 631enum ovs_action_attr {
@@ -631,8 +640,19 @@ enum ovs_action_attr {
631 OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */ 640 OVS_ACTION_ATTR_HASH, /* struct ovs_action_hash. */
632 OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */ 641 OVS_ACTION_ATTR_PUSH_MPLS, /* struct ovs_action_push_mpls. */
633 OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */ 642 OVS_ACTION_ATTR_POP_MPLS, /* __be16 ethertype. */
643 OVS_ACTION_ATTR_SET_MASKED, /* One nested OVS_KEY_ATTR_* including
644 * data immediately followed by a mask.
645 * The data must be zero for the unmasked
646 * bits. */
647
648 __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
649 * from userspace. */
634 650
635 __OVS_ACTION_ATTR_MAX 651#ifdef __KERNEL__
652 OVS_ACTION_ATTR_SET_TO_MASKED, /* Kernel module internal masked
653 * set action converted from
654 * OVS_ACTION_ATTR_SET. */
655#endif
636}; 656};
637 657
638#define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1) 658#define OVS_ACTION_ATTR_MAX (__OVS_ACTION_ATTR_MAX - 1)
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index b4cffe686126..b491c1c296fe 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -185,10 +185,15 @@ static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
185 return 0; 185 return 0;
186} 186}
187 187
188static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key, 188/* 'KEY' must not have any bits set outside of the 'MASK' */
189 const __be32 *mpls_lse) 189#define MASKED(OLD, KEY, MASK) ((KEY) | ((OLD) & ~(MASK)))
190#define SET_MASKED(OLD, KEY, MASK) ((OLD) = MASKED(OLD, KEY, MASK))
191
192static int set_mpls(struct sk_buff *skb, struct sw_flow_key *flow_key,
193 const __be32 *mpls_lse, const __be32 *mask)
190{ 194{
191 __be32 *stack; 195 __be32 *stack;
196 __be32 lse;
192 int err; 197 int err;
193 198
194 err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN); 199 err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN);
@@ -196,14 +201,16 @@ static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key,
196 return err; 201 return err;
197 202
198 stack = (__be32 *)skb_mpls_header(skb); 203 stack = (__be32 *)skb_mpls_header(skb);
204 lse = MASKED(*stack, *mpls_lse, *mask);
199 if (skb->ip_summed == CHECKSUM_COMPLETE) { 205 if (skb->ip_summed == CHECKSUM_COMPLETE) {
200 __be32 diff[] = { ~(*stack), *mpls_lse }; 206 __be32 diff[] = { ~(*stack), lse };
207
201 skb->csum = ~csum_partial((char *)diff, sizeof(diff), 208 skb->csum = ~csum_partial((char *)diff, sizeof(diff),
202 ~skb->csum); 209 ~skb->csum);
203 } 210 }
204 211
205 *stack = *mpls_lse; 212 *stack = lse;
206 key->mpls.top_lse = *mpls_lse; 213 flow_key->mpls.top_lse = lse;
207 return 0; 214 return 0;
208} 215}
209 216
@@ -230,23 +237,39 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
230 ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); 237 ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
231} 238}
232 239
233static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key, 240/* 'src' is already properly masked. */
234 const struct ovs_key_ethernet *eth_key) 241static void ether_addr_copy_masked(u8 *dst_, const u8 *src_, const u8 *mask_)
242{
243 u16 *dst = (u16 *)dst_;
244 const u16 *src = (const u16 *)src_;
245 const u16 *mask = (const u16 *)mask_;
246
247 SET_MASKED(dst[0], src[0], mask[0]);
248 SET_MASKED(dst[1], src[1], mask[1]);
249 SET_MASKED(dst[2], src[2], mask[2]);
250}
251
252static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
253 const struct ovs_key_ethernet *key,
254 const struct ovs_key_ethernet *mask)
235{ 255{
236 int err; 256 int err;
257
237 err = skb_ensure_writable(skb, ETH_HLEN); 258 err = skb_ensure_writable(skb, ETH_HLEN);
238 if (unlikely(err)) 259 if (unlikely(err))
239 return err; 260 return err;
240 261
241 skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); 262 skb_postpull_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
242 263
243 ether_addr_copy(eth_hdr(skb)->h_source, eth_key->eth_src); 264 ether_addr_copy_masked(eth_hdr(skb)->h_source, key->eth_src,
244 ether_addr_copy(eth_hdr(skb)->h_dest, eth_key->eth_dst); 265 mask->eth_src);
266 ether_addr_copy_masked(eth_hdr(skb)->h_dest, key->eth_dst,
267 mask->eth_dst);
245 268
246 ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2); 269 ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
247 270
248 ether_addr_copy(key->eth.src, eth_key->eth_src); 271 ether_addr_copy(flow_key->eth.src, eth_hdr(skb)->h_source);
249 ether_addr_copy(key->eth.dst, eth_key->eth_dst); 272 ether_addr_copy(flow_key->eth.dst, eth_hdr(skb)->h_dest);
250 return 0; 273 return 0;
251} 274}
252 275
@@ -304,6 +327,15 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
304 } 327 }
305} 328}
306 329
330static void mask_ipv6_addr(const __be32 old[4], const __be32 addr[4],
331 const __be32 mask[4], __be32 masked[4])
332{
333 masked[0] = MASKED(old[0], addr[0], mask[0]);
334 masked[1] = MASKED(old[1], addr[1], mask[1]);
335 masked[2] = MASKED(old[2], addr[2], mask[2]);
336 masked[3] = MASKED(old[3], addr[3], mask[3]);
337}
338
307static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, 339static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
308 __be32 addr[4], const __be32 new_addr[4], 340 __be32 addr[4], const __be32 new_addr[4],
309 bool recalculate_csum) 341 bool recalculate_csum)
@@ -315,29 +347,29 @@ static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
315 memcpy(addr, new_addr, sizeof(__be32[4])); 347 memcpy(addr, new_addr, sizeof(__be32[4]));
316} 348}
317 349
318static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) 350static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl, u32 mask)
319{ 351{
320 nh->priority = tc >> 4; 352 /* Bits 21-24 are always unmasked, so this retains their values. */
321 nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4);