+ s += strspn(s, delimiters);
+ if (!*s) {
+ return 0;
+ }
+
+ retval = parse_odp_action(s, port_names, actions);
+ if (retval < 0 || !strchr(delimiters, s[retval])) {
+ ofpbuf_set_size(actions, old_size);
+ return -retval;
+ }
+ s += retval;
+ }
+
+ return 0;
+}
+\f
+/* Returns the correct length of the payload for a flow key attribute of the
+ * specified 'type', -1 if 'type' is unknown, or -2 if the attribute's payload
+ * is variable length. */
+static int
+odp_flow_key_attr_len(uint16_t type)
+{
+ if (type > OVS_KEY_ATTR_MAX) {
+ return -1;
+ }
+
+ switch ((enum ovs_key_attr) type) {
+ case OVS_KEY_ATTR_ENCAP: return -2;
+ case OVS_KEY_ATTR_PRIORITY: return 4;
+ case OVS_KEY_ATTR_SKB_MARK: return 4;
+ case OVS_KEY_ATTR_DP_HASH: return 4;
+ case OVS_KEY_ATTR_RECIRC_ID: return 4;
+ case OVS_KEY_ATTR_TUNNEL: return -2;
+ case OVS_KEY_ATTR_IN_PORT: return 4;
+ case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
+ case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
+ case OVS_KEY_ATTR_ETHERTYPE: return 2;
+ case OVS_KEY_ATTR_MPLS: return -2;
+ case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
+ case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
+ case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
+ case OVS_KEY_ATTR_TCP_FLAGS: return 2;
+ case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
+ case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
+ case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
+ case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6);
+ case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp);
+ case OVS_KEY_ATTR_ND: return sizeof(struct ovs_key_nd);
+
+ case OVS_KEY_ATTR_UNSPEC:
+ case __OVS_KEY_ATTR_MAX:
+ return -1;
+ }
+
+ return -1;
+}
+
+static void
+format_generic_odp_key(const struct nlattr *a, struct ds *ds)
+{
+ size_t len = nl_attr_get_size(a);
+ if (len) {
+ const uint8_t *unspec;
+ unsigned int i;
+
+ unspec = nl_attr_get(a);
+ for (i = 0; i < len; i++) {
+ if (i) {
+ ds_put_char(ds, ' ');
+ }
+ ds_put_format(ds, "%02x", unspec[i]);
+ }
+ }
+}
+
+static const char *
+ovs_frag_type_to_string(enum ovs_frag_type type)
+{
+ switch (type) {
+ case OVS_FRAG_TYPE_NONE:
+ return "no";
+ case OVS_FRAG_TYPE_FIRST:
+ return "first";
+ case OVS_FRAG_TYPE_LATER:
+ return "later";
+ case __OVS_FRAG_TYPE_MAX:
+ default:
+ return "<error>";
+ }
+}
+
+static int
+tunnel_key_attr_len(int type)
+{
+ switch (type) {
+ case OVS_TUNNEL_KEY_ATTR_ID: return 8;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: return 4;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_DST: return 4;
+ case OVS_TUNNEL_KEY_ATTR_TOS: return 1;
+ case OVS_TUNNEL_KEY_ATTR_TTL: return 1;
+ case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: return 0;
+ case OVS_TUNNEL_KEY_ATTR_CSUM: return 0;
+ case __OVS_TUNNEL_KEY_ATTR_MAX:
+ return -1;
+ }
+ return -1;
+}
+
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+ unsigned int left;
+ const struct nlattr *a;
+ bool ttl = false;
+ bool unknown = false;
+
+ NL_NESTED_FOR_EACH(a, left, attr) {
+ uint16_t type = nl_attr_type(a);
+ size_t len = nl_attr_get_size(a);
+ int expected_len = tunnel_key_attr_len(type);
+
+ if (len != expected_len && expected_len >= 0) {
+ return ODP_FIT_ERROR;
+ }
+
+ switch (type) {
+ case OVS_TUNNEL_KEY_ATTR_ID:
+ tun->tun_id = nl_attr_get_be64(a);
+ tun->flags |= FLOW_TNL_F_KEY;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+ tun->ip_src = nl_attr_get_be32(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+ tun->ip_dst = nl_attr_get_be32(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_TOS:
+ tun->ip_tos = nl_attr_get_u8(a);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_TTL:
+ tun->ip_ttl = nl_attr_get_u8(a);
+ ttl = true;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+ tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
+ break;
+ case OVS_TUNNEL_KEY_ATTR_CSUM:
+ tun->flags |= FLOW_TNL_F_CSUM;
+ break;
+ default:
+ /* Allow this to show up as unexpected, if there are unknown
+ * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
+ unknown = true;
+ break;
+ }
+ }
+
+ if (!ttl) {
+ return ODP_FIT_ERROR;
+ }
+ if (unknown) {
+ return ODP_FIT_TOO_MUCH;
+ }
+ return ODP_FIT_PERFECT;
+}
+
+static void
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+{
+ size_t tun_key_ofs;
+
+ tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
+
+ /* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */
+ if (tun_key->tun_id || tun_key->flags & FLOW_TNL_F_KEY) {
+ nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
+ }
+ if (tun_key->ip_src) {
+ nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src);
+ }
+ if (tun_key->ip_dst) {
+ nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
+ }
+ if (tun_key->ip_tos) {
+ nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
+ }
+ nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl);
+ if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
+ nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
+ }
+ if (tun_key->flags & FLOW_TNL_F_CSUM) {
+ nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
+ }
+
+ nl_msg_end_nested(a, tun_key_ofs);
+}
+
+static bool
+odp_mask_attr_is_wildcard(const struct nlattr *ma)
+{
+ return is_all_zeros(nl_attr_get(ma), nl_attr_get_size(ma));
+}
+
+static bool
+odp_mask_attr_is_exact(const struct nlattr *ma)
+{
+ bool is_exact = false;
+ enum ovs_key_attr attr = nl_attr_type(ma);
+
+ if (attr == OVS_KEY_ATTR_TUNNEL) {
+ /* XXX this is a hack for now. Should change
+ * the exact match dection to per field
+ * instead of per attribute.
+ */
+ struct flow_tnl tun_mask;
+ memset(&tun_mask, 0, sizeof tun_mask);
+ odp_tun_key_from_attr(ma, &tun_mask);
+ if (tun_mask.flags == (FLOW_TNL_F_KEY
+ | FLOW_TNL_F_DONT_FRAGMENT
+ | FLOW_TNL_F_CSUM)) {
+ /* The flags are exact match, check the remaining fields. */
+ tun_mask.flags = 0xffff;
+ is_exact = is_all_ones((uint8_t *)&tun_mask,
+ offsetof(struct flow_tnl, ip_ttl));
+ }
+ } else {
+ is_exact = is_all_ones(nl_attr_get(ma), nl_attr_get_size(ma));
+ }
+
+ return is_exact;
+}
+
+void
+odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no,
+ char *port_name)
+{
+ struct odp_portno_names *odp_portno_names;
+
+ odp_portno_names = xmalloc(sizeof *odp_portno_names);
+ odp_portno_names->port_no = port_no;
+ odp_portno_names->name = xstrdup(port_name);
+ hmap_insert(portno_names, &odp_portno_names->hmap_node,
+ hash_odp_port(port_no));
+}
+
+static char *
+odp_portno_names_get(const struct hmap *portno_names, odp_port_t port_no)
+{
+ struct odp_portno_names *odp_portno_names;
+
+ HMAP_FOR_EACH_IN_BUCKET (odp_portno_names, hmap_node,
+ hash_odp_port(port_no), portno_names) {
+ if (odp_portno_names->port_no == port_no) {
+ return odp_portno_names->name;
+ }
+ }
+ return NULL;
+}
+
+void
+odp_portno_names_destroy(struct hmap *portno_names)
+{
+ struct odp_portno_names *odp_portno_names, *odp_portno_names_next;
+ HMAP_FOR_EACH_SAFE (odp_portno_names, odp_portno_names_next,
+ hmap_node, portno_names) {
+ hmap_remove(portno_names, &odp_portno_names->hmap_node);
+ free(odp_portno_names->name);
+ free(odp_portno_names);
+ }
+}
+
+static void
+format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
+ const struct hmap *portno_names, struct ds *ds,
+ bool verbose)
+{
+ struct flow_tnl tun_key;
+ enum ovs_key_attr attr = nl_attr_type(a);
+ char namebuf[OVS_KEY_ATTR_BUFSIZE];
+ int expected_len;
+ bool is_exact;
+
+ is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
+
+ ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
+
+ {
+ expected_len = odp_flow_key_attr_len(nl_attr_type(a));
+ if (expected_len != -2) {
+ bool bad_key_len = nl_attr_get_size(a) != expected_len;
+ bool bad_mask_len = ma && nl_attr_get_size(ma) != expected_len;
+
+ if (bad_key_len || bad_mask_len) {
+ if (bad_key_len) {
+ ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(",
+ nl_attr_get_size(a), expected_len);
+ }
+ format_generic_odp_key(a, ds);
+ if (ma) {
+ ds_put_char(ds, '/');
+ if (bad_mask_len) {
+ ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected %d)(",
+ nl_attr_get_size(ma), expected_len);
+ }
+ format_generic_odp_key(ma, ds);
+ }
+ ds_put_char(ds, ')');
+ return;
+ }
+ }
+ }
+
+ ds_put_char(ds, '(');
+ switch (attr) {
+ case OVS_KEY_ATTR_ENCAP:
+ if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
+ odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
+ nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds,
+ verbose);
+ } else if (nl_attr_get_size(a)) {
+ odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL,
+ ds, verbose);
+ }
+ break;
+
+ case OVS_KEY_ATTR_PRIORITY:
+ case OVS_KEY_ATTR_SKB_MARK:
+ case OVS_KEY_ATTR_DP_HASH:
+ case OVS_KEY_ATTR_RECIRC_ID:
+ ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
+ if (!is_exact) {
+ ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TUNNEL:
+ memset(&tun_key, 0, sizeof tun_key);
+ if (odp_tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+ ds_put_format(ds, "error");
+ } else if (!is_exact) {
+ struct flow_tnl tun_mask;
+
+ memset(&tun_mask, 0, sizeof tun_mask);
+ odp_tun_key_from_attr(ma, &tun_mask);
+ ds_put_format(ds, "tun_id=%#"PRIx64"/%#"PRIx64
+ ",src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+ ",tos=%#"PRIx8"/%#"PRIx8",ttl=%"PRIu8"/%#"PRIx8
+ ",flags(",
+ ntohll(tun_key.tun_id), ntohll(tun_mask.tun_id),
+ IP_ARGS(tun_key.ip_src), IP_ARGS(tun_mask.ip_src),
+ IP_ARGS(tun_key.ip_dst), IP_ARGS(tun_mask.ip_dst),
+ tun_key.ip_tos, tun_mask.ip_tos,
+ tun_key.ip_ttl, tun_mask.ip_ttl);
+
+ format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+
+ /* XXX This code is correct, but enabling it would break the unit
+ test. Disable it for now until the input parser is fixed.
+
+ ds_put_char(ds, '/');
+ format_flags(ds, flow_tun_flag_to_string, tun_mask.flags, ',');
+ */
+ ds_put_char(ds, ')');
+ } else {
+ ds_put_format(ds, "tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+ "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
+ ntohll(tun_key.tun_id),
+ IP_ARGS(tun_key.ip_src),
+ IP_ARGS(tun_key.ip_dst),
+ tun_key.ip_tos, tun_key.ip_ttl);
+
+ format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ',');
+ ds_put_char(ds, ')');
+ }
+ break;
+
+ case OVS_KEY_ATTR_IN_PORT:
+ if (portno_names && verbose && is_exact) {
+ char *name = odp_portno_names_get(portno_names,
+ u32_to_odp(nl_attr_get_u32(a)));
+ if (name) {
+ ds_put_format(ds, "%s", name);
+ } else {
+ ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ }
+ } else {
+ ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ if (!is_exact) {
+ ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_ETHERNET:
+ if (!is_exact) {
+ const struct ovs_key_ethernet *eth_mask = nl_attr_get(ma);
+ const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+ ",dst="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(eth_key->eth_src),
+ ETH_ADDR_ARGS(eth_mask->eth_src),
+ ETH_ADDR_ARGS(eth_key->eth_dst),
+ ETH_ADDR_ARGS(eth_mask->eth_dst));
+ } else {
+ const struct ovs_key_ethernet *eth_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="ETH_ADDR_FMT",dst="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(eth_key->eth_src),
+ ETH_ADDR_ARGS(eth_key->eth_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_VLAN:
+ {
+ ovs_be16 vlan_tci = nl_attr_get_be16(a);
+ if (!is_exact) {
+ ovs_be16 mask = nl_attr_get_be16(ma);
+ ds_put_format(ds, "vid=%"PRIu16"/0x%"PRIx16",pcp=%d/0x%x,cfi=%d/%d",
+ vlan_tci_to_vid(vlan_tci),
+ vlan_tci_to_vid(mask),
+ vlan_tci_to_pcp(vlan_tci),
+ vlan_tci_to_pcp(mask),
+ vlan_tci_to_cfi(vlan_tci),
+ vlan_tci_to_cfi(mask));
+ } else {
+ format_vlan_tci(ds, vlan_tci);
+ }
+ }
+ break;
+
+ case OVS_KEY_ATTR_MPLS: {
+ const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
+ const struct ovs_key_mpls *mpls_mask = NULL;
+ size_t size = nl_attr_get_size(a);
+
+ if (!size || size % sizeof *mpls_key) {
+ ds_put_format(ds, "(bad key length %"PRIuSIZE")",
+ nl_attr_get_size(a));
+ return;
+ }
+ if (!is_exact) {
+ mpls_mask = nl_attr_get(ma);
+ if (nl_attr_get_size(a) != nl_attr_get_size(ma)) {
+ ds_put_format(ds, "(key length %"PRIuSIZE" != "
+ "mask length %"PRIuSIZE")",
+ nl_attr_get_size(a), nl_attr_get_size(ma));
+ return;
+ }
+ }
+ format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
+ break;
+ }
+
+ case OVS_KEY_ATTR_ETHERTYPE:
+ ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
+ case OVS_KEY_ATTR_IPV4:
+ if (!is_exact) {
+ const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
+ const struct ovs_key_ipv4 *ipv4_mask = nl_attr_get(ma);
+
+ ds_put_format(ds, "src="IP_FMT"/"IP_FMT",dst="IP_FMT"/"IP_FMT
+ ",proto=%"PRIu8"/%#"PRIx8",tos=%#"PRIx8"/%#"PRIx8
+ ",ttl=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+ IP_ARGS(ipv4_key->ipv4_src),
+ IP_ARGS(ipv4_mask->ipv4_src),
+ IP_ARGS(ipv4_key->ipv4_dst),
+ IP_ARGS(ipv4_mask->ipv4_dst),
+ ipv4_key->ipv4_proto, ipv4_mask->ipv4_proto,
+ ipv4_key->ipv4_tos, ipv4_mask->ipv4_tos,
+ ipv4_key->ipv4_ttl, ipv4_mask->ipv4_ttl,
+ ovs_frag_type_to_string(ipv4_key->ipv4_frag),
+ ipv4_mask->ipv4_frag);
+ } else {
+ const struct ovs_key_ipv4 *ipv4_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s",
+ IP_ARGS(ipv4_key->ipv4_src),
+ IP_ARGS(ipv4_key->ipv4_dst),
+ ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
+ ipv4_key->ipv4_ttl,
+ ovs_frag_type_to_string(ipv4_key->ipv4_frag));
+ }
+ break;
+
+ case OVS_KEY_ATTR_IPV6:
+ if (!is_exact) {
+ const struct ovs_key_ipv6 *ipv6_key, *ipv6_mask;
+ char src_str[INET6_ADDRSTRLEN];
+ char dst_str[INET6_ADDRSTRLEN];
+ char src_mask[INET6_ADDRSTRLEN];
+ char dst_mask[INET6_ADDRSTRLEN];
+
+ ipv6_key = nl_attr_get(a);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+ ipv6_mask = nl_attr_get(ma);
+ inet_ntop(AF_INET6, ipv6_mask->ipv6_src, src_mask, sizeof src_mask);
+ inet_ntop(AF_INET6, ipv6_mask->ipv6_dst, dst_mask, sizeof dst_mask);
+
+ ds_put_format(ds, "src=%s/%s,dst=%s/%s,label=%#"PRIx32"/%#"PRIx32
+ ",proto=%"PRIu8"/%#"PRIx8",tclass=%#"PRIx8"/%#"PRIx8
+ ",hlimit=%"PRIu8"/%#"PRIx8",frag=%s/%#"PRIx8,
+ src_str, src_mask, dst_str, dst_mask,
+ ntohl(ipv6_key->ipv6_label),
+ ntohl(ipv6_mask->ipv6_label),
+ ipv6_key->ipv6_proto, ipv6_mask->ipv6_proto,
+ ipv6_key->ipv6_tclass, ipv6_mask->ipv6_tclass,
+ ipv6_key->ipv6_hlimit, ipv6_mask->ipv6_hlimit,
+ ovs_frag_type_to_string(ipv6_key->ipv6_frag),
+ ipv6_mask->ipv6_frag);
+ } else {
+ const struct ovs_key_ipv6 *ipv6_key;
+ char src_str[INET6_ADDRSTRLEN];
+ char dst_str[INET6_ADDRSTRLEN];
+
+ ipv6_key = nl_attr_get(a);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_src, src_str, sizeof src_str);
+ inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
+
+ ds_put_format(ds, "src=%s,dst=%s,label=%#"PRIx32",proto=%"PRIu8
+ ",tclass=%#"PRIx8",hlimit=%"PRIu8",frag=%s",
+ src_str, dst_str, ntohl(ipv6_key->ipv6_label),
+ ipv6_key->ipv6_proto, ipv6_key->ipv6_tclass,
+ ipv6_key->ipv6_hlimit,
+ ovs_frag_type_to_string(ipv6_key->ipv6_frag));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TCP:
+ if (!is_exact) {
+ const struct ovs_key_tcp *tcp_mask = nl_attr_get(ma);
+ const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(tcp_key->tcp_src), ntohs(tcp_mask->tcp_src),
+ ntohs(tcp_key->tcp_dst), ntohs(tcp_mask->tcp_dst));
+ } else {
+ const struct ovs_key_tcp *tcp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_TCP_FLAGS:
+ ds_put_format(ds, "0x%03"PRIx16, ntohs(nl_attr_get_be16(a)));
+ if (!is_exact) {
+ ds_put_format(ds, "/0x%03"PRIx16, ntohs(nl_attr_get_be16(ma)));
+ }
+ break;
+
+ case OVS_KEY_ATTR_UDP:
+ if (!is_exact) {
+ const struct ovs_key_udp *udp_mask = nl_attr_get(ma);
+ const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(udp_key->udp_src), ntohs(udp_mask->udp_src),
+ ntohs(udp_key->udp_dst), ntohs(udp_mask->udp_dst));
+ } else {
+ const struct ovs_key_udp *udp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(udp_key->udp_src), ntohs(udp_key->udp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_SCTP:
+ if (ma) {
+ const struct ovs_key_sctp *sctp_mask = nl_attr_get(ma);
+ const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16"/%#"PRIx16
+ ",dst=%"PRIu16"/%#"PRIx16,
+ ntohs(sctp_key->sctp_src), ntohs(sctp_mask->sctp_src),
+ ntohs(sctp_key->sctp_dst), ntohs(sctp_mask->sctp_dst));
+ } else {
+ const struct ovs_key_sctp *sctp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "src=%"PRIu16",dst=%"PRIu16,
+ ntohs(sctp_key->sctp_src), ntohs(sctp_key->sctp_dst));
+ }
+ break;
+
+ case OVS_KEY_ATTR_ICMP:
+ if (!is_exact) {
+ const struct ovs_key_icmp *icmp_mask = nl_attr_get(ma);
+ const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+ icmp_key->icmp_type, icmp_mask->icmp_type,
+ icmp_key->icmp_code, icmp_mask->icmp_code);
+ } else {
+ const struct ovs_key_icmp *icmp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+ icmp_key->icmp_type, icmp_key->icmp_code);
+ }
+ break;
+
+ case OVS_KEY_ATTR_ICMPV6:
+ if (!is_exact) {
+ const struct ovs_key_icmpv6 *icmpv6_mask = nl_attr_get(ma);
+ const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8"/%#"PRIx8",code=%"PRIu8"/%#"PRIx8,
+ icmpv6_key->icmpv6_type, icmpv6_mask->icmpv6_type,
+ icmpv6_key->icmpv6_code, icmpv6_mask->icmpv6_code);
+ } else {
+ const struct ovs_key_icmpv6 *icmpv6_key = nl_attr_get(a);
+
+ ds_put_format(ds, "type=%"PRIu8",code=%"PRIu8,
+ icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code);
+ }
+ break;
+
+ case OVS_KEY_ATTR_ARP:
+ if (!is_exact) {
+ const struct ovs_key_arp *arp_mask = nl_attr_get(ma);
+ const struct ovs_key_arp *arp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "sip="IP_FMT"/"IP_FMT",tip="IP_FMT"/"IP_FMT
+ ",op=%"PRIu16"/%#"PRIx16
+ ",sha="ETH_ADDR_FMT"/"ETH_ADDR_FMT
+ ",tha="ETH_ADDR_FMT"/"ETH_ADDR_FMT,
+ IP_ARGS(arp_key->arp_sip),
+ IP_ARGS(arp_mask->arp_sip),
+ IP_ARGS(arp_key->arp_tip),
+ IP_ARGS(arp_mask->arp_tip),
+ ntohs(arp_key->arp_op), ntohs(arp_mask->arp_op),
+ ETH_ADDR_ARGS(arp_key->arp_sha),
+ ETH_ADDR_ARGS(arp_mask->arp_sha),
+ ETH_ADDR_ARGS(arp_key->arp_tha),
+ ETH_ADDR_ARGS(arp_mask->arp_tha));
+ } else {
+ const struct ovs_key_arp *arp_key = nl_attr_get(a);
+
+ ds_put_format(ds, "sip="IP_FMT",tip="IP_FMT",op=%"PRIu16","
+ "sha="ETH_ADDR_FMT",tha="ETH_ADDR_FMT,
+ IP_ARGS(arp_key->arp_sip), IP_ARGS(arp_key->arp_tip),
+ ntohs(arp_key->arp_op),
+ ETH_ADDR_ARGS(arp_key->arp_sha),
+ ETH_ADDR_ARGS(arp_key->arp_tha));
+ }
+ break;
+
+ case OVS_KEY_ATTR_ND: {
+ const struct ovs_key_nd *nd_key, *nd_mask = NULL;
+ char target[INET6_ADDRSTRLEN];
+
+ nd_key = nl_attr_get(a);
+ if (!is_exact) {
+ nd_mask = nl_attr_get(ma);
+ }
+
+ inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target);
+ ds_put_format(ds, "target=%s", target);
+ if (!is_exact) {
+ inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target);
+ ds_put_format(ds, "/%s", target);
+ }
+
+ if (!eth_addr_is_zero(nd_key->nd_sll)) {
+ ds_put_format(ds, ",sll="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_key->nd_sll));
+ if (!is_exact) {
+ ds_put_format(ds, "/"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_mask->nd_sll));
+ }
+ }
+ if (!eth_addr_is_zero(nd_key->nd_tll)) {
+ ds_put_format(ds, ",tll="ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_key->nd_tll));
+ if (!is_exact) {
+ ds_put_format(ds, "/"ETH_ADDR_FMT,
+ ETH_ADDR_ARGS(nd_mask->nd_tll));
+ }
+ }
+ break;
+ }
+ case OVS_KEY_ATTR_UNSPEC:
+ case __OVS_KEY_ATTR_MAX:
+ default:
+ format_generic_odp_key(a, ds);
+ if (!is_exact) {
+ ds_put_char(ds, '/');
+ format_generic_odp_key(ma, ds);
+ }
+ break;
+ }
+ ds_put_char(ds, ')');
+}
+
+static struct nlattr *
+generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
+{
+ const struct nlattr *a;
+ unsigned int left;
+ int type = nl_attr_type(key);
+ int size = nl_attr_get_size(key);
+
+ if (odp_flow_key_attr_len(type) >=0) {
+ nl_msg_put_unspec_zero(ofp, type, size);
+ } else {
+ size_t nested_mask;
+
+ nested_mask = nl_msg_start_nested(ofp, type);
+ NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) {
+ generate_all_wildcard_mask(ofp, nl_attr_get(a));
+ }
+ nl_msg_end_nested(ofp, nested_mask);
+ }
+
+ return ofpbuf_base(ofp);
+}
+
+/* Appends to 'ds' a string representation of the 'key_len' bytes of
+ * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
+ * 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
+ * non-null and 'verbose' is true, translates odp port number to its name. */
+void
+odp_flow_format(const struct nlattr *key, size_t key_len,
+ const struct nlattr *mask, size_t mask_len,
+ const struct hmap *portno_names, struct ds *ds, bool verbose)
+{
+ if (key_len) {
+ const struct nlattr *a;
+ unsigned int left;
+ bool has_ethtype_key = false;
+ const struct nlattr *ma = NULL;
+ struct ofpbuf ofp;
+ bool first_field = true;
+
+ ofpbuf_init(&ofp, 100);
+ NL_ATTR_FOR_EACH (a, left, key, key_len) {
+ bool is_nested_attr;
+ bool is_wildcard = false;
+ int attr_type = nl_attr_type(a);
+
+ if (attr_type == OVS_KEY_ATTR_ETHERTYPE) {
+ has_ethtype_key = true;
+ }
+
+ is_nested_attr = (odp_flow_key_attr_len(attr_type) == -2);
+
+ if (mask && mask_len) {
+ ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
+ is_wildcard = ma ? odp_mask_attr_is_wildcard(ma) : true;
+ }
+
+ if (verbose || !is_wildcard || is_nested_attr) {
+ if (is_wildcard && !ma) {
+ ma = generate_all_wildcard_mask(&ofp, a);
+ }
+ if (!first_field) {
+ ds_put_char(ds, ',');
+ }
+ format_odp_key_attr(a, ma, portno_names, ds, verbose);
+ first_field = false;
+ }
+ ofpbuf_clear(&ofp);
+ }
+ ofpbuf_uninit(&ofp);
+
+ if (left) {
+ int i;
+
+ if (left == key_len) {
+ ds_put_cstr(ds, "<empty>");
+ }
+ ds_put_format(ds, ",***%u leftover bytes*** (", left);
+ for (i = 0; i < left; i++) {
+ ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
+ }
+ ds_put_char(ds, ')');
+ }
+ if (!has_ethtype_key) {
+ ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE);
+ if (ma) {
+ ds_put_format(ds, ",eth_type(0/0x%04"PRIx16")",
+ ntohs(nl_attr_get_be16(ma)));
+ }
+ }
+ } else {
+ ds_put_cstr(ds, "<empty>");
+ }
+}
+
+/* Appends to 'ds' a string representation of the 'key_len' bytes of
+ * OVS_KEY_ATTR_* attributes in 'key'. */
+void
+odp_flow_key_format(const struct nlattr *key,
+ size_t key_len, struct ds *ds)
+{
+ odp_flow_format(key, key_len, NULL, 0, NULL, ds, true);
+}
+
+static void
+put_nd(struct ovs_key_nd* nd_key, const uint8_t *nd_sll,
+ const uint8_t *nd_tll, struct ofpbuf *key)
+{
+ if (nd_sll) {
+ memcpy(nd_key->nd_sll, nd_sll, ETH_ADDR_LEN);
+ }
+
+ if (nd_tll) {
+ memcpy(nd_key->nd_tll, nd_tll, ETH_ADDR_LEN);
+ }
+
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ND, nd_key, sizeof *nd_key);
+}
+
+static int
+put_nd_key(int n, const char *nd_target_s, const uint8_t *nd_sll,
+ const uint8_t *nd_tll, struct ofpbuf *key)
+{
+ struct ovs_key_nd nd_key;
+
+ memset(&nd_key, 0, sizeof nd_key);
+
+ if (inet_pton(AF_INET6, nd_target_s, nd_key.nd_target) != 1) {
+ return -EINVAL;
+ }
+
+ put_nd(&nd_key, nd_sll, nd_tll, key);
+ return n;
+}
+
+static int
+put_nd_mask(int n, const char *nd_target_s,
+ const uint8_t *nd_sll, const uint8_t *nd_tll, struct ofpbuf *mask)
+{
+ struct ovs_key_nd nd_mask;
+
+ memset(&nd_mask, 0xff, sizeof nd_mask);
+
+ if (strlen(nd_target_s) != 0 &&
+ inet_pton(AF_INET6, nd_target_s, nd_mask.nd_target) != 1) {
+ return -EINVAL;
+ }
+
+ put_nd(&nd_mask, nd_sll, nd_tll, mask);
+ return n;
+}
+
+static bool
+ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type)
+{
+ if (!strcasecmp(s, "no")) {
+ *type = OVS_FRAG_TYPE_NONE;
+ } else if (!strcasecmp(s, "first")) {
+ *type = OVS_FRAG_TYPE_FIRST;
+ } else if (!strcasecmp(s, "later")) {
+ *type = OVS_FRAG_TYPE_LATER;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+static ovs_be32
+mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos)
+{
+ return (htonl((mpls_label << MPLS_LABEL_SHIFT) |
+ (mpls_tc << MPLS_TC_SHIFT) |
+ (mpls_ttl << MPLS_TTL_SHIFT) |
+ (mpls_bos << MPLS_BOS_SHIFT)));
+}
+
+static int
+parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
+ struct ofpbuf *key, struct ofpbuf *mask)
+{
+ {
+ uint32_t priority;
+ uint32_t priority_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "skb_priority(%"SCNi32"/%"SCNi32")%n",
+ &priority, &priority_mask, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, priority_mask);
+ return n;
+ } else if (ovs_scan(s, "skb_priority(%"SCNi32")%n", &priority, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_PRIORITY, priority);
+ if (mask) {
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_PRIORITY, UINT32_MAX);
+ }
+ return n;
+ }
+ }
+
+ {
+ uint32_t mark;
+ uint32_t mark_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "skb_mark(%"SCNi32"/%"SCNi32")%n", &mark,
+ &mark_mask, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, mark_mask);
+ return n;
+ } else if (ovs_scan(s, "skb_mark(%"SCNi32")%n", &mark, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_SKB_MARK, mark);
+ if (mask) {
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_SKB_MARK, UINT32_MAX);
+ }
+ return n;
+ }
+ }
+
+ {
+ uint32_t recirc_id;
+ int n = -1;
+
+ if (ovs_scan(s, "recirc_id(%"SCNi32")%n", &recirc_id, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_RECIRC_ID, recirc_id);
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_RECIRC_ID, UINT32_MAX);
+ return n;
+ }
+ }
+
+ {
+ uint32_t dp_hash;
+ uint32_t dp_hash_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "dp_hash(%"SCNi32"/%"SCNi32")%n", &dp_hash,
+ &dp_hash_mask, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_DP_HASH, dp_hash);
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_DP_HASH, dp_hash_mask);
+ return n;
+ } else if (ovs_scan(s, "dp_hash(%"SCNi32")%n", &dp_hash, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_DP_HASH, dp_hash);
+ if (mask) {
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_DP_HASH, UINT32_MAX);
+ }
+ return n;
+ }
+ }
+
+ {
+ uint64_t tun_id, tun_id_mask;
+ struct flow_tnl tun_key, tun_key_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "tunnel(tun_id=%"SCNi64"/%"SCNi64","
+ "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT
+ "/"IP_SCAN_FMT",tos=%"SCNi8"/%"SCNi8","
+ "ttl=%"SCNi8"/%"SCNi8",flags%n",
+ &tun_id, &tun_id_mask,
+ IP_SCAN_ARGS(&tun_key.ip_src),
+ IP_SCAN_ARGS(&tun_key_mask.ip_src),
+ IP_SCAN_ARGS(&tun_key.ip_dst),
+ IP_SCAN_ARGS(&tun_key_mask.ip_dst),
+ &tun_key.ip_tos, &tun_key_mask.ip_tos,
+ &tun_key.ip_ttl, &tun_key_mask.ip_ttl, &n)) {
+ int res;
+ uint32_t flags;
+
+ tun_key.tun_id = htonll(tun_id);
+ tun_key_mask.tun_id = htonll(tun_id_mask);
+ res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
+ tun_key.flags = flags;
+ tun_key_mask.flags = UINT16_MAX;
+
+ if (res < 0) {
+ return res;
+ }
+ n += res;
+ if (s[n] != ')') {
+ return -EINVAL;
+ }
+ n++;
+ tun_key_to_attr(key, &tun_key);
+ if (mask) {
+ tun_key_to_attr(mask, &tun_key_mask);
+ }
+ return n;
+ } else if (ovs_scan(s, "tunnel(tun_id=%"SCNi64","
+ "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
+ ",tos=%"SCNi8",ttl=%"SCNi8",flags%n", &tun_id,
+ IP_SCAN_ARGS(&tun_key.ip_src),
+ IP_SCAN_ARGS(&tun_key.ip_dst),
+ &tun_key.ip_tos, &tun_key.ip_ttl, &n)) {
+ int res;
+ uint32_t flags;
+
+ tun_key.tun_id = htonll(tun_id);
+ res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
+ tun_key.flags = flags;
+
+ if (res < 0) {
+ return res;
+ }
+ n += res;
+ if (s[n] != ')') {
+ return -EINVAL;
+ }
+ n++;
+ tun_key_to_attr(key, &tun_key);
+
+ if (mask) {
+ memset(&tun_key, 0xff, sizeof tun_key);
+ tun_key_to_attr(mask, &tun_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ uint32_t in_port;
+ uint32_t in_port_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "in_port(%"SCNi32"/%"SCNi32")%n",
+ &in_port, &in_port_mask, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, in_port_mask);
+ return n;
+ } else if (ovs_scan(s, "in_port(%"SCNi32")%n", &in_port, &n)) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, in_port);
+ if (mask) {
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, UINT32_MAX);
+ }
+ return n;
+ }
+ }
+
+
+ if (port_names && !strncmp(s, "in_port(", 8)) {
+ const char *name;
+ const struct simap_node *node;
+ int name_len;
+
+ name = s + 8;
+ name_len = strcspn(name, ")");
+ node = simap_find_len(port_names, name, name_len);
+ if (node) {
+ nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
+
+ if (mask) {
+ nl_msg_put_u32(mask, OVS_KEY_ATTR_IN_PORT, UINT32_MAX);
+ }
+ return 8 + name_len + 1;
+ }
+ }
+
+ {
+ struct ovs_key_ethernet eth_key;
+ struct ovs_key_ethernet eth_key_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s,
+ "eth(src="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "dst="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_dst),
+ ETH_ADDR_SCAN_ARGS(eth_key_mask.eth_dst), &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
+ ð_key, sizeof eth_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
+ ð_key_mask, sizeof eth_key_mask);
+ return n;
+ } else if (ovs_scan(s, "eth(src="ETH_ADDR_SCAN_FMT","
+ "dst="ETH_ADDR_SCAN_FMT")%n",
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_src),
+ ETH_ADDR_SCAN_ARGS(eth_key.eth_dst), &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ETHERNET,
+ ð_key, sizeof eth_key);
+
+ if (mask) {
+ memset(ð_key, 0xff, sizeof eth_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ETHERNET,
+ ð_key, sizeof eth_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ int vid, vid_mask;
+ int pcp, pcp_mask;
+ int cfi, cfi_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "vlan(vid=%i/%i,pcp=%i/%i)%n",
+ &vid, &vid_mask, &pcp, &pcp_mask, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+ htons((vid << VLAN_VID_SHIFT) |
+ (pcp << VLAN_PCP_SHIFT) |
+ VLAN_CFI));
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN,
+ htons((vid_mask << VLAN_VID_SHIFT) |
+ (pcp_mask << VLAN_PCP_SHIFT) |
+ (1 << VLAN_CFI_SHIFT)));
+ return n;
+ } else if (ovs_scan(s, "vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+ htons((vid << VLAN_VID_SHIFT) |
+ (pcp << VLAN_PCP_SHIFT) |
+ VLAN_CFI));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
+ }
+ return n;
+ } else if (mask
+ && ovs_scan(s, "vlan(vid=%i/%i,pcp=%i/%i,cfi=%i/%i)%n",
+ &vid, &vid_mask, &pcp, &pcp_mask,
+ &cfi, &cfi_mask, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+ htons((vid << VLAN_VID_SHIFT) |
+ (pcp << VLAN_PCP_SHIFT) |
+ (cfi ? VLAN_CFI : 0)));
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN,
+ htons((vid_mask << VLAN_VID_SHIFT) |
+ (pcp_mask << VLAN_PCP_SHIFT) |
+ (cfi_mask << VLAN_CFI_SHIFT)));
+ return n;
+ } else if (ovs_scan(s, "vlan(vid=%i,pcp=%i,cfi=%i)%n",
+ &vid, &pcp, &cfi, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_VLAN,
+ htons((vid << VLAN_VID_SHIFT) |
+ (pcp << VLAN_PCP_SHIFT) |
+ (cfi ? VLAN_CFI : 0)));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
+ }
+ return n;
+ }
+ }
+
+ {
+ int eth_type;
+ int eth_type_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "eth_type(%i/%i)%n",
+ ð_type, ð_type_mask, &n)) {
+ if (eth_type != 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
+ }
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type_mask));
+ return n;
+ } else if (ovs_scan(s, "eth_type(%i)%n", ð_type, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
+ }
+ return n;
+ }
+ }
+
+ {
+ int label, tc, ttl, bos;
+ int label_mask, tc_mask, ttl_mask, bos_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "mpls(label=%i/%i,tc=%i/%i,"
+ "ttl=%i/%i,bos=%i/%i)%n",
+ &label, &label_mask, &tc, &tc_mask,
+ &ttl, &ttl_mask, &bos, &bos_mask, &n)) {
+ struct ovs_key_mpls *mpls, *mpls_mask;
+
+ mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
+
+ mpls_mask = nl_msg_put_unspec_uninit(mask, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls_mask);
+ mpls_mask->mpls_lse = mpls_lse_from_components(
+ label_mask, tc_mask, ttl_mask, bos_mask);
+ return n;
+ } else if (ovs_scan(s, "mpls(label=%i,tc=%i,ttl=%i,bos=%i)%n",
+ &label, &tc, &ttl, &bos, &n)) {
+ struct ovs_key_mpls *mpls;
+
+ mpls = nl_msg_put_unspec_uninit(key, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_lse = mpls_lse_from_components(label, tc, ttl, bos);
+ if (mask) {
+ mpls = nl_msg_put_unspec_uninit(mask, OVS_KEY_ATTR_MPLS,
+ sizeof *mpls);
+ mpls->mpls_lse = OVS_BE32_MAX;
+ }
+ return n;
+ }
+ }
+
+
+ {
+ struct ovs_key_ipv4 ipv4_key;
+ struct ovs_key_ipv4 ipv4_mask;
+
+ char frag[8];
+ enum ovs_frag_type ipv4_frag;
+ int n = -1;
+
+ if (mask
+ && ovs_scan(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "dst="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "proto=%"SCNi8"/%"SCNi8","
+ "tos=%"SCNi8"/%"SCNi8","
+ "ttl=%"SCNi8"/%"SCNi8","
+ "frag=%7[a-z]/%"SCNi8")%n",
+ IP_SCAN_ARGS(&ipv4_key.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_mask.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_key.ipv4_dst),
+ IP_SCAN_ARGS(&ipv4_mask.ipv4_dst),
+ &ipv4_key.ipv4_proto, &ipv4_mask.ipv4_proto,
+ &ipv4_key.ipv4_tos, &ipv4_mask.ipv4_tos,
+ &ipv4_key.ipv4_ttl, &ipv4_mask.ipv4_ttl,
+ frag, &ipv4_mask.ipv4_frag, &n)
+ && ovs_frag_type_from_string(frag, &ipv4_frag)) {
+ ipv4_key.ipv4_frag = ipv4_frag;
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof ipv4_key);
+
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
+ &ipv4_mask, sizeof ipv4_mask);
+ return n;
+ } else if (ovs_scan(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
+ "proto=%"SCNi8",tos=%"SCNi8",ttl=%"SCNi8","
+ "frag=%7[a-z])%n",
+ IP_SCAN_ARGS(&ipv4_key.ipv4_src),
+ IP_SCAN_ARGS(&ipv4_key.ipv4_dst),
+ &ipv4_key.ipv4_proto,
+ &ipv4_key.ipv4_tos,
+ &ipv4_key.ipv4_ttl,
+ frag, &n) > 0
+ && ovs_frag_type_from_string(frag, &ipv4_frag)) {
+ ipv4_key.ipv4_frag = ipv4_frag;
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof ipv4_key);
+
+ if (mask) {
+ memset(&ipv4_key, 0xff, sizeof ipv4_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4,
+ &ipv4_key, sizeof ipv4_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ char ipv6_src_s[IPV6_SCAN_LEN + 1];
+ char ipv6_src_mask_s[IPV6_SCAN_LEN + 1];
+ char ipv6_dst_s[IPV6_SCAN_LEN + 1];
+ char ipv6_dst_mask_s[IPV6_SCAN_LEN + 1];
+ int ipv6_label, ipv6_label_mask;
+ int ipv6_proto, ipv6_proto_mask;
+ int ipv6_tclass, ipv6_tclass_mask;
+ int ipv6_hlimit, ipv6_hlimit_mask;
+ char frag[8];
+ enum ovs_frag_type ipv6_frag;
+ int ipv6_frag_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "ipv6(src="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT",dst="
+ IPV6_SCAN_FMT"/"IPV6_SCAN_FMT","
+ "label=%i/%i,proto=%i/%i,tclass=%i/%i,"
+ "hlimit=%i/%i,frag=%7[a-z]/%i)%n",
+ ipv6_src_s, ipv6_src_mask_s,
+ ipv6_dst_s, ipv6_dst_mask_s,
+ &ipv6_label, &ipv6_label_mask, &ipv6_proto,
+ &ipv6_proto_mask, &ipv6_tclass, &ipv6_tclass_mask,
+ &ipv6_hlimit, &ipv6_hlimit_mask, frag,
+ &ipv6_frag_mask, &n)
+ && ovs_frag_type_from_string(frag, &ipv6_frag)) {
+ struct ovs_key_ipv6 ipv6_key;
+ struct ovs_key_ipv6 ipv6_mask;
+
+ 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 ||
+ inet_pton(AF_INET6, ipv6_src_mask_s, &ipv6_mask.ipv6_src) != 1 ||
+ inet_pton(AF_INET6, ipv6_dst_mask_s, &ipv6_mask.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);
+
+ ipv6_mask.ipv6_label = htonl(ipv6_label_mask);
+ ipv6_mask.ipv6_proto = ipv6_proto_mask;
+ ipv6_mask.ipv6_tclass = ipv6_tclass_mask;
+ ipv6_mask.ipv6_hlimit = ipv6_hlimit_mask;
+ ipv6_mask.ipv6_frag = ipv6_frag_mask;
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
+ &ipv6_mask, sizeof ipv6_mask);
+ return n;
+ } else if (ovs_scan(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)
+ && 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);
+
+ if (mask) {
+ memset(&ipv6_key, 0xff, sizeof ipv6_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV6,
+ &ipv6_key, sizeof ipv6_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ int tcp_src;
+ int tcp_dst;
+ int tcp_src_mask;
+ int tcp_dst_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "tcp(src=%i/%i,dst=%i/%i)%n",
+ &tcp_src, &tcp_src_mask, &tcp_dst,
+ &tcp_dst_mask, &n)) {
+ struct ovs_key_tcp tcp_key;
+ struct ovs_key_tcp tcp_mask;
+
+ 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);
+
+ tcp_mask.tcp_src = htons(tcp_src_mask);
+ tcp_mask.tcp_dst = htons(tcp_dst_mask);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
+ &tcp_mask, sizeof tcp_mask);
+ return n;
+ } else if (ovs_scan(s, "tcp(src=%i,dst=%i)%n",
+ &tcp_src, &tcp_dst, &n)) {
+ 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);
+
+ if (mask) {
+ memset(&tcp_key, 0xff, sizeof tcp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_TCP,
+ &tcp_key, sizeof tcp_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ uint16_t tcp_flags, tcp_flags_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "tcp_flags(%"SCNi16"/%"SCNi16")%n",
+ &tcp_flags, &tcp_flags_mask, &n) > 0 && n > 0) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags_mask));
+ return n;
+ } else if (ovs_scan(s, "tcp_flags(%"SCNi16")%n", &tcp_flags, &n)) {
+ nl_msg_put_be16(key, OVS_KEY_ATTR_TCP_FLAGS, htons(tcp_flags));
+ if (mask) {
+ nl_msg_put_be16(mask, OVS_KEY_ATTR_TCP_FLAGS,
+ htons(UINT16_MAX));
+ }
+ return n;
+ }
+ }
+
+ {
+ int udp_src;
+ int udp_dst;
+ int udp_src_mask;
+ int udp_dst_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "udp(src=%i/%i,dst=%i/%i)%n",
+ &udp_src, &udp_src_mask,
+ &udp_dst, &udp_dst_mask, &n)) {
+ struct ovs_key_udp udp_key;
+ struct ovs_key_udp udp_mask;
+
+ 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);
+
+ udp_mask.udp_src = htons(udp_src_mask);
+ udp_mask.udp_dst = htons(udp_dst_mask);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP,
+ &udp_mask, sizeof udp_mask);
+ return n;
+ }
+ if (ovs_scan(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n)) {
+ 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);
+
+ if (mask) {
+ memset(&udp_key, 0xff, sizeof udp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_UDP, &udp_key, sizeof udp_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ int sctp_src;
+ int sctp_dst;
+ int sctp_src_mask;
+ int sctp_dst_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "sctp(src=%i/%i,dst=%i/%i)%n",
+ &sctp_src, &sctp_src_mask,
+ &sctp_dst, &sctp_dst_mask, &n)) {
+ struct ovs_key_sctp sctp_key;
+ struct ovs_key_sctp sctp_mask;
+
+ sctp_key.sctp_src = htons(sctp_src);
+ sctp_key.sctp_dst = htons(sctp_dst);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+
+ sctp_mask.sctp_src = htons(sctp_src_mask);
+ sctp_mask.sctp_dst = htons(sctp_dst_mask);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP,
+ &sctp_mask, sizeof sctp_mask);
+ return n;
+ }
+ if (ovs_scan(s, "sctp(src=%i,dst=%i)%n", &sctp_src, &sctp_dst, &n)) {
+ struct ovs_key_sctp sctp_key;
+
+ sctp_key.sctp_src = htons(sctp_src);
+ sctp_key.sctp_dst = htons(sctp_dst);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+
+ if (mask) {
+ memset(&sctp_key, 0xff, sizeof sctp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_SCTP, &sctp_key, sizeof sctp_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ struct ovs_key_icmp icmp_key;
+ struct ovs_key_icmp icmp_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "icmp(type=%"SCNi8"/%"SCNi8","
+ "code=%"SCNi8"/%"SCNi8")%n",
+ &icmp_key.icmp_type, &icmp_mask.icmp_type,
+ &icmp_key.icmp_code, &icmp_mask.icmp_code, &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
+ &icmp_key, sizeof icmp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP,
+ &icmp_mask, sizeof icmp_mask);
+ return n;
+ } else if (ovs_scan(s, "icmp(type=%"SCNi8",code=%"SCNi8")%n",
+ &icmp_key.icmp_type, &icmp_key.icmp_code, &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMP,
+ &icmp_key, sizeof icmp_key);
+ if (mask) {
+ memset(&icmp_key, 0xff, sizeof icmp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP, &icmp_key,
+ sizeof icmp_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ struct ovs_key_icmpv6 icmpv6_key;
+ struct ovs_key_icmpv6 icmpv6_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "icmpv6(type=%"SCNi8"/%"SCNi8","
+ "code=%"SCNi8"/%"SCNi8")%n",
+ &icmpv6_key.icmpv6_type, &icmpv6_mask.icmpv6_type,
+ &icmpv6_key.icmpv6_code, &icmpv6_mask.icmpv6_code,
+ &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
+ &icmpv6_key, sizeof icmpv6_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_mask,
+ sizeof icmpv6_mask);
+ return n;
+ } else if (ovs_scan(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n",
+ &icmpv6_key.icmpv6_type, &icmpv6_key.icmpv6_code,
+ &n)) {
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6,
+ &icmpv6_key, sizeof icmpv6_key);
+
+ if (mask) {
+ memset(&icmpv6_key, 0xff, sizeof icmpv6_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_key,
+ sizeof icmpv6_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ struct ovs_key_arp arp_key;
+ struct ovs_key_arp arp_mask;
+ uint16_t arp_op, arp_op_mask;
+ int n = -1;
+
+ if (mask && ovs_scan(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "tip="IP_SCAN_FMT"/"IP_SCAN_FMT","
+ "op=%"SCNi16"/%"SCNi16","
+ "sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ IP_SCAN_ARGS(&arp_key.arp_sip),
+ IP_SCAN_ARGS(&arp_mask.arp_sip),
+ IP_SCAN_ARGS(&arp_key.arp_tip),
+ IP_SCAN_ARGS(&arp_mask.arp_tip),
+ &arp_op, &arp_op_mask,
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_mask.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_tha),
+ ETH_ADDR_SCAN_ARGS(arp_mask.arp_tha), &n)) {
+ arp_key.arp_op = htons(arp_op);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+ arp_mask.arp_op = htons(arp_op_mask);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
+ &arp_mask, sizeof arp_mask);
+ return n;
+ } else if (ovs_scan(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT","
+ "op=%"SCNi16",sha="ETH_ADDR_SCAN_FMT","
+ "tha="ETH_ADDR_SCAN_FMT")%n",
+ IP_SCAN_ARGS(&arp_key.arp_sip),
+ IP_SCAN_ARGS(&arp_key.arp_tip),
+ &arp_op,
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_sha),
+ ETH_ADDR_SCAN_ARGS(arp_key.arp_tha), &n)) {
+ arp_key.arp_op = htons(arp_op);
+ nl_msg_put_unspec(key, OVS_KEY_ATTR_ARP, &arp_key, sizeof arp_key);
+
+ if (mask) {
+ memset(&arp_key, 0xff, sizeof arp_key);
+ nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP,
+ &arp_key, sizeof arp_key);
+ }
+ return n;
+ }
+ }
+
+ {
+ char nd_target_s[IPV6_SCAN_LEN + 1];
+ char nd_target_mask_s[IPV6_SCAN_LEN + 1];
+ uint8_t nd_sll[ETH_ADDR_LEN];
+ uint8_t nd_sll_mask[ETH_ADDR_LEN];
+ uint8_t nd_tll[ETH_ADDR_LEN];
+ uint8_t nd_tll_mask[ETH_ADDR_LEN];
+ int n = -1;
+
+ nd_target_mask_s[0] = 0;
+ memset(nd_sll_mask, 0xff, sizeof nd_sll_mask);
+ memset(nd_tll_mask, 0xff, sizeof nd_tll_mask);
+
+ if (mask && ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s, &n)) {
+ put_nd_key(n, nd_target_s, NULL, NULL, key);
+ put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT")%n",
+ nd_target_s, &n)) {
+ put_nd_key(n, nd_target_s, NULL, NULL, key);
+ if (mask) {
+ put_nd_mask(n, nd_target_mask_s, NULL, NULL, mask);
+ }
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_sll_mask), &n)) {
+ put_nd_key(n, nd_target_s, nd_sll, NULL, key);
+ put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT","
+ "sll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_sll), &n)) {
+ put_nd_key(n, nd_target_s, nd_sll, NULL, key);
+ if (mask) {
+ put_nd_mask(n, nd_target_mask_s, nd_sll_mask, NULL, mask);
+ }
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_tll),
+ ETH_ADDR_SCAN_ARGS(nd_tll_mask), &n)) {
+ put_nd_key(n, nd_target_s, NULL, nd_tll, key);
+ put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
+ } else if (ovs_scan(s, "nd(target="IPV6_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, ETH_ADDR_SCAN_ARGS(nd_tll), &n)) {
+ put_nd_key(n, nd_target_s, NULL, nd_tll, key);
+ if (mask) {
+ put_nd_mask(n, nd_target_mask_s, NULL, nd_tll_mask, mask);
+ }
+ } else if (mask &&
+ ovs_scan(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT
+ ",sll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT","
+ "tll="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n",
+ nd_target_s, nd_target_mask_s,
+ ETH_ADDR_SCAN_ARGS(nd_sll),
+ ETH_ADDR_SCAN_ARGS(nd_sll_mask),
+ ETH_ADDR_SCAN_ARGS(nd_tll),
+ ETH_ADDR_SCAN_ARGS(nd_tll_mask),
+ &n)) {
+ put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+ put_nd_mask(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask);
+ } else if (ovs_scan(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)) {
+ put_nd_key(n, nd_target_s, nd_sll, nd_tll, key);
+ if (mask) {
+ put_nd_mask(n, nd_target_mask_s,
+ nd_sll_mask, nd_tll_mask, mask);
+ }
+ }
+
+ if (n != -1)
+ return n;
+
+ }
+
+ if (!strncmp(s, "encap(", 6)) {
+ const char *start = s;
+ size_t encap, encap_mask = 0;
+
+ encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
+ if (mask) {
+ encap_mask = nl_msg_start_nested(mask, 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_mask_attr(s, port_names, key, mask);
+ if (retval < 0) {
+ return retval;
+ }
+ s += retval;
+ }
+ s++;
+
+ nl_msg_end_nested(key, encap);
+ if (mask) {
+ nl_msg_end_nested(mask, encap_mask);
+ }
+
+ 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_from_string(const char *s, const struct simap *port_names,
+ struct ofpbuf *key, struct ofpbuf *mask)
+{
+ const size_t old_size = ofpbuf_size(key);
+ for (;;) {
+ int retval;