+static size_t
+ofputil_get_phy_port_size(enum ofp_version ofp_version)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION:
+ return sizeof(struct ofp10_phy_port);
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ return sizeof(struct ofp11_port);
+ default:
+ NOT_REACHED();
+ }
+}
+
+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(pp->port_no);
+ memcpy(opp->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+ ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+
+ 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);
+ memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
+ ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+
+ 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_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;
+ if (b->size + sizeof *opp <= UINT16_MAX) {
+ opp = ofpbuf_put_uninit(b, sizeof *opp);
+ ofputil_encode_ofp10_phy_port(pp, opp);
+ }
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_port *op;
+ if (b->size + sizeof *op <= UINT16_MAX) {
+ op = ofpbuf_put_uninit(b, sizeof *op);
+ ofputil_encode_ofp11_port(pp, op);
+ }
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+void
+ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version,
+ const struct ofputil_phy_port *pp,
+ struct list *replies)
+{
+ switch (ofp_version) {
+ case OFP10_VERSION: {
+ struct ofp10_phy_port *opp;
+
+ opp = ofpmp_append(replies, sizeof *opp);
+ ofputil_encode_ofp10_phy_port(pp, opp);
+ break;
+ }
+
+ case OFP11_VERSION:
+ case OFP12_VERSION: {
+ struct ofp11_port *op;
+
+ op = ofpmp_append(replies, sizeof *op);
+ ofputil_encode_ofp11_port(pp, op);
+ break;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+\f
+/* ofputil_switch_features */
+
+#define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
+ OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP)
+BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP);
+
+struct ofputil_action_bit_translation {
+ enum ofputil_action_bitmap ofputil_bit;
+ int of_bit;
+};
+
+static const struct ofputil_action_bit_translation of10_action_bits[] = {
+ { OFPUTIL_A_OUTPUT, OFPAT10_OUTPUT },
+ { OFPUTIL_A_SET_VLAN_VID, OFPAT10_SET_VLAN_VID },
+ { OFPUTIL_A_SET_VLAN_PCP, OFPAT10_SET_VLAN_PCP },
+ { OFPUTIL_A_STRIP_VLAN, OFPAT10_STRIP_VLAN },
+ { OFPUTIL_A_SET_DL_SRC, OFPAT10_SET_DL_SRC },
+ { OFPUTIL_A_SET_DL_DST, OFPAT10_SET_DL_DST },
+ { OFPUTIL_A_SET_NW_SRC, OFPAT10_SET_NW_SRC },
+ { OFPUTIL_A_SET_NW_DST, OFPAT10_SET_NW_DST },
+ { OFPUTIL_A_SET_NW_TOS, OFPAT10_SET_NW_TOS },
+ { OFPUTIL_A_SET_TP_SRC, OFPAT10_SET_TP_SRC },
+ { OFPUTIL_A_SET_TP_DST, OFPAT10_SET_TP_DST },
+ { OFPUTIL_A_ENQUEUE, OFPAT10_ENQUEUE },
+ { 0, 0 },
+};
+
+static const struct ofputil_action_bit_translation of11_action_bits[] = {
+ { OFPUTIL_A_OUTPUT, OFPAT11_OUTPUT },
+ { OFPUTIL_A_SET_VLAN_VID, OFPAT11_SET_VLAN_VID },
+ { OFPUTIL_A_SET_VLAN_PCP, OFPAT11_SET_VLAN_PCP },
+ { OFPUTIL_A_SET_DL_SRC, OFPAT11_SET_DL_SRC },
+ { OFPUTIL_A_SET_DL_DST, OFPAT11_SET_DL_DST },
+ { OFPUTIL_A_SET_NW_SRC, OFPAT11_SET_NW_SRC },
+ { OFPUTIL_A_SET_NW_DST, OFPAT11_SET_NW_DST },
+ { OFPUTIL_A_SET_NW_TOS, OFPAT11_SET_NW_TOS },
+ { OFPUTIL_A_SET_NW_ECN, OFPAT11_SET_NW_ECN },
+ { OFPUTIL_A_SET_TP_SRC, OFPAT11_SET_TP_SRC },
+ { OFPUTIL_A_SET_TP_DST, OFPAT11_SET_TP_DST },
+ { OFPUTIL_A_COPY_TTL_OUT, OFPAT11_COPY_TTL_OUT },
+ { OFPUTIL_A_COPY_TTL_IN, OFPAT11_COPY_TTL_IN },
+ { OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL },
+ { OFPUTIL_A_SET_MPLS_TC, OFPAT11_SET_MPLS_TC },
+ { OFPUTIL_A_SET_MPLS_TTL, OFPAT11_SET_MPLS_TTL },
+ { OFPUTIL_A_DEC_MPLS_TTL, OFPAT11_DEC_MPLS_TTL },
+ { OFPUTIL_A_PUSH_VLAN, OFPAT11_PUSH_VLAN },
+ { OFPUTIL_A_POP_VLAN, OFPAT11_POP_VLAN },
+ { OFPUTIL_A_PUSH_MPLS, OFPAT11_PUSH_MPLS },
+ { OFPUTIL_A_POP_MPLS, OFPAT11_POP_MPLS },
+ { OFPUTIL_A_SET_QUEUE, OFPAT11_SET_QUEUE },
+ { OFPUTIL_A_GROUP, OFPAT11_GROUP },
+ { OFPUTIL_A_SET_NW_TTL, OFPAT11_SET_NW_TTL },
+ { OFPUTIL_A_DEC_NW_TTL, OFPAT11_DEC_NW_TTL },
+ { 0, 0 },
+};
+
+static enum ofputil_action_bitmap
+decode_action_bits(ovs_be32 of_actions,
+ const struct ofputil_action_bit_translation *x)
+{
+ enum ofputil_action_bitmap ofputil_actions;
+
+ ofputil_actions = 0;
+ for (; x->ofputil_bit; x++) {
+ if (of_actions & htonl(1u << x->of_bit)) {
+ ofputil_actions |= x->ofputil_bit;
+ }
+ }
+ return ofputil_actions;
+}
+
+/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
+ * abstract representation in '*features'. Initializes '*b' to iterate over
+ * the OpenFlow port structures following 'osf' with later calls to
+ * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an
+ * OFPERR_* value. */
+enum ofperr
+ofputil_decode_switch_features(const struct ofp_header *oh,
+ struct ofputil_switch_features *features,
+ struct ofpbuf *b)
+{
+ const struct ofp_switch_features *osf;
+ enum ofpraw raw;
+
+ ofpbuf_use_const(b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(b);
+
+ osf = ofpbuf_pull(b, sizeof *osf);
+ features->datapath_id = ntohll(osf->datapath_id);
+ features->n_buffers = ntohl(osf->n_buffers);
+ features->n_tables = osf->n_tables;
+
+ features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON;
+
+ if (b->size % ofputil_get_phy_port_size(oh->version)) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ if (raw == OFPRAW_OFPT10_FEATURES_REPLY) {
+ if (osf->capabilities & htonl(OFPC10_STP)) {
+ features->capabilities |= OFPUTIL_C_STP;
+ }
+ features->actions = decode_action_bits(osf->actions, of10_action_bits);
+ } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) {
+ if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
+ features->capabilities |= OFPUTIL_C_GROUP_STATS;
+ }
+ features->actions = decode_action_bits(osf->actions, of11_action_bits);
+ } else {
+ return OFPERR_OFPBRC_BAD_VERSION;
+ }
+
+ return 0;
+}
+
+/* Returns true if the maximum number of ports are in 'oh'. */
+static bool
+max_ports_in_features(const struct ofp_header *oh)
+{
+ size_t pp_size = ofputil_get_phy_port_size(oh->version);
+ return ntohs(oh->length) + pp_size > UINT16_MAX;
+}
+
+/* Given a buffer 'b' that contains a Features Reply message, checks if
+ * it contains the maximum number of ports that will fit. If so, it
+ * returns true and removes the ports from the message. The caller
+ * should then send an OFPST_PORT_DESC stats request to get the ports,
+ * since the switch may have more ports than could be represented in the
+ * Features Reply. Otherwise, returns false.
+ */
+bool
+ofputil_switch_features_ports_trunc(struct ofpbuf *b)
+{
+ struct ofp_header *oh = b->data;
+
+ if (max_ports_in_features(oh)) {
+ /* Remove all the ports. */
+ b->size = (sizeof(struct ofp_header)
+ + sizeof(struct ofp_switch_features));
+ ofpmsg_update_length(b);
+
+ return true;
+ }
+
+ return false;
+}
+
+static ovs_be32
+encode_action_bits(enum ofputil_action_bitmap ofputil_actions,
+ const struct ofputil_action_bit_translation *x)
+{
+ uint32_t of_actions;
+
+ of_actions = 0;
+ for (; x->ofputil_bit; x++) {
+ if (ofputil_actions & x->ofputil_bit) {
+ of_actions |= 1 << x->of_bit;
+ }
+ }
+ return htonl(of_actions);
+}
+
+/* Returns a buffer owned by the caller that encodes 'features' in the format
+ * required by 'protocol' with the given 'xid'. The caller should append port
+ * information to the buffer with subsequent calls to
+ * ofputil_put_switch_features_port(). */
+struct ofpbuf *
+ofputil_encode_switch_features(const struct ofputil_switch_features *features,
+ enum ofputil_protocol protocol, ovs_be32 xid)
+{
+ struct ofp_switch_features *osf;
+ 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_FEATURES_REPLY;
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ raw = OFPRAW_OFPT11_FEATURES_REPLY;
+ break;
+ default:
+ NOT_REACHED();
+ }
+ b = ofpraw_alloc_xid(raw, version, xid, 0);
+ osf = ofpbuf_put_zeros(b, sizeof *osf);
+ osf->datapath_id = htonll(features->datapath_id);
+ osf->n_buffers = htonl(features->n_buffers);
+ osf->n_tables = features->n_tables;
+
+ osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
+ switch (version) {
+ case OFP10_VERSION:
+ if (features->capabilities & OFPUTIL_C_STP) {
+ osf->capabilities |= htonl(OFPC10_STP);
+ }
+ osf->actions = encode_action_bits(features->actions, of10_action_bits);
+ break;
+ case OFP11_VERSION:
+ case OFP12_VERSION:
+ if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
+ osf->capabilities |= htonl(OFPC11_GROUP_STATS);
+ }
+ osf->actions = encode_action_bits(features->actions, of11_action_bits);
+ break;
+ default:
+ NOT_REACHED();
+ }
+
+ return b;
+}
+
+/* Encodes 'pp' into the format required by the switch_features message already
+ * in 'b', which should have been returned by ofputil_encode_switch_features(),
+ * and appends the encoded version to 'b'. */
+void
+ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
+ struct ofpbuf *b)
+{
+ const struct ofp_header *oh = b->data;
+
+ ofputil_put_phy_port(oh->version, pp, b);
+}
+\f
+/* 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)
+{
+ const struct ofp_port_status *ops;
+ struct ofpbuf b;
+ int retval;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+ 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;
+
+ retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc);
+ 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:
+ raw = OFPRAW_OFPT11_PORT_STATUS;
+ break;
+
+ default:
+ 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;
+}
+\f
+/* ofputil_port_mod */
+
+/* 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)
+{
+ enum ofpraw raw;
+ struct ofpbuf b;
+
+ ofpbuf_use_const(&b, oh, ntohs(oh->length));
+ raw = ofpraw_pull_assert(&b);
+
+ if (raw == OFPRAW_OFPT10_PORT_MOD) {
+ const struct ofp10_port_mod *opm = b.data;
+
+ pm->port_no = ntohs(opm->port_no);
+ memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+ pm->config = ntohl(opm->config) & OFPPC10_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
+ pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
+ } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+ const struct ofp11_port_mod *opm = b.data;
+ enum ofperr error;
+
+ error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
+
+ memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+ pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+ } else {
+ return OFPERR_OFPBRC_BAD_TYPE;
+ }
+
+ pm->config &= pm->mask;
+ return 0;
+}
+
+/* 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)