diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | datapath/actions.c | 9 | ||||
-rw-r--r-- | datapath/flow.c | 8 | ||||
-rw-r--r-- | datapath/flow.h | 3 | ||||
-rw-r--r-- | include/linux/openvswitch.h | 2 | ||||
-rw-r--r-- | include/openflow/nicira-ext.h | 9 | ||||
-rw-r--r-- | lib/classifier.c | 15 | ||||
-rw-r--r-- | lib/classifier.h | 1 | ||||
-rw-r--r-- | lib/dpif-netdev.c | 13 | ||||
-rw-r--r-- | lib/flow.c | 29 | ||||
-rw-r--r-- | lib/flow.h | 18 | ||||
-rw-r--r-- | lib/meta-flow.c | 25 | ||||
-rw-r--r-- | lib/meta-flow.h | 1 | ||||
-rw-r--r-- | lib/nx-match.c | 17 | ||||
-rw-r--r-- | lib/nx-match.def | 1 | ||||
-rw-r--r-- | lib/nx-match.h | 3 | ||||
-rw-r--r-- | lib/odp-util.c | 28 | ||||
-rw-r--r-- | lib/odp-util.h | 2 | ||||
-rw-r--r-- | lib/ofp-parse.c | 1 | ||||
-rw-r--r-- | lib/ofp-util.c | 16 | ||||
-rw-r--r-- | ofproto/ofproto-dpif.c | 4 | ||||
-rw-r--r-- | tests/odp.at | 36 | ||||
-rw-r--r-- | tests/ofp-print.at | 2 | ||||
-rw-r--r-- | tests/ofproto-dpif.at | 16 | ||||
-rw-r--r-- | tests/ovs-ofctl.at | 10 | ||||
-rw-r--r-- | utilities/ovs-ofctl.8.in | 9 |
26 files changed, 208 insertions, 72 deletions
@@ -3,7 +3,9 @@ post-v1.3.0 - OpenFlow: - Added ability to match on IPv6 flow label through NXM. - Added ability to match on ECN bits in IPv4 and IPv6 through NXM. + - Added ability to match on TTL in IPv4 and IPv6 through NXM. - Added ability to modify ECN bits in IPv4. + - Added ability to modify TTL in IPv4. - ovs-appctl: - New "fdb/flush" command to flush bridge's MAC learning table. diff --git a/datapath/actions.c b/datapath/actions.c index 3296dee8..61b903f6 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -151,6 +151,12 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, *addr = new_addr; } +static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) +{ + csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); + nh->ttl = new_ttl; +} + static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) { struct iphdr *nh; @@ -172,6 +178,9 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) if (ipv4_key->ipv4_tos != nh->tos) ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos); + if (ipv4_key->ipv4_ttl != nh->ttl) + set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl); + return 0; } diff --git a/datapath/flow.c b/datapath/flow.c index 250b38ee..54d7bda3 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -200,6 +200,7 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, key->ip.proto = NEXTHDR_NONE; key->ip.tos = ipv6_get_dsfield(nh); + key->ip.ttl = nh->hop_limit; key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL); ipv6_addr_copy(&key->ipv6.addr.src, &nh->saddr); ipv6_addr_copy(&key->ipv6.addr.dst, &nh->daddr); @@ -689,6 +690,7 @@ int flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, key->ip.proto = nh->protocol; key->ip.tos = nh->tos; + key->ip.ttl = nh->ttl; offset = nh->frag_off & htons(IP_OFFSET); if (offset) { @@ -961,6 +963,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, goto invalid; swkey->ip.proto = ipv4_key->ipv4_proto; swkey->ip.tos = ipv4_key->ipv4_tos; + swkey->ip.ttl = ipv4_key->ipv4_ttl; swkey->ip.frag = ipv4_key->ipv4_frag; swkey->ipv4.addr.src = ipv4_key->ipv4_src; swkey->ipv4.addr.dst = ipv4_key->ipv4_dst; @@ -976,6 +979,7 @@ int flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, swkey->ipv6.label = ipv6_key->ipv6_label; swkey->ip.proto = ipv6_key->ipv6_proto; swkey->ip.tos = ipv6_key->ipv6_tos; + swkey->ip.ttl = ipv6_key->ipv6_hlimit; swkey->ip.frag = ipv6_key->ipv6_frag; memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src, sizeof(swkey->ipv6.addr.src)); @@ -1240,11 +1244,11 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv4_key = nla_data(nla); - memset(ipv4_key, 0, sizeof(struct ovs_key_ipv4)); ipv4_key->ipv4_src = swkey->ipv4.addr.src; ipv4_key->ipv4_dst = swkey->ipv4.addr.dst; ipv4_key->ipv4_proto = swkey->ip.proto; ipv4_key->ipv4_tos = swkey->ip.tos; + ipv4_key->ipv4_ttl = swkey->ip.ttl; ipv4_key->ipv4_frag = swkey->ip.frag; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { struct ovs_key_ipv6 *ipv6_key; @@ -1253,7 +1257,6 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv6_key = nla_data(nla); - memset(ipv6_key, 0, sizeof(struct ovs_key_ipv6)); memcpy(ipv6_key->ipv6_src, &swkey->ipv6.addr.src, sizeof(ipv6_key->ipv6_src)); memcpy(ipv6_key->ipv6_dst, &swkey->ipv6.addr.dst, @@ -1261,6 +1264,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) ipv6_key->ipv6_label = swkey->ipv6.label; ipv6_key->ipv6_proto = swkey->ip.proto; ipv6_key->ipv6_tos = swkey->ip.tos; + ipv6_key->ipv6_hlimit = swkey->ip.ttl; ipv6_key->ipv6_frag = swkey->ip.frag; } else if (swkey->eth.type == htons(ETH_P_ARP)) { struct ovs_key_arp *arp_key; diff --git a/datapath/flow.h b/datapath/flow.h index b08111fb..c08d3dfe 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -45,6 +45,7 @@ struct sw_flow_key { struct { u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */ u8 tos; /* IP ToS. */ + u8 ttl; /* IP TTL/hop limit. */ u8 frag; /* One of OVS_FRAG_TYPE_*. */ } ip; union { @@ -143,7 +144,7 @@ u64 flow_used_time(unsigned long flow_jiffies); * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_8021Q 4 -- 4 8 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 - * OVS_KEY_ATTR_IPV6 39 1 4 44 + * OVS_KEY_ATTR_IPV6 40 -- 4 44 * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ------------------------------------------------- diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index 62668a62..c94c5347 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -316,6 +316,7 @@ struct ovs_key_ipv4 { __be32 ipv4_dst; __u8 ipv4_proto; __u8 ipv4_tos; + __u8 ipv4_ttl; __u8 ipv4_frag; /* One of OVS_FRAG_TYPE_*. */ }; @@ -325,6 +326,7 @@ struct ovs_key_ipv6 { __be32 ipv6_label; /* 20-bits in least-significant bits. */ __u8 ipv6_proto; __u8 ipv6_tos; + __u8 ipv6_hlimit; __u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */ }; diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h index b55ea9d0..06b9035a 100644 --- a/include/openflow/nicira-ext.h +++ b/include/openflow/nicira-ext.h @@ -1632,6 +1632,15 @@ OFP_ASSERT(sizeof(struct nx_action_output_reg) == 24); * Masking: Not maskable. */ #define NXM_NX_IP_ECN NXM_HEADER (0x0001, 28, 1) +/* The time-to-live/hop limit of the IP header. + * + * Prereqs: NXM_OF_ETH_TYPE must be either 0x0800 or 0x86dd. + * + * Format: 8-bit integer. + * + * Masking: Not maskable. */ +#define NXM_NX_IP_TTL NXM_HEADER (0x0001, 29, 1) + /* ## --------------------- ## */ /* ## Requests and replies. ## */ /* ## --------------------- ## */ diff --git a/lib/classifier.c b/lib/classifier.c index 39a84d52..842166cd 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -334,6 +334,13 @@ cls_rule_set_nw_ecn(struct cls_rule *rule, uint8_t nw_ecn) } void +cls_rule_set_nw_ttl(struct cls_rule *rule, uint8_t nw_ttl) +{ + rule->wc.wildcards &= ~FWW_NW_TTL; + rule->flow.nw_ttl = nw_ttl; +} + +void cls_rule_set_frag(struct cls_rule *rule, uint8_t frag) { rule->wc.frag_mask |= FLOW_FRAG_MASK; @@ -480,7 +487,7 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s) int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6); if (rule->priority != OFP_DEFAULT_PRIORITY) { ds_put_format(s, "priority=%d,", rule->priority); @@ -631,6 +638,9 @@ cls_rule_format(const struct cls_rule *rule, struct ds *s) if (wc->tos_mask & IP_ECN_MASK) { ds_put_format(s, "nw_ecn=%"PRIu8",", f->tos & IP_ECN_MASK); } + if (!(w & FWW_NW_TTL)) { + ds_put_format(s, "nw_ttl=%"PRIu8",", f->nw_ttl); + } switch (wc->frag_mask) { case FLOW_FRAG_ANY | FLOW_FRAG_LATER: ds_put_format(s, "frag=%s,", @@ -1177,7 +1187,7 @@ flow_equal_except(const struct flow *a, const struct flow *b, const flow_wildcards_t wc = wildcards->wildcards; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6); for (i = 0; i < FLOW_N_REGS; i++) { if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) { @@ -1204,6 +1214,7 @@ flow_equal_except(const struct flow *a, const struct flow *b, && (wc & FWW_ETH_MCAST || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01)) && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto) + && (wc & FWW_NW_TTL || a->nw_ttl == b->nw_ttl) && !((a->tos ^ b->tos) & wildcards->tos_mask) && !((a->frag ^ b->frag) & wildcards->frag_mask) && (wc & FWW_ARP_SHA || eth_addr_equals(a->arp_sha, b->arp_sha)) diff --git a/lib/classifier.h b/lib/classifier.h index 581ee832..d6ab74e8 100644 --- a/lib/classifier.h +++ b/lib/classifier.h @@ -118,6 +118,7 @@ void cls_rule_set_nw_dst(struct cls_rule *, ovs_be32); bool cls_rule_set_nw_dst_masked(struct cls_rule *, ovs_be32 ip, ovs_be32 mask); void cls_rule_set_nw_dscp(struct cls_rule *, uint8_t); void cls_rule_set_nw_ecn(struct cls_rule *, uint8_t); +void cls_rule_set_nw_ttl(struct cls_rule *, uint8_t); void cls_rule_set_frag(struct cls_rule *, uint8_t frag); void cls_rule_set_frag_masked(struct cls_rule *, uint8_t frag, uint8_t mask); void cls_rule_set_icmp_type(struct cls_rule *, uint8_t); diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index d7768990..27125d31 100644 --- a/lib/dpif-netdev.c +++ b/lib/dpif-netdev.c @@ -1092,6 +1092,16 @@ dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos) } static void +dp_netdev_set_ip_ttl(struct ip_header *nh, uint8_t new_ttl) +{ + uint8_t *field = &nh->ip_ttl; + + nh->ip_csum = recalc_csum16(nh->ip_csum, htons(*field << 8), + htons(new_ttl << 8)); + *field = new_ttl; +} + +static void dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key) { struct ip_header *nh = packet->l3; @@ -1105,6 +1115,9 @@ dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key) if (nh->ip_tos != ipv4_key->ipv4_tos) { dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos); } + if (nh->ip_ttl != ipv4_key->ipv4_ttl) { + dp_netdev_set_ip_ttl(nh, ipv4_key->ipv4_ttl); + } } static void @@ -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) @@ -35,7 +35,7 @@ struct ofpbuf; /* This sequence number should be incremented whenever anything involving flows * or the wildcarding of flows changes. This will cause build assertion * failures in places which likely need to be updated. */ -#define FLOW_WC_SEQ 5 +#define FLOW_WC_SEQ 6 #define FLOW_N_REGS 5 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS); @@ -73,20 +73,21 @@ struct flow { uint8_t tos; /* IP ToS. */ uint8_t arp_sha[6]; /* ARP/ND source hardware address. */ uint8_t arp_tha[6]; /* ARP/ND target hardware address. */ + uint8_t nw_ttl; /* IP TTL/Hop Limit. */ uint8_t frag; /* FLOW_FRAG_* flags. */ - uint8_t reserved[7]; /* Reserved for 64-bit packing. */ + uint8_t reserved[6]; /* Reserved for 64-bit packing. */ }; /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct * flow", followed by FLOW_PAD_SIZE bytes of padding. */ -#define FLOW_SIG_SIZE (109 + FLOW_N_REGS * 4) -#define FLOW_PAD_SIZE 7 +#define FLOW_SIG_SIZE (110 + FLOW_N_REGS * 4) +#define FLOW_PAD_SIZE 6 BUILD_ASSERT_DECL(offsetof(struct flow, frag) == FLOW_SIG_SIZE - 1); BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->frag) == 1); BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE); /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ -BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 129 && FLOW_WC_SEQ == 5); +BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 130 && FLOW_WC_SEQ == 6); void flow_extract(struct ofpbuf *, uint32_t priority, ovs_be64 tun_id, uint16_t in_port, struct flow *); @@ -143,10 +144,11 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t; #define FWW_ARP_THA ((OVS_FORCE flow_wildcards_t) (1 << 9)) #define FWW_ND_TARGET ((OVS_FORCE flow_wildcards_t) (1 << 10)) #define FWW_IPV6_LABEL ((OVS_FORCE flow_wildcards_t) (1 << 11)) -#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1)) +#define FWW_NW_TTL ((OVS_FORCE flow_wildcards_t) (1 << 12)) +#define FWW_ALL ((OVS_FORCE flow_wildcards_t) (((1 << 13)) - 1)) /* Remember to update FLOW_WC_SEQ when adding or removing FWW_*. */ -BUILD_ASSERT_DECL(FWW_ALL == ((1 << 12) - 1) && FLOW_WC_SEQ == 5); +BUILD_ASSERT_DECL(FWW_ALL == ((1 << 13) - 1) && FLOW_WC_SEQ == 6); /* Information on wildcards for a flow, as a supplement to "struct flow". * @@ -167,7 +169,7 @@ struct flow_wildcards { }; /* Remember to update FLOW_WC_SEQ when updating struct flow_wildcards. */ -BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 5); +BUILD_ASSERT_DECL(sizeof(struct flow_wildcards) == 80 && FLOW_WC_SEQ == 6); void flow_wildcards_init_catchall(struct flow_wildcards *); void flow_wildcards_init_exact(struct flow_wildcards *); diff --git a/lib/meta-flow.c b/lib/meta-flow.c index d8153873..2df1cb48 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -202,6 +202,13 @@ static const struct mf_field mf_fields[MFF_N_IDS] = { MFP_IP_ANY, NXM_NX_IP_ECN, }, { + MFF_IP_TTL, "nw_ttl", NULL, + MF_FIELD_SIZES(u8), + MFM_NONE, FWW_NW_TTL, + MFS_DECIMAL, + MFP_IP_ANY, + NXM_NX_IP_TTL, + }, { MFF_IP_FRAG, "ip_frag", NULL, 1, 2, MFM_FULLY, 0, @@ -369,6 +376,7 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) case MFF_ETH_SRC: case MFF_ETH_TYPE: case MFF_IP_PROTO: + case MFF_IP_TTL: case MFF_IPV6_LABEL: case MFF_ARP_OP: case MFF_ARP_SHA: @@ -462,6 +470,7 @@ mf_get_mask(const struct mf_field *mf, const struct flow_wildcards *wc, case MFF_ETH_SRC: case MFF_ETH_TYPE: case MFF_IP_PROTO: + case MFF_IP_TTL: case MFF_IPV6_LABEL: case MFF_ARP_OP: case MFF_ARP_SHA: @@ -689,6 +698,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_IPV6_SRC: case MFF_IPV6_DST: case MFF_IP_PROTO: + case MFF_IP_TTL: case MFF_ARP_SPA: case MFF_ARP_TPA: case MFF_ARP_SHA: @@ -821,6 +831,10 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, value->u8 = flow->tos & IP_ECN_MASK; break; + case MFF_IP_TTL: + value->u8 = flow->nw_ttl; + break; + case MFF_IP_FRAG: value->u8 = flow->frag; break; @@ -976,6 +990,10 @@ mf_set_value(const struct mf_field *mf, cls_rule_set_nw_ecn(rule, value->u8); break; + case MFF_IP_TTL: + cls_rule_set_nw_ttl(rule, value->u8); + break; + case MFF_IP_FRAG: cls_rule_set_frag(rule, value->u8); break; @@ -1149,6 +1167,11 @@ mf_set_wild(const struct mf_field *mf, struct cls_rule *rule) rule->flow.tos &= ~IP_ECN_MASK; break; + case MFF_IP_TTL: + rule->wc.wildcards |= FWW_NW_TTL; + rule->flow.nw_ttl = 0; + break; + case MFF_IP_FRAG: rule->wc.frag_mask |= FLOW_FRAG_MASK; rule->flow.frag &= ~FLOW_FRAG_MASK; @@ -1228,6 +1251,7 @@ mf_set(const struct mf_field *mf, case MFF_VLAN_PCP: case MFF_IPV6_LABEL: case MFF_IP_PROTO: + case MFF_IP_TTL: case MFF_IP_DSCP: case MFF_IP_ECN: case MFF_ARP_OP: @@ -1433,6 +1457,7 @@ mf_random_value(const struct mf_field *mf, union mf_value *value) case MFF_IPV6_SRC: case MFF_IPV6_DST: case MFF_IP_PROTO: + case MFF_IP_TTL: case MFF_ARP_SPA: case MFF_ARP_TPA: case MFF_ARP_SHA: diff --git a/lib/meta-flow.h b/lib/meta-flow.h index eddc5495..e2079359 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -72,6 +72,7 @@ enum mf_field_id { MFF_IP_PROTO, /* u8 (used for IPv4 or IPv6) */ MFF_IP_DSCP, /* u8 (used for IPv4 or IPv6) */ MFF_IP_ECN, /* u8 (used for IPv4 or IPv6) */ + MFF_IP_TTL, /* u8 (used for IPv4 or IPv6) */ MFF_IP_FRAG, /* u8 (used for IPv4 or IPv6) */ MFF_ARP_OP, /* be16 */ diff --git a/lib/nx-match.c b/lib/nx-match.c index cb73084d..9919e1f1 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -460,7 +460,7 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) int match_len; int i; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6); /* Metadata. */ if (!(wc & FWW_IN_PORT)) { @@ -496,6 +496,10 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) nxm_put_8(b, NXM_NX_IP_ECN, flow->tos & IP_ECN_MASK); } + if (!(wc & FWW_NW_TTL)) { + nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl); + } + if (!(wc & FWW_NW_PROTO)) { nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto); switch (flow->nw_proto) { @@ -550,6 +554,10 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr) nxm_put_8(b, NXM_NX_IP_ECN, flow->tos & IP_ECN_MASK); } + if (!(wc & FWW_NW_TTL)) { + nxm_put_8(b, NXM_NX_IP_TTL, flow->nw_ttl); + } + if (!(wc & FWW_NW_PROTO)) { nxm_put_8(b, NXM_OF_IP_PROTO, flow->nw_proto); switch (flow->nw_proto) { @@ -1058,6 +1066,9 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow) case NFI_NXM_NX_IP_ECN: return flow->tos & IP_ECN_MASK; + case NFI_NXM_NX_IP_TTL: + return flow->nw_ttl; + case NFI_NXM_NX_IP_FRAG: return flow->frag; @@ -1218,6 +1229,10 @@ nxm_write_field(const struct nxm_field *dst, struct flow *flow, flow->tos |= new_value & IP_ECN_MASK; break; + case NFI_NXM_NX_IP_TTL: + flow->nw_ttl = new_value; + break; + case NFI_NXM_NX_IP_FRAG: flow->frag = new_value; break; diff --git a/lib/nx-match.def b/lib/nx-match.def index 3691043d..ea032fc8 100644 --- a/lib/nx-match.def +++ b/lib/nx-match.def @@ -46,6 +46,7 @@ DEFINE_FIELD_M(NX_IPV6_SRC, MFF_IPV6_SRC, false) DEFINE_FIELD_M(NX_IPV6_DST, MFF_IPV6_DST, false) DEFINE_FIELD (NX_IPV6_LABEL, MFF_IPV6_LABEL,false) DEFINE_FIELD (NX_IP_ECN, MFF_IP_ECN, true) +DEFINE_FIELD (NX_IP_TTL, MFF_IP_TTL, true) /* XXX should we have MFF_ICMPV4_TYPE and MFF_ICMPV6_TYPE? */ DEFINE_FIELD (NX_ICMPV6_TYPE,MFF_ICMP_TYPE, false) DEFINE_FIELD (NX_ICMPV6_CODE,MFF_ICMP_CODE, false) diff --git a/lib/nx-match.h b/lib/nx-match.h index 6a33dbe4..0b9a437d 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -103,6 +103,7 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits) * NXM_OF_VLAN_TCI 4 2 2 8 * NXM_OF_IP_TOS 4 1 -- 5 * NXM_NX_IP_ECN 4 1 -- 5 + * NXM_OF_IP_TTL 4 1 -- 5 * NXM_NX_IP_FRAG 4 1 1 8 * NXM_OF_IP_PROTO 4 2 -- 6 * NXM_OF_IPV6_SRC_W 4 16 16 36 @@ -119,7 +120,7 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits) * NXM_NX_REG_W(4) 4 4 4 12 * NXM_NX_TUN_ID_W 4 8 8 20 * ------------------------------------------- - * total 270 + * total 275 * * So this value is conservative. */ diff --git a/lib/odp-util.c b/lib/odp-util.c index aa4931d4..c4046d71 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -369,11 +369,12 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) case OVS_KEY_ATTR_IPV4: ipv4_key = nl_attr_get(a); - ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT"," - "proto=%"PRId8",tos=%#"PRIx8",frag=%s)", + ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRId8 + ",tos=%#"PRIx8",ttl=%"PRIu8",frag=%s)", IP_ARGS(&ipv4_key->ipv4_src), IP_ARGS(&ipv4_key->ipv4_dst), ipv4_key->ipv4_proto, ipv4_key->ipv4_tos, + ipv4_key->ipv4_ttl, ovs_frag_type_to_string(ipv4_key->ipv4_frag)); break; @@ -386,9 +387,10 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds) inet_ntop(AF_INET6, ipv6_key->ipv6_dst, dst_str, sizeof dst_str); ds_put_format(ds, "ipv6(src=%s,dst=%s,label=%#"PRIx32",proto=%"PRId8 - ",tos=%#"PRIx8",frag=%s)", + ",tos=%#"PRIx8",hlimit=%"PRIu8",frag=%s)", src_str, dst_str, ntohl(ipv6_key->ipv6_label), ipv6_key->ipv6_proto, ipv6_key->ipv6_tos, + ipv6_key->ipv6_hlimit, ovs_frag_type_to_string(ipv6_key->ipv6_frag)); break; } @@ -606,23 +608,24 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) ovs_be32 ipv4_dst; int ipv4_proto; int ipv4_tos; + int ipv4_ttl; char frag[8]; enum ovs_frag_type ipv4_frag; int n = -1; if (sscanf(s, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT"," - "proto=%i,tos=%i,frag=%7[a-z])%n", + "proto=%i,tos=%i,ttl=%i,frag=%7[a-z])%n", IP_SCAN_ARGS(&ipv4_src), IP_SCAN_ARGS(&ipv4_dst), - &ipv4_proto, &ipv4_tos, frag, &n) > 0 + &ipv4_proto, &ipv4_tos, &ipv4_ttl, frag, &n) > 0 && n > 0 && ovs_frag_type_from_string(frag, &ipv4_frag)) { struct ovs_key_ipv4 ipv4_key; - memset(&ipv4_key, 0, sizeof ipv4_key); ipv4_key.ipv4_src = ipv4_src; ipv4_key.ipv4_dst = ipv4_dst; ipv4_key.ipv4_proto = ipv4_proto; ipv4_key.ipv4_tos = ipv4_tos; + ipv4_key.ipv4_ttl = ipv4_ttl; ipv4_key.ipv4_frag = ipv4_frag; nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof ipv4_key); @@ -636,19 +639,19 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) int ipv6_label; int ipv6_proto; int ipv6_tos; + int ipv6_hlimit; char frag[8]; enum ovs_frag_type ipv6_frag; int n = -1; if (sscanf(s, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT"," - "label=%i,proto=%i,tos=%i,frag=%7[a-z])%n", + "label=%i,proto=%i,tos=%i,hlimit=%i,frag=%7[a-z])%n", ipv6_src_s, ipv6_dst_s, &ipv6_label, - &ipv6_proto, &ipv6_tos, frag, &n) > 0 + &ipv6_proto, &ipv6_tos, &ipv6_hlimit, frag, &n) > 0 && n > 0 && ovs_frag_type_from_string(frag, &ipv6_frag)) { struct ovs_key_ipv6 ipv6_key; - memset(&ipv6_key, 0, sizeof ipv6_key); if (inet_pton(AF_INET6, ipv6_src_s, &ipv6_key.ipv6_src) != 1 || inet_pton(AF_INET6, ipv6_dst_s, &ipv6_key.ipv6_dst) != 1) { return -EINVAL; @@ -656,6 +659,7 @@ parse_odp_key_attr(const char *s, struct ofpbuf *key) ipv6_key.ipv6_label = htonl(ipv6_label); ipv6_key.ipv6_proto = ipv6_proto; ipv6_key.ipv6_tos = ipv6_tos; + ipv6_key.ipv6_hlimit = ipv6_hlimit; ipv6_key.ipv6_frag = ipv6_frag; nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV6, &ipv6_key, sizeof ipv6_key); @@ -872,23 +876,23 @@ odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow) ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4, sizeof *ipv4_key); - memset(ipv4_key, 0, sizeof *ipv4_key); ipv4_key->ipv4_src = flow->nw_src; ipv4_key->ipv4_dst = flow->nw_dst; ipv4_key->ipv4_proto = flow->nw_proto; ipv4_key->ipv4_tos = flow->tos; + ipv4_key->ipv4_ttl = flow->nw_ttl; ipv4_key->ipv4_frag = ovs_to_odp_frag(flow->frag); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { struct ovs_key_ipv6 *ipv6_key; ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6, sizeof *ipv6_key); - memset(ipv6_key, 0, sizeof *ipv6_key); memcpy(ipv6_key->ipv6_src, &flow->ipv6_src, sizeof ipv6_key->ipv6_src); memcpy(ipv6_key->ipv6_dst, &flow->ipv6_dst, sizeof ipv6_key->ipv6_dst); ipv6_key->ipv6_label = flow->ipv6_label; ipv6_key->ipv6_proto = flow->nw_proto; ipv6_key->ipv6_tos = flow->tos; + ipv6_key->ipv6_hlimit = flow->nw_ttl; ipv6_key->ipv6_frag = ovs_to_odp_frag(flow->frag); } else if (flow->dl_type == htons(ETH_TYPE_ARP)) { struct ovs_key_arp *arp_key; @@ -1061,6 +1065,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, flow->nw_dst = ipv4_key->ipv4_dst; flow->nw_proto = ipv4_key->ipv4_proto; flow->tos = ipv4_key->ipv4_tos; + flow->nw_ttl = ipv4_key->ipv4_ttl; if (!odp_to_ovs_frag(ipv4_key->ipv4_frag, flow)) { return EINVAL; } @@ -1076,6 +1081,7 @@ odp_flow_key_to_flow(const struct nlattr *key, size_t key_len, flow->ipv6_label = ipv6_key->ipv6_label; flow->nw_proto = ipv6_key->ipv6_proto; flow->tos = ipv6_key->ipv6_tos; + flow->nw_ttl = ipv6_key->ipv6_hlimit; if (!odp_to_ovs_frag(ipv6_key->ipv6_frag, flow)) { return EINVAL; } diff --git a/lib/odp-util.h b/lib/odp-util.h index 73c3362a..0f4f9e6b 100644 --- a/lib/odp-util.h +++ b/lib/odp-util.h @@ -72,7 +72,7 @@ void format_odp_actions(struct ds *, const struct nlattr *odp_actions, * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_8021Q 4 -- 4 8 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 - * OVS_KEY_ATTR_IPV6 39 1 4 44 + * OVS_KEY_ATTR_IPV6 40 -- 4 44 * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ------------------------------------------------- diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 0146a333..40215511 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -360,6 +360,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, case OFPUTIL_NXAST_LEARN: learn_parse(b, arg, flow); break; + case OFPUTIL_NXAST_EXIT: ofputil_put_NXAST_EXIT(b); break; diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 1b162799..fac21af7 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -101,15 +101,15 @@ static const flow_wildcards_t WC_INVARIANTS = 0 void ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6); /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS; /* Wildcard fields that aren't defined by ofp_match or tun_id. */ - wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_ND_TARGET - | FWW_IPV6_LABEL); + wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_TTL + | FWW_ND_TARGET | FWW_IPV6_LABEL); if (!(ofpfw & OFPFW_NW_TOS)) { wc->tos_mask |= IP_DSCP_MASK; @@ -859,7 +859,7 @@ ofputil_min_flow_format(const struct cls_rule *rule) { const struct flow_wildcards *wc = &rule->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 5); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 6); /* Only NXM supports separately wildcards the Ethernet multicast bit. */ if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) { @@ -902,6 +902,11 @@ ofputil_min_flow_format(const struct cls_rule *rule) return NXFF_NXM; } + /* Only NXM supports matching IP TTL/hop limit. */ + if (!(wc->wildcards & FWW_NW_TTL)) { + return NXFF_NXM; + } + /* Other formats can express this rule. */ return NXFF_OPENFLOW10; } @@ -2516,7 +2521,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */ MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */ MAY_NW_PROTO = 1 << 2, /* nw_proto */ - MAY_IPVx = 1 << 3, /* tos, frag */ + MAY_IPVx = 1 << 3, /* tos, frag, ttl */ MAY_ARP_SHA = 1 << 4, /* arp_sha */ MAY_ARP_THA = 1 << 5, /* arp_tha */ MAY_IPV6_ADDR = 1 << 6, /* ipv6_src, ipv6_dst */ @@ -2570,6 +2575,7 @@ ofputil_normalize_rule(struct cls_rule *rule, enum nx_flow_format flow_format) if (!(may_match & MAY_IPVx)) { wc.tos_mask = 0; wc.frag_mask = 0; + wc.wildcards |= FWW_NW_TTL; } if (!(may_match & MAY_ARP_SHA)) { wc.wildcards |= FWW_ARP_SHA; diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index b39eaa15..f6eee59a 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -3671,16 +3671,16 @@ commit_set_nw_action(const struct flow *flow, struct flow *base, if (base->nw_src == flow->nw_src && base->nw_dst == flow->nw_dst && base->tos == flow->tos && + base->nw_ttl == flow->nw_ttl && base->frag == flow->frag) { return; } - - memset(&ipv4_key, 0, sizeof(ipv4_key)); ipv4_key.ipv4_src = base->nw_src = flow->nw_src; ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst; ipv4_key.ipv4_proto = base->nw_proto; ipv4_key.ipv4_tos = flow->tos; + ipv4_key.ipv4_ttl = flow->nw_ttl; ipv4_key.ipv4_frag = (base->frag == 0 ? OVS_FRAG_TYPE_NONE : base->frag == FLOW_FRAG_ANY ? OVS_FRAG_TYPE_FIRST : OVS_FRAG_TYPE_LATER); diff --git a/tests/odp.at b/tests/odp.at index e2e76d0d..5f300d43 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -4,24 +4,24 @@ AT_SETUP([OVS datapath parsing and formatting - valid forms]) AT_DATA([odp-base.txt], [dnl in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=no) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x81,frag=no) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=first) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,frag=later) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,frag=no),tcp(src=80,dst=8080) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,frag=no),udp(src=81,dst=6632) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,frag=no),icmp(type=1,code=2) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=no) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x71,frag=no) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=first) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,frag=later) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tos=0,frag=no),tcp(src=80,dst=8080) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tos=0,frag=no),udp(src=6630,dst=22) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=1,code=2) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=135,code=0),nd(target=::3) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=135,code=0),nd(target=::3,sll=00:05:06:07:08:09) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e) -in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=no) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x81,ttl=128,frag=no) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=first) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=5,tos=0x80,ttl=128,frag=later) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80,dst=8080) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81,dst=6632) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1,code=2) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=no) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x71,hlimit=128,frag=no) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=first) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=10,tos=0x70,hlimit=128,frag=later) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tos=0,hlimit=128,frag=no),tcp(src=80,dst=8080) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=17,tos=0,hlimit=128,frag=no),udp(src=6630,dst=22) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=1,code=2) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=135,code=0),nd(target=::3,sll=00:05:06:07:08:09) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,tll=00:0a:0b:0c:0d:0e) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tos=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e) in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18) ]) diff --git a/tests/ofp-print.at b/tests/ofp-print.at index d8e2f353..df5dc9bf 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -237,7 +237,7 @@ dnl The tcpdump output format differs slightly from one version to another, dnl so trim off the end of the line where differences appear. AT_CHECK([sed 's/\(length 60:\).*/\1 .../' stdout], [0], [dnl OFPT_PACKET_IN (xid=0x0): total_len=60 in_port=3 data_len=60 buffer=0x00000111 -priority0:tunnel0:in_port0003:tci(0) mac50:54:00:00:00:05->50:54:00:00:00:06 type0800 proto6 tos0 ip192.168.0.1->192.168.0.2 port10031->0 +priority0:tunnel0:in_port0003:tci(0) mac50:54:00:00:00:05->50:54:00:00:00:06 type0800 proto6 tos0 ttl64 ip192.168.0.1->192.168.0.2 port10031->0 50:54:00:00:00:05 > 50:54:00:00:00:06, ethertype IPv4 (0x0800), length 60: ... ]) AT_CLEANUP diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index d08097b6..e6c2f922 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -11,7 +11,7 @@ table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2) table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2) ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21 ]) @@ -36,7 +36,7 @@ in_port=10,reg1=0xdeadbeef actions=output:21 in_port=11,reg2=0xeef22dea actions=output:22 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 20,21,22 ]) @@ -55,7 +55,7 @@ in_port=6 actions=output:NXM_NX_REG0[[0..15]],output:NXM_NX_REG0[[16..31]] in_port=7 actions=load:0x110000ff->NXM_NX_REG0[[]],output:NXM_NX_REG0[[]] ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 9,55,10,55,66,11,77,88 ]) @@ -73,7 +73,7 @@ in_port=4 actions=set_tunnel:4,set_tunnel:3,output:4 in_port=5 actions=set_tunnel:5 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4 ]) @@ -239,7 +239,7 @@ priority=50 tcp ip_frag=later actions=output:6 ]) AT_CHECK([ovs-ofctl replace-flows br0 flows.txt]) -base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0" +base_flow="in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=128" no_flow="$base_flow,frag=no),tcp(src=12345,dst=80)" first_flow="$base_flow,frag=first),tcp(src=12345,dst=80)" later_flow="$base_flow,frag=later)" @@ -275,15 +275,15 @@ in_port=2 actions=output:12,resubmit:1,output:12 in_port=3 actions=output:13,resubmit:2,output:14 ]) AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 10 ]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 12,10 ]) -AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,frag=no),icmp(type=8,code=0)'], [0], [stdout]) +AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(3),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout]) AT_CHECK([tail -1 stdout], [0], [Datapath actions: 13,12,10 ]) diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at index 39093a80..2a458c8d 100644 --- a/tests/ovs-ofctl.at +++ b/tests/ovs-ofctl.at @@ -224,6 +224,11 @@ NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(01) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(05) NXM_OF_IP_PROTO(05) +# IP TTL +NXM_OF_ETH_TYPE(0800) NXM_NX_IP_TTL(80) +NXM_OF_ETH_TYPE(86dd) NXM_NX_IP_TTL(ff) +NXM_NX_IP_TTL(80) + # IP source NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC(ac100014) NXM_OF_ETH_TYPE(0800) NXM_OF_IP_SRC_W(C0a80000/FFFF0000) @@ -408,6 +413,11 @@ NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(01) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(05) nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ) +# IP TTL +NXM_OF_ETH_TYPE(0800), NXM_NX_IP_TTL(80) +NXM_OF_ETH_TYPE(86dd), NXM_NX_IP_TTL(ff) +nx_pull_match() returned error 44010104 (type OFPET_BAD_REQUEST, code NXBRC_NXM_BAD_PREREQ) + # IP source NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC(ac100014) NXM_OF_ETH_TYPE(0800), NXM_OF_IP_SRC_W(c0a80000/ffff0000) diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in index 30f9c585..cd8d4f20 100644 --- a/utilities/ovs-ofctl.8.in +++ b/utilities/ovs-ofctl.8.in @@ -416,6 +416,15 @@ When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or 0x86dd, the value of \fBnw_ecn\fR is ignored (see \fBFlow Syntax\fR above). . +.IP \fBnw_ttl=\fIttl\fR +Matches IP TTL or IPv6 hop limit value \fIttl\fR, which is +specified as a decimal number between 0 and 255, inclusive. +.IP +When \fBdl_type\fR is wildcarded or set to a value other than 0x0800 or +0x86dd, the value of \fBnw_ttl\fR is ignored (see \fBFlow Syntax\fR +above). +.IP +. .IP \fBtp_src=\fIport\fR .IQ \fBtp_dst=\fIport\fR When \fBdl_type\fR and \fBnw_proto\fR specify TCP or UDP, \fBtp_src\fR |