+/* Creates and returns an OFPT_ECHO_REPLY message matching the
+ * OFPT_ECHO_REQUEST message in 'rq'. */
+struct ofpbuf *
+make_echo_reply(const struct ofp_header *rq)
+{
+ struct ofpbuf rq_buf;
+ struct ofpbuf *reply;
+
+ ofpbuf_use_const(&rq_buf, rq, ntohs(rq->length));
+ ofpraw_pull_assert(&rq_buf);
+
+ reply = ofpraw_alloc_reply(OFPRAW_OFPT_ECHO_REPLY, rq, rq_buf.size);
+ ofpbuf_put(reply, rq_buf.data, rq_buf.size);
+ return reply;
+}
+
+struct ofpbuf *
+ofputil_encode_barrier_request(enum ofp_version ofp_version)
+{
+ enum ofpraw type;
+
+ switch (ofp_version) {
+ case OFP13_VERSION:
+ case OFP12_VERSION:
+ case OFP11_VERSION:
+ type = OFPRAW_OFPT11_BARRIER_REQUEST;
+ break;
+
+ case OFP10_VERSION:
+ type = OFPRAW_OFPT10_BARRIER_REQUEST;
+ break;
+
+ default:
+ NOT_REACHED();
+ }
+
+ return ofpraw_alloc(type, ofp_version, 0);
+}
+
+const char *
+ofputil_frag_handling_to_string(enum ofp_config_flags flags)
+{
+ switch (flags & OFPC_FRAG_MASK) {
+ case OFPC_FRAG_NORMAL: return "normal";
+ case OFPC_FRAG_DROP: return "drop";
+ case OFPC_FRAG_REASM: return "reassemble";
+ case OFPC_FRAG_NX_MATCH: return "nx-match";
+ }
+
+ NOT_REACHED();
+}
+
+bool
+ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
+{
+ if (!strcasecmp(s, "normal")) {
+ *flags = OFPC_FRAG_NORMAL;
+ } else if (!strcasecmp(s, "drop")) {
+ *flags = OFPC_FRAG_DROP;
+ } else if (!strcasecmp(s, "reassemble")) {
+ *flags = OFPC_FRAG_REASM;
+ } else if (!strcasecmp(s, "nx-match")) {
+ *flags = OFPC_FRAG_NX_MATCH;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/* 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;
+ VLOG_WARN_RL(&bad_ofmsg_rl, "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)
+
+/* 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").
+ *
+ * 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, ofp_port_t *portp)
+{
+ uint32_t port32;
+
+ *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[OFP_MAX_PORT_NAME_LEN];
+
+ ofputil_port_to_string(u16_to_ofp(port32), 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 "
+ "%"PRIx16" 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;
+ }
+ }
+ return false;
+ }
+}
+
+/* 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, struct ds *s)
+{
+ char name[OFP_MAX_PORT_NAME_LEN];
+
+ ofputil_port_to_string(port, name, sizeof name);
+ ds_put_cstr(s, name);
+}
+
+/* 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,
+ char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize)
+{
+ switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) \
+ case OFPP_##NAME: \
+ ovs_strlcpy(namebuf, #NAME, bufsize); \
+ break;
+ OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+ default:
+ snprintf(namebuf, bufsize, "%"PRIu16, port);
+ break;
+ }
+}
+
+/* Stores the group id represented by 's' into '*group_idp'. 's' may be an
+ * integer or, for reserved group IDs, the standard OpenFlow name for the group
+ * (either "ANY" or "ALL").
+ *
+ * Returns true if successful, false if 's' is not a valid OpenFlow group ID or
+ * name. */
+bool
+ofputil_group_from_string(const char *s, uint32_t *group_idp)
+{
+ if (!strcasecmp(s, "any")) {
+ *group_idp = OFPG11_ANY;
+ } else if (!strcasecmp(s, "all")) {
+ *group_idp = OFPG11_ALL;
+ } else if (!str_to_uint(s, 10, group_idp)) {
+ VLOG_WARN("%s is not a valid group ID. (Valid group IDs are "
+ "32-bit nonnegative integers or the keywords ANY or "
+ "ALL.)", s);
+ return false;
+ }
+
+ return true;
+}
+
+/* Appends to 's' a string representation of the OpenFlow group ID 'group_id'.
+ * Most groups' string representation is just the number, but for special
+ * groups, e.g. OFPG11_ALL, it is the name, e.g. "ALL". */
+void
+ofputil_format_group(uint32_t group_id, struct ds *s)
+{
+ char name[MAX_GROUP_NAME_LEN];
+
+ ofputil_group_to_string(group_id, name, sizeof name);
+ ds_put_cstr(s, name);
+}
+
+
+/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
+ * representation of OpenFlow group ID 'group_id'. Most group are represented
+ * as just their number, but special groups, e.g. OFPG11_ALL, are represented
+ * by name, e.g. "ALL". */
+void
+ofputil_group_to_string(uint32_t group_id,
+ char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize)
+{
+ switch (group_id) {
+ case OFPG11_ALL:
+ ovs_strlcpy(namebuf, "ALL", bufsize);
+ break;
+
+ case OFPG11_ANY:
+ ovs_strlcpy(namebuf, "ANY", bufsize);
+ break;
+
+ default:
+ snprintf(namebuf, bufsize, "%"PRIu32, group_id);
+ break;
+ }
+}
+
+/* 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)
+{
+ 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;
+ }
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Given a buffer 'b' that contains an array of OpenFlow ports of type
+ * 'ofp_version', returns the number of elements. */
+size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
+{
+ return b->size / ofputil_get_phy_port_size(ofp_version);
+}
+
+/* ofp-util.def lists the mapping from names to action. */
+static const char *const names[OFPUTIL_N_ACTIONS] = {
+ NULL,
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#include "ofp-util.def"
+};
+
+/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
+ * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1
+ * if 'name' is not the name of any action. */
+int
+ofputil_action_code_from_name(const char *name)
+{
+ const char *const *p;
+
+ for (p = names; p < &names[ARRAY_SIZE(names)]; p++) {
+ if (*p && !strcasecmp(name, *p)) {
+ return p - names;
+ }
+ }
+ return -1;
+}
+
+/* Returns name corresponding to the 'enum ofputil_action_code',
+ * or "Unkonwn action", if the name is not available. */
+const char *
+ofputil_action_name_from_code(enum ofputil_action_code code)
+{
+ return code < (int)OFPUTIL_N_ACTIONS && names[code] ? names[code]
+ : "Unknown action";
+}
+
+/* Appends an action of the type specified by 'code' to 'buf' and returns the
+ * action. Initializes the parts of 'action' that identify it as having type
+ * <ENUM> and length 'sizeof *action' and zeros the rest. For actions that
+ * have variable length, the length used and cleared is that of struct
+ * <STRUCT>. */
+void *
+ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
+{
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
+ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#include "ofp-util.def"
+ }
+ NOT_REACHED();
+}
+
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
+ void \
+ ofputil_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(ENUM); \
+ s->len = htons(sizeof *s); \
+ } \
+ \
+ struct STRUCT * \
+ ofputil_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ ofputil_init_##ENUM(s); \
+ return s; \
+ }
+#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ OFPAT10_ACTION(ENUM, STRUCT, NAME)
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ void \
+ ofputil_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(OFPAT10_VENDOR); \
+ s->len = htons(sizeof *s); \
+ s->vendor = htonl(NX_VENDOR_ID); \
+ s->subtype = htons(ENUM); \
+ } \
+ \
+ struct STRUCT * \
+ ofputil_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ ofputil_init_##ENUM(s); \
+ return s; \
+ }
+#include "ofp-util.def"
+
+static void
+ofputil_normalize_match__(struct match *match, bool may_log)
+{
+ enum {
+ MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
+ MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */
+ MAY_NW_PROTO = 1 << 2, /* nw_proto */
+ MAY_IPVx = 1 << 3, /* tos, frag, ttl */
+ MAY_ARP_SHA = 1 << 4, /* arp_sha */
+ MAY_ARP_THA = 1 << 5, /* arp_tha */
+ MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
+ MAY_ND_TARGET = 1 << 7, /* nd_target */
+ MAY_MPLS = 1 << 8, /* mpls label and tc */
+ } may_match;
+
+ struct flow_wildcards wc;
+
+ /* Figure out what fields may be matched. */
+ if (match->flow.dl_type == htons(ETH_TYPE_IP)) {
+ may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
+ if (match->flow.nw_proto == IPPROTO_TCP ||
+ match->flow.nw_proto == IPPROTO_UDP ||
+ match->flow.nw_proto == IPPROTO_SCTP ||
+ match->flow.nw_proto == IPPROTO_ICMP) {
+ may_match |= MAY_TP_ADDR;
+ }
+ } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) {
+ may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
+ if (match->flow.nw_proto == IPPROTO_TCP ||
+ match->flow.nw_proto == IPPROTO_UDP ||
+ match->flow.nw_proto == IPPROTO_SCTP) {
+ may_match |= MAY_TP_ADDR;
+ } else if (match->flow.nw_proto == IPPROTO_ICMPV6) {
+ may_match |= MAY_TP_ADDR;
+ if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_SHA;
+ } else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) {
+ may_match |= MAY_ND_TARGET | MAY_ARP_THA;
+ }
+ }
+ } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) ||
+ match->flow.dl_type == htons(ETH_TYPE_RARP)) {
+ may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
+ } else if (eth_type_mpls(match->flow.dl_type)) {
+ may_match = MAY_MPLS;
+ } else {
+ may_match = 0;
+ }
+
+ /* Clear the fields that may not be matched. */
+ wc = match->wc;
+ if (!(may_match & MAY_NW_ADDR)) {
+ wc.masks.nw_src = wc.masks.nw_dst = htonl(0);
+ }
+ if (!(may_match & MAY_TP_ADDR)) {
+ wc.masks.tp_src = wc.masks.tp_dst = htons(0);
+ }
+ if (!(may_match & MAY_NW_PROTO)) {
+ wc.masks.nw_proto = 0;
+ }
+ if (!(may_match & MAY_IPVx)) {
+ wc.masks.nw_tos = 0;
+ wc.masks.nw_ttl = 0;
+ }
+ if (!(may_match & MAY_ARP_SHA)) {
+ memset(wc.masks.arp_sha, 0, ETH_ADDR_LEN);
+ }
+ if (!(may_match & MAY_ARP_THA)) {
+ memset(wc.masks.arp_tha, 0, ETH_ADDR_LEN);
+ }
+ if (!(may_match & MAY_IPV6)) {
+ wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any;
+ wc.masks.ipv6_label = htonl(0);
+ }
+ if (!(may_match & MAY_ND_TARGET)) {
+ wc.masks.nd_target = in6addr_any;
+ }
+ if (!(may_match & MAY_MPLS)) {
+ wc.masks.mpls_lse = htonl(0);
+ }
+
+ /* Log any changes. */
+ if (!flow_wildcards_equal(&wc, &match->wc)) {
+ bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
+ char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
+
+ match->wc = wc;
+ match_zero_wildcarded_fields(match);
+
+ if (log) {
+ char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+ VLOG_INFO("normalization changed ofp_match, details:");
+ VLOG_INFO(" pre: %s", pre);
+ VLOG_INFO("post: %s", post);
+ free(pre);
+ free(post);
+ }
+ }
+}
+
+/* "Normalizes" the wildcards in 'match'. That means:
+ *
+ * 1. If the type of level N is known, then only the valid fields for that
+ * level may be specified. For example, ARP does not have a TOS field,
+ * so nw_tos must be wildcarded if 'match' specifies an ARP flow.
+ * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and
+ * ipv6_dst (and other fields) must be wildcarded if 'match' specifies an
+ * IPv4 flow.
+ *
+ * 2. If the type of level N is not known (or not understood by Open
+ * vSwitch), then no fields at all for that level may be specified. For
+ * example, Open vSwitch does not understand SCTP, an L4 protocol, so the
+ * L4 fields tp_src and tp_dst must be wildcarded if 'match' specifies an
+ * SCTP flow.
+ *
+ * If this function changes 'match', it logs a rate-limited informational
+ * message. */
+void
+ofputil_normalize_match(struct match *match)
+{
+ ofputil_normalize_match__(match, true);
+}
+
+/* Same as ofputil_normalize_match() without the logging. Thus, this function
+ * is suitable for a program's internal use, whereas ofputil_normalize_match()
+ * sense for use on flows received from elsewhere (so that a bug in the program
+ * that sent them can be reported and corrected). */
+void
+ofputil_normalize_match_quiet(struct match *match)
+{
+ ofputil_normalize_match__(match, false);
+}
+
+/* Parses a key or a key-value pair from '*stringp'.
+ *
+ * On success: Stores the key into '*keyp'. Stores the value, if present, into
+ * '*valuep', otherwise an empty string. Advances '*stringp' past the end of
+ * the key-value pair, preparing it for another call. '*keyp' and '*valuep'
+ * are substrings of '*stringp' created by replacing some of its bytes by null
+ * terminators. Returns true.
+ *
+ * If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to
+ * NULL and returns false. */
+bool
+ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
+{
+ char *pos, *key, *value;
+ size_t key_len;
+
+ pos = *stringp;
+ pos += strspn(pos, ", \t\r\n");
+ if (*pos == '\0') {
+ *keyp = *valuep = NULL;
+ return false;
+ }
+
+ key = pos;
+ key_len = strcspn(pos, ":=(, \t\r\n");
+ if (key[key_len] == ':' || key[key_len] == '=') {
+ /* The value can be separated by a colon. */
+ size_t value_len;
+
+ value = key + key_len + 1;
+ value_len = strcspn(value, ", \t\r\n");
+ pos = value + value_len + (value[value_len] != '\0');
+ value[value_len] = '\0';
+ } else if (key[key_len] == '(') {
+ /* The value can be surrounded by balanced parentheses. The outermost
+ * set of parentheses is removed. */
+ int level = 1;
+ size_t value_len;
+
+ value = key + key_len + 1;
+ for (value_len = 0; level > 0; value_len++) {
+ switch (value[value_len]) {
+ case '\0':
+ level = 0;
+ break;
+
+ case '(':
+ level++;
+ break;
+
+ case ')':
+ level--;
+ break;
+ }
+ }
+ value[value_len - 1] = '\0';
+ pos = value + value_len;
+ } else {
+ /* There might be no value at all. */
+ value = key + key_len; /* Will become the empty string below. */
+ pos = key + key_len + (key[key_len] != '\0');
+ }
+ key[key_len] = '\0';
+
+ *stringp = pos;
+ *keyp = key;
+ *valuep = value;
+ return true;
+}
+
+/* Encode a dump ports request for 'port', the encoded message
+ * will be for Open Flow 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: {
+ 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:
+ 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);
+}
+
+
+/* Encode a ports stat for 'ops' and append it to 'replies'. */
+void
+ofputil_append_port_stat(struct list *replies,
+ const struct ofputil_port_stats *ops)
+{
+ struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
+ struct ofp_header *oh = msg->data;
+
+ switch ((enum ofp_version)oh->version) {
+ 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;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+static enum ofperr
+ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
+ const struct ofp10_port_stats *ps10)
+{
+ memset(ops, 0, sizeof *ops);
+
+ 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;
+
+ memset(ops, 0, sizeof *ops);
+ 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);