[ovs-dev] [PATCH v2 6/6] userspace: Introduce OF 1.5 packet-out
Zoltán Balogh
zoltan.balogh at ericsson.com
Fri May 19 09:24:10 UTC 2017
Introduce OpenFlow 1.5 packet-out
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 at labs.hpe.com>
Signed-off-by: Zoltan Balogh <zoltan.balogh at ericsson.com>
Co-authored-by: Jan Scheurich <jan.scheurich at 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 | 11 ++++
lib/ofp-print.c | 3 +-
lib/ofp-util.c | 57 +++++++++++++++++++--
ofproto/ofproto.c | 8 +++
tests/ofproto.at | 108 ++++++++++++++++++++++++++++++++++++++++
utilities/ovs-ofctl.c | 1 +
10 files changed, 226 insertions(+), 24 deletions(-)
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 f37d181..4e58cdc 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -527,6 +527,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 d3b3cee..ef0f3c3 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1421,6 +1421,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. */
@@ -1472,26 +1474,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);
@@ -1503,15 +1508,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 c443784..d9103d7 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -652,6 +652,17 @@ parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
value);
goto out;
}
+ } else if (!strcmp(name, "packet_type")) {
+ char *ns = value;
+ char *ns_type = strstr(value, ",");
+ if (ns_type) {
+ *ns_type = '\0';
+ po->packet_type = PACKET_TYPE_BE(strtoul(ns, NULL, 0),
+ strtoul(++ns_type,NULL, 0));
+ } else {
+ error = xasprintf("%s(%s) can't be interpreted", name, value);
+ goto out;
+ }
} 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 2408bff..8f7e733 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -255,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..288b32b 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 = OFP_PORT_C(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 7274968..669cf89 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -3475,9 +3475,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 5431f4e..705e3a5 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -4106,6 +4106,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(0,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(1,0x800) 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(1,0x800) 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);
--
1.9.1
More information about the dev
mailing list