X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fflow.c;h=fe05df324c529af3b10f771631d074f93c09ff8f;hb=0b8b6f71d5621a726a3bf5aa1bbee27ed1f4a8fe;hp=4365e22c3549c38136da373389157b4afe974811;hpb=d31f1109f10e5ffb9bf266306b913ebf23781666;p=openvswitch diff --git a/datapath/flow.c b/datapath/flow.c index 4365e22c..fe05df32 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -32,6 +32,9 @@ #include #include #include +#include + +#include "vlan.h" static struct kmem_cache *flow_cache; static unsigned int hash_seed __read_mostly; @@ -314,6 +317,75 @@ static __be16 parse_ethertype(struct sk_buff *skb) return llc->ethertype; } +static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, + int nh_len) +{ + struct ipv6hdr *nh = ipv6_hdr(skb); + int icmp_len = ntohs(nh->payload_len) + sizeof(*nh) - nh_len; + struct icmp6hdr *icmp = icmp6_hdr(skb); + + /* The ICMPv6 type and code fields use the 16-bit transport port + * fields, so we need to store them in 16-bit network byte order. */ + key->tp_src = htons(icmp->icmp6_type); + key->tp_dst = htons(icmp->icmp6_code); + + if (!icmp->icmp6_code + && ((icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) + || (icmp->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT))) { + struct nd_msg *nd; + int offset; + + /* In order to process neighbor discovery options, we need the + * entire packet. */ + if (icmp_len < sizeof(*nd)) + goto invalid; + if (!pskb_may_pull(skb, skb_transport_offset(skb) + icmp_len)) + return -ENOMEM; + + nd = (struct nd_msg *)skb_transport_header(skb); + memcpy(key->nd_target, &nd->target, sizeof(key->nd_target)); + + icmp_len -= sizeof(*nd); + offset = 0; + while (icmp_len >= 8) { + struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd->opt + offset); + int opt_len = nd_opt->nd_opt_len * 8; + + if (!opt_len || (opt_len > icmp_len)) + goto invalid; + + /* Store the link layer address if the appropriate option is + * provided. It is considered an error if the same link + * layer option is specified twice. */ + if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LL_ADDR + && opt_len == 8) { + if (!is_zero_ether_addr(key->arp_sha)) + goto invalid; + memcpy(key->arp_sha, + &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN); + } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LL_ADDR + && opt_len == 8) { + if (!is_zero_ether_addr(key->arp_tha)) + goto invalid; + memcpy(key->arp_tha, + &nd->opt[offset+sizeof(*nd_opt)], ETH_ALEN); + } + + icmp_len -= opt_len; + offset += opt_len; + } + } + + return 0; + +invalid: + memset(key->nd_target, 0, sizeof(key->nd_target)); + memset(key->arp_sha, 0, sizeof(key->arp_sha)); + memset(key->arp_tha, 0, sizeof(key->arp_tha)); + + return 0; +} + /** * flow_extract - extracts a flow key from an Ethernet frame. * @skb: sk_buff that contains the frame, with skb->data pointing to the @@ -379,8 +451,12 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, /* dl_type, dl_vlan, dl_vlan_pcp. */ __skb_pull(skb, 2 * ETH_ALEN); - if (eth->h_proto == htons(ETH_P_8021Q)) + + if (vlan_tx_tag_present(skb)) + key->dl_tci = htons(vlan_get_tci(skb)); + else if (eth->h_proto == htons(ETH_P_8021Q)) parse_vlan(skb, key); + key->dl_type = parse_ethertype(skb); skb_reset_network_header(skb); __skb_push(skb, skb->data - (unsigned char *)eth); @@ -482,12 +558,9 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, } } else if (key->nw_proto == NEXTHDR_ICMP) { if (icmp6hdr_ok(skb)) { - struct icmp6hdr *icmp = icmp6_hdr(skb); - /* The ICMPv6 type and code fields use the 16-bit - * transport port fields, so we need to store them - * in 16-bit network byte order. */ - key->tp_src = htons(icmp->icmp6_type); - key->tp_dst = htons(icmp->icmp6_code); + int error = parse_icmpv6(skb, key, nh_len); + if (error < 0) + return error; } } } @@ -517,7 +590,7 @@ int flow_cmp(const struct tbl_node *node, void *key2_) * elements and | for alternatives: * * [tun_id] in_port ethernet [8021q] [ethertype \ - * [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6] | ARP]] + * [IPv4 [TCP|UDP|ICMP] | IPv6 [TCP|UDP|ICMPv6 [ND]] | ARP]] */ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) { @@ -543,6 +616,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) [ODP_KEY_ATTR_ICMP] = sizeof(struct odp_key_icmp), [ODP_KEY_ATTR_ICMPV6] = sizeof(struct odp_key_icmpv6), [ODP_KEY_ATTR_ARP] = sizeof(struct odp_key_arp), + [ODP_KEY_ATTR_ND] = sizeof(struct odp_key_nd), }; const struct odp_key_ethernet *eth_key; @@ -554,6 +628,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) const struct odp_key_icmp *icmp_key; const struct odp_key_icmpv6 *icmpv6_key; const struct odp_key_arp *arp_key; + const struct odp_key_nd *nd_key; int type = nla_type(nla); @@ -669,6 +744,17 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) memcpy(swkey->arp_tha, arp_key->arp_tha, ETH_ALEN); break; + case TRANSITION(ODP_KEY_ATTR_ICMPV6, ODP_KEY_ATTR_ND): + if (swkey->tp_src != htons(NDISC_NEIGHBOUR_SOLICITATION) + && swkey->tp_src != htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) + return -EINVAL; + nd_key = nla_data(nla); + memcpy(swkey->nd_target, nd_key->nd_target, + sizeof(swkey->nd_target)); + memcpy(swkey->arp_sha, nd_key->nd_sll, ETH_ALEN); + memcpy(swkey->arp_tha, nd_key->nd_tll, ETH_ALEN); + break; + default: return -EINVAL; } @@ -710,11 +796,17 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) return -EINVAL; return 0; + case ODP_KEY_ATTR_ICMPV6: + if (swkey->tp_src == htons(NDISC_NEIGHBOUR_SOLICITATION) || + swkey->tp_src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) + return -EINVAL; + return 0; + case ODP_KEY_ATTR_TCP: case ODP_KEY_ATTR_UDP: case ODP_KEY_ATTR_ICMP: - case ODP_KEY_ATTR_ICMPV6: case ODP_KEY_ATTR_ARP: + case ODP_KEY_ATTR_ND: return 0; } @@ -727,6 +819,11 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) struct odp_key_ethernet *eth_key; struct nlattr *nla; + /* This is an imperfect sanity-check that FLOW_BUFSIZE doesn't need + * to be updated, but will at least raise awareness when new ODP key + * types are added. */ + BUILD_BUG_ON(__ODP_KEY_ATTR_MAX != 14); + if (swkey->tun_id != cpu_to_be64(0)) NLA_PUT_BE64(skb, ODP_KEY_ATTR_TUN_ID, swkey->tun_id); @@ -759,6 +856,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv4_key = nla_data(nla); + memset(ipv4_key, 0, sizeof(struct odp_key_ipv4)); ipv4_key->ipv4_src = swkey->ipv4_src; ipv4_key->ipv4_dst = swkey->ipv4_dst; ipv4_key->ipv4_proto = swkey->nw_proto; @@ -770,6 +868,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv6_key = nla_data(nla); + memset(ipv6_key, 0, sizeof(struct odp_key_ipv6)); memcpy(ipv6_key->ipv6_src, swkey->ipv6_src, sizeof(ipv6_key->ipv6_src)); memcpy(ipv6_key->ipv6_dst, swkey->ipv6_dst, @@ -783,6 +882,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; arp_key = nla_data(nla); + memset(arp_key, 0, sizeof(struct odp_key_arp)); arp_key->arp_sip = swkey->ipv4_src; arp_key->arp_tip = swkey->ipv4_dst; arp_key->arp_op = htons(swkey->nw_proto); @@ -831,6 +931,20 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) icmpv6_key = nla_data(nla); icmpv6_key->icmpv6_type = ntohs(swkey->tp_src); icmpv6_key->icmpv6_code = ntohs(swkey->tp_dst); + + if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION + || icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { + struct odp_key_nd *nd_key; + + nla = nla_reserve(skb, ODP_KEY_ATTR_ND, sizeof(*nd_key)); + if (!nla) + goto nla_put_failure; + nd_key = nla_data(nla); + memcpy(nd_key->nd_target, swkey->nd_target, + sizeof(nd_key->nd_target)); + memcpy(nd_key->nd_sll, swkey->arp_sha, ETH_ALEN); + memcpy(nd_key->nd_tll, swkey->arp_tha, ETH_ALEN); + } } }