Skip to content

Commit

Permalink
openvswitch: add Ethernet push and pop actions
Browse files Browse the repository at this point in the history
It's not allowed to push Ethernet header in front of another Ethernet
header.

It's not allowed to pop Ethernet header if there's a vlan tag. This
preserves the invariant that L3 packet never has a vlan tag.

Based on previous versions by Lorand Jakab and Simon Horman.

Signed-off-by: Lorand Jakab <lojakab@cisco.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jiri Benc authored and David S. Miller committed Nov 13, 2016
1 parent 0a6410f commit 91820da
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 0 deletions.
15 changes: 15 additions & 0 deletions include/uapi/linux/openvswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,15 @@ enum ovs_nat_attr {

#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)

/*
* struct ovs_action_push_eth - %OVS_ACTION_ATTR_PUSH_ETH action argument.
* @addresses: Source and destination MAC addresses.
* @eth_type: Ethernet type
*/
struct ovs_action_push_eth {
struct ovs_key_ethernet addresses;
};

/**
* enum ovs_action_attr - Action types.
*
Expand Down Expand Up @@ -738,6 +747,10 @@ enum ovs_nat_attr {
* is no MPLS label stack, as determined by ethertype, no action is taken.
* @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
* entries in the flow key.
* @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the
* packet.
* @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the
* packet.
*
* Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all
* fields within a header are modifiable, e.g. the IPv4 protocol and fragment
Expand Down Expand Up @@ -765,6 +778,8 @@ enum ovs_action_attr {
* bits. */
OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */
OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */
OVS_ACTION_ATTR_POP_ETH, /* No argument. */

__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */
Expand Down
49 changes: 49 additions & 0 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,47 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
return 0;
}

/* pop_eth does not support VLAN packets as this action is never called
* for them.
*/
static int pop_eth(struct sk_buff *skb, struct sw_flow_key *key)
{
skb_pull_rcsum(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);

/* safe right before invalidate_flow_key */
key->mac_proto = MAC_PROTO_NONE;
invalidate_flow_key(key);
return 0;
}

static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_action_push_eth *ethh)
{
struct ethhdr *hdr;

/* Add the new Ethernet header */
if (skb_cow_head(skb, ETH_HLEN) < 0)
return -ENOMEM;

skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);

hdr = eth_hdr(skb);
ether_addr_copy(hdr->h_source, ethh->addresses.eth_src);
ether_addr_copy(hdr->h_dest, ethh->addresses.eth_dst);
hdr->h_proto = skb->protocol;

skb_postpush_rcsum(skb, hdr, ETH_HLEN);

/* safe right before invalidate_flow_key */
key->mac_proto = MAC_PROTO_ETHERNET;
invalidate_flow_key(key);
return 0;
}

static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
__be32 addr, __be32 new_addr)
{
Expand Down Expand Up @@ -1200,6 +1241,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
if (err)
return err == -EINPROGRESS ? 0 : err;
break;

case OVS_ACTION_ATTR_PUSH_ETH:
err = push_eth(skb, key, nla_data(a));
break;

case OVS_ACTION_ATTR_POP_ETH:
err = pop_eth(skb, key);
break;
}

if (unlikely(err)) {
Expand Down
18 changes: 18 additions & 0 deletions net/openvswitch/flow_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,8 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
[OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
[OVS_ACTION_ATTR_CT] = (u32)-1,
[OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc),
[OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth),
[OVS_ACTION_ATTR_POP_ETH] = 0,
};
const struct ovs_action_push_vlan *vlan;
int type = nla_type(a);
Expand Down Expand Up @@ -2517,6 +2519,22 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
skip_copy = true;
break;

case OVS_ACTION_ATTR_PUSH_ETH:
/* Disallow pushing an Ethernet header if one
* is already present */
if (mac_proto != MAC_PROTO_NONE)
return -EINVAL;
mac_proto = MAC_PROTO_NONE;
break;

case OVS_ACTION_ATTR_POP_ETH:
if (mac_proto != MAC_PROTO_ETHERNET)
return -EINVAL;
if (vlan_tci & htons(VLAN_TAG_PRESENT))
return -EINVAL;
mac_proto = MAC_PROTO_ETHERNET;
break;

default:
OVS_NLERR(log, "Unknown Action type %d", type);
return -EINVAL;
Expand Down

0 comments on commit 91820da

Please sign in to comment.