2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00

nsh: add new flow key 'ttl'

IETF NSH draft added a new filed ttl in NSH header, this patch
is to add new nsh key 'ttl' for it.

Signed-off-by: Yi Yang <yi.y.yang@intel.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Yi Yang 2018-01-11 13:24:01 +08:00 committed by Ben Pfaff
parent 74868f2c38
commit 17553f27ba
18 changed files with 423 additions and 187 deletions

1
NEWS
View File

@ -1,6 +1,7 @@
Post-v2.8.0 Post-v2.8.0
-------------------- --------------------
- NSH implementation now conforms to latest draft (draft-ietf-sfc-nsh-28). - NSH implementation now conforms to latest draft (draft-ietf-sfc-nsh-28).
* Add ttl field.
- OVSDB: - OVSDB:
* New high-level documentation in ovsdb(7). * New high-level documentation in ovsdb(7).
* New file format documentation for developers in ovsdb(5). * New file format documentation for developers in ovsdb(5).

View File

@ -504,9 +504,9 @@ enum ovs_nsh_key_attr {
struct ovs_nsh_key_base { struct ovs_nsh_key_base {
__u8 flags; __u8 flags;
__u8 ttl;
__u8 mdtype; __u8 mdtype;
__u8 np; __u8 np;
__u8 pad;
__be32 path_hdr; __be32 path_hdr;
}; };

View File

@ -146,7 +146,7 @@ struct flow {
struct eth_addr arp_tha; /* ARP/ND target hardware address. */ struct eth_addr arp_tha; /* ARP/ND target hardware address. */
ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */ ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
ovs_be16 pad2; /* Pad to 64 bits. */ ovs_be16 pad2; /* Pad to 64 bits. */
struct flow_nsh nsh; /* Network Service Header keys */ struct ovs_key_nsh nsh; /* Network Service Header keys */
/* L4 (64-bit aligned) */ /* L4 (64-bit aligned) */
ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */ ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */
@ -159,13 +159,13 @@ struct flow {
}; };
BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0);
BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
BUILD_ASSERT_DECL(sizeof(struct flow_nsh) % sizeof(uint64_t) == 0); BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) % sizeof(uint64_t) == 0);
#define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t)) #define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t))
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t) BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
== sizeof(struct flow_tnl) + sizeof(struct flow_nsh) + 300 == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300
&& FLOW_WC_SEQ == 40); && FLOW_WC_SEQ == 40);
/* Incremental points at which flow classification may be performed in /* Incremental points at which flow classification may be performed in

View File

@ -1833,6 +1833,20 @@ enum OVS_PACKED_ENUM mf_field_id {
MFF_NSH_C3, MFF_NSH_C3,
MFF_NSH_C4, MFF_NSH_C4,
/* "nsh_ttl".
*
* TTL field in NSH base header.
*
* Type: u8.
* Maskable: no.
* Formatting: decimal.
* Prerequisites: NSH.
* Access: read/write.
* NXM: none.
* OXM: NXOXM_NSH_TTL(10) since OF1.3 and v2.9.
*/
MFF_NSH_TTL,
MFF_N_IDS MFF_N_IDS
}; };

View File

@ -299,6 +299,108 @@ nsh_reset_ver_flags_ttl_len(struct nsh_hdr *nsh)
nsh->ver_flags_ttl_len = 0; nsh->ver_flags_ttl_len = 0;
} }
static inline uint8_t
nsh_get_ttl(const struct nsh_hdr *nsh)
{
return (ntohs(nsh->ver_flags_ttl_len) & NSH_TTL_MASK) >> NSH_TTL_SHIFT;
}
#ifndef __CHECKER__
static inline ovs_be32
nsh_16aligned_be32(const ovs_16aligned_be32 *x)
{
#ifdef WORDS_BIGENDIAN
return ((ovs_be32) x->hi << 16) | (ovs_be32) x->lo;
#else
return ((ovs_be32) x->lo << 16) | (ovs_be32) x->hi;
#endif
}
#else /* __CHECKER__ */
/* Making sparse happy with these functions also makes them unreadable, so
* don't bother to show it their implementations. */
ovs_be32 nsh_16aligned_be32(const ovs_16aligned_be32 *x);
#endif
static inline ovs_be32
nsh_get_path_hdr(const struct nsh_hdr *nsh)
{
return nsh_16aligned_be32(&nsh->path_hdr);
}
static inline ovs_be32
nsh_get_spi(const struct nsh_hdr *nsh)
{
uint32_t path_hdr = ntohl(nsh_get_path_hdr(nsh));
return htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
}
static inline uint8_t
nsh_get_si(const struct nsh_hdr *nsh)
{
uint32_t path_hdr = ntohl(nsh_get_path_hdr(nsh));
return (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
}
static inline ovs_be32
nsh_path_hdr_to_spi(ovs_be32 path_hdr)
{
return htonl((ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
}
static inline uint32_t
nsh_path_hdr_to_spi_uint32(ovs_be32 path_hdr)
{
return (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
}
static inline uint8_t
nsh_path_hdr_to_si(ovs_be32 path_hdr)
{
return (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
}
static inline ovs_be32
nsh_spi_si_to_path_hdr(uint32_t spi, uint8_t si)
{
return htonl((spi << NSH_SPI_SHIFT) | si);
}
static inline void
nsh_set_flags_and_ttl(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl)
{
nsh->ver_flags_ttl_len
= htons((ntohs(nsh->ver_flags_ttl_len)
& ~(NSH_FLAGS_MASK | NSH_TTL_MASK))
| ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK)
| ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK));
}
static inline void
nsh_set_flags_ttl_len(struct nsh_hdr *nsh, uint8_t flags, uint8_t ttl,
uint16_t len)
{
nsh->ver_flags_ttl_len
= htons((ntohs(nsh->ver_flags_ttl_len)
& ~(NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK))
| ((flags << NSH_FLAGS_SHIFT)& NSH_FLAGS_MASK)
| ((ttl << NSH_TTL_SHIFT) & NSH_TTL_MASK)
| (((len >> 2) << NSH_LEN_SHIFT) & NSH_LEN_MASK));
}
static inline void
nsh_path_hdr_set_spi(ovs_be32 *path_hdr, ovs_be32 spi)
{
*path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SPI_MASK) |
((ntohl(spi) << NSH_SPI_SHIFT) & NSH_SPI_MASK));
}
static inline void
nsh_path_hdr_set_si(ovs_be32 *path_hdr, uint8_t si)
{
*path_hdr = htonl((ntohl(*path_hdr) & ~NSH_SI_MASK) |
((si << NSH_SI_SHIFT) & NSH_SI_MASK));
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -74,21 +74,11 @@ union flow_vlan_hdr {
}; };
}; };
/* Network Service Header keys */
struct flow_nsh {
uint8_t flags;
uint8_t mdtype;
uint8_t np;
uint8_t si;
ovs_be32 spi;
ovs_be32 context[4];
};
struct ovs_key_nsh { struct ovs_key_nsh {
uint8_t flags; uint8_t flags;
uint8_t ttl;
uint8_t mdtype; uint8_t mdtype;
uint8_t np; uint8_t np;
uint8_t pad;
ovs_be32 path_hdr; ovs_be32 path_hdr;
ovs_be32 context[4]; ovs_be32 context[4];
}; };

View File

@ -530,11 +530,10 @@ parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
} }
bool bool
parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key) parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key)
{ {
const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap; const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap;
uint8_t version, length, flags; uint8_t version, length, flags, ttl;
uint32_t path_hdr;
/* Check if it is long enough for NSH header, doesn't support /* Check if it is long enough for NSH header, doesn't support
* MD type 2 yet * MD type 2 yet
@ -546,18 +545,17 @@ parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)
version = nsh_get_ver(nsh); version = nsh_get_ver(nsh);
flags = nsh_get_flags(nsh); flags = nsh_get_flags(nsh);
length = nsh_hdr_len(nsh); length = nsh_hdr_len(nsh);
ttl = nsh_get_ttl(nsh);
if (OVS_UNLIKELY(length > *sizep || version != 0)) { if (OVS_UNLIKELY(length > *sizep || version != 0)) {
return false; return false;
} }
key->flags = flags; key->flags = flags;
key->ttl = ttl;
key->mdtype = nsh->md_type; key->mdtype = nsh->md_type;
key->np = nsh->next_proto; key->np = nsh->next_proto;
key->path_hdr = nsh_get_path_hdr(nsh);
path_hdr = ntohl(get_16aligned_be32(&nsh->path_hdr));
key->si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
key->spi = htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
switch (key->mdtype) { switch (key->mdtype) {
case NSH_M_TYPE1: case NSH_M_TYPE1:
@ -876,11 +874,11 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
miniflow_pad_to_64(mf, arp_tha); miniflow_pad_to_64(mf, arp_tha);
} }
} else if (dl_type == htons(ETH_TYPE_NSH)) { } else if (dl_type == htons(ETH_TYPE_NSH)) {
struct flow_nsh nsh; struct ovs_key_nsh nsh;
if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) { if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) {
miniflow_push_words(mf, nsh, &nsh, miniflow_push_words(mf, nsh, &nsh,
sizeof(struct flow_nsh) / sizeof(struct ovs_key_nsh) /
sizeof(uint64_t)); sizeof(uint64_t));
} }
} }
@ -1684,10 +1682,10 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,
return; return;
} else if (flow->dl_type == htons(ETH_TYPE_NSH)) { } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
WC_MASK_FIELD(wc, nsh.flags); WC_MASK_FIELD(wc, nsh.flags);
WC_MASK_FIELD(wc, nsh.ttl);
WC_MASK_FIELD(wc, nsh.mdtype); WC_MASK_FIELD(wc, nsh.mdtype);
WC_MASK_FIELD(wc, nsh.np); WC_MASK_FIELD(wc, nsh.np);
WC_MASK_FIELD(wc, nsh.spi); WC_MASK_FIELD(wc, nsh.path_hdr);
WC_MASK_FIELD(wc, nsh.si);
WC_MASK_FIELD(wc, nsh.context); WC_MASK_FIELD(wc, nsh.context);
} else { } else {
return; /* Unknown ethertype. */ return; /* Unknown ethertype. */
@ -1820,8 +1818,7 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, nsh.flags); FLOWMAP_SET(map, nsh.flags);
FLOWMAP_SET(map, nsh.mdtype); FLOWMAP_SET(map, nsh.mdtype);
FLOWMAP_SET(map, nsh.np); FLOWMAP_SET(map, nsh.np);
FLOWMAP_SET(map, nsh.spi); FLOWMAP_SET(map, nsh.path_hdr);
FLOWMAP_SET(map, nsh.si);
FLOWMAP_SET(map, nsh.context); FLOWMAP_SET(map, nsh.context);
} }
} }

View File

@ -129,7 +129,7 @@ bool flow_compose(struct dp_packet *, const struct flow *, size_t);
bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto, bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
uint8_t *nw_frag); uint8_t *nw_frag);
ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size); ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size);
bool parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key); bool parse_nsh(const void **datap, size_t *sizep, struct ovs_key_nsh *key);
static inline uint64_t static inline uint64_t
flow_get_xreg(const struct flow *flow, int idx) flow_get_xreg(const struct flow *flow, int idx)

View File

@ -1260,11 +1260,19 @@ format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
static void static void
format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m) format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m)
{ {
ovs_be32 spi_mask = nsh_path_hdr_to_spi(m->nsh.path_hdr);
if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
spi_mask = OVS_BE32_MAX;
}
format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags); format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags);
format_uint8_masked(s, "nsh_ttl", f->nsh.ttl, m->nsh.ttl);
format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype); format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype);
format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np); format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np);
format_be32_masked_hex(s, "nsh_spi", f->nsh.spi, m->nsh.spi);
format_uint8_masked(s, "nsh_si", f->nsh.si, m->nsh.si); format_be32_masked_hex(s, "nsh_spi", nsh_path_hdr_to_spi(f->nsh.path_hdr),
spi_mask);
format_uint8_masked(s, "nsh_si", nsh_path_hdr_to_si(f->nsh.path_hdr),
nsh_path_hdr_to_si(m->nsh.path_hdr));
if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) { if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) {
format_be32_masked_hex(s, "nsh_c1", f->nsh.context[0], format_be32_masked_hex(s, "nsh_c1", f->nsh.context[0],
m->nsh.context[0]); m->nsh.context[0]);

View File

@ -40,6 +40,7 @@
#include "openvswitch/ofp-errors.h" #include "openvswitch/ofp-errors.h"
#include "openvswitch/vlog.h" #include "openvswitch/vlog.h"
#include "vl-mff-map.h" #include "vl-mff-map.h"
#include "openvswitch/nsh.h"
VLOG_DEFINE_THIS_MODULE(meta_flow); VLOG_DEFINE_THIS_MODULE(meta_flow);
@ -361,14 +362,16 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
return !wc->masks.nsh.flags; return !wc->masks.nsh.flags;
case MFF_NSH_TTL:
return !wc->masks.nsh.ttl;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
return !wc->masks.nsh.mdtype; return !wc->masks.nsh.mdtype;
case MFF_NSH_NP: case MFF_NSH_NP:
return !wc->masks.nsh.np; return !wc->masks.nsh.np;
case MFF_NSH_SPI: case MFF_NSH_SPI:
return !wc->masks.nsh.spi; return !(wc->masks.nsh.path_hdr & htonl(NSH_SPI_MASK));
case MFF_NSH_SI: case MFF_NSH_SI:
return !wc->masks.nsh.si; return !(wc->masks.nsh.path_hdr & htonl(NSH_SI_MASK));
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:
case MFF_NSH_C3: case MFF_NSH_C3:
@ -606,6 +609,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
return true; return true;
case MFF_NSH_TTL:
return (value->u8 <= 63);
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
return (value->u8 == 1 || value->u8 == 2); return (value->u8 == 1 || value->u8 == 2);
case MFF_NSH_NP: case MFF_NSH_NP:
@ -899,6 +904,9 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
value->u8 = flow->nsh.flags; value->u8 = flow->nsh.flags;
break; break;
case MFF_NSH_TTL:
value->u8 = flow->nsh.ttl;
break;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
value->u8 = flow->nsh.mdtype; value->u8 = flow->nsh.mdtype;
break; break;
@ -906,10 +914,13 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
value->u8 = flow->nsh.np; value->u8 = flow->nsh.np;
break; break;
case MFF_NSH_SPI: case MFF_NSH_SPI:
value->be32 = flow->nsh.spi; value->be32 = nsh_path_hdr_to_spi(flow->nsh.path_hdr);
if (value->be32 == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
value->be32 = OVS_BE32_MAX;
}
break; break;
case MFF_NSH_SI: case MFF_NSH_SI:
value->u8 = flow->nsh.si; value->u8 = nsh_path_hdr_to_si(flow->nsh.path_hdr);
break; break;
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:
@ -1214,6 +1225,9 @@ mf_set_value(const struct mf_field *mf,
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8); MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
break; break;
case MFF_NSH_TTL:
MATCH_SET_FIELD_UINT8(match, nsh.ttl, value->u8);
break;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
MATCH_SET_FIELD_UINT8(match, nsh.mdtype, value->u8); MATCH_SET_FIELD_UINT8(match, nsh.mdtype, value->u8);
break; break;
@ -1221,10 +1235,12 @@ mf_set_value(const struct mf_field *mf,
MATCH_SET_FIELD_UINT8(match, nsh.np, value->u8); MATCH_SET_FIELD_UINT8(match, nsh.np, value->u8);
break; break;
case MFF_NSH_SPI: case MFF_NSH_SPI:
MATCH_SET_FIELD_BE32(match, nsh.spi, value->be32); match->wc.masks.nsh.path_hdr |= htonl(NSH_SPI_MASK);
nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, value->be32);
break; break;
case MFF_NSH_SI: case MFF_NSH_SI:
MATCH_SET_FIELD_UINT8(match, nsh.si, value->u8); match->wc.masks.nsh.path_hdr |= htonl(NSH_SI_MASK);
nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, value->u8);
break; break;
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:
@ -1606,6 +1622,9 @@ mf_set_flow_value(const struct mf_field *mf,
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
flow->nsh.flags = value->u8; flow->nsh.flags = value->u8;
break; break;
case MFF_NSH_TTL:
flow->nsh.ttl = value->u8;
break;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
flow->nsh.mdtype = value->u8; flow->nsh.mdtype = value->u8;
break; break;
@ -1613,10 +1632,10 @@ mf_set_flow_value(const struct mf_field *mf,
flow->nsh.np = value->u8; flow->nsh.np = value->u8;
break; break;
case MFF_NSH_SPI: case MFF_NSH_SPI:
flow->nsh.spi = value->be32; nsh_path_hdr_set_spi(&flow->nsh.path_hdr, value->be32);
break; break;
case MFF_NSH_SI: case MFF_NSH_SI:
flow->nsh.si = value->u8; nsh_path_hdr_set_si(&flow->nsh.path_hdr, value->u8);
break; break;
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:
@ -1752,6 +1771,7 @@ mf_is_pipeline_field(const struct mf_field *mf)
case MFF_ND_SLL: case MFF_ND_SLL:
case MFF_ND_TLL: case MFF_ND_TLL:
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
case MFF_NSH_TTL:
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
case MFF_NSH_NP: case MFF_NSH_NP:
case MFF_NSH_SPI: case MFF_NSH_SPI:
@ -2097,6 +2117,9 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
MATCH_SET_FIELD_MASKED(match, nsh.flags, 0, 0); MATCH_SET_FIELD_MASKED(match, nsh.flags, 0, 0);
break; break;
case MFF_NSH_TTL:
MATCH_SET_FIELD_MASKED(match, nsh.ttl, 0, 0);
break;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
MATCH_SET_FIELD_MASKED(match, nsh.mdtype, 0, 0); MATCH_SET_FIELD_MASKED(match, nsh.mdtype, 0, 0);
break; break;
@ -2104,10 +2127,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
MATCH_SET_FIELD_MASKED(match, nsh.np, 0, 0); MATCH_SET_FIELD_MASKED(match, nsh.np, 0, 0);
break; break;
case MFF_NSH_SPI: case MFF_NSH_SPI:
MATCH_SET_FIELD_MASKED(match, nsh.spi, htonl(0), htonl(0)); match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SPI_MASK);
nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr, htonl(0));
break; break;
case MFF_NSH_SI: case MFF_NSH_SI:
MATCH_SET_FIELD_MASKED(match, nsh.si, 0, 0); match->wc.masks.nsh.path_hdr &= ~htonl(NSH_SI_MASK);
nsh_path_hdr_set_si(&match->flow.nsh.path_hdr, 0);
break; break;
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:
@ -2357,6 +2382,9 @@ mf_set(const struct mf_field *mf,
case MFF_NSH_FLAGS: case MFF_NSH_FLAGS:
MATCH_SET_FIELD_MASKED(match, nsh.flags, value->u8, mask->u8); MATCH_SET_FIELD_MASKED(match, nsh.flags, value->u8, mask->u8);
break; break;
case MFF_NSH_TTL:
MATCH_SET_FIELD_MASKED(match, nsh.ttl, value->u8, mask->u8);
break;
case MFF_NSH_MDTYPE: case MFF_NSH_MDTYPE:
MATCH_SET_FIELD_MASKED(match, nsh.mdtype, value->u8, mask->u8); MATCH_SET_FIELD_MASKED(match, nsh.mdtype, value->u8, mask->u8);
break; break;
@ -2364,10 +2392,14 @@ mf_set(const struct mf_field *mf,
MATCH_SET_FIELD_MASKED(match, nsh.np, value->u8, mask->u8); MATCH_SET_FIELD_MASKED(match, nsh.np, value->u8, mask->u8);
break; break;
case MFF_NSH_SPI: case MFF_NSH_SPI:
MATCH_SET_FIELD_MASKED(match, nsh.spi, value->be32, mask->be32); match->wc.masks.nsh.path_hdr |= mask->be32;
nsh_path_hdr_set_spi(&match->flow.nsh.path_hdr,
value->be32 & mask->be32);
break; break;
case MFF_NSH_SI: case MFF_NSH_SI:
MATCH_SET_FIELD_MASKED(match, nsh.si, value->u8, mask->u8); match->wc.masks.nsh.path_hdr |= htonl(mask->u8);
nsh_path_hdr_set_si(&match->flow.nsh.path_hdr,
value->u8 & mask->u8);
break; break;
case MFF_NSH_C1: case MFF_NSH_C1:
case MFF_NSH_C2: case MFF_NSH_C2:

View File

@ -1309,27 +1309,6 @@ tcp,tp_src=0x07c0/0xfff0
</field> </field>
</group> </group>
<group title="Network Service Header">
<field id="MFF_NSH_FLAGS"
title="flags field (8 bits)"/>
<field id="MFF_NSH_MDTYPE"
title="mdtype field (8 bits)"/>
<field id="MFF_NSH_NP"
title="np (next protocol) field (8 bits)"/>
<field id="MFF_NSH_SPI"
title="spi (service path identifier) field (24 bits)"/>
<field id="MFF_NSH_SI"
title="si (service index) field (8 bits)"/>
<field id="MFF_NSH_C1"
title="c1 (Network Platform Context) field (32 bits)"/>
<field id="MFF_NSH_C2"
title="c2 (Network Shared Context) field (32 bits)"/>
<field id="MFF_NSH_C3"
title="c3 (Service Platform Context) field (32 bits)"/>
<field id="MFF_NSH_C4"
title="c4 (Service Shared Context) field (32 bits)"/>
</group>
<group title="Tunnel"> <group title="Tunnel">
<p> <p>
The fields in this group relate to tunnels, which Open vSwitch The fields in this group relate to tunnels, which Open vSwitch
@ -3964,7 +3943,7 @@ r r c c c.
listen for <code>OFPR_INVALID_TTL</code> ``packet-in'' messages via listen for <code>OFPR_INVALID_TTL</code> ``packet-in'' messages via
OpenFlow. OpenFlow.
</field> </field>
<field id="MFF_IP_FRAG" title="IPv4/v6 Fragment Bitmask"> <field id="MFF_IP_FRAG" title="IPv4/v6 Fragment Bitmask">
<p> <p>
Specifies what kinds of IP fragments or non-fragments to match. The Specifies what kinds of IP fragments or non-fragments to match. The
@ -4180,6 +4159,115 @@ r r c c c.
<field id="MFF_ARP_THA" title="ARP Target Ethernet Address"/> <field id="MFF_ARP_THA" title="ARP Target Ethernet Address"/>
</group> </group>
<group title="Layer 3: NSH">
<p>
Service functions are widely deployed and essential in many networks.
These service functions provide a range of features such as security,
WAN acceleration, and server load balancing. Service functions may
be instantiated at different points in the network infrastructure
such as the wide area network, data center, and so forth.
</p>
<p>
Prior to development of the SFC architecture [RFC 7665] and the
protocol specified in this document, current service function
deployment models have been relatively static and bound to topology
for insertion and policy selection. Furthermore, they do not adapt
well to elastic service environments enabled by virtualization.
</p>
<p>
New data center network and cloud architectures require more flexible
service function deployment models. Additionally, the transition to
virtual platforms demands an agile service insertion model that
supports dynamic and elastic service delivery. Specifically, the
following functions are necessary:
</p>
<p>
1. The movement of service functions and application workloads in
the network.
</p>
<p>
2. The ability to easily bind service policy to granular
information, such as per-subscriber state.
</p>
<p>
3. The capability to steer traffic to the requisite service
function(s).
</p>
<p>
The Network Service Header (NSH) specification defines a new data
plane protocol, which is an encapsulation for service function
chains. The NSH is designed to encapsulate an original packet or
frame, and in turn be encapsulated by an outer transport
encapsulation (which is used to deliver the NSH to NSH-aware network
elements), as shown in Figure 1:
</p>
<pre>
+------------------------------+
| Transport Encapsulation |
+------------------------------+
| Network Service Header (NSH) |
+------------------------------+
| Original Packet / Frame |
+------------------------------+
Figure 1: Network Service Header Encapsulation
</pre>
<p>
The NSH is composed of the following elements:
</p>
<p>
1. Service Function Path identification.
</p>
<p>
2. Indication of location within a Service Function Path.
</p>
<p>
3. Optional, per packet metadata (fixed length or variable).
</p>
<p>
[RFC 7665] provides an overview of a service chaining architecture
that clearly defines the roles of the various elements and the scope
of a service function chaining encapsulation. Figure 3 of [RFC 7665]
depicts the SFC architectural components after classification. The
NSH is the SFC encapsulation referenced in [RFC 7665].
</p>
<field id="MFF_NSH_FLAGS"
title="flags field (2 bits)"/>
<field id="MFF_NSH_TTL"
title="TTL field (6 bits)"/>
<field id="MFF_NSH_MDTYPE"
title="mdtype field (8 bits)"/>
<field id="MFF_NSH_NP"
title="np (next protocol) field (8 bits)"/>
<field id="MFF_NSH_SPI"
title="spi (service path identifier) field (24 bits)"/>
<field id="MFF_NSH_SI"
title="si (service index) field (8 bits)"/>
<field id="MFF_NSH_C1"
title="c1 (Network Platform Context) field (32 bits)"/>
<field id="MFF_NSH_C2"
title="c2 (Network Shared Context) field (32 bits)"/>
<field id="MFF_NSH_C3"
title="c3 (Service Platform Context) field (32 bits)"/>
<field id="MFF_NSH_C4"
title="c4 (Service Shared Context) field (32 bits)"/>
</group>
<group title="Layer 4: TCP, UDP, and SCTP"> <group title="Layer 4: TCP, UDP, and SCTP">
<p> <p>
For matching purposes, no distinction is made whether these protocols are For matching purposes, no distinction is made whether these protocols are
@ -4567,6 +4655,13 @@ r r c c c.
Layer 3 Networks, '' <url href="https://tools.ietf.org/html/rfc7348"/>. Layer 3 Networks, '' <url href="https://tools.ietf.org/html/rfc7348"/>.
</dd> </dd>
<dt>RFC 7665</dt>
<dd>
J. Halpern, Ed. and C. Pignataro, Ed.,
``Service Function Chaining (SFC) Architecture,''
<url href="https://tools.ietf.org/html/rfc7665"/>.
</dd>
<dt>Srinivasan</dt> <dt>Srinivasan</dt>
<dd> <dd>
V. Srinivasan, S. Suriy, and G. Varghese, ``Packet V. Srinivasan, S. Suriy, and G. Varghese, ``Packet

View File

@ -1022,6 +1022,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
const struct flow *flow = &match->flow; const struct flow *flow = &match->flow;
const size_t start_len = b->size; const size_t start_len = b->size;
ovs_be16 dl_type = get_dl_type(flow); ovs_be16 dl_type = get_dl_type(flow);
ovs_be32 spi_mask;
int match_len; int match_len;
int i; int i;
@ -1157,13 +1158,22 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
/* Network Service Header */ /* Network Service Header */
nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags, nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
match->wc.masks.nsh.flags); match->wc.masks.nsh.flags);
nxm_put_8m(&ctx, MFF_NSH_TTL, oxm, flow->nsh.ttl,
match->wc.masks.nsh.ttl);
nxm_put_8m(&ctx, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype, nxm_put_8m(&ctx, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype,
match->wc.masks.nsh.mdtype); match->wc.masks.nsh.mdtype);
nxm_put_8m(&ctx, MFF_NSH_NP, oxm, flow->nsh.np, nxm_put_8m(&ctx, MFF_NSH_NP, oxm, flow->nsh.np,
match->wc.masks.nsh.np); match->wc.masks.nsh.np);
nxm_put_32m(&ctx, MFF_NSH_SPI, oxm, flow->nsh.spi, spi_mask = nsh_path_hdr_to_spi(match->wc.masks.nsh.path_hdr);
match->wc.masks.nsh.spi); if (spi_mask == htonl(NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si); spi_mask = OVS_BE32_MAX;
}
nxm_put_32m(&ctx, MFF_NSH_SPI, oxm,
nsh_path_hdr_to_spi(flow->nsh.path_hdr),
spi_mask);
nxm_put_8m(&ctx, MFF_NSH_SI, oxm,
nsh_path_hdr_to_si(flow->nsh.path_hdr),
nsh_path_hdr_to_si(match->wc.masks.nsh.path_hdr));
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i], nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.context[i],
match->wc.masks.nsh.context[i]); match->wc.masks.nsh.context[i]);

View File

@ -274,19 +274,16 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
/* Set the NSH header. Assumes the NSH header is present and matches the /* Set the NSH header. Assumes the NSH header is present and matches the
* MD format of the key. The slow path must take case of that. */ * MD format of the key. The slow path must take case of that. */
static void static void
odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key, odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
const struct flow_nsh *mask) const struct ovs_key_nsh *mask)
{ {
struct nsh_hdr *nsh = dp_packet_l3(packet); struct nsh_hdr *nsh = dp_packet_l3(packet);
uint8_t mdtype = nsh_md_type(nsh); uint8_t mdtype = nsh_md_type(nsh);
ovs_be32 path_hdr; ovs_be32 path_hdr;
if (!mask) { if (!mask) {
nsh->ver_flags_ttl_len = htons(key->flags << NSH_FLAGS_SHIFT) | nsh_set_flags_and_ttl(nsh, key->flags, key->ttl);
(nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK)); put_16aligned_be32(&nsh->path_hdr, key->path_hdr);
path_hdr = htonl((ntohl(key->spi) << NSH_SPI_SHIFT) |
key->si);
put_16aligned_be32(&nsh->path_hdr, path_hdr);
switch (mdtype) { switch (mdtype) {
case NSH_M_TYPE1: case NSH_M_TYPE1:
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -299,22 +296,25 @@ odp_set_nsh(struct dp_packet *packet, const struct flow_nsh *key,
break; break;
} }
} else { } else {
uint8_t flags = (ntohs(nsh->ver_flags_ttl_len) & NSH_FLAGS_MASK) >> uint8_t flags = nsh_get_flags(nsh);
NSH_FLAGS_SHIFT; uint8_t ttl = nsh_get_ttl(nsh);
flags = key->flags | (flags & ~mask->flags);
nsh->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT) |
(nsh->ver_flags_ttl_len & ~htons(NSH_FLAGS_MASK));
path_hdr = get_16aligned_be32(&nsh->path_hdr); flags = key->flags | (flags & ~mask->flags);
uint32_t spi = (ntohl(path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT; ttl = key->ttl | (ttl & ~mask->ttl);
uint8_t si = (ntohl(path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; nsh_set_flags_and_ttl(nsh, flags, ttl);
uint32_t spi_mask = ntohl(mask->spi);
uint32_t spi = ntohl(nsh_get_spi(nsh));
uint8_t si = nsh_get_si(nsh);
uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr);
uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr);
if (spi_mask == 0x00ffffff) { if (spi_mask == 0x00ffffff) {
spi_mask = UINT32_MAX; spi_mask = UINT32_MAX;
} }
spi = ntohl(key->spi) | (spi & ~spi_mask); spi = nsh_path_hdr_to_spi_uint32(key->path_hdr) | (spi & ~spi_mask);
si = key->si | (si & ~mask->si); si = nsh_path_hdr_to_si(key->path_hdr) | (si & ~si_mask);
path_hdr = htonl((spi << NSH_SPI_SHIFT) | si); path_hdr = nsh_get_path_hdr(nsh);
nsh_path_hdr_set_spi(&path_hdr, htonl(spi));
nsh_path_hdr_set_si(&path_hdr, si);
put_16aligned_be32(&nsh->path_hdr, path_hdr); put_16aligned_be32(&nsh->path_hdr, path_hdr);
switch (mdtype) { switch (mdtype) {
case NSH_M_TYPE1: case NSH_M_TYPE1:
@ -359,7 +359,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
break; break;
case OVS_KEY_ATTR_NSH: { case OVS_KEY_ATTR_NSH: {
struct flow_nsh nsh; struct ovs_key_nsh nsh;
odp_nsh_key_from_attr(a, &nsh); odp_nsh_key_from_attr(a, &nsh);
odp_set_nsh(packet, &nsh, NULL); odp_set_nsh(packet, &nsh, NULL);
break; break;
@ -490,7 +490,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
break; break;
case OVS_KEY_ATTR_NSH: { case OVS_KEY_ATTR_NSH: {
struct flow_nsh nsh, nsh_mask; struct ovs_key_nsh nsh, nsh_mask;
struct { struct {
struct nlattr nla; struct nlattr nla;
uint8_t data[sizeof(struct ovs_nsh_key_base) + NSH_CTX_HDRS_MAX_LEN uint8_t data[sizeof(struct ovs_nsh_key_base) + NSH_CTX_HDRS_MAX_LEN

View File

@ -246,12 +246,13 @@ static void
format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key) format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)
{ {
ds_put_format(ds, "flags=%d", key->flags); ds_put_format(ds, "flags=%d", key->flags);
ds_put_format(ds, "ttl=%d", key->ttl);
ds_put_format(ds, ",mdtype=%d", key->mdtype); ds_put_format(ds, ",mdtype=%d", key->mdtype);
ds_put_format(ds, ",np=%d", key->np); ds_put_format(ds, ",np=%d", key->np);
ds_put_format(ds, ",spi=0x%x", ds_put_format(ds, ",spi=0x%x",
(ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT); nsh_path_hdr_to_spi_uint32(key->path_hdr));
ds_put_format(ds, ",si=%d", ds_put_format(ds, ",si=%d",
(ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT); nsh_path_hdr_to_si(key->path_hdr));
switch (key->mdtype) { switch (key->mdtype) {
case NSH_M_TYPE1: case NSH_M_TYPE1:
@ -311,17 +312,16 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
format_nsh_key(ds, key); format_nsh_key(ds, key);
} else { } else {
bool first = true; bool first = true;
uint32_t spi = (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT; uint32_t spi = nsh_path_hdr_to_spi_uint32(key->path_hdr);
uint32_t spi_mask = (ntohl(mask->path_hdr) & NSH_SPI_MASK) >> uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr);
NSH_SPI_SHIFT; if (spi_mask == (NSH_SPI_MASK >> NSH_SPI_SHIFT)) {
if (spi_mask == 0x00ffffff) {
spi_mask = UINT32_MAX; spi_mask = UINT32_MAX;
} }
uint8_t si = (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT; uint8_t si = nsh_path_hdr_to_si(key->path_hdr);
uint8_t si_mask = (ntohl(mask->path_hdr) & NSH_SI_MASK) >> uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr);
NSH_SI_SHIFT;
format_uint8_masked(ds, &first, "flags", key->flags, mask->flags); format_uint8_masked(ds, &first, "flags", key->flags, mask->flags);
format_uint8_masked(ds, &first, "ttl", key->ttl, mask->ttl);
format_uint8_masked(ds, &first, "mdtype", key->mdtype, mask->mdtype); format_uint8_masked(ds, &first, "mdtype", key->mdtype, mask->mdtype);
format_uint8_masked(ds, &first, "np", key->np, mask->np); format_uint8_masked(ds, &first, "np", key->np, mask->np);
format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask)); format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask));
@ -342,13 +342,14 @@ format_odp_push_nsh_action(struct ds *ds,
const struct nsh_hdr *nsh_hdr) const struct nsh_hdr *nsh_hdr)
{ {
size_t mdlen = nsh_hdr_len(nsh_hdr) - NSH_BASE_HDR_LEN; size_t mdlen = nsh_hdr_len(nsh_hdr) - NSH_BASE_HDR_LEN;
uint32_t path_hdr = ntohl(get_16aligned_be32(&nsh_hdr->path_hdr)); uint32_t spi = ntohl(nsh_get_spi(nsh_hdr));
uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT; uint8_t si = nsh_get_si(nsh_hdr);
uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
uint8_t flags = nsh_get_flags(nsh_hdr); uint8_t flags = nsh_get_flags(nsh_hdr);
uint8_t ttl = nsh_get_ttl(nsh_hdr);
ds_put_cstr(ds, "push_nsh("); ds_put_cstr(ds, "push_nsh(");
ds_put_format(ds, "flags=%d", flags); ds_put_format(ds, "flags=%d", flags);
ds_put_format(ds, ",ttl=%d", ttl);
ds_put_format(ds, ",mdtype=%d", nsh_hdr->md_type); ds_put_format(ds, ",mdtype=%d", nsh_hdr->md_type);
ds_put_format(ds, ",np=%d", nsh_hdr->next_proto); ds_put_format(ds, ",np=%d", nsh_hdr->next_proto);
ds_put_format(ds, ",spi=0x%x", spi); ds_put_format(ds, ",spi=0x%x", spi);
@ -1826,7 +1827,7 @@ find_end:
} }
static void static void
nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh, nsh_key_to_attr(struct ofpbuf *buf, const struct ovs_key_nsh *nsh,
uint8_t * metadata, size_t md_size, uint8_t * metadata, size_t md_size,
bool is_mask) bool is_mask)
{ {
@ -1834,10 +1835,10 @@ nsh_key_to_attr(struct ofpbuf *buf, const struct flow_nsh *nsh,
struct ovs_nsh_key_base base; struct ovs_nsh_key_base base;
base.flags = nsh->flags; base.flags = nsh->flags;
base.ttl = nsh->ttl;
base.mdtype = nsh->mdtype; base.mdtype = nsh->mdtype;
base.np = nsh->np; base.np = nsh->np;
base.path_hdr = htonl((ntohl(nsh->spi) << NSH_SPI_SHIFT) | base.path_hdr = nsh->path_hdr;
nsh->si);
nsh_key_ofs = nl_msg_start_nested(buf, OVS_KEY_ATTR_NSH); nsh_key_ofs = nl_msg_start_nested(buf, OVS_KEY_ATTR_NSH);
nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_BASE, &base, sizeof base); nl_msg_put_unspec(buf, OVS_NSH_KEY_ATTR_BASE, &base, sizeof base);
@ -1872,8 +1873,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
int n = 0; int n = 0;
int ret = 0; int ret = 0;
uint32_t spi = 0; uint32_t spi = 0;
uint8_t si = 255;
uint32_t cd; uint32_t cd;
struct flow_nsh nsh; struct ovs_key_nsh nsh;
uint8_t metadata[NSH_CTX_HDRS_MAX_LEN]; uint8_t metadata[NSH_CTX_HDRS_MAX_LEN];
uint8_t md_size = 0; uint8_t md_size = 0;
@ -1884,10 +1886,10 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
/* The default is NSH_M_TYPE1 */ /* The default is NSH_M_TYPE1 */
nsh.flags = 0; nsh.flags = 0;
nsh.ttl = 63;
nsh.mdtype = NSH_M_TYPE1; nsh.mdtype = NSH_M_TYPE1;
nsh.np = NSH_P_ETHERNET; nsh.np = NSH_P_ETHERNET;
nsh.spi = 0; nsh.path_hdr = nsh_spi_si_to_path_hdr(0, 255);
nsh.si = 255;
memset(nsh.context, 0, NSH_M_TYPE1_MDLEN); memset(nsh.context, 0, NSH_M_TYPE1_MDLEN);
for (;;) { for (;;) {
@ -1899,6 +1901,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
if (ovs_scan_len(s, &n, "flags=%"SCNi8, &nsh.flags)) { if (ovs_scan_len(s, &n, "flags=%"SCNi8, &nsh.flags)) {
continue; continue;
} }
if (ovs_scan_len(s, &n, "ttl=%"SCNi8, &nsh.ttl)) {
continue;
}
if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &nsh.mdtype)) { if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &nsh.mdtype)) {
switch (nsh.mdtype) { switch (nsh.mdtype) {
case NSH_M_TYPE1: case NSH_M_TYPE1:
@ -1918,10 +1923,9 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
continue; continue;
} }
if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) { if (ovs_scan_len(s, &n, "spi=0x%"SCNx32, &spi)) {
nsh.spi = htonl(spi);
continue; continue;
} }
if (ovs_scan_len(s, &n, "si=%"SCNi8, &nsh.si)) { if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) {
continue; continue;
} }
if (nsh.mdtype == NSH_M_TYPE1) { if (nsh.mdtype == NSH_M_TYPE1) {
@ -1966,6 +1970,7 @@ parse_odp_push_nsh_action(const char *s, struct ofpbuf *actions)
} }
out: out:
if (ret >= 0) { if (ret >= 0) {
nsh.path_hdr = nsh_spi_si_to_path_hdr(spi, si);
size_t offset = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH_NSH); size_t offset = nl_msg_start_nested(actions, OVS_ACTION_ATTR_PUSH_NSH);
nsh_key_to_attr(actions, &nsh, metadata, md_size, false); nsh_key_to_attr(actions, &nsh, metadata, md_size, false);
nl_msg_end_nested(actions, offset); nl_msg_end_nested(actions, offset);
@ -2393,6 +2398,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
const struct nlattr *a; const struct nlattr *a;
bool unknown = false; bool unknown = false;
uint8_t flags = 0; uint8_t flags = 0;
uint8_t ttl = 63;
size_t mdlen = 0; size_t mdlen = 0;
bool has_md1 = false; bool has_md1 = false;
bool has_md2 = false; bool has_md2 = false;
@ -2414,6 +2420,7 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
nsh_hdr->md_type = base->mdtype; nsh_hdr->md_type = base->mdtype;
put_16aligned_be32(&nsh_hdr->path_hdr, base->path_hdr); put_16aligned_be32(&nsh_hdr->path_hdr, base->path_hdr);
flags = base->flags; flags = base->flags;
ttl = base->ttl;
break; break;
} }
case OVS_NSH_KEY_ATTR_MD1: { case OVS_NSH_KEY_ATTR_MD1: {
@ -2457,14 +2464,13 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
} }
/* nsh header length = NSH_BASE_HDR_LEN + mdlen */ /* nsh header length = NSH_BASE_HDR_LEN + mdlen */
nsh_hdr->ver_flags_ttl_len = htons(flags << NSH_FLAGS_SHIFT | nsh_set_flags_ttl_len(nsh_hdr, flags, ttl, NSH_BASE_HDR_LEN + mdlen);
(NSH_BASE_HDR_LEN + mdlen) >> 2);
return ODP_FIT_PERFECT; return ODP_FIT_PERFECT;
} }
enum odp_key_fitness enum odp_key_fitness
odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh) odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh)
{ {
unsigned int left; unsigned int left;
const struct nlattr *a; const struct nlattr *a;
@ -2485,11 +2491,10 @@ odp_nsh_key_from_attr(const struct nlattr *attr, struct flow_nsh *nsh)
case OVS_NSH_KEY_ATTR_BASE: { case OVS_NSH_KEY_ATTR_BASE: {
const struct ovs_nsh_key_base *base = nl_attr_get(a); const struct ovs_nsh_key_base *base = nl_attr_get(a);
nsh->flags = base->flags; nsh->flags = base->flags;
nsh->ttl = base->ttl;
nsh->mdtype = base->mdtype; nsh->mdtype = base->mdtype;
nsh->np = base->np; nsh->np = base->np;
nsh->spi = htonl((ntohl(base->path_hdr) & NSH_SPI_MASK) >> nsh->path_hdr = base->path_hdr;
NSH_SPI_SHIFT);
nsh->si = (ntohl(base->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
break; break;
} }
case OVS_NSH_KEY_ATTR_MD1: { case OVS_NSH_KEY_ATTR_MD1: {
@ -3238,15 +3243,17 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
case OVS_NSH_KEY_ATTR_UNSPEC: case OVS_NSH_KEY_ATTR_UNSPEC:
break; break;
case OVS_NSH_KEY_ATTR_BASE: { case OVS_NSH_KEY_ATTR_BASE: {
const struct ovs_nsh_key_base * base = nl_attr_get(a); const struct ovs_nsh_key_base *base = nl_attr_get(a);
const struct ovs_nsh_key_base * base_mask const struct ovs_nsh_key_base *base_mask
= ma ? nl_attr_get(ma) : NULL; = ma ? nl_attr_get(ma) : NULL;
nsh.flags = base->flags; nsh.flags = base->flags;
nsh.ttl = base->ttl;
nsh.mdtype = base->mdtype; nsh.mdtype = base->mdtype;
nsh.np = base->np; nsh.np = base->np;
nsh.path_hdr = base->path_hdr; nsh.path_hdr = base->path_hdr;
if (base_mask) { if (base_mask) {
nsh_mask.flags = base_mask->flags; nsh_mask.flags = base_mask->flags;
nsh_mask.ttl = base_mask->ttl;
nsh_mask.mdtype = base_mask->mdtype; nsh_mask.mdtype = base_mask->mdtype;
nsh_mask.np = base_mask->np; nsh_mask.np = base_mask->np;
nsh_mask.path_hdr = base_mask->path_hdr; nsh_mask.path_hdr = base_mask->path_hdr;
@ -3254,8 +3261,8 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
break; break;
} }
case OVS_NSH_KEY_ATTR_MD1: { case OVS_NSH_KEY_ATTR_MD1: {
const struct ovs_nsh_key_md1 * md1 = nl_attr_get(a); const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
const struct ovs_nsh_key_md1 * md1_mask const struct ovs_nsh_key_md1 *md1_mask
= ma ? nl_attr_get(ma) : NULL; = ma ? nl_attr_get(ma) : NULL;
memcpy(nsh.context, md1->context, sizeof md1->context); memcpy(nsh.context, md1->context, sizeof md1->context);
if (md1_mask) { if (md1_mask) {
@ -4863,7 +4870,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
if (strncmp(s, "nsh(", 4) == 0) { if (strncmp(s, "nsh(", 4) == 0) {
const char *start = s; const char *start = s;
int len; int len;
struct flow_nsh skey, smask; struct ovs_key_nsh skey, smask;
uint32_t spi = 0, spi_mask = 0;
uint8_t si = 0, si_mask = 0;
s += 4; s += 4;
@ -4904,7 +4913,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
if (strncmp(s, "spi=", 4) == 0) { if (strncmp(s, "spi=", 4) == 0) {
s += 4; s += 4;
len = scan_be32(s, &skey.spi, mask ? &smask.spi : NULL); len = scan_u32(s, &spi, mask ? &spi_mask : NULL);
if (len == 0) { if (len == 0) {
return -EINVAL; return -EINVAL;
} }
@ -4914,7 +4923,7 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
if (strncmp(s, "si=", 3) == 0) { if (strncmp(s, "si=", 3) == 0) {
s += 3; s += 3;
len = scan_u8(s, &skey.si, mask ? &smask.si : NULL); len = scan_u8(s, &si, mask ? &si_mask : NULL);
if (len == 0) { if (len == 0) {
return -EINVAL; return -EINVAL;
} }
@ -4970,6 +4979,9 @@ parse_odp_nsh_key_mask_attr(const char *s, struct ofpbuf *key,
return -EINVAL; return -EINVAL;
} }
skey.path_hdr = nsh_spi_si_to_path_hdr(spi, si);
smask.path_hdr = nsh_spi_si_to_path_hdr(spi_mask, si_mask);
nsh_key_to_attr(key, &skey, NULL, 0, false); nsh_key_to_attr(key, &skey, NULL, 0, false);
if (mask) { if (mask) {
nsh_key_to_attr(mask, &smask, NULL, 0, true); nsh_key_to_attr(mask, &smask, NULL, 0, true);
@ -7031,59 +7043,29 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
return 0; return 0;
} }
static void static inline void
get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask) get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)
{ {
nsh->flags = flow->nsh.flags; *nsh = flow->nsh;
nsh->mdtype = flow->nsh.mdtype; if (!is_mask) {
nsh->np = flow->nsh.np; if (nsh->mdtype != NSH_M_TYPE1) {
nsh->path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) | memset(nsh->context, 0, sizeof(nsh->context));
flow->nsh.si);
if (is_mask) {
for (int i = 0; i < 4; i++) {
nsh->context[i] = flow->nsh.context[i];
}
} else {
switch (nsh->mdtype) {
case NSH_M_TYPE1:
for (int i = 0; i < 4; i++) {
nsh->context[i] = flow->nsh.context[i];
}
break;
case NSH_M_TYPE2:
default:
/* No match support for other MD formats yet. */
break;
} }
} }
} }
static void static inline void
put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow, put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
bool is_mask OVS_UNUSED) bool is_mask OVS_UNUSED)
{ {
flow->nsh.flags = nsh->flags; flow->nsh = *nsh;
flow->nsh.mdtype = nsh->mdtype; if (flow->nsh.mdtype != NSH_M_TYPE1) {
flow->nsh.np = nsh->np; memset(flow->nsh.context, 0, sizeof(flow->nsh.context));
flow->nsh.spi = htonl((ntohl(nsh->path_hdr) & NSH_SPI_MASK) >>
NSH_SPI_SHIFT);
flow->nsh.si = (ntohl(nsh->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
switch (nsh->mdtype) {
case NSH_M_TYPE1:
for (int i = 0; i < 4; i++) {
flow->nsh.context[i] = nsh->context[i];
}
break;
case NSH_M_TYPE2:
default:
/* No match support for other MD formats yet. */
memset(flow->nsh.context, 0, sizeof flow->nsh.context);
break;
} }
} }
static bool static bool
commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set, commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set,
const struct ovs_key_nsh *key, struct ovs_key_nsh *base, const struct ovs_key_nsh *key, struct ovs_key_nsh *base,
struct ovs_key_nsh *mask, size_t size, struct ovs_key_nsh *mask, size_t size,
struct ofpbuf *odp_actions) struct ofpbuf *odp_actions)
@ -7109,11 +7091,13 @@ commit_nsh(const struct flow_nsh * flow_nsh, bool use_masked_set,
OVS_ACTION_ATTR_SET_MASKED); OVS_ACTION_ATTR_SET_MASKED);
nsh_base.flags = key->flags; nsh_base.flags = key->flags;
nsh_base.ttl = key->ttl;
nsh_base.mdtype = key->mdtype; nsh_base.mdtype = key->mdtype;
nsh_base.np = key->np; nsh_base.np = key->np;
nsh_base.path_hdr = key->path_hdr; nsh_base.path_hdr = key->path_hdr;
nsh_base_mask.flags = mask->flags; nsh_base_mask.flags = mask->flags;
nsh_base_mask.ttl = mask->ttl;
nsh_base_mask.mdtype = mask->mdtype; nsh_base_mask.mdtype = mask->mdtype;
nsh_base_mask.np = mask->np; nsh_base_mask.np = mask->np;
nsh_base_mask.path_hdr = mask->path_hdr; nsh_base_mask.path_hdr = mask->path_hdr;

View File

@ -158,7 +158,7 @@ struct odputil_keybuf {
enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *, enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
struct flow_tnl *); struct flow_tnl *);
enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *, enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *,
struct flow_nsh *); struct ovs_key_nsh *);
enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *, enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *,
struct nsh_hdr *, size_t size); struct nsh_hdr *, size_t size);

View File

@ -5774,10 +5774,10 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx,
/* Populate the flow with the new NSH header. */ /* Populate the flow with the new NSH header. */
flow->packet_type = htonl(PT_NSH); flow->packet_type = htonl(PT_NSH);
flow->dl_type = htons(ETH_TYPE_NSH); flow->dl_type = htons(ETH_TYPE_NSH);
flow->nsh.flags = 0; /* */ flow->nsh.flags = 0;
flow->nsh.ttl = 63;
flow->nsh.np = np; flow->nsh.np = np;
flow->nsh.spi = 0; flow->nsh.path_hdr = htonl(255);
flow->nsh.si = 255;
if (md_type == NSH_M_TYPE1) { if (md_type == NSH_M_TYPE1) {
flow->nsh.mdtype = NSH_M_TYPE1; flow->nsh.mdtype = NSH_M_TYPE1;
@ -5790,6 +5790,7 @@ rewrite_flow_push_nsh(struct xlate_ctx *ctx,
} else if (md_type == NSH_M_TYPE2) { } else if (md_type == NSH_M_TYPE2) {
flow->nsh.mdtype = NSH_M_TYPE2; flow->nsh.mdtype = NSH_M_TYPE2;
} }
flow->nsh.mdtype &= NSH_MDTYPE_MASK;
return buf; return buf;
} }

View File

@ -13,7 +13,7 @@ OVS_VSWITCHD_START([dnl
add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2]) add-port br0 p2 -- set Interface p2 type=dummy ofport_request=2])
AT_DATA([flows.txt], [dnl AT_DATA([flows.txt], [dnl
table=0,in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x80->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2 table=0,in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,actions=set_field:0x2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,2
]) ])
AT_CHECK([ AT_CHECK([
@ -21,25 +21,25 @@ AT_CHECK([
ovs-ofctl -Oopenflow13 add-flows br0 flows.txt ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
], [0], [dnl ], [0], [dnl
in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:128->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2 in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:2->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2
]) ])
AT_CHECK([ AT_CHECK([
ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00' ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00'
], [0], [dnl ], [0], [dnl
Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
bridge("br0") bridge("br0")
------------- -------------
0. in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768 0. in_port=1,dl_type=0x894f,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768
set_field:128->nsh_flags set_field:2->nsh_flags
set_field:254->nsh_si set_field:254->nsh_si
set_field:0x44332211->nsh_c1 set_field:0x44332211->nsh_c1
output:2 output:2
Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=128,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=2,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344
Datapath actions: set(nsh(flags=128,spi=0x123456,si=254,c1=0x44332211)),2 Datapath actions: set(nsh(flags=2,ttl=63,spi=0x123456,si=254,c1=0x44332211)),2
]) ])
OVS_VSWITCHD_STOP OVS_VSWITCHD_STOP
@ -103,15 +103,15 @@ bridge("br0")
decap() decap()
decap() decap()
Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no
Datapath actions: push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) Datapath actions: push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
]) ])
AT_CHECK([ AT_CHECK([
ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344' ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_c1=0x11223344'
], [0], [dnl ], [0], [dnl
Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nsh_c1=0x11223344,nsh_c2=0x0,nsh_c3=0x0,nsh_c4=0x0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
bridge("br0") bridge("br0")
------------- -------------
@ -139,7 +139,7 @@ ovs-appctl time/warp 1000
AT_CHECK([ AT_CHECK([
ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort
], [0], [flow-dump from non-dpdk interfaces: ], [0], [flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x1234,si=255,c1=0x11223344,c2=0x0,c3=0x0,c4=0x0),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2
]) ])
@ -172,7 +172,7 @@ ovs-appctl time/warp 1000
AT_CHECK([ AT_CHECK([
ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort
], [0], [flow-dump from non-dpdk interfaces: ], [0], [flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_vlan(vid=100,pcp=0),push_nsh(flags=0,ttl=63,mdtype=1,np=3,spi=0x0,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),pop_nsh(),recirc(0x4)
recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100,pcp=0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2 recirc_id(0x4),in_port(1),packet_type(ns=0,id=0),eth_type(0x8100),vlan(vid=100,pcp=0),encap(eth_type(0x0800),ipv4(frag=no)), packets:1, bytes:102, used:0.0s, actions:2
]) ])
@ -230,15 +230,15 @@ bridge("br0")
decap() decap()
decap() decap()
Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=11:22:33:44:55:66,dl_type=0x894f,nsh_flags=0,nsh_ttl=63,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=255,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no Megaflow: recirc_id=0,eth,ip,in_port=1,dl_dst=66:77:88:99:aa:bb,nw_frag=no
Datapath actions: push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1) Datapath actions: push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x1)
]) ])
AT_CHECK([ AT_CHECK([
ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234' ovs-appctl ofproto/trace br0 'in_port=4,dl_type=0x894f,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234'
], [0], [dnl ], [0], [dnl
Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0 Flow: in_port=4,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_ttl=0,nsh_mdtype=2,nsh_np=3,nsh_spi=0x1234,nsh_si=0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
bridge("br0") bridge("br0")
------------- -------------
@ -266,7 +266,7 @@ ovs-appctl time/warp 1000
AT_CHECK([ AT_CHECK([
ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort
], [0], [flow-dump from non-dpdk interfaces: ], [0], [flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3) recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(dst=1e:2c:e9:2a:66:9e),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:push_nsh(flags=0,ttl=63,mdtype=2,np=3,spi=0x1234,si=255,md2=0x10000a041234567820001408fedcba9876543210),push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),pop_eth,pop_nsh(),set(eth(dst=11:22:33:44:55:66)),recirc(0x3)
recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2 recirc_id(0x3),in_port(1),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:98, used:0.0s, actions:2
]) ])
@ -573,13 +573,14 @@ AT_CHECK([
ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637 ovs-appctl netdev-dummy/receive n1 1e2ce92a669e3a6dd2099cab0800450000548a83400040011aadc0a80a0ac0a80a1e0800b7170a4d0002fd509a5800000000de1c020000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637
], [0], [ignore]) ], [0], [ignore])
ovs-appctl time/warp 1000
ovs-appctl time/warp 1000 ovs-appctl time/warp 1000
ovs-appctl time/warp 1000 ovs-appctl time/warp 1000
AT_CHECK([ AT_CHECK([
ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort
], [0], [flow-dump from non-dpdk interfaces: ], [0], [flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789)) recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.30,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3000,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=30.0.0.1,dst=30.0.0.3)),tnl_pop(4789))
tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3000,si=255), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x1) tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3000,si=255), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x1)
tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x1),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6 tunnel(tun_id=0x0,src=30.0.0.1,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x1),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6
]) ])
@ -633,7 +634,7 @@ ovs-appctl time/warp 1000
AT_CHECK([ AT_CHECK([
ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort ovs-appctl dpctl/dump-flows dummy@ovs-dummy | strip_used | grep -v ipv6 | sort
], [0], [flow-dump from non-dpdk interfaces: ], [0], [flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789)) recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth_type(0x0800),ipv4(dst=192.168.10.20/255.255.255.248,frag=no), packets:1, bytes:98, used:0.0s, actions:pop_eth,push_nsh(flags=0,ttl=63,mdtype=1,np=1,spi=0x3020,si=255,c1=0x0,c2=0x0,c3=0x0,c4=0x0),clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:02,src=aa:55:00:00:00:01,dl_type=0x0800),ipv4(src=10.0.0.1,dst=10.0.0.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(1)),set(ipv4(src=20.0.0.1,dst=20.0.0.2)),tnl_pop(4789))
tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789)) tunnel(tun_id=0x0,src=20.0.0.1,dst=20.0.0.2,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(spi=0x3020,si=255), packets:1, bytes:108, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=11:22:33:44:55:66),set(nsh(spi=0x3020,si=254)),pop_eth,clone(tnl_push(tnl_port(4789),header(size=50,type=4,eth(dst=aa:55:00:00:00:03,src=aa:55:00:00:00:02,dl_type=0x0800),ipv4(src=20.0.0.2,dst=20.0.0.3,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=4789,csum=0x0),vxlan(flags=0xc000004,vni=0x0)),out_port(2)),set(ipv4(src=30.0.0.2,dst=30.0.0.3)),tnl_pop(4789))
tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3020,si=254), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x2) tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0),in_port(4789),packet_type(ns=1,id=0x894f),nsh(np=1,spi=0x3020,si=254), packets:1, bytes:108, used:0.0s, actions:pop_nsh(),recirc(0x2)
tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x2),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6 tunnel(tun_id=0x0,src=30.0.0.2,dst=30.0.0.3,flags(-df-csum+key)),recirc_id(0x2),in_port(4789),packet_type(ns=1,id=0x800),ipv4(frag=no), packets:1, bytes:84, used:0.0s, actions:push_eth(src=00:00:00:00:00:00,dst=aa:55:aa:55:00:03),6

View File

@ -2396,7 +2396,7 @@ head_table () {
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_metadata0 dnl
tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
matching: matching:
dp_hash: arbitrary mask dp_hash: arbitrary mask
recirc_id: exact match or wildcard recirc_id: exact match or wildcard
@ -2567,6 +2567,7 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
nsh_c2: arbitrary mask nsh_c2: arbitrary mask
nsh_c3: arbitrary mask nsh_c3: arbitrary mask
nsh_c4: arbitrary mask nsh_c4: arbitrary mask
nsh_ttl: exact match or wildcard
' $1 ' $1
} }