mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									0a6410fbde
								
							
						
					
					
						commit
						91820da6ae
					
				
					 3 changed files with 82 additions and 0 deletions
				
			
		| 
						 | 
					@ -705,6 +705,15 @@ enum ovs_nat_attr {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OVS_NAT_ATTR_MAX (__OVS_NAT_ATTR_MAX - 1)
 | 
					#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.
 | 
					 * enum ovs_action_attr - Action types.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
| 
						 | 
					@ -738,6 +747,10 @@ enum ovs_nat_attr {
 | 
				
			||||||
 * is no MPLS label stack, as determined by ethertype, no action is taken.
 | 
					 * is no MPLS label stack, as determined by ethertype, no action is taken.
 | 
				
			||||||
 * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
 | 
					 * @OVS_ACTION_ATTR_CT: Track the connection. Populate the conntrack-related
 | 
				
			||||||
 * entries in the flow key.
 | 
					 * 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
 | 
					 * 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
 | 
					 * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
 | 
				
			||||||
| 
						 | 
					@ -765,6 +778,8 @@ enum ovs_action_attr {
 | 
				
			||||||
				       * bits. */
 | 
									       * bits. */
 | 
				
			||||||
	OVS_ACTION_ATTR_CT,           /* Nested OVS_CT_ATTR_* . */
 | 
						OVS_ACTION_ATTR_CT,           /* Nested OVS_CT_ATTR_* . */
 | 
				
			||||||
	OVS_ACTION_ATTR_TRUNC,        /* u32 struct ovs_action_trunc. */
 | 
						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
 | 
						__OVS_ACTION_ATTR_MAX,	      /* Nothing past this will be accepted
 | 
				
			||||||
				       * from userspace. */
 | 
									       * from userspace. */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -317,6 +317,47 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key,
 | 
				
			||||||
	return 0;
 | 
						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,
 | 
					static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
 | 
				
			||||||
				  __be32 addr, __be32 new_addr)
 | 
									  __be32 addr, __be32 new_addr)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1200,6 +1241,14 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 | 
				
			||||||
			if (err)
 | 
								if (err)
 | 
				
			||||||
				return err == -EINPROGRESS ? 0 : err;
 | 
									return err == -EINPROGRESS ? 0 : err;
 | 
				
			||||||
			break;
 | 
								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)) {
 | 
							if (unlikely(err)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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_HASH] = sizeof(struct ovs_action_hash),
 | 
				
			||||||
			[OVS_ACTION_ATTR_CT] = (u32)-1,
 | 
								[OVS_ACTION_ATTR_CT] = (u32)-1,
 | 
				
			||||||
			[OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc),
 | 
								[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;
 | 
							const struct ovs_action_push_vlan *vlan;
 | 
				
			||||||
		int type = nla_type(a);
 | 
							int type = nla_type(a);
 | 
				
			||||||
| 
						 | 
					@ -2517,6 +2519,22 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
 | 
				
			||||||
			skip_copy = true;
 | 
								skip_copy = true;
 | 
				
			||||||
			break;
 | 
								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:
 | 
							default:
 | 
				
			||||||
			OVS_NLERR(log, "Unknown Action type %d", type);
 | 
								OVS_NLERR(log, "Unknown Action type %d", type);
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue