+ {
+ int label, tc, ttl, bos;
+ int n = -1;
+
+ if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n",
+ &label, &tc, &ttl, &bos, &n) > 0 &&
+ n > 0) {
+ struct ovs_key_mpls *mpls;
+
+ mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_top_lse = mpls_lse_from_components(label, tc, ttl, bos);
+ return n;
+ }
+ }
+
+ {
+ ovs_be32 ipv4_src;
+ ovs_be32 ipv4_dst;
+ int ipv4_proto;
+ int ipv4_tos;
+ int ipv4_ttl;
+ char frag[8];
+ enum ovs_frag_type ipv4_frag;
+ int n = -1;
+
+ if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
+ "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
+ IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
+ &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
+ && n > 0
+ && ovs_frag_type_from_string(frag, &ipv4_frag)) {
+ struct ovs_key_ipv4 ipv4_key;
+
+ ipv4_key.ipv4_src = ipv4_src;
+ ipv4_key.ipv4_dst = ipv4_dst;
+ ipv4_key.ipv4_proto = ipv4_proto;
+ ipv4_key.ipv4_tos = ipv4_tos;
+ ipv4_key.ipv4_ttl = ipv4_ttl;
+ ipv4_key.ipv4_frag = ipv4_frag;
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof ipv4_key);
+ return n;
+ }
+ }
+
+ {
+ char ipv6_src_s[IPV6_SCAN_LEN + 1];
+ char ipv6_dst_s[IPV6_SCAN_LEN + 1];
+ int ipv6_label;
+ int ipv6_proto;
+ int ipv6_tclass;
+ int ipv6_hlimit;
+ char frag[8];
+ enum ovs_frag_type ipv6_frag;
+ int n = -1;
+
+ if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
+ "label=%i,proto=%i,tclass=%i,hlimit=%i,frag=%7[a-z])%n",
+ ipv6_src_s, ipv6_dst_s, &ipv6_label,
+ &ipv6_proto, &ipv6_tclass, &ipv6_hlimit, frag, &n) > 0
+ && n > 0
+ && ovs_frag_type_from_string(frag, &ipv6_frag)) {
+ struct ovs_key_ipv6 ipv6_key;
+
+ if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
+ inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) {
+ return -EINVAL;
+ }
+ ipv6_key.ipv6_label = htonl(ipv6_label);
+ ipv6_key.ipv6_proto = ipv6_proto;
+ ipv6_key.ipv6_tclass = ipv6_tclass;
+ ipv6_key.ipv6_hlimit = ipv6_hlimit;
+ ipv6_key.ipv6_frag = ipv6_frag;
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
+ &ipv6_key, sizeof ipv6_key);
+ return n;
+ }
+ }
+
+ {
+ int tcp_src;
+ int tcp_dst;
+ int n = -1;
+
+ if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0
+ && n > 0) {
+ struct ovs_key_tcp tcp_key;
+
+ tcp_key.tcp_src = htons(tcp_src);
+ tcp_key.tcp_dst = htons(tcp_dst);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_TCP, &tcp_key, sizeof tcp_key);
+ return n;
+ }
+ }
+
+ {
+ int udp_src;
+ int udp_dst;
+ int n = -1;
+
+ if (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0
+ && n > 0) {
+ struct ovs_key_udp udp_key;
+
+ udp_key.udp_src = htons(udp_src);
+ udp_key.udp_dst = htons(udp_dst);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
+ return n;
+ }
+ }
+
+ {
+ int icmp_type;
+ int icmp_code;
+ int n = -1;
+
+ if (sscanf(s, "icmp(type=%i,code=%i)%n",
+ &icmp_type, &icmp_code, &n) > 0
+ && n > 0) {
+ struct ovs_key_icmp icmp_key;
+
+ icmp_key.icmp_type = icmp_type;
+ icmp_key.icmp_code = icmp_code;
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
+ &icmp_key, sizeof icmp_key);
+ return n;
+ }
+ }
+
+ {
+ struct ovs_key_icmpv6 icmpv6_key;
+ int n = -1;
+
+ if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
+ &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,&n) > 0
+ && n > 0) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
+ &icmpv6_key, sizeof icmpv6_key);
+ return n;
+ }
+ }
+
+ {
+ ovs_be32 arp_sip;
+ ovs_be32 arp_tip;
+ int arp_op;
+ uint8_t arp_sha[ETH_ADDR_LEN];
+ uint8_t arp_tha[ETH_ADDR_LEN];
+ int n = -1;
+
+ if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
+ "op=%i,sha="ETH_ADDR_SCAN_FMT",tha="ETH_ADDR_SCAN_FMT")%n",
+ IP_SCAN_ARGS(&arp_sip),
+ IP_SCAN_ARGS(&arp_tip),
+ &arp_op,
+ ETH_ADDR_SCAN_ARGS(arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_tha), &n) > 0 && n > 0) {
+ struct ovs_key_arp arp_key;
+
+ memset(&arp_key, 0, sizeof arp_key);
+ arp_key.arp_sip = arp_sip;
+ arp_key.arp_tip = arp_tip;
+ arp_key.arp_op = htons(arp_op);
+ memcpy(arp_key.arp_sha, arp_sha, ETH_ADDR_LEN);
+ memcpy(arp_key.arp_tha, arp_tha, ETH_ADDR_LEN);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+ return n;
+ }
+ }
+
+ {
+ char nd_target_s[IPV6_SCAN_LEN + 1];
+ uint8_t nd_sll[ETH_ADDR_LEN];
+ uint8_t nd_tll[ETH_ADDR_LEN];
+ int n = -1;
+
+ if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n",
+ nd_target_s, &n) > 0 && n > 0) {
+ return put_nd_key(n, nd_target_s, NULL, NULL, key);
+ }
+ if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n) > 0
+ && n > 0) {
+ return put_nd_key(n, nd_target_s, nd_sll, NULL, key);
+ }
+ if (sscanf(s, "nd(target="IPV6_SCAN_FMT",tll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
+ && n > 0) {
+ return put_nd_key(n, nd_target_s, NULL, nd_tll, key);
+ }
+ if (sscanf(s, "nd(target="IPV6_SCAN_FMT",sll="ETH_ADDR_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_tll), &n) > 0
+ && n > 0) {
+ return put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+ }
+ }
+
+ if (!strncmp(s, "encap(", 6)) {
+ const char *start = s;
+ size_t encap;
+
+ encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
+
+ s += 6;
+ for (;;) {
+ int retval;
+
+ s += strspn(s, ", \t\r\n");
+ if (!*s) {
+ return -EINVAL;
+ } else if (*s == ')') {
+ break;
+ }
+
+ retval = parse_odp_key_attr(s, port_names, key);
+ if (retval < 0) {
+ return retval;
+ }
+ s += retval;
+ }
+ s++;
+
+ nl_msg_end_nested(key, encap);
+
+ return s - start;
+ }
+
+ return -EINVAL;
+}
+
+/* Parses the string representation of a datapath flow key, in the
+ * format output by odp_flow_key_format(). Returns 0 if successful,
+ * otherwise a positive errno value. On success, the flow key is
+ * appended to 'key' as a series of Netlink attributes. On failure, no
+ * data is appended to 'key'. Either way, 'key''s data might be
+ * reallocated.
+ *
+ * If 'port_names' is nonnull, it points to an simap that maps from a port name
+ * to a port number. (Port names may be used instead of port numbers in
+ * in_port.)
+ *
+ * On success, the attributes appended to 'key' are individually syntactically
+ * valid, but they may not be valid as a sequence. 'key' might, for example,
+ * have duplicated keys. odp_flow_key_to_flow() will detect those errors. */
+int
+odp_flow_key_from_string(const char *s, const struct simap *port_names,
+ struct ofpbuf *key)
+{
+ const size_t old_size = key->size;
+ for (;;) {
+ int retval;
+
+ s += strspn(s, delimiters);
+ if (!*s) {
+ return 0;
+ }
+
+ retval = parse_odp_key_attr(s, port_names, key);
+ if (retval < 0) {
+ key->size = old_size;
+ return -retval;
+ }
+ s += retval;
+ }
+
+ return 0;
+}
+
+static uint8_t
+ovs_to_odp_frag(uint8_t nw_frag)
+{
+ return (nw_frag == 0 ? OVS_FRAG_TYPE_NONE
+ : nw_frag == FLOW_NW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST
+ : OVS_FRAG_TYPE_LATER);
+}
+
+/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
+ * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
+ * number rather than a datapath port number). Instead, if 'odp_in_port'
+ * is anything other than OVSP_NONE, it is included in 'buf' as the input
+ * port.
+ *
+ * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
+ * capable of being expanded to allow for that much space. */
+void
+odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
+ uint32_t odp_in_port)
+{
+ struct ovs_key_ethernet *eth_key;
+ size_t encap;
+
+ if (flow->skb_priority) {
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, flow->skb_priority);
+ }
+
+ if (flow->tunnel.ip_dst) {
+ tun_key_to_attr(buf, &flow->tunnel);
+ }
+
+ if (flow->skb_mark) {
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, flow->skb_mark);
+ }
+
+ if (odp_in_port != OVSP_NONE) {
+ nl_msg_put_u32(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
+ }
+
+ eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
+ sizeof *eth_key);
+ memcpy(eth_key->eth_src, flow->dl_src, ETH_ADDR_LEN);
+ memcpy(eth_key->eth_dst, flow->dl_dst, ETH_ADDR_LEN);
+
+ if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, flow->vlan_tci);
+ encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
+ if (flow->vlan_tci == htons(0)) {
+ goto unencap;
+ }
+ } else {
+ encap = 0;
+ }
+
+ if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
+ goto unencap;
+ }
+
+ nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, flow->dl_type);
+
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ struct ovs_key_ipv4 *ipv4_key;
+
+ ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
+ sizeof *ipv4_key);
+ ipv4_key->ipv4_src = flow->nw_src;
+ ipv4_key->ipv4_dst = flow->nw_dst;
+ ipv4_key->ipv4_proto = flow->nw_proto;
+ ipv4_key->ipv4_tos = flow->nw_tos;
+ ipv4_key->ipv4_ttl = flow->nw_ttl;
+ ipv4_key->ipv4_frag = ovs_to_odp_frag(flow->nw_frag);
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ struct ovs_key_ipv6 *ipv6_key;
+
+ ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
+ sizeof *ipv6_key);
+ memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src);
+ memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst);
+ ipv6_key->ipv6_label = flow->ipv6_label;
+ ipv6_key->ipv6_proto = flow->nw_proto;
+ ipv6_key->ipv6_tclass = flow->nw_tos;
+ ipv6_key->ipv6_hlimit = flow->nw_ttl;
+ ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->nw_frag);
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
+ struct ovs_key_arp *arp_key;
+
+ arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
+ sizeof *arp_key);
+ memset(arp_key, 0, sizeof *arp_key);
+ arp_key->arp_sip = flow->nw_src;
+ arp_key->arp_tip = flow->nw_dst;
+ arp_key->arp_op = htons(flow->nw_proto);
+ memcpy(arp_key->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(arp_key->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+ }
+
+ if (flow->mpls_depth) {
+ struct ovs_key_mpls *mpls_key;
+
+ mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls_key);
+ mpls_key->mpls_top_lse = flow->mpls_lse;
+ }
+
+ if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
+ if (flow->nw_proto == IPPROTO_TCP) {
+ struct ovs_key_tcp *tcp_key;
+
+ tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP,
+ sizeof *tcp_key);
+ tcp_key->tcp_src = flow->tp_src;
+ tcp_key->tcp_dst = flow->tp_dst;
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ struct ovs_key_udp *udp_key;
+
+ udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP,