mirror of
https://github.com/openvswitch/ovs
synced 2025-09-05 08:45:23 +00:00
datapath: add Ethernet push and pop actions
Upstream commit: commit 91820da6ae85904d95ed53bf3a83f9ec44a6b80a Author: Jiri Benc <jbenc@redhat.com> Date: Thu Nov 10 16:28:23 2016 +0100 openvswitch: add Ethernet push and pop actions 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> [Committer notes] Fix build with the upstream commit by folding in the required switch case enum handlers. Signed-off-by: Yi Yang <yi.y.yang@intel.com> Signed-off-by: Joe Stringer <joe@ovn.org>
This commit is contained in:
@@ -310,6 +310,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)
|
||||
{
|
||||
@@ -1184,6 +1225,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)) {
|
||||
|
@@ -2388,6 +2388,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);
|
||||
@@ -2522,6 +2524,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;
|
||||
|
@@ -711,6 +711,15 @@ enum ovs_ct_attr {
|
||||
|
||||
#define OVS_CT_ATTR_MAX (__OVS_CT_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_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
|
||||
*
|
||||
@@ -783,6 +792,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
|
||||
@@ -815,6 +828,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. */
|
||||
|
||||
#ifndef __KERNEL__
|
||||
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
|
||||
|
@@ -4783,6 +4783,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
|
||||
case OVS_ACTION_ATTR_HASH:
|
||||
case OVS_ACTION_ATTR_UNSPEC:
|
||||
case OVS_ACTION_ATTR_TRUNC:
|
||||
case OVS_ACTION_ATTR_PUSH_ETH:
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
case OVS_ACTION_ATTR_CLONE:
|
||||
case __OVS_ACTION_ATTR_MAX:
|
||||
OVS_NOT_REACHED();
|
||||
|
@@ -1182,6 +1182,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
|
||||
case OVS_ACTION_ATTR_SET_MASKED:
|
||||
case OVS_ACTION_ATTR_SAMPLE:
|
||||
case OVS_ACTION_ATTR_TRUNC:
|
||||
case OVS_ACTION_ATTR_PUSH_ETH:
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
case OVS_ACTION_ATTR_CLONE:
|
||||
case OVS_ACTION_ATTR_UNSPEC:
|
||||
case __OVS_ACTION_ATTR_MAX:
|
||||
|
@@ -580,6 +580,8 @@ requires_datapath_assistance(const struct nlattr *a)
|
||||
case OVS_ACTION_ATTR_PUSH_MPLS:
|
||||
case OVS_ACTION_ATTR_POP_MPLS:
|
||||
case OVS_ACTION_ATTR_TRUNC:
|
||||
case OVS_ACTION_ATTR_PUSH_ETH:
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
case OVS_ACTION_ATTR_CLONE:
|
||||
return false;
|
||||
|
||||
@@ -730,6 +732,8 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
|
||||
case OVS_ACTION_ATTR_USERSPACE:
|
||||
case OVS_ACTION_ATTR_RECIRC:
|
||||
case OVS_ACTION_ATTR_CT:
|
||||
case OVS_ACTION_ATTR_PUSH_ETH:
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
case OVS_ACTION_ATTR_UNSPEC:
|
||||
case __OVS_ACTION_ATTR_MAX:
|
||||
OVS_NOT_REACHED();
|
||||
|
@@ -124,6 +124,8 @@ odp_action_len(uint16_t type)
|
||||
case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE;
|
||||
case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE;
|
||||
case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE;
|
||||
case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);
|
||||
case OVS_ACTION_ATTR_POP_ETH: return 0;
|
||||
case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
|
||||
|
||||
case OVS_ACTION_ATTR_UNSPEC:
|
||||
@@ -850,6 +852,16 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
|
||||
format_odp_key_attr(nl_attr_get(a), NULL, NULL, ds, true);
|
||||
ds_put_cstr(ds, ")");
|
||||
break;
|
||||
case OVS_ACTION_ATTR_PUSH_ETH: {
|
||||
const struct ovs_action_push_eth *eth = nl_attr_get(a);
|
||||
ds_put_format(ds, "push_eth(src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT")",
|
||||
ETH_ADDR_ARGS(eth->addresses.eth_src),
|
||||
ETH_ADDR_ARGS(eth->addresses.eth_dst));
|
||||
break;
|
||||
}
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
ds_put_cstr(ds, "pop_eth");
|
||||
break;
|
||||
case OVS_ACTION_ATTR_PUSH_VLAN: {
|
||||
const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
|
||||
ds_put_cstr(ds, "push_vlan(");
|
||||
|
@@ -1164,6 +1164,13 @@ dpif_sflow_read_actions(const struct flow *flow,
|
||||
dpif_sflow_pop_mpls_lse(sflow_actions);
|
||||
break;
|
||||
}
|
||||
case OVS_ACTION_ATTR_PUSH_ETH:
|
||||
case OVS_ACTION_ATTR_POP_ETH:
|
||||
/* TODO: SFlow does not currently define a MAC-in-MAC
|
||||
* encapsulation structure. We could use an extension
|
||||
* structure to report this.
|
||||
*/
|
||||
break;
|
||||
case OVS_ACTION_ATTR_SAMPLE:
|
||||
case OVS_ACTION_ATTR_CLONE:
|
||||
case OVS_ACTION_ATTR_UNSPEC:
|
||||
|
Reference in New Issue
Block a user