1. Flows with an explicit match on nw_frag, where the LATER bit is 1:
   Prohibit setting transport header fields (port numbers) with
   set_field or move, or using such a field as a source in a move.

2. Flows that wildcard the nw_frag LATER bit: At flow translation
   time, detect the fact that the packet/flow has no transport header,
   and (silently) do nothing when translating a set_field, set_tp_src,
   set_tp_dst, or reg_move action that reads or writes on transport
   headers.  nw_frag is exact matched, so non-LATER packets deal with
   the transport ports as before.

2. alone would suffice for correct behavior, but 1. seems like a right
thing to do, anyway.

Finally, we add tests testing the new behavior.

Signed-off-by: Jarno Rajahalme <jrajaha...@nicira.com>
---
 lib/meta-flow.c              |    9 +++
 lib/nx-match.c               |   14 +++-
 lib/ofp-actions.c            |   28 ++++----
 ofproto/ofproto-dpif-xlate.c |   12 +++-
 ofproto/ofproto-dpif.c       |    2 +-
 tests/ofproto-dpif.at        |  160 ++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 202 insertions(+), 23 deletions(-)

diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 7871545..345fa63 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -1531,6 +1531,15 @@ mf_check__(const struct mf_subfield *sf, const struct 
flow *flow,
         VLOG_WARN_RL(&rl, "%s field %s lacks correct prerequisites",
                      type, sf->field->name);
         return OFPERR_OFPBAC_MATCH_INCONSISTENT;
+    } else if (flow && (flow->nw_frag & FLOW_NW_FRAG_LATER) &&
+               (sf->field->id == MFF_TCP_SRC || sf->field->id == MFF_TCP_DST ||
+                sf->field->id == MFF_UDP_SRC || sf->field->id == MFF_UDP_DST ||
+                sf->field->id == MFF_SCTP_SRC ||
+                sf->field->id == MFF_SCTP_DST)) {
+        VLOG_WARN_RL(&rl,
+                     "transport layer field %s as a %s on a later fragment",
+                     sf->field->name, type);
+        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
     } else {
         return 0;
     }
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 82b472c..3a9a8af 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1378,7 +1378,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(). */
@@ -1390,6 +1390,18 @@ nxm_execute_reg_move(const struct ofpact_reg_move *move,
     union mf_value src_value;
     union mf_value dst_value;
 
+    /* A flow may wildcard nw_frag.  Do nothing if setting a transport
+     * header field on a packet that does not have them. */
+    if ((flow->nw_frag & FLOW_NW_FRAG_LATER) &&
+        (move->dst.field->id == MFF_TCP_SRC ||
+         move->dst.field->id == MFF_TCP_DST ||
+         move->dst.field->id == MFF_UDP_SRC ||
+         move->dst.field->id == MFF_UDP_DST ||
+         move->dst.field->id == MFF_SCTP_SRC ||
+         move->dst.field->id == MFF_SCTP_DST)) {
+        return;
+    }
+
     mf_mask_field_and_prereqs(move->dst.field, &wc->masks);
     mf_mask_field_and_prereqs(move->src.field, &wc->masks);
 
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 41c7622..29f69a0 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:
@@ -5368,6 +5361,15 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, 
struct ofpact *a,
                          mf->name);
             return OFPERR_OFPBAC_MATCH_INCONSISTENT;
         }
+        /* Prohibit setting of transport ports on later fragments. */
+        if (flow->nw_frag & FLOW_NW_FRAG_LATER &&
+            (mf->id == MFF_TCP_SRC || mf->id == MFF_TCP_DST ||
+             mf->id == MFF_UDP_SRC || mf->id == MFF_UDP_DST ||
+             mf->id == MFF_SCTP_SRC || mf->id == MFF_SCTP_DST)) {
+            VLOG_WARN_RL(&rl, "set_field %s on a later fragment",
+                         mf->name);
+            return OFPERR_OFPBAC_MATCH_INCONSISTENT;
+        }
         /* Remember if we saw a vlan tag in the flow to aid translating to
          * OpenFlow 1.1 if need be. */
         ofpact_get_SET_FIELD(a)->flow_has_vlan =
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 12bfb9f..2bde8f6 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3747,7 +3747,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);
@@ -3755,7 +3755,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);
@@ -3802,6 +3802,14 @@ 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. */
+            if (flow->nw_frag & FLOW_NW_FRAG_LATER &&
+                (mf->id == MFF_TCP_SRC || mf->id == MFF_TCP_DST ||
+                 mf->id == MFF_UDP_SRC || mf->id == MFF_UDP_DST ||
+                 mf->id == MFF_SCTP_SRC || mf->id == MFF_SCTP_DST)) {
+                break;
+            }
 
             mf_mask_field_and_prereqs(mf, &wc->masks);
             mf_set_flow_value_masked(mf, &set_field->value, &set_field->mask,
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index d965d38..0544508 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 05bb0e3..345a901 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3284,16 +3284,16 @@ 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=75 tcp ip_frag=later tp_dst=80 actions=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])
 
@@ -3323,9 +3323,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
@@ -3336,6 +3334,156 @@ 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=75 tcp ip_frag=later tp_dst=80 actions=output:3
+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
+transport layer field tcp_dst as a source on a later fragment
+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
+transport layer field tcp_src as a destination on a later fragment
+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 on a later fragment
+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 on a later fragment
+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 on a later fragment
+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 on a later fragment
+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
+transport layer field tcp_dst as a source on a later fragment
+ovs-ofctl: actions are invalid with specified match (OFPBAC_MATCH_INCONSISTENT)
+])
+
+AT_DATA([flows.txt], [dnl
+priority=75 tcp 
actions=move:NXM_NX_REG0[[0..7]]->OXM_OF_TCP_SRC[[0..7]],output:1
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 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(src=12345),
 packets:0, bytes:0, used:never, actions:set(tcp(src=12288)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=first),tcp(src=12345),
 packets:0, bytes:0, used:never, actions:set(tcp(src=12288)),1
+recirc_id(0),in_port(90),eth_type(0x0800),ipv4(proto=6,frag=later), packets:0, 
bytes:0, used:never, 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])
-- 
1.7.10.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to