2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 22:35:15 +00:00

vswitchd: Log all tunnel parameters of given flow.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
This commit is contained in:
Pravin B Shelar
2012-11-21 18:51:36 -08:00
parent 72e8bf28bb
commit 4fe3445afb
9 changed files with 434 additions and 55 deletions

View File

@@ -479,6 +479,50 @@ flow_to_string(const struct flow *flow)
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)
{

View File

@@ -56,6 +56,9 @@ BUILD_ASSERT_DECL(FLOW_NW_FRAG_LATER == NX_IP_FRAG_LATER);
#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;
@@ -123,6 +126,9 @@ void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
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 *);

View File

@@ -62,14 +62,19 @@ match_wc_init(struct match *match, const struct flow *flow)
}
}
if (flow->tunnel.ip_dst || flow->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);
@@ -195,6 +200,71 @@ match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
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)
{
@@ -643,6 +713,39 @@ format_be16_masked(struct ds *s, const char *name,
}
}
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
@@ -717,18 +820,9 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
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;

View File

@@ -50,6 +50,16 @@ void match_set_metadata_masked(struct match *,
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]);

View File

@@ -55,6 +55,51 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
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),
@@ -574,6 +619,11 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
{
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;
@@ -671,6 +721,11 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
{
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:
@@ -887,6 +942,11 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
{
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:
@@ -955,6 +1015,22 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
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;
@@ -1098,6 +1174,22 @@ mf_set_value(const struct mf_field *mf,
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;
@@ -1241,6 +1333,22 @@ mf_set_flow_value(const struct mf_field *mf,
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;
@@ -1399,6 +1507,22 @@ mf_set_wild(const struct mf_field *mf, struct match *match)
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));
@@ -1579,6 +1703,22 @@ mf_set(const struct mf_field *mf,
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;
@@ -1737,6 +1877,11 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
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:
@@ -1995,6 +2140,69 @@ mf_from_frag_string(const char *s, uint8_t *valuep, uint8_t *maskp)
"\"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 *
@@ -2027,6 +2235,9 @@ mf_parse(const struct mf_field *mf, const char *s,
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();
}
@@ -2104,6 +2315,12 @@ mf_format_frag_string(const uint8_t *valuep, const uint8_t *maskp,
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
@@ -2149,6 +2366,10 @@ mf_format(const struct mf_field *mf,
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();
}

View File

@@ -32,6 +32,11 @@ struct match;
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 */
@@ -195,7 +200,8 @@ enum mf_string {
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 {

View File

@@ -190,38 +190,6 @@ slow_path_reason_to_string(uint32_t data)
}
}
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)
@@ -305,8 +273,10 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
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:
@@ -703,7 +673,7 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
}
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:
@@ -767,14 +737,15 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
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:
@@ -1018,7 +989,8 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
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;
}

View File

@@ -979,6 +979,16 @@ regs_fully_wildcarded(const struct flow_wildcards *wc)
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
@@ -990,6 +1000,11 @@ ofputil_usable_protocols(const struct match *match)
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)) {
@@ -1630,7 +1645,6 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM;
}
}
assert(usable_protocols);
return usable_protocols;
}

View File

@@ -31,6 +31,18 @@ OFPT_FLOW_MOD: ADD in_port=0 actions=resubmit:0
]])
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