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 <[email protected]> --- NEWS | 2 + datapath/actions.c | 10 +++++++++ datapath/flow.c | 6 +++++ datapath/flow.h | 3 +- include/linux/openvswitch.h | 2 + include/openflow/nicira-ext.h | 25 +++++++++++++++++++++- lib/classifier.c | 15 +++++++++++- lib/classifier.h | 1 + lib/flow.c | 24 ++++++++++++++------- lib/flow.h | 16 ++++++++------ lib/meta-flow.c | 25 ++++++++++++++++++++++ lib/meta-flow.h | 1 + lib/nx-match.c | 14 +++++++++++- lib/nx-match.def | 1 + lib/nx-match.h | 3 +- lib/odp-util.c | 24 +++++++++++++++------ lib/ofp-parse.c | 5 ++++ lib/ofp-print.c | 6 +++++ lib/ofp-util.c | 45 +++++++++++++++++++++++++--------------- lib/ofp-util.def | 1 + ofproto/ofproto-dpif.c | 8 +++++++ tests/odp.at | 36 ++++++++++++++++---------------- tests/ofp-print.at | 2 +- tests/ofproto-dpif.at | 16 +++++++------- tests/ovs-ofctl.at | 10 +++++++++ utilities/ovs-ofctl.8.in | 13 +++++++++++ 26 files changed, 242 insertions(+), 72 deletions(-) diff --git a/NEWS b/NEWS index d13c906..249ffac 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 and IPv6. + - 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 d8156a0..3c2babc 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -157,6 +157,13 @@ static void set_ip_tos(struct sk_buff *skb, struct iphdr *nh, u8 new_tos) nh->tos = new_tos; } +static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) +{ + csum_replace4(&nh->check, (__force __be32)htons(nh->ttl << 8), + (__force __be32)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; @@ -178,6 +185,9 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) if (ipv4_key->ipv4_tos != nh->tos) set_ip_tos(skb, nh, 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 f79c577..21adbcf 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -201,6 +201,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 = *(u32 *)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) { @@ -959,6 +961,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, ipv4_key = nla_data(nla); 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; @@ -972,6 +975,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_ttl; swkey->ip.frag = ipv6_key->ipv6_frag; memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src, sizeof(swkey->ipv6.addr.src)); @@ -1247,6 +1251,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) 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 & OVS_FRAG_TYPE_MASK; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { struct ovs_key_ipv6 *ipv6_key; @@ -1263,6 +1268,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_ttl = swkey->ip.ttl; ipv6_key->ipv6_frag = swkey->ip.frag & OVS_FRAG_TYPE_MASK; } 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 97dc0db..b5281fe 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -49,6 +49,7 @@ struct sw_flow_key { struct { u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */ u8 tos; /* IP ToS DSCP in high 6 bits. */ + u8 ttl; /* IP TTL/hop limit. */ u8 frag; /* OVS_FRAG_TYPE_* in low 2 bits. */ } ip; union { @@ -148,7 +149,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 01a5337..e2a7b42 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -315,6 +315,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_*. */ }; @@ -324,6 +325,7 @@ struct ovs_key_ipv6 { __be32 ipv6_label; __u8 ipv6_proto; __u8 ipv6_tos; + __u8 ipv6_ttl; __u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */ }; diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index 604a061..7ca59c4 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -297,7 +297,8 @@ enum nx_action_subtype { NXAST_OUTPUT_REG, /* struct nx_action_output_reg */ NXAST_LEARN, /* struct nx_action_learn */ NXAST_EXIT, /* struct nx_action_header */ - NXAST_SET_NW_ECN /* struct nx_action_set_nw_ecn */ + NXAST_SET_NW_ECN, /* struct nx_action_set_nw_ecn */ + NXAST_SET_NW_TTL /* struct nx_action_set_nw_ttl */ }; /* Header for Nicira-defined actions. */ @@ -1074,6 +1075,19 @@ struct nx_action_set_nw_ecn { }; OFP_ASSERT(sizeof(struct nx_action_set_nw_ecn) == 16); +/* Action structure for NXAST_SET_NW_TTL. + * + * Set the time-to-live of the IP header to 'nw_ttl'. */ +struct nx_action_set_nw_ttl { + ovs_be16 type; /* OFPAT_VENDOR. */ + ovs_be16 len; /* Length is 16. */ + ovs_be32 vendor; /* NX_VENDOR_ID. */ + ovs_be16 subtype; /* NXAST_SET_NW_TTL. */ + uint8_t nw_ttl; /* New IP TTL. */ + uint8_t pad[5]; +}; +OFP_ASSERT(sizeof(struct nx_action_set_nw_ttl) == 16); + /* Flexible flow specifications (aka NXM = Nicira Extended Match). * * OpenFlow 1.0 has "struct ofp_match" for specifying flow matches. This @@ -1646,6 +1660,15 @@ OFP_ASSERT(sizeof(struct nx_action_set_nw_ecn) == 16); * Masking: Not maskable. */ #define NXM_NX_IP_ECN NXM_HEADER (0x0001, 28, 1) +/* The time-to-live/next hop 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 4d81841..5573af7 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; @@ -483,7 +490,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); @@ -634,6 +641,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 & FLOW_FRAG_MASK) { case FLOW_FRAG_ANY | FLOW_FRAG_LATER: ds_put_format(s, "frag=%s,", @@ -1180,7 +1190,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]) { @@ -1207,6 +1217,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 581ee83..d6ab74e 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/flow.c b/lib/flow.c index 82b22c1..6ee710f 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) { @@ -377,6 +378,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) { @@ -438,7 +440,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]; @@ -476,6 +478,9 @@ flow_zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards) flow->ipv6_label = 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); @@ -528,8 +533,10 @@ 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%"PRIu8" ipv6", - ntohl(flow->ipv6_label), flow->nw_proto, flow->tos); + ds_put_format(ds, " label%#"PRIx32" proto%"PRIu8" tos%"PRIu8 + " 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); @@ -537,8 +544,9 @@ flow_format(struct ds *ds, const struct flow *flow) } else { ds_put_format(ds, " proto%"PRIu8 " tos%"PRIu8 + " ttl%"PRIu8 " ip"IP_FMT"->"IP_FMT, - flow->nw_proto, flow->tos, + flow->nw_proto, flow->tos, flow->nw_ttl, IP_ARGS(&flow->nw_src), IP_ARGS(&flow->nw_dst)); } @@ -574,7 +582,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); @@ -594,7 +602,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); @@ -616,7 +624,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) @@ -646,7 +654,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 d03645e..8f04e41 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); @@ -71,6 +71,7 @@ struct flow { uint8_t dl_dst[6]; /* Ethernet destination address. */ uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */ uint8_t tos; /* IP ToS. */ + uint8_t nw_ttl; /* IP TTL/Hop Limit. */ uint8_t frag; /* FLOW_FRAG_*. */ uint8_t arp_sha[6]; /* ARP/ND source hardware address. */ uint8_t arp_tha[6]; /* ARP/ND target hardware address. */ @@ -78,14 +79,14 @@ struct flow { /* 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 3 +#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4) +#define FLOW_PAD_SIZE 2 BUILD_ASSERT_DECL(offsetof(struct flow, arp_tha) == FLOW_SIG_SIZE - 6); BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->arp_tha) == 6); 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 *); @@ -142,10 +143,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". * @@ -166,7 +168,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 c1eb753..b228134 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: @@ -688,6 +697,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: @@ -818,6 +828,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 & FLOW_FRAG_MASK; break; @@ -973,6 +987,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; @@ -1146,6 +1164,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; @@ -1225,6 +1248,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: @@ -1430,6 +1454,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 42523fc..61be956 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 c7ad0d0..96a959b 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -462,7 +462,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)) { @@ -498,6 +498,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) { @@ -552,6 +556,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) { @@ -1060,6 +1068,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 & FLOW_FRAG_MASK; @@ -1250,6 +1261,7 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow, case NFI_NXM_OF_IN_PORT: case NFI_NXM_OF_ETH_TYPE: case NFI_NXM_OF_IP_PROTO: + case NFI_NXM_NX_IP_TTL: case NFI_NXM_OF_ARP_OP: case NFI_NXM_OF_ARP_SPA: case NFI_NXM_OF_ARP_TPA: diff --git a/lib/nx-match.def b/lib/nx-match.def index af9e629..6d17184 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, false) /* 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 97eab9b..15d7d0a 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 269 + * total 274 * * So this value is conservative. */ diff --git a/lib/odp-util.c b/lib/odp-util.c index 29abce4..3e0f3e7 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=0x%"PRIx8",frag=%s)", + ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRId8 + ",tos=0x%"PRIx8",ttl=%"PRId8",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=0x%"PRIx32",proto=%"PRId8 - ",tos=0x%"PRIx8",frag=%s)", + ",tos=0x%"PRIx8",ttl=%"PRId8",frag=%s)", src_str, dst_str, ntohl(ipv6_key->ipv6_label), ipv6_key->ipv6_proto, ipv6_key->ipv6_tos, + ipv6_key->ipv6_ttl, ovs_frag_type_to_string(ipv6_key->ipv6_frag)); break; } @@ -606,14 +608,15 @@ 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; @@ -623,6 +626,7 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) 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,14 +640,15 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) int ipv6_label; int ipv6_proto; int ipv6_tos; + int ipv6_ttl; 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,ttl=%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_ttl, frag, &n) > 0 && n > 0 && ovs_frag_type_from_string(frag, &ipv6_frag)) { struct ovs_key_ipv6 ipv6_key; @@ -656,6 +661,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_ttl = ipv6_ttl; ipv6_key.ipv6_frag = ipv6_frag; nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6, &ipv6_key, sizeof ipv6_key); @@ -877,6 +883,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) 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; @@ -889,6 +896,7 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) ipv6_key->ipv6_label = flow->ipv6_label; ipv6_key->ipv6_proto = flow->nw_proto; ipv6_key->ipv6_tos = flow->tos; + ipv6_key->ipv6_ttl = 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 +1069,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 +1085,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_ttl; if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) { return EINVAL; } diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index c627216..1f404ba 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; @@ -367,6 +368,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, case OFPUTIL_NXAST_SET_NW_ECN: ofputil_put_NXAST_SET_NW_ECN(b)->nw_ecn = str_to_u32(arg); break; + + case OFPUTIL_NXAST_SET_NW_TTL: + ofputil_put_NXAST_SET_NW_TTL(b)->nw_ttl = str_to_u32(arg); + break; } } diff --git a/lib/ofp-print.c b/lib/ofp-print.c index fdda13b..5b620f4 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -184,6 +184,7 @@ ofp_print_action(struct ds *s, const union ofp_action *a, const struct nx_action_autopath *naa; const struct nx_action_output_reg *naor; const struct nx_action_set_nw_ecn *nasecn; + const struct nx_action_set_nw_ttl *nasttl; uint16_t port; switch (code) { @@ -348,6 +349,11 @@ ofp_print_action(struct ds *s, const union ofp_action *a, ds_put_format(s, "mod_nw_ecn:%d", nasecn->nw_ecn); break; + case OFPUTIL_NXAST_SET_NW_TTL: + nasttl = (const struct nx_action_set_nw_ttl *) a; + ds_put_format(s, "mod_nw_ttl:%d", nasttl->nw_ttl); + break; + default: break; } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index ad2964b..4838447 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; } @@ -2265,6 +2270,7 @@ validate_actions(const union ofp_action *actions, size_t n_actions, case OFPUTIL_NXAST_SET_TUNNEL64: case OFPUTIL_NXAST_EXIT: case OFPUTIL_NXAST_SET_NW_ECN: + case OFPUTIL_NXAST_SET_NW_TTL: break; } @@ -2514,23 +2520,25 @@ void ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) { enum { - 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_TOS = 1 << 3, /* tos */ - MAY_ARP_SHA = 1 << 4, /* arp_sha */ - MAY_ARP_THA = 1 << 5, /* arp_tha */ - MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */ - MAY_ND_TARGET = 1 << 7, /* nd_target */ - MAY_IPV6_LABEL = 1 << 8, /* ipv6_label */ - MAY_FRAG = 1 << 9 /* frag */ + 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_TOS = 1 << 3, /* tos */ + MAY_ARP_SHA = 1 << 4, /* arp_sha */ + MAY_ARP_THA = 1 << 5, /* arp_tha */ + MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */ + MAY_ND_TARGET = 1 << 7, /* nd_target */ + MAY_IPV6_LABEL = 1 << 8, /* ipv6_label */ + MAY_FRAG = 1 << 9, /* frag */ + MAY_NW_TTL = 1 << 10 /* ttl/hop limit */ } may_match; struct flow_wildcards wc; /* Figure out what fields may be matched. */ if (rule->flow.dl_type == htons(ETH_TYPE_IP)) { - may_match = MAY_NW_PROTO | MAY_TOS | MAY_FRAG | MAY_NW_ADDR; + may_match = MAY_NW_PROTO | MAY_TOS | MAY_NW_TTL | MAY_FRAG + | MAY_NW_ADDR; if (rule->flow.nw_proto == IPPROTO_TCP || rule->flow.nw_proto == IPPROTO_UDP || rule->flow.nw_proto == IPPROTO_ICMP) { @@ -2538,8 +2546,8 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) } } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6) && flow_format == NXFF_NXM) { - may_match = MAY_NW_PROTO | MAY_TOS | MAY_FRAG | MAY_IPV6_ADDR - | MAY_IPV6_LABEL; + may_match = MAY_NW_PROTO | MAY_TOS | MAY_NW_TTL | MAY_FRAG + | MAY_IPV6_ADDR | MAY_IPV6_LABEL; if (rule->flow.nw_proto == IPPROTO_TCP || rule->flow.nw_proto == IPPROTO_UDP) { may_match |= MAY_TP_ADDR; @@ -2574,6 +2582,9 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) if (!(may_match & MAY_TOS)) { wc.tos_mask = 0; } + if (!(may_match & MAY_NW_TTL)) { + wc.wildcards |= FWW_NW_TTL; + } if (!(may_match & MAY_FRAG)) { wc.frag_mask = 0; } diff --git a/lib/ofp-util.def b/lib/ofp-util.def index ac01d94..270888f 100644 --- a/lib/ofp-util.def +++ b/lib/ofp-util.def @@ -37,4 +37,5 @@ NXAST_ACTION(NXAST_OUTPUT_REG, nx_action_output_reg, 0, NULL) NXAST_ACTION(NXAST_LEARN, nx_action_learn, 1, "learn") NXAST_ACTION(NXAST_EXIT, nx_action_header, 0, "exit") NXAST_ACTION(NXAST_SET_NW_ECN, nx_action_set_nw_ecn, 0, "mod_nw_ecn") +NXAST_ACTION(NXAST_SET_NW_TTL, nx_action_set_nw_ttl, 0, "mod_nw_ttl") #undef NXAST_ACTION diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 8176eed..9bd824f 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -3671,6 +3671,7 @@ 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; } @@ -3681,6 +3682,7 @@ commit_set_nw_action(const struct flow *flow, struct flow *base, 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 = (frag == 0 ? OVS_FRAG_TYPE_NONE : frag == FLOW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); @@ -4121,6 +4123,7 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, const struct nx_action_bundle *nab; const struct nx_action_output_reg *naor; const struct nx_action_set_nw_ecn *nasecn; + const struct nx_action_set_nw_ttl *nasttl; enum ofputil_action_code code; ovs_be64 tun_id; @@ -4273,6 +4276,11 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, ctx->flow.tos &= ~IP_ECN_MASK; ctx->flow.tos |= nasecn->nw_ecn & IP_ECN_MASK; break; + + case OFPUTIL_NXAST_SET_NW_TTL: + nasttl = (const struct nx_action_set_nw_ttl *) ia; + ctx->flow.nw_ttl = nasttl->nw_ttl; + break; } } diff --git a/tests/odp.at b/tests/odp.at index edbd401..8dbfb4c 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=0x0,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=0x0,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=0x0,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=0x0,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=0x0,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=0x0,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=0x0,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=0x0,proto=6,tos=0x0,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=0x0,proto=17,tos=0x0,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=0x0,proto=58,tos=0x0,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=0x0,proto=58,tos=0x0,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=0x0,proto=58,tos=0x0,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=0x0,proto=58,tos=0x0,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=0x0,proto=58,tos=0x0,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=0x0,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=0x0,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=0x0,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=0x0,proto=10,tos=0x70,ttl=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=0x0,proto=10,tos=0x71,ttl=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=0x0,proto=10,tos=0x70,ttl=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=0x0,proto=10,tos=0x70,ttl=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=0x0,proto=6,tos=0x0,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(0x86dd),ipv6(src=::1,dst=::2,label=0x0,proto=17,tos=0x0,ttl=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=0x0,proto=58,tos=0x0,ttl=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=0x0,proto=58,tos=0x0,ttl=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=0x0,proto=58,tos=0x0,ttl=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=0x0,proto=58,tos=0x0,ttl=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=0x0,proto=58,tos=0x0,ttl=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 d8e2f35..df5dc9b 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 d08097b..e6c2f92 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 d127215..528b5d2 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 f62ce18..bfb178f 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 @@ -729,6 +738,10 @@ modified. Sets the IP ECN field to \fIecn\fR. Valid values are between 0 and 4, inclusive. . +.IP \fBmod_nw_ttl\fB:\fIttl\fR +Sets the IP time-to-live field to \fIttl\fR. Valid values are between 0 +and 255, inclusive. +. .RE .IP The following actions are Nicira vendor extensions that, as of this writing, are -- 1.7.1 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
