summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarno Rajahalme <jrajahalme@nicira.com>2014-11-05 10:10:13 -0800
committerJarno Rajahalme <jrajahalme@nicira.com>2014-11-10 13:40:03 -0800
commitb8778a0d0bf749536ddeedba00d979bb557f0dfd (patch)
tree7d8e380bf014c3f0a6705fb461d2a9b0ba8c3e16
parentfa8d9001a624d87413ca5e2baa08c22bd553cf10 (diff)
downloadopenvswitch-b8778a0d0bf749536ddeedba00d979bb557f0dfd.tar.gz
Fix setting transport ports with frags.
Packets with 'LATER' fragment do not have a transport header, so it is not possible to either match on or set transport ports on such packets. Matching is prevented by augmenting mf_are_prereqs_ok() with a nw_frag 'LATER' bit check. Setting the transport headers on such packets is prevented in three ways: 1. Flows with an explicit match on nw_frag, where the LATER bit is 1: existing calls to the modified mf_are_prereqs_ok() prohibit using transport header fields (port numbers) in OXM/NXM actions (set_field, move). SET_TP_* actions need a new check on the LATER bit. 2. Flows that wildcard the nw_frag LATER bit: At flow translation time, add calls to mf_are_prereqs_ok() to make sure that we do not use transport ports in flows that do not have them. 3. At action execution time, do not set transport ports, if the packet does not have a full transport header. This ensures that we never call the packet_set functions, that require a valid transport header, with packets that do not have them. For example, if the flow was created with a IPv6 first fragment that had the full TCP header, but the next packet's first fragment is missing them. 3 alone would suffice for correct behavior, but 1 and 2 seem like a right thing to do, anyway. Currently, if we are setting port numbers, we will also match them, due to us tracking the set fields with the same flow_wildcards as the matched fields. Hence, if the incoming port number was not zero, the flow would not match any packets with missing or truncated transport headers. However, relying on no packets having zero port numbers would not be very robust. Also, we may separate the tracking of set and matched fields in the future, which would allow some flows that blindly set port numbers to not match on them at all. For TCP in case 3 we use ofpbuf_get_tcp_payload() that requires the whole (potentially variable size) TCP header to be present. However, when parsing a flow, we only require the fixed size portion of the TCP header to be present, which would be enough to set the port numbers and fix the TCP checksum. Finally, we add tests testing the new behavior. Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
-rw-r--r--lib/meta-flow.c10
-rw-r--r--lib/nx-match.c20
-rw-r--r--lib/odp-execute.c57
-rw-r--r--lib/ofp-actions.c19
-rw-r--r--ofproto/ofproto-dpif-xlate.c13
-rw-r--r--ofproto/ofproto-dpif.c2
-rw-r--r--tests/ofproto-dpif.at180
7 files changed, 245 insertions, 56 deletions
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index ddf043186..434233717 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -274,11 +274,14 @@ mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
return is_ip_any(flow);
case MFP_TCP:
- return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP;
+ return is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER);
case MFP_UDP:
- return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP;
+ return is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER);
case MFP_SCTP:
- return is_ip_any(flow) && flow->nw_proto == IPPROTO_SCTP;
+ return is_ip_any(flow) && flow->nw_proto == IPPROTO_SCTP
+ && !(flow->nw_frag & FLOW_NW_FRAG_LATER);
case MFP_ICMPV4:
return is_icmpv4(flow);
case MFP_ICMPV6:
@@ -324,6 +327,7 @@ mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow *mask)
case MFP_SCTP:
case MFP_ICMPV4:
case MFP_ICMPV6:
+ /* nw_frag always unwildcarded. */
mask->nw_proto = 0xff;
/* Fall through. */
case MFP_ARP:
diff --git a/lib/nx-match.c b/lib/nx-match.c
index e70eeac65..bc6682d19 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1357,7 +1357,7 @@ nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
return error;
}
- return mf_check_dst(&move->dst, NULL);
+ return mf_check_dst(&move->dst, flow);
}
/* nxm_execute_reg_move(). */
@@ -1372,12 +1372,18 @@ nxm_execute_reg_move(const struct ofpact_reg_move *move,
mf_mask_field_and_prereqs(move->dst.field, &wc->masks);
mf_mask_field_and_prereqs(move->src.field, &wc->masks);
- mf_get_value(move->dst.field, flow, &dst_value);
- mf_get_value(move->src.field, flow, &src_value);
- bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
- &dst_value, move->dst.field->n_bytes, move->dst.ofs,
- move->src.n_bits);
- mf_set_flow_value(move->dst.field, &dst_value, flow);
+ /* A flow may wildcard nw_frag. Do nothing if setting a transport
+ * header field on a packet that does not have them. */
+ if (mf_are_prereqs_ok(move->dst.field, flow)
+ && mf_are_prereqs_ok(move->src.field, flow)) {
+
+ mf_get_value(move->dst.field, flow, &dst_value);
+ mf_get_value(move->src.field, flow, &src_value);
+ bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
+ &dst_value, move->dst.field->n_bytes, move->dst.ofs,
+ move->src.n_bits);
+ mf_set_flow_value(move->dst.field, &dst_value, flow);
+ }
}
void
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 230e6e1b6..1406fd810 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -112,9 +112,11 @@ odp_set_tcp(struct ofpbuf *packet, const struct ovs_key_tcp *key,
{
struct tcp_header *th = ofpbuf_l4(packet);
- packet_set_tcp_port(packet,
- key->tcp_src | (th->tcp_src & ~mask->tcp_src),
- key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst));
+ if (OVS_LIKELY(th && ofpbuf_get_tcp_payload(packet))) {
+ packet_set_tcp_port(packet,
+ key->tcp_src | (th->tcp_src & ~mask->tcp_src),
+ key->tcp_dst | (th->tcp_dst & ~mask->tcp_dst));
+ }
}
static void
@@ -123,9 +125,11 @@ odp_set_udp(struct ofpbuf *packet, const struct ovs_key_udp *key,
{
struct udp_header *uh = ofpbuf_l4(packet);
- packet_set_udp_port(packet,
- key->udp_src | (uh->udp_src & ~mask->udp_src),
- key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
+ if (OVS_LIKELY(uh && ofpbuf_get_udp_payload(packet))) {
+ packet_set_udp_port(packet,
+ key->udp_src | (uh->udp_src & ~mask->udp_src),
+ key->udp_dst | (uh->udp_dst & ~mask->udp_dst));
+ }
}
static void
@@ -134,9 +138,11 @@ odp_set_sctp(struct ofpbuf *packet, const struct ovs_key_sctp *key,
{
struct sctp_header *sh = ofpbuf_l4(packet);
- packet_set_sctp_port(packet,
- key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
- key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
+ if (OVS_LIKELY(sh && ofpbuf_get_sctp_payload(packet))) {
+ packet_set_sctp_port(packet,
+ key->sctp_src | (sh->sctp_src & ~mask->sctp_src),
+ key->sctp_dst | (sh->sctp_dst & ~mask->sctp_dst));
+ }
}
static void
@@ -180,9 +186,6 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
enum ovs_key_attr type = nl_attr_type(a);
const struct ovs_key_ipv4 *ipv4_key;
const struct ovs_key_ipv6 *ipv6_key;
- const struct ovs_key_tcp *tcp_key;
- const struct ovs_key_udp *udp_key;
- const struct ovs_key_sctp *sctp_key;
struct pkt_metadata *md = &packet->md;
switch (type) {
@@ -218,21 +221,33 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a)
break;
case OVS_KEY_ATTR_TCP:
- tcp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
- packet_set_tcp_port(&packet->ofpbuf, tcp_key->tcp_src,
- tcp_key->tcp_dst);
+ if (OVS_LIKELY(ofpbuf_get_tcp_payload(&packet->ofpbuf))) {
+ const struct ovs_key_tcp *tcp_key
+ = nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp));
+
+ packet_set_tcp_port(&packet->ofpbuf, tcp_key->tcp_src,
+ tcp_key->tcp_dst);
+ }
break;
case OVS_KEY_ATTR_UDP:
- udp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
- packet_set_udp_port(&packet->ofpbuf, udp_key->udp_src,
- udp_key->udp_dst);
+ if (OVS_LIKELY(ofpbuf_get_udp_payload(&packet->ofpbuf))) {
+ const struct ovs_key_udp *udp_key
+ = nl_attr_get_unspec(a, sizeof(struct ovs_key_udp));
+
+ packet_set_udp_port(&packet->ofpbuf, udp_key->udp_src,
+ udp_key->udp_dst);
+ }
break;
case OVS_KEY_ATTR_SCTP:
- sctp_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
- packet_set_sctp_port(&packet->ofpbuf, sctp_key->sctp_src,
- sctp_key->sctp_dst);
+ if (OVS_LIKELY(ofpbuf_get_sctp_payload(&packet->ofpbuf))) {
+ const struct ovs_key_sctp *sctp_key
+ = nl_attr_get_unspec(a, sizeof(struct ovs_key_sctp));
+
+ packet_set_sctp_port(&packet->ofpbuf, sctp_key->sctp_src,
+ sctp_key->sctp_dst);
+ }
break;
case OVS_KEY_ATTR_MPLS:
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 41c76226d..33b419d75 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -5333,19 +5333,8 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
return 0;
case OFPACT_SET_L4_SRC_PORT:
- if (!is_ip_any(flow) ||
- (flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP
- && flow->nw_proto != IPPROTO_SCTP)) {
- inconsistent_match(usable_protocols);
- }
- /* Note on which transport protocol the port numbers are set.
- * This allows this set action to be converted to an OF1.2 set field
- * action. */
- ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto;
- return 0;
-
case OFPACT_SET_L4_DST_PORT:
- if (!is_ip_any(flow) ||
+ if (!is_ip_any(flow) || (flow->nw_frag & FLOW_NW_FRAG_LATER) ||
(flow->nw_proto != IPPROTO_TCP && flow->nw_proto != IPPROTO_UDP
&& flow->nw_proto != IPPROTO_SCTP)) {
inconsistent_match(usable_protocols);
@@ -5353,7 +5342,11 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
/* Note on which transport protocol the port numbers are set.
* This allows this set action to be converted to an OF1.2 set field
* action. */
- ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto;
+ if (a->type == OFPACT_SET_L4_SRC_PORT) {
+ ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto;
+ } else {
+ ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto;
+ }
return 0;
case OFPACT_REG_MOVE:
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index ba002ba1a..9a21f0554 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3725,7 +3725,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
break;
case OFPACT_SET_L4_SRC_PORT:
- if (is_ip_any(flow)) {
+ if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
@@ -3733,7 +3733,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
break;
case OFPACT_SET_L4_DST_PORT:
- if (is_ip_any(flow)) {
+ if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
@@ -3780,10 +3780,13 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
&& !eth_type_mpls(flow->dl_type)) {
break;
}
-
+ /* A flow may wildcard nw_frag. Do nothing if setting a trasport
+ * header field on a packet that does not have them. */
mf_mask_field_and_prereqs(mf, &wc->masks);
- mf_set_flow_value_masked(mf, &set_field->value, &set_field->mask,
- flow);
+ if (mf_are_prereqs_ok(mf, flow)) {
+ mf_set_flow_value_masked(mf, &set_field->value,
+ &set_field->mask, flow);
+ }
break;
case OFPACT_STACK_PUSH:
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 40401ef36..9c6d386de 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4643,7 +4643,7 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
/* Do the same checks as handle_packet_out() in ofproto.c.
*
- * We pass a 'table_id' of 0 to ofproto_check_ofpacts(), which isn't
+ * We pass a 'table_id' of 0 to ofpacts_check(), which isn't
* strictly correct because these actions aren't in any table, but it's OK
* because it 'table_id' is used only to check goto_table instructions, but
* packet-outs take a list of actions and therefore it can't include
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 3a3392625..9d8efdc5a 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3324,16 +3324,15 @@ OFPST_FLOW reply (OF1.2):
OVS_VSWITCHD_STOP
AT_CLEANUP
-AT_SETUP([ofproto-dpif - fragment handling])
+AT_SETUP([ofproto-dpif - fragment handling - trace])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
AT_DATA([flows.txt], [dnl
priority=75 tcp ip_frag=no tp_dst=80 actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:1
priority=75 tcp ip_frag=first tp_dst=80 actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:2
-priority=75 tcp ip_frag=later tp_dst=80 actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:3
priority=50 tcp ip_frag=no actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:4
priority=50 tcp ip_frag=first actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:5
-priority=50 tcp ip_frag=later actions=move:OXM_OF_TCP_DST[[]]->OXM_OF_TCP_SRC[[]],output:6
+priority=50 tcp ip_frag=later actions=output:6
])
AT_CHECK([ovs-ofctl replace-flows br0 flows.txt])
@@ -3363,9 +3362,7 @@ do
if test $mode = drop && test $type != no; then
echo 'Packets dropped because they are IP fragments and the fragment handling mode is "drop".' >> expout
echo "Datapath actions: $exp_output" >> expout
- elif test $mode = normal && test $type = later; then
- echo "Datapath actions: $exp_output" >> expout
- elif test $mode = nx-match && test $type = later; then
+ elif test $type = later; then
echo "Datapath actions: $exp_output" >> expout
else
echo "Datapath actions: set(tcp(src=80,dst=80)),$exp_output" >> expout
@@ -3376,6 +3373,177 @@ done
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - fragment handling - upcall])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
+AT_DATA([flows.txt], [dnl
+priority=75 tcp ip_frag=no tp_dst=80 actions=set_field:81->tcp_dst,output:1
+priority=75 tcp ip_frag=first tp_dst=80 actions=set_field:81->tcp_dst,output:2
+priority=50 tcp ip_frag=no actions=set_field:81->tcp_dst,output:4
+priority=50 tcp ip_frag=first actions=set_field:81->tcp_dst,output:5
+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,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)"
+
+AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg])
+
+mode=normal
+
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for type in no first later; do
+ eval flow=\$${type}_flow
+ printf "\n%s\n" "----$mode $type-----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),5
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, bytes:0, used:never, actions:6
+])
+
+mode=drop
+
+AT_CHECK([ovs-appctl dpctl/del-flows], [0])
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for type in no first later; do
+ eval flow=\$${type}_flow
+ printf "\n%s\n" "----$mode $type-----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(frag=first), packets:0, bytes:0, used:never, actions:drop
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(frag=later), packets:0, bytes:0, used:never, actions:drop
+])
+
+mode=nx-match
+
+AT_CHECK([ovs-appctl dpctl/del-flows], [0])
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for type in no first later; do
+ eval flow=\$${type}_flow
+ printf "\n%s\n" "----$mode $type-----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "$flow"], [0], [stdout])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(dst=80), packets:0, bytes:0, used:never, actions:set(tcp(dst=81)),2
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, bytes:0, used:never, actions:6
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - fragment handling - actions])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [5], [6], [90])
+
+AT_CHECK([ovs-ofctl add-flow br0 "tcp,ip_frag=later actions=move:OXM_OF_TCP_DST[[0..7]]->OXM_OF_TCP_SRC[[0..7]],output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+source field tcp_dst lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl -O OpenFlow15 add-flow br0 "tcp,ip_frag=later actions=move:OXM_OF_PKT_REG0[[0..7]]->OXM_OF_TCP_SRC[[0..7]],output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+destination field tcp_src lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "udp,ip_frag=later actions=set_field:8888->udp_src,output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+set_field udp_src lacks correct prerequisities
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "udp,ip_frag=later actions=load:8888->NXM_OF_UDP_DST[[]],output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+set_field udp_dst lacks correct prerequisities
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "sctp,ip_frag=later actions=set_field:8888->sctp_src,output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+set_field sctp_src lacks correct prerequisities
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "sctp,ip_frag=later actions=set_field:8888->sctp_dst,output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+set_field sctp_dst lacks correct prerequisities
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 "tcp,ip_frag=later actions=learn(table=1,hard_timeout=60,eth_type=0x800,nw_proto=6,NXM_OF_IP_SRC[[]]=NXM_OF_IP_DST[[]],NXM_OF_TCP_SRC[[]]=NXM_OF_TCP_DST[[]],output:NXM_NX_REG0[[0..15]]),output:1"], [1], [], [stderr])
+AT_CHECK([tail -2 stderr | sed 's/^.*|WARN|//'], [0], [dnl
+source field tcp_dst lacks correct prerequisites
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_DATA([flows.txt], [dnl
+priority=75 tcp actions=load:42->OXM_OF_TCP_SRC[[0..7]],output:1
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 replace-flows br0 flows.txt])
+
+AT_CHECK([ovs-appctl vlog/set dpif:dbg dpif_netdev:dbg])
+
+mode=normal
+
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for frag in 4000 6000 6008 4010; do
+ printf "\n%s\n" "----$mode $frag-----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 003c 2e24 $frag 40 06 465d ac11370d ac11370b 828b 0016 751e267b 00000000 a002 16d0 1736 0000 02 04 05 b4 04 02 08 0a 2d 25 08 5f 00 00 00 00 01 03 03 07"])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=33419), packets:0, bytes:0, used:never, actions:set(tcp(src=33322)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=33419), packets:0, bytes:0, used:never, actions:set(tcp(src=33322)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:74, used:0.001s, actions:1
+])
+
+AT_CHECK([ovs-appctl dpctl/del-flows], [0])
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for frag in 4000 6000 6008 4010; do
+ printf "\n%s\n" "----$mode $frag truncated transport header -----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0018 2e24 $frag 40 06 465d ac11370d ac11370b 828b 0016"])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=0), packets:0, bytes:0, used:never, actions:set(tcp(src=42)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=0), packets:0, bytes:0, used:never, actions:set(tcp(src=42)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:60, used:0.001s, actions:1
+])
+
+AT_CHECK([ovs-appctl dpctl/del-flows], [0])
+AT_CHECK([ovs-ofctl set-frags br0 $mode])
+for frag in 4000 6000 6001 4002; do
+ printf "\n%s\n" "----$mode $frag missing transport header-----"
+
+ AT_CHECK([ovs-appctl netdev-dummy/receive p90 "0021853763af 0026b98cb0f9 0800 4500 0014 2e24 $frag 40 06 465d ac11370d ac11370b"])
+done
+
+AT_CHECK([ovs-appctl dpctl/dump-flows | sed 's/used:[[0-9]].[[0-9]]*s/used:0.001s/'], [0], [dnl
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=no),tcp(src=0), packets:0, bytes:0, used:never, actions:set(tcp(src=42)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=0), packets:0, bytes:0, used:never, actions:set(tcp(src=42)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:1, bytes:60, used:0.001s, actions:1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - exit])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [1], [2], [3], [10], [11], [12], [13], [14])