diff --git a/lib/dpif-linux.c b/lib/dpif-linux.c index 1383b5834..2095cabad 100644 --- a/lib/dpif-linux.c +++ b/lib/dpif-linux.c @@ -105,6 +105,8 @@ struct dpif_linux_flow { * the Netlink version of the command, even if actions_len is zero. */ const struct nlattr *key; /* OVS_FLOW_ATTR_KEY. */ size_t key_len; + const struct nlattr *mask; /* OVS_FLOW_ATTR_MASK. */ + size_t mask_len; const struct nlattr *actions; /* OVS_FLOW_ATTR_ACTIONS. */ size_t actions_len; const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */ @@ -807,6 +809,8 @@ dpif_linux_init_flow_put(struct dpif *dpif_, const struct dpif_flow_put *put, request->dp_ifindex = dpif->dp_ifindex; request->key = put->key; request->key_len = put->key_len; + request->mask = put->mask; + request->mask_len = put->mask_len; /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */ request->actions = (put->actions ? put->actions @@ -901,6 +905,7 @@ dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep) static int dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_, const struct nlattr **key, size_t *key_len, + const struct nlattr **mask, size_t *mask_len, const struct nlattr **actions, size_t *actions_len, const struct dpif_flow_stats **stats) { @@ -941,6 +946,10 @@ dpif_linux_flow_dump_next(const struct dpif *dpif_ OVS_UNUSED, void *state_, *key = state->flow.key; *key_len = state->flow.key_len; } + if (mask) { + *mask = state->flow.mask; + *mask_len = state->flow.mask ? state->flow.mask_len : 0; + } if (stats) { dpif_linux_flow_get_stats(&state->flow, &state->stats); *stats = &state->stats; @@ -1832,6 +1841,7 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow, { static const struct nl_policy ovs_flow_policy[] = { [OVS_FLOW_ATTR_KEY] = { .type = NL_A_NESTED }, + [OVS_FLOW_ATTR_MASK] = { .type = NL_A_NESTED, .optional = true }, [OVS_FLOW_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true }, [OVS_FLOW_ATTR_STATS] = { NL_POLICY_FOR(struct ovs_flow_stats), .optional = true }, @@ -1863,6 +1873,11 @@ dpif_linux_flow_from_ofpbuf(struct dpif_linux_flow *flow, flow->dp_ifindex = ovs_header->dp_ifindex; flow->key = nl_attr_get(a[OVS_FLOW_ATTR_KEY]); flow->key_len = nl_attr_get_size(a[OVS_FLOW_ATTR_KEY]); + + if (a[OVS_FLOW_ATTR_MASK]) { + flow->mask = nl_attr_get(a[OVS_FLOW_ATTR_MASK]); + flow->mask_len = nl_attr_get_size(a[OVS_FLOW_ATTR_MASK]); + } if (a[OVS_FLOW_ATTR_ACTIONS]) { flow->actions = nl_attr_get(a[OVS_FLOW_ATTR_ACTIONS]); flow->actions_len = nl_attr_get_size(a[OVS_FLOW_ATTR_ACTIONS]); @@ -1898,6 +1913,10 @@ dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow, nl_msg_put_unspec(buf, OVS_FLOW_ATTR_KEY, flow->key, flow->key_len); } + if (flow->mask_len) { + nl_msg_put_unspec(buf, OVS_FLOW_ATTR_MASK, flow->mask, flow->mask_len); + } + if (flow->actions || flow->actions_len) { nl_msg_put_unspec(buf, OVS_FLOW_ATTR_ACTIONS, flow->actions, flow->actions_len); diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index 52aedb695..5a54627ef 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -879,6 +879,7 @@ dpif_netdev_flow_dump_start(const struct dpif *dpif OVS_UNUSED, void **statep) static int dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_, const struct nlattr **key, size_t *key_len, + const struct nlattr **mask, size_t *mask_len, const struct nlattr **actions, size_t *actions_len, const struct dpif_flow_stats **stats) { @@ -904,6 +905,11 @@ dpif_netdev_flow_dump_next(const struct dpif *dpif, void *state_, *key_len = buf.size; } + if (mask) { + *mask = NULL; + *mask_len = 0; + } + if (actions) { free(state->actions); state->actions = xmemdup(flow->actions, flow->actions_len); diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h index bea822f29..666c2244e 100644 --- a/lib/dpif-provider.h +++ b/lib/dpif-provider.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -279,12 +279,22 @@ struct dpif_class { * called again once it returns nonzero within a given iteration (but the * 'flow_dump_done' function will be called afterward). * - * On success, if 'key' and 'key_len' are nonnull then '*key' and - * '*key_len' must be set to Netlink attributes with types OVS_KEY_ATTR_* - * representing the dumped flow's key. If 'actions' and 'actions_len' are - * nonnull then they should be set to Netlink attributes with types - * OVS_ACTION_ATTR_* representing the dumped flow's actions. If 'stats' - * is nonnull then it should be set to the dumped flow's statistics. + * On success: + * + * - If 'key' and 'key_len' are nonnull, then '*key' and '*key_len' + * must be set to Netlink attributes with types OVS_KEY_ATTR_* + * representing the dumped flow's key. + * + * - If 'mask' and 'mask_len' are nonnull then '*mask' and '*mask_len' + * must be set to Netlink attributes with types of OVS_KEY_ATTR_* + * representing the dumped flow's mask. + * + * - If 'actions' and 'actions_len' are nonnull then they should be set + * to Netlink attributes with types OVS_ACTION_ATTR_* representing + * the dumped flow's actions. + * + * - If 'stats' is nonnull then it should be set to the dumped flow's + * statistics. * * All of the returned data is owned by 'dpif', not by the caller, and the * caller must not modify or free it. 'dpif' must guarantee that it @@ -292,6 +302,7 @@ struct dpif_class { * 'flow_dump_next' or 'flow_dump_done' for 'state'. */ int (*flow_dump_next)(const struct dpif *dpif, void *state, const struct nlattr **key, size_t *key_len, + const struct nlattr **mask, size_t *mask_len, const struct nlattr **actions, size_t *actions_len, const struct dpif_flow_stats **stats); diff --git a/lib/dpif.c b/lib/dpif.c index 6aa52d57a..370de3c71 100644 --- a/lib/dpif.c +++ b/lib/dpif.c @@ -832,9 +832,11 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put) } /* Adds or modifies a flow in 'dpif'. The flow is specified by the Netlink - * attributes with types OVS_KEY_ATTR_* in the 'key_len' bytes starting at - * 'key'. The associated actions are specified by the Netlink attributes with - * types OVS_ACTION_ATTR_* in the 'actions_len' bytes starting at 'actions'. + * attribute OVS_FLOW_ATTR_KEY with types OVS_KEY_ATTR_* in the 'key_len' bytes + * starting at 'key', and OVS_FLOW_ATTR_MASK with types of OVS_KEY_ATTR_* in the + * 'mask_len' bytes starting at 'mask'. The associated actions are specified by + * the Netlink attributes with types OVS_ACTION_ATTR_* in the 'actions_len' + * bytes starting at 'actions'. * * - If the flow's key does not exist in 'dpif', then the flow will be added if * 'flags' includes DPIF_FP_CREATE. Otherwise the operation will fail with @@ -854,6 +856,7 @@ dpif_flow_put__(struct dpif *dpif, const struct dpif_flow_put *put) int dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags, const struct nlattr *key, size_t key_len, + const struct nlattr *mask, size_t mask_len, const struct nlattr *actions, size_t actions_len, struct dpif_flow_stats *stats) { @@ -862,6 +865,8 @@ dpif_flow_put(struct dpif *dpif, enum dpif_flow_put_flags flags, put.flags = flags; put.key = key; put.key_len = key_len; + put.mask = mask; + put.mask_len = mask_len; put.actions = actions; put.actions_len = actions_len; put.stats = stats; @@ -937,6 +942,7 @@ dpif_flow_dump_start(struct dpif_flow_dump *dump, const struct dpif *dpif) bool dpif_flow_dump_next(struct dpif_flow_dump *dump, const struct nlattr **key, size_t *key_len, + const struct nlattr **mask, size_t *mask_len, const struct nlattr **actions, size_t *actions_len, const struct dpif_flow_stats **stats) { @@ -946,6 +952,7 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump, if (!error) { error = dpif->dpif_class->flow_dump_next(dpif, dump->state, key, key_len, + mask, mask_len, actions, actions_len, stats); if (error) { @@ -957,6 +964,10 @@ dpif_flow_dump_next(struct dpif_flow_dump *dump, *key = NULL; *key_len = 0; } + if (mask) { + *mask = NULL; + *mask_len = 0; + } if (actions) { *actions = NULL; *actions_len = 0; diff --git a/lib/dpif.h b/lib/dpif.h index fd05b2f20..11d7445a6 100644 --- a/lib/dpif.h +++ b/lib/dpif.h @@ -447,6 +447,7 @@ enum dpif_flow_put_flags { int dpif_flow_flush(struct dpif *); int dpif_flow_put(struct dpif *, enum dpif_flow_put_flags, const struct nlattr *key, size_t key_len, + const struct nlattr *mask, size_t mask_len, const struct nlattr *actions, size_t actions_len, struct dpif_flow_stats *); int dpif_flow_del(struct dpif *, @@ -464,6 +465,7 @@ struct dpif_flow_dump { void dpif_flow_dump_start(struct dpif_flow_dump *, const struct dpif *); bool dpif_flow_dump_next(struct dpif_flow_dump *, const struct nlattr **key, size_t *key_len, + const struct nlattr **mask, size_t *mask_len, const struct nlattr **actions, size_t *actions_len, const struct dpif_flow_stats **); int dpif_flow_dump_done(struct dpif_flow_dump *); @@ -492,6 +494,8 @@ struct dpif_flow_put { enum dpif_flow_put_flags flags; /* DPIF_FP_*. */ const struct nlattr *key; /* Flow to put. */ size_t key_len; /* Length of 'key' in bytes. */ + const struct nlattr *mask; /* Mask to put. */ + size_t mask_len; /* Length of 'mask' in bytes. */ const struct nlattr *actions; /* Actions to perform on flow. */ size_t actions_len; /* Length of 'actions' in bytes. */ diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 3e2187e3f..6f0e8f8e6 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -623,7 +623,7 @@ eth_from_packet_or_flow(const char *s) * settle for parsing a datapath key for now. */ ofpbuf_init(&odp_key, 0); - error = odp_flow_key_from_string(s, NULL, &odp_key); + error = odp_flow_from_string(s, NULL, &odp_key, NULL); if (error) { ofpbuf_uninit(&odp_key); return NULL; diff --git a/lib/odp-util.c b/lib/odp-util.c index c4127eab7..2fc61e215 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -48,9 +48,10 @@ VLOG_DEFINE_THIS_MODULE(odp_util); * from another. */ static const char *delimiters = ", \t\r\n"; -static int parse_odp_key_attr(const char *, const struct simap *port_names, - struct ofpbuf *); -static void format_odp_key_attr(const struct nlattr *a, struct ds *ds); +static int parse_odp_key_mask_attr(const char *, const struct simap *port_names, + struct ofpbuf *, struct ofpbuf *); +static void format_odp_key_attr(const struct nlattr *a, + const struct nlattr *ma, struct ds *ds); /* Returns one the following for the action with the given OVS_ACTION_ATTR_* * 'type': @@ -354,6 +355,25 @@ format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse) mpls_lse_to_bos(mpls_lse)); } +static void +format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key, + const struct ovs_key_mpls *mpls_mask) +{ + ovs_be32 key = mpls_key->mpls_lse; + + if (mpls_mask == NULL) { + format_mpls_lse(ds, key); + } else { + ovs_be32 mask = mpls_mask->mpls_lse; + + ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x", + mpls_lse_to_label(key), mpls_lse_to_label(mask), + mpls_lse_to_tc(key), mpls_lse_to_tc(mask), + mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask), + mpls_lse_to_bos(key), mpls_lse_to_bos(mask)); + } +} + static void format_odp_action(struct ds *ds, const struct nlattr *a) { @@ -378,7 +398,7 @@ format_odp_action(struct ds *ds, const struct nlattr *a) break; case OVS_ACTION_ATTR_SET: ds_put_cstr(ds, "set("); - format_odp_key_attr(nl_attr_get(a), ds); + format_odp_key_attr(nl_attr_get(a), NULL, ds); ds_put_cstr(ds, ")"); break; case OVS_ACTION_ATTR_PUSH_VLAN: @@ -580,7 +600,7 @@ parse_odp_action(const char *s, const struct simap *port_names, int retval; start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET); - retval = parse_odp_key_attr(s + 4, port_names, actions); + retval = parse_odp_key_mask_attr(s + 4, port_names, actions, NULL); if (retval < 0) { return retval; } @@ -747,10 +767,11 @@ format_generic_odp_key(const struct nlattr *a, struct ds *ds) unspec = nl_attr_get(a); for (i = 0; i < len; i++) { - ds_put_char(ds, i ? ' ': '('); + if (i) { + ds_put_char(ds, ' '); + } ds_put_format(ds, "%02x", unspec[i]); } - ds_put_char(ds, ')'); } } @@ -875,175 +896,400 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key) nl_msg_end_nested(a, tun_key_ofs); } -static void -format_odp_key_attr(const struct nlattr *a, struct ds *ds) +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; +} + + +static void +format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma, + struct ds *ds) { - const struct ovs_key_ethernet *eth_key; - const struct ovs_key_ipv4 *ipv4_key; - const struct ovs_key_ipv6 *ipv6_key; - const struct ovs_key_tcp *tcp_key; - const struct ovs_key_udp *udp_key; - const struct ovs_key_icmp *icmp_key; - const struct ovs_key_icmpv6 *icmpv6_key; - const struct ovs_key_arp *arp_key; - const struct ovs_key_nd *nd_key; struct flow_tnl tun_key; enum ovs_key_attr attr = nl_attr_type(a); char namebuf[OVS_KEY_ATTR_BUFSIZE]; int expected_len; - 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 && nl_attr_get_size(a) != expected_len) { - ds_put_format(ds, "(bad length %zu, expected %d)", - nl_attr_get_size(a), - odp_flow_key_attr_len(nl_attr_type(a))); - format_generic_odp_key(a, ds); - return; + if (ma && odp_mask_attr_is_exact(ma)) { + ma = NULL; } + 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(a) != expected_len; + + if (bad_key_len || bad_mask_len) { + if (bad_key_len) { + ds_put_format(ds, "(bad key length %zu, expected %d)(", + nl_attr_get_size(a), + odp_flow_key_attr_len(nl_attr_type(a))); + } + format_generic_odp_key(a, ds); + if (bad_mask_len) { + ds_put_char(ds, '/'); + ds_put_format(ds, "(bad mask length %zu, expected %d)(", + nl_attr_get_size(ma), + odp_flow_key_attr_len(nl_attr_type(ma))); + } + format_generic_odp_key(ma, ds); + ds_put_char(ds, ')'); + return; + } + } + } + + ds_put_char(ds, '('); switch (attr) { case OVS_KEY_ATTR_ENCAP: - ds_put_cstr(ds, "("); - if (nl_attr_get_size(a)) { - odp_flow_key_format(nl_attr_get(a), nl_attr_get_size(a), ds); + 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), ds); + } else if (nl_attr_get_size(a)) { + odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, ds); } - ds_put_char(ds, ')'); break; case OVS_KEY_ATTR_PRIORITY: - ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a)); - break; - case OVS_KEY_ATTR_SKB_MARK: - ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a)); + ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a)); + if (ma) { + 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)"); + ds_put_format(ds, "error"); + } else if (ma) { + 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"," + 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, - (uint32_t) tun_key.flags, ','); - ds_put_format(ds, "))"); + format_flags(ds, flow_tun_flag_to_string, tun_key.flags, ','); + ds_put_char(ds, ')'); } break; case OVS_KEY_ATTR_IN_PORT: - ds_put_format(ds, "(%"PRIu32")", nl_attr_get_u32(a)); + ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); + if (ma) { + ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma)); + } break; case OVS_KEY_ATTR_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)); + if (ma) { + 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: - ds_put_char(ds, '('); - format_vlan_tci(ds, nl_attr_get_be16(a)); - ds_put_char(ds, ')'); + { + ovs_be16 vlan_tci = nl_attr_get_be16(a); + if (ma) { + ovs_be16 mask = nl_attr_get_be16(ma); + ds_put_format(ds, "vid=%"PRIu16"/%"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_vid(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); - ds_put_char(ds, '('); - format_mpls_lse(ds, mpls_key->mpls_lse); - ds_put_char(ds, ')'); + const struct ovs_key_mpls *mpls_mask = NULL; + if (ma) { + mpls_mask = nl_attr_get(ma); + } + format_mpls(ds, mpls_key, mpls_mask); break; } case OVS_KEY_ATTR_ETHERTYPE: - ds_put_format(ds, "(0x%04"PRIx16")", - ntohs(nl_attr_get_be16(a))); + ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a))); + if (ma) { + ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma))); + } break; case OVS_KEY_ATTR_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)); + if (ma) { + 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: { - char src_str[INET6_ADDRSTRLEN]; - char dst_str[INET6_ADDRSTRLEN]; + case OVS_KEY_ATTR_IPV6: + if (ma) { + 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_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)); + 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: - tcp_key = nl_attr_get(a); - ds_put_format(ds, "(src=%"PRIu16",dst=%"PRIu16")", - ntohs(tcp_key->tcp_src), ntohs(tcp_key->tcp_dst)); + if (ma) { + 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_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)); + if (ma) { + 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_ICMP: - icmp_key = nl_attr_get(a); - ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")", - icmp_key->icmp_type, icmp_key->icmp_code); + if (ma) { + 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: - icmpv6_key = nl_attr_get(a); - ds_put_format(ds, "(type=%"PRIu8",code=%"PRIu8")", - icmpv6_key->icmpv6_type, icmpv6_key->icmpv6_code); + if (ma) { + 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: - 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)); + if (ma) { + 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; char target[INET6_ADDRSTRLEN]; nd_key = nl_attr_get(a); - inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target); + nd_mask = ma ? nl_attr_get(ma) : NULL; + + inet_ntop(AF_INET6, nd_key->nd_target, target, sizeof target); + ds_put_format(ds, "target=%s", target); + if (nd_mask) { + inet_ntop(AF_INET6, nd_mask->nd_target, target, sizeof target); + ds_put_format(ds, "/%s", target); + } - ds_put_format(ds, "(target=%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 (nd_mask) { + 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 (nd_mask) { + ds_put_format(ds, "/"ETH_ADDR_FMT, + ETH_ADDR_ARGS(nd_mask->nd_tll)); + } } - ds_put_char(ds, ')'); break; } @@ -1051,24 +1297,40 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) case __OVS_KEY_ATTR_MAX: default: format_generic_odp_key(a, ds); + if (ma) { + ds_put_char(ds, '/'); + format_generic_odp_key(ma, ds); + } break; } + ds_put_char(ds, ')'); } /* Appends to 'ds' a string representation of the 'key_len' bytes of - * OVS_KEY_ATTR_* attributes in 'key'. */ + * OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the + * 'mask_len' bytes of 'mask' which apply to 'key'. */ void -odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) +odp_flow_format(const struct nlattr *key, size_t key_len, + const struct nlattr *mask, size_t mask_len, + struct ds *ds) { if (key_len) { const struct nlattr *a; unsigned int left; + bool has_ethtype_key = false; + const struct nlattr *ma = NULL; NL_ATTR_FOR_EACH (a, left, key, key_len) { if (a != key) { ds_put_char(ds, ','); } - format_odp_key_attr(a, ds); + if (nl_attr_type(a) == OVS_KEY_ATTR_ETHERTYPE) { + has_ethtype_key = true; + } + if (mask && mask_len) { + ma = nl_attr_find__(mask, mask_len, nl_attr_type(a)); + } + format_odp_key_attr(a, ma, ds); } if (left) { int i; @@ -1082,11 +1344,27 @@ odp_flow_key_format(const struct nlattr *key, size_t key_len, struct ds *ds) } 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, ""); } } +/* 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, ds); +} + 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) @@ -1132,8 +1410,8 @@ mpls_lse_from_components(int mpls_label, int mpls_tc, int mpls_ttl, int mpls_bos } static int -parse_odp_key_attr(const char *s, const struct simap *port_names, - struct ofpbuf *key) +parse_odp_key_mask_attr(const char *s, const struct simap *port_names, + struct ofpbuf *key, struct ofpbuf *mask) { /* Many of the sscanf calls in this function use oversized destination * fields because some sscanf() implementations truncate the range of %i @@ -1147,31 +1425,87 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, { unsigned long long int priority; + unsigned long long int priority_mask; int n = -1; - if (sscanf(s, "skb_priority(%llx)%n", &priority, &n) > 0 && n > 0) { + if (mask && sscanf(s, "skb_priority(%lli/%lli)%n", &priority, + &priority_mask, &n) > 0 && n > 0) { 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 (sscanf(s, "skb_priority(%lli)%n", + &priority, &n) > 0 && n > 0) { + 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; } } { unsigned long long int mark; + unsigned long long int mark_mask; int n = -1; - if (sscanf(s, "skb_mark(%llx)%n", &mark, &n) > 0 && n > 0) { + if (mask && sscanf(s, "skb_mark(%lli/%lli)%n", &mark, + &mark_mask, &n) > 0 && n > 0) { 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 (sscanf(s, "skb_mark(%lli)%n", &mark, &n) > 0 && n > 0) { + 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; } } { char tun_id_s[32]; - int tos, ttl; - struct flow_tnl tun_key; + int tos, tos_mask, ttl, ttl_mask; + struct flow_tnl tun_key, tun_key_mask; + unsigned long long tun_id_mask; int n = -1; - if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]," + if (mask && sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]/%llx," + "src="IP_SCAN_FMT"/"IP_SCAN_FMT",dst="IP_SCAN_FMT + "/"IP_SCAN_FMT",tos=%i/%i,ttl=%i/%i,flags%n", + tun_id_s, &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), + &tos, &tos_mask, &ttl, &ttl_mask, + &n) > 0 && n > 0) { + int res; + uint32_t flags; + + tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0)); + tun_key_mask.tun_id = htonll(tun_id_mask); + tun_key.ip_tos = tos; + tun_key_mask.ip_tos = tos_mask; + tun_key.ip_ttl = ttl; + tun_key_mask.ip_ttl = ttl_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 (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF]," "src="IP_SCAN_FMT",dst="IP_SCAN_FMT ",tos=%i,ttl=%i,flags%n", tun_id_s, IP_SCAN_ARGS(&tun_key.ip_src), @@ -1184,7 +1518,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, tun_key.ip_tos = tos; tun_key.ip_ttl = ttl; res = parse_flags(&s[n], flow_tun_flag_to_string, &flags); - tun_key.flags = (uint16_t) flags; + tun_key.flags = flags; if (res < 0) { return res; @@ -1195,20 +1529,35 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, } 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; } } { unsigned long long int in_port; + unsigned long long int in_port_mask; int n = -1; - if (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) { + if (mask && sscanf(s, "in_port(%lli/%lli)%n", &in_port, + &in_port_mask, &n) > 0 && n > 0) { 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 (sscanf(s, "in_port(%lli)%n", &in_port, &n) > 0 && n > 0) { + 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; @@ -1219,63 +1568,140 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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 (sscanf(s, + if (mask && sscanf(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) > 0 && n > 0) { + + 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 (sscanf(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) > 0 && n > 0) { 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; } } { - uint16_t vid; - int pcp; - int cfi; + uint16_t vid, vid_mask; + int pcp, pcp_mask; + int cfi, cfi_mask; int n = -1; - if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", &vid, &pcp, &n) > 0 - && n > 0)) { + if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i)%n", + &vid, &vid_mask, &pcp, &pcp_mask, &n) > 0 && n > 0)) { 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 ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n", - &vid, &pcp, &cfi, &n) > 0 - && n > 0)) { + } else if ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i)%n", + &vid, &pcp, &n) > 0 && n > 0)) { + 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, htons(UINT16_MAX)); + } + return n; + } else if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i,cfi=%i/%i)%n", + &vid, &vid_mask, &pcp, &pcp_mask, &cfi, &cfi_mask, &n) > 0 && n > 0)) { 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 ((sscanf(s, "vlan(vid=%"SCNi16",pcp=%i,cfi=%i)%n", + &vid, &pcp, &cfi, &n) > 0 && n > 0)) { + 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, htons(UINT16_MAX)); + } return n; } } { int eth_type; + int eth_type_mask; int n = -1; - if (sscanf(s, "eth_type(%i)%n", ð_type, &n) > 0 && n > 0) { + if (mask && sscanf(s, "eth_type(%i/%i)%n", + ð_type, ð_type_mask, &n) > 0 && n > 0) { + 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 (sscanf(s, "eth_type(%i)%n", ð_type, &n) > 0 && n > 0) { nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type)); + if (mask) { + nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, + htons(UINT16_MAX)); + } return n; } } { int label, tc, ttl, bos; + int label_mask, tc_mask, ttl_mask, bos_mask; int n = -1; - if (sscanf(s, "mpls(label=%"SCNi32",tc=%i,ttl=%i,bos=%i)%n", + if (mask && sscanf(s, "mpls(label=%"SCNi32"/%"SCNi32",tc=%i/%i,ttl=%i/%i,bos=%i/%i)%n", + &label, &label_mask, &tc, &tc_mask, &ttl, &ttl_mask, &bos, &bos_mask, &n) > 0 && n > 0) { + 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 (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; @@ -1283,21 +1709,60 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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 = htonl(UINT32_MAX); + } return n; } } + { - ovs_be32 ipv4_src; - ovs_be32 ipv4_dst; - int ipv4_proto; - int ipv4_tos; - int ipv4_ttl; + ovs_be32 ipv4_src, ipv4_src_mask; + ovs_be32 ipv4_dst, ipv4_dst_mask; + int ipv4_proto, ipv4_proto_mask; + int ipv4_tos, ipv4_tos_mask; + int ipv4_ttl, ipv4_ttl_mask; char frag[8]; + int ipv4_frag_mask; enum ovs_frag_type ipv4_frag; int n = -1; - if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT"," + if (mask && sscanf(s, "ipv4(src="IP_SCAN_FMT"/"IP_SCAN_FMT"," + "dst="IP_SCAN_FMT"/"IP_SCAN_FMT"," + "proto=%i/%i,tos=%i/%i,ttl=%i/%i," + "frag=%7[a-z]/%i)%n", + IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_src_mask), + IP_SCAN_ARGS(&ipv4_dst), IP_SCAN_ARGS(&ipv4_dst_mask), + &ipv4_proto, &ipv4_proto_mask, + &ipv4_tos, &ipv4_tos_mask, &ipv4_ttl, &ipv4_ttl_mask, + frag, &ipv4_frag_mask, &n) > 0 + && n > 0 + && ovs_frag_type_from_string(frag, &ipv4_frag)) { + struct ovs_key_ipv4 ipv4_key; + struct ovs_key_ipv4 ipv4_mask; + + 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); + + ipv4_mask.ipv4_src = ipv4_src_mask; + ipv4_mask.ipv4_dst = ipv4_dst_mask; + ipv4_mask.ipv4_proto = ipv4_proto_mask; + ipv4_mask.ipv4_tos = ipv4_tos_mask; + ipv4_mask.ipv4_ttl = ipv4_ttl_mask; + ipv4_mask.ipv4_frag = ipv4_frag_mask; + nl_msg_put_unspec(mask, OVS_KEY_ATTR_IPV4, + &ipv4_mask, sizeof ipv4_mask); + return n; + } else 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 @@ -1313,22 +1778,68 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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]; - int ipv6_label; - int ipv6_proto; - int ipv6_tclass; - int ipv6_hlimit; + 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 (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT"," + if (mask && sscanf(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) > 0 + && n > 0 + && 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 (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 @@ -1347,6 +1858,12 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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; } } @@ -1354,15 +1871,38 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, { int tcp_src; int tcp_dst; + int tcp_src_mask; + int tcp_dst_mask; int n = -1; - if (sscanf(s, "tcp(src=%i,dst=%i)%n",&tcp_src, &tcp_dst, &n) > 0 + if (mask && sscanf(s, "tcp(src=%i/%i,dst=%i/%i)%n", + &tcp_src, &tcp_src_mask, &tcp_dst, &tcp_dst_mask, &n) > 0 + && n > 0) { + 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 (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); + + 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; } } @@ -1370,8 +1910,26 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, { int udp_src; int udp_dst; + int udp_src_mask; + int udp_dst_mask; int n = -1; + if (mask && sscanf(s, "udp(src=%i/%i,dst=%i/%i)%n", + &udp_src, &udp_src_mask, + &udp_dst, &udp_dst_mask, &n) > 0 && n > 0) { + 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 (sscanf(s, "udp(src=%i,dst=%i)%n", &udp_src, &udp_dst, &n) > 0 && n > 0) { struct ovs_key_udp udp_key; @@ -1379,6 +1937,11 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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; } } @@ -1386,9 +1949,27 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, { int icmp_type; int icmp_code; + int icmp_type_mask; + int icmp_code_mask; int n = -1; - if (sscanf(s, "icmp(type=%i,code=%i)%n", + if (mask && sscanf(s, "icmp(type=%i/%i,code=%i/%i)%n", + &icmp_type, &icmp_type_mask, + &icmp_code, &icmp_code_mask, &n) > 0 && n > 0) { + struct ovs_key_icmp icmp_key; + struct ovs_key_icmp icmp_mask; + + 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); + + icmp_mask.icmp_type = icmp_type_mask; + icmp_mask.icmp_code = icmp_code_mask; + nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMP, + &icmp_mask, sizeof icmp_mask); + return n; + } else if (sscanf(s, "icmp(type=%i,code=%i)%n", &icmp_type, &icmp_code, &n) > 0 && n > 0) { struct ovs_key_icmp icmp_key; @@ -1397,32 +1978,90 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, icmp_key.icmp_code = icmp_code; 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 icmpv6_type_mask; + int icmpv6_code_mask; int n = -1; - if (sscanf(s, "icmpv6(type=%"SCNi8",code=%"SCNi8")%n", + if (mask && sscanf(s, "icmpv6(type=%"SCNi8"/%i,code=%"SCNi8"/%i)%n", + &icmpv6_key.icmpv6_type, &icmpv6_type_mask, + &icmpv6_key.icmpv6_code, &icmpv6_code_mask, &n) > 0 + && n > 0) { + nl_msg_put_unspec(key, OVS_KEY_ATTR_ICMPV6, + &icmpv6_key, sizeof icmpv6_key); + + icmpv6_mask.icmpv6_type = icmpv6_type_mask; + icmpv6_mask.icmpv6_code = icmpv6_code_mask; + nl_msg_put_unspec(mask, OVS_KEY_ATTR_ICMPV6, &icmpv6_mask, + sizeof icmpv6_mask); + return n; + } else 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); + + 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; } } { - ovs_be32 arp_sip; - ovs_be32 arp_tip; - int arp_op; + ovs_be32 arp_sip, arp_sip_mask; + ovs_be32 arp_tip, arp_tip_mask; + int arp_op, arp_op_mask; uint8_t arp_sha[ETH_ADDR_LEN]; + uint8_t arp_sha_mask[ETH_ADDR_LEN]; uint8_t arp_tha[ETH_ADDR_LEN]; + uint8_t arp_tha_mask[ETH_ADDR_LEN]; int n = -1; - if (sscanf(s, "arp(sip="IP_SCAN_FMT",tip="IP_SCAN_FMT"," + if (mask && sscanf(s, "arp(sip="IP_SCAN_FMT"/"IP_SCAN_FMT"," + "tip="IP_SCAN_FMT"/"IP_SCAN_FMT"," + "op=%i/%i,sha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT"," + "tha="ETH_ADDR_SCAN_FMT"/"ETH_ADDR_SCAN_FMT")%n", + IP_SCAN_ARGS(&arp_sip), IP_SCAN_ARGS(&arp_sip_mask), + IP_SCAN_ARGS(&arp_tip), IP_SCAN_ARGS(&arp_tip_mask), + &arp_op, &arp_op_mask, + ETH_ADDR_SCAN_ARGS(arp_sha), + ETH_ADDR_SCAN_ARGS(arp_sha_mask), + ETH_ADDR_SCAN_ARGS(arp_tha), + ETH_ADDR_SCAN_ARGS(arp_tha_mask), &n) > 0 && n > 0) { + struct ovs_key_arp arp_key; + struct ovs_key_arp arp_mask; + + 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); + + arp_mask.arp_sip = arp_sip_mask; + arp_mask.arp_tip = arp_tip_mask; + arp_mask.arp_op = htons(arp_op_mask); + memcpy(arp_mask.arp_sha, arp_sha_mask, ETH_ADDR_LEN); + memcpy(arp_mask.arp_tha, arp_tha_mask, ETH_ADDR_LEN); + nl_msg_put_unspec(mask, OVS_KEY_ATTR_ARP, + &arp_mask, sizeof arp_mask); + return n; + } else 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), @@ -1438,44 +2077,101 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, 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); + + 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; - if (sscanf(s, "nd(target="IPV6_SCAN_FMT")%n", + memset(&nd_target_mask_s[0], 0xff, sizeof nd_target_s); + memset(&nd_sll_mask[0], 0xff, sizeof nd_sll); + memset(&nd_tll_mask [0], 0xff, sizeof nd_tll); + + if (mask && sscanf(s, "nd(target="IPV6_SCAN_FMT"/"IPV6_SCAN_FMT")%n", + nd_target_s, nd_target_mask_s, &n) > 0 && n > 0) { + put_nd_key(n, nd_target_s, NULL, NULL, key); + put_nd_key(n, nd_target_mask_s, NULL, NULL, mask); + } else 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", + put_nd_key(n, nd_target_s, NULL, NULL, key); + if (mask) { + put_nd_key(n, nd_target_mask_s, NULL, NULL, mask); + } + } else if (mask && sscanf(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) > 0 && n > 0) { + put_nd_key(n, nd_target_s, nd_sll, NULL, key); + put_nd_key(n, nd_target_mask_s, nd_sll_mask, NULL, mask); + } else 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", + put_nd_key(n, nd_target_s, nd_sll, NULL, key); + if (mask) { + put_nd_key(n, nd_target_mask_s, nd_sll_mask, NULL, mask); + } + } else if (mask && sscanf(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) > 0 && n > 0) { + put_nd_key(n, nd_target_s, NULL, nd_tll, key); + put_nd_key(n, nd_target_mask_s, NULL, nd_tll_mask, mask); + } else 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"," + put_nd_key(n, nd_target_s, NULL, nd_tll, key); + if (mask) { + put_nd_key(n, nd_target_mask_s, NULL, nd_tll_mask, mask); + } + } else if (mask && sscanf(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) > 0 + && n > 0) { + put_nd_key(n, nd_target_s, nd_sll, nd_tll, key); + put_nd_key(n, nd_target_mask_s, nd_sll_mask, nd_tll_mask, mask); + } else 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); + put_nd_key(n, nd_target_s, nd_sll, nd_tll, key); + if (mask) { + put_nd_key(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; + 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 (;;) { @@ -1488,7 +2184,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, break; } - retval = parse_odp_key_attr(s, port_names, key); + retval = parse_odp_key_mask_attr(s, port_names, key, mask); if (retval < 0) { return retval; } @@ -1497,6 +2193,9 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, s++; nl_msg_end_nested(key, encap); + if (mask) { + nl_msg_end_nested(mask, encap_mask); + } return s - start; } @@ -1519,8 +2218,8 @@ parse_odp_key_attr(const char *s, const struct simap *port_names, * 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) +odp_flow_from_string(const char *s, const struct simap *port_names, + struct ofpbuf *key, struct ofpbuf *mask) { const size_t old_size = key->size; for (;;) { @@ -1531,7 +2230,7 @@ odp_flow_key_from_string(const char *s, const struct simap *port_names, return 0; } - retval = parse_odp_key_attr(s, port_names, key); + retval = parse_odp_key_mask_attr(s, port_names, key, mask); if (retval < 0) { key->size = old_size; return -retval; diff --git a/lib/odp-util.h b/lib/odp-util.h index 7d4add1b1..d38fb82f6 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -91,9 +91,13 @@ struct odputil_keybuf { enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, struct flow_tnl *); +void odp_flow_format(const struct nlattr *key, size_t key_len, + const struct nlattr *mask, size_t mask_len, + struct ds *); void odp_flow_key_format(const struct nlattr *, size_t, struct ds *); -int odp_flow_key_from_string(const char *s, const struct simap *port_names, - struct ofpbuf *); +int odp_flow_from_string(const char *s, + const struct simap *port_names, + struct ofpbuf *, struct ofpbuf *); void odp_flow_key_from_flow(struct ofpbuf *, const struct flow *, uint32_t odp_in_port); diff --git a/lib/packets.h b/lib/packets.h index b73ff63f7..cc9ab3df6 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -259,6 +259,7 @@ BUILD_ASSERT_DECL(LLC_SNAP_HEADER_LEN == sizeof(struct llc_snap_header)); #define VLAN_PCP_SHIFT 13 #define VLAN_CFI 0x1000 +#define VLAN_CFI_SHIFT 12 /* Given the vlan_tci field from an 802.1Q header, in network byte order, * returns the VLAN ID in host byte order. */ @@ -276,6 +277,14 @@ vlan_tci_to_pcp(ovs_be16 vlan_tci) return (ntohs(vlan_tci) & VLAN_PCP_MASK) >> VLAN_PCP_SHIFT; } +/* Given the vlan_tci field from an 802.1Q header, in network byte order, + * returns the Canonical Format Indicator (CFI). */ +static inline int +vlan_tci_to_cfi(ovs_be16 vlan_tci) +{ + return (vlan_tci & htons(VLAN_CFI)) != 0; +} + #define VLAN_HEADER_LEN 4 struct vlan_header { ovs_be16 vlan_tci; /* Lowest 12 bits are VLAN ID. */ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 6f7fd0d68..3ae3cb868 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -3461,6 +3461,8 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY; put->key = miss->key; put->key_len = miss->key_len; + put->mask = NULL; + put->mask_len = 0; if (want_path == SF_FAST_PATH) { put->actions = facet->xout.odp_actions.data; put->actions_len = facet->xout.odp_actions.size; @@ -3721,7 +3723,8 @@ handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls, hmap_insert(&backer->drop_keys, &drop_key->hmap_node, hash_bytes(drop_key->key, drop_key->key_len, 0)); dpif_flow_put(backer->dpif, DPIF_FP_CREATE | DPIF_FP_MODIFY, - drop_key->key, drop_key->key_len, NULL, 0, NULL); + drop_key->key, drop_key->key_len, + NULL, 0, NULL, 0, NULL); } continue; } @@ -4117,7 +4120,8 @@ update_stats(struct dpif_backer *backer) size_t key_len; dpif_flow_dump_start(&dump, backer->dpif); - while (dpif_flow_dump_next(&dump, &key, &key_len, NULL, NULL, &stats)) { + while (dpif_flow_dump_next(&dump, &key, &key_len, + NULL, NULL, NULL, NULL, &stats)) { struct subfacet *subfacet; uint32_t key_hash; @@ -5022,8 +5026,9 @@ subfacet_install(struct subfacet *subfacet, const struct ofpbuf *odp_actions, &actions, &actions_len); } - ret = dpif_flow_put(subfacet->backer->dpif, flags, subfacet->key, - subfacet->key_len, actions, actions_len, stats); + ret = dpif_flow_put(ofproto->backer->dpif, flags, subfacet->key, + subfacet->key_len, NULL, 0, + actions, actions_len, stats); if (stats) { subfacet_reset_dp_stats(subfacet, stats); @@ -5868,7 +5873,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], * bridge is specified. If function odp_flow_key_from_string() * returns 0, the flow is a odp_flow. If function * parse_ofp_exact_flow() returns 0, the flow is a br_flow. */ - if (!odp_flow_key_from_string(argv[argc - 1], NULL, &odp_key)) { + if (!odp_flow_from_string(argv[argc - 1], NULL, &odp_key, NULL)) { /* If the odp_flow is the second argument, * the datapath name is the first argument. */ if (argc == 3) { diff --git a/tests/odp.at b/tests/odp.at index fd6a192ee..062f82880 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -80,6 +80,71 @@ AT_CHECK_UNQUOTED([test-odp parse-keys < odp.txt], [0], [`cat odp.txt` ]) AT_CLEANUP +AT_SETUP([OVS datapath wildcarded key parsing and formatting - valid forms]) +dnl We could add a test for invalid forms, but that's less important. +AT_DATA([odp-base.txt], [dnl +in_port(1/0xff),eth(src=00:01:02:03:04:05/ff:ff:ff:ff:ff:f0,dst=10:11:12:13:14:15/ff:ff:ff:ff:ff:f0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1/0xf0,code=2/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tclass=0,hlimit=128,frag=no),udp(src=6630/0xff00,dst=22/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=1/0xf0,code=2/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3/::250) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3/::250,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) +skb_mark(0x1234/0xfff0),in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) +]) + +(echo '# Valid forms without tun_id or VLAN header.' + cat odp-base.txt + + echo + echo '# Valid forms with tunnel header.' + sed 's/^/tunnel(tun_id=0x7f10354\/0xff,src=10.10.10.10\/255.255.255.0,dst=20.20.20.20\/255.255.255.0,tos=0\/0xff,ttl=64\/0xff,flags(csum,key)),/' odp-base.txt + + echo + echo '# Valid forms with VLAN header.' + sed 's/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ +s/$/)/' odp-base.txt + + echo + echo '# Valid forms with MPLS header.' + sed 's/\(eth([[^)]]*),?\)/\1,eth_type(0x8847),mpls(label=100\/0xff,tc=7\/7,ttl=64\/0xff,bos=1\/1)/' odp-base.txt + + echo + echo '# Valid forms with QoS priority.' + sed 's/^/skb_priority(0x1234\/0xff),/' odp-base.txt + + echo + echo '# Valid forms with tunnel and VLAN headers.' + sed 's/^/tunnel(tun_id=0xfedcba9876543210,src=10.0.0.1,dst=10.0.0.2,tos=0x8,ttl=128,flags(key)),/ +s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99/0xff0,pcp=7/0xe),encap(/ +s/$/)/' odp-base.txt + + echo + echo '# Valid forms with QOS priority, tunnel, and VLAN headers.' + sed 's/^/skb_priority(0x1234),tunnel(tun_id=0xfedcba9876543210,src=10.10.10.10,dst=20.20.20.20,tos=0x8,ttl=64,flags(key)),/ +s/\(eth([[^)]]*)\),*/\1,eth_type(0x8100),vlan(vid=99,pcp=7),encap(/ +s/$/)/' odp-base.txt + + echo + echo '# Valid forms with IP first fragment.' +sed -n 's/,frag=no),/,frag=first),/p' odp-base.txt + + echo + echo '# Valid forms with IP later fragment.' +sed -n 's/,frag=no),.*/,frag=later)/p' odp-base.txt) > odp.txt +AT_CAPTURE_FILE([odp.txt]) +AT_CHECK_UNQUOTED([test-odp parse-wc-keys < odp.txt], [0], [`cat odp.txt` +]) +AT_CLEANUP + AT_SETUP([OVS datapath actions parsing and formatting - valid forms]) AT_DATA([actions.txt], [dnl 1,2,3 diff --git a/tests/test-odp.c b/tests/test-odp.c index 268a10518..3bb3a0cb8 100644 --- a/tests/test-odp.c +++ b/tests/test-odp.c @@ -26,7 +26,7 @@ #include "vlog.h" static int -parse_keys(void) +parse_keys(bool wc_keys) { int exit_code = 0; struct ds in; @@ -36,51 +36,60 @@ parse_keys(void) while (!ds_get_test_line(&in, stdin)) { enum odp_key_fitness fitness; struct ofpbuf odp_key; + struct ofpbuf odp_mask; struct flow flow; struct ds out; int error; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); - error = odp_flow_key_from_string(ds_cstr(&in), NULL, &odp_key); + ofpbuf_init(&odp_mask, 0); + error = odp_flow_from_string(ds_cstr(&in), NULL, + &odp_key, &odp_mask); if (error) { - printf("odp_flow_key_from_string: error\n"); + printf("odp_flow_from_string: error\n"); goto next; } - /* Convert odp_key to flow. */ - fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); - switch (fitness) { - case ODP_FIT_PERFECT: - break; + if (!wc_keys) { + /* Convert odp_key to flow. */ + fitness = odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); + switch (fitness) { + case ODP_FIT_PERFECT: + break; - case ODP_FIT_TOO_LITTLE: - printf("ODP_FIT_TOO_LITTLE: "); - break; + case ODP_FIT_TOO_LITTLE: + printf("ODP_FIT_TOO_LITTLE: "); + break; - case ODP_FIT_TOO_MUCH: - printf("ODP_FIT_TOO_MUCH: "); - break; + case ODP_FIT_TOO_MUCH: + printf("ODP_FIT_TOO_MUCH: "); + break; - case ODP_FIT_ERROR: - printf("odp_flow_key_to_flow: error\n"); - goto next; - } + case ODP_FIT_ERROR: + printf("odp_flow_key_to_flow: error\n"); + goto next; + } + /* Convert cls_rule back to odp_key. */ + ofpbuf_uninit(&odp_key); + ofpbuf_init(&odp_key, 0); + odp_flow_key_from_flow(&odp_key, &flow, flow.in_port); - /* Convert cls_rule back to odp_key. */ - ofpbuf_uninit(&odp_key); - ofpbuf_init(&odp_key, 0); - odp_flow_key_from_flow(&odp_key, &flow, flow.in_port); - - if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) { - printf ("too long: %zu > %d\n", - odp_key.size, ODPUTIL_FLOW_KEY_BYTES); - exit_code = 1; + if (odp_key.size > ODPUTIL_FLOW_KEY_BYTES) { + printf ("too long: %zu > %d\n", + odp_key.size, ODPUTIL_FLOW_KEY_BYTES); + exit_code = 1; + } } /* Convert odp_key to string. */ ds_init(&out); - odp_flow_key_format(odp_key.data, odp_key.size, &out); + if (wc_keys) { + odp_flow_format(odp_key.data, odp_key.size, + odp_mask.data, odp_mask.size, &out); + } else { + odp_flow_key_format(odp_key.data, odp_key.size, &out); + } puts(ds_cstr(&out)); ds_destroy(&out); @@ -130,10 +139,12 @@ int main(int argc, char *argv[]) { if (argc == 2 &&!strcmp(argv[1], "parse-keys")) { - return parse_keys(); + return parse_keys(false); + } else if (argc == 2 &&!strcmp(argv[1], "parse-wc-keys")) { + return parse_keys(true); } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) { return parse_actions(); } else { - ovs_fatal(0, "usage: %s parse-keys | parse-actions", argv[0]); + ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions", argv[0]); } } diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c index 54505e85a..c82171afb 100644 --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@ -743,9 +743,11 @@ dpctl_dump_flows(int argc, char *argv[]) const struct nlattr *actions; struct dpif_flow_dump dump; const struct nlattr *key; + const struct nlattr *mask; size_t actions_len; struct dpif *dpif; size_t key_len; + size_t mask_len; struct ds ds; char *name; @@ -756,10 +758,12 @@ dpctl_dump_flows(int argc, char *argv[]) ds_init(&ds); dpif_flow_dump_start(&dump, dpif); while (dpif_flow_dump_next(&dump, &key, &key_len, + &mask, &mask_len, &actions, &actions_len, &stats)) { ds_clear(&ds); - odp_flow_key_format(key, key_len, &ds); + odp_flow_format(key, key_len, mask, mask_len, &ds); ds_put_cstr(&ds, ", "); + dpif_flow_stats_format(stats, &ds); ds_put_cstr(&ds, ", actions:"); format_odp_actions(&ds, actions, actions_len); @@ -778,26 +782,32 @@ dpctl_put_flow(int argc, char *argv[], enum dpif_flow_put_flags flags) struct dpif_flow_stats stats; struct ofpbuf actions; struct ofpbuf key; + struct ofpbuf mask; struct dpif *dpif; + struct ds s; char *dp_name; + ds_init(&s); ofpbuf_init(&key, 0); - run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key"); + ofpbuf_init(&mask, 0); + run(odp_flow_from_string(key_s, NULL, &key, &mask), "parsing flow key"); ofpbuf_init(&actions, 0); run(odp_actions_from_string(actions_s, NULL, &actions), "parsing actions"); - dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp(); + dp_name = argc == 4 ? xstrdup(argv[1]) : get_one_dp(); run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath"); free(dp_name); run(dpif_flow_put(dpif, flags, key.data, key.size, + mask.size == 0 ? NULL : mask.data, mask.size, actions.data, actions.size, print_statistics ? &stats : NULL), "updating flow table"); ofpbuf_uninit(&key); + ofpbuf_uninit(&mask); ofpbuf_uninit(&actions); if (print_statistics) { @@ -838,11 +848,13 @@ dpctl_del_flow(int argc, char *argv[]) const char *key_s = argv[argc - 1]; struct dpif_flow_stats stats; struct ofpbuf key; + struct ofpbuf mask; /* To be ignored. */ struct dpif *dpif; char *dp_name; ofpbuf_init(&key, 0); - run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key"); + ofpbuf_init(&mask, 0); + run(odp_flow_from_string(key_s, NULL, &key, &mask), "parsing flow key"); dp_name = argc == 2 ? xstrdup(argv[1]) : get_one_dp(); run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath"); @@ -853,6 +865,7 @@ dpctl_del_flow(int argc, char *argv[]) print_statistics ? &stats : NULL), "deleting flow"); ofpbuf_uninit(&key); + ofpbuf_uninit(&mask); if (print_statistics) { struct ds s; @@ -1032,11 +1045,11 @@ dpctl_normalize_actions(int argc, char *argv[]) /* Parse flow key. */ ofpbuf_init(&keybuf, 0); - run(odp_flow_key_from_string(argv[1], &port_names, &keybuf), + run(odp_flow_from_string(argv[1], &port_names, &keybuf, NULL), "odp_flow_key_from_string"); ds_clear(&s); - odp_flow_key_format(keybuf.data, keybuf.size, &s); + odp_flow_format(keybuf.data, keybuf.size, NULL, 0, &s); printf("input flow: %s\n", ds_cstr(&s)); run(odp_flow_key_to_flow(keybuf.data, keybuf.size, &flow),