X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fflow.c;h=f264866a7eef793a107f24f3cd919212528672cd;hb=aac19178a43f5ed3367e2d7a7a19ae34fdf56cfb;hp=4365e22c3549c38136da373389157b4afe974811;hpb=d31f1109f10e5ffb9bf266306b913ebf23781666;p=openvswitch diff --git a/datapath/flow.c b/datapath/flow.c index 4365e22c..f264866a 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; @@ -103,7 +106,6 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) unsigned int nh_ofs = skb_network_offset(skb); unsigned int nh_len; int payload_ofs; - int payload_len; struct ipv6hdr *nh; uint8_t nexthdr; @@ -113,30 +115,17 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) nh = ipv6_hdr(skb); nexthdr = nh->nexthdr; payload_ofs = (u8 *)(nh + 1) - skb->data; - payload_len = ntohs(nh->payload_len); - memcpy(key->ipv6_src, nh->saddr.in6_u.u6_addr8, sizeof(key->ipv6_src)); - memcpy(key->ipv6_dst, nh->daddr.in6_u.u6_addr8, sizeof(key->ipv6_dst)); + ipv6_addr_copy(&key->ipv6_src, &nh->saddr); + ipv6_addr_copy(&key->ipv6_dst, &nh->daddr); key->nw_tos = ipv6_get_dsfield(nh) & ~INET_ECN_MASK; key->nw_proto = NEXTHDR_NONE; - /* We don't process jumbograms. */ - if (!payload_len) - return -EINVAL; - - if (unlikely(skb->len < nh_ofs + sizeof(*nh) + payload_len)) - return -EINVAL; - payload_ofs = ipv6_skip_exthdr(skb, payload_ofs, &nexthdr); - if (payload_ofs < 0) { + if (unlikely(payload_ofs < 0)) return -EINVAL; - } - nh_len = payload_ofs - nh_ofs; - /* Ensure that the payload length claimed is at least large enough - * for the headers we've already processed. */ - if (payload_len < nh_len - sizeof(*nh)) - return -EINVAL; + nh_len = payload_ofs - nh_ofs; /* Pull enough header bytes to account for the IP header plus the * longest transport header that we parse, currently 20 bytes for TCP. @@ -314,6 +303,77 @@ 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 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 == 0 && + (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION || + icmp->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT)) { + int icmp_len = skb->len - skb_transport_offset(skb); + struct nd_msg *nd; + int offset; + + /* In order to process neighbor discovery options, we need the + * entire packet. + */ + if (unlikely(icmp_len < sizeof(*nd))) + return 0; + if (unlikely(skb_linearize(skb))) + return -ENOMEM; + + nd = (struct nd_msg *)skb_transport_header(skb); + ipv6_addr_copy(&key->nd_target, &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 (unlikely(!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 (unlikely(!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 (unlikely(!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 +439,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 +546,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 +578,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 +604,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 +616,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); @@ -612,9 +675,9 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, const struct nlattr *attr) if (swkey->dl_type != htons(ETH_P_IPV6)) return -EINVAL; ipv6_key = nla_data(nla); - memcpy(swkey->ipv6_src, ipv6_key->ipv6_src, + memcpy(&swkey->ipv6_src, ipv6_key->ipv6_src, sizeof(swkey->ipv6_src)); - memcpy(swkey->ipv6_dst, ipv6_key->ipv6_dst, + memcpy(&swkey->ipv6_dst, ipv6_key->ipv6_dst, sizeof(swkey->ipv6_dst)); swkey->nw_proto = ipv6_key->ipv6_proto; swkey->nw_tos = ipv6_key->ipv6_tos; @@ -669,6 +732,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 +784,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 +807,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 +844,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,9 +856,10 @@ 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); - memcpy(ipv6_key->ipv6_src, swkey->ipv6_src, + 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, + memcpy(ipv6_key->ipv6_dst, &swkey->ipv6_dst, sizeof(ipv6_key->ipv6_dst)); ipv6_key->ipv6_proto = swkey->nw_proto; ipv6_key->ipv6_tos = swkey->nw_tos; @@ -783,6 +870,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); @@ -790,8 +878,8 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) memcpy(arp_key->arp_tha, swkey->arp_tha, ETH_ALEN); } - if (swkey->dl_type == htons(ETH_P_IP) - || swkey->dl_type == htons(ETH_P_IPV6)) { + if (swkey->dl_type == htons(ETH_P_IP) || + swkey->dl_type == htons(ETH_P_IPV6)) { if (swkey->nw_proto == IPPROTO_TCP) { struct odp_key_tcp *tcp_key; @@ -811,8 +899,8 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) udp_key = nla_data(nla); udp_key->udp_src = swkey->tp_src; udp_key->udp_dst = swkey->tp_dst; - } else if (swkey->dl_type == htons(ETH_P_IP) - && swkey->nw_proto == IPPROTO_ICMP) { + } else if (swkey->dl_type == htons(ETH_P_IP) && + swkey->nw_proto == IPPROTO_ICMP) { struct odp_key_icmp *icmp_key; nla = nla_reserve(skb, ODP_KEY_ATTR_ICMP, sizeof(*icmp_key)); @@ -821,16 +909,31 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) icmp_key = nla_data(nla); icmp_key->icmp_type = ntohs(swkey->tp_src); icmp_key->icmp_code = ntohs(swkey->tp_dst); - } else if (swkey->dl_type == htons(ETH_P_IPV6) - && swkey->nw_proto == IPPROTO_ICMPV6) { + } else if (swkey->dl_type == htons(ETH_P_IPV6) && + swkey->nw_proto == IPPROTO_ICMPV6) { struct odp_key_icmpv6 *icmpv6_key; - nla = nla_reserve(skb, ODP_KEY_ATTR_ICMPV6, sizeof(*icmpv6_key)); + nla = nla_reserve(skb, ODP_KEY_ATTR_ICMPV6, + sizeof(*icmpv6_key)); if (!nla) goto nla_put_failure; 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); + } } }