diff options
| author | Jarno Rajahalme <jrajahalme@nicira.com> | 2015-02-05 16:40:49 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2015-02-08 01:40:17 -0500 |
| commit | 83d2b9ba1abca241df44a502b6da950a25856b5b (patch) | |
| tree | 16b6618df6b9cdbbf955457352cb71acd8cca324 | |
| parent | 2150f984258e7909327cb90115c8ff41b4e9acb5 (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.h | 22 | ||||
| -rw-r--r-- | net/openvswitch/actions.c | 373 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 161 |
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 | ||
| 622 | enum ovs_action_attr { | 631 | enum 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 | ||
| 188 | static 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 | |||
| 192 | static 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 | ||
| 233 | static 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) | 241 | static 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 | |||
| 252 | static 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 | ||
| 330 | static 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 | |||
| 307 | static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, | 339 | static 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 | ||
| 318 | static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) | 350 | static 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); | ||
