forked from mirrors/linux
		
	openvswitch: add processing of L3 packets
Support receiving, extracting flow key and sending of L3 packets (packets without an Ethernet header). Note that even after this patch, non-Ethernet interfaces are still not allowed to be added to bridges. Similarly, netlink interface for sending and receiving L3 packets to/from user space is not in place yet. 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
							
								
									1560a074df
								
							
						
					
					
						commit
						5108bbaddc
					
				
					 3 changed files with 101 additions and 37 deletions
				
			
		|  | @ -560,7 +560,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) | ||||||
| 	struct sw_flow *flow; | 	struct sw_flow *flow; | ||||||
| 	struct sw_flow_actions *sf_acts; | 	struct sw_flow_actions *sf_acts; | ||||||
| 	struct datapath *dp; | 	struct datapath *dp; | ||||||
| 	struct ethhdr *eth; |  | ||||||
| 	struct vport *input_vport; | 	struct vport *input_vport; | ||||||
| 	u16 mru = 0; | 	u16 mru = 0; | ||||||
| 	int len; | 	int len; | ||||||
|  | @ -581,17 +580,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) | ||||||
| 
 | 
 | ||||||
| 	nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len); | 	nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len); | ||||||
| 
 | 
 | ||||||
| 	skb_reset_mac_header(packet); |  | ||||||
| 	eth = eth_hdr(packet); |  | ||||||
| 
 |  | ||||||
| 	/* Normally, setting the skb 'protocol' field would be handled by a
 |  | ||||||
| 	 * call to eth_type_trans(), but it assumes there's a sending |  | ||||||
| 	 * device, which we may not have. */ |  | ||||||
| 	if (eth_proto_is_802_3(eth->h_proto)) |  | ||||||
| 		packet->protocol = eth->h_proto; |  | ||||||
| 	else |  | ||||||
| 		packet->protocol = htons(ETH_P_802_2); |  | ||||||
| 
 |  | ||||||
| 	/* Set packet's mru */ | 	/* Set packet's mru */ | ||||||
| 	if (a[OVS_PACKET_ATTR_MRU]) { | 	if (a[OVS_PACKET_ATTR_MRU]) { | ||||||
| 		mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]); | 		mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]); | ||||||
|  | @ -618,6 +606,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) | ||||||
| 	rcu_assign_pointer(flow->sf_acts, acts); | 	rcu_assign_pointer(flow->sf_acts, acts); | ||||||
| 	packet->priority = flow->key.phy.priority; | 	packet->priority = flow->key.phy.priority; | ||||||
| 	packet->mark = flow->key.phy.skb_mark; | 	packet->mark = flow->key.phy.skb_mark; | ||||||
|  | 	packet->protocol = flow->key.eth.type; | ||||||
| 
 | 
 | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
| 	dp = get_dp_rcu(net, ovs_header->dp_ifindex); | 	dp = get_dp_rcu(net, ovs_header->dp_ifindex); | ||||||
|  |  | ||||||
|  | @ -334,14 +334,17 @@ static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh) | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) | static void clear_vlan(struct sw_flow_key *key) | ||||||
| { | { | ||||||
| 	int res; |  | ||||||
| 
 |  | ||||||
| 	key->eth.vlan.tci = 0; | 	key->eth.vlan.tci = 0; | ||||||
| 	key->eth.vlan.tpid = 0; | 	key->eth.vlan.tpid = 0; | ||||||
| 	key->eth.cvlan.tci = 0; | 	key->eth.cvlan.tci = 0; | ||||||
| 	key->eth.cvlan.tpid = 0; | 	key->eth.cvlan.tpid = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) | ||||||
|  | { | ||||||
|  | 	int res; | ||||||
| 
 | 
 | ||||||
| 	if (skb_vlan_tag_present(skb)) { | 	if (skb_vlan_tag_present(skb)) { | ||||||
| 		key->eth.vlan.tci = htons(skb->vlan_tci); | 		key->eth.vlan.tci = htons(skb->vlan_tci); | ||||||
|  | @ -483,17 +486,20 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, | ||||||
|  * |  * | ||||||
|  * Returns 0 if successful, otherwise a negative errno value. |  * Returns 0 if successful, otherwise a negative errno value. | ||||||
|  * |  * | ||||||
|  * Initializes @skb header pointers as follows: |  * Initializes @skb header fields as follows: | ||||||
|  * |  * | ||||||
|  *    - skb->mac_header: the Ethernet header. |  *    - skb->mac_header: the L2 header. | ||||||
|  * |  * | ||||||
|  *    - skb->network_header: just past the Ethernet header, or just past the |  *    - skb->network_header: just past the L2 header, or just past the | ||||||
|  *      VLAN header, to the first byte of the Ethernet payload. |  *      VLAN header, to the first byte of the L2 payload. | ||||||
|  * |  * | ||||||
|  *    - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6 |  *    - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6 | ||||||
|  *      on output, then just past the IP header, if one is present and |  *      on output, then just past the IP header, if one is present and | ||||||
|  *      of a correct length, otherwise the same as skb->network_header. |  *      of a correct length, otherwise the same as skb->network_header. | ||||||
|  *      For other key->eth.type values it is left untouched. |  *      For other key->eth.type values it is left untouched. | ||||||
|  |  * | ||||||
|  |  *    - skb->protocol: the type of the data starting at skb->network_header. | ||||||
|  |  *      Equals to key->eth.type. | ||||||
|  */ |  */ | ||||||
| static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) | static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) | ||||||
| { | { | ||||||
|  | @ -505,28 +511,35 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) | ||||||
| 
 | 
 | ||||||
| 	skb_reset_mac_header(skb); | 	skb_reset_mac_header(skb); | ||||||
| 
 | 
 | ||||||
| 	/* Link layer.  We are guaranteed to have at least the 14 byte Ethernet
 | 	/* Link layer. */ | ||||||
| 	 * header in the linear data area. | 	clear_vlan(key); | ||||||
| 	 */ | 	if (key->mac_proto == MAC_PROTO_NONE) { | ||||||
| 	eth = eth_hdr(skb); | 		if (unlikely(eth_type_vlan(skb->protocol))) | ||||||
| 	ether_addr_copy(key->eth.src, eth->h_source); | 			return -EINVAL; | ||||||
| 	ether_addr_copy(key->eth.dst, eth->h_dest); |  | ||||||
| 
 | 
 | ||||||
| 	__skb_pull(skb, 2 * ETH_ALEN); | 		skb_reset_network_header(skb); | ||||||
| 	/* We are going to push all headers that we pull, so no need to
 | 	} else { | ||||||
| 	 * update skb->csum here. | 		eth = eth_hdr(skb); | ||||||
| 	 */ | 		ether_addr_copy(key->eth.src, eth->h_source); | ||||||
|  | 		ether_addr_copy(key->eth.dst, eth->h_dest); | ||||||
| 
 | 
 | ||||||
| 	if (unlikely(parse_vlan(skb, key))) | 		__skb_pull(skb, 2 * ETH_ALEN); | ||||||
| 		return -ENOMEM; | 		/* We are going to push all headers that we pull, so no need to
 | ||||||
|  | 		* update skb->csum here. | ||||||
|  | 		*/ | ||||||
| 
 | 
 | ||||||
| 	key->eth.type = parse_ethertype(skb); | 		if (unlikely(parse_vlan(skb, key))) | ||||||
| 	if (unlikely(key->eth.type == htons(0))) | 			return -ENOMEM; | ||||||
| 		return -ENOMEM; |  | ||||||
| 
 | 
 | ||||||
| 	skb_reset_network_header(skb); | 		skb->protocol = parse_ethertype(skb); | ||||||
|  | 		if (unlikely(skb->protocol == htons(0))) | ||||||
|  | 			return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 		skb_reset_network_header(skb); | ||||||
|  | 		__skb_push(skb, skb->data - skb_mac_header(skb)); | ||||||
|  | 	} | ||||||
| 	skb_reset_mac_len(skb); | 	skb_reset_mac_len(skb); | ||||||
| 	__skb_push(skb, skb->data - skb_mac_header(skb)); | 	key->eth.type = skb->protocol; | ||||||
| 
 | 
 | ||||||
| 	/* Network layer. */ | 	/* Network layer. */ | ||||||
| 	if (key->eth.type == htons(ETH_P_IP)) { | 	if (key->eth.type == htons(ETH_P_IP)) { | ||||||
|  | @ -721,9 +734,25 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key) | ||||||
| 	return key_extract(skb, key); | 	return key_extract(skb, key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static int key_extract_mac_proto(struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	switch (skb->dev->type) { | ||||||
|  | 	case ARPHRD_ETHER: | ||||||
|  | 		return MAC_PROTO_ETHERNET; | ||||||
|  | 	case ARPHRD_NONE: | ||||||
|  | 		if (skb->protocol == htons(ETH_P_TEB)) | ||||||
|  | 			return MAC_PROTO_ETHERNET; | ||||||
|  | 		return MAC_PROTO_NONE; | ||||||
|  | 	} | ||||||
|  | 	WARN_ON_ONCE(1); | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, | int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, | ||||||
| 			 struct sk_buff *skb, struct sw_flow_key *key) | 			 struct sk_buff *skb, struct sw_flow_key *key) | ||||||
| { | { | ||||||
|  | 	int res; | ||||||
|  | 
 | ||||||
| 	/* Extract metadata from packet. */ | 	/* Extract metadata from packet. */ | ||||||
| 	if (tun_info) { | 	if (tun_info) { | ||||||
| 		key->tun_proto = ip_tunnel_info_af(tun_info); | 		key->tun_proto = ip_tunnel_info_af(tun_info); | ||||||
|  | @ -751,7 +780,10 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info, | ||||||
| 	key->phy.skb_mark = skb->mark; | 	key->phy.skb_mark = skb->mark; | ||||||
| 	ovs_ct_fill_key(skb, key); | 	ovs_ct_fill_key(skb, key); | ||||||
| 	key->ovs_flow_hash = 0; | 	key->ovs_flow_hash = 0; | ||||||
| 	key->mac_proto = MAC_PROTO_ETHERNET; | 	res = key_extract_mac_proto(skb); | ||||||
|  | 	if (res < 0) | ||||||
|  | 		return res; | ||||||
|  | 	key->mac_proto = res; | ||||||
| 	key->recirc_id = 0; | 	key->recirc_id = 0; | ||||||
| 
 | 
 | ||||||
| 	return key_extract(skb, key); | 	return key_extract(skb, key); | ||||||
|  | @ -768,5 +800,29 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr, | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
| 
 | 
 | ||||||
|  | 	if (ovs_key_mac_proto(key) == MAC_PROTO_NONE) { | ||||||
|  | 		/* key_extract assumes that skb->protocol is set-up for
 | ||||||
|  | 		 * layer 3 packets which is the case for other callers, | ||||||
|  | 		 * in particular packets recieved from the network stack. | ||||||
|  | 		 * Here the correct value can be set from the metadata | ||||||
|  | 		 * extracted above. | ||||||
|  | 		 */ | ||||||
|  | 		skb->protocol = key->eth.type; | ||||||
|  | 	} else { | ||||||
|  | 		struct ethhdr *eth; | ||||||
|  | 
 | ||||||
|  | 		skb_reset_mac_header(skb); | ||||||
|  | 		eth = eth_hdr(skb); | ||||||
|  | 
 | ||||||
|  | 		/* Normally, setting the skb 'protocol' field would be
 | ||||||
|  | 		 * handled by a call to eth_type_trans(), but it assumes | ||||||
|  | 		 * there's a sending device, which we may not have. | ||||||
|  | 		 */ | ||||||
|  | 		if (eth_proto_is_802_3(eth->h_proto)) | ||||||
|  | 			skb->protocol = eth->h_proto; | ||||||
|  | 		else | ||||||
|  | 			skb->protocol = htons(ETH_P_802_2); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return key_extract(skb, key); | 	return key_extract(skb, key); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -485,6 +485,25 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto) | ||||||
| { | { | ||||||
| 	int mtu = vport->dev->mtu; | 	int mtu = vport->dev->mtu; | ||||||
| 
 | 
 | ||||||
|  | 	switch (vport->dev->type) { | ||||||
|  | 	case ARPHRD_NONE: | ||||||
|  | 		if (mac_proto == MAC_PROTO_ETHERNET) { | ||||||
|  | 			skb_reset_network_header(skb); | ||||||
|  | 			skb_reset_mac_len(skb); | ||||||
|  | 			skb->protocol = htons(ETH_P_TEB); | ||||||
|  | 		} else if (mac_proto != MAC_PROTO_NONE) { | ||||||
|  | 			WARN_ON_ONCE(1); | ||||||
|  | 			goto drop; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case ARPHRD_ETHER: | ||||||
|  | 		if (mac_proto != MAC_PROTO_ETHERNET) | ||||||
|  | 			goto drop; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		goto drop; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (unlikely(packet_length(skb, vport->dev) > mtu && | 	if (unlikely(packet_length(skb, vport->dev) > mtu && | ||||||
| 		     !skb_is_gso(skb))) { | 		     !skb_is_gso(skb))) { | ||||||
| 		net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", | 		net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jiri Benc
						Jiri Benc