mirror of
https://github.com/openvswitch/ovs
synced 2025-10-27 15:18:06 +00:00
1629 lines
54 KiB
C
1629 lines
54 KiB
C
/*
|
||
* 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-port.h"
|
||
#include "byte-order.h"
|
||
#include "flow.h"
|
||
#include "openflow/intel-ext.h"
|
||
#include "openvswitch/json.h"
|
||
#include "openvswitch/ofp-errors.h"
|
||
#include "openvswitch/ofp-msgs.h"
|
||
#include "openvswitch/ofp-prop.h"
|
||
#include "openvswitch/ofpbuf.h"
|
||
#include "openvswitch/vlog.h"
|
||
|
||
VLOG_DEFINE_THIS_MODULE(ofp_port);
|
||
|
||
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
||
|
||
/* ofputil_port_map. */
|
||
|
||
void
|
||
ofputil_port_map_init(struct ofputil_port_map *map)
|
||
{
|
||
namemap_init(&map->map);
|
||
}
|
||
|
||
void
|
||
ofputil_port_map_put(struct ofputil_port_map *map,
|
||
ofp_port_t ofp_port, const char *name)
|
||
{
|
||
namemap_put(&map->map, ofp_to_u16(ofp_port), name);
|
||
}
|
||
|
||
const char *
|
||
ofputil_port_map_get_name(const struct ofputil_port_map *map,
|
||
ofp_port_t ofp_port)
|
||
{
|
||
struct namemap_node *node
|
||
= (map
|
||
? namemap_find_by_number(&map->map, ofp_to_u16(ofp_port))
|
||
: NULL);
|
||
return node && !node->duplicate ? node->name : NULL;
|
||
}
|
||
|
||
ofp_port_t
|
||
ofputil_port_map_get_number(const struct ofputil_port_map *map,
|
||
const char *name)
|
||
{
|
||
struct namemap_node *node
|
||
= map ? namemap_find_by_name(&map->map, name) : NULL;
|
||
return node && !node->duplicate ? u16_to_ofp(node->number) : OFPP_NONE;
|
||
}
|
||
|
||
void
|
||
ofputil_port_map_destroy(struct ofputil_port_map *map)
|
||
{
|
||
namemap_destroy(&map->map);
|
||
}
|
||
|
||
/* Converts the OpenFlow 1.1+ port number 'ofp11_port' into an OpenFlow 1.0
|
||
* port number and stores the latter in '*ofp10_port', for the purpose of
|
||
* decoding OpenFlow 1.1+ protocol messages. Returns 0 if successful,
|
||
* otherwise an OFPERR_* number. On error, stores OFPP_NONE in '*ofp10_port'.
|
||
*
|
||
* See the definition of OFP11_MAX for an explanation of the mapping. */
|
||
enum ofperr
|
||
ofputil_port_from_ofp11(ovs_be32 ofp11_port, ofp_port_t *ofp10_port)
|
||
{
|
||
uint32_t ofp11_port_h = ntohl(ofp11_port);
|
||
|
||
if (ofp11_port_h < ofp_to_u16(OFPP_MAX)) {
|
||
*ofp10_port = u16_to_ofp(ofp11_port_h);
|
||
return 0;
|
||
} else if (ofp11_port_h >= ofp11_to_u32(OFPP11_MAX)) {
|
||
*ofp10_port = u16_to_ofp(ofp11_port_h - OFPP11_OFFSET);
|
||
return 0;
|
||
} else {
|
||
*ofp10_port = OFPP_NONE;
|
||
|
||
static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 5);
|
||
VLOG_WARN_RL(&rll, "port %"PRIu32" is outside the supported "
|
||
"range 0 through %d or 0x%"PRIx32" through 0x%"PRIx32,
|
||
ofp11_port_h, ofp_to_u16(OFPP_MAX) - 1,
|
||
ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
|
||
|
||
return OFPERR_OFPBAC_BAD_OUT_PORT;
|
||
}
|
||
}
|
||
|
||
/* Returns the OpenFlow 1.1+ port number equivalent to the OpenFlow 1.0 port
|
||
* number 'ofp10_port', for encoding OpenFlow 1.1+ protocol messages.
|
||
*
|
||
* See the definition of OFP11_MAX for an explanation of the mapping. */
|
||
ovs_be32
|
||
ofputil_port_to_ofp11(ofp_port_t ofp10_port)
|
||
{
|
||
return htonl(ofp_to_u16(ofp10_port) < ofp_to_u16(OFPP_MAX)
|
||
? ofp_to_u16(ofp10_port)
|
||
: ofp_to_u16(ofp10_port) + OFPP11_OFFSET);
|
||
}
|
||
|
||
#define OFPUTIL_NAMED_PORTS \
|
||
OFPUTIL_NAMED_PORT(IN_PORT) \
|
||
OFPUTIL_NAMED_PORT(TABLE) \
|
||
OFPUTIL_NAMED_PORT(NORMAL) \
|
||
OFPUTIL_NAMED_PORT(FLOOD) \
|
||
OFPUTIL_NAMED_PORT(ALL) \
|
||
OFPUTIL_NAMED_PORT(CONTROLLER) \
|
||
OFPUTIL_NAMED_PORT(LOCAL) \
|
||
OFPUTIL_NAMED_PORT(ANY) \
|
||
OFPUTIL_NAMED_PORT(UNSET)
|
||
|
||
/* For backwards compatibility, so that "none" is recognized as OFPP_ANY */
|
||
#define OFPUTIL_NAMED_PORTS_WITH_NONE \
|
||
OFPUTIL_NAMED_PORTS \
|
||
OFPUTIL_NAMED_PORT(NONE)
|
||
|
||
/* Stores the port number represented by 's' into '*portp'. 's' may be an
|
||
* integer or, for reserved ports, the standard OpenFlow name for the port
|
||
* (e.g. "LOCAL"). If 'port_map' is nonnull, also accepts names in it (quoted
|
||
* or unquoted).
|
||
*
|
||
* Returns true if successful, false if 's' is not a valid OpenFlow port number
|
||
* or name. The caller should issue an error message in this case, because
|
||
* this function usually does not. (This gives the caller an opportunity to
|
||
* look up the port name another way, e.g. by contacting the switch and listing
|
||
* the names of all its ports).
|
||
*
|
||
* This function accepts OpenFlow 1.0 port numbers. It also accepts a subset
|
||
* of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
|
||
* range as described in include/openflow/openflow-1.1.h. */
|
||
bool
|
||
ofputil_port_from_string(const char *s,
|
||
const struct ofputil_port_map *port_map,
|
||
ofp_port_t *portp)
|
||
{
|
||
unsigned int port32; /* int is at least 32 bits wide. */
|
||
|
||
if (*s == '-') {
|
||
VLOG_WARN("Negative value %s is not a valid port number.", s);
|
||
return false;
|
||
}
|
||
*portp = 0;
|
||
if (str_to_uint(s, 10, &port32)) {
|
||
if (port32 < ofp_to_u16(OFPP_MAX)) {
|
||
/* Pass. */
|
||
} else if (port32 < ofp_to_u16(OFPP_FIRST_RESV)) {
|
||
VLOG_WARN("port %u is a reserved OF1.0 port number that will "
|
||
"be translated to %u when talking to an OF1.1 or "
|
||
"later controller", port32, port32 + OFPP11_OFFSET);
|
||
} else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
|
||
char name[OFP10_MAX_PORT_NAME_LEN];
|
||
|
||
ofputil_port_to_string(u16_to_ofp(port32), NULL,
|
||
name, sizeof name);
|
||
VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
|
||
"for compatibility with OpenFlow 1.1 and later",
|
||
name, port32);
|
||
} else if (port32 < ofp11_to_u32(OFPP11_MAX)) {
|
||
VLOG_WARN("port %u is outside the supported range 0 through "
|
||
"%x or 0x%x through 0x%"PRIx32, port32,
|
||
UINT16_MAX, ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
|
||
return false;
|
||
} else {
|
||
port32 -= OFPP11_OFFSET;
|
||
}
|
||
|
||
*portp = u16_to_ofp(port32);
|
||
return true;
|
||
} else {
|
||
struct pair {
|
||
const char *name;
|
||
ofp_port_t value;
|
||
};
|
||
static const struct pair pairs[] = {
|
||
#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME},
|
||
OFPUTIL_NAMED_PORTS_WITH_NONE
|
||
#undef OFPUTIL_NAMED_PORT
|
||
};
|
||
const struct pair *p;
|
||
|
||
for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) {
|
||
if (!strcasecmp(s, p->name)) {
|
||
*portp = p->value;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
ofp_port_t ofp_port = OFPP_NONE;
|
||
if (s[0] != '"') {
|
||
ofp_port = ofputil_port_map_get_number(port_map, s);
|
||
} else {
|
||
size_t length = strlen(s);
|
||
char *name = NULL;
|
||
if (length > 1
|
||
&& s[length - 1] == '"'
|
||
&& json_string_unescape(s + 1, length - 2, &name)) {
|
||
ofp_port = ofputil_port_map_get_number(port_map, name);
|
||
}
|
||
free(name);
|
||
}
|
||
if (ofp_port != OFPP_NONE) {
|
||
*portp = ofp_port;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
const char *
|
||
ofputil_port_get_reserved_name(ofp_port_t port)
|
||
{
|
||
switch (port) {
|
||
#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: return #NAME;
|
||
OFPUTIL_NAMED_PORTS
|
||
#undef OFPUTIL_NAMED_PORT
|
||
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
/* Appends to 's' a string representation of the OpenFlow port number 'port'.
|
||
* Most ports' string representation is just the port number, but for special
|
||
* ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
|
||
void
|
||
ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *port_map,
|
||
struct ds *s)
|
||
{
|
||
const char *reserved_name = ofputil_port_get_reserved_name(port);
|
||
if (reserved_name) {
|
||
ds_put_cstr(s, reserved_name);
|
||
return;
|
||
}
|
||
|
||
const char *port_name = ofputil_port_map_get_name(port_map, port);
|
||
if (port_name) {
|
||
namemap_put_name(port_name, s);
|
||
return;
|
||
}
|
||
|
||
ds_put_format(s, "%"PRIu32, port);
|
||
}
|
||
|
||
/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
|
||
* representation of OpenFlow port number 'port'. Most ports are represented
|
||
* as just the port number, but special ports, e.g. OFPP_LOCAL, are represented
|
||
* by name, e.g. "LOCAL". */
|
||
void
|
||
ofputil_port_to_string(ofp_port_t port,
|
||
const struct ofputil_port_map *port_map,
|
||
char *namebuf, size_t bufsize)
|
||
{
|
||
const char *reserved_name = ofputil_port_get_reserved_name(port);
|
||
if (reserved_name) {
|
||
ovs_strlcpy(namebuf, reserved_name, bufsize);
|
||
return;
|
||
}
|
||
|
||
const char *port_name = ofputil_port_map_get_name(port_map, port);
|
||
if (port_name) {
|
||
struct ds s = DS_EMPTY_INITIALIZER;
|
||
namemap_put_name(port_name, &s);
|
||
ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
|
||
ds_destroy(&s);
|
||
return;
|
||
}
|
||
|
||
snprintf(namebuf, bufsize, "%"PRIu32, port);
|
||
}
|
||
|
||
/* ofputil_phy_port */
|
||
|
||
/* NETDEV_F_* to and from OFPPF_* and OFPPF10_*. */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
|
||
|
||
/* NETDEV_F_ bits 11...15 are OFPPF10_ bits 7...11: */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF10_COPPER << 4));
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF10_FIBER << 4));
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF10_AUTONEG << 4));
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF10_PAUSE << 4));
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF10_PAUSE_ASYM << 4));
|
||
|
||
static enum netdev_features
|
||
netdev_port_features_from_ofp10(ovs_be32 ofp10_)
|
||
{
|
||
uint32_t ofp10 = ntohl(ofp10_);
|
||
return (ofp10 & 0x7f) | ((ofp10 & 0xf80) << 4);
|
||
}
|
||
|
||
static ovs_be32
|
||
netdev_port_features_to_ofp10(enum netdev_features features)
|
||
{
|
||
return htonl((features & 0x7f) | ((features & 0xf800) >> 4));
|
||
}
|
||
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_40GB_FD == OFPPF11_40GB_FD); /* bit 7 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_100GB_FD == OFPPF11_100GB_FD); /* bit 8 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_1TB_FD == OFPPF11_1TB_FD); /* bit 9 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_OTHER == OFPPF11_OTHER); /* bit 10 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == OFPPF11_COPPER); /* bit 11 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == OFPPF11_FIBER); /* bit 12 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == OFPPF11_AUTONEG); /* bit 13 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == OFPPF11_PAUSE); /* bit 14 */
|
||
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == OFPPF11_PAUSE_ASYM);/* bit 15 */
|
||
|
||
static enum netdev_features
|
||
netdev_port_features_from_ofp11(ovs_be32 ofp11)
|
||
{
|
||
return ntohl(ofp11) & 0xffff;
|
||
}
|
||
|
||
static ovs_be32
|
||
netdev_port_features_to_ofp11(enum netdev_features features)
|
||
{
|
||
return htonl(features & 0xffff);
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
|
||
const struct ofp10_phy_port *opp)
|
||
{
|
||
pp->port_no = u16_to_ofp(ntohs(opp->port_no));
|
||
pp->hw_addr = opp->hw_addr;
|
||
ovs_strlcpy_arrays(pp->name, opp->name);
|
||
|
||
pp->config = ntohl(opp->config) & OFPPC10_ALL;
|
||
pp->state = ntohl(opp->state) & OFPPS10_ALL;
|
||
|
||
pp->curr = netdev_port_features_from_ofp10(opp->curr);
|
||
pp->advertised = netdev_port_features_from_ofp10(opp->advertised);
|
||
pp->supported = netdev_port_features_from_ofp10(opp->supported);
|
||
pp->peer = netdev_port_features_from_ofp10(opp->peer);
|
||
|
||
pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000;
|
||
pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
|
||
const struct ofp11_port *op)
|
||
{
|
||
enum ofperr error;
|
||
|
||
error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
pp->hw_addr = op->hw_addr;
|
||
ovs_strlcpy_arrays(pp->name, op->name);
|
||
|
||
pp->config = ntohl(op->config) & OFPPC11_ALL;
|
||
pp->state = ntohl(op->state) & OFPPS11_ALL;
|
||
|
||
pp->curr = netdev_port_features_from_ofp11(op->curr);
|
||
pp->advertised = netdev_port_features_from_ofp11(op->advertised);
|
||
pp->supported = netdev_port_features_from_ofp11(op->supported);
|
||
pp->peer = netdev_port_features_from_ofp11(op->peer);
|
||
|
||
pp->curr_speed = ntohl(op->curr_speed);
|
||
pp->max_speed = ntohl(op->max_speed);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
parse_ofp14_port_ethernet_property(const struct ofpbuf *payload,
|
||
struct ofputil_phy_port *pp)
|
||
{
|
||
struct ofp14_port_desc_prop_ethernet *eth = payload->data;
|
||
|
||
if (payload->size != sizeof *eth) {
|
||
return OFPERR_OFPBPC_BAD_LEN;
|
||
}
|
||
|
||
pp->curr = netdev_port_features_from_ofp11(eth->curr);
|
||
pp->advertised = netdev_port_features_from_ofp11(eth->advertised);
|
||
pp->supported = netdev_port_features_from_ofp11(eth->supported);
|
||
pp->peer = netdev_port_features_from_ofp11(eth->peer);
|
||
|
||
pp->curr_speed = ntohl(eth->curr_speed);
|
||
pp->max_speed = ntohl(eth->max_speed);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_pull_ofp14_port_properties(const void *props, size_t len,
|
||
struct ofputil_phy_port *pp)
|
||
{
|
||
struct ofpbuf properties = ofpbuf_const_initializer(props, len);
|
||
while (properties.size > 0) {
|
||
struct ofpbuf payload;
|
||
enum ofperr error;
|
||
uint64_t type;
|
||
|
||
error = ofpprop_pull(&properties, &payload, &type);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
switch (type) {
|
||
case OFPPDPT14_ETHERNET:
|
||
error = parse_ofp14_port_ethernet_property(&payload, pp);
|
||
break;
|
||
|
||
default:
|
||
error = OFPPROP_UNKNOWN(true, "port", type);
|
||
break;
|
||
}
|
||
|
||
if (error) {
|
||
return error;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
|
||
{
|
||
const struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
|
||
if (!op) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
|
||
size_t len = ntohs(op->length);
|
||
if (len < sizeof *op || len - sizeof *op > msg->size) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
len -= sizeof *op;
|
||
|
||
enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
pp->hw_addr = op->hw_addr;
|
||
ovs_strlcpy_arrays(pp->name, op->name);
|
||
|
||
pp->config = ntohl(op->config) & OFPPC11_ALL;
|
||
pp->state = ntohl(op->state) & OFPPS11_ALL;
|
||
|
||
return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_pull_ofp16_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
|
||
{
|
||
const struct ofp16_port *op = ofpbuf_try_pull(msg, sizeof *op);
|
||
if (!op) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
|
||
size_t len = ntohs(op->length);
|
||
if (len < sizeof *op || len - sizeof *op > msg->size) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
len -= sizeof *op;
|
||
|
||
enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
pp->hw_addr = op->hw_addr;
|
||
pp->hw_addr64 = op->hw_addr64;
|
||
ovs_strlcpy_arrays(pp->name, op->name);
|
||
|
||
pp->config = ntohl(op->config) & OFPPC11_ALL;
|
||
pp->state = ntohl(op->state) & OFPPS11_ALL;
|
||
|
||
return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
|
||
}
|
||
|
||
static void
|
||
ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
|
||
struct ofp10_phy_port *opp)
|
||
{
|
||
memset(opp, 0, sizeof *opp);
|
||
|
||
opp->port_no = htons(ofp_to_u16(pp->port_no));
|
||
opp->hw_addr = pp->hw_addr;
|
||
ovs_strlcpy_arrays(opp->name, pp->name);
|
||
|
||
opp->config = htonl(pp->config & OFPPC10_ALL);
|
||
opp->state = htonl(pp->state & OFPPS10_ALL);
|
||
|
||
opp->curr = netdev_port_features_to_ofp10(pp->curr);
|
||
opp->advertised = netdev_port_features_to_ofp10(pp->advertised);
|
||
opp->supported = netdev_port_features_to_ofp10(pp->supported);
|
||
opp->peer = netdev_port_features_to_ofp10(pp->peer);
|
||
}
|
||
|
||
static void
|
||
ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
|
||
struct ofp11_port *op)
|
||
{
|
||
memset(op, 0, sizeof *op);
|
||
|
||
op->port_no = ofputil_port_to_ofp11(pp->port_no);
|
||
op->hw_addr = pp->hw_addr;
|
||
ovs_strlcpy_arrays(op->name, pp->name);
|
||
|
||
op->config = htonl(pp->config & OFPPC11_ALL);
|
||
op->state = htonl(pp->state & OFPPS11_ALL);
|
||
|
||
op->curr = netdev_port_features_to_ofp11(pp->curr);
|
||
op->advertised = netdev_port_features_to_ofp11(pp->advertised);
|
||
op->supported = netdev_port_features_to_ofp11(pp->supported);
|
||
op->peer = netdev_port_features_to_ofp11(pp->peer);
|
||
|
||
op->curr_speed = htonl(pp->curr_speed);
|
||
op->max_speed = htonl(pp->max_speed);
|
||
}
|
||
|
||
static void
|
||
ofputil_encode_ofp14_port_ethernet_prop(
|
||
const struct ofputil_phy_port *pp,
|
||
struct ofp14_port_desc_prop_ethernet *eth)
|
||
{
|
||
eth->curr = netdev_port_features_to_ofp11(pp->curr);
|
||
eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
|
||
eth->supported = netdev_port_features_to_ofp11(pp->supported);
|
||
eth->peer = netdev_port_features_to_ofp11(pp->peer);
|
||
eth->curr_speed = htonl(pp->curr_speed);
|
||
eth->max_speed = htonl(pp->max_speed);
|
||
}
|
||
|
||
static void
|
||
ofputil_put_ofp14_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
|
||
{
|
||
struct ofp14_port *op;
|
||
struct ofp14_port_desc_prop_ethernet *eth;
|
||
|
||
ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
|
||
|
||
op = ofpbuf_put_zeros(b, sizeof *op);
|
||
op->port_no = ofputil_port_to_ofp11(pp->port_no);
|
||
op->length = htons(sizeof *op + sizeof *eth);
|
||
op->hw_addr = pp->hw_addr;
|
||
ovs_strlcpy_arrays(op->name, pp->name);
|
||
op->config = htonl(pp->config & OFPPC11_ALL);
|
||
op->state = htonl(pp->state & OFPPS11_ALL);
|
||
|
||
eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
|
||
ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
|
||
}
|
||
|
||
static void
|
||
ofputil_put_ofp16_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
|
||
{
|
||
struct ofp16_port *op;
|
||
struct ofp14_port_desc_prop_ethernet *eth;
|
||
|
||
ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
|
||
|
||
op = ofpbuf_put_zeros(b, sizeof *op);
|
||
op->port_no = ofputil_port_to_ofp11(pp->port_no);
|
||
op->length = htons(sizeof *op + sizeof *eth);
|
||
op->hw_addr = pp->hw_addr;
|
||
op->hw_addr64 = pp->hw_addr64;
|
||
ovs_strlcpy_arrays(op->name, pp->name);
|
||
op->config = htonl(pp->config & OFPPC11_ALL);
|
||
op->state = htonl(pp->state & OFPPS11_ALL);
|
||
|
||
eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
|
||
ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
|
||
}
|
||
|
||
void
|
||
ofputil_put_phy_port(enum ofp_version ofp_version,
|
||
const struct ofputil_phy_port *pp, struct ofpbuf *b)
|
||
{
|
||
switch (ofp_version) {
|
||
case OFP10_VERSION: {
|
||
struct ofp10_phy_port *opp = ofpbuf_put_uninit(b, sizeof *opp);
|
||
ofputil_encode_ofp10_phy_port(pp, opp);
|
||
break;
|
||
}
|
||
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION: {
|
||
struct ofp11_port *op = ofpbuf_put_uninit(b, sizeof *op);
|
||
ofputil_encode_ofp11_port(pp, op);
|
||
break;
|
||
}
|
||
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION:
|
||
ofputil_put_ofp14_port(pp, b);
|
||
break;
|
||
case OFP16_VERSION:
|
||
ofputil_put_ofp16_port(pp, b);
|
||
break;
|
||
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|
||
enum ofperr
|
||
ofputil_decode_port_desc_stats_request(const struct ofp_header *request,
|
||
ofp_port_t *port)
|
||
{
|
||
struct ofpbuf b = ofpbuf_const_initializer(request,
|
||
ntohs(request->length));
|
||
enum ofpraw raw = ofpraw_pull_assert(&b);
|
||
if (raw == OFPRAW_OFPST10_PORT_DESC_REQUEST) {
|
||
*port = OFPP_ANY;
|
||
return 0;
|
||
} else if (raw == OFPRAW_OFPST15_PORT_DESC_REQUEST) {
|
||
ovs_be32 *ofp11_port;
|
||
|
||
ofp11_port = ofpbuf_pull(&b, sizeof *ofp11_port);
|
||
return ofputil_port_from_ofp11(*ofp11_port, port);
|
||
} else {
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|
||
struct ofpbuf *
|
||
ofputil_encode_port_desc_stats_request(enum ofp_version ofp_version,
|
||
ofp_port_t port)
|
||
{
|
||
struct ofpbuf *request;
|
||
|
||
switch (ofp_version) {
|
||
case OFP10_VERSION:
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION:
|
||
case OFP14_VERSION:
|
||
request = ofpraw_alloc(OFPRAW_OFPST10_PORT_DESC_REQUEST,
|
||
ofp_version, 0);
|
||
break;
|
||
case OFP15_VERSION:
|
||
case OFP16_VERSION:{
|
||
struct ofp15_port_desc_request *req;
|
||
request = ofpraw_alloc(OFPRAW_OFPST15_PORT_DESC_REQUEST,
|
||
ofp_version, 0);
|
||
req = ofpbuf_put_zeros(request, sizeof *req);
|
||
req->port_no = ofputil_port_to_ofp11(port);
|
||
break;
|
||
}
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
return request;
|
||
}
|
||
|
||
void
|
||
ofputil_append_port_desc_stats_reply(const struct ofputil_phy_port *pp,
|
||
struct ovs_list *replies)
|
||
{
|
||
struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
|
||
size_t start_ofs = reply->size;
|
||
|
||
ofputil_put_phy_port(ofpmp_version(replies), pp, reply);
|
||
ofpmp_postappend(replies, start_ofs);
|
||
}
|
||
|
||
/* Given a buffer 'b' that contains an array of OpenFlow ports of type
|
||
* 'ofp_version', tries to pull the first element from the array. If
|
||
* successful, initializes '*pp' with an abstract representation of the
|
||
* port and returns 0. If no ports remain to be decoded, returns EOF.
|
||
* On an error, returns a positive OFPERR_* value. */
|
||
int
|
||
ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b,
|
||
struct ofputil_phy_port *pp)
|
||
{
|
||
memset(pp, 0, sizeof *pp);
|
||
|
||
switch (ofp_version) {
|
||
case OFP10_VERSION: {
|
||
const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
|
||
return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
|
||
}
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION: {
|
||
const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
|
||
return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
|
||
}
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION:
|
||
return b->size ? ofputil_pull_ofp14_port(pp, b) : EOF;
|
||
case OFP16_VERSION:
|
||
return b->size ? ofputil_pull_ofp16_port(pp, b) : EOF;
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|
||
/* ofputil_port_status */
|
||
|
||
/* Decodes the OpenFlow "port status" message in '*ops' into an abstract form
|
||
* in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */
|
||
enum ofperr
|
||
ofputil_decode_port_status(const struct ofp_header *oh,
|
||
struct ofputil_port_status *ps)
|
||
{
|
||
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
||
ofpraw_pull_assert(&b);
|
||
|
||
const struct ofp_port_status *ops = ofpbuf_pull(&b, sizeof *ops);
|
||
if (ops->reason != OFPPR_ADD &&
|
||
ops->reason != OFPPR_DELETE &&
|
||
ops->reason != OFPPR_MODIFY) {
|
||
return OFPERR_NXBRC_BAD_REASON;
|
||
}
|
||
ps->reason = ops->reason;
|
||
|
||
int retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
|
||
ovs_assert(retval != EOF);
|
||
return retval;
|
||
}
|
||
|
||
/* Converts the abstract form of a "port status" message in '*ps' into an
|
||
* OpenFlow message suitable for 'protocol', and returns that encoded form in
|
||
* a buffer owned by the caller. */
|
||
struct ofpbuf *
|
||
ofputil_encode_port_status(const struct ofputil_port_status *ps,
|
||
enum ofputil_protocol protocol)
|
||
{
|
||
struct ofp_port_status *ops;
|
||
struct ofpbuf *b;
|
||
enum ofp_version version;
|
||
enum ofpraw raw;
|
||
|
||
version = ofputil_protocol_to_ofp_version(protocol);
|
||
switch (version) {
|
||
case OFP10_VERSION:
|
||
raw = OFPRAW_OFPT10_PORT_STATUS;
|
||
break;
|
||
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION:
|
||
raw = OFPRAW_OFPT11_PORT_STATUS;
|
||
break;
|
||
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION:
|
||
raw = OFPRAW_OFPT14_PORT_STATUS;
|
||
break;
|
||
|
||
case OFP16_VERSION:
|
||
raw = OFPRAW_OFPT16_PORT_STATUS;
|
||
break;
|
||
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
b = ofpraw_alloc_xid(raw, version, htonl(0), 0);
|
||
ops = ofpbuf_put_zeros(b, sizeof *ops);
|
||
ops->reason = ps->reason;
|
||
ofputil_put_phy_port(version, &ps->desc, b);
|
||
ofpmsg_update_length(b);
|
||
return b;
|
||
}
|
||
|
||
/* ofputil_port_mod */
|
||
|
||
static enum ofperr
|
||
parse_port_mod_ethernet_property(struct ofpbuf *property,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
ovs_be32 advertise;
|
||
enum ofperr error;
|
||
|
||
error = ofpprop_parse_be32(property, &advertise);
|
||
if (!error) {
|
||
pm->advertise = netdev_port_features_from_ofp11(advertise);
|
||
}
|
||
return error;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp10_port_mod(const struct ofp10_port_mod *opm,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
pm->port_no = u16_to_ofp(ntohs(opm->port_no));
|
||
pm->hw_addr = opm->hw_addr;
|
||
pm->config = ntohl(opm->config) & OFPPC10_ALL;
|
||
pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
|
||
pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp11_port_mod(const struct ofp11_port_mod *opm,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
enum ofperr error;
|
||
|
||
error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
pm->hw_addr = opm->hw_addr;
|
||
pm->config = ntohl(opm->config) & OFPPC11_ALL;
|
||
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
|
||
pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp14_port_mod_properties(struct ofpbuf *b, bool loose,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
while (b->size > 0) {
|
||
struct ofpbuf property;
|
||
enum ofperr error;
|
||
uint64_t type;
|
||
|
||
error = ofpprop_pull(b, &property, &type);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
switch (type) {
|
||
case OFPPMPT14_ETHERNET:
|
||
error = parse_port_mod_ethernet_property(&property, pm);
|
||
break;
|
||
|
||
default:
|
||
error = OFPPROP_UNKNOWN(loose, "port_mod", type);
|
||
break;
|
||
}
|
||
|
||
if (error) {
|
||
return error;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp14_port_mod(struct ofpbuf *b, bool loose,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
const struct ofp14_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
|
||
enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
pm->hw_addr = opm->hw_addr;
|
||
pm->config = ntohl(opm->config) & OFPPC11_ALL;
|
||
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
|
||
|
||
return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_decode_ofp16_port_mod(struct ofpbuf *b, bool loose,
|
||
struct ofputil_port_mod *pm)
|
||
{
|
||
const struct ofp16_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
|
||
enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
pm->hw_addr = opm->hw_addr;
|
||
pm->hw_addr64 = opm->hw_addr64;
|
||
pm->config = ntohl(opm->config) & OFPPC11_ALL;
|
||
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
|
||
|
||
return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
|
||
}
|
||
|
||
/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
|
||
* '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
|
||
enum ofperr
|
||
ofputil_decode_port_mod(const struct ofp_header *oh,
|
||
struct ofputil_port_mod *pm, bool loose)
|
||
{
|
||
memset(pm, 0, sizeof *pm);
|
||
|
||
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
||
enum ofpraw raw = ofpraw_pull_assert(&b);
|
||
|
||
enum ofperr error;
|
||
if (raw == OFPRAW_OFPT10_PORT_MOD) {
|
||
error = ofputil_decode_ofp10_port_mod(b.data, pm);
|
||
} else if (raw == OFPRAW_OFPT11_PORT_MOD) {
|
||
error = ofputil_decode_ofp11_port_mod(b.data, pm);
|
||
} else if (raw == OFPRAW_OFPT14_PORT_MOD) {
|
||
error = ofputil_decode_ofp14_port_mod(&b, loose, pm);
|
||
} else if (raw == OFPRAW_OFPT16_PORT_MOD) {
|
||
error = ofputil_decode_ofp16_port_mod(&b, loose, pm);
|
||
} else {
|
||
error = OFPERR_OFPBRC_BAD_TYPE;
|
||
}
|
||
|
||
pm->config &= pm->mask;
|
||
return error;
|
||
}
|
||
|
||
/* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
|
||
* message suitable for 'protocol', and returns that encoded form in a buffer
|
||
* owned by the caller. */
|
||
struct ofpbuf *
|
||
ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
|
||
enum ofputil_protocol protocol)
|
||
{
|
||
enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol);
|
||
struct ofpbuf *b;
|
||
|
||
switch (ofp_version) {
|
||
case OFP10_VERSION: {
|
||
struct ofp10_port_mod *opm;
|
||
|
||
b = ofpraw_alloc(OFPRAW_OFPT10_PORT_MOD, ofp_version, 0);
|
||
opm = ofpbuf_put_zeros(b, sizeof *opm);
|
||
opm->port_no = htons(ofp_to_u16(pm->port_no));
|
||
opm->hw_addr = pm->hw_addr;
|
||
opm->config = htonl(pm->config & OFPPC10_ALL);
|
||
opm->mask = htonl(pm->mask & OFPPC10_ALL);
|
||
opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
|
||
break;
|
||
}
|
||
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION: {
|
||
struct ofp11_port_mod *opm;
|
||
|
||
b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0);
|
||
opm = ofpbuf_put_zeros(b, sizeof *opm);
|
||
opm->port_no = ofputil_port_to_ofp11(pm->port_no);
|
||
opm->hw_addr = pm->hw_addr;
|
||
opm->config = htonl(pm->config & OFPPC11_ALL);
|
||
opm->mask = htonl(pm->mask & OFPPC11_ALL);
|
||
opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
|
||
break;
|
||
}
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION: {
|
||
struct ofp14_port_mod *opm;
|
||
|
||
b = ofpraw_alloc(OFPRAW_OFPT14_PORT_MOD, ofp_version, 0);
|
||
opm = ofpbuf_put_zeros(b, sizeof *opm);
|
||
opm->port_no = ofputil_port_to_ofp11(pm->port_no);
|
||
opm->hw_addr = pm->hw_addr;
|
||
opm->config = htonl(pm->config & OFPPC11_ALL);
|
||
opm->mask = htonl(pm->mask & OFPPC11_ALL);
|
||
|
||
if (pm->advertise) {
|
||
ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
|
||
netdev_port_features_to_ofp11(pm->advertise));
|
||
}
|
||
break;
|
||
}
|
||
case OFP16_VERSION: {
|
||
struct ofp16_port_mod *opm;
|
||
|
||
b = ofpraw_alloc(OFPRAW_OFPT16_PORT_MOD, ofp_version, 0);
|
||
opm = ofpbuf_put_zeros(b, sizeof *opm);
|
||
opm->port_no = ofputil_port_to_ofp11(pm->port_no);
|
||
opm->hw_addr = pm->hw_addr;
|
||
opm->hw_addr64 = pm->hw_addr64;
|
||
opm->config = htonl(pm->config & OFPPC11_ALL);
|
||
opm->mask = htonl(pm->mask & OFPPC11_ALL);
|
||
|
||
if (pm->advertise) {
|
||
ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
|
||
netdev_port_features_to_ofp11(pm->advertise));
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
return b;
|
||
}
|
||
|
||
/* Encode a dump ports request for 'port', the encoded message
|
||
* will be for OpenFlow version 'ofp_version'. Returns message
|
||
* as a struct ofpbuf. Returns encoded message on success, NULL on error */
|
||
struct ofpbuf *
|
||
ofputil_encode_dump_ports_request(enum ofp_version ofp_version,
|
||
ofp_port_t port)
|
||
{
|
||
struct ofpbuf *request;
|
||
|
||
switch (ofp_version) {
|
||
case OFP10_VERSION: {
|
||
struct ofp10_port_stats_request *req;
|
||
request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0);
|
||
req = ofpbuf_put_zeros(request, sizeof *req);
|
||
req->port_no = htons(ofp_to_u16(port));
|
||
break;
|
||
}
|
||
case OFP11_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP13_VERSION:
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION:
|
||
case OFP16_VERSION: {
|
||
struct ofp11_port_stats_request *req;
|
||
request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0);
|
||
req = ofpbuf_put_zeros(request, sizeof *req);
|
||
req->port_no = ofputil_port_to_ofp11(port);
|
||
break;
|
||
}
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
return request;
|
||
}
|
||
|
||
static void
|
||
ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops,
|
||
struct ofp10_port_stats *ps10)
|
||
{
|
||
ps10->port_no = htons(ofp_to_u16(ops->port_no));
|
||
memset(ps10->pad, 0, sizeof ps10->pad);
|
||
put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets));
|
||
put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets));
|
||
put_32aligned_be64(&ps10->rx_bytes, htonll(ops->stats.rx_bytes));
|
||
put_32aligned_be64(&ps10->tx_bytes, htonll(ops->stats.tx_bytes));
|
||
put_32aligned_be64(&ps10->rx_dropped, htonll(ops->stats.rx_dropped));
|
||
put_32aligned_be64(&ps10->tx_dropped, htonll(ops->stats.tx_dropped));
|
||
put_32aligned_be64(&ps10->rx_errors, htonll(ops->stats.rx_errors));
|
||
put_32aligned_be64(&ps10->tx_errors, htonll(ops->stats.tx_errors));
|
||
put_32aligned_be64(&ps10->rx_frame_err,
|
||
htonll(ops->stats.rx_frame_errors));
|
||
put_32aligned_be64(&ps10->rx_over_err, htonll(ops->stats.rx_over_errors));
|
||
put_32aligned_be64(&ps10->rx_crc_err, htonll(ops->stats.rx_crc_errors));
|
||
put_32aligned_be64(&ps10->collisions, htonll(ops->stats.collisions));
|
||
}
|
||
|
||
static void
|
||
ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops,
|
||
struct ofp11_port_stats *ps11)
|
||
{
|
||
ps11->port_no = ofputil_port_to_ofp11(ops->port_no);
|
||
memset(ps11->pad, 0, sizeof ps11->pad);
|
||
ps11->rx_packets = htonll(ops->stats.rx_packets);
|
||
ps11->tx_packets = htonll(ops->stats.tx_packets);
|
||
ps11->rx_bytes = htonll(ops->stats.rx_bytes);
|
||
ps11->tx_bytes = htonll(ops->stats.tx_bytes);
|
||
ps11->rx_dropped = htonll(ops->stats.rx_dropped);
|
||
ps11->tx_dropped = htonll(ops->stats.tx_dropped);
|
||
ps11->rx_errors = htonll(ops->stats.rx_errors);
|
||
ps11->tx_errors = htonll(ops->stats.tx_errors);
|
||
ps11->rx_frame_err = htonll(ops->stats.rx_frame_errors);
|
||
ps11->rx_over_err = htonll(ops->stats.rx_over_errors);
|
||
ps11->rx_crc_err = htonll(ops->stats.rx_crc_errors);
|
||
ps11->collisions = htonll(ops->stats.collisions);
|
||
}
|
||
|
||
static void
|
||
ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops,
|
||
struct ofp13_port_stats *ps13)
|
||
{
|
||
ofputil_port_stats_to_ofp11(ops, &ps13->ps);
|
||
ps13->duration_sec = htonl(ops->duration_sec);
|
||
ps13->duration_nsec = htonl(ops->duration_nsec);
|
||
}
|
||
|
||
static void
|
||
ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
|
||
struct ovs_list *replies)
|
||
{
|
||
struct ofp14_port_stats_prop_ethernet *eth;
|
||
struct intel_port_stats_rfc2819 *stats_rfc2819;
|
||
struct intel_port_custom_stats *stats_custom;
|
||
struct ofp14_port_stats *ps14;
|
||
struct ofpbuf *reply;
|
||
uint16_t i;
|
||
ovs_be64 counter_value;
|
||
size_t custom_stats_start, start_ofs;
|
||
|
||
reply = ofpbuf_from_list(ovs_list_back(replies));
|
||
start_ofs = reply->size;
|
||
|
||
ps14 = ofpbuf_put_uninit(reply, sizeof *ps14);
|
||
|
||
memset(ps14->pad, 0, sizeof ps14->pad);
|
||
ps14->port_no = ofputil_port_to_ofp11(ops->port_no);
|
||
ps14->duration_sec = htonl(ops->duration_sec);
|
||
ps14->duration_nsec = htonl(ops->duration_nsec);
|
||
ps14->rx_packets = htonll(ops->stats.rx_packets);
|
||
ps14->tx_packets = htonll(ops->stats.tx_packets);
|
||
ps14->rx_bytes = htonll(ops->stats.rx_bytes);
|
||
ps14->tx_bytes = htonll(ops->stats.tx_bytes);
|
||
ps14->rx_dropped = htonll(ops->stats.rx_dropped);
|
||
ps14->tx_dropped = htonll(ops->stats.tx_dropped);
|
||
ps14->rx_errors = htonll(ops->stats.rx_errors);
|
||
ps14->tx_errors = htonll(ops->stats.tx_errors);
|
||
|
||
eth = ofpprop_put_zeros(reply, OFPPSPT14_ETHERNET, sizeof *eth);
|
||
eth->rx_frame_err = htonll(ops->stats.rx_frame_errors);
|
||
eth->rx_over_err = htonll(ops->stats.rx_over_errors);
|
||
eth->rx_crc_err = htonll(ops->stats.rx_crc_errors);
|
||
eth->collisions = htonll(ops->stats.collisions);
|
||
|
||
uint64_t prop_type_stats = OFPPROP_EXP(INTEL_VENDOR_ID,
|
||
INTEL_PORT_STATS_RFC2819);
|
||
|
||
stats_rfc2819 = ofpprop_put_zeros(reply, prop_type_stats,
|
||
sizeof *stats_rfc2819);
|
||
|
||
memset(stats_rfc2819->pad, 0, sizeof stats_rfc2819->pad);
|
||
stats_rfc2819->rx_1_to_64_packets = htonll(ops->stats.rx_1_to_64_packets);
|
||
stats_rfc2819->rx_65_to_127_packets =
|
||
htonll(ops->stats.rx_65_to_127_packets);
|
||
stats_rfc2819->rx_128_to_255_packets =
|
||
htonll(ops->stats.rx_128_to_255_packets);
|
||
stats_rfc2819->rx_256_to_511_packets =
|
||
htonll(ops->stats.rx_256_to_511_packets);
|
||
stats_rfc2819->rx_512_to_1023_packets =
|
||
htonll(ops->stats.rx_512_to_1023_packets);
|
||
stats_rfc2819->rx_1024_to_1522_packets =
|
||
htonll(ops->stats.rx_1024_to_1522_packets);
|
||
stats_rfc2819->rx_1523_to_max_packets =
|
||
htonll(ops->stats.rx_1523_to_max_packets);
|
||
|
||
stats_rfc2819->tx_1_to_64_packets = htonll(ops->stats.tx_1_to_64_packets);
|
||
stats_rfc2819->tx_65_to_127_packets =
|
||
htonll(ops->stats.tx_65_to_127_packets);
|
||
stats_rfc2819->tx_128_to_255_packets =
|
||
htonll(ops->stats.tx_128_to_255_packets);
|
||
stats_rfc2819->tx_256_to_511_packets =
|
||
htonll(ops->stats.tx_256_to_511_packets);
|
||
stats_rfc2819->tx_512_to_1023_packets =
|
||
htonll(ops->stats.tx_512_to_1023_packets);
|
||
stats_rfc2819->tx_1024_to_1522_packets =
|
||
htonll(ops->stats.tx_1024_to_1522_packets);
|
||
stats_rfc2819->tx_1523_to_max_packets =
|
||
htonll(ops->stats.tx_1523_to_max_packets);
|
||
|
||
stats_rfc2819->tx_multicast_packets =
|
||
htonll(ops->stats.tx_multicast_packets);
|
||
stats_rfc2819->rx_broadcast_packets =
|
||
htonll(ops->stats.rx_broadcast_packets);
|
||
stats_rfc2819->tx_broadcast_packets =
|
||
htonll(ops->stats.tx_broadcast_packets);
|
||
stats_rfc2819->rx_undersized_errors =
|
||
htonll(ops->stats.rx_undersized_errors);
|
||
stats_rfc2819->rx_oversize_errors =
|
||
htonll(ops->stats.rx_oversize_errors);
|
||
stats_rfc2819->rx_fragmented_errors =
|
||
htonll(ops->stats.rx_fragmented_errors);
|
||
stats_rfc2819->rx_jabber_errors =
|
||
htonll(ops->stats.rx_jabber_errors);
|
||
|
||
if (ops->custom_stats.counters && ops->custom_stats.size) {
|
||
custom_stats_start = reply->size;
|
||
|
||
uint64_t prop_type_custom = OFPPROP_EXP(INTEL_VENDOR_ID,
|
||
INTEL_PORT_STATS_CUSTOM);
|
||
|
||
stats_custom = ofpprop_put_zeros(reply, prop_type_custom,
|
||
sizeof *stats_custom);
|
||
|
||
stats_custom->stats_array_size = htons(ops->custom_stats.size);
|
||
|
||
for (i = 0; i < ops->custom_stats.size; i++) {
|
||
uint8_t counter_size = strlen(ops->custom_stats.counters[i].name);
|
||
/* Counter name size */
|
||
ofpbuf_put(reply, &counter_size, sizeof(counter_size));
|
||
/* Counter name */
|
||
ofpbuf_put(reply, ops->custom_stats.counters[i].name,
|
||
counter_size);
|
||
/* Counter value */
|
||
counter_value = htonll(ops->custom_stats.counters[i].value);
|
||
ofpbuf_put(reply, &counter_value,
|
||
sizeof(ops->custom_stats.counters[i].value));
|
||
}
|
||
|
||
ofpprop_end(reply, custom_stats_start);
|
||
}
|
||
|
||
ps14 = ofpbuf_at_assert(reply, start_ofs, sizeof *ps14);
|
||
ps14->length = htons(reply->size - start_ofs);
|
||
|
||
ofpmp_postappend(replies, start_ofs);
|
||
}
|
||
|
||
/* Encode a ports stat for 'ops' and append it to 'replies'. */
|
||
void
|
||
ofputil_append_port_stat(struct ovs_list *replies,
|
||
const struct ofputil_port_stats *ops)
|
||
{
|
||
switch (ofpmp_version(replies)) {
|
||
case OFP13_VERSION: {
|
||
struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply);
|
||
ofputil_port_stats_to_ofp13(ops, reply);
|
||
break;
|
||
}
|
||
case OFP12_VERSION:
|
||
case OFP11_VERSION: {
|
||
struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply);
|
||
ofputil_port_stats_to_ofp11(ops, reply);
|
||
break;
|
||
}
|
||
|
||
case OFP10_VERSION: {
|
||
struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply);
|
||
ofputil_port_stats_to_ofp10(ops, reply);
|
||
break;
|
||
}
|
||
|
||
case OFP14_VERSION:
|
||
case OFP15_VERSION:
|
||
case OFP16_VERSION:
|
||
ofputil_append_ofp14_port_stats(ops, replies);
|
||
break;
|
||
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
|
||
const struct ofp10_port_stats *ps10)
|
||
{
|
||
|
||
ops->port_no = u16_to_ofp(ntohs(ps10->port_no));
|
||
ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets));
|
||
ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets));
|
||
ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes));
|
||
ops->stats.tx_bytes = ntohll(get_32aligned_be64(&ps10->tx_bytes));
|
||
ops->stats.rx_dropped = ntohll(get_32aligned_be64(&ps10->rx_dropped));
|
||
ops->stats.tx_dropped = ntohll(get_32aligned_be64(&ps10->tx_dropped));
|
||
ops->stats.rx_errors = ntohll(get_32aligned_be64(&ps10->rx_errors));
|
||
ops->stats.tx_errors = ntohll(get_32aligned_be64(&ps10->tx_errors));
|
||
ops->stats.rx_frame_errors =
|
||
ntohll(get_32aligned_be64(&ps10->rx_frame_err));
|
||
ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err));
|
||
ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err));
|
||
ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions));
|
||
ops->duration_sec = ops->duration_nsec = UINT32_MAX;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
|
||
const struct ofp11_port_stats *ps11)
|
||
{
|
||
enum ofperr error;
|
||
|
||
error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
ops->stats.rx_packets = ntohll(ps11->rx_packets);
|
||
ops->stats.tx_packets = ntohll(ps11->tx_packets);
|
||
ops->stats.rx_bytes = ntohll(ps11->rx_bytes);
|
||
ops->stats.tx_bytes = ntohll(ps11->tx_bytes);
|
||
ops->stats.rx_dropped = ntohll(ps11->rx_dropped);
|
||
ops->stats.tx_dropped = ntohll(ps11->tx_dropped);
|
||
ops->stats.rx_errors = ntohll(ps11->rx_errors);
|
||
ops->stats.tx_errors = ntohll(ps11->tx_errors);
|
||
ops->stats.rx_frame_errors = ntohll(ps11->rx_frame_err);
|
||
ops->stats.rx_over_errors = ntohll(ps11->rx_over_err);
|
||
ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err);
|
||
ops->stats.collisions = ntohll(ps11->collisions);
|
||
ops->duration_sec = ops->duration_nsec = UINT32_MAX;
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops,
|
||
const struct ofp13_port_stats *ps13)
|
||
{
|
||
enum ofperr error = ofputil_port_stats_from_ofp11(ops, &ps13->ps);
|
||
if (!error) {
|
||
ops->duration_sec = ntohl(ps13->duration_sec);
|
||
ops->duration_nsec = ntohl(ps13->duration_nsec);
|
||
}
|
||
return error;
|
||
}
|
||
|
||
static enum ofperr
|
||
parse_ofp14_port_stats_ethernet_property(const struct ofpbuf *payload,
|
||
struct ofputil_port_stats *ops)
|
||
{
|
||
const struct ofp14_port_stats_prop_ethernet *eth = payload->data;
|
||
|
||
if (payload->size != sizeof *eth) {
|
||
return OFPERR_OFPBPC_BAD_LEN;
|
||
}
|
||
|
||
ops->stats.rx_frame_errors = ntohll(eth->rx_frame_err);
|
||
ops->stats.rx_over_errors = ntohll(eth->rx_over_err);
|
||
ops->stats.rx_crc_errors = ntohll(eth->rx_crc_err);
|
||
ops->stats.collisions = ntohll(eth->collisions);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
parse_intel_port_stats_rfc2819_property(const struct ofpbuf *payload,
|
||
struct ofputil_port_stats *ops)
|
||
{
|
||
const struct intel_port_stats_rfc2819 *rfc2819 = payload->data;
|
||
|
||
if (payload->size != sizeof *rfc2819) {
|
||
return OFPERR_OFPBPC_BAD_LEN;
|
||
}
|
||
ops->stats.rx_1_to_64_packets = ntohll(rfc2819->rx_1_to_64_packets);
|
||
ops->stats.rx_65_to_127_packets = ntohll(rfc2819->rx_65_to_127_packets);
|
||
ops->stats.rx_128_to_255_packets = ntohll(rfc2819->rx_128_to_255_packets);
|
||
ops->stats.rx_256_to_511_packets = ntohll(rfc2819->rx_256_to_511_packets);
|
||
ops->stats.rx_512_to_1023_packets =
|
||
ntohll(rfc2819->rx_512_to_1023_packets);
|
||
ops->stats.rx_1024_to_1522_packets =
|
||
ntohll(rfc2819->rx_1024_to_1522_packets);
|
||
ops->stats.rx_1523_to_max_packets =
|
||
ntohll(rfc2819->rx_1523_to_max_packets);
|
||
|
||
ops->stats.tx_1_to_64_packets = ntohll(rfc2819->tx_1_to_64_packets);
|
||
ops->stats.tx_65_to_127_packets = ntohll(rfc2819->tx_65_to_127_packets);
|
||
ops->stats.tx_128_to_255_packets = ntohll(rfc2819->tx_128_to_255_packets);
|
||
ops->stats.tx_256_to_511_packets = ntohll(rfc2819->tx_256_to_511_packets);
|
||
ops->stats.tx_512_to_1023_packets =
|
||
ntohll(rfc2819->tx_512_to_1023_packets);
|
||
ops->stats.tx_1024_to_1522_packets =
|
||
ntohll(rfc2819->tx_1024_to_1522_packets);
|
||
ops->stats.tx_1523_to_max_packets =
|
||
ntohll(rfc2819->tx_1523_to_max_packets);
|
||
|
||
ops->stats.tx_multicast_packets = ntohll(rfc2819->tx_multicast_packets);
|
||
ops->stats.rx_broadcast_packets = ntohll(rfc2819->rx_broadcast_packets);
|
||
ops->stats.tx_broadcast_packets = ntohll(rfc2819->tx_broadcast_packets);
|
||
ops->stats.rx_undersized_errors = ntohll(rfc2819->rx_undersized_errors);
|
||
|
||
ops->stats.rx_oversize_errors = ntohll(rfc2819->rx_oversize_errors);
|
||
ops->stats.rx_fragmented_errors = ntohll(rfc2819->rx_fragmented_errors);
|
||
ops->stats.rx_jabber_errors = ntohll(rfc2819->rx_jabber_errors);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
parse_intel_port_custom_property(const struct ofpbuf *payload,
|
||
struct ofputil_port_stats *ops)
|
||
{
|
||
const struct intel_port_custom_stats *custom_stats = payload->data;
|
||
|
||
ops->custom_stats.size = ntohs(custom_stats->stats_array_size);
|
||
|
||
ops->custom_stats.counters = xcalloc(ops->custom_stats.size,
|
||
sizeof *ops->custom_stats.counters);
|
||
|
||
uint16_t msg_size = ntohs(custom_stats->length);
|
||
uint16_t current_len = sizeof *custom_stats;
|
||
uint8_t *current = (uint8_t *)payload->data + current_len;
|
||
uint8_t string_size = 0;
|
||
uint8_t value_size = 0;
|
||
ovs_be64 counter_value = 0;
|
||
|
||
for (int i = 0; i < ops->custom_stats.size; i++) {
|
||
current_len += string_size + value_size;
|
||
current += string_size + value_size;
|
||
|
||
value_size = sizeof(uint64_t);
|
||
/* Counter name size */
|
||
string_size = *current;
|
||
|
||
/* Buffer overrun check */
|
||
if (current_len + string_size + value_size > msg_size) {
|
||
VLOG_WARN_RL(&rl, "Custom statistics buffer overrun! "
|
||
"Further message parsing is aborted.");
|
||
break;
|
||
}
|
||
|
||
current++;
|
||
current_len++;
|
||
|
||
/* Counter name. */
|
||
struct netdev_custom_counter *c = &ops->custom_stats.counters[i];
|
||
size_t len = MIN(string_size, sizeof c->name - 1);
|
||
memcpy(c->name, current, len);
|
||
c->name[len] = '\0';
|
||
memcpy(&counter_value, current + string_size, value_size);
|
||
|
||
/* Counter value. */
|
||
c->value = ntohll(counter_value);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
static enum ofperr
|
||
parse_intel_port_stats_property(const struct ofpbuf *payload,
|
||
uint32_t exp_type,
|
||
struct ofputil_port_stats *ops)
|
||
{
|
||
enum ofperr error;
|
||
|
||
switch (exp_type) {
|
||
case INTEL_PORT_STATS_RFC2819:
|
||
error = parse_intel_port_stats_rfc2819_property(payload, ops);
|
||
break;
|
||
case INTEL_PORT_STATS_CUSTOM:
|
||
error = parse_intel_port_custom_property(payload, ops);
|
||
break;
|
||
default:
|
||
error = OFPERR_OFPBPC_BAD_EXP_TYPE;
|
||
break;
|
||
}
|
||
|
||
return error;
|
||
}
|
||
|
||
static enum ofperr
|
||
ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
|
||
struct ofpbuf *msg)
|
||
{
|
||
const struct ofp14_port_stats *ps14 = ofpbuf_try_pull(msg, sizeof *ps14);
|
||
if (!ps14) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
|
||
size_t len = ntohs(ps14->length);
|
||
if (len < sizeof *ps14 || len - sizeof *ps14 > msg->size) {
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
len -= sizeof *ps14;
|
||
|
||
enum ofperr error = ofputil_port_from_ofp11(ps14->port_no, &ops->port_no);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
ops->duration_sec = ntohl(ps14->duration_sec);
|
||
ops->duration_nsec = ntohl(ps14->duration_nsec);
|
||
ops->stats.rx_packets = ntohll(ps14->rx_packets);
|
||
ops->stats.tx_packets = ntohll(ps14->tx_packets);
|
||
ops->stats.rx_bytes = ntohll(ps14->rx_bytes);
|
||
ops->stats.tx_bytes = ntohll(ps14->tx_bytes);
|
||
ops->stats.rx_dropped = ntohll(ps14->rx_dropped);
|
||
ops->stats.tx_dropped = ntohll(ps14->tx_dropped);
|
||
ops->stats.rx_errors = ntohll(ps14->rx_errors);
|
||
ops->stats.tx_errors = ntohll(ps14->tx_errors);
|
||
|
||
|
||
struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
|
||
len);
|
||
while (properties.size > 0) {
|
||
struct ofpbuf payload;
|
||
uint64_t type = 0;
|
||
|
||
error = ofpprop_pull(&properties, &payload, &type);
|
||
if (error) {
|
||
return error;
|
||
}
|
||
switch (type) {
|
||
case OFPPSPT14_ETHERNET:
|
||
error = parse_ofp14_port_stats_ethernet_property(&payload, ops);
|
||
break;
|
||
case OFPPROP_EXP(INTEL_VENDOR_ID, INTEL_PORT_STATS_RFC2819):
|
||
error = parse_intel_port_stats_property(&payload,
|
||
INTEL_PORT_STATS_RFC2819,
|
||
ops);
|
||
break;
|
||
case OFPPROP_EXP(INTEL_VENDOR_ID, INTEL_PORT_STATS_CUSTOM):
|
||
error = parse_intel_port_stats_property(&payload,
|
||
INTEL_PORT_STATS_CUSTOM,
|
||
ops);
|
||
break;
|
||
default:
|
||
error = OFPPROP_UNKNOWN(true, "port stats", type);
|
||
break;
|
||
}
|
||
|
||
if (error) {
|
||
return error;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY
|
||
* message 'oh'. */
|
||
size_t
|
||
ofputil_count_port_stats(const struct ofp_header *oh)
|
||
{
|
||
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
|
||
ofpraw_pull_assert(&b);
|
||
|
||
for (size_t n = 0; ; n++) {
|
||
struct ofputil_port_stats ps;
|
||
if (ofputil_decode_port_stats(&ps, &b)) {
|
||
return n;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract
|
||
* ofputil_port_stats in 'ps'.
|
||
*
|
||
* Multiple OFPST_PORT_STATS replies can be packed into a single OpenFlow
|
||
* message. Calling this function multiple times for a single 'msg' iterates
|
||
* through the replies. The caller must initially leave 'msg''s layer pointers
|
||
* null and not modify them between calls.
|
||
*
|
||
* Returns 0 if successful, EOF if no replies were left in this 'msg',
|
||
* otherwise a positive errno value. */
|
||
int
|
||
ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
|
||
{
|
||
enum ofperr error;
|
||
enum ofpraw raw;
|
||
|
||
memset(&(ps->stats), 0xFF, sizeof (ps->stats));
|
||
memset(&(ps->custom_stats), 0, sizeof (ps->custom_stats));
|
||
|
||
error = (msg->header ? ofpraw_decode(&raw, msg->header)
|
||
: ofpraw_pull(&raw, msg));
|
||
if (error) {
|
||
return error;
|
||
}
|
||
|
||
if (!msg->size) {
|
||
return EOF;
|
||
} else if (raw == OFPRAW_OFPST14_PORT_REPLY) {
|
||
return ofputil_pull_ofp14_port_stats(ps, msg);
|
||
} else if (raw == OFPRAW_OFPST13_PORT_REPLY) {
|
||
const struct ofp13_port_stats *ps13;
|
||
ps13 = ofpbuf_try_pull(msg, sizeof *ps13);
|
||
if (!ps13) {
|
||
goto bad_len;
|
||
}
|
||
return ofputil_port_stats_from_ofp13(ps, ps13);
|
||
} else if (raw == OFPRAW_OFPST11_PORT_REPLY) {
|
||
const struct ofp11_port_stats *ps11;
|
||
|
||
ps11 = ofpbuf_try_pull(msg, sizeof *ps11);
|
||
if (!ps11) {
|
||
goto bad_len;
|
||
}
|
||
return ofputil_port_stats_from_ofp11(ps, ps11);
|
||
} else if (raw == OFPRAW_OFPST10_PORT_REPLY) {
|
||
const struct ofp10_port_stats *ps10;
|
||
|
||
ps10 = ofpbuf_try_pull(msg, sizeof *ps10);
|
||
if (!ps10) {
|
||
goto bad_len;
|
||
}
|
||
return ofputil_port_stats_from_ofp10(ps, ps10);
|
||
} else {
|
||
OVS_NOT_REACHED();
|
||
}
|
||
|
||
bad_len:
|
||
VLOG_WARN_RL(&rl, "OFPST_PORT reply has %"PRIu32" leftover "
|
||
"bytes at end", msg->size);
|
||
return OFPERR_OFPBRC_BAD_LEN;
|
||
}
|
||
|
||
/* Parse a port status request message into a 16 bit OpenFlow 1.0
|
||
* port number and stores the latter in '*ofp10_port'.
|
||
* Returns 0 if successful, otherwise an OFPERR_* number. */
|
||
enum ofperr
|
||
ofputil_decode_port_stats_request(const struct ofp_header *request,
|
||
ofp_port_t *ofp10_port)
|
||
{
|
||
switch ((enum ofp_version)request->version) {
|
||
case OFP16_VERSION:
|
||
case OFP15_VERSION:
|
||
case OFP14_VERSION:
|
||
case OFP13_VERSION:
|
||
case OFP12_VERSION:
|
||
case OFP11_VERSION: {
|
||
const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request);
|
||
return ofputil_port_from_ofp11(psr11->port_no, ofp10_port);
|
||
}
|
||
|
||
case OFP10_VERSION: {
|
||
const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request);
|
||
*ofp10_port = u16_to_ofp(ntohs(psr10->port_no));
|
||
return 0;
|
||
}
|
||
|
||
default:
|
||
OVS_NOT_REACHED();
|
||
}
|
||
}
|
||
|