From: Justin Pettit Date: Sat, 5 Nov 2011 22:48:12 +0000 (-0700) Subject: Support matching and modifying IP TTL. X-Git-Tag: v1.4.0~189 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=a61680c6d15fa1f1ae3072a83c0e3d7ed08f6048;hp=530180fd5a99e2c55107831f99fee84d6780f38c;p=sliver-openvswitch.git 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 --- diff --git a/NEWS b/NEWS index 6f3b2397c..30714c500 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 3296dee84..61b903f68 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 250b38eef..54d7bda37 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 b08111fbb..c08d3dfeb 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 62668a62f..c94c5347e 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 b55ea9d0c..06b9035ab 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 39a84d525..842166cd4 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 581ee832e..d6ab74e8a 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 d7768990d..27125d316 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 bbef34a61..e1ce19b91 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 af634fb96..e02cff677 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 d8153873f..2df1cb485 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 eddc54957..e2079359f 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 cb73084df..9919e1f10 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 3691043df..ea032fc8e 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 6a33dbe42..0b9a437d1 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 aa4931d41..c4046d71d 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 73c3362ab..0f4f9e6b7 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 0146a333c..40215511d 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 1b162799e..fac21af7e 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 b39eaa155..f6eee59a9 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 e2e76d0da..5f300d431 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 d8e2f353a..df5dc9bfa 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 d08097b62..e6c2f922a 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 39093a80f..2a458c8df 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 30f9c5852..cd8d4f207 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