return ds_cstr(&ds);
}
+const char *
+flow_tun_flag_to_string(uint32_t flags)
+{
+ switch (flags) {
+ case FLOW_TNL_F_DONT_FRAGMENT:
+ return "df";
+ case FLOW_TNL_F_CSUM:
+ return "csum";
+ case FLOW_TNL_F_KEY:
+ return "key";
+ default:
+ return NULL;
+ }
+}
+
+void
+format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
+ uint32_t flags, char del)
+{
+ uint32_t bad = 0;
+
+ if (!flags) {
+ return;
+ }
+ while (flags) {
+ uint32_t bit = rightmost_1bit(flags);
+ const char *s;
+
+ s = bit_to_string(bit);
+ if (s) {
+ ds_put_format(ds, "%s%c", s, del);
+ } else {
+ bad |= bit;
+ }
+
+ flags &= ~bit;
+ }
+
+ if (bad) {
+ ds_put_format(ds, "0x%"PRIx32"%c", bad, del);
+ }
+ ds_chomp(ds, del);
+}
+
void
flow_format(struct ds *ds, const struct flow *flow)
{
#define FLOW_TNL_F_DONT_FRAGMENT (1 << 0)
#define FLOW_TNL_F_CSUM (1 << 1)
#define FLOW_TNL_F_KEY (1 << 2)
+
+const char *flow_tun_flag_to_string(uint32_t flags);
+
struct flow_tnl {
ovs_be64 tun_id;
ovs_be32 ip_src;
void flow_get_metadata(const struct flow *, struct flow_metadata *);
char *flow_to_string(const struct flow *);
+void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
+ uint32_t flags, char del);
+
void flow_format(struct ds *, const struct flow *);
void flow_print(FILE *, const struct flow *);
static inline int flow_compare_3way(const struct flow *, const struct flow *);
}
}
- if (flow->tunnel.ip_dst || flow->tunnel.tun_id) {
- memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+ if (flow->tunnel.ip_dst) {
+ if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
+ memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
+ }
memset(&wc->masks.tunnel.ip_src, 0xff, sizeof wc->masks.tunnel.ip_src);
memset(&wc->masks.tunnel.ip_dst, 0xff, sizeof wc->masks.tunnel.ip_dst);
memset(&wc->masks.tunnel.flags, 0xff, sizeof wc->masks.tunnel.flags);
memset(&wc->masks.tunnel.ip_tos, 0xff, sizeof wc->masks.tunnel.ip_tos);
memset(&wc->masks.tunnel.ip_ttl, 0xff, sizeof wc->masks.tunnel.ip_ttl);
+ } else if (flow->tunnel.tun_id) {
+ memset(&wc->masks.tunnel.tun_id, 0xff, sizeof wc->masks.tunnel.tun_id);
}
+
memset(&wc->masks.metadata, 0xff, sizeof wc->masks.metadata);
memset(&wc->masks.in_port, 0xff, sizeof wc->masks.in_port);
memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
match->flow.tunnel.tun_id = tun_id & mask;
}
+void
+match_set_tun_src(struct match *match, ovs_be32 src)
+{
+ match_set_tun_src_masked(match, src, htonl(UINT32_MAX));
+}
+
+void
+match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask)
+{
+ match->wc.masks.tunnel.ip_src = mask;
+ match->flow.tunnel.ip_src = src & mask;
+}
+
+void
+match_set_tun_dst(struct match *match, ovs_be32 dst)
+{
+ match_set_tun_dst_masked(match, dst, htonl(UINT32_MAX));
+}
+
+void
+match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask)
+{
+ match->wc.masks.tunnel.ip_dst = mask;
+ match->flow.tunnel.ip_dst = dst & mask;
+}
+
+void
+match_set_tun_ttl(struct match *match, uint8_t ttl)
+{
+ match_set_tun_ttl_masked(match, ttl, UINT8_MAX);
+}
+
+void
+match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask)
+{
+ match->wc.masks.tunnel.ip_ttl = mask;
+ match->flow.tunnel.ip_ttl = ttl & mask;
+}
+
+void
+match_set_tun_tos(struct match *match, uint8_t tos)
+{
+ match_set_tun_tos_masked(match, tos, UINT8_MAX);
+}
+
+void
+match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask)
+{
+ match->wc.masks.tunnel.ip_tos = mask;
+ match->flow.tunnel.ip_tos = tos & mask;
+}
+
+void
+match_set_tun_flags(struct match *match, uint16_t flags)
+{
+ match_set_tun_flags_masked(match, flags, UINT16_MAX);
+}
+
+void
+match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask)
+{
+ match->wc.masks.tunnel.flags = mask;
+ match->flow.tunnel.flags = flags & mask;
+}
+
void
match_set_in_port(struct match *match, uint16_t ofp_port)
{
}
}
+static void
+format_flow_tunnel(struct ds *s, const struct match *match)
+{
+ const struct flow_wildcards *wc = &match->wc;
+ const struct flow_tnl *tnl = &match->flow.tunnel;
+
+ switch (wc->masks.tunnel.tun_id) {
+ case 0:
+ break;
+ case CONSTANT_HTONLL(UINT64_MAX):
+ ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(tnl->tun_id));
+ break;
+ default:
+ ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
+ ntohll(tnl->tun_id),
+ ntohll(wc->masks.tunnel.tun_id));
+ break;
+ }
+ format_ip_netmask(s, "tun_src", tnl->ip_src, wc->masks.tunnel.ip_src);
+ format_ip_netmask(s, "tun_dst", tnl->ip_dst, wc->masks.tunnel.ip_dst);
+
+ if (wc->masks.tunnel.ip_tos) {
+ ds_put_format(s, "tun_tos=%"PRIx8",", tnl->ip_tos);
+ }
+ if (wc->masks.tunnel.ip_ttl) {
+ ds_put_format(s, "tun_ttl=%"PRIu8",", tnl->ip_ttl);
+ }
+ if (wc->masks.tunnel.flags) {
+ format_flags(s, flow_tun_flag_to_string, tnl->flags, '|');
+ ds_put_char(s, ',');
+ }
+}
+
/* Appends a string representation of 'match' to 's'. If 'priority' is
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
void
break;
}
}
- switch (wc->masks.tunnel.tun_id) {
- case 0:
- break;
- case CONSTANT_HTONLL(UINT64_MAX):
- ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(f->tunnel.tun_id));
- break;
- default:
- ds_put_format(s, "tun_id=%#"PRIx64"/%#"PRIx64",",
- ntohll(f->tunnel.tun_id),
- ntohll(wc->masks.tunnel.tun_id));
- break;
- }
+
+ format_flow_tunnel(s, match);
+
switch (wc->masks.metadata) {
case 0:
break;
ovs_be64 metadata, ovs_be64 mask);
void match_set_tun_id(struct match *, ovs_be64 tun_id);
void match_set_tun_id_masked(struct match *, ovs_be64 tun_id, ovs_be64 mask);
+void match_set_tun_src(struct match *match, ovs_be32 src);
+void match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask);
+void match_set_tun_dst(struct match *match, ovs_be32 dst);
+void match_set_tun_dst_masked(struct match *match, ovs_be32 dst, ovs_be32 mask);
+void match_set_tun_ttl(struct match *match, uint8_t ttl);
+void match_set_tun_ttl_masked(struct match *match, uint8_t ttl, uint8_t mask);
+void match_set_tun_tos(struct match *match, uint8_t tos);
+void match_set_tun_tos_masked(struct match *match, uint8_t tos, uint8_t mask);
+void match_set_tun_flags(struct match *match, uint16_t flags);
+void match_set_tun_flags_masked(struct match *match, uint16_t flags, uint16_t mask);
void match_set_in_port(struct match *, uint16_t ofp_port);
void match_set_dl_type(struct match *, ovs_be16);
void match_set_dl_src(struct match *, const uint8_t[6]);
true,
NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
NXM_NX_TUN_ID, "NXM_NX_TUN_ID",
+ }, {
+ MFF_TUN_SRC, "tun_src", NULL,
+ MF_FIELD_SIZES(be32),
+ MFM_NONE,
+ MFS_IPV4,
+ MFP_NONE,
+ false,
+ 0, NULL,
+ 0, NULL,
+ }, {
+ MFF_TUN_DST, "tun_dst", NULL,
+ MF_FIELD_SIZES(be32),
+ MFM_NONE,
+ MFS_IPV4,
+ MFP_NONE,
+ false,
+ 0, NULL,
+ 0, NULL,
+ }, {
+ MFF_TUN_FLAGS, "tun_flags", NULL,
+ MF_FIELD_SIZES(be16),
+ MFM_NONE,
+ MFS_TNL_FLAGS,
+ MFP_NONE,
+ false,
+ 0, NULL,
+ 0, NULL,
+ }, {
+ MFF_TUN_TOS, "tun_tos", NULL,
+ MF_FIELD_SIZES(u8),
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_NONE,
+ false,
+ 0, NULL,
+ 0, NULL,
+ }, {
+ MFF_TUN_TTL, "tun_ttl", NULL,
+ MF_FIELD_SIZES(u8),
+ MFM_NONE,
+ MFS_DECIMAL,
+ MFP_NONE,
+ false,
+ 0, NULL,
+ 0, NULL,
}, {
MFF_METADATA, "metadata", NULL,
MF_FIELD_SIZES(be64),
{
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_TUN_SRC:
+ case MFF_TUN_DST:
+ case MFF_TUN_TOS:
+ case MFF_TUN_TTL:
+ case MFF_TUN_FLAGS:
return !wc->masks.tunnel.tun_id;
case MFF_METADATA:
return !wc->masks.metadata;
{
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_TUN_SRC:
+ case MFF_TUN_DST:
+ case MFF_TUN_TOS:
+ case MFF_TUN_TTL:
+ case MFF_TUN_FLAGS:
mask->be64 = wc->masks.tunnel.tun_id;
break;
case MFF_METADATA:
{
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_TUN_SRC:
+ case MFF_TUN_DST:
+ case MFF_TUN_TOS:
+ case MFF_TUN_TTL:
+ case MFF_TUN_FLAGS:
case MFF_METADATA:
case MFF_IN_PORT:
CASE_MFF_REGS:
case MFF_TUN_ID:
value->be64 = flow->tunnel.tun_id;
break;
+ case MFF_TUN_SRC:
+ value->be32 = flow->tunnel.ip_src;
+ break;
+ case MFF_TUN_DST:
+ value->be32 = flow->tunnel.ip_dst;
+ break;
+ case MFF_TUN_FLAGS:
+ value->be16 = htons(flow->tunnel.flags);
+ break;
+ case MFF_TUN_TTL:
+ value->u8 = flow->tunnel.ip_ttl;
+ break;
+ case MFF_TUN_TOS:
+ value->u8 = flow->tunnel.ip_tos;
+ break;
+
case MFF_METADATA:
value->be64 = flow->metadata;
break;
case MFF_TUN_ID:
match_set_tun_id(match, value->be64);
break;
+ case MFF_TUN_SRC:
+ match_set_tun_src(match, value->be32);
+ break;
+ case MFF_TUN_DST:
+ match_set_tun_dst(match, value->be32);
+ break;
+ case MFF_TUN_FLAGS:
+ match_set_tun_flags(match, ntohs(value->be16));
+ break;
+ case MFF_TUN_TOS:
+ match_set_tun_tos(match, value->u8);
+ break;
+ case MFF_TUN_TTL:
+ match_set_tun_ttl(match, value->u8);
+ break;
+
case MFF_METADATA:
match_set_metadata(match, value->be64);
break;
case MFF_TUN_ID:
flow->tunnel.tun_id = value->be64;
break;
+ case MFF_TUN_SRC:
+ flow->tunnel.ip_src = value->be32;
+ break;
+ case MFF_TUN_DST:
+ flow->tunnel.ip_dst = value->be32;
+ break;
+ case MFF_TUN_FLAGS:
+ flow->tunnel.flags = ntohs(value->be16);
+ break;
+ case MFF_TUN_TOS:
+ flow->tunnel.ip_tos = value->u8;
+ break;
+ case MFF_TUN_TTL:
+ flow->tunnel.ip_ttl = value->u8;
+ break;
+
case MFF_METADATA:
flow->metadata = value->be64;
break;
case MFF_TUN_ID:
match_set_tun_id_masked(match, htonll(0), htonll(0));
break;
+ case MFF_TUN_SRC:
+ match_set_tun_src_masked(match, htonl(0), htonl(0));
+ break;
+ case MFF_TUN_DST:
+ match_set_tun_dst_masked(match, htonl(0), htonl(0));
+ break;
+ case MFF_TUN_FLAGS:
+ match_set_tun_flags_masked(match, 0, 0);
+ break;
+ case MFF_TUN_TOS:
+ match_set_tun_tos_masked(match, 0, 0);
+ break;
+ case MFF_TUN_TTL:
+ match_set_tun_ttl_masked(match, 0, 0);
+ break;
+
case MFF_METADATA:
match_set_metadata_masked(match, htonll(0), htonll(0));
case MFF_TUN_ID:
match_set_tun_id_masked(match, value->be64, mask->be64);
break;
+ case MFF_TUN_SRC:
+ match_set_tun_src_masked(match, value->be32, mask->be32);
+ break;
+ case MFF_TUN_DST:
+ match_set_tun_dst_masked(match, value->be32, mask->be32);
+ break;
+ case MFF_TUN_FLAGS:
+ match_set_tun_flags_masked(match, ntohs(value->be16), ntohs(mask->be16));
+ break;
+ case MFF_TUN_TTL:
+ match_set_tun_ttl_masked(match, value->u8, mask->u8);
+ break;
+ case MFF_TUN_TOS:
+ match_set_tun_tos_masked(match, value->u8, mask->u8);
+ break;
+
case MFF_METADATA:
match_set_metadata_masked(match, value->be64, mask->be64);
break;
switch (mf->id) {
case MFF_TUN_ID:
+ case MFF_TUN_SRC:
+ case MFF_TUN_DST:
+ case MFF_TUN_TOS:
+ case MFF_TUN_TTL:
+ case MFF_TUN_FLAGS:
case MFF_METADATA:
case MFF_IN_PORT:
CASE_MFF_REGS:
"\"yes\", \"first\", \"later\", \"not_first\"", s);
}
+static int
+parse_flow_tun_flags(const char *s_, const char *(*bit_to_string)(uint32_t),
+ ovs_be16 *res)
+{
+ uint32_t result = 0;
+ char *save_ptr = NULL;
+ char *name;
+ int rc = 0;
+ char *s = xstrdup(s_);
+
+ for (name = strtok_r((char *)s, " |", &save_ptr); name;
+ name = strtok_r(NULL, " |", &save_ptr)) {
+ int name_len;
+ unsigned long long int flags;
+ uint32_t bit;
+ int n0;
+
+ if (sscanf(name, "%lli%n", &flags, &n0) > 0 && n0 > 0) {
+ result |= flags;
+ continue;
+ }
+ name_len = strlen(name);
+ for (bit = 1; bit; bit <<= 1) {
+ const char *fname = bit_to_string(bit);
+ size_t len;
+
+ if (!fname) {
+ continue;
+ }
+
+ len = strlen(fname);
+ if (len != name_len) {
+ continue;
+ }
+ if (!strncmp(name, fname, len)) {
+ result |= bit;
+ break;
+ }
+ }
+
+ if (!bit) {
+ rc = -ENOENT;
+ goto out;
+ }
+ }
+
+ *res = htons(result);
+out:
+ free(s);
+ return rc;
+}
+
+static char *
+mf_from_tun_flags_string(const char *s, ovs_be16 *valuep)
+{
+ if (!parse_flow_tun_flags(s, flow_tun_flag_to_string, valuep)) {
+ return NULL;
+ }
+
+ return xasprintf("%s: unknown tunnel flags (valid flags are \"df\", "
+ "\"csum\", \"key\"", s);
+}
+
/* Parses 's', a string value for field 'mf', into 'value' and 'mask'. Returns
* NULL if successful, otherwise a malloc()'d string describing the error. */
char *
case MFS_FRAG:
return mf_from_frag_string(s, &value->u8, &mask->u8);
+
+ case MFS_TNL_FLAGS:
+ return mf_from_tun_flags_string(s, &value->be16);
}
NOT_REACHED();
}
ds_put_cstr(s, "<error>");
}
+static void
+mf_format_tnl_flags_string(const ovs_be16 *valuep, struct ds *s)
+{
+ format_flags(s, flow_tun_flag_to_string, ntohs(*valuep), '|');
+}
+
/* Appends to 's' a string representation of field 'mf' whose value is in
* 'value' and 'mask'. 'mask' may be NULL to indicate an exact match. */
void
mf_format_frag_string(&value->u8, &mask->u8, s);
break;
+ case MFS_TNL_FLAGS:
+ mf_format_tnl_flags_string(&value->be16, s);
+ break;
+
default:
NOT_REACHED();
}
enum mf_field_id {
/* Metadata. */
MFF_TUN_ID, /* be64 */
+ MFF_TUN_SRC, /* be32 */
+ MFF_TUN_DST, /* be32 */
+ MFF_TUN_FLAGS, /* be16 */
+ MFF_TUN_TTL, /* u8 */
+ MFF_TUN_TOS, /* u8 */
MFF_METADATA, /* be64 */
MFF_IN_PORT, /* be16 */
MFS_IPV4,
MFS_IPV6,
MFS_OFP_PORT, /* An OpenFlow port number or name. */
- MFS_FRAG /* no, yes, first, later, not_later */
+ MFS_FRAG, /* no, yes, first, later, not_later */
+ MFS_TNL_FLAGS, /* FLOW_TNL_F_* flags */
};
struct mf_field {
}
}
-static void
-format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
- uint32_t flags)
-{
- uint32_t bad = 0;
-
- ds_put_format(ds, "(");
- if (!flags) {
- goto out;
- }
- while (flags) {
- uint32_t bit = rightmost_1bit(flags);
- const char *s;
-
- s = bit_to_string(bit);
- if (s) {
- ds_put_format(ds, "%s,", s);
- } else {
- bad |= bit;
- }
-
- flags &= ~bit;
- }
-
- if (bad) {
- ds_put_format(ds, "0x%"PRIx32",", bad);
- }
- ds_chomp(ds, ',');
-out:
- ds_put_format(ds, ")");
-}
-
static int
parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
uint32_t *res)
break;
case USER_ACTION_COOKIE_SLOW_PATH:
- ds_put_cstr(ds, ",slow_path");
- format_flags(ds, slow_path_reason_to_string, cookie.slow_path.reason);
+ ds_put_cstr(ds, ",slow_path(");
+ format_flags(ds, slow_path_reason_to_string,
+ cookie.slow_path.reason, ',');
+ ds_put_format(ds, ")");
break;
case USER_ACTION_COOKIE_UNSPEC:
}
static const char *
-tun_flag_to_string(uint32_t flags)
+odp_tun_flag_to_string(uint32_t flags)
{
switch (flags) {
case OVS_TNL_F_DONT_FRAGMENT:
case OVS_KEY_ATTR_IPV4_TUNNEL:
ipv4_tun_key = nl_attr_get(a);
ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
- "tos=0x%"PRIx8",ttl=%"PRIu8",flags",
+ "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
ntohll(ipv4_tun_key->tun_id),
IP_ARGS(&ipv4_tun_key->ipv4_src),
IP_ARGS(&ipv4_tun_key->ipv4_dst),
ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl);
- format_flags(ds, tun_flag_to_string, ipv4_tun_key->tun_flags);
- ds_put_format(ds, ")");
+ format_flags(ds, odp_tun_flag_to_string,
+ ipv4_tun_key->tun_flags, ',');
+ ds_put_format(ds, "))");
break;
case OVS_KEY_ATTR_IN_PORT:
tun_key.ipv4_tos = tos;
tun_key.ipv4_ttl = ttl;
- res = parse_flags(&s[n], tun_flag_to_string, &tun_key.tun_flags);
+ res = parse_flags(&s[n], odp_tun_flag_to_string,
+ &tun_key.tun_flags);
if (res < 0) {
return res;
}
return true;
}
+static bool
+tun_parms_fully_wildcarded(const struct flow_wildcards *wc)
+{
+ return (!wc->masks.tunnel.ip_src &&
+ !wc->masks.tunnel.ip_dst &&
+ !wc->masks.tunnel.ip_ttl &&
+ !wc->masks.tunnel.ip_tos &&
+ !wc->masks.tunnel.flags);
+}
+
/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match'
* to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs,
* registers, or fixing the Ethernet multicast bit. Otherwise, it's better to
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18);
+ /* tunnel params other than tun_id can't be sent in a flow_mod */
+ if (!tun_parms_fully_wildcarded(wc)) {
+ return OFPUTIL_P_NONE;
+ }
+
/* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */
if (!eth_mask_is_exact(wc->masks.dl_src)
&& !eth_addr_is_zero(wc->masks.dl_src)) {
usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
}
}
- assert(usable_protocols);
return usable_protocols;
}
]])
AT_CLEANUP
+AT_SETUP([ovs-ofctl parse-flows (With Tunnel-Parameters)])
+AT_DATA([flows.txt], [[
+tun_id=0x1234000056780000/0xffff0000ffff0000,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0x3,tun_ttl=20,tun_flags=key|csum actions=drop
+]])
+
+AT_CHECK([ovs-ofctl parse-flows flows.txt
+], [1], [usable protocols: none
+], [stderr])
+
+AT_CLEANUP
+
+
AT_SETUP([ovs-ofctl parse-flows (NXM)])
AT_DATA([flows.txt], [[
# comment