Skip to content
Snippets Groups Projects
flow.c 35.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		return -EINVAL;
    	attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET);
    
    	eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]);
    	memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN);
    	memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN);
    
    	if (attrs & (1u << OVS_KEY_ATTR_ETHERTYPE) &&
    	    nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) {
    		const struct nlattr *encap;
    		__be16 tci;
    
    		if (attrs != ((1 << OVS_KEY_ATTR_VLAN) |
    			      (1 << OVS_KEY_ATTR_ETHERTYPE) |
    			      (1 << OVS_KEY_ATTR_ENCAP)))
    			return -EINVAL;
    
    		encap = a[OVS_KEY_ATTR_ENCAP];
    		tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
    		if (tci & htons(VLAN_TAG_PRESENT)) {
    			swkey->eth.tci = tci;
    
    			err = parse_flow_nlattrs(encap, a, &attrs);
    			if (err)
    				return err;
    		} else if (!tci) {
    			/* Corner case for truncated 802.1Q header. */
    			if (nla_len(encap))
    				return -EINVAL;
    
    			swkey->eth.type = htons(ETH_P_8021Q);
    			*key_lenp = key_len;
    			return 0;
    		} else {
    			return -EINVAL;
    		}
    	}
    
    	if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
    		swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
    
    Simon Horman's avatar
    Simon Horman committed
    		if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
    
    			return -EINVAL;
    		attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
    	} else {
    		swkey->eth.type = htons(ETH_P_802_2);
    	}
    
    	if (swkey->eth.type == htons(ETH_P_IP)) {
    		const struct ovs_key_ipv4 *ipv4_key;
    
    		if (!(attrs & (1 << OVS_KEY_ATTR_IPV4)))
    			return -EINVAL;
    		attrs &= ~(1 << OVS_KEY_ATTR_IPV4);
    
    		key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
    		ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
    		if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX)
    			return -EINVAL;
    		swkey->ip.proto = ipv4_key->ipv4_proto;
    		swkey->ip.tos = ipv4_key->ipv4_tos;
    		swkey->ip.ttl = ipv4_key->ipv4_ttl;
    		swkey->ip.frag = ipv4_key->ipv4_frag;
    		swkey->ipv4.addr.src = ipv4_key->ipv4_src;
    		swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
    
    		if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
    			err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs);
    			if (err)
    				return err;
    		}
    	} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
    		const struct ovs_key_ipv6 *ipv6_key;
    
    		if (!(attrs & (1 << OVS_KEY_ATTR_IPV6)))
    			return -EINVAL;
    		attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
    
    		key_len = SW_FLOW_KEY_OFFSET(ipv6.label);
    		ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
    		if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX)
    			return -EINVAL;
    		swkey->ipv6.label = ipv6_key->ipv6_label;
    		swkey->ip.proto = ipv6_key->ipv6_proto;
    		swkey->ip.tos = ipv6_key->ipv6_tclass;
    		swkey->ip.ttl = ipv6_key->ipv6_hlimit;
    		swkey->ip.frag = ipv6_key->ipv6_frag;
    		memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src,
    		       sizeof(swkey->ipv6.addr.src));
    		memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst,
    		       sizeof(swkey->ipv6.addr.dst));
    
    		if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
    			err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs);
    			if (err)
    				return err;
    		}
    
    	} else if (swkey->eth.type == htons(ETH_P_ARP) ||
    		   swkey->eth.type == htons(ETH_P_RARP)) {
    
    		const struct ovs_key_arp *arp_key;
    
    		if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
    			return -EINVAL;
    		attrs &= ~(1 << OVS_KEY_ATTR_ARP);
    
    		key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
    		arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
    		swkey->ipv4.addr.src = arp_key->arp_sip;
    		swkey->ipv4.addr.dst = arp_key->arp_tip;
    		if (arp_key->arp_op & htons(0xff00))
    			return -EINVAL;
    		swkey->ip.proto = ntohs(arp_key->arp_op);
    		memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN);
    		memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN);
    	}
    
    	if (attrs)
    		return -EINVAL;
    	*key_lenp = key_len;
    
    	return 0;
    }
    
    /**
     * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
    
     * @priority: receives the skb priority
     * @mark: receives the skb mark
    
     * @in_port: receives the extracted input port.
     * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
     * sequence.
     *
     * This parses a series of Netlink attributes that form a flow key, which must
     * take the same form accepted by flow_from_nlattrs(), but only enough of it to
     * get the metadata, that is, the parts of the flow key that cannot be
     * extracted from the packet itself.
     */
    
    int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port,
    
    			       const struct nlattr *attr)
    {
    	const struct nlattr *nla;
    	int rem;
    
    
    	*priority = 0;
    
    
    	nla_for_each_nested(nla, attr, rem) {
    		int type = nla_type(nla);
    
    		if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) {
    			if (nla_len(nla) != ovs_key_lens[type])
    				return -EINVAL;
    
    			switch (type) {
    			case OVS_KEY_ATTR_PRIORITY:
    				*priority = nla_get_u32(nla);
    				break;
    
    			case OVS_KEY_ATTR_IN_PORT:
    				if (nla_get_u32(nla) >= DP_MAX_PORTS)
    					return -EINVAL;
    				*in_port = nla_get_u32(nla);
    				break;
    
    
    			case OVS_KEY_ATTR_SKB_MARK:
    				*mark = nla_get_u32(nla);
    				break;
    
    			}
    		}
    	}
    	if (rem)
    		return -EINVAL;
    	return 0;
    }
    
    int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
    {
    	struct ovs_key_ethernet *eth_key;
    	struct nlattr *nla, *encap;
    
    
    	if (swkey->phy.priority &&
    	    nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority))
    		goto nla_put_failure;
    
    	if (swkey->phy.in_port != DP_MAX_PORTS &&
    
    	    nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port))
    		goto nla_put_failure;
    
    	if (swkey->phy.skb_mark &&
    	    nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark))
    		goto nla_put_failure;
    
    
    	nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
    	if (!nla)
    		goto nla_put_failure;
    	eth_key = nla_data(nla);
    	memcpy(eth_key->eth_src, swkey->eth.src, ETH_ALEN);
    	memcpy(eth_key->eth_dst, swkey->eth.dst, ETH_ALEN);
    
    	if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
    
    		if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_P_8021Q)) ||
    		    nla_put_be16(skb, OVS_KEY_ATTR_VLAN, swkey->eth.tci))
    			goto nla_put_failure;
    
    		encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
    		if (!swkey->eth.tci)
    			goto unencap;
    	} else {
    		encap = NULL;
    	}
    
    	if (swkey->eth.type == htons(ETH_P_802_2))
    		goto unencap;
    
    
    	if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, swkey->eth.type))
    		goto nla_put_failure;
    
    
    	if (swkey->eth.type == htons(ETH_P_IP)) {
    		struct ovs_key_ipv4 *ipv4_key;
    
    		nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key));
    		if (!nla)
    			goto nla_put_failure;
    		ipv4_key = nla_data(nla);
    		ipv4_key->ipv4_src = swkey->ipv4.addr.src;
    		ipv4_key->ipv4_dst = swkey->ipv4.addr.dst;
    		ipv4_key->ipv4_proto = swkey->ip.proto;
    		ipv4_key->ipv4_tos = swkey->ip.tos;
    		ipv4_key->ipv4_ttl = swkey->ip.ttl;
    		ipv4_key->ipv4_frag = swkey->ip.frag;
    	} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
    		struct ovs_key_ipv6 *ipv6_key;
    
    		nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));
    		if (!nla)
    			goto nla_put_failure;
    		ipv6_key = nla_data(nla);
    		memcpy(ipv6_key->ipv6_src, &swkey->ipv6.addr.src,
    				sizeof(ipv6_key->ipv6_src));
    		memcpy(ipv6_key->ipv6_dst, &swkey->ipv6.addr.dst,
    				sizeof(ipv6_key->ipv6_dst));
    		ipv6_key->ipv6_label = swkey->ipv6.label;
    		ipv6_key->ipv6_proto = swkey->ip.proto;
    		ipv6_key->ipv6_tclass = swkey->ip.tos;
    		ipv6_key->ipv6_hlimit = swkey->ip.ttl;
    		ipv6_key->ipv6_frag = swkey->ip.frag;
    
    	} else if (swkey->eth.type == htons(ETH_P_ARP) ||
    		   swkey->eth.type == htons(ETH_P_RARP)) {
    
    		struct ovs_key_arp *arp_key;
    
    		nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key));
    		if (!nla)
    			goto nla_put_failure;
    		arp_key = nla_data(nla);
    		memset(arp_key, 0, sizeof(struct ovs_key_arp));
    		arp_key->arp_sip = swkey->ipv4.addr.src;
    		arp_key->arp_tip = swkey->ipv4.addr.dst;
    		arp_key->arp_op = htons(swkey->ip.proto);
    		memcpy(arp_key->arp_sha, swkey->ipv4.arp.sha, ETH_ALEN);
    		memcpy(arp_key->arp_tha, swkey->ipv4.arp.tha, ETH_ALEN);
    	}
    
    	if ((swkey->eth.type == htons(ETH_P_IP) ||
    	     swkey->eth.type == htons(ETH_P_IPV6)) &&
    	     swkey->ip.frag != OVS_FRAG_TYPE_LATER) {
    
    		if (swkey->ip.proto == IPPROTO_TCP) {
    			struct ovs_key_tcp *tcp_key;
    
    			nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key));
    			if (!nla)
    				goto nla_put_failure;
    			tcp_key = nla_data(nla);
    			if (swkey->eth.type == htons(ETH_P_IP)) {
    				tcp_key->tcp_src = swkey->ipv4.tp.src;
    				tcp_key->tcp_dst = swkey->ipv4.tp.dst;
    			} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
    				tcp_key->tcp_src = swkey->ipv6.tp.src;
    				tcp_key->tcp_dst = swkey->ipv6.tp.dst;
    			}
    		} else if (swkey->ip.proto == IPPROTO_UDP) {
    			struct ovs_key_udp *udp_key;
    
    			nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key));
    			if (!nla)
    				goto nla_put_failure;
    			udp_key = nla_data(nla);
    			if (swkey->eth.type == htons(ETH_P_IP)) {
    				udp_key->udp_src = swkey->ipv4.tp.src;
    				udp_key->udp_dst = swkey->ipv4.tp.dst;
    			} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
    				udp_key->udp_src = swkey->ipv6.tp.src;
    				udp_key->udp_dst = swkey->ipv6.tp.dst;
    			}
    		} else if (swkey->eth.type == htons(ETH_P_IP) &&
    			   swkey->ip.proto == IPPROTO_ICMP) {
    			struct ovs_key_icmp *icmp_key;
    
    			nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key));
    			if (!nla)
    				goto nla_put_failure;
    			icmp_key = nla_data(nla);
    			icmp_key->icmp_type = ntohs(swkey->ipv4.tp.src);
    			icmp_key->icmp_code = ntohs(swkey->ipv4.tp.dst);
    		} else if (swkey->eth.type == htons(ETH_P_IPV6) &&
    			   swkey->ip.proto == IPPROTO_ICMPV6) {
    			struct ovs_key_icmpv6 *icmpv6_key;
    
    			nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6,
    						sizeof(*icmpv6_key));
    			if (!nla)
    				goto nla_put_failure;
    			icmpv6_key = nla_data(nla);
    			icmpv6_key->icmpv6_type = ntohs(swkey->ipv6.tp.src);
    			icmpv6_key->icmpv6_code = ntohs(swkey->ipv6.tp.dst);
    
    			if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION ||
    			    icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
    				struct ovs_key_nd *nd_key;
    
    				nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key));
    				if (!nla)
    					goto nla_put_failure;
    				nd_key = nla_data(nla);
    				memcpy(nd_key->nd_target, &swkey->ipv6.nd.target,
    							sizeof(nd_key->nd_target));
    				memcpy(nd_key->nd_sll, swkey->ipv6.nd.sll, ETH_ALEN);
    				memcpy(nd_key->nd_tll, swkey->ipv6.nd.tll, ETH_ALEN);
    			}
    		}
    	}
    
    unencap:
    	if (encap)
    		nla_nest_end(skb, encap);
    
    	return 0;
    
    nla_put_failure:
    	return -EMSGSIZE;
    }
    
    /* Initializes the flow module.
     * Returns zero if successful or a negative error code. */
    int ovs_flow_init(void)
    {
    	flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0,
    					0, NULL);
    	if (flow_cache == NULL)
    		return -ENOMEM;
    
    	return 0;
    }
    
    /* Uninitializes the flow module. */
    void ovs_flow_exit(void)
    {
    	kmem_cache_destroy(flow_cache);
    }