- OpenFlow:
- Added ability to match on IPv6 flow label through NXM.
- Added ability to match on ECN bits in IPv4 and IPv6 through NXM.
+ - Added ability to match on TTL in IPv4 and IPv6 through NXM.
- Added ability to modify ECN bits in IPv4.
+ - Added ability to modify TTL in IPv4.
- ovs-appctl:
- New "fdb/flush" command to flush bridge's MAC learning table.
*addr = new_addr;
}
+static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
+{
+ csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
+ nh->ttl = new_ttl;
+}
+
static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
{
struct iphdr *nh;
if (ipv4_key->ipv4_tos != nh->tos)
ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos);
+ if (ipv4_key->ipv4_ttl != nh->ttl)
+ set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl);
+
return 0;
}
key->ip.proto = NEXTHDR_NONE;
key->ip.tos = ipv6_get_dsfield(nh);
+ key->ip.ttl = nh->hop_limit;
key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL);
ipv6_addr_copy(&key->ipv6.addr.src, &nh->saddr);
ipv6_addr_copy(&key->ipv6.addr.dst, &nh->daddr);
key->ip.proto = nh->protocol;
key->ip.tos = nh->tos;
+ key->ip.ttl = nh->ttl;
offset = nh->frag_off & htons(IP_OFFSET);
if (offset) {
goto invalid;
swkey->ip.proto = ipv4_key->ipv4_proto;
swkey->ip.tos = ipv4_key->ipv4_tos;
+ swkey->ip.ttl = ipv4_key->ipv4_ttl;
swkey->ip.frag = ipv4_key->ipv4_frag;
swkey->ipv4.addr.src = ipv4_key->ipv4_src;
swkey->ipv4.addr.dst = ipv4_key->ipv4_dst;
swkey->ipv6.label = ipv6_key->ipv6_label;
swkey->ip.proto = ipv6_key->ipv6_proto;
swkey->ip.tos = ipv6_key->ipv6_tos;
+ swkey->ip.ttl = ipv6_key->ipv6_hlimit;
swkey->ip.frag = ipv6_key->ipv6_frag;
memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src,
sizeof(swkey->ipv6.addr.src));
if (!nla)
goto nla_put_failure;
ipv4_key = nla_data(nla);
- memset(ipv4_key, 0, sizeof(struct ovs_key_ipv4));
ipv4_key->ipv4_src = swkey->ipv4.addr.src;
ipv4_key->ipv4_dst = swkey->ipv4.addr.dst;
ipv4_key->ipv4_proto = swkey->ip.proto;
ipv4_key->ipv4_tos = swkey->ip.tos;
+ ipv4_key->ipv4_ttl = swkey->ip.ttl;
ipv4_key->ipv4_frag = swkey->ip.frag;
} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
struct ovs_key_ipv6 *ipv6_key;
if (!nla)
goto nla_put_failure;
ipv6_key = nla_data(nla);
- memset(ipv6_key, 0, sizeof(struct ovs_key_ipv6));
memcpy(ipv6_key->ipv6_src, &swkey->ipv6.addr.src,
sizeof(ipv6_key->ipv6_src));
memcpy(ipv6_key->ipv6_dst, &swkey->ipv6.addr.dst,
ipv6_key->ipv6_label = swkey->ipv6.label;
ipv6_key->ipv6_proto = swkey->ip.proto;
ipv6_key->ipv6_tos = swkey->ip.tos;
+ ipv6_key->ipv6_hlimit = swkey->ip.ttl;
ipv6_key->ipv6_frag = swkey->ip.frag;
} else if (swkey->eth.type == htons(ETH_P_ARP)) {
struct ovs_key_arp *arp_key;
struct {
u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */
u8 tos; /* IP ToS. */
+ u8 ttl; /* IP TTL/hop limit. */
u8 frag; /* One of OVS_FRAG_TYPE_*. */
} ip;
union {
* OVS_KEY_ATTR_ETHERNET 12 -- 4 16
* OVS_KEY_ATTR_8021Q 4 -- 4 8
* OVS_KEY_ATTR_ETHERTYPE 2 2 4 8
- * OVS_KEY_ATTR_IPV6 39 1 4 44
+ * OVS_KEY_ATTR_IPV6 40 -- 4 44
* OVS_KEY_ATTR_ICMPV6 2 2 4 8
* OVS_KEY_ATTR_ND 28 -- 4 32
* -------------------------------------------------
__be32 ipv4_dst;
__u8 ipv4_proto;
__u8 ipv4_tos;
+ __u8 ipv4_ttl;
__u8 ipv4_frag; /* One of OVS_FRAG_TYPE_*. */
};
__be32 ipv6_label; /* 20-bits in least-significant bits. */
__u8 ipv6_proto;
__u8 ipv6_tos;
+ __u8 ipv6_hlimit;
__u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */
};
* Masking: Not maskable. */
#define NXM_NX_IP_ECN NXM_HEADER (0x0001, 28, 1)
+/* The time-to-live/hop limit of the IP header.
+ *
+ * Prereqs: NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd.
+ *
+ * Format: 8-bit integer.
+ *
+ * Masking: Not maskable. */
+#define NXM_NX_IP_TTL NXM_HEADER (0x0001, 29, 1)
+
/* ## --------------------- ## */
/* ## Requests and replies. ## */
/* ## --------------------- ## */
rule->flow.tos |= nw_ecn & IP_ECN_MASK;
}
+void
+cls_rule_set_nw_ttl(struct cls_rule *rule, uint8_t nw_ttl)
+{
+ rule->wc.wildcards &= ~FWW_NW_TTL;
+ rule->flow.nw_ttl = nw_ttl;
+}
+
void
cls_rule_set_frag(struct cls_rule *rule, uint8_t frag)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
if (rule->priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%d,", rule->priority);
if (wc->tos_mask & IP_ECN_MASK) {
ds_put_format(s, "nw_ecn=%"PRIu8",", f->tos & IP_ECN_MASK);
}
+ if (!(w & FWW_NW_TTL)) {
+ ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl);
+ }
switch (wc->frag_mask) {
case FLOW_FRAG_ANY | FLOW_FRAG_LATER:
ds_put_format(s, "frag=%s,",
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
&& (wc & FWW_ETH_MCAST
|| !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
&& (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
+ && (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl)
&& !((a->tos ^ b->tos) & wildcards->tos_mask)
&& !((a->frag ^ b->frag) & wildcards->frag_mask)
&& (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha))
bool cls_rule_set_nw_dst_masked(struct cls_rule *, ovs_be32 ip, ovs_be32 mask);
void cls_rule_set_nw_dscp(struct cls_rule *, uint8_t);
void cls_rule_set_nw_ecn(struct cls_rule *, uint8_t);
+void cls_rule_set_nw_ttl(struct cls_rule *, uint8_t);
void cls_rule_set_frag(struct cls_rule *, uint8_t frag);
void cls_rule_set_frag_masked(struct cls_rule *, uint8_t frag, uint8_t mask);
void cls_rule_set_icmp_type(struct cls_rule *, uint8_t);
*field = new_tos;
}
+static void
+dp_netdev_set_ip_ttl(struct ip_header *nh, uint8_t new_ttl)
+{
+ uint8_t *field = &nh->ip_ttl;
+
+ nh->ip_csum = recalc_csum16(nh->ip_csum, htons(*field << 8),
+ htons(new_ttl << 8));
+ *field = new_ttl;
+}
+
static void
dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key)
{
if (nh->ip_tos != ipv4_key->ipv4_tos) {
dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos);
}
+ if (nh->ip_ttl != ipv4_key->ipv4_ttl) {
+ dp_netdev_set_ip_ttl(nh, ipv4_key->ipv4_ttl);
+ }
}
static void
tc_flow = get_unaligned_be32(&nh->ip6_flow);
flow->tos = ntohl(tc_flow) >> 4;
flow->ipv6_label = tc_flow & htonl(IPV6_LABEL_MASK);
+ flow->nw_ttl = nh->ip6_hlim;
flow->nw_proto = IPPROTO_NONE;
while (1) {
flow->frag |= FLOW_FRAG_LATER;
}
}
+ flow->nw_ttl = nh->ip_ttl;
if (!(nh->ip_frag_off & htons(IP_FRAG_OFF_MASK))) {
if (flow->nw_proto == IPPROTO_TCP) {
const flow_wildcards_t wc = wildcards->wildcards;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[i];
flow->ipv6_label = htonl(0);
}
flow->tos &= wildcards->tos_mask;
+ if (wc & FWW_NW_TTL) {
+ flow->nw_ttl = 0;
+ }
flow->frag &= wildcards->frag_mask;
if (wc & FWW_ARP_SHA) {
memset(flow->arp_sha, 0, sizeof flow->arp_sha);
ntohs(flow->dl_type));
if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
- ds_put_format(ds, " label%#"PRIx32" proto%"PRIu8" tos%#"PRIx8" ipv6",
- ntohl(flow->ipv6_label), flow->nw_proto, flow->tos);
+ ds_put_format(ds, " label%#"PRIx32" proto%"PRIu8" tos%#"PRIx8
+ " ttl%"PRIu8" ipv6",
+ ntohl(flow->ipv6_label), flow->nw_proto,
+ flow->tos, flow->nw_ttl);
print_ipv6_addr(ds, &flow->ipv6_src);
ds_put_cstr(ds, "->");
print_ipv6_addr(ds, &flow->ipv6_dst);
} else {
- ds_put_format(ds, " proto%"PRIu8" tos%#"PRIx8" ip"IP_FMT"->"IP_FMT,
- flow->nw_proto, flow->tos,
- IP_ARGS(&flow->nw_src),
- IP_ARGS(&flow->nw_dst));
+ ds_put_format(ds, " proto%"PRIu8" tos%#"PRIx8" ttl%"PRIu8
+ " ip"IP_FMT"->"IP_FMT,
+ flow->nw_proto, flow->tos, flow->nw_ttl,
+ IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst));
}
if (flow->frag) {
ds_put_format(ds, " frag(%s)",
void
flow_wildcards_init_catchall(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
wc->wildcards = FWW_ALL;
wc->tun_id_mask = htonll(0);
void
flow_wildcards_init_exact(struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
wc->wildcards = 0;
wc->tun_id_mask = htonll(UINT64_MAX);
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
if (wc->wildcards
|| wc->tun_id_mask != htonll(UINT64_MAX)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
if (wc->wildcards != FWW_ALL
|| wc->tun_id_mask != htonll(0)
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 5
+#define FLOW_WC_SEQ 6
#define FLOW_N_REGS 5
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
uint8_t tos; /* IP ToS. */
uint8_t arp_sha[6]; /* ARP/ND source hardware address. */
uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
+ uint8_t nw_ttl; /* IP TTL/Hop Limit. */
uint8_t frag; /* FLOW_FRAG_* flags. */
- uint8_t reserved[7]; /* Reserved for 64-bit packing. */
+ uint8_t reserved[6]; /* Reserved for 64-bit packing. */
};
/* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
* flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (109 + FLOW_N_REGS * 4)
-#define FLOW_PAD_SIZE 7
+#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4)
+#define FLOW_PAD_SIZE 6
BUILD_ASSERT_DECL(offsetof(struct flow, frag) == FLOW_SIG_SIZE - 1);
BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->frag) == 1);
BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
-BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 129 && FLOW_WC_SEQ == 5);
+BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 130 && FLOW_WC_SEQ == 6);
void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id,
uint16_t in_port, struct flow *);
#define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 9))
#define FWW_ND_TARGET ((OVS_FORCE flow_wildcards_t) (1 << 10))
#define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 11))
-#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
+#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 12))
+#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 13)) - 1))
/* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */
-BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 5);
+BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 6);
/* Information on wildcards for a flow, as a supplement to "struct flow".
*
};
/* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */
-BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 5);
+BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 6);
void flow_wildcards_init_catchall(struct flow_wildcards *);
void flow_wildcards_init_exact(struct flow_wildcards *);
MFS_DECIMAL,
MFP_IP_ANY,
NXM_NX_IP_ECN,
+ }, {
+ MFF_IP_TTL, "nw_ttl", NULL,
+ MF_FIELD_SIZES(u8),
+ MFM_NONE, FWW_NW_TTL,
+ MFS_DECIMAL,
+ MFP_IP_ANY,
+ NXM_NX_IP_TTL,
}, {
MFF_IP_FRAG, "ip_frag", NULL,
1, 2,
case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
+ case MFF_IP_TTL:
case MFF_IPV6_LABEL:
case MFF_ARP_OP:
case MFF_ARP_SHA:
case MFF_ETH_SRC:
case MFF_ETH_TYPE:
case MFF_IP_PROTO:
+ case MFF_IP_TTL:
case MFF_IPV6_LABEL:
case MFF_ARP_OP:
case MFF_ARP_SHA:
case MFF_IPV6_SRC:
case MFF_IPV6_DST:
case MFF_IP_PROTO:
+ case MFF_IP_TTL:
case MFF_ARP_SPA:
case MFF_ARP_TPA:
case MFF_ARP_SHA:
value->u8 = flow->tos & IP_ECN_MASK;
break;
+ case MFF_IP_TTL:
+ value->u8 = flow->nw_ttl;
+ break;
+
case MFF_IP_FRAG:
value->u8 = flow->frag;
break;
cls_rule_set_nw_ecn(rule, value->u8);
break;
+ case MFF_IP_TTL:
+ cls_rule_set_nw_ttl(rule, value->u8);
+ break;
+
case MFF_IP_FRAG:
cls_rule_set_frag(rule, value->u8);
break;
rule->flow.tos &= ~IP_ECN_MASK;
break;
+ case MFF_IP_TTL:
+ rule->wc.wildcards |= FWW_NW_TTL;
+ rule->flow.nw_ttl = 0;
+ break;
+
case MFF_IP_FRAG:
rule->wc.frag_mask |= FLOW_FRAG_MASK;
rule->flow.frag &= ~FLOW_FRAG_MASK;
case MFF_VLAN_PCP:
case MFF_IPV6_LABEL:
case MFF_IP_PROTO:
+ case MFF_IP_TTL:
case MFF_IP_DSCP:
case MFF_IP_ECN:
case MFF_ARP_OP:
case MFF_IPV6_SRC:
case MFF_IPV6_DST:
case MFF_IP_PROTO:
+ case MFF_IP_TTL:
case MFF_ARP_SPA:
case MFF_ARP_TPA:
case MFF_ARP_SHA:
MFF_IP_PROTO, /* u8 (used for IPv4 or IPv6) */
MFF_IP_DSCP, /* u8 (used for IPv4 or IPv6) */
MFF_IP_ECN, /* u8 (used for IPv4 or IPv6) */
+ MFF_IP_TTL, /* u8 (used for IPv4 or IPv6) */
MFF_IP_FRAG, /* u8 (used for IPv4 or IPv6) */
MFF_ARP_OP, /* be16 */
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
/* Metadata. */
if (!(wc & FWW_IN_PORT)) {
nxm_put_8(b, NXM_NX_IP_ECN, flow->tos & IP_ECN_MASK);
}
+ if (!(wc & FWW_NW_TTL)) {
+ nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl);
+ }
+
if (!(wc & FWW_NW_PROTO)) {
nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto);
switch (flow->nw_proto) {
nxm_put_8(b, NXM_NX_IP_ECN, flow->tos & IP_ECN_MASK);
}
+ if (!(wc & FWW_NW_TTL)) {
+ nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl);
+ }
+
if (!(wc & FWW_NW_PROTO)) {
nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto);
switch (flow->nw_proto) {
case NFI_NXM_NX_IP_ECN:
return flow->tos & IP_ECN_MASK;
+ case NFI_NXM_NX_IP_TTL:
+ return flow->nw_ttl;
+
case NFI_NXM_NX_IP_FRAG:
return flow->frag;
flow->tos |= new_value & IP_ECN_MASK;
break;
+ case NFI_NXM_NX_IP_TTL:
+ flow->nw_ttl = new_value;
+ break;
+
case NFI_NXM_NX_IP_FRAG:
flow->frag = new_value;
break;
DEFINE_FIELD_M(NX_IPV6_DST, MFF_IPV6_DST, false)
DEFINE_FIELD (NX_IPV6_LABEL, MFF_IPV6_LABEL,false)
DEFINE_FIELD (NX_IP_ECN, MFF_IP_ECN, true)
+DEFINE_FIELD (NX_IP_TTL, MFF_IP_TTL, true)
/* XXX should we have MFF_ICMPV4_TYPE and MFF_ICMPV6_TYPE? */
DEFINE_FIELD (NX_ICMPV6_TYPE,MFF_ICMP_TYPE, false)
DEFINE_FIELD (NX_ICMPV6_CODE,MFF_ICMP_CODE, false)
* NXM_OF_VLAN_TCI 4 2 2 8
* NXM_OF_IP_TOS 4 1 -- 5
* NXM_NX_IP_ECN 4 1 -- 5
+ * NXM_OF_IP_TTL 4 1 -- 5
* NXM_NX_IP_FRAG 4 1 1 8
* NXM_OF_IP_PROTO 4 2 -- 6
* NXM_OF_IPV6_SRC_W 4 16 16 36
* NXM_NX_REG_W(4) 4 4 4 12
* NXM_NX_TUN_ID_W 4 8 8 20
* -------------------------------------------
- * total 270
+ * total 275
*
* So this value is conservative.
*/
case OVS_KEY_ATTR_IPV4:
ipv4_key = nl_attr_get(a);
- ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT","
- "proto=%"PRId8",tos=%#"PRIx8",frag=%s)",
+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRId8
+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s)",
IP_ARGS(&ipv4_key->ipv4_src),
IP_ARGS(&ipv4_key->ipv4_dst),
ipv4_key->ipv4_proto, ipv4_key->ipv4_tos,
+ ipv4_key->ipv4_ttl,
ovs_frag_type_to_string(ipv4_key->ipv4_frag));
break;
inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str);
ds_put_format(ds, "ipv6(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRId8
- ",tos=%#"PRIx8",frag=%s)",
+ ",tos=%#"PRIx8",hlimit=%"PRIu8",frag=%s)",
src_str, dst_str, ntohl(ipv6_key->ipv6_label),
ipv6_key->ipv6_proto, ipv6_key->ipv6_tos,
+ ipv6_key->ipv6_hlimit,
ovs_frag_type_to_string(ipv6_key->ipv6_frag));
break;
}
ovs_be32 ipv4_dst;
int ipv4_proto;
int ipv4_tos;
+ int ipv4_ttl;
char frag[8];
enum ovs_frag_type ipv4_frag;
int n = -1;
if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT","
- "proto=%i,tos=%i,frag=%7[a-z])%n",
+ "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n",
IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst),
- &ipv4_proto, &ipv4_tos, frag, &n) > 0
+ &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0
&& n > 0
&& ovs_frag_type_from_string(frag, &ipv4_frag)) {
struct ovs_key_ipv4 ipv4_key;
- memset(&ipv4_key, 0, sizeof ipv4_key);
ipv4_key.ipv4_src = ipv4_src;
ipv4_key.ipv4_dst = ipv4_dst;
ipv4_key.ipv4_proto = ipv4_proto;
ipv4_key.ipv4_tos = ipv4_tos;
+ ipv4_key.ipv4_ttl = ipv4_ttl;
ipv4_key.ipv4_frag = ipv4_frag;
nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4,
&ipv4_key, sizeof ipv4_key);
int ipv6_label;
int ipv6_proto;
int ipv6_tos;
+ int ipv6_hlimit;
char frag[8];
enum ovs_frag_type ipv6_frag;
int n = -1;
if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT","
- "label=%i,proto=%i,tos=%i,frag=%7[a-z])%n",
+ "label=%i,proto=%i,tos=%i,hlimit=%i,frag=%7[a-z])%n",
ipv6_src_s, ipv6_dst_s, &ipv6_label,
- &ipv6_proto, &ipv6_tos, frag, &n) > 0
+ &ipv6_proto, &ipv6_tos, &ipv6_hlimit, frag, &n) > 0
&& n > 0
&& ovs_frag_type_from_string(frag, &ipv6_frag)) {
struct ovs_key_ipv6 ipv6_key;
- memset(&ipv6_key, 0, sizeof ipv6_key);
if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 ||
inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) {
return -EINVAL;
ipv6_key.ipv6_label = htonl(ipv6_label);
ipv6_key.ipv6_proto = ipv6_proto;
ipv6_key.ipv6_tos = ipv6_tos;
+ ipv6_key.ipv6_hlimit = ipv6_hlimit;
ipv6_key.ipv6_frag = ipv6_frag;
nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6,
&ipv6_key, sizeof ipv6_key);
ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
sizeof *ipv4_key);
- memset(ipv4_key, 0, sizeof *ipv4_key);
ipv4_key->ipv4_src = flow->nw_src;
ipv4_key->ipv4_dst = flow->nw_dst;
ipv4_key->ipv4_proto = flow->nw_proto;
ipv4_key->ipv4_tos = flow->tos;
+ ipv4_key->ipv4_ttl = flow->nw_ttl;
ipv4_key->ipv4_frag = ovs_to_odp_frag(flow->frag);
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
struct ovs_key_ipv6 *ipv6_key;
ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
sizeof *ipv6_key);
- memset(ipv6_key, 0, sizeof *ipv6_key);
memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src);
memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst);
ipv6_key->ipv6_label = flow->ipv6_label;
ipv6_key->ipv6_proto = flow->nw_proto;
ipv6_key->ipv6_tos = flow->tos;
+ ipv6_key->ipv6_hlimit = flow->nw_ttl;
ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->frag);
} else if (flow->dl_type == htons(ETH_TYPE_ARP)) {
struct ovs_key_arp *arp_key;
flow->nw_dst = ipv4_key->ipv4_dst;
flow->nw_proto = ipv4_key->ipv4_proto;
flow->tos = ipv4_key->ipv4_tos;
+ flow->nw_ttl = ipv4_key->ipv4_ttl;
if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) {
return EINVAL;
}
flow->ipv6_label = ipv6_key->ipv6_label;
flow->nw_proto = ipv6_key->ipv6_proto;
flow->tos = ipv6_key->ipv6_tos;
+ flow->nw_ttl = ipv6_key->ipv6_hlimit;
if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) {
return EINVAL;
}
* OVS_KEY_ATTR_ETHERNET 12 -- 4 16
* OVS_KEY_ATTR_8021Q 4 -- 4 8
* OVS_KEY_ATTR_ETHERTYPE 2 2 4 8
- * OVS_KEY_ATTR_IPV6 39 1 4 44
+ * OVS_KEY_ATTR_IPV6 40 -- 4 44
* OVS_KEY_ATTR_ICMPV6 2 2 4 8
* OVS_KEY_ATTR_ND 28 -- 4 32
* -------------------------------------------------
case OFPUTIL_NXAST_LEARN:
learn_parse(b, arg, flow);
break;
+
case OFPUTIL_NXAST_EXIT:
ofputil_put_NXAST_EXIT(b);
break;
void
ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
/* Initialize most of rule->wc. */
flow_wildcards_init_catchall(wc);
wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
/* Wildcard fields that aren't defined by ofp_match or tun_id. */
- wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET
- | FWW_IPV6_LABEL);
+ wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_TTL
+ | FWW_ND_TARGET | FWW_IPV6_LABEL);
if (!(ofpfw & OFPFW_NW_TOS)) {
wc->tos_mask |= IP_DSCP_MASK;
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6);
/* Only NXM supports separately wildcards the Ethernet multicast bit. */
if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
return NXFF_NXM;
}
+ /* Only NXM supports matching IP TTL/hop limit. */
+ if (!(wc->wildcards & FWW_NW_TTL)) {
+ return NXFF_NXM;
+ }
+
/* Other formats can express this rule. */
return NXFF_OPENFLOW10;
}
MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */
MAY_NW_PROTO = 1 << 2, /* nw_proto */
- MAY_IPVx = 1 << 3, /* tos, frag */
+ MAY_IPVx = 1 << 3, /* tos, frag, ttl */
MAY_ARP_SHA = 1 << 4, /* arp_sha */
MAY_ARP_THA = 1 << 5, /* arp_tha */
MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */
if (!(may_match & MAY_IPVx)) {
wc.tos_mask = 0;
wc.frag_mask = 0;
+ wc.wildcards |= FWW_NW_TTL;
}
if (!(may_match & MAY_ARP_SHA)) {
wc.wildcards |= FWW_ARP_SHA;
if (base->nw_src == flow->nw_src &&
base->nw_dst == flow->nw_dst &&
base->tos == flow->tos &&
+ base->nw_ttl == flow->nw_ttl &&
base->frag == flow->frag) {
return;
}
-
- memset(&ipv4_key, 0, sizeof(ipv4_key));
ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
ipv4_key.ipv4_proto = base->nw_proto;
ipv4_key.ipv4_tos = flow->tos;
+ ipv4_key.ipv4_ttl = flow->nw_ttl;
ipv4_key.ipv4_frag = (base->frag == 0 ? OVS_FRAG_TYPE_NONE
: base->frag == FLOW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST
: OVS_FRAG_TYPE_LATER);
AT_DATA([odp-base.txt], [dnl
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=no)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x81,frag=no)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=first)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=later)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,frag=no),tcp(src=80,dst=8080)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,frag=no),udp(src=81,dst=6632)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,frag=no),icmp(type=1,code=2)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=no)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x71,frag=no)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=first)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=later)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tos=0,frag=no),tcp(src=80,dst=8080)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tos=0,frag=no),udp(src=6630,dst=22)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=1,code=2)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=135,code=0),nd(target=::3)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=135,code=0),nd(target=::3,sll=00:05:06:07:08:09)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e)
-in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x81,ttl=128,frag=no)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=first)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=later)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80,dst=8080)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81,dst=6632)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1,code=2)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=no)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x71,hlimit=128,frag=no)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=first)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=later)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tos=0,hlimit=128,frag=no),tcp(src=80,dst=8080)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tos=0,hlimit=128,frag=no),udp(src=6630,dst=22)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=1,code=2)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3,sll=00:05:06:07:08:09)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)
])
dnl so trim off the end of the line where differences appear.
AT_CHECK([sed 's/\(length 60:\).*/\1 .../' stdout], [0], [dnl
OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 data_len=60 buffer=0x00000111
-priority0:tunnel0:in_port0003:tci(0) mac50:54:00:00:00:05->50:54:00:00:00:06 type0800 proto6 tos0 ip192.168.0.1->192.168.0.2 port10031->0
+priority0:tunnel0:in_port0003:tci(0) mac50:54:00:00:00:05->50:54:00:00:00:06 type0800 proto6 tos0 ttl64 ip192.168.0.1->192.168.0.2 port10031->0
50:54:00:00:00:05 > 50:54:00:00:00:06, ethertype IPv4 (0x0800), length 60: ...
])
AT_CLEANUP
table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2)
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21
])
in_port=11,reg2=0xeef22dea actions=output:22
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 20,21,22
])
in_port=7 actions=load:0x110000ff->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]]
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 9,55,10,55,66,11,77,88
])
in_port=5 actions=set_tunnel:5
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4
])
])
AT_CHECK([ovs-ofctl replace-flows br0 flows.txt])
-base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0"
+base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128"
no_flow="$base_flow,frag=no),tcp(src=12345,dst=80)"
first_flow="$base_flow,frag=first),tcp(src=12345,dst=80)"
later_flow="$base_flow,frag=later)"
in_port=3 actions=output:13,resubmit:2,output:14
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 10
])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 12,10
])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 13,12,10
])
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(05)
NXM_OF_IP_PROTO(05)
+# IP TTL
+NXM_OF_ETH_TYPE(0800) NXM_NX_IP_TTL(80)
+NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_TTL(ff)
+NXM_NX_IP_TTL(80)
+
# IP source
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014)
NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(05)
nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+# IP TTL
+NXM_OF_ETH_TYPE(0800), NXM_NX_IP_TTL(80)
+NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_TTL(ff)
+nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ)
+
# IP source
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014)
NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000)
0x86dd, the value of \fBnw_ecn\fR is ignored (see \fBFlow Syntax\fR
above).
.
+.IP \fBnw_ttl=\fIttl\fR
+Matches IP TTL or IPv6 hop limit value \fIttl\fR, which is
+specified as a decimal number between 0 and 255, inclusive.
+.IP
+When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
+0x86dd, the value of \fBnw_ttl\fR is ignored (see \fBFlow Syntax\fR
+above).
+.IP
+.
.IP \fBtp_src=\fIport\fR
.IQ \fBtp_dst=\fIport\fR
When \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP, \fBtp_src\fR