2010-11-09 17:00:59 -08:00
|
|
|
|
/*
|
2014-03-10 23:50:54 -07:00
|
|
|
|
* Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
2010-11-09 17:00:59 -08:00
|
|
|
|
*
|
|
|
|
|
|
* 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 "nx-match.h"
|
|
|
|
|
|
|
2011-02-01 22:54:11 -08:00
|
|
|
|
#include <netinet/icmp6.h>
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "classifier.h"
|
|
|
|
|
|
#include "dynamic-string.h"
|
2014-09-16 22:13:44 -07:00
|
|
|
|
#include "hmap.h"
|
2011-09-12 12:11:50 -07:00
|
|
|
|
#include "meta-flow.h"
|
2012-07-03 22:17:14 -07:00
|
|
|
|
#include "ofp-actions.h"
|
2012-01-12 15:48:19 -08:00
|
|
|
|
#include "ofp-errors.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "ofp-util.h"
|
|
|
|
|
|
#include "ofpbuf.h"
|
|
|
|
|
|
#include "openflow/nicira-ext.h"
|
|
|
|
|
|
#include "packets.h"
|
2014-09-16 22:13:44 -07:00
|
|
|
|
#include "shash.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "unaligned.h"
|
2012-01-17 16:38:23 -08:00
|
|
|
|
#include "util.h"
|
2010-11-09 17:00:59 -08:00
|
|
|
|
#include "vlog.h"
|
|
|
|
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(nx_match);
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
/*
|
|
|
|
|
|
* OXM Class IDs.
|
|
|
|
|
|
* The high order bit differentiate reserved classes from member classes.
|
|
|
|
|
|
* Classes 0x0000 to 0x7FFF are member classes, allocated by ONF.
|
|
|
|
|
|
* Classes 0x8000 to 0xFFFE are reserved classes, reserved for standardisation.
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum ofp12_oxm_class {
|
|
|
|
|
|
OFPXMC12_NXM_0 = 0x0000, /* Backward compatibility with NXM */
|
|
|
|
|
|
OFPXMC12_NXM_1 = 0x0001, /* Backward compatibility with NXM */
|
|
|
|
|
|
OFPXMC12_OPENFLOW_BASIC = 0x8000, /* Basic class for OpenFlow */
|
|
|
|
|
|
OFPXMC15_PACKET_REGS = 0x8001, /* Packet registers (pipeline fields). */
|
|
|
|
|
|
OFPXMC12_EXPERIMENTER = 0xffff, /* Experimenter class */
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Functions for extracting fields from OXM/NXM headers. */
|
|
|
|
|
|
static int nxm_vendor(uint32_t header) { return header >> 16; }
|
|
|
|
|
|
static int nxm_field(uint32_t header) { return (header >> 9) & 0x7f; }
|
|
|
|
|
|
static bool nxm_hasmask(uint32_t header) { return (header & 0x100) != 0; }
|
|
|
|
|
|
static int nxm_length(uint32_t header) { return header & 0xff; }
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns true if 'header' is a legacy NXM header, false if it is an OXM
|
|
|
|
|
|
* header.*/
|
|
|
|
|
|
static bool
|
|
|
|
|
|
is_nxm_header(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
return nxm_vendor(header) <= 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define NXM_HEADER(VENDOR, FIELD, HASMASK, LENGTH) \
|
|
|
|
|
|
(((VENDOR) << 16) | ((FIELD) << 9) | ((HASMASK) << 8) | (LENGTH))
|
|
|
|
|
|
|
|
|
|
|
|
#define NXM_HEADER_FMT "%d:%d:%d:%d"
|
|
|
|
|
|
#define NXM_HEADER_ARGS(HEADER) \
|
|
|
|
|
|
nxm_vendor(HEADER), nxm_field(HEADER), \
|
|
|
|
|
|
nxm_hasmask(HEADER), nxm_length(HEADER)
|
|
|
|
|
|
|
|
|
|
|
|
/* Functions for turning the "hasmask" bit on or off. (This also requires
|
|
|
|
|
|
* adjusting the length.) */
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
|
nxm_make_exact_header(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
return NXM_HEADER(nxm_vendor(header), nxm_field(header), 0,
|
|
|
|
|
|
nxm_length(header) / 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
|
nxm_make_wild_header(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
return NXM_HEADER(nxm_vendor(header), nxm_field(header), 1,
|
|
|
|
|
|
nxm_length(header) * 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Flow cookie.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This may be used to gain the OpenFlow 1.1-like ability to restrict
|
|
|
|
|
|
* certain NXM-based Flow Mod and Flow Stats Request messages to flows
|
|
|
|
|
|
* with specific cookies. See the "nx_flow_mod" and "nx_flow_stats_request"
|
|
|
|
|
|
* structure definitions for more details. This match is otherwise not
|
|
|
|
|
|
* allowed. */
|
|
|
|
|
|
#define NXM_NX_COOKIE NXM_HEADER (0x0001, 30, 0, 8)
|
|
|
|
|
|
#define NXM_NX_COOKIE_W nxm_make_wild_header(NXM_NX_COOKIE)
|
|
|
|
|
|
|
|
|
|
|
|
struct nxm_field {
|
|
|
|
|
|
uint32_t header;
|
|
|
|
|
|
enum ofp_version version;
|
|
|
|
|
|
const char *name; /* e.g. "NXM_OF_IN_PORT". */
|
|
|
|
|
|
|
|
|
|
|
|
enum mf_field_id id;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *nxm_field_by_header(uint32_t header);
|
|
|
|
|
|
static const struct nxm_field *nxm_field_by_name(const char *name, size_t len);
|
|
|
|
|
|
static const struct nxm_field *nxm_field_by_mf_id(enum mf_field_id);
|
|
|
|
|
|
static const struct nxm_field *oxm_field_by_mf_id(enum mf_field_id);
|
|
|
|
|
|
|
2014-09-30 17:26:28 -07:00
|
|
|
|
static void nx_put_header__(struct ofpbuf *, uint32_t header, bool masked);
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
/* Rate limit for nx_match parse errors. These always indicate a bug in the
|
|
|
|
|
|
* peer and so there's not much point in showing a lot of them. */
|
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
mf_parse_subfield_name(const char *name, int name_len, bool *wild);
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
nxm_field_from_mf_field(enum mf_field_id id, enum ofp_version version)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct nxm_field *oxm = oxm_field_by_mf_id(id);
|
|
|
|
|
|
const struct nxm_field *nxm = nxm_field_by_mf_id(id);
|
|
|
|
|
|
return oxm && (version >= oxm->version || !nxm) ? oxm : nxm;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns the preferred OXM header to use for field 'id' in OpenFlow version
|
|
|
|
|
|
* 'version'. Specify 0 for 'version' if an NXM legacy header should be
|
|
|
|
|
|
* preferred over any standardized OXM header. Returns 0 if field 'id' cannot
|
|
|
|
|
|
* be expressed in NXM or OXM. */
|
|
|
|
|
|
uint32_t
|
|
|
|
|
|
mf_oxm_header(enum mf_field_id id, enum ofp_version version)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct nxm_field *f = nxm_field_from_mf_field(id, version);
|
|
|
|
|
|
return f ? f->header : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns the "struct mf_field" that corresponds to NXM or OXM header
|
|
|
|
|
|
* 'header', or NULL if 'header' doesn't correspond to any known field. */
|
|
|
|
|
|
const struct mf_field *
|
|
|
|
|
|
mf_from_nxm_header(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct nxm_field *f = nxm_field_by_header(header);
|
|
|
|
|
|
return f ? mf_from_id(f->id) : NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
/* Returns the width of the data for a field with the given 'header', in
|
|
|
|
|
|
* bytes. */
|
2014-09-16 22:13:44 -07:00
|
|
|
|
static int
|
2010-11-09 17:00:59 -08:00
|
|
|
|
nxm_field_bytes(uint32_t header)
|
|
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
unsigned int length = nxm_length(header);
|
|
|
|
|
|
return nxm_hasmask(header) ? length / 2 : length;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2010-11-11 10:41:33 -08:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
/* Returns the earliest version of OpenFlow that standardized an OXM header for
|
|
|
|
|
|
* field 'id', or UINT8_MAX if no version of OpenFlow does. */
|
|
|
|
|
|
static enum ofp_version
|
|
|
|
|
|
mf_oxm_version(enum mf_field_id id)
|
2010-11-11 10:41:33 -08:00
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
const struct nxm_field *oxm = oxm_field_by_mf_id(id);
|
|
|
|
|
|
return oxm ? oxm->version : UINT8_MAX;
|
2010-11-11 10:41:33 -08:00
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
|
2011-09-12 12:11:50 -07:00
|
|
|
|
/* nx_pull_match() and helpers. */
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
/* Given NXM/OXM value 'value' and mask 'mask' associated with 'header', checks
|
|
|
|
|
|
* for any 1-bit in the value where there is a 0-bit in the mask. Returns 0 if
|
|
|
|
|
|
* none, otherwise an error code. */
|
|
|
|
|
|
static bool
|
|
|
|
|
|
is_mask_consistent(uint32_t header, const uint8_t *value, const uint8_t *mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
unsigned int width = nxm_field_bytes(header);
|
|
|
|
|
|
unsigned int i;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
for (i = 0; i < width; i++) {
|
|
|
|
|
|
if (value[i] & ~mask[i]) {
|
|
|
|
|
|
if (!VLOG_DROP_WARN(&rl)) {
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "Rejecting NXM/OXM entry "NXM_HEADER_FMT " "
|
|
|
|
|
|
"with 1-bits in value for bits wildcarded by the "
|
|
|
|
|
|
"mask.", NXM_HEADER_ARGS(header));
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
static bool
|
|
|
|
|
|
is_cookie_pseudoheader(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
return header == NXM_NX_COOKIE || header == NXM_NX_COOKIE_W;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
|
nx_pull_header__(struct ofpbuf *b, bool allow_cookie, uint32_t *header,
|
|
|
|
|
|
const struct mf_field **field)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ofpbuf_size(b) < 4) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "encountered partial (%"PRIu32"-byte) OXM entry",
|
|
|
|
|
|
ofpbuf_size(b));
|
|
|
|
|
|
goto error;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
*header = ntohl(get_unaligned_be32(ofpbuf_pull(b, 4)));
|
|
|
|
|
|
if (nxm_length(*header) == 0) {
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "OXM header "NXM_HEADER_FMT" has zero length",
|
|
|
|
|
|
NXM_HEADER_ARGS(*header));
|
|
|
|
|
|
goto error;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (field) {
|
|
|
|
|
|
*field = mf_from_nxm_header(*header);
|
|
|
|
|
|
if (!*field && !(allow_cookie && is_cookie_pseudoheader(*header))) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" is unknown",
|
|
|
|
|
|
NXM_HEADER_ARGS(*header));
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_FIELD;
|
|
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
|
*header = 0;
|
|
|
|
|
|
*field = NULL;
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-04 13:34:37 -07:00
|
|
|
|
static enum ofperr
|
2014-09-16 22:13:44 -07:00
|
|
|
|
nx_pull_entry__(struct ofpbuf *b, bool allow_cookie, uint32_t *header,
|
|
|
|
|
|
const struct mf_field **field,
|
|
|
|
|
|
union mf_value *value, union mf_value *mask)
|
2012-10-03 09:48:57 -07:00
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
enum ofperr header_error;
|
|
|
|
|
|
unsigned int payload_len;
|
|
|
|
|
|
const uint8_t *payload;
|
|
|
|
|
|
int width;
|
2012-10-03 09:48:57 -07:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
header_error = nx_pull_header__(b, allow_cookie, header, field);
|
|
|
|
|
|
if (header_error && header_error != OFPERR_OFPBMC_BAD_FIELD) {
|
|
|
|
|
|
return header_error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
payload_len = nxm_length(*header);
|
|
|
|
|
|
payload = ofpbuf_try_pull(b, payload_len);
|
|
|
|
|
|
if (!payload) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" calls for %u-byte "
|
|
|
|
|
|
"payload but only %"PRIu32" bytes follow OXM header",
|
|
|
|
|
|
NXM_HEADER_ARGS(*header), payload_len, ofpbuf_size(b));
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
width = nxm_field_bytes(*header);
|
|
|
|
|
|
if (nxm_hasmask(*header)
|
|
|
|
|
|
&& !is_mask_consistent(*header, payload, payload + width)) {
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_WILDCARDS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(value, payload, MIN(width, sizeof *value));
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
|
if (nxm_hasmask(*header)) {
|
|
|
|
|
|
memcpy(mask, payload + width, MIN(width, sizeof *mask));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
memset(mask, 0xff, MIN(width, sizeof *mask));
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (nxm_hasmask(*header)) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "OXM header "NXM_HEADER_FMT" includes mask but "
|
|
|
|
|
|
"masked OXMs are not allowed here",
|
|
|
|
|
|
NXM_HEADER_ARGS(*header));
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_MASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return header_error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Attempts to pull an NXM or OXM header, value, and mask (if present) from the
|
|
|
|
|
|
* beginning of 'b'. If successful, stores a pointer to the "struct mf_field"
|
|
|
|
|
|
* corresponding to the pulled header in '*field', the value into '*value',
|
|
|
|
|
|
* and the mask into '*mask', and returns 0. On error, returns an OpenFlow
|
|
|
|
|
|
* error; in this case, some bytes might have been pulled off 'b' anyhow, and
|
|
|
|
|
|
* the output parameters might have been modified.
|
|
|
|
|
|
*
|
|
|
|
|
|
* If a NULL 'mask' is supplied, masked OXM or NXM entries are treated as
|
|
|
|
|
|
* errors (with OFPERR_OFPBMC_BAD_MASK).
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
|
nx_pull_entry(struct ofpbuf *b, const struct mf_field **field,
|
|
|
|
|
|
union mf_value *value, union mf_value *mask)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t header;
|
|
|
|
|
|
|
|
|
|
|
|
return nx_pull_entry__(b, false, &header, field, value, mask);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Attempts to pull an NXM or OXM header from the beginning of 'b'. If
|
|
|
|
|
|
* successful, stores a pointer to the "struct mf_field" corresponding to the
|
|
|
|
|
|
* pulled header in '*field', stores the header's hasmask bit in '*masked'
|
|
|
|
|
|
* (true if hasmask=1, false if hasmask=0), and returns 0. On error, returns
|
|
|
|
|
|
* an OpenFlow error; in this case, some bytes might have been pulled off 'b'
|
|
|
|
|
|
* anyhow, and the output parameters might have been modified.
|
|
|
|
|
|
*
|
|
|
|
|
|
* If NULL 'masked' is supplied, masked OXM or NXM headers are treated as
|
|
|
|
|
|
* errors (with OFPERR_OFPBMC_BAD_MASK).
|
|
|
|
|
|
*/
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
|
nx_pull_header(struct ofpbuf *b, const struct mf_field **field, bool *masked)
|
|
|
|
|
|
{
|
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
uint32_t header;
|
|
|
|
|
|
|
|
|
|
|
|
error = nx_pull_header__(b, false, &header, field);
|
|
|
|
|
|
if (masked) {
|
|
|
|
|
|
*masked = !error && nxm_hasmask(header);
|
|
|
|
|
|
} else if (!error && nxm_hasmask(header)) {
|
|
|
|
|
|
error = OFPERR_OFPBMC_BAD_MASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
return error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
|
nx_pull_match_entry(struct ofpbuf *b, bool allow_cookie,
|
|
|
|
|
|
const struct mf_field **field,
|
|
|
|
|
|
union mf_value *value, union mf_value *mask)
|
|
|
|
|
|
{
|
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
uint32_t header;
|
|
|
|
|
|
|
|
|
|
|
|
error = nx_pull_entry__(b, allow_cookie, &header, field, value, mask);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
return error;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (field && *field) {
|
|
|
|
|
|
if (!mf_is_mask_valid(*field, mask)) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "bad mask for field %s", (*field)->name);
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_MASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!mf_is_value_valid(*field, value)) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "bad value for field %s", (*field)->name);
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_VALUE;
|
2012-10-03 09:48:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-09-04 13:34:37 -07:00
|
|
|
|
return 0;
|
2012-10-03 09:48:57 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2012-08-01 16:01:45 +09:00
|
|
|
|
nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
struct ofpbuf b;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2012-11-06 13:14:55 -08:00
|
|
|
|
ovs_assert((cookie != NULL) == (cookie_mask != NULL));
|
2011-12-23 12:23:24 -08:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_init_catchall(match);
|
2012-07-19 00:15:19 -07:00
|
|
|
|
if (cookie) {
|
|
|
|
|
|
*cookie = *cookie_mask = htonll(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
ofpbuf_use_const(&b, p, match_len);
|
|
|
|
|
|
while (ofpbuf_size(&b)) {
|
|
|
|
|
|
const uint8_t *pos = ofpbuf_data(&b);
|
|
|
|
|
|
const struct mf_field *field;
|
|
|
|
|
|
union mf_value value;
|
|
|
|
|
|
union mf_value mask;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
error = nx_pull_match_entry(&b, cookie != NULL, &field, &value, &mask);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
if (error == OFPERR_OFPBMC_BAD_FIELD && !strict) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (!field) {
|
|
|
|
|
|
if (!cookie) {
|
2012-03-26 13:46:35 -07:00
|
|
|
|
error = OFPERR_OFPBMC_BAD_FIELD;
|
2014-09-16 22:13:44 -07:00
|
|
|
|
} else if (*cookie_mask) {
|
|
|
|
|
|
error = OFPERR_OFPBMC_DUP_FIELD;
|
2012-01-03 13:30:45 -08:00
|
|
|
|
} else {
|
2014-09-16 22:13:44 -07:00
|
|
|
|
*cookie = value.be64;
|
|
|
|
|
|
*cookie_mask = mask.be64;
|
2012-01-03 13:30:45 -08:00
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
} else if (!mf_are_prereqs_ok(field, &match->flow)) {
|
2012-03-26 13:46:35 -07:00
|
|
|
|
error = OFPERR_OFPBMC_BAD_PREREQ;
|
2014-09-16 22:13:44 -07:00
|
|
|
|
} else if (!mf_is_all_wild(field, &match->wc)) {
|
2012-03-26 13:46:35 -07:00
|
|
|
|
error = OFPERR_OFPBMC_DUP_FIELD;
|
2013-05-19 15:44:53 +03:00
|
|
|
|
} else {
|
2014-09-16 22:13:44 -07:00
|
|
|
|
mf_set(field, &value, &mask, match);
|
2011-12-23 12:23:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
if (error) {
|
2014-09-16 22:13:44 -07:00
|
|
|
|
VLOG_DBG_RL(&rl, "error parsing OXM at offset %"PRIdPTR" "
|
|
|
|
|
|
"within match (%s)", pos -
|
|
|
|
|
|
p, ofperr_to_string(error));
|
2010-11-09 17:00:59 -08:00
|
|
|
|
return error;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
return 0;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2012-01-03 13:30:45 -08:00
|
|
|
|
|
2012-08-01 16:01:45 +09:00
|
|
|
|
static enum ofperr
|
|
|
|
|
|
nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match *match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
ovs_be64 *cookie, ovs_be64 *cookie_mask)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t *p = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
if (match_len) {
|
|
|
|
|
|
p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
|
|
|
|
|
|
if (!p) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a "
|
|
|
|
|
|
"multiple of 8, is longer than space in message (max "
|
2014-03-30 01:31:50 -07:00
|
|
|
|
"length %"PRIu32")", match_len, ofpbuf_size(b));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-01-03 13:30:45 -08:00
|
|
|
|
/* Parses the nx_match formatted match description in 'b' with length
|
2012-08-07 15:28:18 -07:00
|
|
|
|
* 'match_len'. Stores the results in 'match'. If 'cookie' and 'cookie_mask'
|
|
|
|
|
|
* are valid pointers, then stores the cookie and mask in them if 'b' contains
|
|
|
|
|
|
* a "NXM_NX_COOKIE*" match. Otherwise, stores 0 in both.
|
2012-01-03 13:30:45 -08:00
|
|
|
|
*
|
2012-08-07 15:28:18 -07:00
|
|
|
|
* Fails with an error upon encountering an unknown NXM header.
|
2012-01-03 13:30:45 -08:00
|
|
|
|
*
|
|
|
|
|
|
* Returns 0 if successful, otherwise an OpenFlow error code. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2012-08-07 15:28:18 -07:00
|
|
|
|
nx_pull_match(struct ofpbuf *b, unsigned int match_len, struct match *match,
|
2012-01-03 13:30:45 -08:00
|
|
|
|
ovs_be64 *cookie, ovs_be64 *cookie_mask)
|
|
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
return nx_pull_match__(b, match_len, true, match, cookie, cookie_mask);
|
2012-01-03 13:30:45 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Behaves the same as nx_pull_match(), but skips over unknown NXM headers,
|
|
|
|
|
|
* instead of failing with an error. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2012-01-03 13:30:45 -08:00
|
|
|
|
nx_pull_match_loose(struct ofpbuf *b, unsigned int match_len,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match *match,
|
2012-01-03 13:30:45 -08:00
|
|
|
|
ovs_be64 *cookie, ovs_be64 *cookie_mask)
|
|
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
return nx_pull_match__(b, match_len, false, match, cookie, cookie_mask);
|
2012-01-03 13:30:45 -08:00
|
|
|
|
}
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
2012-08-07 15:28:18 -07:00
|
|
|
|
oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match)
|
2012-08-01 16:01:45 +09:00
|
|
|
|
{
|
2014-03-30 01:31:50 -07:00
|
|
|
|
struct ofp11_match_header *omh = ofpbuf_data(b);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
uint16_t match_len;
|
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(b) < sizeof *omh) {
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match_len = ntohs(omh->length);
|
|
|
|
|
|
if (match_len < sizeof *omh) {
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (omh->type != htons(OFPMT_OXM)) {
|
|
|
|
|
|
return OFPERR_OFPBMC_BAD_TYPE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8));
|
|
|
|
|
|
if (!p) {
|
|
|
|
|
|
VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a "
|
|
|
|
|
|
"multiple of 8, is longer than space in message (max "
|
2014-03-30 01:31:50 -07:00
|
|
|
|
"length %"PRIu32")", match_len, ofpbuf_size(b));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return OFPERR_OFPBMC_BAD_LEN;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
strict, match, NULL, NULL);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-10-16 17:26:19 +09:00
|
|
|
|
/* Parses the oxm formatted match description preceded by a struct
|
|
|
|
|
|
* ofp11_match_header in 'b'. Stores the result in 'match'.
|
2012-08-01 16:01:45 +09:00
|
|
|
|
*
|
|
|
|
|
|
* Fails with an error when encountering unknown OXM headers.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns 0 if successful, otherwise an OpenFlow error code. */
|
|
|
|
|
|
enum ofperr
|
2012-08-07 15:28:18 -07:00
|
|
|
|
oxm_pull_match(struct ofpbuf *b, struct match *match)
|
2012-08-01 16:01:45 +09:00
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
return oxm_pull_match__(b, true, match);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Behaves the same as oxm_pull_match() with one exception. Skips over unknown
|
2013-10-16 17:26:19 +09:00
|
|
|
|
* OXM headers instead of failing with an error when they are encountered. */
|
2012-08-01 16:01:45 +09:00
|
|
|
|
enum ofperr
|
2012-08-07 15:28:18 -07:00
|
|
|
|
oxm_pull_match_loose(struct ofpbuf *b, struct match *match)
|
2012-08-01 16:01:45 +09:00
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
return oxm_pull_match__(b, false, match);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
|
/* nx_put_match() and helpers.
|
|
|
|
|
|
*
|
|
|
|
|
|
* 'put' functions whose names end in 'w' add a wildcarded field.
|
|
|
|
|
|
* 'put' functions whose names end in 'm' add a field that might be wildcarded.
|
|
|
|
|
|
* Other 'put' functions add exact-match fields.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_unmasked(struct ofpbuf *b, enum mf_field_id field,
|
|
|
|
|
|
enum ofp_version version, const void *value, size_t n_bytes)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nx_put_header(b, field, version, false);
|
|
|
|
|
|
ofpbuf_put(b, value, n_bytes);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
const void *value, const void *mask, size_t n_bytes)
|
2010-11-23 10:06:28 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
if (!is_all_zeros(mask, n_bytes)) {
|
|
|
|
|
|
bool masked = !is_all_ones(mask, n_bytes);
|
|
|
|
|
|
nx_put_header(b, field, version, masked);
|
|
|
|
|
|
ofpbuf_put(b, value, n_bytes);
|
|
|
|
|
|
if (masked) {
|
|
|
|
|
|
ofpbuf_put(b, mask, n_bytes);
|
|
|
|
|
|
}
|
2010-11-23 10:06:28 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
uint8_t value, uint8_t mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, &value, &mask, sizeof value);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
uint8_t value)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_unmasked(b, field, version, &value, sizeof value);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
ovs_be16 value, ovs_be16 mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, &value, &mask, sizeof value);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
ovs_be16 value)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_unmasked(b, field, version, &value, sizeof value);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2011-01-20 15:29:00 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
ovs_be32 value, ovs_be32 mask)
|
2011-01-20 15:29:00 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, &value, &mask, sizeof value);
|
2011-01-20 15:29:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
ovs_be32 value)
|
2011-01-20 15:29:00 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_unmasked(b, field, version, &value, sizeof value);
|
2011-01-20 15:29:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_64m(struct ofpbuf *b, enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
ovs_be64 value, ovs_be64 mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, &value, &mask, sizeof value);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-11 10:46:23 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(struct ofpbuf *b,
|
|
|
|
|
|
enum mf_field_id field, enum ofp_version version,
|
2012-05-29 00:38:21 +12:00
|
|
|
|
const uint8_t value[ETH_ADDR_LEN],
|
|
|
|
|
|
const uint8_t mask[ETH_ADDR_LEN])
|
2010-11-11 10:46:23 -08:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, value, mask, ETH_ADDR_LEN);
|
2010-11-11 10:46:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-29 19:03:46 -08:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_ipv6(struct ofpbuf *b,
|
|
|
|
|
|
enum mf_field_id field, enum ofp_version version,
|
2010-12-29 19:03:46 -08:00
|
|
|
|
const struct in6_addr *value, const struct in6_addr *mask)
|
|
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put(b, field, version, value->s6_addr, mask->s6_addr,
|
|
|
|
|
|
sizeof value->s6_addr);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static void
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_frag(struct ofpbuf *b, const struct match *match,
|
|
|
|
|
|
enum ofp_version version)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
uint8_t nw_frag = match->flow.nw_frag & FLOW_NW_FRAG_MASK;
|
|
|
|
|
|
uint8_t nw_frag_mask = match->wc.masks.nw_frag & FLOW_NW_FRAG_MASK;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8m(b, MFF_IP_FRAG, version, nw_frag,
|
|
|
|
|
|
nw_frag_mask == FLOW_NW_FRAG_MASK ? UINT8_MAX : nw_frag_mask);
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-09 18:16:38 -07:00
|
|
|
|
/* Appends to 'b' a set of OXM or NXM matches for the IPv4 or IPv6 fields in
|
|
|
|
|
|
* 'match'. */
|
2012-02-02 16:44:44 -08:00
|
|
|
|
static void
|
2014-05-09 18:18:17 -07:00
|
|
|
|
nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
|
2012-02-02 16:44:44 -08:00
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
const struct flow *flow = &match->flow;
|
2012-02-02 16:44:44 -08:00
|
|
|
|
|
2014-05-09 18:16:38 -07:00
|
|
|
|
if (flow->dl_type == htons(ETH_TYPE_IP)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_IPV4_SRC, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
flow->nw_src, match->wc.masks.nw_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_IPV4_DST, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
flow->nw_dst, match->wc.masks.nw_dst);
|
|
|
|
|
|
} else {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_ipv6(b, MFF_IPV6_SRC, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
&flow->ipv6_src, &match->wc.masks.ipv6_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_ipv6(b, MFF_IPV6_DST, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
&flow->ipv6_dst, &match->wc.masks.ipv6_dst);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-02 16:47:01 -07:00
|
|
|
|
nxm_put_frag(b, match, oxm);
|
2012-02-02 16:44:44 -08:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.nw_tos & IP_DSCP_MASK) {
|
2013-04-18 09:40:48 -07:00
|
|
|
|
if (oxm) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_IP_DSCP_SHIFTED, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
flow->nw_tos >> 2);
|
2013-04-18 09:40:48 -07:00
|
|
|
|
} else {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_IP_DSCP, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
flow->nw_tos & IP_DSCP_MASK);
|
2013-04-18 09:40:48 -07:00
|
|
|
|
}
|
2012-02-02 16:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.nw_tos & IP_ECN_MASK) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_IP_ECN, oxm,
|
2012-06-11 09:56:12 -07:00
|
|
|
|
flow->nw_tos & IP_ECN_MASK);
|
2012-02-02 16:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (!oxm && match->wc.masks.nw_ttl) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_IP_TTL, oxm, flow->nw_ttl);
|
2012-02-02 16:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_IPV6_LABEL, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
flow->ipv6_label, match->wc.masks.ipv6_label);
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.nw_proto) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_IP_PROTO, oxm, flow->nw_proto);
|
2012-02-02 16:44:44 -08:00
|
|
|
|
|
|
|
|
|
|
if (flow->nw_proto == IPPROTO_TCP) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_TCP_SRC, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->tp_src, match->wc.masks.tp_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_TCP_DST, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->tp_dst, match->wc.masks.tp_dst);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_TCP_FLAGS, oxm,
|
2013-10-28 13:54:40 -07:00
|
|
|
|
flow->tcp_flags, match->wc.masks.tcp_flags);
|
2012-02-02 16:44:44 -08:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_UDP) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_UDP_SRC, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->tp_src, match->wc.masks.tp_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_UDP_DST, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->tp_dst, match->wc.masks.tp_dst);
|
2013-08-22 20:24:45 +12:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_SCTP) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_SCTP_SRC, oxm, flow->tp_src,
|
2013-08-22 20:24:45 +12:00
|
|
|
|
match->wc.masks.tp_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_SCTP_DST, oxm, flow->tp_dst,
|
2013-08-22 20:24:45 +12:00
|
|
|
|
match->wc.masks.tp_dst);
|
2014-05-09 18:16:38 -07:00
|
|
|
|
} else if (is_icmpv4(flow)) {
|
|
|
|
|
|
if (match->wc.masks.tp_src) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_ICMPV4_TYPE, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
ntohs(flow->tp_src));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (match->wc.masks.tp_dst) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_ICMPV4_CODE, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
ntohs(flow->tp_dst));
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (is_icmpv6(flow)) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.tp_src) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_ICMPV6_TYPE, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
ntohs(flow->tp_src));
|
2012-02-02 16:44:44 -08:00
|
|
|
|
}
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.tp_dst) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_ICMPV6_CODE, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
ntohs(flow->tp_dst));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
|
|
|
|
|
|
flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_ipv6(b, MFF_ND_TARGET, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
&flow->nd_target, &match->wc.masks.nd_target);
|
|
|
|
|
|
if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ND_SLL, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
flow->arp_sha, match->wc.masks.arp_sha);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (flow->tp_src == htons(ND_NEIGHBOR_ADVERT)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ND_TLL, oxm,
|
2014-05-09 18:16:38 -07:00
|
|
|
|
flow->arp_tha, match->wc.masks.arp_tha);
|
|
|
|
|
|
}
|
2012-02-02 16:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Appends to 'b' the nx_match format that expresses 'match'. For Flow Mod and
|
2012-08-01 16:01:45 +09:00
|
|
|
|
* Flow Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
|
|
|
|
|
|
* Otherwise, 'cookie_mask' should be zero.
|
2010-12-15 09:48:16 -08:00
|
|
|
|
*
|
2014-05-09 18:18:17 -07:00
|
|
|
|
* Specify 'oxm' as 0 to express the match in NXM format; otherwise, specify
|
|
|
|
|
|
* 'oxm' as the OpenFlow version number for the OXM format to use.
|
|
|
|
|
|
*
|
2010-12-15 09:48:16 -08:00
|
|
|
|
* This function can cause 'b''s data to be reallocated.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns the number of bytes appended to 'b', excluding padding.
|
|
|
|
|
|
*
|
2012-08-07 15:28:18 -07:00
|
|
|
|
* If 'match' is a catch-all rule that matches every packet, then this function
|
2010-12-15 09:48:16 -08:00
|
|
|
|
* appends nothing to 'b' and returns 0. */
|
2012-08-01 16:01:45 +09:00
|
|
|
|
static int
|
2014-05-09 18:18:17 -07:00
|
|
|
|
nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
ovs_be64 cookie, ovs_be64 cookie_mask)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2012-08-07 15:28:18 -07:00
|
|
|
|
const struct flow *flow = &match->flow;
|
2014-03-30 01:31:50 -07:00
|
|
|
|
const size_t start_len = ofpbuf_size(b);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
int match_len;
|
2010-11-11 10:41:33 -08:00
|
|
|
|
int i;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2014-06-18 22:14:30 -03:00
|
|
|
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);
|
2011-07-29 13:15:09 -07:00
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
/* Metadata. */
|
2014-03-04 14:20:19 -08:00
|
|
|
|
if (match->wc.masks.dp_hash) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_DP_HASH, oxm,
|
2014-09-12 14:42:47 -07:00
|
|
|
|
htonl(flow->dp_hash), htonl(match->wc.masks.dp_hash));
|
2014-03-04 14:20:19 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (match->wc.masks.recirc_id) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32(b, MFF_RECIRC_ID, oxm, htonl(flow->recirc_id));
|
2014-03-04 14:20:19 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-06-19 16:58:44 -07:00
|
|
|
|
if (match->wc.masks.in_port.ofp_port) {
|
|
|
|
|
|
ofp_port_t in_port = flow->in_port.ofp_port;
|
2012-06-11 09:56:12 -07:00
|
|
|
|
if (oxm) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32(b, MFF_IN_PORT_OXM, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
ofputil_port_to_ofp11(in_port));
|
2012-06-11 09:56:12 -07:00
|
|
|
|
} else {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16(b, MFF_IN_PORT, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
htons(ofp_to_u16(in_port)));
|
2012-06-11 09:56:12 -07:00
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Ethernet. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ETH_SRC, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->dl_src, match->wc.masks.dl_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ETH_DST, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->dl_dst, match->wc.masks.dl_dst);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_ETH_TYPE, oxm,
|
2012-06-18 13:33:13 -07:00
|
|
|
|
ofputil_dl_type_to_openflow(flow->dl_type),
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match->wc.masks.dl_type);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2012-07-23 11:36:43 +09:00
|
|
|
|
/* 802.1Q. */
|
|
|
|
|
|
if (oxm) {
|
2012-08-07 13:38:38 -07:00
|
|
|
|
ovs_be16 VID_CFI_MASK = htons(VLAN_VID_MASK | VLAN_CFI);
|
|
|
|
|
|
ovs_be16 vid = flow->vlan_tci & VID_CFI_MASK;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
ovs_be16 mask = match->wc.masks.vlan_tci & VID_CFI_MASK;
|
2012-07-23 11:36:43 +09:00
|
|
|
|
|
|
|
|
|
|
if (mask == htons(VLAN_VID_MASK | VLAN_CFI)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16(b, MFF_VLAN_VID, oxm, vid);
|
2012-07-23 11:36:43 +09:00
|
|
|
|
} else if (mask) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_VLAN_VID, oxm, vid, mask);
|
2012-07-23 11:36:43 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (vid && vlan_tci_to_pcp(match->wc.masks.vlan_tci)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_VLAN_PCP, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
vlan_tci_to_pcp(flow->vlan_tci));
|
2012-07-23 11:36:43 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16m(b, MFF_VLAN_TCI, oxm, flow->vlan_tci,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match->wc.masks.vlan_tci);
|
2012-07-23 11:36:43 +09:00
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2013-01-25 16:22:07 +09:00
|
|
|
|
/* MPLS. */
|
|
|
|
|
|
if (eth_type_mpls(flow->dl_type)) {
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (match->wc.masks.mpls_lse[0] & htonl(MPLS_TC_MASK)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_MPLS_TC, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
mpls_lse_to_tc(flow->mpls_lse[0]));
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (match->wc.masks.mpls_lse[0] & htonl(MPLS_BOS_MASK)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_8(b, MFF_MPLS_BOS, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
mpls_lse_to_bos(flow->mpls_lse[0]));
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (match->wc.masks.mpls_lse[0] & htonl(MPLS_LABEL_MASK)) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32(b, MFF_MPLS_LABEL, oxm,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
htonl(mpls_lse_to_label(flow->mpls_lse[0])));
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
/* L3. */
|
2014-05-09 18:16:38 -07:00
|
|
|
|
if (is_ip_any(flow)) {
|
|
|
|
|
|
nxm_put_ip(b, match, oxm);
|
2012-11-02 11:43:46 -07:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
|
|
|
|
|
|
flow->dl_type == htons(ETH_TYPE_RARP)) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
/* ARP. */
|
2012-08-07 15:28:18 -07:00
|
|
|
|
if (match->wc.masks.nw_proto) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_16(b, MFF_ARP_OP, oxm,
|
2012-06-11 09:56:12 -07:00
|
|
|
|
htons(flow->nw_proto));
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_ARP_SPA, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->nw_src, match->wc.masks.nw_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_ARP_TPA, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->nw_dst, match->wc.masks.nw_dst);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ARP_SHA, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->arp_sha, match->wc.masks.arp_sha);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_eth_masked(b, MFF_ARP_THA, oxm,
|
2012-08-07 15:28:18 -07:00
|
|
|
|
flow->arp_tha, match->wc.masks.arp_tha);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Tunnel ID. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_64m(b, MFF_TUN_ID, oxm,
|
2013-05-09 15:24:16 +03:00
|
|
|
|
flow->tunnel.tun_id, match->wc.masks.tunnel.tun_id);
|
|
|
|
|
|
|
|
|
|
|
|
/* Other tunnel metadata. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_TUN_SRC, oxm,
|
2013-05-09 15:24:16 +03:00
|
|
|
|
flow->tunnel.ip_src, match->wc.masks.tunnel.ip_src);
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_TUN_DST, oxm,
|
2013-05-09 15:24:16 +03:00
|
|
|
|
flow->tunnel.ip_dst, match->wc.masks.tunnel.ip_dst);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2010-11-11 10:41:33 -08:00
|
|
|
|
/* Registers. */
|
2014-08-20 20:59:43 -07:00
|
|
|
|
if (oxm < OFP15_VERSION) {
|
|
|
|
|
|
for (i = 0; i < FLOW_N_REGS; i++) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_REG0 + i, oxm,
|
2014-08-20 20:59:43 -07:00
|
|
|
|
htonl(flow->regs[i]), htonl(match->wc.masks.regs[i]));
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
for (i = 0; i < FLOW_N_XREGS; i++) {
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_64m(b, MFF_XREG0 + i, oxm,
|
2014-08-20 20:59:43 -07:00
|
|
|
|
htonll(flow_get_xreg(flow, i)),
|
|
|
|
|
|
htonll(flow_get_xreg(&match->wc.masks, i)));
|
|
|
|
|
|
}
|
2010-11-11 10:41:33 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-08-06 12:57:16 -07:00
|
|
|
|
/* Mark. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_32m(b, MFF_PKT_MARK, oxm, htonl(flow->pkt_mark),
|
2013-08-06 12:57:16 -07:00
|
|
|
|
htonl(match->wc.masks.pkt_mark));
|
|
|
|
|
|
|
2012-06-27 01:09:44 +12:00
|
|
|
|
/* OpenFlow 1.1+ Metadata. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nxm_put_64m(b, MFF_METADATA, oxm,
|
2014-05-09 18:18:17 -07:00
|
|
|
|
flow->metadata, match->wc.masks.metadata);
|
2012-06-27 01:09:44 +12:00
|
|
|
|
|
2011-12-23 12:23:24 -08:00
|
|
|
|
/* Cookie. */
|
2014-09-30 17:26:28 -07:00
|
|
|
|
if (cookie_mask) {
|
|
|
|
|
|
bool masked = cookie_mask != OVS_BE64_MAX;
|
|
|
|
|
|
|
|
|
|
|
|
cookie &= cookie_mask;
|
|
|
|
|
|
nx_put_header__(b, NXM_NX_COOKIE, masked);
|
|
|
|
|
|
ofpbuf_put(b, &cookie, sizeof cookie);
|
|
|
|
|
|
if (masked) {
|
|
|
|
|
|
ofpbuf_put(b, &cookie_mask, sizeof cookie_mask);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2011-12-23 12:23:24 -08:00
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
match_len = ofpbuf_size(b) - start_len;
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return match_len;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
/* Appends to 'b' the nx_match format that expresses 'match', plus enough zero
|
2012-08-01 16:01:45 +09:00
|
|
|
|
* bytes to pad the nx_match out to a multiple of 8. For Flow Mod and Flow
|
|
|
|
|
|
* Stats Requests messages, a 'cookie' and 'cookie_mask' may be supplied.
|
|
|
|
|
|
* Otherwise, 'cookie_mask' should be zero.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This function can cause 'b''s data to be reallocated.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns the number of bytes appended to 'b', excluding padding. The return
|
|
|
|
|
|
* value can be zero if it appended nothing at all to 'b' (which happens if
|
|
|
|
|
|
* 'cr' is a catch-all rule that matches every packet). */
|
|
|
|
|
|
int
|
2012-08-07 15:28:18 -07:00
|
|
|
|
nx_put_match(struct ofpbuf *b, const struct match *match,
|
2012-08-01 16:01:45 +09:00
|
|
|
|
ovs_be64 cookie, ovs_be64 cookie_mask)
|
|
|
|
|
|
{
|
2014-05-09 18:18:17 -07:00
|
|
|
|
int match_len = nx_put_raw(b, 0, match, cookie, cookie_mask);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
2014-03-10 23:50:54 -07:00
|
|
|
|
ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return match_len;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-05-09 18:18:17 -07:00
|
|
|
|
/* Appends to 'b' an struct ofp11_match_header followed by the OXM format that
|
2012-08-07 15:28:18 -07:00
|
|
|
|
* expresses 'cr', plus enough zero bytes to pad the data appended out to a
|
|
|
|
|
|
* multiple of 8.
|
2012-08-01 16:01:45 +09:00
|
|
|
|
*
|
2014-05-09 18:18:17 -07:00
|
|
|
|
* OXM differs slightly among versions of OpenFlow. Specify the OpenFlow
|
|
|
|
|
|
* version in use as 'version'.
|
|
|
|
|
|
*
|
2012-08-01 16:01:45 +09:00
|
|
|
|
* This function can cause 'b''s data to be reallocated.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns the number of bytes appended to 'b', excluding the padding. Never
|
|
|
|
|
|
* returns zero. */
|
|
|
|
|
|
int
|
2014-05-09 18:18:17 -07:00
|
|
|
|
oxm_put_match(struct ofpbuf *b, const struct match *match,
|
|
|
|
|
|
enum ofp_version version)
|
2012-08-01 16:01:45 +09:00
|
|
|
|
{
|
|
|
|
|
|
int match_len;
|
|
|
|
|
|
struct ofp11_match_header *omh;
|
2014-03-30 01:31:50 -07:00
|
|
|
|
size_t start_len = ofpbuf_size(b);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
ovs_be64 cookie = htonll(0), cookie_mask = htonll(0);
|
|
|
|
|
|
|
|
|
|
|
|
ofpbuf_put_uninit(b, sizeof *omh);
|
2014-05-09 18:18:17 -07:00
|
|
|
|
match_len = (nx_put_raw(b, version, match, cookie, cookie_mask)
|
|
|
|
|
|
+ sizeof *omh);
|
2014-03-10 23:50:54 -07:00
|
|
|
|
ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
2013-07-22 15:47:19 -07:00
|
|
|
|
omh = ofpbuf_at(b, start_len, sizeof *omh);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
omh->type = htons(OFPMT_OXM);
|
|
|
|
|
|
omh->length = htons(match_len);
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
return match_len;
|
|
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
|
2014-09-30 17:26:28 -07:00
|
|
|
|
static void
|
|
|
|
|
|
nx_put_header__(struct ofpbuf *b, uint32_t header, bool masked)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t masked_header = masked ? nxm_make_wild_header(header) : header;
|
|
|
|
|
|
ovs_be32 network_header = htonl(masked_header);
|
|
|
|
|
|
|
|
|
|
|
|
ofpbuf_put(b, &network_header, sizeof network_header);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
void
|
|
|
|
|
|
nx_put_header(struct ofpbuf *b, enum mf_field_id field,
|
|
|
|
|
|
enum ofp_version version, bool masked)
|
|
|
|
|
|
{
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nx_put_header__(b, mf_oxm_header(field, version), masked);
|
2014-09-16 22:13:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
nx_put_entry(struct ofpbuf *b,
|
|
|
|
|
|
enum mf_field_id field, enum ofp_version version,
|
|
|
|
|
|
const union mf_value *value, const union mf_value *mask)
|
|
|
|
|
|
{
|
|
|
|
|
|
int n_bytes = mf_from_id(field)->n_bytes;
|
|
|
|
|
|
bool masked = mask && !is_all_ones(mask, n_bytes);
|
|
|
|
|
|
|
|
|
|
|
|
nx_put_header(b, field, version, masked);
|
|
|
|
|
|
ofpbuf_put(b, value, n_bytes);
|
|
|
|
|
|
if (masked) {
|
|
|
|
|
|
ofpbuf_put(b, mask, n_bytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
|
/* nx_match_to_string() and helpers. */
|
|
|
|
|
|
|
2010-12-09 11:03:35 -08:00
|
|
|
|
static void format_nxm_field_name(struct ds *, uint32_t header);
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
char *
|
|
|
|
|
|
nx_match_to_string(const uint8_t *p, unsigned int match_len)
|
|
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
struct ofpbuf b;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
|
|
|
|
if (!match_len) {
|
|
|
|
|
|
return xstrdup("<any>");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
ofpbuf_use_const(&b, p, match_len);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ds_init(&s);
|
2014-09-16 22:13:44 -07:00
|
|
|
|
while (ofpbuf_size(&b)) {
|
|
|
|
|
|
union mf_value value;
|
|
|
|
|
|
union mf_value mask;
|
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
uint32_t header;
|
|
|
|
|
|
int value_len;
|
|
|
|
|
|
|
|
|
|
|
|
error = nx_pull_entry__(&b, true, &header, NULL, &value, &mask);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
value_len = MIN(sizeof value, nxm_field_bytes(header));
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
|
if (s.length) {
|
|
|
|
|
|
ds_put_cstr(&s, ", ");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-09 11:03:35 -08:00
|
|
|
|
format_nxm_field_name(&s, header);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ds_put_char(&s, '(');
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
for (int i = 0; i < value_len; i++) {
|
|
|
|
|
|
ds_put_format(&s, "%02x", ((const uint8_t *) &value)[i]);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
if (nxm_hasmask(header)) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ds_put_char(&s, '/');
|
2014-09-16 22:13:44 -07:00
|
|
|
|
for (int i = 0; i < value_len; i++) {
|
|
|
|
|
|
ds_put_format(&s, "%02x", ((const uint8_t *) &mask)[i]);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ds_put_char(&s, ')');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
if (ofpbuf_size(&b)) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
if (s.length) {
|
|
|
|
|
|
ds_put_cstr(&s, ", ");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
ds_put_format(&s, "<%u invalid bytes>", ofpbuf_size(&b));
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ds_steal_cstr(&s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2012-08-01 16:01:45 +09:00
|
|
|
|
char *
|
2013-07-22 15:47:19 -07:00
|
|
|
|
oxm_match_to_string(const struct ofpbuf *p, unsigned int match_len)
|
2012-08-01 16:01:45 +09:00
|
|
|
|
{
|
2014-03-30 01:31:50 -07:00
|
|
|
|
const struct ofp11_match_header *omh = ofpbuf_data(p);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
uint16_t match_len_;
|
|
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
|
|
|
|
|
|
|
|
|
|
|
if (match_len < sizeof *omh) {
|
|
|
|
|
|
ds_put_format(&s, "<match too short: %u>", match_len);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (omh->type != htons(OFPMT_OXM)) {
|
|
|
|
|
|
ds_put_format(&s, "<bad match type field: %u>", ntohs(omh->type));
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match_len_ = ntohs(omh->length);
|
|
|
|
|
|
if (match_len_ < sizeof *omh) {
|
|
|
|
|
|
ds_put_format(&s, "<match length field too short: %u>", match_len_);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (match_len_ != match_len) {
|
|
|
|
|
|
ds_put_format(&s, "<match length field incorrect: %u != %u>",
|
|
|
|
|
|
match_len_, match_len);
|
|
|
|
|
|
goto err;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-22 15:47:19 -07:00
|
|
|
|
return nx_match_to_string(ofpbuf_at(p, sizeof *omh, 0),
|
|
|
|
|
|
match_len - sizeof *omh);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
|
return ds_steal_cstr(&s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
void
|
|
|
|
|
|
nx_format_field_name(enum mf_field_id id, enum ofp_version version,
|
|
|
|
|
|
struct ds *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
format_nxm_field_name(s, mf_oxm_header(id, version));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-09 11:03:35 -08:00
|
|
|
|
static void
|
|
|
|
|
|
format_nxm_field_name(struct ds *s, uint32_t header)
|
|
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
const struct nxm_field *f = nxm_field_by_header(header);
|
|
|
|
|
|
if (f) {
|
|
|
|
|
|
ds_put_cstr(s, f->name);
|
|
|
|
|
|
if (nxm_hasmask(header)) {
|
2011-10-12 17:04:13 -07:00
|
|
|
|
ds_put_cstr(s, "_W");
|
|
|
|
|
|
}
|
2011-12-23 12:23:24 -08:00
|
|
|
|
} else if (header == NXM_NX_COOKIE) {
|
|
|
|
|
|
ds_put_cstr(s, "NXM_NX_COOKIE");
|
|
|
|
|
|
} else if (header == NXM_NX_COOKIE_W) {
|
|
|
|
|
|
ds_put_cstr(s, "NXM_NX_COOKIE_W");
|
2010-12-09 11:03:35 -08:00
|
|
|
|
} else {
|
2014-09-16 22:13:44 -07:00
|
|
|
|
ds_put_format(s, "%d:%d", nxm_vendor(header), nxm_field(header));
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
static bool
|
|
|
|
|
|
streq_len(const char *a, size_t a_len, const char *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
return strlen(b) == a_len && !memcmp(a, b, a_len);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-07 11:04:15 -08:00
|
|
|
|
static uint32_t
|
|
|
|
|
|
parse_nxm_field_name(const char *name, int name_len)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
2014-09-16 22:13:44 -07:00
|
|
|
|
const struct nxm_field *f;
|
2011-10-12 17:04:13 -07:00
|
|
|
|
bool wild;
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
f = mf_parse_subfield_name(name, name_len, &wild);
|
|
|
|
|
|
if (f) {
|
2012-06-11 09:56:12 -07:00
|
|
|
|
if (!wild) {
|
2014-09-16 22:13:44 -07:00
|
|
|
|
return f->header;
|
|
|
|
|
|
} else if (mf_from_id(f->id)->maskable != MFM_NONE) {
|
|
|
|
|
|
return nxm_make_wild_header(f->header);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-09-16 22:13:44 -07:00
|
|
|
|
if (streq_len(name, name_len, "NXM_NX_COOKIE")) {
|
|
|
|
|
|
return NXM_NX_COOKIE;
|
|
|
|
|
|
} else if (streq_len(name, name_len, "NXM_NX_COOKIE_W")) {
|
|
|
|
|
|
return NXM_NX_COOKIE_W;
|
2011-12-23 12:23:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-07 11:04:15 -08:00
|
|
|
|
/* Check whether it's a 32-bit field header value as hex.
|
|
|
|
|
|
* (This isn't ordinarily useful except for testing error behavior.) */
|
|
|
|
|
|
if (name_len == 8) {
|
|
|
|
|
|
uint32_t header = hexits_value(name, name_len, NULL);
|
|
|
|
|
|
if (header != UINT_MAX) {
|
|
|
|
|
|
return header;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* nx_match_from_string(). */
|
|
|
|
|
|
|
2012-08-01 16:01:45 +09:00
|
|
|
|
static int
|
|
|
|
|
|
nx_match_from_string_raw(const char *s, struct ofpbuf *b)
|
2010-11-09 17:00:59 -08:00
|
|
|
|
{
|
|
|
|
|
|
const char *full_s = s;
|
2014-03-30 01:31:50 -07:00
|
|
|
|
const size_t start_len = ofpbuf_size(b);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
|
|
|
|
|
if (!strcmp(s, "<any>")) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
/* Ensure that 'ofpbuf_data(b)' isn't actually null. */
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ofpbuf_prealloc_tailroom(b, 1);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (s += strspn(s, ", "); *s; s += strspn(s, ", ")) {
|
2010-12-07 11:04:15 -08:00
|
|
|
|
const char *name;
|
|
|
|
|
|
uint32_t header;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
int name_len;
|
2010-12-08 13:09:59 -08:00
|
|
|
|
size_t n;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
|
2010-12-07 11:04:15 -08:00
|
|
|
|
name = s;
|
2010-11-09 17:00:59 -08:00
|
|
|
|
name_len = strcspn(s, "(");
|
|
|
|
|
|
if (s[name_len] != '(') {
|
|
|
|
|
|
ovs_fatal(0, "%s: missing ( at end of nx_match", full_s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-12-07 11:04:15 -08:00
|
|
|
|
header = parse_nxm_field_name(name, name_len);
|
|
|
|
|
|
if (!header) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
ovs_fatal(0, "%s: unknown field `%.*s'", full_s, name_len, s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
s += name_len + 1;
|
|
|
|
|
|
|
2014-09-30 17:26:28 -07:00
|
|
|
|
nx_put_header__(b, header, false);
|
2010-12-08 13:09:59 -08:00
|
|
|
|
s = ofpbuf_put_hex(b, s, &n);
|
|
|
|
|
|
if (n != nxm_field_bytes(header)) {
|
|
|
|
|
|
ovs_fatal(0, "%.2s: hex digits expected", s);
|
|
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
if (nxm_hasmask(header)) {
|
2010-11-09 17:00:59 -08:00
|
|
|
|
s += strspn(s, " ");
|
|
|
|
|
|
if (*s != '/') {
|
2010-12-07 11:04:15 -08:00
|
|
|
|
ovs_fatal(0, "%s: missing / in masked field %.*s",
|
|
|
|
|
|
full_s, name_len, name);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
2010-12-08 13:09:59 -08:00
|
|
|
|
s = ofpbuf_put_hex(b, s + 1, &n);
|
|
|
|
|
|
if (n != nxm_field_bytes(header)) {
|
|
|
|
|
|
ovs_fatal(0, "%.2s: hex digits expected", s);
|
|
|
|
|
|
}
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
s += strspn(s, " ");
|
|
|
|
|
|
if (*s != ')') {
|
2010-12-07 11:04:15 -08:00
|
|
|
|
ovs_fatal(0, "%s: missing ) following field %.*s",
|
|
|
|
|
|
full_s, name_len, name);
|
2010-11-09 17:00:59 -08:00
|
|
|
|
}
|
|
|
|
|
|
s++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
return ofpbuf_size(b) - start_len;
|
2012-08-01 16:01:45 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
nx_match_from_string(const char *s, struct ofpbuf *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
int match_len = nx_match_from_string_raw(s, b);
|
2014-03-10 23:50:54 -07:00
|
|
|
|
ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
return match_len;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
|
oxm_match_from_string(const char *s, struct ofpbuf *b)
|
|
|
|
|
|
{
|
|
|
|
|
|
int match_len;
|
|
|
|
|
|
struct ofp11_match_header *omh;
|
2014-03-30 01:31:50 -07:00
|
|
|
|
size_t start_len = ofpbuf_size(b);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
|
|
|
|
|
ofpbuf_put_uninit(b, sizeof *omh);
|
|
|
|
|
|
match_len = nx_match_from_string_raw(s, b) + sizeof *omh;
|
2014-03-10 23:50:54 -07:00
|
|
|
|
ofpbuf_put_zeros(b, PAD_SIZE(match_len, 8));
|
2012-08-01 16:01:45 +09:00
|
|
|
|
|
2013-07-22 15:47:19 -07:00
|
|
|
|
omh = ofpbuf_at(b, start_len, sizeof *omh);
|
2012-08-01 16:01:45 +09:00
|
|
|
|
omh->type = htons(OFPMT_OXM);
|
|
|
|
|
|
omh->length = htons(match_len);
|
|
|
|
|
|
|
2010-11-09 17:00:59 -08:00
|
|
|
|
return match_len;
|
|
|
|
|
|
}
|
2010-11-11 10:41:33 -08:00
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
|
/* Parses 's' as a "move" action, in the form described in ovs-ofctl(8), into
|
|
|
|
|
|
* '*move'.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
|
|
char * WARN_UNUSED_RESULT
|
2012-07-03 22:17:14 -07:00
|
|
|
|
nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
|
2010-12-09 11:03:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
const char *full_s = s;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
2010-12-09 11:03:35 -08:00
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
|
error = mf_parse_subfield__(&move->src, &s);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
return error;
|
|
|
|
|
|
}
|
2010-12-09 11:03:35 -08:00
|
|
|
|
if (strncmp(s, "->", 2)) {
|
2013-07-08 10:15:00 -07:00
|
|
|
|
return xasprintf("%s: missing `->' following source", full_s);
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
s += 2;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
error = mf_parse_subfield(&move->dst, s);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
return error;
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
if (move->src.n_bits != move->dst.n_bits) {
|
2013-07-08 10:15:00 -07:00
|
|
|
|
return xasprintf("%s: source field is %d bits wide but destination is "
|
|
|
|
|
|
"%d bits wide", full_s,
|
|
|
|
|
|
move->src.n_bits, move->dst.n_bits);
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
|
return NULL;
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-10-07 16:49:50 -07:00
|
|
|
|
/* nxm_format_reg_move(). */
|
2010-12-09 11:03:35 -08:00
|
|
|
|
|
|
|
|
|
|
void
|
2012-07-03 22:17:14 -07:00
|
|
|
|
nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
|
2010-12-09 11:03:35 -08:00
|
|
|
|
{
|
|
|
|
|
|
ds_put_format(s, "move:");
|
2012-07-03 22:17:14 -07:00
|
|
|
|
mf_format_subfield(&move->src, s);
|
2010-12-09 11:03:35 -08:00
|
|
|
|
ds_put_cstr(s, "->");
|
2012-07-03 22:17:14 -07:00
|
|
|
|
mf_format_subfield(&move->dst, s);
|
2010-12-09 11:03:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
|
nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
|
2011-07-19 15:47:02 -07:00
|
|
|
|
{
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2011-07-19 15:47:02 -07:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
error = mf_check_src(&move->src, flow);
|
2011-07-19 15:47:02 -07:00
|
|
|
|
if (error) {
|
|
|
|
|
|
return error;
|
2010-11-11 10:41:33 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
return mf_check_dst(&move->dst, NULL);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-10-07 16:49:50 -07:00
|
|
|
|
/* nxm_execute_reg_move(). */
|
2010-11-11 10:41:33 -08:00
|
|
|
|
|
|
|
|
|
|
void
|
2012-07-03 22:17:14 -07:00
|
|
|
|
nxm_execute_reg_move(const struct ofpact_reg_move *move,
|
2013-05-14 18:24:43 -07:00
|
|
|
|
struct flow *flow, struct flow_wildcards *wc)
|
2010-11-11 10:41:33 -08:00
|
|
|
|
{
|
2012-07-03 22:17:14 -07:00
|
|
|
|
union mf_value src_value;
|
|
|
|
|
|
union mf_value dst_value;
|
2011-12-28 12:42:14 -08:00
|
|
|
|
|
Set datapath mask bits when setting a flow field.
Since at the datapath interface we do not have set actions for
individual fields, but larger sets of fields for a given protocol
layer, the set action will in practice only ever apply to exactly
matched flows for the given protocol layer. For example, if the
reg_load changes the IP TTL, the corresponding datapath action will
rewrite also the IP addresses and TOS byte. Since these other field
values may not be explicitly set, they depend on the incoming flow field
values, and are hence all of them are set in the wildcards masks, when
the action is committed to the datapath. For the rare case, where the
reg_load action does not actually change the value, and no other flow
field values are set (or loaded), the datapath action is skipped, and
no mask bits are set. Such a datapath flow should, however, be
dependent on the specific field value, so the corresponding wildcard
mask bits must be set, lest the datapath flow be applied to packets
containing some other value in the field and the field value remain
unchanged regardless of the incoming value.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Signed-off-by: Ben Pfaff <blp@nicira.com>
2013-10-15 12:40:38 -07:00
|
|
|
|
mf_mask_field_and_prereqs(move->dst.field, &wc->masks);
|
|
|
|
|
|
mf_mask_field_and_prereqs(move->src.field, &wc->masks);
|
2013-05-14 18:24:43 -07:00
|
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
|
mf_get_value(move->dst.field, flow, &dst_value);
|
|
|
|
|
|
mf_get_value(move->src.field, flow, &src_value);
|
|
|
|
|
|
bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
|
|
|
|
|
|
&dst_value, move->dst.field->n_bytes, move->dst.ofs,
|
|
|
|
|
|
move->src.n_bits);
|
|
|
|
|
|
mf_set_flow_value(move->dst.field, &dst_value, flow);
|
2011-07-19 15:47:02 -07:00
|
|
|
|
}
|
2010-11-11 10:41:33 -08:00
|
|
|
|
|
2011-12-28 12:42:14 -08:00
|
|
|
|
void
|
2012-07-03 22:17:14 -07:00
|
|
|
|
nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
|
2013-08-02 21:17:31 -07:00
|
|
|
|
struct flow *flow, struct flow_wildcards *wc)
|
2011-12-28 12:42:14 -08:00
|
|
|
|
{
|
2012-09-12 17:44:28 +09:00
|
|
|
|
union mf_subvalue src_subvalue;
|
2013-08-02 21:17:31 -07:00
|
|
|
|
union mf_subvalue mask_value;
|
2012-09-12 17:44:28 +09:00
|
|
|
|
ovs_be64 src_data_be = htonll(src_data);
|
2012-07-03 22:17:14 -07:00
|
|
|
|
|
2013-08-02 21:17:31 -07:00
|
|
|
|
memset(&mask_value, 0xff, sizeof mask_value);
|
|
|
|
|
|
mf_write_subfield_flow(dst, &mask_value, &wc->masks);
|
|
|
|
|
|
|
2012-09-12 17:44:28 +09:00
|
|
|
|
bitwise_copy(&src_data_be, sizeof src_data_be, 0,
|
|
|
|
|
|
&src_subvalue, sizeof src_subvalue, 0,
|
|
|
|
|
|
sizeof src_data_be * 8);
|
|
|
|
|
|
mf_write_subfield_flow(dst, &src_subvalue, flow);
|
2010-11-11 10:41:33 -08:00
|
|
|
|
}
|
2013-03-05 16:27:55 -08:00
|
|
|
|
|
|
|
|
|
|
/* nxm_parse_stack_action, works for both push() and pop(). */
|
2013-07-08 10:15:00 -07:00
|
|
|
|
|
|
|
|
|
|
/* Parses 's' as a "push" or "pop" action, in the form described in
|
|
|
|
|
|
* ovs-ofctl(8), into '*stack_action'.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
|
|
char * WARN_UNUSED_RESULT
|
2013-03-05 16:27:55 -08:00
|
|
|
|
nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s)
|
|
|
|
|
|
{
|
2013-07-08 10:15:00 -07:00
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
|
|
error = mf_parse_subfield__(&stack_action->subfield, &s);
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
return error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-03-05 16:27:55 -08:00
|
|
|
|
if (*s != '\0') {
|
2013-07-08 10:15:00 -07:00
|
|
|
|
return xasprintf("%s: trailing garbage following push or pop", s);
|
2013-03-05 16:27:55 -08:00
|
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
|
|
|
|
|
|
|
return NULL;
|
2013-03-05 16:27:55 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
nxm_format_stack_push(const struct ofpact_stack *push, struct ds *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
ds_put_cstr(s, "push:");
|
|
|
|
|
|
mf_format_subfield(&push->subfield, s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
ds_put_cstr(s, "pop:");
|
|
|
|
|
|
mf_format_subfield(&pop->subfield, s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
|
nxm_stack_push_check(const struct ofpact_stack *push,
|
|
|
|
|
|
const struct flow *flow)
|
|
|
|
|
|
{
|
|
|
|
|
|
return mf_check_src(&push->subfield, flow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum ofperr
|
|
|
|
|
|
nxm_stack_pop_check(const struct ofpact_stack *pop,
|
|
|
|
|
|
const struct flow *flow)
|
|
|
|
|
|
{
|
|
|
|
|
|
return mf_check_dst(&pop->subfield, flow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* nxm_execute_stack_push(), nxm_execute_stack_pop(). */
|
|
|
|
|
|
static void
|
|
|
|
|
|
nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v)
|
|
|
|
|
|
{
|
|
|
|
|
|
ofpbuf_put(stack, v, sizeof *v);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static union mf_subvalue *
|
|
|
|
|
|
nx_stack_pop(struct ofpbuf *stack)
|
|
|
|
|
|
{
|
|
|
|
|
|
union mf_subvalue *v = NULL;
|
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
if (ofpbuf_size(stack)) {
|
|
|
|
|
|
|
|
|
|
|
|
ofpbuf_set_size(stack, ofpbuf_size(stack) - sizeof *v);
|
2013-03-05 16:27:55 -08:00
|
|
|
|
v = (union mf_subvalue *) ofpbuf_tail(stack);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return v;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
nxm_execute_stack_push(const struct ofpact_stack *push,
|
2013-05-14 18:24:43 -07:00
|
|
|
|
const struct flow *flow, struct flow_wildcards *wc,
|
|
|
|
|
|
struct ofpbuf *stack)
|
2013-03-05 16:27:55 -08:00
|
|
|
|
{
|
2013-05-14 18:24:43 -07:00
|
|
|
|
union mf_subvalue mask_value;
|
2013-03-05 16:27:55 -08:00
|
|
|
|
union mf_subvalue dst_value;
|
|
|
|
|
|
|
2013-05-14 18:24:43 -07:00
|
|
|
|
memset(&mask_value, 0xff, sizeof mask_value);
|
|
|
|
|
|
mf_write_subfield_flow(&push->subfield, &mask_value, &wc->masks);
|
|
|
|
|
|
|
2013-03-05 16:27:55 -08:00
|
|
|
|
mf_read_subfield(&push->subfield, flow, &dst_value);
|
|
|
|
|
|
nx_stack_push(stack, &dst_value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
nxm_execute_stack_pop(const struct ofpact_stack *pop,
|
2013-08-02 21:17:31 -07:00
|
|
|
|
struct flow *flow, struct flow_wildcards *wc,
|
|
|
|
|
|
struct ofpbuf *stack)
|
2013-03-05 16:27:55 -08:00
|
|
|
|
{
|
|
|
|
|
|
union mf_subvalue *src_value;
|
|
|
|
|
|
|
|
|
|
|
|
src_value = nx_stack_pop(stack);
|
|
|
|
|
|
|
|
|
|
|
|
/* Only pop if stack is not empty. Otherwise, give warning. */
|
|
|
|
|
|
if (src_value) {
|
2013-08-02 21:17:31 -07:00
|
|
|
|
union mf_subvalue mask_value;
|
|
|
|
|
|
|
|
|
|
|
|
memset(&mask_value, 0xff, sizeof mask_value);
|
|
|
|
|
|
mf_write_subfield_flow(&pop->subfield, &mask_value, &wc->masks);
|
2013-03-05 16:27:55 -08:00
|
|
|
|
mf_write_subfield_flow(&pop->subfield, src_value, flow);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (!VLOG_DROP_WARN(&rl)) {
|
|
|
|
|
|
char *flow_str = flow_to_string(flow);
|
|
|
|
|
|
VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow \n"
|
|
|
|
|
|
" %s", flow_str);
|
|
|
|
|
|
free(flow_str);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-09-16 22:13:44 -07:00
|
|
|
|
|
|
|
|
|
|
/* Formats 'sf' into 's' in a format normally acceptable to
|
|
|
|
|
|
* mf_parse_subfield(). (It won't be acceptable if sf->field is NULL or if
|
|
|
|
|
|
* sf->field has no NXM name.) */
|
|
|
|
|
|
void
|
|
|
|
|
|
mf_format_subfield(const struct mf_subfield *sf, struct ds *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!sf->field) {
|
|
|
|
|
|
ds_put_cstr(s, "<unknown>");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const struct nxm_field *f = nxm_field_from_mf_field(sf->field->id, 0);
|
|
|
|
|
|
ds_put_cstr(s, f ? f->name : sf->field->name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sf->field && sf->ofs == 0 && sf->n_bits == sf->field->n_bits) {
|
|
|
|
|
|
ds_put_cstr(s, "[]");
|
|
|
|
|
|
} else if (sf->n_bits == 1) {
|
|
|
|
|
|
ds_put_format(s, "[%d]", sf->ofs);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ds_put_format(s, "[%d..%d]", sf->ofs, sf->ofs + sf->n_bits - 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
mf_parse_subfield_name(const char *name, int name_len, bool *wild)
|
|
|
|
|
|
{
|
|
|
|
|
|
*wild = name_len > 2 && !memcmp(&name[name_len - 2], "_W", 2);
|
|
|
|
|
|
if (*wild) {
|
|
|
|
|
|
name_len -= 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nxm_field_by_name(name, name_len);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Parses a subfield from the beginning of '*sp' into 'sf'. If successful,
|
|
|
|
|
|
* returns NULL and advances '*sp' to the first byte following the parsed
|
|
|
|
|
|
* string. On failure, returns a malloc()'d error message, does not modify
|
|
|
|
|
|
* '*sp', and does not properly initialize 'sf'.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The syntax parsed from '*sp' takes the form "header[start..end]" where
|
|
|
|
|
|
* 'header' is the name of an NXM field and 'start' and 'end' are (inclusive)
|
|
|
|
|
|
* bit indexes. "..end" may be omitted to indicate a single bit. "start..end"
|
|
|
|
|
|
* may both be omitted (the [] are still required) to indicate an entire
|
|
|
|
|
|
* field. */
|
|
|
|
|
|
char * WARN_UNUSED_RESULT
|
|
|
|
|
|
mf_parse_subfield__(struct mf_subfield *sf, const char **sp)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct mf_field *field;
|
|
|
|
|
|
const struct nxm_field *f;
|
|
|
|
|
|
const char *name;
|
|
|
|
|
|
int start, end;
|
|
|
|
|
|
const char *s;
|
|
|
|
|
|
int name_len;
|
|
|
|
|
|
bool wild;
|
|
|
|
|
|
|
|
|
|
|
|
s = *sp;
|
|
|
|
|
|
name = s;
|
|
|
|
|
|
name_len = strcspn(s, "[");
|
|
|
|
|
|
if (s[name_len] != '[') {
|
|
|
|
|
|
return xasprintf("%s: missing [ looking for field name", *sp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
f = mf_parse_subfield_name(name, name_len, &wild);
|
|
|
|
|
|
if (!f) {
|
|
|
|
|
|
return xasprintf("%s: unknown field `%.*s'", *sp, name_len, s);
|
|
|
|
|
|
}
|
|
|
|
|
|
field = mf_from_id(f->id);
|
|
|
|
|
|
|
|
|
|
|
|
s += name_len;
|
|
|
|
|
|
if (ovs_scan(s, "[%d..%d]", &start, &end)) {
|
|
|
|
|
|
/* Nothing to do. */
|
|
|
|
|
|
} else if (ovs_scan(s, "[%d]", &start)) {
|
|
|
|
|
|
end = start;
|
|
|
|
|
|
} else if (!strncmp(s, "[]", 2)) {
|
|
|
|
|
|
start = 0;
|
|
|
|
|
|
end = field->n_bits - 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return xasprintf("%s: syntax error expecting [] or [<bit>] or "
|
|
|
|
|
|
"[<start>..<end>]", *sp);
|
|
|
|
|
|
}
|
|
|
|
|
|
s = strchr(s, ']') + 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (start > end) {
|
|
|
|
|
|
return xasprintf("%s: starting bit %d is after ending bit %d",
|
|
|
|
|
|
*sp, start, end);
|
|
|
|
|
|
} else if (start >= field->n_bits) {
|
|
|
|
|
|
return xasprintf("%s: starting bit %d is not valid because field is "
|
|
|
|
|
|
"only %d bits wide", *sp, start, field->n_bits);
|
|
|
|
|
|
} else if (end >= field->n_bits){
|
|
|
|
|
|
return xasprintf("%s: ending bit %d is not valid because field is "
|
|
|
|
|
|
"only %d bits wide", *sp, end, field->n_bits);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sf->field = field;
|
|
|
|
|
|
sf->ofs = start;
|
|
|
|
|
|
sf->n_bits = end - start + 1;
|
|
|
|
|
|
|
|
|
|
|
|
*sp = s;
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Parses a subfield from the entirety of 's' into 'sf'. Returns NULL if
|
|
|
|
|
|
* successful, otherwise a malloc()'d string describing the error. The caller
|
|
|
|
|
|
* is responsible for freeing the returned string.
|
|
|
|
|
|
*
|
|
|
|
|
|
* The syntax parsed from 's' takes the form "header[start..end]" where
|
|
|
|
|
|
* 'header' is the name of an NXM field and 'start' and 'end' are (inclusive)
|
|
|
|
|
|
* bit indexes. "..end" may be omitted to indicate a single bit. "start..end"
|
|
|
|
|
|
* may both be omitted (the [] are still required) to indicate an entire
|
|
|
|
|
|
* field. */
|
|
|
|
|
|
char * WARN_UNUSED_RESULT
|
|
|
|
|
|
mf_parse_subfield(struct mf_subfield *sf, const char *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
char *error = mf_parse_subfield__(sf, &s);
|
|
|
|
|
|
if (!error && s[0]) {
|
|
|
|
|
|
error = xstrdup("unexpected input following field syntax");
|
|
|
|
|
|
}
|
|
|
|
|
|
return error;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns an bitmap in which each bit corresponds to the like-numbered field
|
|
|
|
|
|
* in the OFPXMC12_OPENFLOW_BASIC OXM class, in which the bit values are taken
|
|
|
|
|
|
* from the 'fields' bitmap. Only fields defined in OpenFlow 'version' are
|
|
|
|
|
|
* considered.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This is useful for encoding OpenFlow 1.2 table stats messages. */
|
|
|
|
|
|
ovs_be64
|
|
|
|
|
|
oxm_bitmap_from_mf_bitmap(const struct mf_bitmap *fields,
|
|
|
|
|
|
enum ofp_version version)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint64_t oxm_bitmap = 0;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
BITMAP_FOR_EACH_1 (i, MFF_N_IDS, fields->bm) {
|
|
|
|
|
|
uint32_t oxm = mf_oxm_header(i, version);
|
|
|
|
|
|
uint32_t vendor = nxm_vendor(oxm);
|
|
|
|
|
|
int field = nxm_field(oxm);
|
|
|
|
|
|
|
|
|
|
|
|
if (vendor == OFPXMC12_OPENFLOW_BASIC && field < 64) {
|
|
|
|
|
|
oxm_bitmap |= UINT64_C(1) << field;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return htonll(oxm_bitmap);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Opposite conversion from oxm_bitmap_from_mf_bitmap().
|
|
|
|
|
|
*
|
|
|
|
|
|
* This is useful for decoding OpenFlow 1.2 table stats messages. */
|
|
|
|
|
|
struct mf_bitmap
|
|
|
|
|
|
oxm_bitmap_to_mf_bitmap(ovs_be64 oxm_bitmap, enum ofp_version version)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct mf_bitmap fields = MF_BITMAP_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
|
|
for (enum mf_field_id id = 0; id < MFF_N_IDS; id++) {
|
|
|
|
|
|
if (version >= mf_oxm_version(id)) {
|
|
|
|
|
|
uint32_t oxm = mf_oxm_header(id, version);
|
|
|
|
|
|
uint32_t vendor = nxm_vendor(oxm);
|
|
|
|
|
|
int field = nxm_field(oxm);
|
|
|
|
|
|
|
|
|
|
|
|
if (vendor == OFPXMC12_OPENFLOW_BASIC
|
|
|
|
|
|
&& field < 64
|
|
|
|
|
|
&& oxm_bitmap & htonll(UINT64_C(1) << field)) {
|
|
|
|
|
|
bitmap_set1(fields.bm, id);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return fields;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns a bitmap of fields that can be encoded in OXM and that can be
|
|
|
|
|
|
* modified with a "set_field" action. */
|
|
|
|
|
|
struct mf_bitmap
|
|
|
|
|
|
oxm_writable_fields(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct mf_bitmap b = MF_BITMAP_INITIALIZER;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MFF_N_IDS; i++) {
|
|
|
|
|
|
if (mf_oxm_header(i, 0) && mf_from_id(i)->writable) {
|
|
|
|
|
|
bitmap_set1(b.bm, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns a bitmap of fields that can be encoded in OXM and that can be
|
|
|
|
|
|
* matched in a flow table. */
|
|
|
|
|
|
struct mf_bitmap
|
|
|
|
|
|
oxm_matchable_fields(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct mf_bitmap b = MF_BITMAP_INITIALIZER;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MFF_N_IDS; i++) {
|
|
|
|
|
|
if (mf_oxm_header(i, 0)) {
|
|
|
|
|
|
bitmap_set1(b.bm, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns a bitmap of fields that can be encoded in OXM and that can be
|
|
|
|
|
|
* matched in a flow table with an arbitrary bitmask. */
|
|
|
|
|
|
struct mf_bitmap
|
|
|
|
|
|
oxm_maskable_fields(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
struct mf_bitmap b = MF_BITMAP_INITIALIZER;
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MFF_N_IDS; i++) {
|
|
|
|
|
|
if (mf_oxm_header(i, 0) && mf_from_id(i)->maskable == MFM_FULLY) {
|
|
|
|
|
|
bitmap_set1(b.bm, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct nxm_field_index {
|
|
|
|
|
|
struct hmap_node header_node;
|
|
|
|
|
|
struct hmap_node name_node;
|
|
|
|
|
|
struct nxm_field nf;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#include "nx-match.inc"
|
|
|
|
|
|
|
|
|
|
|
|
static struct hmap nxm_header_map;
|
|
|
|
|
|
static struct hmap nxm_name_map;
|
|
|
|
|
|
static struct nxm_field *nxm_fields[MFF_N_IDS];
|
|
|
|
|
|
static struct nxm_field *oxm_fields[MFF_N_IDS];
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
|
nxm_init(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
|
|
|
|
|
if (ovsthread_once_start(&once)) {
|
|
|
|
|
|
hmap_init(&nxm_header_map);
|
|
|
|
|
|
hmap_init(&nxm_name_map);
|
|
|
|
|
|
for (struct nxm_field_index *nfi = all_nxm_fields;
|
|
|
|
|
|
nfi < &all_nxm_fields[ARRAY_SIZE(all_nxm_fields)]; nfi++) {
|
|
|
|
|
|
hmap_insert(&nxm_header_map, &nfi->header_node,
|
|
|
|
|
|
hash_int(nfi->nf.header, 0));
|
|
|
|
|
|
hmap_insert(&nxm_name_map, &nfi->name_node,
|
|
|
|
|
|
hash_string(nfi->nf.name, 0));
|
|
|
|
|
|
if (is_nxm_header(nfi->nf.header)) {
|
|
|
|
|
|
nxm_fields[nfi->nf.id] = &nfi->nf;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
oxm_fields[nfi->nf.id] = &nfi->nf;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
ovsthread_once_done(&once);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
nxm_field_by_header(uint32_t header)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct nxm_field_index *nfi;
|
|
|
|
|
|
|
|
|
|
|
|
nxm_init();
|
|
|
|
|
|
if (nxm_hasmask(header)) {
|
|
|
|
|
|
header = nxm_make_exact_header(header);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_IN_BUCKET (nfi, header_node, hash_int(header, 0),
|
|
|
|
|
|
&nxm_header_map) {
|
|
|
|
|
|
if (header == nfi->nf.header) {
|
|
|
|
|
|
return &nfi->nf;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
nxm_field_by_name(const char *name, size_t len)
|
|
|
|
|
|
{
|
|
|
|
|
|
const struct nxm_field_index *nfi;
|
|
|
|
|
|
|
|
|
|
|
|
nxm_init();
|
|
|
|
|
|
HMAP_FOR_EACH_WITH_HASH (nfi, name_node, hash_bytes(name, len, 0),
|
|
|
|
|
|
&nxm_name_map) {
|
|
|
|
|
|
if (strlen(nfi->nf.name) == len && !memcmp(nfi->nf.name, name, len)) {
|
|
|
|
|
|
return &nfi->nf;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
nxm_field_by_mf_id(enum mf_field_id id)
|
|
|
|
|
|
{
|
|
|
|
|
|
nxm_init();
|
|
|
|
|
|
return nxm_fields[id];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct nxm_field *
|
|
|
|
|
|
oxm_field_by_mf_id(enum mf_field_id id)
|
|
|
|
|
|
{
|
|
|
|
|
|
nxm_init();
|
|
|
|
|
|
return oxm_fields[id];
|
|
|
|
|
|
}
|
|
|
|
|
|
|