[ovs-dev,10/10] userspace: Introduce OF 1.5 packet-out

Message ID AM2PR07MB1042492F22DF51C1BB84C9BB8A190@AM2PR07MB1042.eurprd07.prod.outlook.com
State Deferred
Headers show

Commit Message

Zoltán Balogh April 18, 2017, 11:20 a.m.
Partly based on Jean Tourrilhes's work.

Add test cases for OF1.5 packet-out
Add negative test case for OF1.5 packet-out
Modify wildcarding and packet-out test printout.

Signed-off-by: Jean Tourrilhes <jt@labs.hpe.com>
Signed-off-by: Zoltan Balogh <zoltan.balogh@ericsson.com>
Co-authored-by: Jan Scheurich <jan.scheurich@ericsson.com>
---
 include/openflow/openflow-1.5.h |  17 +++++++
 include/openvswitch/ofp-msgs.h  |   7 ++-
 include/openvswitch/ofp-util.h  |   1 +
 lib/flow.c                      |  37 ++++++++------
 lib/ofp-parse.c                 |   2 +
 lib/ofp-print.c                 |   6 +--
 lib/ofp-util.c                  |  57 +++++++++++++++++++--
 ofproto/ofproto.c               |   8 +++
 tests/ofproto.at                | 108 ++++++++++++++++++++++++++++++++++++++++
 utilities/ovs-ofctl.c           |   1 +
 10 files changed, 218 insertions(+), 26 deletions(-)

Patch

diff --git a/include/openflow/openflow-1.5.h b/include/openflow/openflow-1.5.h
index 3649e6c..0f770d4 100644
--- a/include/openflow/openflow-1.5.h
+++ b/include/openflow/openflow-1.5.h
@@ -39,6 +39,23 @@ 
 
 #include <openflow/openflow-common.h>
 
+
+/* Send packet (controller -> datapath). */
+struct ofp15_packet_out {
+    ovs_be32 buffer_id;         /* ID assigned by datapath (OFP_NO_BUFFER (-1)
+                                   if none). */
+    ovs_be16 actions_len;       /* Size of action array in bytes. */
+    uint8_t  pad[2];            /* Align to 64 bits. */
+    /* struct ofp12_match match; */ /* Packet pipeline fields. Variable size. */
+    /* The variable size and padded match is followed by the list of actions. */
+    /* struct ofp_action_header actions[0]; */ /* Action list - 0 or more. */
+    /* The variable size action list is optionally followed by packet data.
+     * This data is only present and meaningful if buffer_id = -1. */
+    /* uint8_t data[0]; */      /* Packet data. The length is inferred
+                                   from the length field in the header. */
+};
+OFP_ASSERT(sizeof(struct ofp15_packet_out) == 8);
+
 /* Body for ofp15_multipart_request of type OFPMP_PORT_DESC. */
 struct ofp15_port_desc_request {
     ovs_be32 port_no;         /* All ports if OFPP_ANY. */
diff --git a/include/openvswitch/ofp-msgs.h b/include/openvswitch/ofp-msgs.h
index 34708f3..6dc0b60 100644
--- a/include/openvswitch/ofp-msgs.h
+++ b/include/openvswitch/ofp-msgs.h
@@ -177,8 +177,10 @@  enum ofpraw {
 
     /* OFPT 1.0 (13): struct ofp10_packet_out, uint8_t[]. */
     OFPRAW_OFPT10_PACKET_OUT,
-    /* OFPT 1.1+ (13): struct ofp11_packet_out, uint8_t[]. */
+    /* OFPT 1.1-1.4 (13): struct ofp11_packet_out, uint8_t[]. */
     OFPRAW_OFPT11_PACKET_OUT,
+    /* OFPT 1.5+ (13): struct ofp15_packet_out, uint8_t[]. */
+    OFPRAW_OFPT15_PACKET_OUT,
 
     /* OFPT 1.0 (14): struct ofp10_flow_mod, uint8_t[8][]. */
     OFPRAW_OFPT10_FLOW_MOD,
@@ -561,7 +563,8 @@  enum ofptype {
 
     /* Controller command messages. */
     OFPTYPE_PACKET_OUT,          /* OFPRAW_OFPT10_PACKET_OUT.
-                                  * OFPRAW_OFPT11_PACKET_OUT. */
+                                  * OFPRAW_OFPT11_PACKET_OUT.
+                                  * OFPRAW_OFPT15_PACKET_OUT. */
     OFPTYPE_FLOW_MOD,            /* OFPRAW_OFPT10_FLOW_MOD.
                                   * OFPRAW_OFPT11_FLOW_MOD.
                                   * OFPRAW_NXT_FLOW_MOD. */
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index f664055..430205f 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -526,6 +526,7 @@  struct ofputil_packet_out {
     size_t packet_len;          /* Length of packet data in bytes. */
     uint32_t buffer_id;         /* Buffer id or UINT32_MAX if no buffer. */
     ofp_port_t in_port;         /* Packet's input port. */
+    ovs_be32 packet_type;       /* Packet's packet type. */
     struct ofpact *ofpacts;     /* Actions. */
     size_t ofpacts_len;         /* Size of ofpacts in bytes. */
 };
diff --git a/lib/flow.c b/lib/flow.c
index bd0b36e..f50c73b 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1384,6 +1384,8 @@  void
 flow_wildcards_init_for_packet(struct flow_wildcards *wc,
                                const struct flow *flow)
 {
+    ovs_be16 dl_type = OVS_BE16_MAX;
+
     memset(&wc->masks, 0x0, sizeof wc->masks);
 
     /* Update this function whenever struct flow changes. */
@@ -1435,26 +1437,29 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
     WC_MASK_FIELD(wc, in_port);
 
     /* actset_output wildcarded. */
-
-    WC_MASK_FIELD(wc, dl_dst);
-    WC_MASK_FIELD(wc, dl_src);
-    WC_MASK_FIELD(wc, dl_type);
-
-    /* No need to set mask of inner VLANs that don't exist. */
-    for (int i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
-        /* Always show the first zero VLAN. */
-        WC_MASK_FIELD(wc, vlans[i]);
-        if (flow->vlans[i].tci == htons(0)) {
-            break;
+    if (flow->packet_type == htonl(PT_ETH)) {
+        WC_MASK_FIELD(wc, dl_dst);
+        WC_MASK_FIELD(wc, dl_src);
+        WC_MASK_FIELD(wc, dl_type);
+        /* No need to set mask of inner VLANs that don't exist. */
+        for (int i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
+            /* Always show the first zero VLAN. */
+            WC_MASK_FIELD(wc, vlans[i]);
+            if (flow->vlans[i].tci == htons(0)) {
+                break;
+            }
         }
+        dl_type = flow->dl_type;
+    } else {
+        dl_type = pt_ns_type_be(flow->packet_type);
     }
 
-    if (flow->dl_type == htons(ETH_TYPE_IP)) {
+    if (dl_type == htons(ETH_TYPE_IP)) {
         WC_MASK_FIELD(wc, nw_src);
         WC_MASK_FIELD(wc, nw_dst);
         WC_MASK_FIELD(wc, ct_nw_src);
         WC_MASK_FIELD(wc, ct_nw_dst);
-    } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+    } else if (dl_type == htons(ETH_TYPE_IPV6)) {
         WC_MASK_FIELD(wc, ipv6_src);
         WC_MASK_FIELD(wc, ipv6_dst);
         WC_MASK_FIELD(wc, ipv6_label);
@@ -1466,15 +1471,15 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
             WC_MASK_FIELD(wc, ct_ipv6_src);
             WC_MASK_FIELD(wc, ct_ipv6_dst);
         }
-    } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
-               flow->dl_type == htons(ETH_TYPE_RARP)) {
+    } else if (dl_type == htons(ETH_TYPE_ARP) ||
+               dl_type == htons(ETH_TYPE_RARP)) {
         WC_MASK_FIELD(wc, nw_src);
         WC_MASK_FIELD(wc, nw_dst);
         WC_MASK_FIELD(wc, nw_proto);
         WC_MASK_FIELD(wc, arp_sha);
         WC_MASK_FIELD(wc, arp_tha);
         return;
-    } else if (eth_type_mpls(flow->dl_type)) {
+    } else if (eth_type_mpls(dl_type)) {
         for (int i = 0; i < FLOW_MAX_MPLS_LABELS; i++) {
             WC_MASK_FIELD(wc, mpls_lse[i]);
             if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index bc7f7fa..ab9202f 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -652,6 +652,8 @@  parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
                               value);
                 goto out;
             }
+        } else if (!strcmp(name, "packet_type")) {
+            po->packet_type = htonl((uint32_t) strtoul(value, NULL, 0));
         } else if (!strcmp(name, "packet")) {
             const char *error_msg = eth_from_hex(value, &packet);
             if (error_msg) {
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 651a84b..41551bc 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -217,10 +217,9 @@  ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
     }
 
     if (verbosity > 0) {
-        /* Packet In can only carry Ethernet packets. */
         char *packet = ofp_packet_to_string(public->packet,
                                             public->packet_len,
-                                            htonl(PT_ETH));
+                                            public->flow_metadata.flow.packet_type);
         ds_put_cstr(string, packet);
         free(packet);
     }
@@ -256,9 +255,8 @@  ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
     if (po.buffer_id == UINT32_MAX) {
         ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
         if (verbosity > 0 && po.packet_len > 0) {
-            /* Packet Out can only carry Ethernet packets. */
             char *packet = ofp_packet_to_string(po.packet, po.packet_len,
-                                                htonl(PT_ETH));
+                                                po.packet_type);
             ds_put_char(string, '\n');
             ds_put_cstr(string, packet);
             free(packet);
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 758f905..75ee220 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -4204,11 +4204,37 @@  ofputil_decode_packet_out(struct ofputil_packet_out *po,
     enum ofpraw raw = ofpraw_pull_assert(&b);
 
     ofpbuf_clear(ofpacts);
-    if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+    if (raw == OFPRAW_OFPT15_PACKET_OUT) {
+        enum ofperr error;
+        const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+        struct match match;
+
+        /* Copy buffer_id from packet_out structure. */
+        po->buffer_id = ntohl(opo->buffer_id);
+
+        /* Expand OXM matches into match. */
+        error = oxm_pull_match_loose(&b, NULL, &match);
+        if (error) {
+            return error;
+        }
+
+        /* Pull actions. */
+        error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+                                              oh->version, NULL, NULL,
+                                              ofpacts);
+        if (error) {
+            return error;
+        }
+
+        /* Copy in_port and packet_type from match. */
+        po->in_port = match.flow.in_port.ofp_port;
+        po->packet_type = match.flow.packet_type;
+    } else if (raw == OFPRAW_OFPT11_PACKET_OUT) {
         enum ofperr error;
         const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
 
         po->buffer_id = ntohl(opo->buffer_id);
+        po->packet_type = htonl(PT_ETH);
         error = ofputil_port_from_ofp11(opo->in_port, &po->in_port);
         if (error) {
             return error;
@@ -4226,6 +4252,7 @@  ofputil_decode_packet_out(struct ofputil_packet_out *po,
 
         po->buffer_id = ntohl(opo->buffer_id);
         po->in_port = u16_to_ofp(ntohs(opo->in_port));
+        po->packet_type = htonl(PT_ETH);
 
         error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
                                               oh->version, NULL, NULL,
@@ -7059,9 +7086,7 @@  ofputil_encode_packet_out(const struct ofputil_packet_out *po,
     case OFP11_VERSION:
     case OFP12_VERSION:
     case OFP13_VERSION:
-    case OFP14_VERSION:
-    case OFP15_VERSION:
-    case OFP16_VERSION: {
+    case OFP14_VERSION: {
         struct ofp11_packet_out *opo;
         size_t len;
 
@@ -7076,6 +7101,30 @@  ofputil_encode_packet_out(const struct ofputil_packet_out *po,
         break;
     }
 
+    case OFP15_VERSION:
+    case OFP16_VERSION: {
+        struct ofp15_packet_out *opo;
+        struct match match;
+        size_t len;
+
+        memset((char *) &match, '\0', sizeof(match));
+        match.flow.in_port.ofp_port = po->in_port;
+        match.wc.masks.in_port.ofp_port = UINT32_MAX;
+        match.flow.packet_type = po->packet_type;
+        match.wc.masks.packet_type = OVS_BE32_MAX;
+
+        size += sizeof(struct match) * 2;
+
+        msg = ofpraw_alloc(OFPRAW_OFPT15_PACKET_OUT, ofp_version, size);
+        ofpbuf_put_zeros(msg, sizeof *opo);
+        oxm_put_match(msg, &match, ofputil_protocol_to_ofp_version(protocol));
+        len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
+                                           ofp_version);
+        opo = msg->msg;
+        opo->buffer_id = htonl(po->buffer_id);
+        opo->actions_len = htons(len);
+        break;
+    }
     default:
         OVS_NOT_REACHED();
     }
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 31e64b6..080d407 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3473,9 +3473,17 @@  ofproto_packet_out_init(struct ofproto *ofproto,
         return OFPERR_OFPBRC_BUFFER_UNKNOWN;
     }
 
+    /* NON-PTAP bridge should accept only Ethernet packet. */
+    if (!ofproto->packet_type_aware && po->packet_type != PT_ETH) {
+        return OFPERR_OFPBRC_BAD_PACKET;
+    }
+
     /* Ensure that the L3 header is 32-bit aligned. */
     opo->packet = dp_packet_clone_data_with_headroom(po->packet,
                                                      po->packet_len, 2);
+    /* Take the received packet_tpye as packet_type of the packet. */
+    opo->packet->packet_type = po->packet_type;
+
     /* Store struct flow. */
     opo->flow = xmalloc(sizeof *opo->flow);
     flow_extract(opo->packet, opo->flow);
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 5c0d076..0d7239f 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -4093,6 +4093,114 @@  OFPT_BARRIER_REPLY:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+dnl This test checks that 1.5 packet_out is properly encoded/decoded.
+AT_SETUP([ofproto - packet-out with set_field metadata (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a couple of set-field action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 CONTROLLER 'set_field:0xfafafafa5a5a5a5a->metadata, controller' '0001020304050010203040501234'])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.3): total_len=14 metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_BARRIER_REPLY (OF1.3):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that packet_type PT_ETH is properly encoded/decoded in 1.5 packet_out.
+AT_SETUP([ofproto - packet-out with set_field metadata with packet_type PT_ETH (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a couple of set-field action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=0001020304050010203040501234 packet_type=0x0 actions=set_field:0xfafafafa5a5a5a5a->metadata,controller"])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.3): total_len=14 metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=14 (unbuffered)
+vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
+OFPT_BARRIER_REPLY (OF1.3):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that packet_type PT_IPV4 is properly encoded/decoded in 1.5 packet_out.
+AT_SETUP([ofproto - packet-out with set_field metadata with packet_type PT_IPV4 on PTAP bridge (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+
+AT_CHECK([
+    ovs-vsctl set bridge br0 other-config:packet-type-aware=true
+], [0])
+
+# Start a monitor listening for packet-ins.
+AT_CHECK([ovs-ofctl -P standard -O OpenFlow13 monitor br0 --detach --no-chdir --pidfile])
+ovs-appctl -t ovs-ofctl ofctl/send 0409000c0123456700000080
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl ofctl/set-output-file monitor.log
+AT_CAPTURE_FILE([monitor.log])
+
+# Send a packet-out with a couple of set-field action to set some metadata, and forward to controller
+AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=4500002012344000ff1155670a0000140a00001e006400c8000cea78ffffffff packet_type=0x10800 actions=set_field:0xfafafafa5a5a5a5a->metadata,controller"])
+
+# Stop the monitor and check its output.
+ovs-appctl -t ovs-ofctl ofctl/barrier
+ovs-appctl -t ovs-ofctl exit
+
+AT_CHECK([sed 's/ (xid=0x[[0-9a-fA-F]]*)//' monitor.log], [0], [dnl
+OFPT_PACKET_IN (OF1.3): total_len=32 packet_type=(1,0x800),metadata=0xfafafafa5a5a5a5a,in_port=CONTROLLER (via action) data_len=32 (unbuffered)
+nw_src=10.0.0.20,nw_dst=10.0.0.30,nw_proto=17,nw_tos=0,nw_ecn=0,nw_ttl=255,tp_src=100,tp_dst=200 udp_csum:ea78
+OFPT_BARRIER_REPLY (OF1.3):
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+dnl This test checks that NON-PTAP bridge rejects 1.5 packet-out with packet_type PT_IPV4.
+AT_SETUP([ofproto - packet-out with set_field metadata with packet_type PT_IPV4 on NON-PTAP bridge (OpenFlow 1.5)])
+OVS_VSWITCHD_START
+
+AT_CHECK([
+    ovs-vsctl set bridge br0 other-config:packet-type-aware=false
+], [0])
+
+AT_CHECK([ovs-ofctl -O OpenFlow15 packet-out br0 "in_port=controller packet=4500002012344000ff1155670a0000140a00001e006400c8000cea78ffffffff packet_type=0x10800 actions=set_field:0xfafafafa5a5a5a5a->metadata,controller"],
+[1], [], [dnl
+OFPT_ERROR (OF1.5) (xid=0x2): OFPBRC_BAD_PACKET
+OFPT_PACKET_OUT (OF1.5) (xid=0x2):
+(***truncated to 64 bytes from 104***)
+00000000  06 0d 00 68 00 00 00 02-ff ff ff ff 00 20 00 00 |...h......... ..|
+00000010  00 01 00 14 80 00 58 04-00 01 08 00 80 00 00 04 |......X.........|
+00000020  ff ff ff fd 00 00 00 00-00 19 00 10 80 00 04 08 |................|
+00000030  fa fa fa fa 5a 5a 5a 5a-00 00 00 10 ff ff ff fd |....ZZZZ........|
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+
 dnl This test checks that metadata is encoded in packet_in structures,
 dnl supported by NXAST.
 AT_SETUP([ofproto - packet-out with metadata (NXM)])
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 1a5e234..c396dd5 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -2094,6 +2094,7 @@  ofctl_packet_out(struct ovs_cmdl_context *ctx)
         po.in_port = str_to_port_no(ctx->argv[1], ctx->argv[2]);
         po.ofpacts = ofpacts.data;
         po.ofpacts_len = ofpacts.size;
+        po.packet_type = htonl(PT_ETH);
 
         protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn,
                                            usable_protocols);