From a61680c6d15fa1f1ae3072a83c0e3d7ed08f6048 Mon Sep 17 00:00:00 2001 From: Justin Pettit Date: Sat, 5 Nov 2011 15:48:12 -0700 Subject: [PATCH] Support matching and modifying IP TTL. Add support matching the IPv4 TTL and IPv6 hop limit fields. This commit also adds support for modifying the IPv4 TTL. Modifying the IPv6 hop limit isn't currently supported, since we don't support modifying IPv6 headers. We will likely want to change the user-space interface, since basic matching and setting the TTL are not generally useful. We will probably want the ability to match on extraordinary events (such as TTL of 0 or 1) and a decrement action. Feature #8024 Signed-off-by: Justin Pettit Acked-by: Jesse Gross --- NEWS | 2 ++ datapath/actions.c | 9 +++++++++ datapath/flow.c | 8 ++++++-- datapath/flow.h | 3 ++- include/linux/openvswitch.h | 2 ++ include/openflow/nicira-ext.h | 9 +++++++++ lib/classifier.c | 15 +++++++++++++-- lib/classifier.h | 1 + lib/dpif-netdev.c | 13 +++++++++++++ lib/flow.c | 29 +++++++++++++++++----------- lib/flow.h | 18 ++++++++++-------- lib/meta-flow.c | 25 ++++++++++++++++++++++++ lib/meta-flow.h | 1 + lib/nx-match.c | 17 ++++++++++++++++- lib/nx-match.def | 1 + lib/nx-match.h | 3 ++- lib/odp-util.c | 28 ++++++++++++++++----------- lib/odp-util.h | 2 +- lib/ofp-parse.c | 1 + lib/ofp-util.c | 16 +++++++++++----- ofproto/ofproto-dpif.c | 4 ++-- tests/odp.at | 36 +++++++++++++++++------------------ tests/ofp-print.at | 2 +- tests/ofproto-dpif.at | 16 ++++++++-------- tests/ovs-ofctl.at | 10 ++++++++++ utilities/ovs-ofctl.8.in | 9 +++++++++ 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 @@ -333,6 +333,13 @@ cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn) 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) { @@ -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 @@ -1091,6 +1091,16 @@ dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos) *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) { @@ -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 @@ -201,6 +201,13 @@ static const struct mf_field mf_fields[MFF_N_IDS] = { 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, @@ -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 -- 2.30.2