aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--datapath/actions.c9
-rw-r--r--datapath/flow.c8
-rw-r--r--datapath/flow.h3
-rw-r--r--include/linux/openvswitch.h2
-rw-r--r--include/openflow/nicira-ext.h9
-rw-r--r--lib/classifier.c15
-rw-r--r--lib/classifier.h1
-rw-r--r--lib/dpif-netdev.c13
-rw-r--r--lib/flow.c29
-rw-r--r--lib/flow.h18
-rw-r--r--lib/meta-flow.c25
-rw-r--r--lib/meta-flow.h1
-rw-r--r--lib/nx-match.c17
-rw-r--r--lib/nx-match.def1
-rw-r--r--lib/nx-match.h3
-rw-r--r--lib/odp-util.c28
-rw-r--r--lib/odp-util.h2
-rw-r--r--lib/ofp-parse.c1
-rw-r--r--lib/ofp-util.c16
-rw-r--r--ofproto/ofproto-dpif.c4
-rw-r--r--tests/odp.at36
-rw-r--r--tests/ofp-print.at2
-rw-r--r--tests/ofproto-dpif.at16
-rw-r--r--tests/ovs-ofctl.at10
-rw-r--r--utilities/ovs-ofctl.8.in9
26 files changed, 208 insertions, 72 deletions
diff --git a/NEWS b/NEWS
index 6f3b2397..30714c50 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,9 @@ post-v1.3.0
- 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.
diff --git a/datapath/actions.c b/datapath/actions.c
index 3296dee8..61b903f6 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -151,6 +151,12 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
*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;
@@ -172,6 +178,9 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
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;
}
diff --git a/datapath/flow.c b/datapath/flow.c
index 250b38ee..54d7bda3 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -200,6 +200,7 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key,
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);
@@ -689,6 +690,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
key->ip.proto = nh->protocol;
key->ip.tos = nh->tos;
+ key->ip.ttl = nh->ttl;
offset = nh->frag_off & htons(IP_OFFSET);
if (offset) {
@@ -961,6 +963,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
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;
@@ -976,6 +979,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
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));
@@ -1240,11 +1244,11 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
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;
@@ -1253,7 +1257,6 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
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,
@@ -1261,6 +1264,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
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;
diff --git a/datapath/flow.h b/datapath/flow.h
index b08111fb..c08d3dfe 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -45,6 +45,7 @@ struct sw_flow_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 {
@@ -143,7 +144,7 @@ u64 flow_used_time(unsigned long flow_jiffies);
* 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
* -------------------------------------------------
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 62668a62..c94c5347 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -316,6 +316,7 @@ struct ovs_key_ipv4 {
__be32 ipv4_dst;
__u8 ipv4_proto;
__u8 ipv4_tos;
+ __u8 ipv4_ttl;
__u8 ipv4_frag; /* One of OVS_FRAG_TYPE_*. */
};
@@ -325,6 +326,7 @@ struct ovs_key_ipv6 {
__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_*. */
};
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index b55ea9d0..06b9035a 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -1632,6 +1632,15 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24);
* 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. ## */
/* ## --------------------- ## */
diff --git a/lib/classifier.c b/lib/classifier.c
index 39a84d52..842166cd 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -334,6 +334,13 @@ cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn)
}
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)
{
rule->wc.frag_mask |= FLOW_FRAG_MASK;
@@ -480,7 +487,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
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);
@@ -631,6 +638,9 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s)
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,",
@@ -1177,7 +1187,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
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]) {
@@ -1204,6 +1214,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
&& (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))
diff --git a/lib/classifier.h b/lib/classifier.h
index 581ee832..d6ab74e8 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -118,6 +118,7 @@ void cls_rule_set_nw_dst(struct cls_rule *, ovs_be32);
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);
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index d7768990..27125d31 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -1092,6 +1092,16 @@ dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t 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)
{
struct ip_header *nh = packet->l3;
@@ -1105,6 +1115,9 @@ 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
diff --git a/lib/flow.c b/lib/flow.c
index bbef34a6..e1ce19b9 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -150,6 +150,7 @@ parse_ipv6(struct ofpbuf *packet, struct flow *flow)
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) {
@@ -376,6 +377,7 @@ flow_extract(struct ofpbuf *packet, uint32_t priority, ovs_be64 tun_id,
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) {
@@ -437,7 +439,7 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
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];
@@ -475,6 +477,9 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
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);
@@ -525,17 +530,19 @@ flow_format(struct ds *ds, const struct flow *flow)
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)",
@@ -568,7 +575,7 @@ flow_print(FILE *stream, const struct flow *flow)
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);
@@ -588,7 +595,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
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);
@@ -610,7 +617,7 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
{
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)
@@ -640,7 +647,7 @@ flow_wildcards_is_catchall(const struct flow_wildcards *wc)
{
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)
diff --git a/lib/flow.h b/lib/flow.h
index af634fb9..e02cff67 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -35,7 +35,7 @@ struct ofpbuf;
/* 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);
@@ -73,20 +73,21 @@ struct flow {
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 *);
@@ -143,10 +144,11 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
#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".
*
@@ -167,7 +169,7 @@ struct flow_wildcards {
};
/* 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 *);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index d8153873..2df1cb48 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -202,6 +202,13 @@ static const struct mf_field mf_fields[MFF_N_IDS] = {
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,
MFM_FULLY, 0,
@@ -369,6 +376,7 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
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:
@@ -462,6 +470,7 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc,
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:
@@ -689,6 +698,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
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:
@@ -821,6 +831,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
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;
@@ -976,6 +990,10 @@ mf_set_value(const struct mf_field *mf,
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;
@@ -1149,6 +1167,11 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule)
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;
@@ -1228,6 +1251,7 @@ mf_set(const struct mf_field *mf,
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:
@@ -1433,6 +1457,7 @@ mf_random_value(const struct mf_field *mf, union mf_value *value)
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:
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index eddc5495..e2079359 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -72,6 +72,7 @@ enum mf_field_id {
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 */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index cb73084d..9919e1f1 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -460,7 +460,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
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)) {
@@ -496,6 +496,10 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
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) {
@@ -550,6 +554,10 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
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) {
@@ -1058,6 +1066,9 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
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;
@@ -1218,6 +1229,10 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow,
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;
diff --git a/lib/nx-match.def b/lib/nx-match.def
index 3691043d..ea032fc8 100644
--- a/lib/nx-match.def
+++ b/lib/nx-match.def
@@ -46,6 +46,7 @@ DEFINE_FIELD_M(NX_IPV6_SRC, MFF_IPV6_SRC, false)
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)
diff --git a/lib/nx-match.h b/lib/nx-match.h
index 6a33dbe4..0b9a437d 100644
--- a/lib/nx-match.h
+++ b/lib/nx-match.h
@@ -103,6 +103,7 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
* 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
@@ -119,7 +120,7 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits)
* 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.
*/
diff --git a/lib/odp-util.c b/lib/odp-util.c
index aa4931d4..c4046d71 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -369,11 +369,12 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
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;
@@ -386,9 +387,10 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
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;
}
@@ -606,23 +608,24 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key)
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);
@@ -636,19 +639,19 @@ parse_odp_key_attr(const char *s, struct ofpbuf *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;
@@ -656,6 +659,7 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key)
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);
@@ -872,23 +876,23 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow)
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;
@@ -1061,6 +1065,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
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;
}
@@ -1076,6 +1081,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
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;
}
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 73c3362a..0f4f9e6b 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -72,7 +72,7 @@ void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
* 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
* -------------------------------------------------
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 0146a333..40215511 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -360,6 +360,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
case OFPUTIL_NXAST_LEARN:
learn_parse(b, arg, flow);
break;
+
case OFPUTIL_NXAST_EXIT:
ofputil_put_NXAST_EXIT(b);
break;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 1b162799..fac21af7 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -101,15 +101,15 @@ static const flow_wildcards_t WC_INVARIANTS = 0
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;
@@ -859,7 +859,7 @@ ofputil_min_flow_format(const struct cls_rule *rule)
{
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)) {
@@ -902,6 +902,11 @@ ofputil_min_flow_format(const struct cls_rule *rule)
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;
}
@@ -2516,7 +2521,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
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 */
@@ -2570,6 +2575,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format)
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;
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index b39eaa15..f6eee59a 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -3671,16 +3671,16 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
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);
diff --git a/tests/odp.at b/tests/odp.at
index e2e76d0d..5f300d43 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -4,24 +4,24 @@ AT_SETUP([OVS datapath parsing and formatting - valid forms])
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)
])
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index d8e2f353..df5dc9bf 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -237,7 +237,7 @@ dnl The tcpdump output format differs slightly from one version to another,
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
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index d08097b6..e6c2f922 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -11,7 +11,7 @@ table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2)
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
])
@@ -36,7 +36,7 @@ in_port=10,reg1=0xdeadbeef actions=output: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
])
@@ -55,7 +55,7 @@ in_port=6 actions=output:NXM_NX_REG0[[0..15]],output:NXM_NX_REG0[[16..31]]
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
])
@@ -73,7 +73,7 @@ in_port=4 actions=set_tunnel:4,set_tunnel:3,output:4
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
])
@@ -239,7 +239,7 @@ priority=50 tcp ip_frag=later actions=output:6
])
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)"
@@ -275,15 +275,15 @@ in_port=2 actions=output:12,resubmit:1,output:12
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
])
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 39093a80..2a458c8d 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -224,6 +224,11 @@ NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01)
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)
@@ -408,6 +413,11 @@ NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01)
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)
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 30f9c585..cd8d4f20 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -416,6 +416,15 @@ When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or
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