2018-02-09 10:04:26 -08:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2008-2017 Nicira, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include "openvswitch/ofp-packet.h"
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include "dp-packet.h"
|
|
|
|
|
#include "nx-match.h"
|
|
|
|
|
#include "openvswitch/ofp-actions.h"
|
|
|
|
|
#include "openvswitch/ofp-errors.h"
|
|
|
|
|
#include "openvswitch/ofp-msgs.h"
|
|
|
|
|
#include "openvswitch/ofp-parse.h"
|
2018-02-16 14:03:51 -08:00
|
|
|
|
#include "openvswitch/ofp-print.h"
|
2018-02-09 10:04:26 -08:00
|
|
|
|
#include "openvswitch/ofp-port.h"
|
|
|
|
|
#include "openvswitch/ofp-prop.h"
|
2018-02-16 14:03:51 -08:00
|
|
|
|
#include "openvswitch/ofp-table.h"
|
2018-02-09 10:04:26 -08:00
|
|
|
|
#include "openvswitch/ofpbuf.h"
|
|
|
|
|
#include "openvswitch/vlog.h"
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "uuid.h"
|
|
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(ofp_packet);
|
|
|
|
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
|
|
|
|
const char *
|
2018-02-16 11:43:56 -08:00
|
|
|
|
ofputil_packet_in_format_to_string(enum ofputil_packet_in_format format)
|
2018-02-09 10:04:26 -08:00
|
|
|
|
{
|
2018-02-16 11:43:56 -08:00
|
|
|
|
switch (format) {
|
|
|
|
|
case OFPUTIL_PACKET_IN_STD:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
return "standard";
|
2018-02-16 11:43:56 -08:00
|
|
|
|
case OFPUTIL_PACKET_IN_NXT:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
return "nxt_packet_in";
|
2018-02-16 11:43:56 -08:00
|
|
|
|
case OFPUTIL_PACKET_IN_NXT2:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
return "nxt_packet_in2";
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ofputil_packet_in_format_from_string(const char *s)
|
|
|
|
|
{
|
|
|
|
|
return (!strcmp(s, "standard") || !strcmp(s, "openflow10")
|
2018-02-16 11:43:56 -08:00
|
|
|
|
? OFPUTIL_PACKET_IN_STD
|
2018-02-09 10:04:26 -08:00
|
|
|
|
: !strcmp(s, "nxt_packet_in") || !strcmp(s, "nxm")
|
2018-02-16 11:43:56 -08:00
|
|
|
|
? OFPUTIL_PACKET_IN_NXT
|
2018-02-09 10:04:26 -08:00
|
|
|
|
: !strcmp(s, "nxt_packet_in2")
|
2018-02-16 11:43:56 -08:00
|
|
|
|
? OFPUTIL_PACKET_IN_NXT2
|
2018-02-09 10:04:26 -08:00
|
|
|
|
: -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
2018-02-16 11:43:56 -08:00
|
|
|
|
ofputil_encode_set_packet_in_format(enum ofp_version ofp_version,
|
|
|
|
|
enum ofputil_packet_in_format format)
|
2018-02-09 10:04:26 -08:00
|
|
|
|
{
|
2018-02-16 11:43:56 -08:00
|
|
|
|
struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT,
|
|
|
|
|
ofp_version, 0);
|
|
|
|
|
ovs_be32 *spif = ofpbuf_put_uninit(msg, sizeof *spif);
|
|
|
|
|
*spif = htonl(format);
|
2018-02-09 10:04:26 -08:00
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
2018-02-16 11:43:56 -08:00
|
|
|
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_set_packet_in_format(const struct ofp_header *oh,
|
|
|
|
|
enum ofputil_packet_in_format *format)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
|
|
|
|
ovs_assert(ofpraw_pull_assert(&b) == OFPRAW_NXT_SET_PACKET_IN_FORMAT);
|
|
|
|
|
ovs_be32 *spifp = ofpbuf_pull(&b, sizeof *spifp);
|
|
|
|
|
uint32_t spif = ntohl(*spifp);
|
|
|
|
|
|
|
|
|
|
switch (spif) {
|
|
|
|
|
case OFPUTIL_PACKET_IN_STD:
|
|
|
|
|
case OFPUTIL_PACKET_IN_NXT:
|
|
|
|
|
case OFPUTIL_PACKET_IN_NXT2:
|
|
|
|
|
*format = spif;
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
VLOG_WARN_RL(&rl, "NXT_SET_PACKET_IN_FORMAT message specified invalid "
|
|
|
|
|
"packet-in format %"PRIu32, spif);
|
|
|
|
|
return OFPERR_OFPBRC_EPERM;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-09 10:04:26 -08:00
|
|
|
|
|
|
|
|
|
/* The caller has done basic initialization of '*pin'; the other output
|
|
|
|
|
* arguments needs to be initialized. */
|
|
|
|
|
static enum ofperr
|
|
|
|
|
decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
|
|
|
|
|
const struct tun_table *tun_table,
|
|
|
|
|
const struct vl_mff_map *vl_mff_map,
|
|
|
|
|
struct ofputil_packet_in *pin,
|
|
|
|
|
size_t *total_len, uint32_t *buffer_id,
|
|
|
|
|
struct ofpbuf *continuation)
|
|
|
|
|
{
|
|
|
|
|
*total_len = 0;
|
|
|
|
|
*buffer_id = UINT32_MAX;
|
|
|
|
|
|
|
|
|
|
struct ofpbuf properties;
|
|
|
|
|
ofpbuf_use_const(&properties, oh, ntohs(oh->length));
|
|
|
|
|
ofpraw_pull_assert(&properties);
|
|
|
|
|
|
|
|
|
|
while (properties.size > 0) {
|
|
|
|
|
struct ofpbuf payload;
|
|
|
|
|
uint64_t type;
|
|
|
|
|
|
|
|
|
|
enum ofperr error = ofpprop_pull(&properties, &payload, &type);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case NXPINT_PACKET:
|
|
|
|
|
pin->packet = payload.msg;
|
|
|
|
|
pin->packet_len = ofpbuf_msgsize(&payload);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_FULL_LEN: {
|
|
|
|
|
uint32_t u32;
|
|
|
|
|
error = ofpprop_parse_u32(&payload, &u32);
|
|
|
|
|
*total_len = u32;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case NXPINT_BUFFER_ID:
|
|
|
|
|
error = ofpprop_parse_u32(&payload, buffer_id);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_TABLE_ID:
|
|
|
|
|
error = ofpprop_parse_u8(&payload, &pin->table_id);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_COOKIE:
|
|
|
|
|
error = ofpprop_parse_be64(&payload, &pin->cookie);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_REASON: {
|
|
|
|
|
uint8_t reason;
|
|
|
|
|
error = ofpprop_parse_u8(&payload, &reason);
|
|
|
|
|
pin->reason = reason;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case NXPINT_METADATA:
|
|
|
|
|
error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload),
|
|
|
|
|
loose, tun_table, vl_mff_map,
|
|
|
|
|
&pin->flow_metadata);
|
ofp-packet: Fix NXT_RESUME with geneve tunnel metadata
The patch address vswitchd crash when it receives NXT_RESUME with geneve
tunnel metadata. The crash is due to segmentation fault with the
following stack trace, and it is observed only in kernel datapath.
A test is added to prevent regression.
Thread 1 "ovs-vswitchd" received signal SIGSEGV, Segmentation fault.
0 0x00007fcffd0c5412 in tun_metadata_to_geneve__ (flow=flow@entry=0x7ffcb7106680, b=b@entry=0x7ffcb70eb5a8, crit_opt=crit_opt@entry=0x7ffcb70eb287)
at lib/tun-metadata.c:676
1 0x00007fcffd0c6858 in tun_metadata_to_geneve_nlattr_flow (b=0x7ffcb70eb5a8, flow=0x7ffcb7106638) at lib/tun-metadata.c:706
2 tun_metadata_to_geneve_nlattr (tun=tun@entry=0x7ffcb7106638, flow=flow@entry=0x7ffcb7106638, key=key@entry=0x0, b=b@entry=0x7ffcb70eb5a8)
at lib/tun-metadata.c:810
3 0x00007fcffd048464 in tun_key_to_attr (a=a@entry=0x7ffcb70eb5a8, tun_key=tun_key@entry=0x7ffcb7106638, tun_flow_key=tun_flow_key@entry=0x7ffcb7106638,
key_buf=key_buf@entry=0x0, tnl_type=<optimized out>, tnl_type@entry=0x0) at lib/odp-util.c:2886
4 0x00007fcffd0551cf in odp_key_from_dp_packet (buf=buf@entry=0x7ffcb70eb5a8, packet=0x7ffcb7106590) at lib/odp-util.c:5909
5 0x00007fcffd0d7870 in dpif_netlink_encode_execute (buf=0x7ffcb70eb5a8, d_exec=0x7ffcb7106428, dp_ifindex=<optimized out>) at lib/dpif-netlink.c:1873
6 dpif_netlink_operate__ (dpif=dpif@entry=0xe65e00, ops=ops@entry=0x7ffcb7106418, n_ops=n_ops@entry=1) at lib/dpif-netlink.c:1959
7 0x00007fcffd0d842e in dpif_netlink_operate_chunks (n_ops=1, ops=0x7ffcb7106418, dpif=<optimized out>) at lib/dpif-netlink.c:2258
8 dpif_netlink_operate (dpif_=0xe65e00, ops=<optimized out>, n_ops=<optimized out>) at lib/dpif-netlink.c:2294
9 0x00007fcffd014680 in dpif_operate (dpif=<optimized out>, ops=<optimized out>, ops@entry=0x7ffcb7106418, n_ops=n_ops@entry=1) at lib/dpif.c:1359
10 0x00007fcffd014c58 in dpif_execute (dpif=<optimized out>, execute=execute@entry=0x7ffcb71064e0) at lib/dpif.c:1324
11 0x00007fcffd40d3e6 in nxt_resume (ofproto_=0xe6af50, pin=0x7ffcb7107150) at ofproto/ofproto-dpif.c:4885
12 0x00007fcffd3f88c3 in handle_nxt_resume (ofconn=ofconn@entry=0xf8c8f0, oh=oh@entry=0xf7ebd0) at ofproto/ofproto.c:3612
13 0x00007fcffd404a3b in handle_openflow__ (msg=0xeac460, ofconn=0xf8c8f0) at ofproto/ofproto.c:8137
14 handle_openflow (ofconn=0xf8c8f0, ofp_msg=0xeac460) at ofproto/ofproto.c:8258
15 0x00007fcffd3f4653 in ofconn_run (handle_openflow=0x7fcffd4046f0 <handle_openflow>, ofconn=0xf8c8f0) at ofproto/connmgr.c:1432
16 connmgr_run (mgr=0xe422f0, handle_openflow=handle_openflow@entry=0x7fcffd4046f0 <handle_openflow>) at ofproto/connmgr.c:363
17 0x00007fcffd3fdc76 in ofproto_run (p=0xe6af50) at ofproto/ofproto.c:1821
18 0x000000000040ca94 in bridge_run__ () at vswitchd/bridge.c:2939
19 0x0000000000411d44 in bridge_run () at vswitchd/bridge.c:2997
20 0x00000000004094fd in main (argc=12, argv=0x7ffcb71085b8) at vswitchd/ovs-vswitchd.c:119
VMWare-BZ: #2210216
Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2018-10-05 09:19:54 -07:00
|
|
|
|
pin->flow_metadata.flow.tunnel.metadata.tab = tun_table;
|
2018-02-09 10:04:26 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_USERDATA:
|
|
|
|
|
pin->userdata = payload.msg;
|
|
|
|
|
pin->userdata_len = ofpbuf_msgsize(&payload);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXPINT_CONTINUATION:
|
|
|
|
|
if (continuation) {
|
|
|
|
|
error = ofpprop_parse_nested(&payload, continuation);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
error = OFPPROP_UNKNOWN(loose, "NX_PACKET_IN2", type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pin->packet_len) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "NXT_PACKET_IN2 lacks packet");
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
} else if (!*total_len) {
|
|
|
|
|
*total_len = pin->packet_len;
|
|
|
|
|
} else if (*total_len < pin->packet_len) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "NXT_PACKET_IN2 claimed full_len < len");
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decodes the packet-in message starting at 'oh' into '*pin'. Populates
|
|
|
|
|
* 'pin->packet' and 'pin->packet_len' with the part of the packet actually
|
|
|
|
|
* included in the message. If 'total_lenp' is nonnull, populates
|
|
|
|
|
* '*total_lenp' with the original length of the packet (which is larger than
|
|
|
|
|
* 'packet->len' if only part of the packet was included). If 'buffer_idp' is
|
|
|
|
|
* nonnull, stores the packet's buffer ID in '*buffer_idp' (UINT32_MAX if it
|
|
|
|
|
* was not buffered).
|
|
|
|
|
*
|
|
|
|
|
* Populates 'continuation', if nonnull, with the continuation data from the
|
|
|
|
|
* packet-in (an empty buffer, if 'oh' did not contain continuation data). The
|
|
|
|
|
* format of this data is supposed to be opaque to anything other than
|
|
|
|
|
* ovs-vswitchd, so that in any other process the only reasonable use of this
|
|
|
|
|
* data is to be copied into an NXT_RESUME message via ofputil_encode_resume().
|
|
|
|
|
*
|
|
|
|
|
* This function points 'pin->packet' into 'oh', so the caller should not free
|
|
|
|
|
* it separately from the original OpenFlow message. This is also true for
|
|
|
|
|
* 'pin->userdata' (which could also end up NULL if there is no userdata).
|
|
|
|
|
*
|
|
|
|
|
* 'vl_mff_map' is an optional parameter that is used to validate the length
|
|
|
|
|
* of variable length mf_fields in 'match'. If it is not provided, the
|
|
|
|
|
* default mf_fields with maximum length will be used.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if successful, otherwise an OpenFlow error code. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
|
|
|
|
|
const struct tun_table *tun_table,
|
|
|
|
|
const struct vl_mff_map *vl_mff_map,
|
|
|
|
|
struct ofputil_packet_in *pin,
|
|
|
|
|
size_t *total_lenp, uint32_t *buffer_idp,
|
|
|
|
|
struct ofpbuf *continuation)
|
|
|
|
|
{
|
|
|
|
|
uint32_t buffer_id;
|
|
|
|
|
size_t total_len;
|
|
|
|
|
|
|
|
|
|
memset(pin, 0, sizeof *pin);
|
|
|
|
|
pin->cookie = OVS_BE64_MAX;
|
|
|
|
|
if (continuation) {
|
|
|
|
|
ofpbuf_use_const(continuation, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
|
|
|
|
enum ofpraw raw = ofpraw_pull_assert(&b);
|
|
|
|
|
if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) {
|
|
|
|
|
const struct ofp12_packet_in *opi = ofpbuf_pull(&b, sizeof *opi);
|
|
|
|
|
const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
|
|
|
|
|
? ofpbuf_pull(&b, sizeof *cookie)
|
|
|
|
|
: NULL);
|
|
|
|
|
enum ofperr error = oxm_pull_match_loose(&b, false, tun_table,
|
|
|
|
|
&pin->flow_metadata);
|
ofp-packet: Fix NXT_RESUME with geneve tunnel metadata
The patch address vswitchd crash when it receives NXT_RESUME with geneve
tunnel metadata. The crash is due to segmentation fault with the
following stack trace, and it is observed only in kernel datapath.
A test is added to prevent regression.
Thread 1 "ovs-vswitchd" received signal SIGSEGV, Segmentation fault.
0 0x00007fcffd0c5412 in tun_metadata_to_geneve__ (flow=flow@entry=0x7ffcb7106680, b=b@entry=0x7ffcb70eb5a8, crit_opt=crit_opt@entry=0x7ffcb70eb287)
at lib/tun-metadata.c:676
1 0x00007fcffd0c6858 in tun_metadata_to_geneve_nlattr_flow (b=0x7ffcb70eb5a8, flow=0x7ffcb7106638) at lib/tun-metadata.c:706
2 tun_metadata_to_geneve_nlattr (tun=tun@entry=0x7ffcb7106638, flow=flow@entry=0x7ffcb7106638, key=key@entry=0x0, b=b@entry=0x7ffcb70eb5a8)
at lib/tun-metadata.c:810
3 0x00007fcffd048464 in tun_key_to_attr (a=a@entry=0x7ffcb70eb5a8, tun_key=tun_key@entry=0x7ffcb7106638, tun_flow_key=tun_flow_key@entry=0x7ffcb7106638,
key_buf=key_buf@entry=0x0, tnl_type=<optimized out>, tnl_type@entry=0x0) at lib/odp-util.c:2886
4 0x00007fcffd0551cf in odp_key_from_dp_packet (buf=buf@entry=0x7ffcb70eb5a8, packet=0x7ffcb7106590) at lib/odp-util.c:5909
5 0x00007fcffd0d7870 in dpif_netlink_encode_execute (buf=0x7ffcb70eb5a8, d_exec=0x7ffcb7106428, dp_ifindex=<optimized out>) at lib/dpif-netlink.c:1873
6 dpif_netlink_operate__ (dpif=dpif@entry=0xe65e00, ops=ops@entry=0x7ffcb7106418, n_ops=n_ops@entry=1) at lib/dpif-netlink.c:1959
7 0x00007fcffd0d842e in dpif_netlink_operate_chunks (n_ops=1, ops=0x7ffcb7106418, dpif=<optimized out>) at lib/dpif-netlink.c:2258
8 dpif_netlink_operate (dpif_=0xe65e00, ops=<optimized out>, n_ops=<optimized out>) at lib/dpif-netlink.c:2294
9 0x00007fcffd014680 in dpif_operate (dpif=<optimized out>, ops=<optimized out>, ops@entry=0x7ffcb7106418, n_ops=n_ops@entry=1) at lib/dpif.c:1359
10 0x00007fcffd014c58 in dpif_execute (dpif=<optimized out>, execute=execute@entry=0x7ffcb71064e0) at lib/dpif.c:1324
11 0x00007fcffd40d3e6 in nxt_resume (ofproto_=0xe6af50, pin=0x7ffcb7107150) at ofproto/ofproto-dpif.c:4885
12 0x00007fcffd3f88c3 in handle_nxt_resume (ofconn=ofconn@entry=0xf8c8f0, oh=oh@entry=0xf7ebd0) at ofproto/ofproto.c:3612
13 0x00007fcffd404a3b in handle_openflow__ (msg=0xeac460, ofconn=0xf8c8f0) at ofproto/ofproto.c:8137
14 handle_openflow (ofconn=0xf8c8f0, ofp_msg=0xeac460) at ofproto/ofproto.c:8258
15 0x00007fcffd3f4653 in ofconn_run (handle_openflow=0x7fcffd4046f0 <handle_openflow>, ofconn=0xf8c8f0) at ofproto/connmgr.c:1432
16 connmgr_run (mgr=0xe422f0, handle_openflow=handle_openflow@entry=0x7fcffd4046f0 <handle_openflow>) at ofproto/connmgr.c:363
17 0x00007fcffd3fdc76 in ofproto_run (p=0xe6af50) at ofproto/ofproto.c:1821
18 0x000000000040ca94 in bridge_run__ () at vswitchd/bridge.c:2939
19 0x0000000000411d44 in bridge_run () at vswitchd/bridge.c:2997
20 0x00000000004094fd in main (argc=12, argv=0x7ffcb71085b8) at vswitchd/ovs-vswitchd.c:119
VMWare-BZ: #2210216
Signed-off-by: Yi-Hung Wei <yihung.wei@gmail.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2018-10-05 09:19:54 -07:00
|
|
|
|
pin->flow_metadata.flow.tunnel.metadata.tab = tun_table;
|
2018-02-09 10:04:26 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ofpbuf_try_pull(&b, 2)) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pin->reason = opi->reason;
|
|
|
|
|
pin->table_id = opi->table_id;
|
|
|
|
|
buffer_id = ntohl(opi->buffer_id);
|
|
|
|
|
total_len = ntohs(opi->total_len);
|
|
|
|
|
if (cookie) {
|
|
|
|
|
pin->cookie = *cookie;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pin->packet = b.data;
|
|
|
|
|
pin->packet_len = b.size;
|
|
|
|
|
} else if (raw == OFPRAW_OFPT10_PACKET_IN) {
|
|
|
|
|
const struct ofp10_packet_in *opi;
|
|
|
|
|
|
|
|
|
|
opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data));
|
|
|
|
|
|
|
|
|
|
pin->packet = CONST_CAST(uint8_t *, opi->data);
|
|
|
|
|
pin->packet_len = b.size;
|
|
|
|
|
|
|
|
|
|
match_init_catchall(&pin->flow_metadata);
|
|
|
|
|
match_set_in_port(&pin->flow_metadata,
|
|
|
|
|
u16_to_ofp(ntohs(opi->in_port)));
|
|
|
|
|
pin->reason = opi->reason;
|
|
|
|
|
buffer_id = ntohl(opi->buffer_id);
|
|
|
|
|
total_len = ntohs(opi->total_len);
|
|
|
|
|
} else if (raw == OFPRAW_OFPT11_PACKET_IN) {
|
|
|
|
|
const struct ofp11_packet_in *opi;
|
|
|
|
|
ofp_port_t in_port;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
|
|
|
|
opi = ofpbuf_pull(&b, sizeof *opi);
|
|
|
|
|
|
|
|
|
|
pin->packet = b.data;
|
|
|
|
|
pin->packet_len = b.size;
|
|
|
|
|
|
|
|
|
|
buffer_id = ntohl(opi->buffer_id);
|
|
|
|
|
error = ofputil_port_from_ofp11(opi->in_port, &in_port);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
match_init_catchall(&pin->flow_metadata);
|
|
|
|
|
match_set_in_port(&pin->flow_metadata, in_port);
|
|
|
|
|
total_len = ntohs(opi->total_len);
|
|
|
|
|
pin->reason = opi->reason;
|
|
|
|
|
pin->table_id = opi->table_id;
|
|
|
|
|
} else if (raw == OFPRAW_NXT_PACKET_IN) {
|
|
|
|
|
const struct nx_packet_in *npi;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
npi = ofpbuf_pull(&b, sizeof *npi);
|
|
|
|
|
error = nx_pull_match_loose(&b, ntohs(npi->match_len),
|
|
|
|
|
&pin->flow_metadata, NULL, NULL, false,
|
|
|
|
|
NULL);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ofpbuf_try_pull(&b, 2)) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pin->reason = npi->reason;
|
|
|
|
|
pin->table_id = npi->table_id;
|
|
|
|
|
pin->cookie = npi->cookie;
|
|
|
|
|
|
|
|
|
|
buffer_id = ntohl(npi->buffer_id);
|
|
|
|
|
total_len = ntohs(npi->total_len);
|
|
|
|
|
|
|
|
|
|
pin->packet = b.data;
|
|
|
|
|
pin->packet_len = b.size;
|
|
|
|
|
} else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) {
|
|
|
|
|
enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table,
|
|
|
|
|
vl_mff_map, pin, &total_len,
|
|
|
|
|
&buffer_id, continuation);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (total_lenp) {
|
|
|
|
|
*total_lenp = total_len;
|
|
|
|
|
}
|
|
|
|
|
if (buffer_idp) {
|
|
|
|
|
*buffer_idp = buffer_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
encode_packet_in_reason(enum ofp_packet_in_reason reason,
|
|
|
|
|
enum ofp_version version)
|
|
|
|
|
{
|
|
|
|
|
switch (reason) {
|
|
|
|
|
case OFPR_NO_MATCH:
|
|
|
|
|
case OFPR_ACTION:
|
|
|
|
|
case OFPR_INVALID_TTL:
|
|
|
|
|
return reason;
|
|
|
|
|
|
|
|
|
|
case OFPR_ACTION_SET:
|
|
|
|
|
case OFPR_GROUP:
|
|
|
|
|
case OFPR_PACKET_OUT:
|
|
|
|
|
return version < OFP14_VERSION ? OFPR_ACTION : reason;
|
|
|
|
|
|
|
|
|
|
case OFPR_EXPLICIT_MISS:
|
|
|
|
|
return version < OFP13_VERSION ? OFPR_ACTION : OFPR_NO_MATCH;
|
|
|
|
|
|
|
|
|
|
case OFPR_IMPLICIT_MISS:
|
|
|
|
|
return OFPR_NO_MATCH;
|
|
|
|
|
|
|
|
|
|
case OFPR_N_REASONS:
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this
|
|
|
|
|
* function omits it. The caller can add it itself if desired. */
|
|
|
|
|
static void
|
|
|
|
|
ofputil_put_packet_in(const struct ofputil_packet_in *pin,
|
|
|
|
|
enum ofp_version version, size_t include_bytes,
|
|
|
|
|
struct ofpbuf *msg)
|
|
|
|
|
{
|
|
|
|
|
/* Add packet properties. */
|
|
|
|
|
ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes);
|
|
|
|
|
if (include_bytes != pin->packet_len) {
|
|
|
|
|
ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add flow properties. */
|
|
|
|
|
ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id);
|
|
|
|
|
if (pin->cookie != OVS_BE64_MAX) {
|
|
|
|
|
ofpprop_put_be64(msg, NXPINT_COOKIE, pin->cookie);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add other properties. */
|
|
|
|
|
ofpprop_put_u8(msg, NXPINT_REASON,
|
|
|
|
|
encode_packet_in_reason(pin->reason, version));
|
|
|
|
|
|
|
|
|
|
size_t start = ofpprop_start(msg, NXPINT_METADATA);
|
|
|
|
|
oxm_put_raw(msg, &pin->flow_metadata, version);
|
|
|
|
|
ofpprop_end(msg, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
put_actions_property(struct ofpbuf *msg, uint64_t prop_type,
|
|
|
|
|
enum ofp_version version,
|
|
|
|
|
const struct ofpact *actions, size_t actions_len)
|
|
|
|
|
{
|
|
|
|
|
if (actions_len) {
|
|
|
|
|
size_t start = ofpprop_start_nested(msg, prop_type);
|
|
|
|
|
ofpacts_put_openflow_actions(actions, actions_len, msg, version);
|
|
|
|
|
ofpprop_end(msg, start);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum nx_continuation_prop_type {
|
|
|
|
|
NXCPT_BRIDGE = 0x8000,
|
|
|
|
|
NXCPT_STACK,
|
|
|
|
|
NXCPT_MIRRORS,
|
|
|
|
|
NXCPT_CONNTRACKED,
|
|
|
|
|
NXCPT_TABLE_ID,
|
|
|
|
|
NXCPT_COOKIE,
|
|
|
|
|
NXCPT_ACTIONS,
|
|
|
|
|
NXCPT_ACTION_SET,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Only NXT_PACKET_IN2 (not NXT_RESUME) should include NXCPT_USERDATA, so this
|
|
|
|
|
* function omits it. The caller can add it itself if desired. */
|
|
|
|
|
static void
|
|
|
|
|
ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin,
|
|
|
|
|
enum ofp_version version, size_t include_bytes,
|
|
|
|
|
struct ofpbuf *msg)
|
|
|
|
|
{
|
|
|
|
|
ofputil_put_packet_in(&pin->base, version, include_bytes, msg);
|
|
|
|
|
|
|
|
|
|
size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION);
|
|
|
|
|
size_t inner_ofs = msg->size;
|
|
|
|
|
|
|
|
|
|
if (!uuid_is_zero(&pin->bridge)) {
|
|
|
|
|
ofpprop_put_uuid(msg, NXCPT_BRIDGE, &pin->bridge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf pin_stack;
|
|
|
|
|
ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size);
|
|
|
|
|
|
|
|
|
|
while (pin_stack.size) {
|
|
|
|
|
uint8_t len;
|
|
|
|
|
uint8_t *val = nx_stack_pop(&pin_stack, &len);
|
|
|
|
|
ofpprop_put(msg, NXCPT_STACK, val, len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->mirrors) {
|
|
|
|
|
ofpprop_put_u32(msg, NXCPT_MIRRORS, pin->mirrors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->conntracked) {
|
|
|
|
|
ofpprop_put_flag(msg, NXCPT_CONNTRACKED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->actions_len) {
|
|
|
|
|
/* Divide 'pin->actions' into groups that begins with an
|
|
|
|
|
* unroll_xlate action. For each group, emit a NXCPT_TABLE_ID and
|
|
|
|
|
* NXCPT_COOKIE property (if either has changed; each is initially
|
|
|
|
|
* assumed 0), then a NXCPT_ACTIONS property with the grouped
|
|
|
|
|
* actions.
|
|
|
|
|
*
|
|
|
|
|
* The alternative is to make OFPACT_UNROLL_XLATE public. We can
|
|
|
|
|
* always do that later, since this is a private property. */
|
|
|
|
|
const struct ofpact *const end = ofpact_end(pin->actions,
|
|
|
|
|
pin->actions_len);
|
|
|
|
|
const struct ofpact_unroll_xlate *unroll = NULL;
|
|
|
|
|
uint8_t table_id = 0;
|
|
|
|
|
ovs_be64 cookie = 0;
|
|
|
|
|
|
|
|
|
|
const struct ofpact *a;
|
|
|
|
|
for (a = pin->actions; ; a = ofpact_next(a)) {
|
|
|
|
|
if (a == end || a->type == OFPACT_UNROLL_XLATE) {
|
|
|
|
|
if (unroll) {
|
|
|
|
|
if (table_id != unroll->rule_table_id) {
|
|
|
|
|
ofpprop_put_u8(msg, NXCPT_TABLE_ID,
|
|
|
|
|
unroll->rule_table_id);
|
|
|
|
|
table_id = unroll->rule_table_id;
|
|
|
|
|
}
|
|
|
|
|
if (cookie != unroll->rule_cookie) {
|
|
|
|
|
ofpprop_put_be64(msg, NXCPT_COOKIE,
|
|
|
|
|
unroll->rule_cookie);
|
|
|
|
|
cookie = unroll->rule_cookie;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const struct ofpact *start
|
|
|
|
|
= unroll ? ofpact_next(&unroll->ofpact) : pin->actions;
|
|
|
|
|
put_actions_property(msg, NXCPT_ACTIONS, version,
|
|
|
|
|
start, (a - start) * sizeof *a);
|
|
|
|
|
|
|
|
|
|
if (a == end) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
unroll = ofpact_get_UNROLL_XLATE(a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->action_set_len) {
|
|
|
|
|
size_t start = ofpprop_start_nested(msg, NXCPT_ACTION_SET);
|
|
|
|
|
ofpacts_put_openflow_actions(pin->action_set,
|
|
|
|
|
pin->action_set_len, msg, version);
|
|
|
|
|
ofpprop_end(msg, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msg->size > inner_ofs) {
|
|
|
|
|
ofpprop_end(msg, continuation_ofs);
|
|
|
|
|
} else {
|
|
|
|
|
msg->size = continuation_ofs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ofpbuf *
|
|
|
|
|
ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
|
|
|
|
|
{
|
|
|
|
|
struct ofp10_packet_in *opi;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION,
|
|
|
|
|
htonl(0), pin->packet_len);
|
|
|
|
|
opi = ofpbuf_put_zeros(msg, offsetof(struct ofp10_packet_in, data));
|
|
|
|
|
opi->total_len = htons(pin->packet_len);
|
|
|
|
|
opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port));
|
|
|
|
|
opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION);
|
|
|
|
|
opi->buffer_id = htonl(UINT32_MAX);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ofpbuf *
|
|
|
|
|
ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
|
|
|
|
|
enum ofp_version version)
|
|
|
|
|
{
|
|
|
|
|
struct nx_packet_in *npi;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
size_t match_len;
|
|
|
|
|
|
|
|
|
|
/* The final argument is just an estimate of the space required. */
|
|
|
|
|
msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, version,
|
|
|
|
|
htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
|
|
|
|
|
ofpbuf_put_zeros(msg, sizeof *npi);
|
|
|
|
|
match_len = nx_put_match(msg, &pin->flow_metadata, 0, 0);
|
|
|
|
|
ofpbuf_put_zeros(msg, 2);
|
|
|
|
|
|
|
|
|
|
npi = msg->msg;
|
|
|
|
|
npi->buffer_id = htonl(UINT32_MAX);
|
|
|
|
|
npi->total_len = htons(pin->packet_len);
|
|
|
|
|
npi->reason = encode_packet_in_reason(pin->reason, version);
|
|
|
|
|
npi->table_id = pin->table_id;
|
|
|
|
|
npi->cookie = pin->cookie;
|
|
|
|
|
npi->match_len = htons(match_len);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ofpbuf *
|
|
|
|
|
ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin,
|
|
|
|
|
enum ofp_version version, size_t include_bytes)
|
|
|
|
|
{
|
|
|
|
|
/* 'extra' is just an estimate of the space required. */
|
|
|
|
|
size_t extra = (pin->base.packet_len
|
|
|
|
|
+ NXM_TYPICAL_LEN /* flow_metadata */
|
|
|
|
|
+ pin->stack_size * 4
|
|
|
|
|
+ pin->actions_len
|
|
|
|
|
+ pin->action_set_len
|
|
|
|
|
+ 256); /* fudge factor */
|
|
|
|
|
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version,
|
|
|
|
|
htonl(0), extra);
|
|
|
|
|
|
|
|
|
|
ofputil_put_packet_in_private(pin, version, include_bytes, msg);
|
|
|
|
|
if (pin->base.userdata_len) {
|
|
|
|
|
ofpprop_put(msg, NXPINT_USERDATA, pin->base.userdata,
|
|
|
|
|
pin->base.userdata_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpmsg_update_length(msg);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ofpbuf *
|
|
|
|
|
ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin)
|
|
|
|
|
{
|
|
|
|
|
struct ofp11_packet_in *opi;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION,
|
|
|
|
|
htonl(0), pin->packet_len);
|
|
|
|
|
opi = ofpbuf_put_zeros(msg, sizeof *opi);
|
|
|
|
|
opi->buffer_id = htonl(UINT32_MAX);
|
|
|
|
|
opi->in_port = ofputil_port_to_ofp11(
|
|
|
|
|
pin->flow_metadata.flow.in_port.ofp_port);
|
|
|
|
|
opi->in_phy_port = opi->in_port;
|
|
|
|
|
opi->total_len = htons(pin->packet_len);
|
|
|
|
|
opi->reason = encode_packet_in_reason(pin->reason, OFP11_VERSION);
|
|
|
|
|
opi->table_id = pin->table_id;
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct ofpbuf *
|
|
|
|
|
ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
|
|
|
|
|
enum ofp_version version)
|
|
|
|
|
{
|
|
|
|
|
enum ofpraw raw = (version >= OFP13_VERSION
|
|
|
|
|
? OFPRAW_OFPT13_PACKET_IN
|
|
|
|
|
: OFPRAW_OFPT12_PACKET_IN);
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
/* The final argument is just an estimate of the space required. */
|
|
|
|
|
msg = ofpraw_alloc_xid(raw, version,
|
|
|
|
|
htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
|
|
|
|
|
|
|
|
|
|
struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi);
|
|
|
|
|
opi->buffer_id = htonl(UINT32_MAX);
|
|
|
|
|
opi->total_len = htons(pin->packet_len);
|
|
|
|
|
opi->reason = encode_packet_in_reason(pin->reason, version);
|
|
|
|
|
opi->table_id = pin->table_id;
|
|
|
|
|
|
|
|
|
|
if (version >= OFP13_VERSION) {
|
|
|
|
|
ovs_be64 cookie = pin->cookie;
|
|
|
|
|
ofpbuf_put(msg, &cookie, sizeof cookie);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oxm_put_match(msg, &pin->flow_metadata, version);
|
|
|
|
|
ofpbuf_put_zeros(msg, 2);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts abstract ofputil_packet_in_private 'pin' into a PACKET_IN message
|
2018-02-16 11:43:56 -08:00
|
|
|
|
* for 'protocol', using the packet-in format specified by 'format'.
|
2018-02-09 10:04:26 -08:00
|
|
|
|
*
|
|
|
|
|
* This function is really meant only for use by ovs-vswitchd. To any other
|
|
|
|
|
* code, the "continuation" data, i.e. the data that is in struct
|
|
|
|
|
* ofputil_packet_in_private but not in struct ofputil_packet_in, is supposed
|
|
|
|
|
* to be opaque (and it might change from one OVS version to another). Thus,
|
|
|
|
|
* if any other code wants to encode a packet-in, it should use a non-"private"
|
|
|
|
|
* version of this function. (Such a version doesn't currently exist because
|
|
|
|
|
* only ovs-vswitchd currently wants to encode packet-ins. If you need one,
|
|
|
|
|
* write it...) */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
|
|
|
|
|
enum ofputil_protocol protocol,
|
2018-02-16 11:43:56 -08:00
|
|
|
|
enum ofputil_packet_in_format format)
|
2018-02-09 10:04:26 -08:00
|
|
|
|
{
|
|
|
|
|
enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *msg;
|
2018-02-16 11:43:56 -08:00
|
|
|
|
switch (format) {
|
|
|
|
|
case OFPUTIL_PACKET_IN_STD:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
switch (protocol) {
|
|
|
|
|
case OFPUTIL_P_OF10_STD:
|
|
|
|
|
case OFPUTIL_P_OF10_STD_TID:
|
|
|
|
|
case OFPUTIL_P_OF10_NXM:
|
|
|
|
|
case OFPUTIL_P_OF10_NXM_TID:
|
|
|
|
|
msg = ofputil_encode_ofp10_packet_in(&pin->base);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF11_STD:
|
|
|
|
|
msg = ofputil_encode_ofp11_packet_in(&pin->base);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF12_OXM:
|
|
|
|
|
case OFPUTIL_P_OF13_OXM:
|
|
|
|
|
case OFPUTIL_P_OF14_OXM:
|
|
|
|
|
case OFPUTIL_P_OF15_OXM:
|
|
|
|
|
case OFPUTIL_P_OF16_OXM:
|
|
|
|
|
msg = ofputil_encode_ofp12_packet_in(&pin->base, version);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2018-02-16 11:43:56 -08:00
|
|
|
|
case OFPUTIL_PACKET_IN_NXT:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
msg = ofputil_encode_nx_packet_in(&pin->base, version);
|
|
|
|
|
break;
|
|
|
|
|
|
2018-02-16 11:43:56 -08:00
|
|
|
|
case OFPUTIL_PACKET_IN_NXT2:
|
2018-02-09 10:04:26 -08:00
|
|
|
|
return ofputil_encode_nx_packet_in2(pin, version,
|
|
|
|
|
pin->base.packet_len);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_put(msg, pin->base.packet, pin->base.packet_len);
|
|
|
|
|
ofpmsg_update_length(msg);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a string form of 'reason'. The return value is either a statically
|
|
|
|
|
* allocated constant string or the 'bufsize'-byte buffer 'reasonbuf'.
|
|
|
|
|
* 'bufsize' should be at least OFPUTIL_PACKET_IN_REASON_BUFSIZE. */
|
|
|
|
|
const char *
|
|
|
|
|
ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason,
|
|
|
|
|
char *reasonbuf, size_t bufsize)
|
|
|
|
|
{
|
|
|
|
|
switch (reason) {
|
|
|
|
|
case OFPR_NO_MATCH:
|
|
|
|
|
return "no_match";
|
|
|
|
|
case OFPR_ACTION:
|
|
|
|
|
return "action";
|
|
|
|
|
case OFPR_INVALID_TTL:
|
|
|
|
|
return "invalid_ttl";
|
|
|
|
|
case OFPR_ACTION_SET:
|
|
|
|
|
return "action_set";
|
|
|
|
|
case OFPR_GROUP:
|
|
|
|
|
return "group";
|
|
|
|
|
case OFPR_PACKET_OUT:
|
|
|
|
|
return "packet_out";
|
|
|
|
|
case OFPR_EXPLICIT_MISS:
|
|
|
|
|
case OFPR_IMPLICIT_MISS:
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
|
|
case OFPR_N_REASONS:
|
|
|
|
|
default:
|
|
|
|
|
snprintf(reasonbuf, bufsize, "%d", (int) reason);
|
|
|
|
|
return reasonbuf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ofputil_packet_in_reason_from_string(const char *s,
|
|
|
|
|
enum ofp_packet_in_reason *reason)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < OFPR_N_REASONS; i++) {
|
|
|
|
|
char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
|
|
|
|
|
const char *reason_s;
|
|
|
|
|
|
|
|
|
|
reason_s = ofputil_packet_in_reason_to_string(i, reasonbuf,
|
|
|
|
|
sizeof reasonbuf);
|
|
|
|
|
if (!strcasecmp(s, reason_s)) {
|
|
|
|
|
*reason = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a newly allocated NXT_RESUME message for 'pin', with the given
|
|
|
|
|
* 'continuation', for 'protocol'. This message is suitable for resuming the
|
|
|
|
|
* pipeline traveral of the packet represented by 'pin', if sent to the switch
|
|
|
|
|
* from which 'pin' was received. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_resume(const struct ofputil_packet_in *pin,
|
|
|
|
|
const struct ofpbuf *continuation,
|
|
|
|
|
enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
size_t extra = pin->packet_len + NXM_TYPICAL_LEN + continuation->size;
|
|
|
|
|
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_RESUME, version,
|
|
|
|
|
0, extra);
|
|
|
|
|
ofputil_put_packet_in(pin, version, pin->packet_len, msg);
|
|
|
|
|
ofpprop_put_nested(msg, NXPINT_CONTINUATION, continuation);
|
|
|
|
|
ofpmsg_update_length(msg);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
parse_stack_prop(const struct ofpbuf *property, struct ofpbuf *stack)
|
|
|
|
|
{
|
|
|
|
|
unsigned int len = ofpbuf_msgsize(property);
|
|
|
|
|
if (len > sizeof(union mf_subvalue)) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "NXCPT_STACK property has bad length %u",
|
|
|
|
|
len);
|
|
|
|
|
return OFPERR_OFPBPC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
nx_stack_push_bottom(stack, property->msg, len);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
parse_actions_property(struct ofpbuf *property, enum ofp_version version,
|
|
|
|
|
struct ofpbuf *ofpacts)
|
|
|
|
|
{
|
|
|
|
|
if (!ofpbuf_try_pull(property, ROUND_UP(ofpbuf_headersize(property), 8))) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "actions property has bad length %"PRIu32,
|
|
|
|
|
property->size);
|
|
|
|
|
return OFPERR_OFPBPC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ofpacts_pull_openflow_actions(property, property->size,
|
|
|
|
|
version, NULL, NULL, ofpacts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is like ofputil_decode_packet_in(), except that it decodes the
|
|
|
|
|
* continuation data into 'pin'. The format of this data is supposed to be
|
|
|
|
|
* opaque to any process other than ovs-vswitchd, so this function should not
|
|
|
|
|
* be used outside ovs-vswitchd.
|
|
|
|
|
*
|
|
|
|
|
* 'vl_mff_map' is an optional parameter that is used to validate the length
|
|
|
|
|
* of variable length mf_fields in 'match'. If it is not provided, the
|
|
|
|
|
* default mf_fields with maximum length will be used.
|
|
|
|
|
*
|
|
|
|
|
* When successful, 'pin' contains some dynamically allocated data. Call
|
|
|
|
|
* ofputil_packet_in_private_destroy() to free this data. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
|
|
|
|
|
const struct tun_table *tun_table,
|
|
|
|
|
const struct vl_mff_map *vl_mff_map,
|
|
|
|
|
struct ofputil_packet_in_private *pin,
|
|
|
|
|
size_t *total_len, uint32_t *buffer_id)
|
|
|
|
|
{
|
|
|
|
|
memset(pin, 0, sizeof *pin);
|
|
|
|
|
|
|
|
|
|
struct ofpbuf continuation;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
error = ofputil_decode_packet_in(oh, loose, tun_table, vl_mff_map,
|
|
|
|
|
&pin->base, total_len, buffer_id,
|
|
|
|
|
&continuation);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf actions, action_set;
|
|
|
|
|
ofpbuf_init(&actions, 0);
|
|
|
|
|
ofpbuf_init(&action_set, 0);
|
|
|
|
|
|
|
|
|
|
uint8_t table_id = 0;
|
|
|
|
|
ovs_be64 cookie = 0;
|
|
|
|
|
|
|
|
|
|
struct ofpbuf stack;
|
|
|
|
|
ofpbuf_init(&stack, 0);
|
|
|
|
|
|
|
|
|
|
while (continuation.size > 0) {
|
|
|
|
|
struct ofpbuf payload;
|
|
|
|
|
uint64_t type;
|
|
|
|
|
|
|
|
|
|
error = ofpprop_pull(&continuation, &payload, &type);
|
|
|
|
|
if (error) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case NXCPT_BRIDGE:
|
|
|
|
|
error = ofpprop_parse_uuid(&payload, &pin->bridge);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_STACK:
|
|
|
|
|
error = parse_stack_prop(&payload, &stack);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_MIRRORS:
|
|
|
|
|
error = ofpprop_parse_u32(&payload, &pin->mirrors);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_CONNTRACKED:
|
|
|
|
|
pin->conntracked = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_TABLE_ID:
|
|
|
|
|
error = ofpprop_parse_u8(&payload, &table_id);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_COOKIE:
|
|
|
|
|
error = ofpprop_parse_be64(&payload, &cookie);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case NXCPT_ACTIONS: {
|
|
|
|
|
struct ofpact_unroll_xlate *unroll
|
|
|
|
|
= ofpact_put_UNROLL_XLATE(&actions);
|
|
|
|
|
unroll->rule_table_id = table_id;
|
|
|
|
|
unroll->rule_cookie = cookie;
|
|
|
|
|
error = parse_actions_property(&payload, oh->version, &actions);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case NXCPT_ACTION_SET:
|
|
|
|
|
error = parse_actions_property(&payload, oh->version, &action_set);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
error = OFPPROP_UNKNOWN(loose, "continuation", type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (error) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pin->actions_len = actions.size;
|
|
|
|
|
pin->actions = ofpbuf_steal_data(&actions);
|
|
|
|
|
pin->action_set_len = action_set.size;
|
|
|
|
|
pin->action_set = ofpbuf_steal_data(&action_set);
|
|
|
|
|
pin->stack_size = stack.size;
|
|
|
|
|
pin->stack = ofpbuf_steal_data(&stack);
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
ofputil_packet_in_private_destroy(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-16 14:03:51 -08:00
|
|
|
|
static void
|
|
|
|
|
format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
|
|
|
|
|
{
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
if (i) {
|
|
|
|
|
ds_put_char(s, '.');
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(s, "%02"PRIx8, data[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ofputil_packet_in_private_format(struct ds *s,
|
|
|
|
|
const struct ofputil_packet_in_private *pin,
|
|
|
|
|
size_t total_len, uint32_t buffer_id,
|
|
|
|
|
const struct ofputil_port_map *port_map,
|
|
|
|
|
const struct ofputil_table_map *table_map,
|
|
|
|
|
int verbosity)
|
|
|
|
|
{
|
|
|
|
|
char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
|
|
|
|
|
const struct ofputil_packet_in *public = &pin->base;
|
|
|
|
|
|
|
|
|
|
if (public->table_id
|
|
|
|
|
|| ofputil_table_map_get_name(table_map, public->table_id)) {
|
|
|
|
|
ds_put_format(s, " table_id=");
|
|
|
|
|
ofputil_format_table(public->table_id, table_map, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (public->cookie != OVS_BE64_MAX) {
|
|
|
|
|
ds_put_format(s, " cookie=0x%"PRIx64, ntohll(public->cookie));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_format(s, " total_len=%"PRIuSIZE" ", total_len);
|
|
|
|
|
|
|
|
|
|
match_format(&public->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY);
|
|
|
|
|
|
|
|
|
|
ds_put_format(s, " (via %s)",
|
|
|
|
|
ofputil_packet_in_reason_to_string(public->reason,
|
|
|
|
|
reasonbuf,
|
|
|
|
|
sizeof reasonbuf));
|
|
|
|
|
|
|
|
|
|
ds_put_format(s, " data_len=%"PRIuSIZE, public->packet_len);
|
|
|
|
|
if (buffer_id == UINT32_MAX) {
|
|
|
|
|
ds_put_format(s, " (unbuffered)");
|
|
|
|
|
if (total_len != public->packet_len) {
|
|
|
|
|
ds_put_format(s, " (***total_len != data_len***)");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(s, " buffer=0x%08"PRIx32, buffer_id);
|
|
|
|
|
if (total_len < public->packet_len) {
|
|
|
|
|
ds_put_format(s, " (***total_len < data_len***)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(s, '\n');
|
|
|
|
|
|
|
|
|
|
if (public->userdata_len) {
|
|
|
|
|
ds_put_cstr(s, " userdata=");
|
|
|
|
|
format_hex_arg(s, pin->base.userdata, pin->base.userdata_len);
|
|
|
|
|
ds_put_char(s, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!uuid_is_zero(&pin->bridge)) {
|
|
|
|
|
ds_put_format(s, " continuation.bridge="UUID_FMT"\n",
|
|
|
|
|
UUID_ARGS(&pin->bridge));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->stack_size) {
|
|
|
|
|
ds_put_cstr(s, " continuation.stack=(top)");
|
|
|
|
|
|
|
|
|
|
struct ofpbuf pin_stack;
|
|
|
|
|
ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size);
|
|
|
|
|
|
|
|
|
|
while (pin_stack.size) {
|
|
|
|
|
uint8_t len;
|
|
|
|
|
uint8_t *val = nx_stack_pop(&pin_stack, &len);
|
|
|
|
|
union mf_subvalue value;
|
|
|
|
|
|
|
|
|
|
ds_put_char(s, ' ');
|
|
|
|
|
memset(&value, 0, sizeof value - len);
|
|
|
|
|
memcpy(&value.u8[sizeof value - len], val, len);
|
|
|
|
|
mf_subvalue_format(&value, s);
|
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(s, " (bottom)\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->mirrors) {
|
|
|
|
|
ds_put_format(s, " continuation.mirrors=0x%"PRIx32"\n",
|
|
|
|
|
pin->mirrors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->conntracked) {
|
|
|
|
|
ds_put_cstr(s, " continuation.conntracked=true\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpact_format_params fp = {
|
|
|
|
|
.port_map = port_map,
|
|
|
|
|
.table_map = table_map,
|
|
|
|
|
.s = s,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (pin->actions_len) {
|
|
|
|
|
ds_put_cstr(s, " continuation.actions=");
|
|
|
|
|
ofpacts_format(pin->actions, pin->actions_len, &fp);
|
|
|
|
|
ds_put_char(s, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pin->action_set_len) {
|
|
|
|
|
ds_put_cstr(s, " continuation.action_set=");
|
|
|
|
|
ofpacts_format(pin->action_set, pin->action_set_len, &fp);
|
|
|
|
|
ds_put_char(s, '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verbosity > 0) {
|
|
|
|
|
char *packet = ofp_packet_to_string(
|
|
|
|
|
public->packet, public->packet_len,
|
|
|
|
|
public->flow_metadata.flow.packet_type);
|
|
|
|
|
ds_put_cstr(s, packet);
|
|
|
|
|
free(packet);
|
|
|
|
|
}
|
|
|
|
|
if (verbosity > 2) {
|
|
|
|
|
ds_put_hex_dump(s, public->packet, public->packet_len, 0, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-09 10:04:26 -08:00
|
|
|
|
/* Frees data in 'pin' that is dynamically allocated by
|
|
|
|
|
* ofputil_decode_packet_in_private().
|
|
|
|
|
*
|
|
|
|
|
* 'pin->base' contains some pointer members that
|
|
|
|
|
* ofputil_decode_packet_in_private() doesn't initialize to newly allocated
|
|
|
|
|
* data, so this function doesn't free those. */
|
|
|
|
|
void
|
|
|
|
|
ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin)
|
|
|
|
|
{
|
|
|
|
|
if (pin) {
|
|
|
|
|
free(pin->stack);
|
|
|
|
|
free(pin->actions);
|
|
|
|
|
free(pin->action_set);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
|
|
|
|
|
* 'po'.
|
|
|
|
|
*
|
|
|
|
|
* Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
|
|
|
|
|
* message's actions. The caller must initialize 'ofpacts' and retains
|
|
|
|
|
* ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
|
|
|
|
|
*
|
|
|
|
|
* 'po->packet' refers to the packet data in 'oh', so the buffer containing
|
|
|
|
|
* 'oh' must not be destroyed while 'po' is being used.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 if successful, otherwise an OFPERR_* value. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_packet_out(struct ofputil_packet_out *po,
|
|
|
|
|
const struct ofp_header *oh,
|
|
|
|
|
const struct tun_table *tun_table,
|
|
|
|
|
struct ofpbuf *ofpacts)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
|
|
|
|
enum ofpraw raw = ofpraw_pull_assert(&b);
|
|
|
|
|
|
|
|
|
|
ofpbuf_clear(ofpacts);
|
|
|
|
|
match_init_catchall(&po->flow_metadata);
|
|
|
|
|
if (raw == OFPRAW_OFPT15_PACKET_OUT) {
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
|
|
|
|
|
|
|
|
|
|
po->buffer_id = ntohl(opo->buffer_id);
|
|
|
|
|
error = oxm_pull_match_loose(&b, true, tun_table, &po->flow_metadata);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!po->flow_metadata.wc.masks.in_port.ofp_port) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_PORT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
|
|
|
|
|
oh->version, NULL, NULL,
|
|
|
|
|
ofpacts);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
} else if (raw == OFPRAW_OFPT11_PACKET_OUT) {
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
ofp_port_t in_port;
|
|
|
|
|
const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
|
|
|
|
|
|
|
|
|
|
po->buffer_id = ntohl(opo->buffer_id);
|
|
|
|
|
error = ofputil_port_from_ofp11(opo->in_port, &in_port);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
match_set_packet_type(&po->flow_metadata, htonl(PT_ETH));
|
|
|
|
|
match_set_in_port(&po->flow_metadata, in_port);
|
|
|
|
|
|
|
|
|
|
error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
|
|
|
|
|
oh->version, NULL, NULL,
|
|
|
|
|
ofpacts);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
} else if (raw == OFPRAW_OFPT10_PACKET_OUT) {
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
|
|
|
|
|
|
|
|
|
|
po->buffer_id = ntohl(opo->buffer_id);
|
|
|
|
|
match_set_packet_type(&po->flow_metadata, htonl(PT_ETH));
|
|
|
|
|
match_set_in_port(&po->flow_metadata, u16_to_ofp(ntohs(opo->in_port)));
|
|
|
|
|
|
|
|
|
|
error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
|
|
|
|
|
oh->version, NULL, NULL,
|
|
|
|
|
ofpacts);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
OVS_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofp_port_t in_port = po->flow_metadata.flow.in_port.ofp_port;
|
|
|
|
|
if (ofp_to_u16(in_port) >= ofp_to_u16(OFPP_MAX)
|
|
|
|
|
&& in_port != OFPP_LOCAL
|
|
|
|
|
&& in_port != OFPP_NONE
|
|
|
|
|
&& in_port != OFPP_CONTROLLER) {
|
|
|
|
|
VLOG_WARN_RL(&rl, "packet-out has bad input port %#"PRIx32,
|
|
|
|
|
po->flow_metadata.flow.in_port.ofp_port);
|
|
|
|
|
return OFPERR_OFPBRC_BAD_PORT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
po->ofpacts = ofpacts->data;
|
|
|
|
|
po->ofpacts_len = ofpacts->size;
|
|
|
|
|
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
po->packet = b.data;
|
|
|
|
|
po->packet_len = b.size;
|
|
|
|
|
} else {
|
|
|
|
|
po->packet = NULL;
|
|
|
|
|
po->packet_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_packet_out(const struct ofputil_packet_out *po,
|
|
|
|
|
enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
size = po->ofpacts_len;
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
size += po->packet_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ofp_version) {
|
|
|
|
|
case OFP10_VERSION: {
|
|
|
|
|
struct ofp10_packet_out *opo;
|
|
|
|
|
size_t actions_ofs;
|
|
|
|
|
|
|
|
|
|
msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size);
|
|
|
|
|
ofpbuf_put_zeros(msg, sizeof *opo);
|
|
|
|
|
actions_ofs = msg->size;
|
|
|
|
|
ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
|
|
|
|
|
ofp_version);
|
|
|
|
|
|
|
|
|
|
opo = msg->msg;
|
|
|
|
|
opo->buffer_id = htonl(po->buffer_id);
|
|
|
|
|
opo->in_port =htons(ofp_to_u16(
|
|
|
|
|
po->flow_metadata.flow.in_port.ofp_port));
|
|
|
|
|
opo->actions_len = htons(msg->size - actions_ofs);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OFP11_VERSION:
|
|
|
|
|
case OFP12_VERSION:
|
|
|
|
|
case OFP13_VERSION:
|
|
|
|
|
case OFP14_VERSION: {
|
|
|
|
|
struct ofp11_packet_out *opo;
|
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
|
|
msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size);
|
|
|
|
|
ofpbuf_put_zeros(msg, sizeof *opo);
|
|
|
|
|
len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
|
|
|
|
|
ofp_version);
|
|
|
|
|
opo = msg->msg;
|
|
|
|
|
opo->buffer_id = htonl(po->buffer_id);
|
|
|
|
|
opo->in_port =
|
|
|
|
|
ofputil_port_to_ofp11(po->flow_metadata.flow.in_port.ofp_port);
|
|
|
|
|
opo->actions_len = htons(len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OFP15_VERSION:
|
|
|
|
|
case OFP16_VERSION: {
|
|
|
|
|
struct ofp15_packet_out *opo;
|
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
|
|
/* The final argument is just an estimate of the space required. */
|
|
|
|
|
msg = ofpraw_alloc(OFPRAW_OFPT15_PACKET_OUT, ofp_version,
|
|
|
|
|
size + NXM_TYPICAL_LEN);
|
|
|
|
|
ofpbuf_put_zeros(msg, sizeof *opo);
|
|
|
|
|
oxm_put_match(msg, &po->flow_metadata, ofp_version);
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
ofpbuf_put(msg, po->packet, po->packet_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpmsg_update_length(msg);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
2018-02-16 14:03:51 -08:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ofputil_packet_out_format(struct ds *s, const struct ofputil_packet_out *po,
|
|
|
|
|
const struct ofputil_port_map *port_map,
|
|
|
|
|
const struct ofputil_table_map *table_map,
|
|
|
|
|
int verbosity)
|
|
|
|
|
{
|
|
|
|
|
ds_put_char(s, ' ');
|
|
|
|
|
match_format(&po->flow_metadata, port_map, s, OFP_DEFAULT_PRIORITY);
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(s, " actions=");
|
|
|
|
|
struct ofpact_format_params fp = {
|
|
|
|
|
.port_map = port_map,
|
|
|
|
|
.table_map = table_map,
|
|
|
|
|
.s = s,
|
|
|
|
|
};
|
|
|
|
|
ofpacts_format(po->ofpacts, po->ofpacts_len, &fp);
|
|
|
|
|
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
ds_put_format(s, " data_len=%"PRIuSIZE, po->packet_len);
|
|
|
|
|
if (verbosity > 0 && po->packet_len > 0) {
|
|
|
|
|
ovs_be32 po_packet_type = po->flow_metadata.flow.packet_type;
|
|
|
|
|
char *packet = ofp_packet_to_string(po->packet, po->packet_len,
|
|
|
|
|
po_packet_type);
|
|
|
|
|
ds_put_char(s, '\n');
|
|
|
|
|
ds_put_cstr(s, packet);
|
|
|
|
|
free(packet);
|
|
|
|
|
}
|
|
|
|
|
if (verbosity > 2) {
|
|
|
|
|
ds_put_hex_dump(s, po->packet, po->packet_len, 0, false);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(s, " buffer=0x%08"PRIx32, po->buffer_id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-09 10:04:26 -08:00
|
|
|
|
|
|
|
|
|
/* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful,
|
|
|
|
|
* both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
|
|
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
|
|
|
|
|
const struct ofputil_port_map *port_map,
|
|
|
|
|
const struct ofputil_table_map *table_map,
|
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol action_usable_protocols;
|
|
|
|
|
uint64_t stub[256 / 8];
|
|
|
|
|
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
|
|
|
|
|
struct dp_packet *packet = NULL;
|
|
|
|
|
char *act_str = NULL;
|
|
|
|
|
char *name, *value;
|
|
|
|
|
char *error = NULL;
|
|
|
|
|
|
|
|
|
|
*usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
|
|
|
|
|
*po = (struct ofputil_packet_out) {
|
|
|
|
|
.buffer_id = UINT32_MAX,
|
|
|
|
|
};
|
|
|
|
|
match_init_catchall(&po->flow_metadata);
|
|
|
|
|
match_set_in_port(&po->flow_metadata, OFPP_CONTROLLER);
|
|
|
|
|
|
|
|
|
|
act_str = ofp_extract_actions(string);
|
|
|
|
|
|
|
|
|
|
while (ofputil_parse_key_value(&string, &name, &value)) {
|
|
|
|
|
if (!*value) {
|
|
|
|
|
error = xasprintf("field %s missing value", name);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(name, "in_port")) {
|
|
|
|
|
ofp_port_t in_port;
|
|
|
|
|
if (!ofputil_port_from_string(value, port_map, &in_port)) {
|
|
|
|
|
error = xasprintf("%s is not a valid OpenFlow port", value);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (ofp_to_u16(in_port) > ofp_to_u16(OFPP_MAX)
|
|
|
|
|
&& in_port != OFPP_LOCAL
|
|
|
|
|
&& in_port != OFPP_NONE
|
|
|
|
|
&& in_port != OFPP_CONTROLLER) {
|
|
|
|
|
error = xasprintf(
|
|
|
|
|
"%s is not a valid OpenFlow port for PACKET_OUT",
|
|
|
|
|
value);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
match_set_in_port(&po->flow_metadata, in_port);
|
|
|
|
|
} else if (!strcmp(name, "packet_type")) {
|
|
|
|
|
char *ns = value;
|
|
|
|
|
char *ns_type = strstr(value, ",");
|
|
|
|
|
if (ns_type) {
|
|
|
|
|
ovs_be32 packet_type;
|
|
|
|
|
*ns_type = '\0';
|
|
|
|
|
packet_type = PACKET_TYPE_BE(strtoul(ns, NULL, 0),
|
|
|
|
|
strtoul(++ns_type, NULL, 0));
|
|
|
|
|
match_set_packet_type(&po->flow_metadata, packet_type);
|
|
|
|
|
} 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) {
|
|
|
|
|
error = xasprintf("%s: %s", name, error_msg);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const struct mf_field *mf = mf_from_name(name);
|
|
|
|
|
if (!mf) {
|
|
|
|
|
error = xasprintf("unknown keyword %s", name);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error = ofp_parse_field(mf, value, port_map, &po->flow_metadata,
|
|
|
|
|
usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (!mf_is_pipeline_field(mf)) {
|
|
|
|
|
error = xasprintf("%s is not a valid pipeline field "
|
|
|
|
|
"for PACKET_OUT", name);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!packet || !dp_packet_size(packet)) {
|
|
|
|
|
error = xstrdup("must specify packet");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (act_str) {
|
|
|
|
|
struct ofpact_parse_params pp = {
|
|
|
|
|
.port_map = port_map,
|
|
|
|
|
.table_map = table_map,
|
|
|
|
|
.ofpacts = &ofpacts,
|
|
|
|
|
.usable_protocols = &action_usable_protocols,
|
|
|
|
|
};
|
|
|
|
|
error = ofpacts_parse_actions(act_str, &pp);
|
|
|
|
|
*usable_protocols &= action_usable_protocols;
|
|
|
|
|
if (error) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
po->ofpacts_len = ofpacts.size;
|
|
|
|
|
po->ofpacts = ofpbuf_steal_data(&ofpacts);
|
|
|
|
|
|
|
|
|
|
po->packet_len = dp_packet_size(packet);
|
|
|
|
|
po->packet = dp_packet_steal_data(packet);
|
|
|
|
|
out:
|
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
|
dp_packet_delete(packet);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert 'str_' (as described in the Packet-Out Syntax section of the
|
|
|
|
|
* ovs-ofctl man page) into 'po' for sending a OFPT_PACKET_OUT message to a
|
|
|
|
|
* switch. Returns the set of usable protocols in '*usable_protocols'.
|
|
|
|
|
*
|
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
|
* error. The caller is responsible for freeing the returned string.
|
|
|
|
|
* If successful, both 'po->ofpacts' and 'po->packet' must be free()d by
|
|
|
|
|
* the caller. */
|
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
|
parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
|
|
|
|
|
const struct ofputil_port_map *port_map,
|
|
|
|
|
const struct ofputil_table_map *table_map,
|
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
|
{
|
|
|
|
|
char *string = xstrdup(str_);
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
error = parse_ofp_packet_out_str__(po, string, port_map, table_map,
|
|
|
|
|
usable_protocols);
|
|
|
|
|
if (error) {
|
|
|
|
|
po->ofpacts = NULL;
|
|
|
|
|
po->ofpacts_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(string);
|
|
|
|
|
return error;
|
|
|
|
|
}
|