From f25d0cf3c366d92042269a4f787f19c741c2530c Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Tue, 3 Jul 2012 22:17:14 -0700 Subject: [PATCH] Introduce ofpacts, an abstraction of OpenFlow actions. OpenFlow actions have always been somewhat awkward to handle. Moreover, over time we've started creating actions that require more complicated parsing. When we maintain those actions internally in their wire format, we end up parsing them multiple times, whenever we have to look at the set of actions. When we add support for OpenFlow 1.1 or later protocols, the situation will get worse, because these newer protocols support many of the same actions but with different representations. It becomes unrealistic to handle each protocol in its wire format. This commit adopts a new strategy, by converting OpenFlow actions into an internal form from the wire format when they are read, and converting them back to the wire format when flows are dumped. I believe that this will be more maintainable over time. Thanks to Simon Horman and Pravin Shelar for reviews. Signed-off-by: Ben Pfaff --- DESIGN | 20 + NEWS | 3 + lib/automake.mk | 2 + lib/autopath.c | 70 ++- lib/autopath.h | 15 +- lib/bundle.c | 277 ++++---- lib/bundle.h | 26 +- lib/compiler.h | 20 +- lib/learn.c | 672 ++++++++++---------- lib/learn.h | 17 +- lib/learning-switch.c | 99 ++- lib/multipath.c | 159 +++-- lib/multipath.h | 18 +- lib/nx-match.c | 217 +++---- lib/nx-match.h | 35 +- lib/ofp-actions.c | 1218 ++++++++++++++++++++++++++++++++++++ lib/ofp-actions.h | 485 ++++++++++++++ lib/ofp-parse.c | 252 ++++---- lib/ofp-parse.h | 2 +- lib/ofp-print.c | 291 +-------- lib/ofp-print.h | 2 - lib/ofp-util.c | 526 +++------------- lib/ofp-util.h | 69 +- ofproto/connmgr.c | 15 +- ofproto/fail-open.c | 14 +- ofproto/in-band.c | 37 +- ofproto/ofproto-dpif.c | 401 ++++++------ ofproto/ofproto-provider.h | 34 +- ofproto/ofproto.c | 122 ++-- tests/automake.mk | 1 + tests/learn.at | 4 +- tests/ofp-actions.at | 121 ++++ tests/test-bundle.c | 55 +- tests/test-multipath.c | 11 +- tests/testsuite.at | 1 + utilities/ovs-ofctl.c | 133 +++- 36 files changed, 3356 insertions(+), 2088 deletions(-) create mode 100644 lib/ofp-actions.c create mode 100644 lib/ofp-actions.h create mode 100644 tests/ofp-actions.at diff --git a/DESIGN b/DESIGN index a3a62b2e..7dd6efa2 100644 --- a/DESIGN +++ b/DESIGN @@ -612,6 +612,26 @@ The following are explicitly *not* supported by in-band control: gateway. +Action Reproduction +=================== + +It seems likely that many controllers, at least at startup, use the +OpenFlow "flow statistics" request to obtain existing flows, then +compare the flows' actions against the actions that they expect to +find. Before version 1.8.0, Open vSwitch always returned exact, +byte-for-byte copies of the actions that had been added to the flow +table. The current version of Open vSwitch does not always do this in +some exceptional cases. This section lists the exceptions that +controller authors must keep in mind if they compare actual actions +against desired actions in a bytewise fashion: + + - Open vSwitch zeros padding bytes in action structures, + regardless of their values when the flows were added. + +Please report other discrepancies, if you notice any, so that we can +fix or document them. + + Suggestions =========== diff --git a/NEWS b/NEWS index 1966ac21..84f1ec11 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ post-v1.7.0 ------------------------ - New FAQ. Please send updates and additions! + - Authors of controllers, please read the new section titled "Action + Reproduction" in DESIGN, which describes an Open vSwitch change in + behavior in corner cases that may affect some controllers. - ovs-l3ping: - A new test utility that can create L3 tunnel between two Open vSwitches and detect connectivity issues. diff --git a/lib/automake.mk b/lib/automake.mk index feac1d45..de4ec031 100644 --- a/lib/automake.mk +++ b/lib/automake.mk @@ -96,6 +96,8 @@ lib_libopenvswitch_a_SOURCES = \ lib/nx-match.h \ lib/odp-util.c \ lib/odp-util.h \ + lib/ofp-actions.c \ + lib/ofp-actions.h \ lib/ofp-errors.c \ lib/ofp-errors.h \ lib/ofp-parse.c \ diff --git a/lib/autopath.c b/lib/autopath.c index 9511a6d2..93fedd1a 100644 --- a/lib/autopath.c +++ b/lib/autopath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Nicira, Inc. + * Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ #include "flow.h" #include "meta-flow.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/nicira-ext.h" @@ -31,32 +32,21 @@ VLOG_DEFINE_THIS_MODULE(autopath); -/* Loads 'ofp_port' into the appropriate register in accordance with the - * autopath action. */ void -autopath_execute(const struct nx_action_autopath *ap, struct flow *flow, - uint16_t ofp_port) -{ - struct mf_subfield dst; - - nxm_decode(&dst, ap->dst, ap->ofs_nbits); - mf_set_subfield_value(&dst, ofp_port, flow); -} - -void -autopath_parse(struct nx_action_autopath *ap, const char *s_) +autopath_parse(struct ofpact_autopath *ap, const char *s_) { char *s; - char *id_str, *dst_s, *save_ptr; - struct mf_subfield dst; int id_int; + char *id_str, *dst, *save_ptr; + + ofpact_init_AUTOPATH(ap); s = xstrdup(s_); save_ptr = NULL; id_str = strtok_r(s, ", ", &save_ptr); - dst_s = strtok_r(NULL, ", ", &save_ptr); + dst = strtok_r(NULL, ", ", &save_ptr); - if (!dst_s) { + if (!dst) { ovs_fatal(0, "%s: not enough arguments to autopath action", s_); } @@ -65,33 +55,51 @@ autopath_parse(struct nx_action_autopath *ap, const char *s_) ovs_fatal(0, "%s: autopath id %d is not in valid range " "1 to %"PRIu32, s_, id_int, UINT32_MAX); } + ap->port = id_int; - mf_parse_subfield(&dst, dst_s); - if (dst.n_bits < 16) { + mf_parse_subfield(&ap->dst, dst); + if (ap->dst.n_bits < 16) { ovs_fatal(0, "%s: %d-bit destination field has %u possible values, " "less than required 65536", - s_, dst.n_bits, 1u << dst.n_bits); + s_, ap->dst.n_bits, 1u << ap->dst.n_bits); } - ofputil_init_NXAST_AUTOPATH(ap); - ap->id = htonl(id_int); - ap->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); - ap->dst = htonl(dst.field->nxm_header); - free(s); } enum ofperr -autopath_check(const struct nx_action_autopath *ap, const struct flow *flow) +autopath_from_openflow(const struct nx_action_autopath *nap, + struct ofpact_autopath *autopath) { - struct mf_subfield dst; + ofpact_init_AUTOPATH(autopath); + autopath->dst.field = mf_from_nxm_header(ntohl(nap->dst)); + autopath->dst.ofs = nxm_decode_ofs(nap->ofs_nbits); + autopath->dst.n_bits = nxm_decode_n_bits(nap->ofs_nbits); + autopath->port = ntohl(nap->id); - nxm_decode(&dst, ap->dst, ap->ofs_nbits); - if (dst.n_bits < 16) { + if (autopath->dst.n_bits < 16) { VLOG_WARN("at least 16 bit destination is required for autopath " "action."); return OFPERR_OFPBAC_BAD_ARGUMENT; } - return mf_check_dst(&dst, flow); + return autopath_check(autopath, NULL); +} + +enum ofperr +autopath_check(const struct ofpact_autopath *autopath, const struct flow *flow) +{ + return mf_check_dst(&autopath->dst, flow); +} + +void +autopath_to_nxast(const struct ofpact_autopath *autopath, + struct ofpbuf *openflow) +{ + struct nx_action_autopath *ap = ofputil_put_NXAST_AUTOPATH(openflow); + + ap->ofs_nbits = nxm_encode_ofs_nbits(autopath->dst.ofs, + autopath->dst.n_bits); + ap->dst = htonl(autopath->dst.field->nxm_header); + ap->id = htonl(autopath->port); } diff --git a/lib/autopath.h b/lib/autopath.h index 480d40ae..337e7d1f 100644 --- a/lib/autopath.h +++ b/lib/autopath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Nicira, Inc. + * Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,15 +22,20 @@ struct flow; struct nx_action_autopath; +struct ofpact_autopath; +struct ofpbuf; /* NXAST_AUTOPATH helper functions. * * See include/openflow/nicira-ext.h for NXAST_AUTOPATH specification. */ -void autopath_execute(const struct nx_action_autopath *, struct flow *, - uint16_t ofp_port); -void autopath_parse(struct nx_action_autopath *, const char *); -enum ofperr autopath_check(const struct nx_action_autopath *, +void autopath_parse(struct ofpact_autopath *, const char *); + +enum ofperr autopath_from_openflow(const struct nx_action_autopath *, + struct ofpact_autopath *); +enum ofperr autopath_check(const struct ofpact_autopath *, const struct flow *); +void autopath_to_nxast(const struct ofpact_autopath *, + struct ofpbuf *openflow); #endif /* autopath.h */ diff --git a/lib/bundle.c b/lib/bundle.c index a2059740..c6b1f75e 100644 --- a/lib/bundle.c +++ b/lib/bundle.c @@ -25,6 +25,7 @@ #include "meta-flow.h" #include "nx-match.h" #include "ofpbuf.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/nicira-ext.h" @@ -35,14 +36,13 @@ VLOG_DEFINE_THIS_MODULE(bundle); static uint16_t -execute_ab(const struct nx_action_bundle *nab, +execute_ab(const struct ofpact_bundle *bundle, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux) { size_t i; - for (i = 0; i < ntohs(nab->n_slaves); i++) { - uint16_t slave = bundle_get_slave(nab, i); - + for (i = 0; i < bundle->n_slaves; i++) { + uint16_t slave = bundle->slaves[i]; if (slave_enabled(slave, aux)) { return slave; } @@ -52,18 +52,18 @@ execute_ab(const struct nx_action_bundle *nab, } static uint16_t -execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow, +execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux) { uint32_t flow_hash, best_hash; int best, i; - flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis)); + flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis); best = -1; best_hash = 0; - for (i = 0; i < ntohs(nab->n_slaves); i++) { - if (slave_enabled(bundle_get_slave(nab, i), aux)) { + for (i = 0; i < bundle->n_slaves; i++) { + if (slave_enabled(bundle->slaves[i], aux)) { uint32_t hash = hash_2words(i, flow_hash); if (best < 0 || hash > best_hash) { @@ -73,33 +73,26 @@ execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow, } } - return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE; + return best >= 0 ? bundle->slaves[best] : OFPP_NONE; } -/* Executes 'nab' on 'flow'. Uses 'slave_enabled' to determine if the slave +/* Executes 'bundle' on 'flow'. Uses 'slave_enabled' to determine if the slave * designated by 'ofp_port' is up. Returns the chosen slave, or OFPP_NONE if * none of the slaves are acceptable. */ uint16_t -bundle_execute(const struct nx_action_bundle *nab, const struct flow *flow, +bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux) { - switch (ntohs(nab->algorithm)) { - case NX_BD_ALG_HRW: return execute_hrw(nab, flow, slave_enabled, aux); - case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(nab, slave_enabled, aux); - default: NOT_REACHED(); - } -} + switch (bundle->algorithm) { + case NX_BD_ALG_HRW: + return execute_hrw(bundle, flow, slave_enabled, aux); -void -bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow, - bool (*slave_enabled)(uint16_t ofp_port, void *aux), - void *aux) -{ - struct mf_subfield dst; + case NX_BD_ALG_ACTIVE_BACKUP: + return execute_ab(bundle, slave_enabled, aux); - nxm_decode(&dst, nab->dst, nab->ofs_nbits); - mf_set_subfield_value(&dst, bundle_execute(nab, flow, slave_enabled, aux), - flow); + default: + NOT_REACHED(); + } } /* Checks that 'nab' specifies a bundle action which is supported by this @@ -107,41 +100,43 @@ bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow, * ofputil_check_output_port(). Returns 0 if 'nab' is supported, otherwise an * OFPERR_* error code. */ enum ofperr -bundle_check(const struct nx_action_bundle *nab, int max_ports, - const struct flow *flow) +bundle_from_openflow(const struct nx_action_bundle *nab, + struct ofpbuf *ofpacts) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - uint16_t n_slaves, fields, algorithm, subtype; + struct ofpact_bundle *bundle; + uint16_t subtype; uint32_t slave_type; size_t slaves_size, i; enum ofperr error; + bundle = ofpact_put_BUNDLE(ofpacts); + subtype = ntohs(nab->subtype); - n_slaves = ntohs(nab->n_slaves); - fields = ntohs(nab->fields); - algorithm = ntohs(nab->algorithm); + bundle->n_slaves = ntohs(nab->n_slaves); + bundle->basis = ntohs(nab->basis); + bundle->fields = ntohs(nab->fields); + bundle->algorithm = ntohs(nab->algorithm); slave_type = ntohl(nab->slave_type); slaves_size = ntohs(nab->len) - sizeof *nab; error = OFPERR_OFPBAC_BAD_ARGUMENT; - if (!flow_hash_fields_valid(fields)) { - VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields); - } else if (n_slaves > BUNDLE_MAX_SLAVES) { + if (!flow_hash_fields_valid(bundle->fields)) { + VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields); + } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) { VLOG_WARN_RL(&rl, "too may slaves"); - } else if (algorithm != NX_BD_ALG_HRW - && algorithm != NX_BD_ALG_ACTIVE_BACKUP) { - VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, algorithm); + } else if (bundle->algorithm != NX_BD_ALG_HRW + && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) { + VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm); } else if (slave_type != NXM_OF_IN_PORT) { VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type); } else { error = 0; } - for (i = 0; i < sizeof(nab->zero); i++) { - if (nab->zero[i]) { - VLOG_WARN_RL(&rl, "reserved field is nonzero"); - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } + if (!is_all_zeros(nab->zero, sizeof nab->zero)) { + VLOG_WARN_RL(&rl, "reserved field is nonzero"); + error = OFPERR_OFPBAC_BAD_ARGUMENT; } if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) { @@ -150,34 +145,61 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports, } if (subtype == NXAST_BUNDLE_LOAD) { - struct mf_subfield dst; + bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst)); + bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits); + bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits); - nxm_decode(&dst, nab->dst, nab->ofs_nbits); - if (dst.n_bits < 16) { + if (bundle->dst.n_bits < 16) { VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit " "destination."); error = OFPERR_OFPBAC_BAD_ARGUMENT; - } else if (!error) { - error = mf_check_dst(&dst, flow); } } - if (slaves_size < n_slaves * sizeof(ovs_be16)) { + if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) { VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes " "allocated for slaves. %zu bytes are required for " "%"PRIu16" slaves.", subtype, slaves_size, - n_slaves * sizeof(ovs_be16), n_slaves); + bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves); error = OFPERR_OFPBAC_BAD_LEN; } - for (i = 0; i < n_slaves; i++) { - uint16_t ofp_port = bundle_get_slave(nab, i); - enum ofperr ofputil_error; + for (i = 0; i < bundle->n_slaves; i++) { + uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]); + ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port); + } + + bundle = ofpacts->l2; + ofpact_update_len(ofpacts, &bundle->ofpact); + + if (!error) { + error = bundle_check(bundle, OFPP_MAX, NULL); + } + return error; +} + +enum ofperr +bundle_check(const struct ofpact_bundle *bundle, int max_ports, + const struct flow *flow) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + size_t i; + + if (bundle->dst.field) { + enum ofperr error = mf_check_dst(&bundle->dst, flow); + if (error) { + return error; + } + } + + for (i = 0; i < bundle->n_slaves; i++) { + uint16_t ofp_port = bundle->slaves[i]; + enum ofperr error; - ofputil_error = ofputil_check_output_port(ofp_port, max_ports); - if (ofputil_error) { + error = ofputil_check_output_port(ofp_port, max_ports); + if (error) { VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port); - error = ofputil_error; + return error; } /* Controller slaves are unsupported due to the lack of a max_len @@ -185,23 +207,50 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports, * seem to be a real-world use-case for supporting it. */ if (ofp_port == OFPP_CONTROLLER) { VLOG_WARN_RL(&rl, "unsupported controller slave"); - error = OFPERR_OFPBAC_BAD_OUT_PORT; + return OFPERR_OFPBAC_BAD_OUT_PORT; } } - return error; + return 0; +} + +void +bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow) +{ + int slaves_len = ROUND_UP(bundle->n_slaves, OFP_ACTION_ALIGN); + struct nx_action_bundle *nab; + ovs_be16 *slaves; + size_t i; + + nab = (bundle->dst.field + ? ofputil_put_NXAST_BUNDLE_LOAD(openflow) + : ofputil_put_NXAST_BUNDLE(openflow)); + nab->len = htons(ntohs(nab->len) + slaves_len); + nab->algorithm = htons(bundle->algorithm); + nab->fields = htons(bundle->fields); + nab->basis = htons(bundle->basis); + nab->slave_type = htonl(NXM_OF_IN_PORT); + nab->n_slaves = htons(bundle->n_slaves); + if (bundle->dst.field) { + nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs, + bundle->dst.n_bits); + nab->dst = htonl(bundle->dst.field->nxm_header); + } + + slaves = ofpbuf_put_zeros(openflow, slaves_len); + for (i = 0; i < bundle->n_slaves; i++) { + slaves[i] = htons(bundle->slaves[i]); + } } /* Helper for bundle_parse and bundle_parse_load. */ static void -bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr, +bundle_parse__(const char *s, char **save_ptr, const char *fields, const char *basis, const char *algorithm, - const char *slave_type, const char *dst_s, - const char *slave_delim) + const char *slave_type, const char *dst, + const char *slave_delim, struct ofpbuf *ofpacts) { - enum ofputil_action_code code; - struct nx_action_bundle *nab; - uint16_t n_slaves; + struct ofpact_bundle *bundle; if (!slave_delim) { ovs_fatal(0, "%s: not enough arguments to bundle action", s); @@ -212,72 +261,56 @@ bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr, s, slave_delim); } - code = dst_s ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE; - b->l2 = ofputil_put_action(code, b); + bundle = ofpact_put_BUNDLE(ofpacts); - n_slaves = 0; for (;;) { - ovs_be16 slave_be; + uint16_t slave_port; char *slave; slave = strtok_r(NULL, ", [", save_ptr); - if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) { + if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) { break; } - slave_be = htons(atoi(slave)); - ofpbuf_put(b, &slave_be, sizeof slave_be); + slave_port = atoi(slave); + ofpbuf_put(ofpacts, &slave_port, sizeof slave_port); - n_slaves++; + bundle = ofpacts->l2; + bundle->n_slaves++; } + ofpact_update_len(ofpacts, &bundle->ofpact); - /* Slaves array must be multiple of 8 bytes long. */ - if (b->size % 8) { - ofpbuf_put_zeros(b, 8 - (b->size % 8)); - } - - nab = b->l2; - nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data)); - nab->n_slaves = htons(n_slaves); - nab->basis = htons(atoi(basis)); + bundle->basis = atoi(basis); if (!strcasecmp(fields, "eth_src")) { - nab->fields = htons(NX_HASH_FIELDS_ETH_SRC); + bundle->fields = NX_HASH_FIELDS_ETH_SRC; } else if (!strcasecmp(fields, "symmetric_l4")) { - nab->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4); + bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4; } else { ovs_fatal(0, "%s: unknown fields `%s'", s, fields); } if (!strcasecmp(algorithm, "active_backup")) { - nab->algorithm = htons(NX_BD_ALG_ACTIVE_BACKUP); + bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP; } else if (!strcasecmp(algorithm, "hrw")) { - nab->algorithm = htons(NX_BD_ALG_HRW); + bundle->algorithm = NX_BD_ALG_HRW; } else { ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm); } - if (!strcasecmp(slave_type, "ofport")) { - nab->slave_type = htonl(NXM_OF_IN_PORT); - } else { + if (strcasecmp(slave_type, "ofport")) { ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type); } - if (dst_s) { - struct mf_subfield dst; - - mf_parse_subfield(&dst, dst_s); - nab->dst = htonl(dst.field->nxm_header); - nab->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); + if (dst) { + mf_parse_subfield(&bundle->dst, dst); } - - b->l2 = NULL; } /* Converts a bundle action string contained in 's' to an nx_action_bundle and * stores it in 'b'. Sets 'b''s l2 pointer to NULL. */ void -bundle_parse(struct ofpbuf *b, const char *s) +bundle_parse(const char *s, struct ofpbuf *ofpacts) { char *fields, *basis, *algorithm, *slave_type, *slave_delim; char *tokstr, *save_ptr; @@ -290,15 +323,15 @@ bundle_parse(struct ofpbuf *b, const char *s) slave_type = strtok_r(NULL, ", ", &save_ptr); slave_delim = strtok_r(NULL, ": ", &save_ptr); - bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL, - slave_delim); + bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL, + slave_delim, ofpacts); free(tokstr); } /* Converts a bundle_load action string contained in 's' to an nx_action_bundle * and stores it in 'b'. Sets 'b''s l2 pointer to NULL. */ void -bundle_parse_load(struct ofpbuf *b, const char *s) +bundle_parse_load(const char *s, struct ofpbuf *ofpacts) { char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim; char *tokstr, *save_ptr; @@ -312,22 +345,22 @@ bundle_parse_load(struct ofpbuf *b, const char *s) dst = strtok_r(NULL, ", ", &save_ptr); slave_delim = strtok_r(NULL, ": ", &save_ptr); - bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst, - slave_delim); + bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst, + slave_delim, ofpacts); free(tokstr); } /* Appends a human-readable representation of 'nab' to 's'. */ void -bundle_format(const struct nx_action_bundle *nab, struct ds *s) +bundle_format(const struct ofpact_bundle *bundle, struct ds *s) { - const char *action, *fields, *algorithm, *slave_type; + const char *action, *fields, *algorithm; size_t i; - fields = flow_hash_fields_to_str(ntohs(nab->fields)); + fields = flow_hash_fields_to_str(bundle->fields); - switch (ntohs(nab->algorithm)) { + switch (bundle->algorithm) { case NX_BD_ALG_HRW: algorithm = "hrw"; break; @@ -338,43 +371,23 @@ bundle_format(const struct nx_action_bundle *nab, struct ds *s) algorithm = ""; } - switch (ntohl(nab->slave_type)) { - case NXM_OF_IN_PORT: - slave_type = "ofport"; - break; - default: - slave_type = ""; - } - - switch (ntohs(nab->subtype)) { - case NXAST_BUNDLE: - action = "bundle"; - break; - case NXAST_BUNDLE_LOAD: - action = "bundle_load"; - break; - default: - NOT_REACHED(); - } + action = bundle->dst.field ? "bundle_load" : "bundle"; ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields, - ntohs(nab->basis), algorithm, slave_type); - - if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) { - struct mf_subfield dst; + bundle->basis, algorithm, "ofport"); - nxm_decode(&dst, nab->dst, nab->ofs_nbits); - mf_format_subfield(&dst, s); + if (bundle->dst.field) { + mf_format_subfield(&bundle->dst, s); ds_put_cstr(s, ","); } ds_put_cstr(s, "slaves:"); - for (i = 0; i < ntohs(nab->n_slaves); i++) { + for (i = 0; i < bundle->n_slaves; i++) { if (i) { ds_put_cstr(s, ","); } - ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i)); + ds_put_format(s, "%"PRIu16, bundle->slaves[i]); } ds_put_cstr(s, ")"); diff --git a/lib/bundle.h b/lib/bundle.h index 4c0dff5d..5b6bb674 100644 --- a/lib/bundle.h +++ b/lib/bundle.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 Nicira, Inc. +/* Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,29 +27,23 @@ struct ds; struct flow; +struct ofpact_bundle; struct ofpbuf; /* NXAST_BUNDLE helper functions. * * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */ -uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *, +uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux); -void bundle_execute_load(const struct nx_action_bundle *, struct flow *, - bool (*slave_enabled)(uint16_t ofp_port, void *aux), - void *aux); -enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports, +enum ofperr bundle_from_openflow(const struct nx_action_bundle *, + struct ofpbuf *ofpact); +enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports, const struct flow *); -void bundle_parse(struct ofpbuf *, const char *); -void bundle_parse_load(struct ofpbuf *b, const char *); -void bundle_format(const struct nx_action_bundle *, struct ds *); - -/* Returns the 'i'th slave in 'nab'. */ -static inline uint16_t -bundle_get_slave(const struct nx_action_bundle *nab, size_t i) -{ - return ntohs(((ovs_be16 *)(nab + 1))[i]); -} +void bundle_to_nxast(const struct ofpact_bundle *, struct ofpbuf *of10); +void bundle_parse(const char *, struct ofpbuf *ofpacts); +void bundle_parse_load(const char *, struct ofpbuf *ofpacts); +void bundle_format(const struct ofpact_bundle *, struct ds *); #endif /* bundle.h */ diff --git a/lib/compiler.h b/lib/compiler.h index 75e86103..27612a74 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,4 +37,22 @@ #define SENTINEL(N) #endif +/* ISO C says that a C implementation may choose any integer type for an enum + * that is sufficient to hold all of its values. Common ABIs (such as the + * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even + * when a smaller type would suffice. + * + * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an + * enum compatible with a type that is no bigger than necessary. This is the + * intended use of OVS_PACKED_ENUM. + * + * OVS_PACKED_ENUM is intended for use only as a space optimization, since it + * only works with GCC. That means that it must not be used in wire protocols + * or otherwise exposed outside of a single process. */ +#if __GNUC__ && !__CHECKER__ +#define OVS_PACKED_ENUM __attribute__((__packed__)) +#else +#define OVS_PACKED_ENUM +#endif + #endif /* compiler.h */ diff --git a/lib/learn.c b/lib/learn.c index 5478b748..28b8012c 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -22,6 +22,7 @@ #include "dynamic-string.h" #include "meta-flow.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -46,19 +47,6 @@ get_be32(const void **pp) return value; } -static uint64_t -get_bits(int n_bits, const void **p) -{ - int n_segs = DIV_ROUND_UP(n_bits, 16); - uint64_t value; - - value = 0; - while (n_segs-- > 0) { - value = (value << 16) | ntohs(get_be16(p)); - } - return value; -} - static void get_subfield(int n_bits, const void **p, struct mf_subfield *sf) { @@ -90,105 +78,85 @@ learn_min_len(uint16_t header) return min_len; } -static enum ofperr -learn_check_header(uint16_t header, size_t len) +/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to + * 'ofpacts'. Returns 0 if successful, otherwise an OFPERR_*. */ +enum ofperr +learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts) { - int src_type = header & NX_LEARN_SRC_MASK; - int dst_type = header & NX_LEARN_DST_MASK; + struct ofpact_learn *learn; + const void *p, *end; - /* Check for valid src and dst type combination. */ - if (dst_type == NX_LEARN_DST_MATCH || - dst_type == NX_LEARN_DST_LOAD || - (dst_type == NX_LEARN_DST_OUTPUT && - src_type == NX_LEARN_SRC_FIELD)) { - /* OK. */ - } else { + if (nal->pad) { return OFPERR_OFPBAC_BAD_ARGUMENT; } - /* Check that the arguments don't overrun the end of the action. */ - if (len < learn_min_len(header)) { - return OFPERR_OFPBAC_BAD_LEN; - } + learn = ofpact_put_LEARN(ofpacts); - return 0; -} - -/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a - * valid action on 'flow'. */ -enum ofperr -learn_check(const struct nx_action_learn *learn, const struct flow *flow) -{ - struct cls_rule rule; - const void *p, *end; - - cls_rule_init_catchall(&rule, 0); + learn->idle_timeout = ntohs(nal->idle_timeout); + learn->hard_timeout = ntohs(nal->hard_timeout); + learn->priority = ntohs(nal->priority); + learn->cookie = ntohll(nal->cookie); + learn->flags = ntohs(nal->flags); + learn->table_id = nal->table_id; + learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout); + learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout); - if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM) - || learn->pad - || learn->table_id == 0xff) { + if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } - end = (char *) learn + ntohs(learn->len); - for (p = learn + 1; p != end; ) { + end = (char *) nal + ntohs(nal->len); + for (p = nal + 1; p != end; ) { + struct ofpact_learn_spec *spec; uint16_t header = ntohs(get_be16(&p)); - int n_bits = header & NX_LEARN_N_BITS_MASK; - int src_type = header & NX_LEARN_SRC_MASK; - int dst_type = header & NX_LEARN_DST_MASK; - - enum ofperr error; - uint64_t value; if (!header) { break; } - error = learn_check_header(header, (char *) end - (char *) p); - if (error) { - return error; - } + spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); + learn = ofpacts->l2; + learn->n_specs++; - /* Check the source. */ - if (src_type == NX_LEARN_SRC_FIELD) { - struct mf_subfield src; + spec->src_type = header & NX_LEARN_SRC_MASK; + spec->dst_type = header & NX_LEARN_DST_MASK; + spec->n_bits = header & NX_LEARN_N_BITS_MASK; - get_subfield(n_bits, &p, &src); - error = mf_check_src(&src, flow); - if (error) { - return error; - } - value = 0; + /* Check for valid src and dst type combination. */ + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD || + (spec->dst_type == NX_LEARN_DST_OUTPUT && + spec->src_type == NX_LEARN_SRC_FIELD)) { + /* OK. */ } else { - value = get_bits(n_bits, &p); + return OFPERR_OFPBAC_BAD_ARGUMENT; } - /* Check the destination. */ - if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { - struct mf_subfield dst; + /* Check that the arguments don't overrun the end of the action. */ + if ((char *) end - (char *) p < learn_min_len(header)) { + return OFPERR_OFPBAC_BAD_LEN; + } - get_subfield(n_bits, &p, &dst); - error = (dst_type == NX_LEARN_DST_LOAD - ? mf_check_dst(&dst, &rule.flow) - : mf_check_src(&dst, &rule.flow)); - if (error) { - return error; - } + /* Get the source. */ + if (spec->src_type == NX_LEARN_SRC_FIELD) { + get_subfield(spec->n_bits, &p, &spec->src); + } else { + int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); - if (dst_type == NX_LEARN_DST_MATCH - && src_type == NX_LEARN_SRC_IMMEDIATE) { - if (n_bits <= 64) { - mf_set_subfield(&dst, value, &rule); - } else { - /* We're only setting subfields to allow us to check - * prerequisites. No prerequisite depends on the value of - * a field that is wider than 64 bits. So just skip - * setting it entirely. */ - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12); - } - } + bitwise_copy(p, p_bytes, 0, + &spec->src_imm, sizeof spec->src_imm, 0, + spec->n_bits); + p = (const uint8_t *) p + p_bytes; + } + + /* Get the destination. */ + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD) { + get_subfield(spec->n_bits, &p, &spec->dst); } } + ofpact_update_len(ofpacts, &learn->ofpact); + if (!is_all_zeros(p, (char *) end - (char *) p)) { return OFPERR_OFPBAC_BAD_ARGUMENT; } @@ -196,98 +164,50 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow) return 0; } -void -learn_execute(const struct nx_action_learn *learn, const struct flow *flow, - struct ofputil_flow_mod *fm) +/* Checks that 'learn' is a valid action on 'flow'. Returns 0 if it is valid, + * otherwise an OFPERR_*. */ +enum ofperr +learn_check(const struct ofpact_learn *learn, const struct flow *flow) { - const void *p, *end; - struct ofpbuf actions; - - cls_rule_init_catchall(&fm->cr, ntohs(learn->priority)); - fm->cookie = htonll(0); - fm->cookie_mask = htonll(0); - fm->new_cookie = learn->cookie; - fm->table_id = learn->table_id; - fm->command = OFPFC_MODIFY_STRICT; - fm->idle_timeout = ntohs(learn->idle_timeout); - fm->hard_timeout = ntohs(learn->hard_timeout); - fm->buffer_id = UINT32_MAX; - fm->out_port = OFPP_NONE; - fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM; - fm->actions = NULL; - fm->n_actions = 0; - - ofpbuf_init(&actions, 64); - - if (learn->fin_idle_timeout || learn->fin_hard_timeout) { - struct nx_action_fin_timeout *naft; - - naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions); - naft->fin_idle_timeout = learn->fin_idle_timeout; - naft->fin_hard_timeout = learn->fin_hard_timeout; - } - - for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) { - uint16_t header = ntohs(get_be16(&p)); - int n_bits = header & NX_LEARN_N_BITS_MASK; - int src_type = header & NX_LEARN_SRC_MASK; - int dst_type = header & NX_LEARN_DST_MASK; - union mf_subvalue value; - - struct mf_subfield dst; - int chunk, ofs; - - if (!header) { - break; - } - - if (src_type == NX_LEARN_SRC_FIELD) { - struct mf_subfield src; + const struct ofpact_learn_spec *spec; + struct cls_rule rule; - get_subfield(n_bits, &p, &src); - mf_read_subfield(&src, flow, &value); - } else { - int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16); + cls_rule_init_catchall(&rule, 0); + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { + enum ofperr error; - memset(&value, 0, sizeof value); - bitwise_copy(p, p_bytes, 0, - &value, sizeof value, 0, - n_bits); - p = (const uint8_t *) p + p_bytes; + /* Check the source. */ + if (spec->src_type == NX_LEARN_SRC_FIELD) { + error = mf_check_src(&spec->src, flow); + if (error) { + return error; + } } - switch (dst_type) { + /* Check the destination. */ + switch (spec->dst_type) { case NX_LEARN_DST_MATCH: - get_subfield(n_bits, &p, &dst); - mf_write_subfield(&dst, &value, &fm->cr); + error = mf_check_src(&spec->dst, &rule.flow); + if (error) { + return error; + } + + mf_write_subfield(&spec->dst, &spec->src_imm, &rule); break; case NX_LEARN_DST_LOAD: - get_subfield(n_bits, &p, &dst); - for (ofs = 0; ofs < n_bits; ofs += chunk) { - struct nx_action_reg_load *load; - - chunk = MIN(n_bits - ofs, 64); - - load = ofputil_put_NXAST_REG_LOAD(&actions); - load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk); - load->dst = htonl(dst.field->nxm_header); - bitwise_copy(&value, sizeof value, ofs, - &load->value, sizeof load->value, 0, - chunk); + error = mf_check_dst(&spec->dst, &rule.flow); + if (error) { + return error; } break; case NX_LEARN_DST_OUTPUT: - if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { - ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7]; - } + /* Nothing to do. */ break; } } - - fm->actions = ofpbuf_steal_data(&actions); - fm->n_actions = actions.size / sizeof(struct ofp_action_header); + return 0; } static void @@ -314,19 +234,150 @@ put_u32(struct ofpbuf *b, uint32_t x) put_be32(b, htonl(x)); } -struct learn_spec { - int n_bits; +/* Converts 'learn' into a "struct nx_action_learn" and appends that action to + * 'ofpacts'. */ +void +learn_to_nxast(const struct ofpact_learn *learn, struct ofpbuf *openflow) +{ + const struct ofpact_learn_spec *spec; + struct nx_action_learn *nal; + size_t start_ofs; + + start_ofs = openflow->size; + nal = ofputil_put_NXAST_LEARN(openflow); + nal->idle_timeout = htons(learn->idle_timeout); + nal->hard_timeout = htons(learn->hard_timeout); + nal->fin_idle_timeout = htons(learn->fin_idle_timeout); + nal->fin_hard_timeout = htons(learn->fin_hard_timeout); + nal->priority = htons(learn->priority); + nal->cookie = htonll(learn->cookie); + nal->flags = htons(learn->flags); + nal->table_id = learn->table_id; + + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { + put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type); + + if (spec->src_type == NX_LEARN_SRC_FIELD) { + put_u32(openflow, spec->src.field->nxm_header); + put_u16(openflow, spec->src.ofs); + } else { + size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16); + uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes); + bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, + bits, n_dst_bytes, 0, + spec->n_bits); + } + + if (spec->dst_type == NX_LEARN_DST_MATCH || + spec->dst_type == NX_LEARN_DST_LOAD) { + put_u32(openflow, spec->dst.field->nxm_header); + put_u16(openflow, spec->dst.ofs); + } + } + + if ((openflow->size - start_ofs) % 8) { + ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8); + } + + nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal); + nal->len = htons(openflow->size - start_ofs); +} + +/* Composes 'fm' so that executing it will implement 'learn' given that the + * packet being processed has 'flow' as its flow. + * + * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize + * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the + * 'ofpacts' buffer. + * + * The caller has to actually execute 'fm'. */ +void +learn_execute(const struct ofpact_learn *learn, const struct flow *flow, + struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) +{ + const struct ofpact_learn_spec *spec; - int src_type; - struct mf_subfield src; - union mf_subvalue src_imm; + cls_rule_init_catchall(&fm->cr, learn->priority); + fm->cookie = htonll(0); + fm->cookie_mask = htonll(0); + fm->new_cookie = htonll(learn->cookie); + fm->table_id = learn->table_id; + fm->command = OFPFC_MODIFY_STRICT; + fm->idle_timeout = learn->idle_timeout; + fm->hard_timeout = learn->hard_timeout; + fm->buffer_id = UINT32_MAX; + fm->out_port = OFPP_NONE; + fm->flags = learn->flags; + fm->ofpacts = NULL; + fm->ofpacts_len = 0; - int dst_type; - struct mf_subfield dst; -}; + if (learn->fin_idle_timeout || learn->fin_hard_timeout) { + struct ofpact_fin_timeout *oft; + + oft = ofpact_put_FIN_TIMEOUT(ofpacts); + oft->fin_idle_timeout = learn->fin_idle_timeout; + oft->fin_hard_timeout = learn->fin_hard_timeout; + } + + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { + union mf_subvalue value; + int chunk, ofs; + + if (spec->src_type == NX_LEARN_SRC_FIELD) { + mf_read_subfield(&spec->src, flow, &value); + } else { + value = spec->src_imm; + } + + switch (spec->dst_type) { + case NX_LEARN_DST_MATCH: + mf_write_subfield(&spec->dst, &value, &fm->cr); + break; + + case NX_LEARN_DST_LOAD: + for (ofs = 0; ofs < spec->n_bits; ofs += chunk) { + struct ofpact_reg_load *load; + ovs_be64 value_be; + + chunk = MIN(spec->n_bits - ofs, 64); + + load = ofpact_put_REG_LOAD(ofpacts); + load->dst.field = spec->dst.field; + load->dst.ofs = spec->dst.ofs + ofs; + load->dst.n_bits = chunk; + + memset(&value_be, 0, sizeof value_be); + bitwise_copy(&value, sizeof value, ofs, + &value_be, sizeof value_be, 0, + chunk); + load->value = ntohll(value_be); + } + break; + + case NX_LEARN_DST_OUTPUT: + if (spec->n_bits <= 16 + || is_all_zeros(value.u8, sizeof value - 2)) { + uint16_t port = ntohs(value.be16[7]); + + if (port < OFPP_MAX + || port == OFPP_IN_PORT + || port == OFPP_FLOOD + || port == OFPP_LOCAL + || port == OFPP_ALL) { + ofpact_put_OUTPUT(ofpacts)->port = port; + } + } + break; + } + } + ofpact_pad(ofpacts); + + fm->ofpacts = ofpacts->data; + fm->ofpacts_len = ofpacts->size; +} static void -learn_parse_load_immediate(const char *s, struct learn_spec *spec) +learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec) { const char *full_s = s; const char *arrow = strstr(s, "->"); @@ -377,9 +428,8 @@ learn_parse_load_immediate(const char *s, struct learn_spec *spec) static void learn_parse_spec(const char *orig, char *name, char *value, - struct learn_spec *spec) + struct ofpact_learn_spec *spec) { - memset(spec, 0, sizeof *spec); if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; @@ -428,17 +478,15 @@ learn_parse_spec(const char *orig, char *name, char *value, if (value[strcspn(value, "[-")] == '-') { learn_parse_load_immediate(value, spec); } else { - struct nx_action_reg_move move; + struct ofpact_reg_move move; nxm_parse_reg_move(&move, value); - spec->n_bits = ntohs(move.n_bits); + spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; - nxm_decode_discrete(&spec->src, - move.src, move.src_ofs, move.n_bits); + spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; - nxm_decode_discrete(&spec->dst, - move.dst, move.dst_ofs, move.n_bits); + spec->dst = move.dst; } } else if (!strcmp(name, "output")) { if (mf_parse_subfield(&spec->src, value)[0] != '\0') { @@ -455,8 +503,8 @@ learn_parse_spec(const char *orig, char *name, char *value, } /* Parses 'arg' as a set of arguments to the "learn" action and appends a - * matching NXAST_LEARN action to 'b'. The format parsed is described in - * ovs-ofctl(8). + * matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the + * format parsed. * * Prints an error on stderr and aborts the program if 'arg' syntax is invalid. * @@ -466,29 +514,23 @@ learn_parse_spec(const char *orig, char *name, char *value, * * Modifies 'arg'. */ void -learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) +learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts) { char *orig = xstrdup(arg); char *name, *value; - enum ofperr error; - size_t learn_ofs; - size_t len; - struct nx_action_learn *learn; + struct ofpact_learn *learn; struct cls_rule rule; + enum ofperr error; - learn_ofs = b->size; - learn = ofputil_put_NXAST_LEARN(b); - learn->idle_timeout = htons(OFP_FLOW_PERMANENT); - learn->hard_timeout = htons(OFP_FLOW_PERMANENT); - learn->priority = htons(OFP_DEFAULT_PRIORITY); - learn->cookie = htonll(0); - learn->flags = htons(0); + learn = ofpact_put_LEARN(ofpacts); + learn->idle_timeout = OFP_FLOW_PERMANENT; + learn->hard_timeout = OFP_FLOW_PERMANENT; + learn->priority = OFP_DEFAULT_PRIORITY; learn->table_id = 1; cls_rule_init_catchall(&rule, 0); while (ofputil_parse_key_value(&arg, &name, &value)) { - learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { @@ -496,73 +538,50 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) orig); } } else if (!strcmp(name, "priority")) { - learn->priority = htons(atoi(value)); + learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { - learn->idle_timeout = htons(atoi(value)); + learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { - learn->hard_timeout = htons(atoi(value)); + learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { - learn->fin_idle_timeout = htons(atoi(value)); + learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { - learn->fin_hard_timeout = htons(atoi(value)); + learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { - learn->cookie = htonll(strtoull(value, NULL, 0)); + learn->cookie = strtoull(value, NULL, 0); } else { - struct learn_spec spec; + struct ofpact_learn_spec *spec; + + spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); + learn = ofpacts->l2; + learn->n_specs++; - learn_parse_spec(orig, name, value, &spec); + learn_parse_spec(orig, name, value, spec); /* Check prerequisites. */ - if (spec.src_type == NX_LEARN_SRC_FIELD - && flow && !mf_are_prereqs_ok(spec.src.field, flow)) { + if (spec->src_type == NX_LEARN_SRC_FIELD + && flow && !mf_are_prereqs_ok(spec->src.field, flow)) { ovs_fatal(0, "%s: cannot specify source field %s because " "prerequisites are not satisfied", - orig, spec.src.field->name); + orig, spec->src.field->name); } - if ((spec.dst_type == NX_LEARN_DST_MATCH - || spec.dst_type == NX_LEARN_DST_LOAD) - && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) { + if ((spec->dst_type == NX_LEARN_DST_MATCH + || spec->dst_type == NX_LEARN_DST_LOAD) + && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) { ovs_fatal(0, "%s: cannot specify destination field %s because " "prerequisites are not satisfied", - orig, spec.dst.field->name); + orig, spec->dst.field->name); } /* Update 'rule' to allow for satisfying destination * prerequisites. */ - if (spec.src_type == NX_LEARN_SRC_IMMEDIATE - && spec.dst_type == NX_LEARN_DST_MATCH) { - mf_write_subfield(&spec.dst, &spec.src_imm, &rule); - } - - /* Output the flow_mod_spec. */ - put_u16(b, spec.n_bits | spec.src_type | spec.dst_type); - if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) { - int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2; - int ofs = sizeof spec.src_imm - n_bytes; - ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes); - } else { - put_u32(b, spec.src.field->nxm_header); - put_u16(b, spec.src.ofs); - } - if (spec.dst_type == NX_LEARN_DST_MATCH || - spec.dst_type == NX_LEARN_DST_LOAD) { - put_u32(b, spec.dst.field->nxm_header); - put_u16(b, spec.dst.ofs); - } else { - assert(spec.dst_type == NX_LEARN_DST_OUTPUT); + if (spec->src_type == NX_LEARN_SRC_IMMEDIATE + && spec->dst_type == NX_LEARN_DST_MATCH) { + mf_write_subfield(&spec->dst, &spec->src_imm, &rule); } } } - - put_u16(b, 0); - - len = b->size - learn_ofs; - if (len % 8) { - ofpbuf_put_zeros(b, 8 - len % 8); - } - - learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn); - learn->len = htons(b->size - learn_ofs); + ofpact_update_len(ofpacts, &learn->ofpact); /* In theory the above should have caught any errors, but... */ if (flow) { @@ -574,169 +593,106 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow) free(orig); } +static void +format_subvalue(const union mf_subvalue *subvalue, struct ds *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) { + if (subvalue->u8[i]) { + ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]); + for (i++; i < ARRAY_SIZE(subvalue->u8); i++) { + ds_put_format(s, "%02"PRIx8, subvalue->u8[i]); + } + return; + } + } + ds_put_char(s, '0'); +} + +/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8) + * describes. */ void -learn_format(const struct nx_action_learn *learn, struct ds *s) +learn_format(const struct ofpact_learn *learn, struct ds *s) { + const struct ofpact_learn_spec *spec; struct cls_rule rule; - const void *p, *end; cls_rule_init_catchall(&rule, 0); ds_put_format(s, "learn(table=%"PRIu8, learn->table_id); - if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) { - ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout)); + if (learn->idle_timeout != OFP_FLOW_PERMANENT) { + ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout); } - if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) { - ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout)); + if (learn->hard_timeout != OFP_FLOW_PERMANENT) { + ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout); } if (learn->fin_idle_timeout) { - ds_put_format(s, ",fin_idle_timeout=%"PRIu16, - ntohs(learn->fin_idle_timeout)); + ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout); } if (learn->fin_hard_timeout) { - ds_put_format(s, ",fin_hard_timeout=%"PRIu16, - ntohs(learn->fin_hard_timeout)); + ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout); } - if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) { - ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority)); + if (learn->priority != OFP_DEFAULT_PRIORITY) { + ds_put_format(s, ",priority=%"PRIu16, learn->priority); } - if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) { + if (learn->flags & OFPFF_SEND_FLOW_REM) { ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM"); } - if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) { - ds_put_format(s, ",***flags=%"PRIu16"***", - ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM); + if (learn->cookie != 0) { + ds_put_format(s, ",cookie=%#"PRIx64, learn->cookie); } - if (learn->cookie != htonll(0)) { - ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie)); - } - if (learn->pad != 0) { - ds_put_cstr(s, ",***nonzero pad***"); - } - - end = (char *) learn + ntohs(learn->len); - for (p = learn + 1; p != end; ) { - uint16_t header = ntohs(get_be16(&p)); - int n_bits = header & NX_LEARN_N_BITS_MASK; - - int src_type = header & NX_LEARN_SRC_MASK; - struct mf_subfield src; - const uint8_t *src_value; - int src_value_bytes; - - int dst_type = header & NX_LEARN_DST_MASK; - struct mf_subfield dst; - - enum ofperr error; - int i; - - if (!header) { - break; - } - - error = learn_check_header(header, (char *) end - (char *) p); - if (error == OFPERR_OFPBAC_BAD_ARGUMENT) { - ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)", - header); - return; - } else if (error == OFPERR_OFPBAC_BAD_LEN) { - ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes " - "long but only %td bytes are left***)", - (char *) p - (char *) (learn + 1) - 2, - learn_min_len(header) + 2, - (char *) end - (char *) p + 2); - return; - } - assert(!error); - - /* Get the source. */ - if (src_type == NX_LEARN_SRC_FIELD) { - get_subfield(n_bits, &p, &src); - src_value_bytes = 0; - src_value = NULL; - } else { - src.field = NULL; - src.ofs = 0; - src.n_bits = 0; - src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16); - src_value = p; - p = (const void *) ((const uint8_t *) p + src_value_bytes); - } - - /* Get the destination. */ - if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) { - get_subfield(n_bits, &p, &dst); - } else { - dst.field = NULL; - dst.ofs = 0; - dst.n_bits = 0; - } + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { ds_put_char(s, ','); - switch (src_type | dst_type) { + switch (spec->src_type | spec->dst_type) { case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH: - if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) { + if (spec->dst.ofs == 0 + && spec->dst.n_bits == spec->dst.field->n_bits) { union mf_value value; - uint8_t *bytes = (uint8_t *) &value; - - if (src_value_bytes > dst.field->n_bytes) { - /* The destination field is an odd number of bytes, which - * got rounded up to a multiple of 2 to be put into the - * learning action. Skip over the leading byte, which - * should be zero anyway. Otherwise the memcpy() below - * will overrun the start of 'value'. */ - int diff = src_value_bytes - dst.field->n_bytes; - src_value += diff; - src_value_bytes -= diff; - } memset(&value, 0, sizeof value); - memcpy(&bytes[dst.field->n_bytes - src_value_bytes], - src_value, src_value_bytes); - ds_put_format(s, "%s=", dst.field->name); - mf_format(dst.field, &value, NULL, s); + bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0, + &value, spec->dst.field->n_bytes, 0, + spec->dst.field->n_bits); + ds_put_format(s, "%s=", spec->dst.field->name); + mf_format(spec->dst.field, &value, NULL, s); } else { - mf_format_subfield(&dst, s); - ds_put_cstr(s, "=0x"); - for (i = 0; i < src_value_bytes; i++) { - ds_put_format(s, "%02"PRIx8, src_value[i]); - } + mf_format_subfield(&spec->dst, s); + ds_put_char(s, '='); + format_subvalue(&spec->src_imm, s); } break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH: - mf_format_subfield(&dst, s); - if (src.field != dst.field || src.ofs != dst.ofs) { + mf_format_subfield(&spec->dst, s); + if (spec->src.field != spec->dst.field || + spec->src.ofs != spec->dst.ofs) { ds_put_char(s, '='); - mf_format_subfield(&src, s); + mf_format_subfield(&spec->src, s); } break; case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD: - ds_put_cstr(s, "load:0x"); - for (i = 0; i < src_value_bytes; i++) { - ds_put_format(s, "%02"PRIx8, src_value[i]); - } + ds_put_format(s, "load:"); + format_subvalue(&spec->src_imm, s); ds_put_cstr(s, "->"); - mf_format_subfield(&dst, s); + mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD: ds_put_cstr(s, "load:"); - mf_format_subfield(&src, s); + mf_format_subfield(&spec->src, s); ds_put_cstr(s, "->"); - mf_format_subfield(&dst, s); + mf_format_subfield(&spec->dst, s); break; case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT: ds_put_cstr(s, "output:"); - mf_format_subfield(&src, s); + mf_format_subfield(&spec->src, s); break; } } - if (!is_all_zeros(p, (char *) end - (char *) p)) { - ds_put_cstr(s, ",***nonzero trailer***"); - } ds_put_char(s, ')'); } diff --git a/lib/learn.h b/lib/learn.h index 28591727..adf597e9 100644 --- a/lib/learn.h +++ b/lib/learn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Nicira, Inc. + * Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ struct ds; struct flow; struct ofpbuf; +struct ofpact_learn; struct ofputil_flow_mod; struct nx_action_learn; @@ -30,11 +31,15 @@ struct nx_action_learn; * See include/openflow/nicira-ext.h for NXAST_LEARN specification. */ -enum ofperr learn_check(const struct nx_action_learn *, const struct flow *); -void learn_execute(const struct nx_action_learn *, const struct flow *, - struct ofputil_flow_mod *); +enum ofperr learn_from_openflow(const struct nx_action_learn *, + struct ofpbuf *ofpacts); +enum ofperr learn_check(const struct ofpact_learn *, const struct flow *); +void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow); -void learn_parse(struct ofpbuf *, char *, const struct flow *); -void learn_format(const struct nx_action_learn *, struct ds *); +void learn_execute(const struct ofpact_learn *, const struct flow *, + struct ofputil_flow_mod *, struct ofpbuf *ofpacts); + +void learn_parse(char *, const struct flow *, struct ofpbuf *ofpacts); +void learn_format(const struct ofpact_learn *, struct ds *); #endif /* learn.h */ diff --git a/lib/learning-switch.c b/lib/learning-switch.c index 6b74f82f..cb0e49bc 100644 --- a/lib/learning-switch.c +++ b/lib/learning-switch.c @@ -29,6 +29,7 @@ #include "hmap.h" #include "mac-learning.h" #include "ofpbuf.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-parse.h" #include "ofp-print.h" @@ -56,6 +57,7 @@ struct lswitch { * Otherwise, the switch processes every packet. */ int max_idle; + enum ofputil_protocol protocol; unsigned long long int datapath_id; time_t last_features_request; struct mac_learning *ml; /* NULL to act as hub instead of switch. */ @@ -81,7 +83,7 @@ static void send_features_request(struct lswitch *, struct rconn *); static enum ofperr process_switch_features(struct lswitch *, struct ofp_switch_features *); static void process_packet_in(struct lswitch *, struct rconn *, - const struct ofp_packet_in *); + const struct ofp_header *); static void process_echo_request(struct lswitch *, struct rconn *, const struct ofp_header *); @@ -92,6 +94,7 @@ static void process_echo_request(struct lswitch *, struct rconn *, struct lswitch * lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) { + enum ofputil_protocol protocol; struct lswitch *sw; sw = xzalloc(sizeof *sw); @@ -139,18 +142,13 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) sw->queued = rconn_packet_counter_create(); send_features_request(sw, rconn); + protocol = ofputil_protocol_from_ofp_version(rconn_get_version(rconn)); if (cfg->default_flows) { enum ofputil_protocol usable_protocols; - enum ofputil_protocol protocol; struct ofpbuf *msg = NULL; - int ofp_version; int error = 0; size_t i; - /* Figure out the initial protocol on the connection. */ - ofp_version = rconn_get_version(rconn); - protocol = ofputil_protocol_from_ofp_version(ofp_version); - /* If the initial protocol isn't good enough for default_flows, then * pick one that will work and encode messages to set up that * protocol. @@ -181,6 +179,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg) rconn_get_name(rconn), strerror(error)); } } + sw->protocol = protocol; return sw; } @@ -250,6 +249,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, break; case OFPUTIL_OFPT_PACKET_IN: + case OFPUTIL_NXT_PACKET_IN: process_packet_in(sw, rconn, msg->data); break; @@ -292,7 +292,6 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn, case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: case OFPUTIL_NXT_SET_FLOW_FORMAT: case OFPUTIL_NXT_SET_PACKET_IN_FORMAT: - case OFPUTIL_NXT_PACKET_IN: case OFPUTIL_NXT_FLOW_MOD: case OFPUTIL_NXT_FLOW_REMOVED: case OFPUTIL_NXT_FLOW_AGE: @@ -439,67 +438,58 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port) static void process_packet_in(struct lswitch *sw, struct rconn *rconn, - const struct ofp_packet_in *opi) + const struct ofp_header *oh) { - uint16_t in_port = ntohs(opi->in_port); + struct ofputil_packet_in pi; uint32_t queue_id; uint16_t out_port; - struct ofp_action_header actions[2]; - size_t actions_len; + uint64_t ofpacts_stub[64 / 8]; + struct ofpbuf ofpacts; struct ofputil_packet_out po; + enum ofperr error; - size_t pkt_ofs, pkt_len; struct ofpbuf pkt; struct flow flow; + error = ofputil_decode_packet_in(&pi, oh); + if (error) { + VLOG_WARN_RL(&rl, "failed to decode packet-in: %s", + ofperr_to_string(error)); + return; + } + /* Ignore packets sent via output to OFPP_CONTROLLER. This library never * uses such an action. You never know what experiments might be going on, * though, and it seems best not to interfere with them. */ - if (opi->reason != OFPR_NO_MATCH) { + if (pi.reason != OFPR_NO_MATCH) { return; } /* Extract flow data from 'opi' into 'flow'. */ - pkt_ofs = offsetof(struct ofp_packet_in, data); - pkt_len = ntohs(opi->header.length) - pkt_ofs; - ofpbuf_use_const(&pkt, opi->data, pkt_len); - flow_extract(&pkt, 0, 0, in_port, &flow); + ofpbuf_use_const(&pkt, pi.packet, pi.packet_len); + flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow); /* Choose output port. */ out_port = lswitch_choose_destination(sw, &flow); /* Make actions. */ - queue_id = get_queue_id(sw, in_port); + queue_id = get_queue_id(sw, pi.fmd.in_port); + ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); if (out_port == OFPP_NONE) { - actions_len = 0; + /* No actions. */ } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) { - struct ofp_action_output oao; - - memset(&oao, 0, sizeof oao); - oao.type = htons(OFPAT10_OUTPUT); - oao.len = htons(sizeof oao); - oao.port = htons(out_port); - - memcpy(actions, &oao, sizeof oao); - actions_len = sizeof oao; + ofpact_put_OUTPUT(&ofpacts)->port = out_port; } else { - struct ofp_action_enqueue oae; - - memset(&oae, 0, sizeof oae); - oae.type = htons(OFPAT10_ENQUEUE); - oae.len = htons(sizeof oae); - oae.port = htons(out_port); - oae.queue_id = htonl(queue_id); - - memcpy(actions, &oae, sizeof oae); - actions_len = sizeof oae; + struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts); + enqueue->port = out_port; + enqueue->queue = queue_id; } - assert(actions_len <= sizeof actions); + ofpact_pad(&ofpacts); /* Prepare packet_out in case we need one. */ - po.buffer_id = ntohl(opi->buffer_id); + po.buffer_id = pi.buffer_id; if (po.buffer_id == UINT32_MAX) { po.packet = pkt.data; po.packet_len = pkt.size; @@ -507,31 +497,38 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, po.packet = NULL; po.packet_len = 0; } - po.in_port = in_port; - po.actions = (union ofp_action *) actions; - po.n_actions = actions_len / sizeof *actions; + po.in_port = pi.fmd.in_port; + po.ofpacts = ofpacts.data; + po.ofpacts_len = ofpacts.size; /* Send the packet, and possibly the whole flow, to the output port. */ if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) { + struct ofputil_flow_mod fm; struct ofpbuf *buffer; - struct cls_rule rule; /* The output port is known, or we always flood everything, so add a * new flow. */ - cls_rule_init(&flow, &sw->wc, 0, &rule); - buffer = make_add_flow(&rule, ntohl(opi->buffer_id), - sw->max_idle, actions_len); - ofpbuf_put(buffer, actions, actions_len); + memset(&fm, 0, sizeof fm); + cls_rule_init(&flow, &sw->wc, 0, &fm.cr); + fm.table_id = 0xff; + fm.command = OFPFC_ADD; + fm.idle_timeout = sw->max_idle; + fm.buffer_id = pi.buffer_id; + fm.out_port = OFPP_NONE; + fm.ofpacts = ofpacts.data; + fm.ofpacts_len = ofpacts.size; + buffer = ofputil_encode_flow_mod(&fm, sw->protocol); + queue_tx(sw, rconn, buffer); /* If the switch didn't buffer the packet, we need to send a copy. */ - if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) { + if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) { queue_tx(sw, rconn, ofputil_encode_packet_out(&po)); } } else { /* We don't know that MAC, or we don't set up flows. Send along the * packet without setting up a flow. */ - if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) { + if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) { queue_tx(sw, rconn, ofputil_encode_packet_out(&po)); } } diff --git a/lib/multipath.c b/lib/multipath.c index 8d932111..f6a1a0ae 100644 --- a/lib/multipath.c +++ b/lib/multipath.c @@ -22,8 +22,8 @@ #include #include #include "dynamic-string.h" -#include "meta-flow.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "openflow/nicira-ext.h" @@ -34,37 +34,66 @@ VLOG_DEFINE_THIS_MODULE(multipath); static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); -/* multipath_check(). */ +/* Converts 'nam' into 'mp'. Returns 0 if successful, otherwise an + * OFPERR_*. */ enum ofperr -multipath_check(const struct nx_action_multipath *mp, const struct flow *flow) +multipath_from_openflow(const struct nx_action_multipath *nam, + struct ofpact_multipath *mp) { - uint32_t n_links = ntohs(mp->max_link) + 1; + uint32_t n_links = ntohs(nam->max_link) + 1; size_t min_n_bits = log_2_ceil(n_links); - struct mf_subfield dst; - enum ofperr error; - nxm_decode(&dst, mp->dst, mp->ofs_nbits); - error = mf_check_dst(&dst, flow); - if (error) { - return error; - } - - if (!flow_hash_fields_valid(ntohs(mp->fields))) { - VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, ntohs(mp->fields)); - } else if (mp->algorithm != htons(NX_MP_ALG_MODULO_N) - && mp->algorithm != htons(NX_MP_ALG_HASH_THRESHOLD) - && mp->algorithm != htons(NX_MP_ALG_HRW) - && mp->algorithm != htons(NX_MP_ALG_ITER_HASH)) { - VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, - ntohs(mp->algorithm)); - } else if (dst.n_bits < min_n_bits) { + ofpact_init_MULTIPATH(mp); + mp->fields = ntohs(nam->fields); + mp->basis = ntohs(nam->basis); + mp->algorithm = ntohs(nam->algorithm); + mp->max_link = ntohs(nam->max_link); + mp->arg = ntohl(nam->arg); + mp->dst.field = mf_from_nxm_header(ntohl(nam->dst)); + mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits); + mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits); + + if (!flow_hash_fields_valid(mp->fields)) { + VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else if (mp->algorithm != NX_MP_ALG_MODULO_N + && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD + && mp->algorithm != NX_MP_ALG_HRW + && mp->algorithm != NX_MP_ALG_ITER_HASH) { + VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm); + return OFPERR_OFPBAC_BAD_ARGUMENT; + } else if (mp->dst.n_bits < min_n_bits) { VLOG_WARN_RL(&rl, "multipath action requires at least %zu bits for " "%"PRIu32" links", min_n_bits, n_links); - } else { - return 0; + return OFPERR_OFPBAC_BAD_ARGUMENT; } - return OFPERR_OFPBAC_BAD_ARGUMENT; + return multipath_check(mp, NULL); +} + +/* Checks that 'mp' is valid on flow. Returns 0 if it is valid, otherwise an + * OFPERR_*. */ +enum ofperr +multipath_check(const struct ofpact_multipath *mp, + const struct flow *flow) +{ + return mf_check_dst(&mp->dst, flow); +} + +/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to + * 'openflow'. */ +void +multipath_to_nxast(const struct ofpact_multipath *mp, struct ofpbuf *openflow) +{ + struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow); + + nam->fields = htons(mp->fields); + nam->basis = htons(mp->basis); + nam->algorithm = htons(mp->algorithm); + nam->max_link = htons(mp->max_link); + nam->arg = htonl(mp->arg); + nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits); + nam->dst = htonl(mp->dst.field->nxm_header); } /* multipath_execute(). */ @@ -72,19 +101,17 @@ multipath_check(const struct nx_action_multipath *mp, const struct flow *flow) static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm, unsigned int n_links, unsigned int arg); +/* Executes 'mp' based on the current contents of 'flow', writing the results + * back into 'flow'. */ void -multipath_execute(const struct nx_action_multipath *mp, struct flow *flow) +multipath_execute(const struct ofpact_multipath *mp, struct flow *flow) { /* Calculate value to store. */ - uint32_t hash = flow_hash_fields(flow, ntohs(mp->fields), - ntohs(mp->basis)); - uint16_t link = multipath_algorithm(hash, ntohs(mp->algorithm), - ntohs(mp->max_link) + 1, - ntohl(mp->arg)); - struct mf_subfield dst; - - nxm_decode(&dst, mp->dst, mp->ofs_nbits); - mf_set_subfield_value(&dst, link, flow); + uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis); + uint16_t link = multipath_algorithm(hash, mp->algorithm, + mp->max_link + 1, mp->arg); + + nxm_reg_load(&mp->dst, link, flow); } static uint16_t @@ -162,15 +189,17 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm, NOT_REACHED(); } -/* multipath_parse(). */ - +/* Parses 's_' as a set of arguments to the "multipath" action and initializes + * 'mp' accordingly. ovs-ofctl(8) describes the format parsed. + * + * Prints an error on stderr and aborts the program if 's_' syntax is + * invalid. */ void -multipath_parse(struct nx_action_multipath *mp, const char *s_) +multipath_parse(struct ofpact_multipath *mp, const char *s_) { char *s = xstrdup(s_); char *save_ptr = NULL; - char *fields, *basis, *algorithm, *n_links_str, *arg, *dst_s; - struct mf_subfield dst; + char *fields, *basis, *algorithm, *n_links_str, *arg, *dst; int n_links; fields = strtok_r(s, ", ", &save_ptr); @@ -178,28 +207,28 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_) algorithm = strtok_r(NULL, ", ", &save_ptr); n_links_str = strtok_r(NULL, ", ", &save_ptr); arg = strtok_r(NULL, ", ", &save_ptr); - dst_s = strtok_r(NULL, ", ", &save_ptr); - if (!dst_s) { + dst = strtok_r(NULL, ", ", &save_ptr); + if (!dst) { ovs_fatal(0, "%s: not enough arguments to multipath action", s_); } - ofputil_init_NXAST_MULTIPATH(mp); + ofpact_init_MULTIPATH(mp); if (!strcasecmp(fields, "eth_src")) { - mp->fields = htons(NX_HASH_FIELDS_ETH_SRC); + mp->fields = NX_HASH_FIELDS_ETH_SRC; } else if (!strcasecmp(fields, "symmetric_l4")) { - mp->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4); + mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4; } else { ovs_fatal(0, "%s: unknown fields `%s'", s_, fields); } - mp->basis = htons(atoi(basis)); + mp->basis = atoi(basis); if (!strcasecmp(algorithm, "modulo_n")) { - mp->algorithm = htons(NX_MP_ALG_MODULO_N); + mp->algorithm = NX_MP_ALG_MODULO_N; } else if (!strcasecmp(algorithm, "hash_threshold")) { - mp->algorithm = htons(NX_MP_ALG_HASH_THRESHOLD); + mp->algorithm = NX_MP_ALG_HASH_THRESHOLD; } else if (!strcasecmp(algorithm, "hrw")) { - mp->algorithm = htons(NX_MP_ALG_HRW); + mp->algorithm = NX_MP_ALG_HRW; } else if (!strcasecmp(algorithm, "iter_hash")) { - mp->algorithm = htons(NX_MP_ALG_ITER_HASH); + mp->algorithm = NX_MP_ALG_ITER_HASH; } else { ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm); } @@ -208,34 +237,29 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_) ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536", s_, n_links); } - mp->max_link = htons(n_links - 1); - mp->arg = htonl(atoi(arg)); + mp->max_link = n_links - 1; + mp->arg = atoi(arg); - mf_parse_subfield(&dst, dst_s); - if (dst.n_bits < 16 && n_links > (1u << dst.n_bits)) { + mf_parse_subfield(&mp->dst, dst); + if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) { ovs_fatal(0, "%s: %d-bit destination field has %u possible values, " "less than specified n_links %d", - s_, dst.n_bits, 1u << dst.n_bits, n_links); + s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links); } - mp->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); - mp->dst = htonl(dst.field->nxm_header); free(s); } +/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8) + * describes. */ void -multipath_format(const struct nx_action_multipath *mp, struct ds *s) +multipath_format(const struct ofpact_multipath *mp, struct ds *s) { const char *fields, *algorithm; - uint16_t mp_fields = ntohs(mp->fields); - uint16_t mp_algorithm = ntohs(mp->algorithm); - - struct mf_subfield dst; - - fields = flow_hash_fields_to_str(mp_fields); + fields = flow_hash_fields_to_str(mp->fields); - switch ((enum nx_mp_algorithm) mp_algorithm) { + switch (mp->algorithm) { case NX_MP_ALG_MODULO_N: algorithm = "modulo_n"; break; @@ -253,9 +277,8 @@ multipath_format(const struct nx_action_multipath *mp, struct ds *s) } ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",", - fields, ntohs(mp->basis), algorithm, ntohs(mp->max_link) + 1, - ntohl(mp->arg)); - nxm_decode(&dst, mp->dst, mp->ofs_nbits); - mf_format_subfield(&dst, s); + fields, mp->basis, algorithm, mp->max_link + 1, + mp->arg); + mf_format_subfield(&mp->dst, s); ds_put_char(s, ')'); } diff --git a/lib/multipath.h b/lib/multipath.h index 2cd646cb..1b5160dd 100644 --- a/lib/multipath.h +++ b/lib/multipath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011 Nicira, Inc. + * Copyright (c) 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,18 +23,24 @@ struct ds; struct flow; struct nx_action_multipath; -struct nx_action_reg_move; +struct ofpact_multipath; +struct ofpbuf; /* NXAST_MULTIPATH helper functions. * * See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification. */ -enum ofperr multipath_check(const struct nx_action_multipath *, +enum ofperr multipath_from_openflow(const struct nx_action_multipath *, + struct ofpact_multipath *); +enum ofperr multipath_check(const struct ofpact_multipath *, const struct flow *); -void multipath_execute(const struct nx_action_multipath *, struct flow *); +void multipath_to_nxast(const struct ofpact_multipath *, + struct ofpbuf *openflow); -void multipath_parse(struct nx_action_multipath *, const char *); -void multipath_format(const struct nx_action_multipath *, struct ds *); +void multipath_execute(const struct ofpact_multipath *, struct flow *); + +void multipath_parse(struct ofpact_multipath *, const char *); +void multipath_format(const struct ofpact_multipath *, struct ds *); #endif /* multipath.h */ diff --git a/lib/nx-match.c b/lib/nx-match.c index 92e94f88..3e3b4c69 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -23,6 +23,7 @@ #include "classifier.h" #include "dynamic-string.h" #include "meta-flow.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -789,199 +790,183 @@ nx_match_from_string(const char *s, struct ofpbuf *b) } void -nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s) +nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s) { const char *full_s = s; - struct mf_subfield src, dst; - s = mf_parse_subfield(&src, s); + s = mf_parse_subfield(&move->src, s); if (strncmp(s, "->", 2)) { ovs_fatal(0, "%s: missing `->' following source", full_s); } s += 2; - s = mf_parse_subfield(&dst, s); + s = mf_parse_subfield(&move->dst, s); if (*s != '\0') { ovs_fatal(0, "%s: trailing garbage following destination", full_s); } - if (src.n_bits != dst.n_bits) { + if (move->src.n_bits != move->dst.n_bits) { ovs_fatal(0, "%s: source field is %d bits wide but destination is " - "%d bits wide", full_s, src.n_bits, dst.n_bits); + "%d bits wide", full_s, + move->src.n_bits, move->dst.n_bits); } - - ofputil_init_NXAST_REG_MOVE(move); - move->n_bits = htons(src.n_bits); - move->src_ofs = htons(src.ofs); - move->dst_ofs = htons(dst.ofs); - move->src = htonl(src.field->nxm_header); - move->dst = htonl(dst.field->nxm_header); } void -nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s) +nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s) { const char *full_s = s; - struct mf_subfield dst; - uint64_t value; - value = strtoull(s, (char **) &s, 0); + load->value = strtoull(s, (char **) &s, 0); if (strncmp(s, "->", 2)) { ovs_fatal(0, "%s: missing `->' following value", full_s); } s += 2; - s = mf_parse_subfield(&dst, s); + s = mf_parse_subfield(&load->dst, s); if (*s != '\0') { ovs_fatal(0, "%s: trailing garbage following destination", full_s); } - if (dst.n_bits < 64 && (value >> dst.n_bits) != 0) { - ovs_fatal(0, "%s: value %"PRIu64" does not fit into %u bits", - full_s, value, dst.n_bits); + if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) { + ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits", + full_s, load->value, load->dst.n_bits); } - - ofputil_init_NXAST_REG_LOAD(load); - load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits); - load->dst = htonl(dst.field->nxm_header); - load->value = htonll(value); } /* nxm_format_reg_move(), nxm_format_reg_load(). */ void -nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s) +nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s) { - struct mf_subfield src, dst; - - nxm_decode_discrete(&src, move->src, move->src_ofs, move->n_bits); - nxm_decode_discrete(&dst, move->dst, move->dst_ofs, move->n_bits); - ds_put_format(s, "move:"); - mf_format_subfield(&src, s); + mf_format_subfield(&move->src, s); ds_put_cstr(s, "->"); - mf_format_subfield(&dst, s); + mf_format_subfield(&move->dst, s); } void -nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s) +nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s) +{ + ds_put_format(s, "load:%#"PRIx64"->", load->value); + mf_format_subfield(&load->dst, s); +} + +enum ofperr +nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm, + struct ofpbuf *ofpacts) { - struct mf_subfield dst; + struct ofpact_reg_move *move; - ds_put_format(s, "load:%#"PRIx64"->", ntohll(load->value)); + move = ofpact_put_REG_MOVE(ofpacts); + move->src.field = mf_from_nxm_header(ntohl(narm->src)); + move->src.ofs = ntohs(narm->src_ofs); + move->src.n_bits = ntohs(narm->n_bits); + move->dst.field = mf_from_nxm_header(ntohl(narm->dst)); + move->dst.ofs = ntohs(narm->dst_ofs); + move->dst.n_bits = ntohs(narm->n_bits); - nxm_decode(&dst, load->dst, load->ofs_nbits); - mf_format_subfield(&dst, s); + return nxm_reg_move_check(move, NULL); } - -/* nxm_check_reg_move(), nxm_check_reg_load(). */ enum ofperr -nxm_check_reg_move(const struct nx_action_reg_move *action, - const struct flow *flow) +nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl, + struct ofpbuf *ofpacts) { - struct mf_subfield src; - struct mf_subfield dst; - int error; + struct ofpact_reg_load *load; - nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits); - error = mf_check_src(&src, flow); - if (error) { - return error; + load = ofpact_put_REG_LOAD(ofpacts); + load->dst.field = mf_from_nxm_header(ntohl(narl->dst)); + load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits); + load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits); + load->value = ntohll(narl->value); + + /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in + * narl->value. */ + if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) { + return OFPERR_OFPBAC_BAD_ARGUMENT; } - nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits); - return mf_check_dst(&dst, flow); + return nxm_reg_load_check(load, NULL); } - + enum ofperr -nxm_check_reg_load(const struct nx_action_reg_load *action, - const struct flow *flow) +nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow) { - struct mf_subfield dst; enum ofperr error; - nxm_decode(&dst, action->dst, action->ofs_nbits); - error = mf_check_dst(&dst, flow); + error = mf_check_src(&move->src, flow); if (error) { return error; } - /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in - * action->value. */ - if (dst.n_bits < 64 && ntohll(action->value) >> dst.n_bits) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } + return mf_check_dst(&move->dst, NULL); +} - return 0; +enum ofperr +nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow) +{ + return mf_check_dst(&load->dst, flow); } -/* nxm_execute_reg_move(), nxm_execute_reg_load(). */ - void -nxm_execute_reg_move(const struct nx_action_reg_move *action, - struct flow *flow) +nxm_reg_move_to_nxast(const struct ofpact_reg_move *move, + struct ofpbuf *openflow) { - struct mf_subfield src, dst; - union mf_value src_value; - union mf_value dst_value; + struct nx_action_reg_move *narm; - nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits); - nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits); + narm = ofputil_put_NXAST_REG_MOVE(openflow); + narm->n_bits = htons(move->dst.n_bits); + narm->src_ofs = htons(move->src.ofs); + narm->dst_ofs = htons(move->dst.ofs); + narm->src = htonl(move->src.field->nxm_header); + narm->dst = htonl(move->dst.field->nxm_header); +} - mf_get_value(dst.field, flow, &dst_value); - mf_get_value(src.field, flow, &src_value); - bitwise_copy(&src_value, src.field->n_bytes, src.ofs, - &dst_value, dst.field->n_bytes, dst.ofs, - src.n_bits); - mf_set_flow_value(dst.field, &dst_value, flow); +void +nxm_reg_load_to_nxast(const struct ofpact_reg_load *load, + struct ofpbuf *openflow) +{ + struct nx_action_reg_load *narl; + + narl = ofputil_put_NXAST_REG_LOAD(openflow); + narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits); + narl->dst = htonl(load->dst.field->nxm_header); + narl->value = htonll(load->value); } + +/* nxm_execute_reg_move(), nxm_execute_reg_load(). */ void -nxm_execute_reg_load(const struct nx_action_reg_load *action, +nxm_execute_reg_move(const struct ofpact_reg_move *move, struct flow *flow) { - struct mf_subfield dst; + union mf_value src_value; + union mf_value dst_value; - nxm_decode(&dst, action->dst, action->ofs_nbits); - mf_set_subfield_value(&dst, ntohll(action->value), flow); + mf_get_value(move->dst.field, flow, &dst_value); + mf_get_value(move->src.field, flow, &src_value); + bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs, + &dst_value, move->dst.field->n_bytes, move->dst.ofs, + move->src.n_bits); + mf_set_flow_value(move->dst.field, &dst_value, flow); } -/* Initializes 'sf->field' with the field corresponding to the given NXM - * 'header' and 'sf->ofs' and 'sf->n_bits' decoded from 'ofs_nbits' with - * nxm_decode_ofs() and nxm_decode_n_bits(), respectively. - * - * Afterward, 'sf' might be invalid in a few different ways: - * - * - 'sf->field' will be NULL if 'header' is unknown. - * - * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field. - * - * The caller should call mf_check_src() or mf_check_dst() to check for these - * problems. */ void -nxm_decode(struct mf_subfield *sf, ovs_be32 header, ovs_be16 ofs_nbits) +nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow) { - sf->field = mf_from_nxm_header(ntohl(header)); - sf->ofs = nxm_decode_ofs(ofs_nbits); - sf->n_bits = nxm_decode_n_bits(ofs_nbits); + nxm_reg_load(&load->dst, load->value, flow); } -/* Initializes 'sf->field' with the field corresponding to the given NXM - * 'header' and 'sf->ofs' and 'sf->n_bits' from 'ofs' and 'n_bits', - * respectively. - * - * Afterward, 'sf' might be invalid in a few different ways: - * - * - 'sf->field' will be NULL if 'header' is unknown. - * - * - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field. - * - * The caller should call mf_check_src() or mf_check_dst() to check for these - * problems. */ void -nxm_decode_discrete(struct mf_subfield *sf, ovs_be32 header, - ovs_be16 ofs, ovs_be16 n_bits) +nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data, + struct flow *flow) { - sf->field = mf_from_nxm_header(ntohl(header)); - sf->ofs = ntohs(ofs); - sf->n_bits = ntohs(n_bits); + union mf_value dst_value; + union mf_value src_value; + + mf_get_value(dst->field, flow, &dst_value); + src_value.be64 = htonll(src_data); + bitwise_copy(&src_value, sizeof src_value.be64, 0, + &dst_value, dst->field->n_bytes, dst->ofs, + dst->n_bits); + mf_set_flow_value(dst->field, &dst_value, flow); } diff --git a/lib/nx-match.h b/lib/nx-match.h index c814275f..6248b2a4 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -21,6 +21,7 @@ #include #include #include "flow.h" +#include "ofp-errors.h" #include "openvswitch/types.h" #include "ofp-errors.h" @@ -28,6 +29,8 @@ struct cls_rule; struct ds; struct flow; struct mf_subfield; +struct ofpact_reg_move; +struct ofpact_reg_load; struct ofpbuf; struct nx_action_reg_load; struct nx_action_reg_move; @@ -49,19 +52,31 @@ int nx_put_match(struct ofpbuf *, bool oxm, const struct cls_rule *, char *nx_match_to_string(const uint8_t *, unsigned int match_len); int nx_match_from_string(const char *, struct ofpbuf *); -void nxm_parse_reg_move(struct nx_action_reg_move *, const char *); -void nxm_parse_reg_load(struct nx_action_reg_load *, const char *); +void nxm_parse_reg_move(struct ofpact_reg_move *, const char *); +void nxm_parse_reg_load(struct ofpact_reg_load *, const char *); + +void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *); +void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *); -void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *); -void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *); +enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *, + struct ofpbuf *ofpacts); +enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *, + struct ofpbuf *ofpacts); -enum ofperr nxm_check_reg_move(const struct nx_action_reg_move *, +enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *, const struct flow *); -enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *, +enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *, const struct flow *); -void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *); -void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *); +void nxm_reg_move_to_nxast(const struct ofpact_reg_move *, + struct ofpbuf *openflow); +void nxm_reg_load_to_nxast(const struct ofpact_reg_load *, + struct ofpbuf *openflow); + +void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *); +void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *); +void nxm_reg_load(const struct mf_subfield *, uint64_t src_data, + struct flow *); int nxm_field_bytes(uint32_t header); int nxm_field_bits(uint32_t header); @@ -85,10 +100,6 @@ nxm_decode_n_bits(ovs_be16 ofs_nbits) { return (ntohs(ofs_nbits) & 0x3f) + 1; } - -void nxm_decode(struct mf_subfield *, ovs_be32 header, ovs_be16 ofs_nbits); -void nxm_decode_discrete(struct mf_subfield *, ovs_be32 header, - ovs_be16 ofs, ovs_be16 n_bits); BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12); /* Upper bound on the length of an nx_match. The longest nx_match (an diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c new file mode 100644 index 00000000..12cc0b0b --- /dev/null +++ b/lib/ofp-actions.c @@ -0,0 +1,1218 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks. + * + * 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 +#include "ofp-actions.h" +#include "autopath.h" +#include "bundle.h" +#include "byte-order.h" +#include "compiler.h" +#include "dynamic-string.h" +#include "learn.h" +#include "meta-flow.h" +#include "multipath.h" +#include "nx-match.h" +#include "ofp-util.h" +#include "ofpbuf.h" +#include "vlog.h" + +VLOG_DEFINE_THIS_MODULE(ofp_actions); + +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + +/* Converting OpenFlow 1.0 to ofpacts. */ + +static enum ofperr +output_from_openflow10(const struct ofp_action_output *oao, + struct ofpbuf *out) +{ + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(out); + output->port = ntohs(oao->port); + output->max_len = ntohs(oao->max_len); + + return ofputil_check_output_port(output->port, OFPP_MAX); +} + +static enum ofperr +enqueue_from_openflow10(const struct ofp_action_enqueue *oae, + struct ofpbuf *out) +{ + struct ofpact_enqueue *enqueue; + + enqueue = ofpact_put_ENQUEUE(out); + enqueue->port = ntohs(oae->port); + enqueue->queue = ntohl(oae->queue_id); + if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT + && enqueue->port != OFPP_LOCAL) { + return OFPERR_OFPBAC_BAD_OUT_PORT; + } + return 0; +} + +static void +resubmit_from_openflow(const struct nx_action_resubmit *nar, + struct ofpbuf *out) +{ + struct ofpact_resubmit *resubmit; + + resubmit = ofpact_put_RESUBMIT(out); + resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT; + resubmit->in_port = ntohs(nar->in_port); + resubmit->table_id = 0xff; +} + +static enum ofperr +resubmit_table_from_openflow(const struct nx_action_resubmit *nar, + struct ofpbuf *out) +{ + struct ofpact_resubmit *resubmit; + + if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + resubmit = ofpact_put_RESUBMIT(out); + resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE; + resubmit->in_port = ntohs(nar->in_port); + resubmit->table_id = nar->table; + return 0; +} + +static enum ofperr +output_reg_from_openflow(const struct nx_action_output_reg *naor, + struct ofpbuf *out) +{ + struct ofpact_output_reg *output_reg; + + if (!is_all_zeros(naor->zero, sizeof naor->zero)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + + output_reg = ofpact_put_OUTPUT_REG(out); + output_reg->src.field = mf_from_nxm_header(ntohl(naor->src)); + output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits); + output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits); + output_reg->max_len = ntohs(naor->max_len); + + return mf_check_src(&output_reg->src, NULL); +} + +static void +fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft, + struct ofpbuf *out) +{ + struct ofpact_fin_timeout *oft; + + oft = ofpact_put_FIN_TIMEOUT(out); + oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout); + oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout); +} + +static void +controller_from_openflow(const struct nx_action_controller *nac, + struct ofpbuf *out) +{ + struct ofpact_controller *oc; + + oc = ofpact_put_CONTROLLER(out); + oc->max_len = ntohs(nac->max_len); + oc->controller_id = ntohs(nac->controller_id); + oc->reason = nac->reason; +} + +static void +note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) +{ + struct ofpact_note *note; + unsigned int length; + + length = ntohs(nan->len) - offsetof(struct nx_action_note, note); + note = ofpact_put(out, OFPACT_NOTE, + offsetof(struct ofpact_note, data) + length); + note->length = length; + memcpy(note->data, nan->note, length); +} + +static enum ofperr +decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code) +{ + const struct nx_action_header *nah = (const struct nx_action_header *) a; + uint16_t len = ntohs(a->header.len); + + if (len < sizeof(struct nx_action_header)) { + return OFPERR_OFPBAC_BAD_LEN; + } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) { + return OFPERR_OFPBAC_BAD_VENDOR; + } + + switch (nah->subtype) { +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ + case CONSTANT_HTONS(ENUM): \ + if (EXTENSIBLE \ + ? len >= sizeof(struct STRUCT) \ + : len == sizeof(struct STRUCT)) { \ + *code = OFPUTIL_##ENUM; \ + return 0; \ + } else { \ + return OFPERR_OFPBAC_BAD_LEN; \ + } \ + NOT_REACHED(); +#include "ofp-util.def" + + case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE): + case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE): + default: + return OFPERR_OFPBAC_BAD_TYPE; + } +} + +/* Parses 'a' to determine its type. On success stores the correct type into + * '*code' and returns 0. On failure returns an OFPERR_* error code and + * '*code' is indeterminate. + * + * The caller must have already verified that 'a''s length is potentially + * correct (that is, a->header.len is nonzero and a multiple of sizeof(union + * ofp_action) and no longer than the amount of space allocated to 'a'). + * + * This function verifies that 'a''s length is correct for the type of action + * that it represents. */ +static enum ofperr +decode_openflow10_action(const union ofp_action *a, + enum ofputil_action_code *code) +{ + switch (a->type) { + case CONSTANT_HTONS(OFPAT10_VENDOR): + return decode_nxast_action(a, code); + +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ + case CONSTANT_HTONS(ENUM): \ + if (a->header.len == htons(sizeof(struct STRUCT))) { \ + *code = OFPUTIL_##ENUM; \ + return 0; \ + } else { \ + return OFPERR_OFPBAC_BAD_LEN; \ + } \ + break; +#include "ofp-util.def" + + default: + return OFPERR_OFPBAC_BAD_TYPE; + } +} + +static enum ofperr +ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out) +{ + const struct nx_action_resubmit *nar; + const struct nx_action_set_tunnel *nast; + const struct nx_action_set_queue *nasq; + const struct nx_action_note *nan; + const struct nx_action_set_tunnel64 *nast64; + struct ofpact_tunnel *tunnel; + enum ofputil_action_code code; + enum ofperr error; + + error = decode_openflow10_action(a, &code); + if (error) { + return error; + } + + switch (code) { + case OFPUTIL_ACTION_INVALID: + NOT_REACHED(); + + case OFPUTIL_OFPAT10_OUTPUT: + return output_from_openflow10((const struct ofp_action_output *) a, + out); + + case OFPUTIL_OFPAT10_SET_VLAN_VID: + if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid); + break; + + case OFPUTIL_OFPAT10_SET_VLAN_PCP: + if (a->vlan_pcp.vlan_pcp & ~7) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp; + break; + + case OFPUTIL_OFPAT10_STRIP_VLAN: + ofpact_put_STRIP_VLAN(out); + break; + + case OFPUTIL_OFPAT10_SET_DL_SRC: + memcpy(ofpact_put_SET_ETH_SRC(out)->mac, + ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN); + break; + + case OFPUTIL_OFPAT10_SET_DL_DST: + memcpy(ofpact_put_SET_ETH_DST(out)->mac, + ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN); + break; + + case OFPUTIL_OFPAT10_SET_NW_SRC: + ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr; + break; + + case OFPUTIL_OFPAT10_SET_NW_DST: + ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr; + break; + + case OFPUTIL_OFPAT10_SET_NW_TOS: + if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos; + break; + + case OFPUTIL_OFPAT10_SET_TP_SRC: + ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port); + break; + + case OFPUTIL_OFPAT10_SET_TP_DST: + ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port); + + break; + + case OFPUTIL_OFPAT10_ENQUEUE: + error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a, + out); + break; + + case OFPUTIL_NXAST_RESUBMIT: + resubmit_from_openflow((const struct nx_action_resubmit *) a, out); + break; + + case OFPUTIL_NXAST_SET_TUNNEL: + nast = (const struct nx_action_set_tunnel *) a; + tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.compat = code; + tunnel->tun_id = ntohl(nast->tun_id); + break; + + case OFPUTIL_NXAST_SET_QUEUE: + nasq = (const struct nx_action_set_queue *) a; + ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id); + break; + + case OFPUTIL_NXAST_POP_QUEUE: + ofpact_put_POP_QUEUE(out); + break; + + case OFPUTIL_NXAST_REG_MOVE: + error = nxm_reg_move_from_openflow( + (const struct nx_action_reg_move *) a, out); + break; + + case OFPUTIL_NXAST_REG_LOAD: + error = nxm_reg_load_from_openflow( + (const struct nx_action_reg_load *) a, out); + break; + + case OFPUTIL_NXAST_NOTE: + nan = (const struct nx_action_note *) a; + note_from_openflow(nan, out); + break; + + case OFPUTIL_NXAST_SET_TUNNEL64: + nast64 = (const struct nx_action_set_tunnel64 *) a; + tunnel = ofpact_put_SET_TUNNEL(out); + tunnel->ofpact.compat = code; + tunnel->tun_id = ntohll(nast64->tun_id); + break; + + case OFPUTIL_NXAST_MULTIPATH: + error = multipath_from_openflow((const struct nx_action_multipath *) a, + ofpact_put_MULTIPATH(out)); + break; + + case OFPUTIL_NXAST_AUTOPATH: + error = autopath_from_openflow((const struct nx_action_autopath *) a, + ofpact_put_AUTOPATH(out)); + break; + + case OFPUTIL_NXAST_BUNDLE: + case OFPUTIL_NXAST_BUNDLE_LOAD: + error = bundle_from_openflow((const struct nx_action_bundle *) a, out); + break; + + case OFPUTIL_NXAST_OUTPUT_REG: + error = output_reg_from_openflow( + (const struct nx_action_output_reg *) a, out); + break; + + case OFPUTIL_NXAST_RESUBMIT_TABLE: + nar = (const struct nx_action_resubmit *) a; + error = resubmit_table_from_openflow(nar, out); + break; + + case OFPUTIL_NXAST_LEARN: + error = learn_from_openflow((const struct nx_action_learn *) a, out); + break; + + case OFPUTIL_NXAST_EXIT: + ofpact_put_EXIT(out); + break; + + case OFPUTIL_NXAST_DEC_TTL: + ofpact_put_DEC_TTL(out); + break; + + case OFPUTIL_NXAST_FIN_TIMEOUT: + fin_timeout_from_openflow( + (const struct nx_action_fin_timeout *) a, out); + break; + + case OFPUTIL_NXAST_CONTROLLER: + controller_from_openflow((const struct nx_action_controller *) a, out); + break; + } + + return error; +} + +static inline union ofp_action * +action_next(const union ofp_action *a) +{ + return ((union ofp_action *) (void *) + ((uint8_t *) a + ntohs(a->header.len))); +} + +static inline bool +action_is_valid(const union ofp_action *a, size_t n_actions) +{ + uint16_t len = ntohs(a->header.len); + return (!(len % OFP_ACTION_ALIGN) + && len >= sizeof *a + && len / sizeof *a <= n_actions); +} + +/* This macro is careful to check for actions with bad lengths. */ +#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \ + for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \ + (LEFT) > 0 && action_is_valid(ITER, LEFT); \ + ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \ + (ITER) = action_next(ITER))) + +static enum ofperr +ofpact_from_openflow10(const union ofp_action *in, size_t n_in, + struct ofpbuf *out) +{ + const union ofp_action *a; + size_t left; + + ACTION_FOR_EACH (a, left, in, n_in) { + enum ofperr error = ofpact_from_openflow10__(a, out); + if (error) { + VLOG_WARN_RL(&rl, "bad action at offset %td (%s)", + (a - in) * sizeof *a, ofperr_get_name(error)); + return error; + } + } + if (left) { + VLOG_WARN_RL(&rl, "bad action format at offset %zu", + (n_in - left) * sizeof *a); + return OFPERR_OFPBAC_BAD_LEN; + } + + ofpact_pad(out); + return 0; +} + +/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the front + * of 'openflow' into ofpacts. On success, replaces any existing content in + * 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'. Returns 0 + * if successful, otherwise an OpenFlow error. + * + * This function does not check that the actions are valid in a given context. + * The caller should do so, with ofpacts_check(). */ +enum ofperr +ofpacts_pull_openflow(struct ofpbuf *openflow, unsigned int actions_len, + struct ofpbuf *ofpacts) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + const union ofp_action *actions; + enum ofperr error; + + ofpbuf_clear(ofpacts); + + if (actions_len % OFP_ACTION_ALIGN != 0) { + VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a " + "multiple of %d", actions_len, OFP_ACTION_ALIGN); + return OFPERR_OFPBRC_BAD_LEN; + } + + actions = ofpbuf_try_pull(openflow, actions_len); + if (actions == NULL) { + VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " + "remaining message length (%zu)", + actions_len, openflow->size); + return OFPERR_OFPBRC_BAD_LEN; + } + + error = ofpact_from_openflow10(actions, actions_len / OFP_ACTION_ALIGN, + ofpacts); + if (error) { + ofpbuf_clear(ofpacts); + } + return 0; +} + +static enum ofperr +ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) +{ + const struct ofpact_enqueue *enqueue; + + switch (a->type) { + case OFPACT_OUTPUT: + return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port, + max_ports); + + case OFPACT_CONTROLLER: + return 0; + + case OFPACT_ENQUEUE: + enqueue = ofpact_get_ENQUEUE(a); + if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT + && enqueue->port != OFPP_LOCAL) { + return OFPERR_OFPBAC_BAD_OUT_PORT; + } + return 0; + + case OFPACT_OUTPUT_REG: + return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow); + + case OFPACT_BUNDLE: + return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow); + + case OFPACT_SET_VLAN_VID: + case OFPACT_SET_VLAN_PCP: + case OFPACT_STRIP_VLAN: + case OFPACT_SET_ETH_SRC: + case OFPACT_SET_ETH_DST: + case OFPACT_SET_IPV4_SRC: + case OFPACT_SET_IPV4_DST: + case OFPACT_SET_IPV4_DSCP: + case OFPACT_SET_L4_SRC_PORT: + case OFPACT_SET_L4_DST_PORT: + return 0; + + case OFPACT_REG_MOVE: + return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow); + + case OFPACT_REG_LOAD: + return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); + + case OFPACT_DEC_TTL: + case OFPACT_SET_TUNNEL: + case OFPACT_SET_QUEUE: + case OFPACT_POP_QUEUE: + case OFPACT_FIN_TIMEOUT: + case OFPACT_RESUBMIT: + return 0; + + case OFPACT_LEARN: + return learn_check(ofpact_get_LEARN(a), flow); + + case OFPACT_MULTIPATH: + return multipath_check(ofpact_get_MULTIPATH(a), flow); + + case OFPACT_AUTOPATH: + return autopath_check(ofpact_get_AUTOPATH(a), flow); + + case OFPACT_NOTE: + case OFPACT_EXIT: + return 0; + + default: + NOT_REACHED(); + } +} + +/* Checks that the 'ofpacts_len' bytes of actions in 'ofpacts' are + * appropriate for a packet with the prerequisites satisfied by 'flow' in a + * switch with no more than 'max_ports' ports. */ +enum ofperr +ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, + const struct flow *flow, int max_ports) +{ + const struct ofpact *a; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + enum ofperr error = ofpact_check__(a, flow, max_ports); + if (error) { + return error; + } + } + + return 0; +} + +/* Converting ofpacts to Nicira OpenFlow extensions. */ + +static void +ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg, + struct ofpbuf *out) +{ + struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out); + + naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs, + output_reg->src.n_bits); + naor->src = htonl(output_reg->src.field->nxm_header); + naor->max_len = htons(output_reg->max_len); +} + +static void +ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit, + struct ofpbuf *out) +{ + struct nx_action_resubmit *nar; + + if (resubmit->table_id == 0xff + && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) { + nar = ofputil_put_NXAST_RESUBMIT(out); + } else { + nar = ofputil_put_NXAST_RESUBMIT_TABLE(out); + nar->table = resubmit->table_id; + } + nar->in_port = htons(resubmit->in_port); +} + +static void +ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel, + struct ofpbuf *out) +{ + uint64_t tun_id = tunnel->tun_id; + + if (tun_id <= UINT32_MAX + && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) { + ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id); + } else { + ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id); + } +} + +static void +ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out) +{ + size_t start_ofs = out->size; + struct nx_action_note *nan; + unsigned int remainder; + unsigned int len; + + nan = ofputil_put_NXAST_NOTE(out); + out->size -= sizeof nan->note; + + ofpbuf_put(out, note->data, note->length); + + len = out->size - start_ofs; + remainder = len % OFP_ACTION_ALIGN; + if (remainder) { + ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder); + } + nan = (struct nx_action_note *)((char *)out->data + start_ofs); + nan->len = htons(out->size - start_ofs); +} + +static void +ofpact_controller_to_nxast(const struct ofpact_controller *oc, + struct ofpbuf *out) +{ + struct nx_action_controller *nac; + + nac = ofputil_put_NXAST_CONTROLLER(out); + nac->max_len = htons(oc->max_len); + nac->controller_id = htons(oc->controller_id); + nac->reason = oc->reason; +} + +static void +ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout, + struct ofpbuf *out) +{ + struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out); + naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout); + naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout); +} + +static void +ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) +{ + switch (a->type) { + case OFPACT_CONTROLLER: + ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out); + break; + + case OFPACT_OUTPUT_REG: + ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out); + break; + + case OFPACT_BUNDLE: + bundle_to_nxast(ofpact_get_BUNDLE(a), out); + break; + + case OFPACT_REG_MOVE: + nxm_reg_move_to_nxast(ofpact_get_REG_MOVE(a), out); + break; + + case OFPACT_REG_LOAD: + nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out); + break; + + case OFPACT_DEC_TTL: + ofputil_put_NXAST_DEC_TTL(out); + break; + + case OFPACT_SET_TUNNEL: + ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out); + break; + + case OFPACT_SET_QUEUE: + ofputil_put_NXAST_SET_QUEUE(out)->queue_id + = htonl(ofpact_get_SET_QUEUE(a)->queue_id); + break; + + case OFPACT_POP_QUEUE: + ofputil_put_NXAST_POP_QUEUE(out); + break; + + case OFPACT_FIN_TIMEOUT: + ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out); + break; + + case OFPACT_RESUBMIT: + ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out); + break; + + case OFPACT_LEARN: + learn_to_nxast(ofpact_get_LEARN(a), out); + break; + + case OFPACT_MULTIPATH: + multipath_to_nxast(ofpact_get_MULTIPATH(a), out); + break; + + case OFPACT_AUTOPATH: + autopath_to_nxast(ofpact_get_AUTOPATH(a), out); + break; + + case OFPACT_NOTE: + ofpact_note_to_nxast(ofpact_get_NOTE(a), out); + break; + + case OFPACT_EXIT: + ofputil_put_NXAST_EXIT(out); + break; + + case OFPACT_OUTPUT: + case OFPACT_ENQUEUE: + case OFPACT_SET_VLAN_VID: + case OFPACT_SET_VLAN_PCP: + case OFPACT_STRIP_VLAN: + case OFPACT_SET_ETH_SRC: + case OFPACT_SET_ETH_DST: + case OFPACT_SET_IPV4_SRC: + case OFPACT_SET_IPV4_DST: + case OFPACT_SET_IPV4_DSCP: + case OFPACT_SET_L4_SRC_PORT: + case OFPACT_SET_L4_DST_PORT: + NOT_REACHED(); + } +} + +/* Converting ofpacts to OpenFlow 1.0. */ + +static void +ofpact_output_to_openflow10(const struct ofpact_output *output, + struct ofpbuf *out) +{ + struct ofp_action_output *oao; + + oao = ofputil_put_OFPAT10_OUTPUT(out); + oao->port = htons(output->port); + oao->max_len = htons(output->max_len); +} + +static void +ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue, + struct ofpbuf *out) +{ + struct ofp_action_enqueue *oae; + + oae = ofputil_put_OFPAT10_ENQUEUE(out); + oae->port = htons(enqueue->port); + oae->queue_id = htonl(enqueue->queue); +} + +static void +ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) +{ + switch (a->type) { + case OFPACT_OUTPUT: + ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out); + break; + + case OFPACT_ENQUEUE: + ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), out); + break; + + case OFPACT_SET_VLAN_VID: + ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid + = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid); + break; + + case OFPACT_SET_VLAN_PCP: + ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp + = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; + break; + + case OFPACT_STRIP_VLAN: + ofputil_put_OFPAT10_STRIP_VLAN(out); + break; + + case OFPACT_SET_ETH_SRC: + memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr, + ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN); + break; + + case OFPACT_SET_ETH_DST: + memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr, + ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN); + break; + + case OFPACT_SET_IPV4_SRC: + ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr + = ofpact_get_SET_IPV4_SRC(a)->ipv4; + break; + + case OFPACT_SET_IPV4_DST: + ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr + = ofpact_get_SET_IPV4_DST(a)->ipv4; + break; + + case OFPACT_SET_IPV4_DSCP: + ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos + = ofpact_get_SET_IPV4_DSCP(a)->dscp; + break; + + case OFPACT_SET_L4_SRC_PORT: + ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port + = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); + break; + + case OFPACT_SET_L4_DST_PORT: + ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port + = htons(ofpact_get_SET_L4_DST_PORT(a)->port); + break; + + case OFPACT_CONTROLLER: + case OFPACT_OUTPUT_REG: + case OFPACT_BUNDLE: + case OFPACT_REG_MOVE: + case OFPACT_REG_LOAD: + case OFPACT_DEC_TTL: + case OFPACT_SET_TUNNEL: + case OFPACT_SET_QUEUE: + case OFPACT_POP_QUEUE: + case OFPACT_FIN_TIMEOUT: + case OFPACT_RESUBMIT: + case OFPACT_LEARN: + case OFPACT_MULTIPATH: + case OFPACT_AUTOPATH: + case OFPACT_NOTE: + case OFPACT_EXIT: + ofpact_to_nxast(a, out); + break; + } +} + +/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow + * actions in 'openflow', appending the actions to any existing data in + * 'openflow'. */ +void +ofpacts_to_openflow(const struct ofpact ofpacts[], size_t ofpacts_len, + struct ofpbuf *openflow) +{ + const struct ofpact *a; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + ofpact_to_openflow10(a, openflow); + } +} + +/* Returns true if 'action' outputs to 'port', false otherwise. */ +static bool +ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) +{ + switch (ofpact->type) { + case OFPACT_OUTPUT: + return ofpact_get_OUTPUT(ofpact)->port == port; + case OFPACT_ENQUEUE: + return ofpact_get_ENQUEUE(ofpact)->port == port; + case OFPACT_CONTROLLER: + return port == OFPP_CONTROLLER; + + case OFPACT_OUTPUT_REG: + case OFPACT_BUNDLE: + case OFPACT_SET_VLAN_VID: + case OFPACT_SET_VLAN_PCP: + case OFPACT_STRIP_VLAN: + case OFPACT_SET_ETH_SRC: + case OFPACT_SET_ETH_DST: + case OFPACT_SET_IPV4_SRC: + case OFPACT_SET_IPV4_DST: + case OFPACT_SET_IPV4_DSCP: + case OFPACT_SET_L4_SRC_PORT: + case OFPACT_SET_L4_DST_PORT: + case OFPACT_REG_MOVE: + case OFPACT_REG_LOAD: + case OFPACT_DEC_TTL: + case OFPACT_SET_TUNNEL: + case OFPACT_SET_QUEUE: + case OFPACT_POP_QUEUE: + case OFPACT_FIN_TIMEOUT: + case OFPACT_RESUBMIT: + case OFPACT_LEARN: + case OFPACT_MULTIPATH: + case OFPACT_AUTOPATH: + case OFPACT_NOTE: + case OFPACT_EXIT: + default: + return false; + } +} + +/* Returns true if any action in the 'ofpacts_len' bytes of 'ofpacts' outputs + * to 'port', false otherwise. */ +bool +ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len, + uint16_t port) +{ + const struct ofpact *a; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (ofpact_outputs_to_port(a, port)) { + return true; + } + } + + return false; +} + +bool +ofpacts_equal(const struct ofpact *a, size_t a_len, + const struct ofpact *b, size_t b_len) +{ + return a_len == b_len && !memcmp(a, b, a_len); +} + +/* Formatting ofpacts. */ + +static void +print_note(const struct ofpact_note *note, struct ds *string) +{ + size_t i; + + ds_put_cstr(string, "note:"); + for (i = 0; i < note->length; i++) { + if (i) { + ds_put_char(string, '.'); + } + ds_put_format(string, "%02"PRIx8, note->data[i]); + } +} + +static void +print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout, + struct ds *s) +{ + ds_put_cstr(s, "fin_timeout("); + if (fin_timeout->fin_idle_timeout) { + ds_put_format(s, "idle_timeout=%"PRIu16",", + fin_timeout->fin_idle_timeout); + } + if (fin_timeout->fin_hard_timeout) { + ds_put_format(s, "hard_timeout=%"PRIu16",", + fin_timeout->fin_hard_timeout); + } + ds_chomp(s, ','); + ds_put_char(s, ')'); +} + +static void +ofpact_format(const struct ofpact *a, struct ds *s) +{ + const struct ofpact_enqueue *enqueue; + const struct ofpact_resubmit *resubmit; + const struct ofpact_autopath *autopath; + const struct ofpact_controller *controller; + const struct ofpact_tunnel *tunnel; + uint16_t port; + + switch (a->type) { + case OFPACT_OUTPUT: + port = ofpact_get_OUTPUT(a)->port; + if (port < OFPP_MAX) { + ds_put_format(s, "output:%"PRIu16, port); + } else { + ofputil_format_port(port, s); + if (port == OFPP_CONTROLLER) { + ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len); + } + } + break; + + case OFPACT_CONTROLLER: + controller = ofpact_get_CONTROLLER(a); + if (controller->reason == OFPR_ACTION && + controller->controller_id == 0) { + ds_put_format(s, "CONTROLLER:%"PRIu16, + ofpact_get_CONTROLLER(a)->max_len); + } else { + enum ofp_packet_in_reason reason = controller->reason; + + ds_put_cstr(s, "controller("); + if (reason != OFPR_ACTION) { + ds_put_format(s, "reason=%s,", + ofputil_packet_in_reason_to_string(reason)); + } + if (controller->max_len != UINT16_MAX) { + ds_put_format(s, "max_len=%"PRIu16",", controller->max_len); + } + if (controller->controller_id != 0) { + ds_put_format(s, "id=%"PRIu16",", controller->controller_id); + } + ds_chomp(s, ','); + ds_put_char(s, ')'); + } + break; + + case OFPACT_ENQUEUE: + enqueue = ofpact_get_ENQUEUE(a); + ds_put_format(s, "enqueue:"); + ofputil_format_port(enqueue->port, s); + ds_put_format(s, "q%"PRIu32, enqueue->queue); + break; + + case OFPACT_OUTPUT_REG: + ds_put_cstr(s, "output:"); + mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s); + break; + + case OFPACT_BUNDLE: + bundle_format(ofpact_get_BUNDLE(a), s); + break; + + case OFPACT_SET_VLAN_VID: + ds_put_format(s, "mod_vlan_vid:%"PRIu16, + ofpact_get_SET_VLAN_VID(a)->vlan_vid); + break; + + case OFPACT_SET_VLAN_PCP: + ds_put_format(s, "mod_vlan_pcp:%"PRIu8, + ofpact_get_SET_VLAN_PCP(a)->vlan_pcp); + break; + + case OFPACT_STRIP_VLAN: + ds_put_cstr(s, "strip_vlan"); + break; + + case OFPACT_SET_ETH_SRC: + ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, + ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac)); + break; + + case OFPACT_SET_ETH_DST: + ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, + ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac)); + break; + + case OFPACT_SET_IPV4_SRC: + ds_put_format(s, "mod_nw_src:"IP_FMT, + IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4)); + break; + + case OFPACT_SET_IPV4_DST: + ds_put_format(s, "mod_nw_dst:"IP_FMT, + IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4)); + break; + + case OFPACT_SET_IPV4_DSCP: + ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IPV4_DSCP(a)->dscp); + break; + + case OFPACT_SET_L4_SRC_PORT: + ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port); + break; + + case OFPACT_SET_L4_DST_PORT: + ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port); + break; + + case OFPACT_REG_MOVE: + nxm_format_reg_move(ofpact_get_REG_MOVE(a), s); + break; + + case OFPACT_REG_LOAD: + nxm_format_reg_load(ofpact_get_REG_LOAD(a), s); + break; + + case OFPACT_DEC_TTL: + ds_put_cstr(s, "dec_ttl"); + break; + + case OFPACT_SET_TUNNEL: + tunnel = ofpact_get_SET_TUNNEL(a); + ds_put_format(s, "set_tunnel%s:%#"PRIx64, + (tunnel->tun_id > UINT32_MAX + || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""), + tunnel->tun_id); + break; + + case OFPACT_SET_QUEUE: + ds_put_format(s, "set_queue:%"PRIu32, + ofpact_get_SET_QUEUE(a)->queue_id); + break; + + case OFPACT_POP_QUEUE: + ds_put_cstr(s, "pop_queue"); + break; + + case OFPACT_FIN_TIMEOUT: + print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s); + break; + + case OFPACT_RESUBMIT: + resubmit = ofpact_get_RESUBMIT(a); + if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) { + ds_put_format(s, "resubmit:%"PRIu16, resubmit->in_port); + } else { + ds_put_format(s, "resubmit("); + if (resubmit->in_port != OFPP_IN_PORT) { + ofputil_format_port(resubmit->in_port, s); + } + ds_put_char(s, ','); + if (resubmit->table_id != 255) { + ds_put_format(s, "%"PRIu8, resubmit->table_id); + } + ds_put_char(s, ')'); + } + break; + + case OFPACT_LEARN: + learn_format(ofpact_get_LEARN(a), s); + break; + + case OFPACT_MULTIPATH: + multipath_format(ofpact_get_MULTIPATH(a), s); + break; + + case OFPACT_AUTOPATH: + autopath = ofpact_get_AUTOPATH(a); + ds_put_format(s, "autopath(%u,", autopath->port); + mf_format_subfield(&autopath->dst, s); + ds_put_char(s, ')'); + break; + + case OFPACT_NOTE: + print_note(ofpact_get_NOTE(a), s); + break; + + case OFPACT_EXIT: + ds_put_cstr(s, "exit"); + break; + } +} + +/* Appends a string representing the 'ofpacts_len' bytes of ofpacts in + * 'ofpacts' to 'string'. */ +void +ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, + struct ds *string) +{ + ds_put_cstr(string, "actions="); + if (!ofpacts_len) { + ds_put_cstr(string, "drop"); + } else { + const struct ofpact *a; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + if (a != ofpacts) { + ds_put_cstr(string, ","); + } + ofpact_format(a, string); + } + } +} + +/* Internal use by helpers. */ + +void * +ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len) +{ + struct ofpact *ofpact; + + ofpact_pad(ofpacts); + ofpact = ofpacts->l2 = ofpbuf_put_uninit(ofpacts, len); + ofpact_init(ofpact, type, len); + return ofpact; +} + +void +ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len) +{ + memset(ofpact, 0, len); + ofpact->type = type; + ofpact->compat = OFPUTIL_ACTION_INVALID; + ofpact->len = len; +} + +/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts' + * starting at 'ofpact'. + * + * This is the correct way to update a variable-length ofpact's length after + * adding the variable-length part of the payload. (See the large comment + * near the end of ofp-actions.h for more information.) */ +void +ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact) +{ + assert(ofpact == ofpacts->l2); + ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact; +} + +/* Pads out 'ofpacts' to a multiple of OFPACT_ALIGNTO bytes in length. Each + * ofpact_put_() calls this function automatically beforehand, but the + * client must call this itself after adding the final ofpact to an array of + * them. + * + * (The consequences of failing to call this function are probably not dire. + * OFPACT_FOR_EACH will calculate a pointer beyond the end of the ofpacts, but + * not dereference it. That's undefined behavior, technically, but it will not + * cause a real problem on common systems. Still, it seems better to call + * it.) */ +void +ofpact_pad(struct ofpbuf *ofpacts) +{ + unsigned int rem = ofpacts->size % OFPACT_ALIGNTO; + if (rem) { + ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem); + } +} diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h new file mode 100644 index 00000000..59b9846b --- /dev/null +++ b/lib/ofp-actions.h @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2012 Nicira Networks. + * + * 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. + */ + +#ifndef OFP_ACTIONS_H +#define OFP_ACTIONS_H 1 + +#include +#include "meta-flow.h" +#include "ofp-errors.h" +#include "ofp-util.h" +#include "openflow/openflow.h" +#include "openflow/nicira-ext.h" +#include "openvswitch/types.h" + +/* List of OVS abstracted actions. + * + * This macro is used directly only internally by this header, but the list is + * still of interest to developers. + * + * Each DEFINE_OFPACT invocation has the following parameters: + * + * 1. , used below in the enum definition of OFPACT_, and + * elsewhere. + * + * 2. corresponding to a structure "struct ", that must be + * defined below. This structure must be an abstract definition of the + * action. Its first member must have type "struct ofpact" and name + * "ofpact". It may be fixed length or end with a flexible array member + * (e.g. "int member[];"). + * + * 3. , which has one of two possible values: + * + * - If "struct " is fixed-length, it must be "ofpact". + * + * - If "struct " is variable-length, it must be the name of the + * flexible array member. + */ +#define OFPACTS \ + /* Output. */ \ + DEFINE_OFPACT(OUTPUT, ofpact_output, ofpact) \ + DEFINE_OFPACT(CONTROLLER, ofpact_controller, ofpact) \ + DEFINE_OFPACT(ENQUEUE, ofpact_enqueue, ofpact) \ + DEFINE_OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact) \ + DEFINE_OFPACT(BUNDLE, ofpact_bundle, slaves) \ + \ + /* Header changes. */ \ + DEFINE_OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact) \ + DEFINE_OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact) \ + DEFINE_OFPACT(STRIP_VLAN, ofpact_null, ofpact) \ + DEFINE_OFPACT(SET_ETH_SRC, ofpact_mac, ofpact) \ + DEFINE_OFPACT(SET_ETH_DST, ofpact_mac, ofpact) \ + DEFINE_OFPACT(SET_IPV4_SRC, ofpact_ipv4, ofpact) \ + DEFINE_OFPACT(SET_IPV4_DST, ofpact_ipv4, ofpact) \ + DEFINE_OFPACT(SET_IPV4_DSCP, ofpact_dscp, ofpact) \ + DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port, ofpact) \ + DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact) \ + DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \ + DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \ + DEFINE_OFPACT(DEC_TTL, ofpact_null, ofpact) \ + \ + /* Metadata. */ \ + DEFINE_OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact) \ + DEFINE_OFPACT(SET_QUEUE, ofpact_queue, ofpact) \ + DEFINE_OFPACT(POP_QUEUE, ofpact_null, ofpact) \ + DEFINE_OFPACT(FIN_TIMEOUT, ofpact_fin_timeout, ofpact) \ + \ + /* Flow table interaction. */ \ + DEFINE_OFPACT(RESUBMIT, ofpact_resubmit, ofpact) \ + DEFINE_OFPACT(LEARN, ofpact_learn, specs) \ + \ + /* Arithmetic. */ \ + DEFINE_OFPACT(MULTIPATH, ofpact_multipath, ofpact) \ + DEFINE_OFPACT(AUTOPATH, ofpact_autopath, ofpact) \ + \ + /* Other. */ \ + DEFINE_OFPACT(NOTE, ofpact_note, data) \ + DEFINE_OFPACT(EXIT, ofpact_null, ofpact) + +/* enum ofpact_type, with a member OFPACT_ for each action. */ +enum OVS_PACKED_ENUM ofpact_type { +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM, + OFPACTS +#undef DEFINE_OFPACT +}; + +/* N_OFPACTS, the number of values of "enum ofpact_type". */ +enum { + N_OFPACTS = +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1 + OFPACTS +#undef DEFINE_OFPACT +}; + +/* Header for an action. + * + * Each action is a structure "struct ofpact_*" that begins with "struct + * ofpact", usually followed by other data that describes the action. Actions + * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. */ +struct ofpact { + enum ofpact_type type; /* OFPACT_*. */ + enum ofputil_action_code compat; /* Original type when added, if any. */ + uint16_t len; /* Length of the action, in bytes, including + * struct ofpact, excluding padding. */ +}; + +#ifdef __GNUC__ +/* Make sure that OVS_PACKED_ENUM really worked. */ +BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4); +#endif + +/* Alignment. */ +#define OFPACT_ALIGNTO 8 +#define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO) + +static inline struct ofpact * +ofpact_next(const struct ofpact *ofpact) +{ + return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len)); +} + +static inline struct ofpact * +ofpact_end(const struct ofpact *ofpacts, size_t ofpacts_len) +{ + return (void *) ((uint8_t *) ofpacts + ofpacts_len); +} + +/* Assigns POS to each ofpact, in turn, in the OFPACTS_LEN bytes of ofpacts + * starting at OFPACTS. */ +#define OFPACT_FOR_EACH(POS, OFPACTS, OFPACTS_LEN) \ + for ((POS) = (OFPACTS); (POS) < ofpact_end(OFPACTS, OFPACTS_LEN); \ + (POS) = ofpact_next(POS)) + +/* Action structure for each OFPACT_*. */ + +/* OFPACT_STRIP_VLAN, OFPACT_DEC_TTL, OFPACT_POP_QUEUE, OFPACT_EXIT. + * + * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT. + * + * Action structure for actions that do not have any extra data beyond the + * action type. */ +struct ofpact_null { + struct ofpact ofpact; +}; + +/* OFPACT_OUTPUT. + * + * Used for OFPAT10_OUTPUT. */ +struct ofpact_output { + struct ofpact ofpact; + uint16_t port; /* Output port. */ + uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER. */ +}; + +/* OFPACT_CONTROLLER. + * + * Used for NXAST_CONTROLLER. */ +struct ofpact_controller { + struct ofpact ofpact; + uint16_t max_len; /* Maximum length to send to controller. */ + uint16_t controller_id; /* Controller ID to send packet-in. */ + enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */ +}; + +/* OFPACT_ENQUEUE. + * + * Used for OFPAT10_ENQUEUE. */ +struct ofpact_enqueue { + struct ofpact ofpact; + uint16_t port; + uint32_t queue; +}; + +/* OFPACT_OUTPUT_REG. + * + * Used for NXAST_OUTPUT_REG. */ +struct ofpact_output_reg { + struct ofpact ofpact; + struct mf_subfield src; + uint16_t max_len; +}; + +/* OFPACT_BUNDLE. + * + * Used for NXAST_BUNDLE. */ +struct ofpact_bundle { + struct ofpact ofpact; + + /* Slave choice algorithm to apply to hash value. */ + enum nx_bd_algorithm algorithm; + + /* What fields to hash and how. */ + enum nx_hash_fields fields; + uint16_t basis; /* Universal hash parameter. */ + + struct mf_subfield dst; + + /* Slaves for output. */ + unsigned int n_slaves; + uint16_t slaves[]; +}; + +/* OFPACT_SET_VLAN_VID. + * + * Used for OFPAT10_SET_VLAN_VID. */ +struct ofpact_vlan_vid { + struct ofpact ofpact; + uint16_t vlan_vid; /* VLAN VID in low 12 bits, 0 in other bits. */ +}; + +/* OFPACT_SET_VLAN_PCP. + * + * Used for OFPAT10_SET_VLAN_PCP. */ +struct ofpact_vlan_pcp { + struct ofpact ofpact; + uint8_t vlan_pcp; /* VLAN PCP in low 3 bits, 0 in other bits. */ +}; + +/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST. + * + * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */ +struct ofpact_mac { + struct ofpact ofpact; + uint8_t mac[ETH_ADDR_LEN]; +}; + +/* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST. + * + * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */ +struct ofpact_ipv4 { + struct ofpact ofpact; + ovs_be32 ipv4; +}; + +/* OFPACT_SET_IPV4_DSCP. + * + * Used for OFPAT10_SET_NW_TOS. */ +struct ofpact_dscp { + struct ofpact ofpact; + uint8_t dscp; /* DSCP in high 6 bits, rest ignored. */ +}; + +/* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT. + * + * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */ +struct ofpact_l4_port { + struct ofpact ofpact; + uint16_t port; /* TCP or UDP port number. */ +}; + +/* OFPACT_REG_MOVE. + * + * Used for NXAST_REG_MOVE. */ +struct ofpact_reg_move { + struct ofpact ofpact; + struct mf_subfield src; + struct mf_subfield dst; +}; + +/* OFPACT_REG_LOAD. + * + * Used for NXAST_REG_LOAD. */ +struct ofpact_reg_load { + struct ofpact ofpact; + struct mf_subfield dst; + uint64_t value; +}; + +/* OFPACT_SET_TUNNEL. + * + * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */ +struct ofpact_tunnel { + struct ofpact ofpact; + uint64_t tun_id; +}; + +/* OFPACT_SET_QUEUE. + * + * Used for NXAST_SET_QUEUE. */ +struct ofpact_queue { + struct ofpact ofpact; + uint32_t queue_id; +}; + +/* OFPACT_FIN_TIMEOUT. + * + * Used for NXAST_FIN_TIMEOUT. */ +struct ofpact_fin_timeout { + struct ofpact ofpact; + uint16_t fin_idle_timeout; + uint16_t fin_hard_timeout; +}; + +/* OFPACT_RESUBMIT. + * + * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */ +struct ofpact_resubmit { + struct ofpact ofpact; + uint16_t in_port; + uint8_t table_id; +}; + +/* Part of struct ofpact_learn, below. */ +struct ofpact_learn_spec { + int n_bits; + + int src_type; + struct mf_subfield src; + union mf_subvalue src_imm; + + int dst_type; + struct mf_subfield dst; +}; + +/* OFPACT_LEARN. + * + * Used for NXAST_LEARN. */ +struct ofpact_learn { + struct ofpact ofpact; + + uint16_t idle_timeout; /* Idle time before discarding (seconds). */ + uint16_t hard_timeout; /* Max time before discarding (seconds). */ + uint16_t priority; /* Priority level of flow entry. */ + uint64_t cookie; /* Cookie for new flow. */ + uint16_t flags; /* Either 0 or OFPFF_SEND_FLOW_REM. */ + uint8_t table_id; /* Table to insert flow entry. */ + uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */ + uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */ + + unsigned int n_specs; + struct ofpact_learn_spec specs[]; +}; + +/* OFPACT_MULTIPATH. + * + * Used for NXAST_MULTIPATH. */ +struct ofpact_multipath { + struct ofpact ofpact; + + /* What fields to hash and how. */ + enum nx_hash_fields fields; + uint16_t basis; /* Universal hash parameter. */ + + /* Multipath link choice algorithm to apply to hash value. */ + enum nx_mp_algorithm algorithm; + uint16_t max_link; /* Number of output links, minus 1. */ + uint32_t arg; /* Algorithm-specific argument. */ + + /* Where to store the result. */ + struct mf_subfield dst; +}; + +/* OFPACT_AUTOPATH. + * + * Used for NXAST_AUTOPATH. */ +struct ofpact_autopath { + struct ofpact ofpact; + struct mf_subfield dst; + uint32_t port; +}; + +/* OFPACT_NOTE. + * + * Used for NXAST_NOTE. */ +struct ofpact_note { + struct ofpact ofpact; + size_t length; + uint8_t data[]; +}; + +/* Converting OpenFlow to ofpacts. */ +enum ofperr ofpacts_pull_openflow(struct ofpbuf *openflow, + unsigned int actions_len, + struct ofpbuf *ofpacts); +enum ofperr ofpacts_check(const struct ofpact[], size_t ofpacts_len, + const struct flow *, int max_ports); + +/* Converting ofpacts to OpenFlow. */ +void ofpacts_to_openflow(const struct ofpact[], size_t ofpacts_len, + struct ofpbuf *openflow); + +/* Working with ofpacts. */ +bool ofpacts_output_to_port(const struct ofpact[], size_t ofpacts_len, + uint16_t port); +bool ofpacts_equal(const struct ofpact a[], size_t a_len, + const struct ofpact b[], size_t b_len); + +/* Formatting ofpacts. + * + * (For parsing ofpacts, see ofp-parse.h.) */ +void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *); + +/* Internal use by the helpers below. */ +void ofpact_init(struct ofpact *, enum ofpact_type, size_t len); +void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len); + +/* For each OFPACT_ with a corresponding struct , this defines + * the following commonly useful functions: + * + * struct *ofpact_put_(struct ofpbuf *ofpacts); + * + * Appends a new 'ofpact', of length OFPACT__RAW_SIZE, to 'ofpacts', + * initializes it with ofpact_init_(), and returns it. Also sets + * 'ofpacts->l2' to the returned action. + * + * After using this function to add a variable-length action, add the + * elements of the flexible array (e.g. with ofpbuf_put()), then use + * ofpact_update_len() to update the length embedded into the action. + * (Keep in mind the need to refresh the structure from 'ofpacts->l2' after + * adding data to 'ofpacts'.) + * + * struct *ofpact_get_(const struct ofpact *ofpact); + * + * Returns 'ofpact' cast to "struct *". 'ofpact->type' must be + * OFPACT_. + * + * as well as the following more rarely useful definitions: + * + * void ofpact_init_(struct *ofpact); + * + * Initializes the parts of 'ofpact' that identify it as having type + * OFPACT_ and length OFPACT__RAW_SIZE and zeros the rest. + * + * _RAW_SIZE + * + * The size of the action structure. For a fixed-length action, this is + * sizeof(struct ). For a variable-length action, this is the + * offset to the variable-length part. + * + * _SIZE + * + * An integer constant, the value of OFPACT__RAW_SIZE rounded up to a + * multiple of OFPACT_ALIGNTO. + */ +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) \ + BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0); \ + \ + enum { OFPACT_##ENUM##_RAW_SIZE \ + = (offsetof(struct STRUCT, MEMBER) \ + ? offsetof(struct STRUCT, MEMBER) \ + : sizeof(struct STRUCT)) }; \ + \ + enum { OFPACT_##ENUM##_SIZE \ + = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) }; \ + \ + static inline struct STRUCT * \ + ofpact_get_##ENUM(const struct ofpact *ofpact) \ + { \ + assert(ofpact->type == OFPACT_##ENUM); \ + return (struct STRUCT *) ofpact; \ + } \ + \ + static inline struct STRUCT * \ + ofpact_put_##ENUM(struct ofpbuf *ofpacts) \ + { \ + return ofpact_put(ofpacts, OFPACT_##ENUM, \ + OFPACT_##ENUM##_RAW_SIZE); \ + } \ + \ + static inline void \ + ofpact_init_##ENUM(struct STRUCT *ofpact) \ + { \ + ofpact_init(&ofpact->ofpact, OFPACT_##ENUM, \ + OFPACT_##ENUM##_RAW_SIZE); \ + } +OFPACTS +#undef DEFINE_OFPACT + +/* Functions to use after adding ofpacts to a buffer. */ +void ofpact_update_len(struct ofpbuf *, struct ofpact *); +void ofpact_pad(struct ofpbuf *); + +#endif /* ofp-actions.h */ diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 1d331bb4..6236e500 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -28,9 +28,10 @@ #include "dynamic-string.h" #include "learn.h" #include "meta-flow.h" -#include "netdev.h" #include "multipath.h" +#include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-util.h" #include "ofpbuf.h" #include "openflow/openflow.h" @@ -122,107 +123,73 @@ str_to_ip(const char *str, ovs_be32 *ip) *ip = in_addr.s_addr; } -static struct ofp_action_output * -put_output_action(struct ofpbuf *b, uint16_t port) -{ - struct ofp_action_output *oao; - - oao = ofputil_put_OFPAT10_OUTPUT(b); - oao->port = htons(port); - return oao; -} - static void -parse_enqueue(struct ofpbuf *b, char *arg) +parse_enqueue(char *arg, struct ofpbuf *ofpacts) { char *sp = NULL; char *port = strtok_r(arg, ":q", &sp); char *queue = strtok_r(NULL, "", &sp); - struct ofp_action_enqueue *oae; + struct ofpact_enqueue *enqueue; if (port == NULL || queue == NULL) { ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\""); } - oae = ofputil_put_OFPAT10_ENQUEUE(b); - oae->port = htons(str_to_u32(port)); - oae->queue_id = htonl(str_to_u32(queue)); + enqueue = ofpact_put_ENQUEUE(ofpacts); + enqueue->port = str_to_u32(port); + enqueue->queue = str_to_u32(queue); } static void -parse_output(struct ofpbuf *b, char *arg) +parse_output(char *arg, struct ofpbuf *ofpacts) { if (strchr(arg, '[')) { - struct nx_action_output_reg *naor; - struct mf_subfield src; - - mf_parse_subfield(&src, arg); + struct ofpact_output_reg *output_reg; - naor = ofputil_put_NXAST_OUTPUT_REG(b); - naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits); - naor->src = htonl(src.field->nxm_header); - naor->max_len = htons(UINT16_MAX); + output_reg = ofpact_put_OUTPUT_REG(ofpacts); + mf_parse_subfield(&output_reg->src, arg); + output_reg->max_len = UINT16_MAX; } else { - put_output_action(b, str_to_u32(arg)); + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(ofpacts); + output->port = str_to_u32(arg); + output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0; } } static void -parse_resubmit(struct ofpbuf *b, char *arg) +parse_resubmit(char *arg, struct ofpbuf *ofpacts) { - struct nx_action_resubmit *nar; + struct ofpact_resubmit *resubmit; char *in_port_s, *table_s; - uint16_t in_port; - uint8_t table; + + resubmit = ofpact_put_RESUBMIT(ofpacts); in_port_s = strsep(&arg, ","); if (in_port_s && in_port_s[0]) { - if (!ofputil_port_from_string(in_port_s, &in_port)) { - in_port = str_to_u32(in_port_s); + if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) { + resubmit->in_port = str_to_u32(in_port_s); } } else { - in_port = OFPP_IN_PORT; + resubmit->in_port = OFPP_IN_PORT; } table_s = strsep(&arg, ","); - table = table_s && table_s[0] ? str_to_u32(table_s) : 255; + resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255; - if (in_port == OFPP_IN_PORT && table == 255) { + if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) { ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified " " on resubmit"); } - - if (in_port != OFPP_IN_PORT && table == 255) { - nar = ofputil_put_NXAST_RESUBMIT(b); - } else { - nar = ofputil_put_NXAST_RESUBMIT_TABLE(b); - nar->table = table; - } - nar->in_port = htons(in_port); -} - -static void -parse_set_tunnel(struct ofpbuf *b, const char *arg) -{ - uint64_t tun_id = str_to_u64(arg); - if (tun_id > UINT32_MAX) { - ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id); - } else { - ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id); - } } static void -parse_note(struct ofpbuf *b, const char *arg) +parse_note(const char *arg, struct ofpbuf *ofpacts) { - size_t start_ofs = b->size; - struct nx_action_note *nan; - int remainder; - size_t len; + struct ofpact_note *note; - nan = ofputil_put_NXAST_NOTE(b); - - b->size -= sizeof nan->note; + note = ofpact_put_NOTE(ofpacts); while (*arg != '\0') { uint8_t byte; bool ok; @@ -238,32 +205,27 @@ parse_note(struct ofpbuf *b, const char *arg) if (!ok) { ovs_fatal(0, "bad hex digit in `note' argument"); } - ofpbuf_put(b, &byte, 1); + ofpbuf_put(ofpacts, &byte, 1); - arg += 2; - } + note = ofpacts->l2; + note->length++; - len = b->size - start_ofs; - remainder = len % OFP_ACTION_ALIGN; - if (remainder) { - ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); + arg += 2; } - nan = (struct nx_action_note *)((char *)b->data + start_ofs); - nan->len = htons(b->size - start_ofs); + ofpact_update_len(ofpacts, ¬e->ofpact); } static void parse_fin_timeout(struct ofpbuf *b, char *arg) { - struct nx_action_fin_timeout *naft; + struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b); char *key, *value; - naft = ofputil_put_NXAST_FIN_TIMEOUT(b); while (ofputil_parse_key_value(&arg, &key, &value)) { if (!strcmp(key, "idle_timeout")) { - naft->fin_idle_timeout = htons(str_to_u16(value, key)); + oft->fin_idle_timeout = str_to_u16(value, key); } else if (!strcmp(key, "hard_timeout")) { - naft->fin_hard_timeout = htons(str_to_u16(value, key)); + oft->fin_hard_timeout = str_to_u16(value, key); } else { ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key); } @@ -301,121 +263,142 @@ parse_controller(struct ofpbuf *b, char *arg) } if (reason == OFPR_ACTION && controller_id == 0) { - put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len); + struct ofpact_output *output; + + output = ofpact_put_OUTPUT(b); + output->port = OFPP_CONTROLLER; + output->max_len = max_len; } else { - struct nx_action_controller *nac; + struct ofpact_controller *controller; - nac = ofputil_put_NXAST_CONTROLLER(b); - nac->max_len = htons(max_len); - nac->reason = reason; - nac->controller_id = htons(controller_id); + controller = ofpact_put_CONTROLLER(b); + controller->max_len = max_len; + controller->reason = reason; + controller->controller_id = controller_id; } } static void parse_named_action(enum ofputil_action_code code, const struct flow *flow, - struct ofpbuf *b, char *arg) + char *arg, struct ofpbuf *ofpacts) { - struct ofp_action_dl_addr *oada; - struct ofp_action_vlan_pcp *oavp; - struct ofp_action_vlan_vid *oavv; - struct ofp_action_nw_addr *oana; - struct ofp_action_tp_port *oata; + struct ofpact_tunnel *tunnel; + uint16_t vid; + ovs_be32 ip; + uint8_t pcp; + uint8_t tos; switch (code) { case OFPUTIL_ACTION_INVALID: NOT_REACHED(); case OFPUTIL_OFPAT10_OUTPUT: - parse_output(b, arg); + parse_output(arg, ofpacts); break; case OFPUTIL_OFPAT10_SET_VLAN_VID: - oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b); - oavv->vlan_vid = htons(str_to_u32(arg)); + vid = str_to_u32(arg); + if (vid & ~VLAN_VID_MASK) { + ovs_fatal(0, "%s: not a valid VLAN VID", arg); + } + ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid; break; case OFPUTIL_OFPAT10_SET_VLAN_PCP: - oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b); - oavp->vlan_pcp = str_to_u32(arg); + pcp = str_to_u32(arg); + if (pcp & ~7) { + ovs_fatal(0, "%s: not a valid VLAN PCP", arg); + } + ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp; break; case OFPUTIL_OFPAT10_STRIP_VLAN: - ofputil_put_OFPAT10_STRIP_VLAN(b); + ofpact_put_STRIP_VLAN(ofpacts); break; case OFPUTIL_OFPAT10_SET_DL_SRC: + str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac); + break; + case OFPUTIL_OFPAT10_SET_DL_DST: - oada = ofputil_put_action(code, b); - str_to_mac(arg, oada->dl_addr); + str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac); break; case OFPUTIL_OFPAT10_SET_NW_SRC: + str_to_ip(arg, &ip); + ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip; + break; + case OFPUTIL_OFPAT10_SET_NW_DST: - oana = ofputil_put_action(code, b); - str_to_ip(arg, &oana->nw_addr); + str_to_ip(arg, &ip); + ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip; break; case OFPUTIL_OFPAT10_SET_NW_TOS: - ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg); + tos = str_to_u32(arg); + if (tos & ~IP_DSCP_MASK) { + ovs_fatal(0, "%s: not a valid TOS", arg); + } + ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos; break; case OFPUTIL_OFPAT10_SET_TP_SRC: + ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg); + break; + case OFPUTIL_OFPAT10_SET_TP_DST: - oata = ofputil_put_action(code, b); - oata->tp_port = htons(str_to_u32(arg)); + ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg); break; case OFPUTIL_OFPAT10_ENQUEUE: - parse_enqueue(b, arg); + parse_enqueue(arg, ofpacts); break; case OFPUTIL_NXAST_RESUBMIT: - parse_resubmit(b, arg); + parse_resubmit(arg, ofpacts); break; case OFPUTIL_NXAST_SET_TUNNEL: - parse_set_tunnel(b, arg); + case OFPUTIL_NXAST_SET_TUNNEL64: + tunnel = ofpact_put_SET_TUNNEL(ofpacts); + tunnel->ofpact.compat = code; + tunnel->tun_id = str_to_u64(arg); break; case OFPUTIL_NXAST_SET_QUEUE: - ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg)); + ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg); break; case OFPUTIL_NXAST_POP_QUEUE: - ofputil_put_NXAST_POP_QUEUE(b); + ofpact_put_POP_QUEUE(ofpacts); break; case OFPUTIL_NXAST_REG_MOVE: - nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg); + nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg); break; case OFPUTIL_NXAST_REG_LOAD: - nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg); + nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg); break; case OFPUTIL_NXAST_NOTE: - parse_note(b, arg); - break; - - case OFPUTIL_NXAST_SET_TUNNEL64: - ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg)); + parse_note(arg, ofpacts); break; case OFPUTIL_NXAST_MULTIPATH: - multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg); + multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg); break; case OFPUTIL_NXAST_AUTOPATH: - autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg); + autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg); break; case OFPUTIL_NXAST_BUNDLE: - bundle_parse(b, arg); + bundle_parse(arg, ofpacts); break; case OFPUTIL_NXAST_BUNDLE_LOAD: - bundle_parse_load(b, arg); + bundle_parse_load(arg, ofpacts); break; case OFPUTIL_NXAST_RESUBMIT_TABLE: @@ -423,29 +406,29 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow, NOT_REACHED(); case OFPUTIL_NXAST_LEARN: - learn_parse(b, arg, flow); + learn_parse(arg, flow, ofpacts); break; case OFPUTIL_NXAST_EXIT: - ofputil_put_NXAST_EXIT(b); + ofpact_put_EXIT(ofpacts); break; case OFPUTIL_NXAST_DEC_TTL: - ofputil_put_NXAST_DEC_TTL(b); + ofpact_put_DEC_TTL(ofpacts); break; case OFPUTIL_NXAST_FIN_TIMEOUT: - parse_fin_timeout(b, arg); + parse_fin_timeout(ofpacts, arg); break; case OFPUTIL_NXAST_CONTROLLER: - parse_controller(b, arg); + parse_controller(ofpacts, arg); break; } } static void -str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) +str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts) { char *pos, *act, *arg; int n_actions; @@ -458,10 +441,8 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) code = ofputil_action_code_from_name(act); if (code >= 0) { - parse_named_action(code, flow, b, arg); + parse_named_action(code, flow, arg, ofpacts); } else if (!strcasecmp(act, "drop")) { - /* A drop action in OpenFlow occurs by just not setting - * an action. */ if (n_actions) { ovs_fatal(0, "Drop actions must not be preceded by other " "actions"); @@ -471,12 +452,13 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b) } break; } else if (ofputil_port_from_string(act, &port)) { - put_output_action(b, port); + ofpact_put_OUTPUT(ofpacts)->port = port; } else { ovs_fatal(0, "Unknown action: %s", act); } n_actions++; } + ofpact_pad(ofpacts); } struct protocol { @@ -694,15 +676,15 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, fm->new_cookie = htonll(0); } if (fields & F_ACTIONS) { - struct ofpbuf actions; + struct ofpbuf ofpacts; - ofpbuf_init(&actions, sizeof(union ofp_action)); - str_to_action(&fm->cr.flow, act_str, &actions); - fm->actions = ofpbuf_steal_data(&actions); - fm->n_actions = actions.size / sizeof(union ofp_action); + ofpbuf_init(&ofpacts, 32); + str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts); + fm->ofpacts_len = ofpacts.size; + fm->ofpacts = ofpbuf_steal_data(&ofpacts); } else { - fm->actions = NULL; - fm->n_actions = 0; + fm->ofpacts_len = 0; + fm->ofpacts = NULL; } free(string); @@ -714,10 +696,10 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_, * Prints an error on stderr and aborts the program if 's' syntax is * invalid. */ void -parse_ofp_actions(const char *s_, struct ofpbuf *actions) +parse_ofpacts(const char *s_, struct ofpbuf *ofpacts) { char *s = xstrdup(s_); - str_to_action(NULL, s, actions); + str_to_ofpacts(NULL, s, ofpacts); free(s); } diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 3e5e62a6..e9303885 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -40,7 +40,7 @@ void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *, bool aggregate, const char *string); -void parse_ofp_actions(const char *, struct ofpbuf *actions); +void parse_ofpacts(const char *, struct ofpbuf *ofpacts); char *parse_ofp_exact_flow(struct flow *, const char *); diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 335ebed9..c7c8a190 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -36,6 +36,7 @@ #include "meta-flow.h" #include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -153,279 +154,18 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh, } } -static void -print_note(struct ds *string, const struct nx_action_note *nan) -{ - size_t len; - size_t i; - - ds_put_cstr(string, "note:"); - len = ntohs(nan->len) - offsetof(struct nx_action_note, note); - for (i = 0; i < len; i++) { - if (i) { - ds_put_char(string, '.'); - } - ds_put_format(string, "%02"PRIx8, nan->note[i]); - } -} - -static void -ofp_print_action(struct ds *s, const union ofp_action *a, - enum ofputil_action_code code) -{ - const struct ofp_action_enqueue *oae; - const struct ofp_action_dl_addr *oada; - const struct nx_action_set_tunnel64 *nast64; - const struct nx_action_set_tunnel *nast; - const struct nx_action_set_queue *nasq; - const struct nx_action_resubmit *nar; - const struct nx_action_reg_move *move; - const struct nx_action_reg_load *load; - const struct nx_action_multipath *nam; - const struct nx_action_autopath *naa; - const struct nx_action_output_reg *naor; - const struct nx_action_fin_timeout *naft; - const struct nx_action_controller *nac; - struct mf_subfield subfield; - uint16_t port; - - switch (code) { - case OFPUTIL_ACTION_INVALID: - NOT_REACHED(); - - case OFPUTIL_OFPAT10_OUTPUT: - port = ntohs(a->output.port); - if (port < OFPP_MAX) { - ds_put_format(s, "output:%"PRIu16, port); - } else { - ofputil_format_port(port, s); - if (port == OFPP_CONTROLLER) { - if (a->output.max_len != htons(0)) { - ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len)); - } else { - ds_put_cstr(s, ":all"); - } - } - } - break; - - case OFPUTIL_OFPAT10_ENQUEUE: - oae = (const struct ofp_action_enqueue *) a; - ds_put_format(s, "enqueue:"); - ofputil_format_port(ntohs(oae->port), s); - ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id)); - break; - - case OFPUTIL_OFPAT10_SET_VLAN_VID: - ds_put_format(s, "mod_vlan_vid:%"PRIu16, - ntohs(a->vlan_vid.vlan_vid)); - break; - - case OFPUTIL_OFPAT10_SET_VLAN_PCP: - ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp); - break; - - case OFPUTIL_OFPAT10_STRIP_VLAN: - ds_put_cstr(s, "strip_vlan"); - break; - - case OFPUTIL_OFPAT10_SET_DL_SRC: - oada = (const struct ofp_action_dl_addr *) a; - ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, - ETH_ADDR_ARGS(oada->dl_addr)); - break; - - case OFPUTIL_OFPAT10_SET_DL_DST: - oada = (const struct ofp_action_dl_addr *) a; - ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, - ETH_ADDR_ARGS(oada->dl_addr)); - break; - - case OFPUTIL_OFPAT10_SET_NW_SRC: - ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr)); - break; - - case OFPUTIL_OFPAT10_SET_NW_DST: - ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr)); - break; - - case OFPUTIL_OFPAT10_SET_NW_TOS: - ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos); - break; - - case OFPUTIL_OFPAT10_SET_TP_SRC: - ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port)); - break; - - case OFPUTIL_OFPAT10_SET_TP_DST: - ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port)); - break; - - case OFPUTIL_NXAST_RESUBMIT: - nar = (struct nx_action_resubmit *)a; - ds_put_format(s, "resubmit:"); - ofputil_format_port(ntohs(nar->in_port), s); - break; - - case OFPUTIL_NXAST_RESUBMIT_TABLE: - nar = (struct nx_action_resubmit *)a; - ds_put_format(s, "resubmit("); - if (nar->in_port != htons(OFPP_IN_PORT)) { - ofputil_format_port(ntohs(nar->in_port), s); - } - ds_put_char(s, ','); - if (nar->table != 255) { - ds_put_format(s, "%"PRIu8, nar->table); - } - ds_put_char(s, ')'); - break; - - case OFPUTIL_NXAST_SET_TUNNEL: - nast = (struct nx_action_set_tunnel *)a; - ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id)); - break; - - case OFPUTIL_NXAST_SET_QUEUE: - nasq = (struct nx_action_set_queue *)a; - ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id)); - break; - - case OFPUTIL_NXAST_POP_QUEUE: - ds_put_cstr(s, "pop_queue"); - break; - - case OFPUTIL_NXAST_NOTE: - print_note(s, (const struct nx_action_note *) a); - break; - - case OFPUTIL_NXAST_REG_MOVE: - move = (const struct nx_action_reg_move *) a; - nxm_format_reg_move(move, s); - break; - - case OFPUTIL_NXAST_REG_LOAD: - load = (const struct nx_action_reg_load *) a; - nxm_format_reg_load(load, s); - break; - - case OFPUTIL_NXAST_SET_TUNNEL64: - nast64 = (const struct nx_action_set_tunnel64 *) a; - ds_put_format(s, "set_tunnel64:%#"PRIx64, - ntohll(nast64->tun_id)); - break; - - case OFPUTIL_NXAST_MULTIPATH: - nam = (const struct nx_action_multipath *) a; - multipath_format(nam, s); - break; - - case OFPUTIL_NXAST_AUTOPATH: - naa = (const struct nx_action_autopath *)a; - ds_put_format(s, "autopath(%u,", ntohl(naa->id)); - nxm_decode(&subfield, naa->dst, naa->ofs_nbits); - mf_format_subfield(&subfield, s); - ds_put_char(s, ')'); - break; - - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - bundle_format((const struct nx_action_bundle *) a, s); - break; - - case OFPUTIL_NXAST_OUTPUT_REG: - naor = (const struct nx_action_output_reg *) a; - ds_put_cstr(s, "output:"); - nxm_decode(&subfield, naor->src, naor->ofs_nbits); - mf_format_subfield(&subfield, s); - break; - - case OFPUTIL_NXAST_LEARN: - learn_format((const struct nx_action_learn *) a, s); - break; - - case OFPUTIL_NXAST_DEC_TTL: - ds_put_cstr(s, "dec_ttl"); - break; - - case OFPUTIL_NXAST_EXIT: - ds_put_cstr(s, "exit"); - break; - - case OFPUTIL_NXAST_FIN_TIMEOUT: - naft = (const struct nx_action_fin_timeout *) a; - ds_put_cstr(s, "fin_timeout("); - if (naft->fin_idle_timeout) { - ds_put_format(s, "idle_timeout=%"PRIu16",", - ntohs(naft->fin_idle_timeout)); - } - if (naft->fin_hard_timeout) { - ds_put_format(s, "hard_timeout=%"PRIu16",", - ntohs(naft->fin_hard_timeout)); - } - ds_chomp(s, ','); - ds_put_char(s, ')'); - break; - - case OFPUTIL_NXAST_CONTROLLER: - nac = (const struct nx_action_controller *) a; - ds_put_cstr(s, "controller("); - if (nac->reason != OFPR_ACTION) { - ds_put_format(s, "reason=%s,", - ofputil_packet_in_reason_to_string(nac->reason)); - } - if (nac->max_len != htons(UINT16_MAX)) { - ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len)); - } - if (nac->controller_id != htons(0)) { - ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id)); - } - ds_chomp(s, ','); - ds_put_char(s, ')'); - break; - - default: - break; - } -} - -void -ofp_print_actions(struct ds *string, const union ofp_action *actions, - size_t n_actions) -{ - const union ofp_action *a; - size_t left; - - ds_put_cstr(string, "actions="); - if (!n_actions) { - ds_put_cstr(string, "drop"); - } - - OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { - int code = ofputil_decode_action(a); - if (code >= 0) { - if (a != actions) { - ds_put_cstr(string, ","); - } - ofp_print_action(string, a, code); - } else { - ofp_print_error(string, -code); - } - } - if (left > 0) { - ds_put_format(string, " ***%zu leftover bytes following actions", - left * sizeof *a); - } -} - static void ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo, int verbosity) { struct ofputil_packet_out po; + struct ofpbuf ofpacts; enum ofperr error; - error = ofputil_decode_packet_out(&po, opo); + ofpbuf_init(&ofpacts, 64); + error = ofputil_decode_packet_out(&po, opo, &ofpacts); if (error) { + ofpbuf_uninit(&ofpacts); ofp_print_error(string, error); return; } @@ -434,7 +174,7 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo, ofputil_format_port(po.in_port, string); ds_put_char(string, ' '); - ofp_print_actions(string, po.actions, po.n_actions); + ofpacts_format(po.ofpacts, po.ofpacts_len, string); if (po.buffer_id == UINT32_MAX) { ds_put_format(string, " data_len=%zu", po.packet_len); @@ -448,6 +188,8 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo, ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id); } ds_put_char(string, '\n'); + + ofpbuf_uninit(&ofpacts); } /* qsort comparison function. */ @@ -928,11 +670,14 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, enum ofputil_msg_code code, int verbosity) { struct ofputil_flow_mod fm; + struct ofpbuf ofpacts; bool need_priority; enum ofperr error; - error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID); + ofpbuf_init(&ofpacts, 64); + error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID, &ofpacts); if (error) { + ofpbuf_uninit(&ofpacts); ofp_print_error(s, error); return; } @@ -1027,7 +772,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, } } - ofp_print_actions(s, fm.actions, fm.n_actions); + ofpacts_format(fm.ofpacts, fm.ofpacts_len, s); + ofpbuf_uninit(&ofpacts); } static void @@ -1229,14 +975,16 @@ ofp_print_flow_stats_request(struct ds *string, static void ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh) { + struct ofpbuf ofpacts; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpbuf_init(&ofpacts, 64); for (;;) { struct ofputil_flow_stats fs; int retval; - retval = ofputil_decode_flow_stats_reply(&fs, &b, true); + retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts); if (retval) { if (retval != EOF) { ds_put_cstr(string, " ***parse error***"); @@ -1269,8 +1017,9 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh) if (string->string[string->length - 1] != ' ') { ds_put_char(string, ' '); } - ofp_print_actions(string, fs.actions, fs.n_actions); - } + ofpacts_format(fs.ofpacts, fs.ofpacts_len, string); + } + ofpbuf_uninit(&ofpacts); } static void diff --git a/lib/ofp-print.h b/lib/ofp-print.h index ae868a4b..49bcfcde 100644 --- a/lib/ofp-print.h +++ b/lib/ofp-print.h @@ -25,7 +25,6 @@ struct ofp_flow_mod; struct ofp10_match; struct ds; -union ofp_action; #ifdef __cplusplus extern "C" { @@ -34,7 +33,6 @@ extern "C" { void ofp_print(FILE *, const void *, size_t, int verbosity); void ofp_print_packet(FILE *stream, const void *data, size_t len); -void ofp_print_actions(struct ds *, const union ofp_action *, size_t); void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity); char *ofp_to_string(const void *, size_t, int verbosity); diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 3329ed7f..4864815c 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -32,6 +32,7 @@ #include "multipath.h" #include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -1644,11 +1645,17 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id) * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. * - * Does not validate the flow_mod actions. */ + * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions. + * The caller must initialize 'ofpacts' and retains ownership of it. + * 'fm->ofpacts' will point into the 'ofpacts' buffer. + * + * Does not validate the flow_mod actions. The caller should do that, with + * ofpacts_check(). */ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, const struct ofp_header *oh, - enum ofputil_protocol protocol) + enum ofputil_protocol protocol, + struct ofpbuf *ofpacts) { const struct ofputil_msg_type *type; uint16_t command; @@ -1663,12 +1670,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, uint16_t priority; enum ofperr error; - /* Dissect the message. */ + /* Get the ofp_flow_mod. */ ofm = ofpbuf_pull(&b, sizeof *ofm); - error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); - if (error) { - return error; - } /* Set priority based on original wildcards. Normally we'd allow * ofputil_cls_rule_from_match() to do this for us, but @@ -1683,6 +1686,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr); ofputil_normalize_rule(&fm->cr); + /* Now get the actions. */ + error = ofpacts_pull_openflow(&b, b.size, ofpacts); + if (error) { + return error; + } + /* Translate the message. */ command = ntohs(ofm->command); fm->cookie = htonll(0); @@ -1705,7 +1714,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, if (error) { return error; } - error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); + error = ofpacts_pull_openflow(&b, b.size, ofpacts); if (error) { return error; } @@ -1727,6 +1736,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, NOT_REACHED(); } + fm->ofpacts = ofpacts->data; + fm->ofpacts_len = ofpacts->size; if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; @@ -1744,7 +1755,6 @@ struct ofpbuf * ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol) { - size_t actions_len = fm->n_actions * sizeof *fm->actions; struct ofp_flow_mod *ofm; struct nx_flow_mod *nfm; struct ofpbuf *msg; @@ -1758,7 +1768,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, switch (protocol) { case OFPUTIL_P_OF10: case OFPUTIL_P_OF10_TID: - msg = ofpbuf_new(sizeof *ofm + actions_len); + msg = ofpbuf_new(sizeof *ofm + fm->ofpacts_len); ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match); ofm->cookie = fm->new_cookie; @@ -1773,7 +1783,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, case OFPUTIL_P_NXM: case OFPUTIL_P_NXM_TID: - msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len); + msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + fm->ofpacts_len); put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg); nfm = msg->data; nfm->command = htons(command); @@ -1794,7 +1804,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, NOT_REACHED(); } - ofpbuf_put(msg, fm->actions, actions_len); + if (fm->ofpacts) { + ofpacts_to_openflow(fm->ofpacts, fm->ofpacts_len, msg); + } update_openflow_length(msg); return msg; } @@ -1988,12 +2000,17 @@ ofputil_flow_stats_request_usable_protocols( * 'flow_age_extension' as true so that the contents of 'msg' determine the * 'idle_age' and 'hard_age' members in 'fs'. * + * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats + * reply's actions. The caller must initialize 'ofpacts' and retains ownership + * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer. + * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, struct ofpbuf *msg, - bool flow_age_extension) + bool flow_age_extension, + struct ofpbuf *ofpacts) { const struct ofputil_msg_type *type; int code; @@ -2031,8 +2048,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return EINVAL; } - if (ofputil_pull_actions(msg, length - sizeof *ofs, - &fs->actions, &fs->n_actions)) { + if (ofpacts_pull_openflow(msg, length - sizeof *ofs, ofpacts)) { return EINVAL; } @@ -2050,7 +2066,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count)); } else if (code == OFPUTIL_NXST_FLOW_REPLY) { const struct nx_flow_stats *nfs; - size_t match_len, length; + size_t match_len, actions_len, length; nfs = ofpbuf_try_pull(msg, sizeof *nfs); if (!nfs) { @@ -2071,9 +2087,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return EINVAL; } - if (ofputil_pull_actions(msg, - length - sizeof *nfs - ROUND_UP(match_len, 8), - &fs->actions, &fs->n_actions)) { + actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8); + if (ofpacts_pull_openflow(msg, actions_len, ofpacts)) { return EINVAL; } @@ -2099,6 +2114,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, NOT_REACHED(); } + fs->ofpacts = ofpacts->data; + fs->ofpacts_len = ofpacts->size; + return 0; } @@ -2119,16 +2137,14 @@ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct list *replies) { - size_t act_len = fs->n_actions * sizeof *fs->actions; - const struct ofp_stats_msg *osm; + struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); + const struct ofp_stats_msg *osm = reply->data; + size_t start_ofs = reply->size; - osm = ofpbuf_from_list(list_back(replies))->data; if (osm->type == htons(OFPST_FLOW)) { - size_t len = offsetof(struct ofp_flow_stats, actions) + act_len; struct ofp_flow_stats *ofs; - ofs = ofputil_append_stats_reply(len, replies); - ofs->length = htons(len); + ofs = ofpbuf_put_uninit(reply, sizeof *ofs); ofs->table_id = fs->table_id; ofs->pad = 0; ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match); @@ -2143,17 +2159,14 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, htonll(unknown_to_zero(fs->packet_count))); put_32aligned_be64(&ofs->byte_count, htonll(unknown_to_zero(fs->byte_count))); - memcpy(ofs->actions, fs->actions, act_len); + ofpacts_to_openflow(fs->ofpacts, fs->ofpacts_len, reply); + + ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); + ofs->length = htons(reply->size - start_ofs); } else if (osm->type == htons(OFPST_VENDOR)) { struct nx_flow_stats *nfs; - struct ofpbuf *msg; - size_t start_len; - - msg = ofputil_reserve_stats_reply( - sizeof *nfs + NXM_MAX_LEN + act_len, replies); - start_len = msg->size; - nfs = ofpbuf_put_uninit(msg, sizeof *nfs); + nfs = ofpbuf_put_uninit(reply, sizeof *nfs); nfs->table_id = fs->table_id; nfs->pad = 0; nfs->duration_sec = htonl(fs->duration_sec); @@ -2167,15 +2180,19 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->hard_age = htons(fs->hard_age < 0 ? 0 : fs->hard_age < UINT16_MAX ? fs->hard_age + 1 : UINT16_MAX); - nfs->match_len = htons(nx_put_match(msg, false, &fs->rule, 0, 0)); + nfs->match_len = htons(nx_put_match(reply, false, &fs->rule, 0, 0)); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); - ofpbuf_put(msg, fs->actions, act_len); - nfs->length = htons(msg->size - start_len); + ofpacts_to_openflow(fs->ofpacts, fs->ofpacts_len, reply); + + nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs); + nfs->length = htons(reply->size - start_ofs); } else { NOT_REACHED(); } + + ofputil_postappend_stats_reply(start_ofs, replies); } /* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or @@ -2501,9 +2518,18 @@ ofputil_packet_in_reason_from_string(const char *s, return false; } +/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in + * 'po'. + * + * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out + * message's actions. The caller must initialize 'ofpacts' and retains + * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer. + * + * Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *po, - const struct ofp_packet_out *opo) + const struct ofp_packet_out *opo, + struct ofpbuf *ofpacts) { enum ofperr error; struct ofpbuf b; @@ -2520,11 +2546,12 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, ofpbuf_use_const(&b, opo, ntohs(opo->header.length)); ofpbuf_pull(&b, sizeof *opo); - error = ofputil_pull_actions(&b, ntohs(opo->actions_len), - &po->actions, &po->n_actions); + error = ofpacts_pull_openflow(&b, ntohs(opo->actions_len), ofpacts); if (error) { return error; } + po->ofpacts = ofpacts->data; + po->ofpacts_len = ofpacts->size; if (po->buffer_id == UINT32_MAX) { po->packet = b.data; @@ -3070,25 +3097,27 @@ struct ofpbuf * ofputil_encode_packet_out(const struct ofputil_packet_out *po) { struct ofp_packet_out *opo; - size_t actions_len; struct ofpbuf *msg; size_t size; - actions_len = po->n_actions * sizeof *po->actions; - size = sizeof *opo + actions_len; + size = sizeof *opo + po->ofpacts_len; if (po->buffer_id == UINT32_MAX) { size += po->packet_len; } msg = ofpbuf_new(size); - opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg); + put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg); + ofpacts_to_openflow(po->ofpacts, po->ofpacts_len, msg); + + opo = msg->data; opo->buffer_id = htonl(po->buffer_id); opo->in_port = htons(po->in_port); - opo->actions_len = htons(actions_len); - ofpbuf_put(msg, po->actions, actions_len); + opo->actions_len = htons(msg->size - sizeof *opo); + if (po->buffer_id == UINT32_MAX) { ofpbuf_put(msg, po->packet, po->packet_len); } + update_openflow_length(msg); return msg; @@ -3362,6 +3391,20 @@ ofputil_append_stats_reply(size_t len, struct list *replies) return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len); } +void +ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + + assert(start_ofs <= UINT16_MAX); + if (msg->size > UINT16_MAX) { + size_t len = msg->size - start_ofs; + memcpy(ofputil_append_stats_reply(len, replies), + (const uint8_t *) msg->data + start_ofs, len); + msg->size = start_ofs; + } +} + /* Returns the first byte past the ofp_stats_msg header in 'oh'. */ const void * ofputil_stats_body(const struct ofp_header *oh) @@ -3394,58 +3437,6 @@ ofputil_nxstats_body_len(const struct ofp_header *oh) return ntohs(oh->length) - sizeof(struct nicira_stats_msg); } -struct ofpbuf * -make_flow_mod(uint16_t command, const struct cls_rule *rule, - size_t actions_len) -{ - struct ofp_flow_mod *ofm; - size_t size = sizeof *ofm + actions_len; - struct ofpbuf *out = ofpbuf_new(size); - ofm = ofpbuf_put_zeros(out, sizeof *ofm); - ofm->header.version = OFP10_VERSION; - ofm->header.type = OFPT_FLOW_MOD; - ofm->header.length = htons(size); - ofm->cookie = 0; - ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); - ofputil_cls_rule_to_ofp10_match(rule, &ofm->match); - ofm->command = htons(command); - return out; -} - -struct ofpbuf * -make_add_flow(const struct cls_rule *rule, uint32_t buffer_id, - uint16_t idle_timeout, size_t actions_len) -{ - struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len); - struct ofp_flow_mod *ofm = out->data; - ofm->idle_timeout = htons(idle_timeout); - ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); - ofm->buffer_id = htonl(buffer_id); - return out; -} - -struct ofpbuf * -make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason, - const struct ofpbuf *payload, int max_send_len) -{ - struct ofp_packet_in *opi; - struct ofpbuf *buf; - int send_len; - - send_len = MIN(max_send_len, payload->size); - buf = ofpbuf_new(sizeof *opi + send_len); - opi = put_openflow_xid(offsetof(struct ofp_packet_in, data), - OFPT_PACKET_IN, 0, buf); - opi->buffer_id = htonl(buffer_id); - opi->total_len = htons(payload->size); - opi->in_port = htons(in_port); - opi->reason = reason; - ofpbuf_put(buf, payload->data, send_len); - update_openflow_length(buf); - - return buf; -} - /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */ struct ofpbuf * make_echo_request(void) @@ -3661,280 +3652,6 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b) return b->size / ofputil_get_phy_port_size(ofp_version); } -static enum ofperr -check_resubmit_table(const struct nx_action_resubmit *nar) -{ - if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - return 0; -} - -static enum ofperr -check_output_reg(const struct nx_action_output_reg *naor, - const struct flow *flow) -{ - struct mf_subfield src; - size_t i; - - for (i = 0; i < sizeof naor->zero; i++) { - if (naor->zero[i]) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - } - - nxm_decode(&src, naor->src, naor->ofs_nbits); - return mf_check_src(&src, flow); -} - -enum ofperr -validate_actions(const union ofp_action *actions, size_t n_actions, - const struct flow *flow, int max_ports) -{ - const union ofp_action *a; - size_t left; - - OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { - enum ofperr error; - uint16_t port; - int code; - - code = ofputil_decode_action(a); - if (code < 0) { - error = -code; - VLOG_WARN_RL(&bad_ofmsg_rl, - "action decoding error at offset %td (%s)", - (a - actions) * sizeof *a, ofperr_get_name(error)); - - return error; - } - - error = 0; - switch ((enum ofputil_action_code) code) { - case OFPUTIL_ACTION_INVALID: - NOT_REACHED(); - - case OFPUTIL_OFPAT10_OUTPUT: - error = ofputil_check_output_port(ntohs(a->output.port), - max_ports); - break; - - case OFPUTIL_OFPAT10_SET_VLAN_VID: - if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - break; - - case OFPUTIL_OFPAT10_SET_VLAN_PCP: - if (a->vlan_pcp.vlan_pcp & ~7) { - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - break; - - case OFPUTIL_OFPAT10_ENQUEUE: - port = ntohs(((const struct ofp_action_enqueue *) a)->port); - if (port >= max_ports && port != OFPP_IN_PORT - && port != OFPP_LOCAL) { - error = OFPERR_OFPBAC_BAD_OUT_PORT; - } - break; - - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_check_reg_move((const struct nx_action_reg_move *) a, - flow); - break; - - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_check_reg_load((const struct nx_action_reg_load *) a, - flow); - break; - - case OFPUTIL_NXAST_MULTIPATH: - error = multipath_check((const struct nx_action_multipath *) a, - flow); - break; - - case OFPUTIL_NXAST_AUTOPATH: - error = autopath_check((const struct nx_action_autopath *) a, - flow); - break; - - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_check((const struct nx_action_bundle *) a, - max_ports, flow); - break; - - case OFPUTIL_NXAST_OUTPUT_REG: - error = check_output_reg((const struct nx_action_output_reg *) a, - flow); - break; - - case OFPUTIL_NXAST_RESUBMIT_TABLE: - error = check_resubmit_table( - (const struct nx_action_resubmit *) a); - break; - - case OFPUTIL_NXAST_LEARN: - error = learn_check((const struct nx_action_learn *) a, flow); - break; - - case OFPUTIL_NXAST_CONTROLLER: - if (((const struct nx_action_controller *) a)->zero) { - error = OFPERR_NXBAC_MUST_BE_ZERO; - } - break; - - case OFPUTIL_OFPAT10_STRIP_VLAN: - case OFPUTIL_OFPAT10_SET_NW_SRC: - case OFPUTIL_OFPAT10_SET_NW_DST: - case OFPUTIL_OFPAT10_SET_NW_TOS: - case OFPUTIL_OFPAT10_SET_TP_SRC: - case OFPUTIL_OFPAT10_SET_TP_DST: - case OFPUTIL_OFPAT10_SET_DL_SRC: - case OFPUTIL_OFPAT10_SET_DL_DST: - case OFPUTIL_NXAST_RESUBMIT: - case OFPUTIL_NXAST_SET_TUNNEL: - case OFPUTIL_NXAST_SET_QUEUE: - case OFPUTIL_NXAST_POP_QUEUE: - case OFPUTIL_NXAST_NOTE: - case OFPUTIL_NXAST_SET_TUNNEL64: - case OFPUTIL_NXAST_EXIT: - case OFPUTIL_NXAST_DEC_TTL: - case OFPUTIL_NXAST_FIN_TIMEOUT: - break; - } - - if (error) { - VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)", - (a - actions) * sizeof *a, ofperr_get_name(error)); - return error; - } - } - if (left) { - VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu", - (n_actions - left) * sizeof *a); - return OFPERR_OFPBAC_BAD_LEN; - } - return 0; -} - -struct ofputil_action { - int code; - unsigned int min_len; - unsigned int max_len; -}; - -static const struct ofputil_action action_bad_type - = { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX }; -static const struct ofputil_action action_bad_len - = { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX }; -static const struct ofputil_action action_bad_vendor - = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX }; - -static const struct ofputil_action * -ofputil_decode_ofpat_action(const union ofp_action *a) -{ - enum ofp10_action_type type = ntohs(a->type); - - switch (type) { -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - case ENUM: { \ - static const struct ofputil_action action = { \ - OFPUTIL_##ENUM, \ - sizeof(struct STRUCT), \ - sizeof(struct STRUCT) \ - }; \ - return &action; \ - } -#include "ofp-util.def" - - case OFPAT10_VENDOR: - default: - return &action_bad_type; - } -} - -static const struct ofputil_action * -ofputil_decode_nxast_action(const union ofp_action *a) -{ - const struct nx_action_header *nah = (const struct nx_action_header *) a; - enum nx_action_subtype subtype = ntohs(nah->subtype); - - switch (subtype) { -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case ENUM: { \ - static const struct ofputil_action action = { \ - OFPUTIL_##ENUM, \ - sizeof(struct STRUCT), \ - EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT) \ - }; \ - return &action; \ - } -#include "ofp-util.def" - - case NXAST_SNAT__OBSOLETE: - case NXAST_DROP_SPOOFED_ARP__OBSOLETE: - default: - return &action_bad_type; - } -} - -/* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT10_* or - * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error - * code. - * - * The caller must have already verified that 'a''s length is correct (that is, - * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no - * longer than the amount of space allocated to 'a'). - * - * This function verifies that 'a''s length is correct for the type of action - * that it represents. */ -int -ofputil_decode_action(const union ofp_action *a) -{ - const struct ofputil_action *action; - uint16_t len = ntohs(a->header.len); - - if (a->type != htons(OFPAT10_VENDOR)) { - action = ofputil_decode_ofpat_action(a); - } else { - switch (ntohl(a->vendor.vendor)) { - case NX_VENDOR_ID: - if (len < sizeof(struct nx_action_header)) { - return -OFPERR_OFPBAC_BAD_LEN; - } - action = ofputil_decode_nxast_action(a); - break; - default: - action = &action_bad_vendor; - break; - } - } - - return (len >= action->min_len && len <= action->max_len - ? action->code - : -OFPERR_OFPBAC_BAD_LEN); -} - -/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_* - * constant. The caller must have already validated that 'a' is a valid action - * understood by Open vSwitch (e.g. by a previous successful call to - * ofputil_decode_action()). */ -enum ofputil_action_code -ofputil_decode_action_unsafe(const union ofp_action *a) -{ - const struct ofputil_action *action; - - if (a->type != htons(OFPAT10_VENDOR)) { - action = ofputil_decode_ofpat_action(a); - } else { - action = ofputil_decode_nxast_action(a); - } - - return action->code; -} - /* 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. @@ -4017,22 +3734,6 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) } #include "ofp-util.def" -/* Returns true if 'action' outputs to 'port', false otherwise. */ -bool -action_outputs_to_port(const union ofp_action *action, ovs_be16 port) -{ - switch (ofputil_decode_action(action)) { - case OFPUTIL_OFPAT10_OUTPUT: - return action->output.port == port; - case OFPUTIL_OFPAT10_ENQUEUE: - return ((const struct ofp_action_enqueue *) action)->port == port; - case OFPUTIL_NXAST_CONTROLLER: - return port == htons(OFPP_CONTROLLER); - default: - return false; - } -} - /* "Normalizes" the wildcards in 'rule'. That means: * * 1. If the type of level N is known, then only the valid fields for that @@ -4140,57 +3841,6 @@ ofputil_normalize_rule(struct cls_rule *rule) } } -/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if - * successful, otherwise an OpenFlow error. - * - * If successful, the first action is stored in '*actionsp' and the number of - * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0 - * are stored, respectively. - * - * This function does not check that the actions are valid (the caller should - * do so, with validate_actions()). The caller is also responsible for making - * sure that 'b->data' is initially aligned appropriately for "union - * ofp_action". */ -enum ofperr -ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, - union ofp_action **actionsp, size_t *n_actionsp) -{ - if (actions_len % OFP_ACTION_ALIGN != 0) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " - "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN); - goto error; - } - - *actionsp = ofpbuf_try_pull(b, actions_len); - if (*actionsp == NULL) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " - "exceeds remaining message length (%zu)", - actions_len, b->size); - goto error; - } - - *n_actionsp = actions_len / OFP_ACTION_ALIGN; - return 0; - -error: - *actionsp = NULL; - *n_actionsp = 0; - return OFPERR_OFPBRC_BAD_LEN; -} - -bool -ofputil_actions_equal(const union ofp_action *a, size_t n_a, - const union ofp_action *b, size_t n_b) -{ - return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a)); -} - -union ofp_action * -ofputil_actions_clone(const union ofp_action *actions, size_t n) -{ - return n ? xmemdup(actions, n * sizeof *actions) : NULL; -} - /* Parses a key or a key-value pair from '*stringp'. * * On success: Stores the key into '*keyp'. Stores the value, if present, into diff --git a/lib/ofp-util.h b/lib/ofp-util.h index 3f9e440b..30e04c4b 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -22,6 +22,7 @@ #include #include #include "classifier.h" +#include "compiler.h" #include "flow.h" #include "netdev.h" #include "openflow/nicira-ext.h" @@ -236,13 +237,14 @@ struct ofputil_flow_mod { uint32_t buffer_id; uint16_t out_port; uint16_t flags; - union ofp_action *actions; - size_t n_actions; + struct ofpact *ofpacts; /* Series of "struct ofpact"s. */ + size_t ofpacts_len; /* Length of ofpacts, in bytes. */ }; enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *, const struct ofp_header *, - enum ofputil_protocol); + enum ofputil_protocol, + struct ofpbuf *ofpacts); struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *, enum ofputil_protocol); @@ -279,13 +281,14 @@ struct ofputil_flow_stats { int hard_age; /* Seconds since last change, -1 if unknown. */ uint64_t packet_count; /* Packet count, UINT64_MAX if unknown. */ uint64_t byte_count; /* Byte count, UINT64_MAX if unknown. */ - union ofp_action *actions; - size_t n_actions; + struct ofpact *ofpacts; + size_t ofpacts_len; }; int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *, struct ofpbuf *msg, - bool flow_age_extension); + bool flow_age_extension, + struct ofpbuf *ofpacts); void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *, struct list *replies); @@ -352,12 +355,13 @@ struct ofputil_packet_out { size_t packet_len; /* Length of packet data in bytes. */ uint32_t buffer_id; /* Buffer id or UINT32_MAX if no buffer. */ uint16_t in_port; /* Packet's input port. */ - union ofp_action *actions; /* Actions. */ - size_t n_actions; /* Number of elements in 'actions' array. */ + struct ofpact *ofpacts; /* Actions. */ + size_t ofpacts_len; /* Size of ofpacts in bytes. */ }; enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *, - const struct ofp_packet_out *); + const struct ofp_packet_out *, + struct ofpbuf *ofpacts); struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *); enum ofputil_port_config { @@ -531,6 +535,7 @@ void ofputil_start_stats_reply(const struct ofp_stats_msg *request, struct list *); struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *); void *ofputil_append_stats_reply(size_t len, struct list *); +void ofputil_postappend_stats_reply(size_t start_ofs, struct list *); void ofputil_append_port_desc_stats_reply(uint8_t ofp_version, const struct ofputil_phy_port *pp, @@ -542,13 +547,7 @@ size_t ofputil_stats_body_len(const struct ofp_header *); const void *ofputil_nxstats_body(const struct ofp_header *); size_t ofputil_nxstats_body_len(const struct ofp_header *); -struct ofpbuf *make_flow_mod(uint16_t command, const struct cls_rule *, - size_t actions_len); -struct ofpbuf *make_add_flow(const struct cls_rule *, uint32_t buffer_id, - uint16_t max_idle, size_t actions_len); -struct ofpbuf *make_packet_in(uint32_t buffer_id, uint16_t in_port, - uint8_t reason, - const struct ofpbuf *payload, int max_send_len); +/* */ struct ofpbuf *make_echo_request(void); struct ofpbuf *make_echo_reply(const struct ofp_header *rq); @@ -597,7 +596,7 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *); * * (The above list helps developers who want to "grep" for these definitions.) */ -enum ofputil_action_code { +enum OVS_PACKED_ENUM ofputil_action_code { OFPUTIL_ACTION_INVALID, #define OFPAT10_ACTION(ENUM, STRUCT, NAME) OFPUTIL_##ENUM, #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM, @@ -612,10 +611,6 @@ enum { #include "ofp-util.def" }; -int ofputil_decode_action(const union ofp_action *); -enum ofputil_action_code ofputil_decode_action_unsafe( - const union ofp_action *); - int ofputil_action_code_from_name(const char *); void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf); @@ -644,38 +639,6 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf); #define OFP_ACTION_ALIGN 8 /* Alignment of ofp_actions. */ -static inline union ofp_action * -ofputil_action_next(const union ofp_action *a) -{ - return ((union ofp_action *) (void *) - ((uint8_t *) a + ntohs(a->header.len))); -} - -static inline bool -ofputil_action_is_valid(const union ofp_action *a, size_t n_actions) -{ - uint16_t len = ntohs(a->header.len); - return (!(len % OFP_ACTION_ALIGN) - && len >= sizeof *a - && len / sizeof *a <= n_actions); -} - -/* This macro is careful to check for actions with bad lengths. */ -#define OFPUTIL_ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS) \ - for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \ - (LEFT) > 0 && ofputil_action_is_valid(ITER, LEFT); \ - ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \ - (ITER) = ofputil_action_next(ITER))) - -/* This macro does not check for actions with bad lengths. It should only be - * used with actions from trusted sources or with actions that have already - * been validated (e.g. with OFPUTIL_ACTION_FOR_EACH). */ -#define OFPUTIL_ACTION_FOR_EACH_UNSAFE(ITER, LEFT, ACTIONS, N_ACTIONS) \ - for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS); \ - (LEFT) > 0; \ - ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \ - (ITER) = ofputil_action_next(ITER))) - enum ofperr validate_actions(const union ofp_action *, size_t n_actions, const struct flow *, int max_ports); bool action_outputs_to_port(const union ofp_action *, ovs_be16 port); diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c index 8cdaa1f8..87dc2ad3 100644 --- a/ofproto/connmgr.c +++ b/ofproto/connmgr.c @@ -25,6 +25,7 @@ #include "fail-open.h" #include "in-band.h" #include "odp-util.h" +#include "ofp-actions.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto-provider.h" @@ -1573,15 +1574,17 @@ connmgr_flushed(struct connmgr *mgr) * traffic until a controller has been defined and it tells us to do so. */ if (!connmgr_has_controllers(mgr) && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) { - union ofp_action action; + struct ofpbuf ofpacts; struct cls_rule rule; - memset(&action, 0, sizeof action); - action.type = htons(OFPAT10_OUTPUT); - action.output.len = htons(sizeof action); - action.output.port = htons(OFPP_NORMAL); + ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE); + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; + ofpact_pad(&ofpacts); + cls_rule_init_catchall(&rule, 0); - ofproto_add_flow(mgr->ofproto, &rule, &action, 1); + ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size); + + ofpbuf_uninit(&ofpacts); } } diff --git a/ofproto/fail-open.c b/ofproto/fail-open.c index 912dc4e4..495197e7 100644 --- a/ofproto/fail-open.c +++ b/ofproto/fail-open.c @@ -23,6 +23,7 @@ #include "flow.h" #include "mac-learning.h" #include "odp-util.h" +#include "ofp-actions.h" #include "ofp-util.h" #include "ofpbuf.h" #include "ofproto.h" @@ -214,18 +215,19 @@ fail_open_flushed(struct fail_open *fo) int disconn_secs = connmgr_failure_duration(fo->connmgr); bool open = disconn_secs >= trigger_duration(fo); if (open) { - union ofp_action action; + struct ofpbuf ofpacts; struct cls_rule rule; /* Set up a flow that matches every packet and directs them to * OFPP_NORMAL. */ - memset(&action, 0, sizeof action); - action.type = htons(OFPAT10_OUTPUT); - action.output.len = htons(sizeof action); - action.output.port = htons(OFPP_NORMAL); + ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE); + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; + ofpact_pad(&ofpacts); cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY); - ofproto_add_flow(fo->ofproto, &rule, &action, 1); + ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size); + + ofpbuf_uninit(&ofpacts); } } diff --git a/ofproto/in-band.c b/ofproto/in-band.c index 2013869c..43461ad4 100644 --- a/ofproto/in-band.c +++ b/ofproto/in-band.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ #include "netdev.h" #include "netlink.h" #include "odp-util.h" +#include "ofp-actions.h" #include "ofproto.h" #include "ofpbuf.h" #include "ofproto-provider.h" @@ -402,32 +403,17 @@ update_rules(struct in_band *ib) bool in_band_run(struct in_band *ib) { - struct { - struct nx_action_set_queue nxsq; - union ofp_action oa; - } actions; - const void *a; - size_t na; + uint64_t ofpacts_stub[128 / 8]; + struct ofpbuf ofpacts; struct in_band_rule *rule, *next; - memset(&actions, 0, sizeof actions); - actions.oa.output.type = htons(OFPAT10_OUTPUT); - actions.oa.output.len = htons(sizeof actions.oa); - actions.oa.output.port = htons(OFPP_NORMAL); - actions.oa.output.max_len = htons(0); - if (ib->queue_id < 0) { - a = &actions.oa; - na = sizeof actions.oa / sizeof(union ofp_action); - } else { - actions.nxsq.type = htons(OFPAT10_VENDOR); - actions.nxsq.len = htons(sizeof actions.nxsq); - actions.nxsq.vendor = htonl(NX_VENDOR_ID); - actions.nxsq.subtype = htons(NXAST_SET_QUEUE); - actions.nxsq.queue_id = htonl(ib->queue_id); - a = &actions; - na = sizeof actions / sizeof(union ofp_action); + ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); + + if (ib->queue_id >= 0) { + ofpact_put_SET_QUEUE(&ofpacts)->queue_id = ib->queue_id; } + ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL; refresh_local(ib); refresh_remotes(ib); @@ -437,7 +423,8 @@ in_band_run(struct in_band *ib) HMAP_FOR_EACH_SAFE (rule, next, cls_rule.hmap_node, &ib->rules) { switch (rule->op) { case ADD: - ofproto_add_flow(ib->ofproto, &rule->cls_rule, a, na); + ofproto_add_flow(ib->ofproto, &rule->cls_rule, + ofpacts.data, ofpacts.size); break; case DELETE: @@ -451,6 +438,8 @@ in_band_run(struct in_band *ib) } } + ofpbuf_uninit(&ofpacts); + return ib->n_remotes || !hmap_is_empty(&ib->rules); } diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index f51182af..2451d44d 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -42,6 +42,7 @@ #include "odp-util.h" #include "ofp-util.h" #include "ofpbuf.h" +#include "ofp-actions.h" #include "ofp-parse.h" #include "ofp-print.h" #include "ofproto-dpif-governor.h" @@ -281,11 +282,11 @@ static void action_xlate_ctx_init(struct action_xlate_ctx *, ovs_be16 initial_tci, struct rule_dpif *, uint8_t tcp_flags, const struct ofpbuf *); static void xlate_actions(struct action_xlate_ctx *, - const union ofp_action *in, size_t n_in, + const struct ofpact *ofpacts, size_t ofpacts_len, struct ofpbuf *odp_actions); static void xlate_actions_for_side_effects(struct action_xlate_ctx *, - const union ofp_action *in, - size_t n_in); + const struct ofpact *ofpacts, + size_t ofpacts_len); static size_t put_userspace_action(const struct ofproto_dpif *, struct ofpbuf *odp_actions, @@ -804,7 +805,7 @@ construct(struct ofproto *ofproto_) static int add_internal_flow(struct ofproto_dpif *ofproto, int id, - const struct ofpbuf *actions, struct rule_dpif **rulep) + const struct ofpbuf *ofpacts, struct rule_dpif **rulep) { struct ofputil_flow_mod fm; int error; @@ -821,8 +822,8 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id, fm.buffer_id = 0; fm.out_port = 0; fm.flags = 0; - fm.actions = actions->data; - fm.n_actions = actions->size / sizeof(union ofp_action); + fm.ofpacts = ofpacts->data; + fm.ofpacts_len = ofpacts->size; error = ofproto_flow_mod(&ofproto->up, &fm); if (error) { @@ -840,26 +841,28 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id, static int add_internal_flows(struct ofproto_dpif *ofproto) { - struct nx_action_controller *nac; - uint64_t actions_stub[128 / 8]; - struct ofpbuf actions; + struct ofpact_controller *controller; + uint64_t ofpacts_stub[128 / 8]; + struct ofpbuf ofpacts; int error; int id; - ofpbuf_use_stack(&actions, actions_stub, sizeof actions_stub); + ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); id = 1; - nac = ofputil_put_NXAST_CONTROLLER(&actions); - nac->max_len = htons(UINT16_MAX); - nac->controller_id = htons(0); - nac->reason = OFPR_NO_MATCH; - error = add_internal_flow(ofproto, id++, &actions, &ofproto->miss_rule); + controller = ofpact_put_CONTROLLER(&ofpacts); + controller->max_len = UINT16_MAX; + controller->controller_id = 0; + controller->reason = OFPR_NO_MATCH; + ofpact_pad(&ofpacts); + + error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule); if (error) { return error; } - ofpbuf_clear(&actions); - error = add_internal_flow(ofproto, id++, &actions, + ofpbuf_clear(&ofpacts); + error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->no_packet_in_rule); return error; } @@ -2864,7 +2867,7 @@ handle_flow_miss_without_facet(struct flow_miss *miss, action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci, rule, 0, packet); ctx.resubmit_stats = &stats; - xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, + xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions); if (odp_actions.size) { @@ -3699,8 +3702,8 @@ facet_learn(struct facet *facet) facet->flow.vlan_tci, facet->rule, facet->tcp_flags, NULL); ctx.may_learn = true; - xlate_actions_for_side_effects(&ctx, facet->rule->up.actions, - facet->rule->up.n_actions); + xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts, + facet->rule->up.ofpacts_len); } static void @@ -3761,10 +3764,17 @@ facet_account(struct facet *facet) static bool facet_is_controller_flow(struct facet *facet) { - return (facet - && facet->rule->up.n_actions == 1 - && action_outputs_to_port(&facet->rule->up.actions[0], - htons(OFPP_CONTROLLER))); + if (facet) { + const struct rule *rule = &facet->rule->up; + const struct ofpact *ofpacts = rule->ofpacts; + size_t ofpacts_len = rule->ofpacts_len; + + if (ofpacts->type == OFPACT_CONTROLLER && + ofpact_next(ofpacts) >= ofpact_end(ofpacts, ofpacts_len)) { + return true; + } + } + return false; } /* Folds all of 'facet''s statistics into its rule. Also updates the @@ -3939,7 +3949,7 @@ facet_check_consistency(struct facet *facet) action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci, rule, 0, NULL); - xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, + xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions); if (subfacet->path == SF_NOT_INSTALLED) { @@ -4049,7 +4059,7 @@ facet_revalidate(struct facet *facet) action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci, new_rule, 0, NULL); - xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions, + xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len, &odp_actions); slow = (subfacet->slow & SLOW_MATCH) | ctx.slow; @@ -4179,7 +4189,8 @@ flow_push_stats(struct rule_dpif *rule, action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule, 0, NULL); ctx.resubmit_stats = stats; - xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions); + xlate_actions_for_side_effects(&ctx, rule->up.ofpacts, + rule->up.ofpacts_len); } /* Subfacets. */ @@ -4339,7 +4350,7 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet, action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci, rule, 0, packet); - xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions); + xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions); facet->tags = ctx.tags; facet->has_learn = ctx.has_learn; facet->has_normal = ctx.has_normal; @@ -4576,8 +4587,8 @@ rule_construct(struct rule *rule_) uint8_t table_id; enum ofperr error; - error = validate_actions(rule->up.actions, rule->up.n_actions, - &rule->up.cr.flow, ofproto->max_ports); + error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len, + &rule->up.cr.flow, ofproto->max_ports); if (error) { return error; } @@ -4669,7 +4680,7 @@ rule_execute(struct rule *rule_, const struct flow *flow, action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule, stats.tcp_flags, packet); ctx.resubmit_stats = &stats; - xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions); + xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions); execute_odp_actions(ofproto, flow, odp_actions.data, odp_actions.size, packet); @@ -4686,8 +4697,8 @@ rule_modify_actions(struct rule *rule_) struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto); enum ofperr error; - error = validate_actions(rule->up.actions, rule->up.n_actions, - &rule->up.cr.flow, ofproto->max_ports); + error = ofpacts_check(rule->up.ofpacts, rule->up.ofpacts_len, + &rule->up.cr.flow, ofproto->max_ports); if (error) { ofoperation_complete(rule->up.pending, error); return; @@ -4740,8 +4751,8 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet) /* OpenFlow to datapath action translation. */ -static void do_xlate_actions(const union ofp_action *in, size_t n_in, - struct action_xlate_ctx *ctx); +static void do_xlate_actions(const struct ofpact *, size_t ofpacts_len, + struct action_xlate_ctx *); static void xlate_normal(struct action_xlate_ctx *); /* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'. @@ -4987,7 +4998,7 @@ xlate_table_action(struct action_xlate_ctx *ctx, ctx->recurse++; ctx->rule = rule; - do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx); + do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx); ctx->rule = old_rule; ctx->recurse--; } @@ -5003,16 +5014,21 @@ xlate_table_action(struct action_xlate_ctx *ctx, } static void -xlate_resubmit_table(struct action_xlate_ctx *ctx, - const struct nx_action_resubmit *nar) +xlate_ofpact_resubmit(struct action_xlate_ctx *ctx, + const struct ofpact_resubmit *resubmit) { uint16_t in_port; uint8_t table_id; - in_port = (nar->in_port == htons(OFPP_IN_PORT) - ? ctx->flow.in_port - : ntohs(nar->in_port)); - table_id = nar->table == 255 ? ctx->table_id : nar->table; + in_port = resubmit->in_port; + if (in_port == OFPP_IN_PORT) { + in_port = ctx->flow.in_port; + } + + table_id = resubmit->table_id; + if (table_id == 255) { + table_id = ctx->table_id; + } xlate_table_action(ctx, in_port, table_id); } @@ -5125,8 +5141,8 @@ compose_dec_ttl(struct action_xlate_ctx *ctx) } static void -xlate_output_action__(struct action_xlate_ctx *ctx, - uint16_t port, uint16_t max_len) +xlate_output_action(struct action_xlate_ctx *ctx, + uint16_t port, uint16_t max_len) { uint16_t prev_nf_output_iface = ctx->nf_output_iface; @@ -5173,44 +5189,32 @@ xlate_output_action__(struct action_xlate_ctx *ctx, static void xlate_output_reg_action(struct action_xlate_ctx *ctx, - const struct nx_action_output_reg *naor) + const struct ofpact_output_reg *or) { - struct mf_subfield src; - uint64_t ofp_port; - - nxm_decode(&src, naor->src, naor->ofs_nbits); - ofp_port = mf_get_subfield(&src, &ctx->flow); - - if (ofp_port <= UINT16_MAX) { - xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len)); + uint64_t port = mf_get_subfield(&or->src, &ctx->flow); + if (port <= UINT16_MAX) { + xlate_output_action(ctx, port, or->max_len); } } -static void -xlate_output_action(struct action_xlate_ctx *ctx, - const struct ofp_action_output *oao) -{ - xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len)); -} - static void xlate_enqueue_action(struct action_xlate_ctx *ctx, - const struct ofp_action_enqueue *oae) + const struct ofpact_enqueue *enqueue) { - uint16_t ofp_port; + uint16_t ofp_port = enqueue->port; + uint32_t queue_id = enqueue->queue; uint32_t flow_priority, priority; int error; - error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id), - &priority); + /* Translate queue to priority. */ + error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority); if (error) { /* Fall back to ordinary output action. */ - xlate_output_action__(ctx, ntohs(oae->port), 0); + xlate_output_action(ctx, enqueue->port, 0); return; } - /* Figure out datapath output port. */ - ofp_port = ntohs(oae->port); + /* Check output port. */ if (ofp_port == OFPP_IN_PORT) { ofp_port = ctx->flow.in_port; } else if (ofp_port == ctx->flow.in_port) { @@ -5232,21 +5236,16 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx, } static void -xlate_set_queue_action(struct action_xlate_ctx *ctx, - const struct nx_action_set_queue *nasq) +xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id) { - uint32_t priority; - int error; + uint32_t skb_priority; - error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id), - &priority); - if (error) { - /* Couldn't translate queue to a priority, so ignore. A warning + if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) { + ctx->flow.skb_priority = skb_priority; + } else { + /* Couldn't translate queue to a priority. Nothing to do. A warning * has already been logged. */ - return; } - - ctx->flow.skb_priority = priority; } struct xlate_reg_state { @@ -5256,9 +5255,9 @@ struct xlate_reg_state { static void xlate_autopath(struct action_xlate_ctx *ctx, - const struct nx_action_autopath *naa) + const struct ofpact_autopath *ap) { - uint16_t ofp_port = ntohl(naa->id); + uint16_t ofp_port = ap->port; struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port); if (!port || !port->bundle) { @@ -5271,7 +5270,7 @@ xlate_autopath(struct action_xlate_ctx *ctx, ofp_port = slave->up.ofp_port; } } - autopath_execute(naa, &ctx->flow, ofp_port); + nxm_reg_load(&ap->dst, ofp_port, &ctx->flow); } static bool @@ -5296,15 +5295,32 @@ slave_enabled_cb(uint16_t ofp_port, void *ofproto_) } } +static void +xlate_bundle_action(struct action_xlate_ctx *ctx, + const struct ofpact_bundle *bundle) +{ + uint16_t port; + + port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto); + if (bundle->dst.field) { + nxm_reg_load(&bundle->dst, port, &ctx->flow); + } else { + xlate_output_action(ctx, port, 0); + } +} + static void xlate_learn_action(struct action_xlate_ctx *ctx, - const struct nx_action_learn *learn) + const struct ofpact_learn *learn) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); struct ofputil_flow_mod fm; + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts; int error; - learn_execute(learn, &ctx->flow, &fm); + ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); + learn_execute(learn, &ctx->flow, &fm, &ofpacts); error = ofproto_flow_mod(&ctx->ofproto->up, &fm); if (error && !VLOG_DROP_WARN(&rl)) { @@ -5312,7 +5328,7 @@ xlate_learn_action(struct action_xlate_ctx *ctx, ofperr_get_name(error)); } - free(fm.actions); + ofpbuf_uninit(&ofpacts); } /* Reduces '*timeout' to no more than 'max'. A value of zero in either case @@ -5327,13 +5343,13 @@ reduce_timeout(uint16_t max, uint16_t *timeout) static void xlate_fin_timeout(struct action_xlate_ctx *ctx, - const struct nx_action_fin_timeout *naft) + const struct ofpact_fin_timeout *oft) { if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) { struct rule_dpif *rule = ctx->rule; - reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout); - reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout); + reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout); + reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout); } } @@ -5359,13 +5375,12 @@ may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx) } static void -do_xlate_actions(const union ofp_action *in, size_t n_in, +do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, struct action_xlate_ctx *ctx) { const struct ofport_dpif *port; - const union ofp_action *ia; bool was_evictable = true; - size_t left; + const struct ofpact *a; port = get_ofp_port(ctx->ofproto, ctx->flow.in_port); if (port && !may_receive(port, ctx)) { @@ -5378,184 +5393,146 @@ do_xlate_actions(const union ofp_action *in, size_t n_in, was_evictable = ctx->rule->up.evictable; ctx->rule->up.evictable = false; } - OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) { - const struct ofp_action_dl_addr *oada; - const struct nx_action_resubmit *nar; - const struct nx_action_set_tunnel *nast; - const struct nx_action_set_queue *nasq; - const struct nx_action_multipath *nam; - const struct nx_action_autopath *naa; - const struct nx_action_bundle *nab; - const struct nx_action_output_reg *naor; - const struct nx_action_controller *nac; - enum ofputil_action_code code; - ovs_be64 tun_id; + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + struct ofpact_controller *controller; if (ctx->exit) { break; } - code = ofputil_decode_action_unsafe(ia); - switch (code) { - case OFPUTIL_ACTION_INVALID: - NOT_REACHED(); + switch (a->type) { + case OFPACT_OUTPUT: + xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port, + ofpact_get_OUTPUT(a)->max_len); + break; + + case OFPACT_CONTROLLER: + controller = ofpact_get_CONTROLLER(a); + execute_controller_action(ctx, controller->max_len, + controller->reason, + controller->controller_id); + break; - case OFPUTIL_OFPAT10_OUTPUT: - xlate_output_action(ctx, &ia->output); + case OFPACT_ENQUEUE: + xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a)); break; - case OFPUTIL_OFPAT10_SET_VLAN_VID: + case OFPACT_SET_VLAN_VID: ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK); - ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI); + ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid) + | htons(VLAN_CFI)); break; - case OFPUTIL_OFPAT10_SET_VLAN_PCP: + case OFPACT_SET_VLAN_PCP: ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK); - ctx->flow.vlan_tci |= htons( - (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI); + ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp + << VLAN_PCP_SHIFT) + | VLAN_CFI); break; - case OFPUTIL_OFPAT10_STRIP_VLAN: + case OFPACT_STRIP_VLAN: ctx->flow.vlan_tci = htons(0); break; - case OFPUTIL_OFPAT10_SET_DL_SRC: - oada = ((struct ofp_action_dl_addr *) ia); - memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN); + case OFPACT_SET_ETH_SRC: + memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac, + ETH_ADDR_LEN); break; - case OFPUTIL_OFPAT10_SET_DL_DST: - oada = ((struct ofp_action_dl_addr *) ia); - memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN); + case OFPACT_SET_ETH_DST: + memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac, + ETH_ADDR_LEN); break; - case OFPUTIL_OFPAT10_SET_NW_SRC: - ctx->flow.nw_src = ia->nw_addr.nw_addr; + case OFPACT_SET_IPV4_SRC: + ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4; break; - case OFPUTIL_OFPAT10_SET_NW_DST: - ctx->flow.nw_dst = ia->nw_addr.nw_addr; + case OFPACT_SET_IPV4_DST: + ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4; break; - case OFPUTIL_OFPAT10_SET_NW_TOS: + case OFPACT_SET_IPV4_DSCP: /* OpenFlow 1.0 only supports IPv4. */ if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) { ctx->flow.nw_tos &= ~IP_DSCP_MASK; - ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK; + ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp; } break; - case OFPUTIL_OFPAT10_SET_TP_SRC: - ctx->flow.tp_src = ia->tp_port.tp_port; + case OFPACT_SET_L4_SRC_PORT: + ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); break; - case OFPUTIL_OFPAT10_SET_TP_DST: - ctx->flow.tp_dst = ia->tp_port.tp_port; + case OFPACT_SET_L4_DST_PORT: + ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port); break; - case OFPUTIL_OFPAT10_ENQUEUE: - xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia); + case OFPACT_RESUBMIT: + xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a)); break; - case OFPUTIL_NXAST_RESUBMIT: - nar = (const struct nx_action_resubmit *) ia; - xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id); + case OFPACT_SET_TUNNEL: + ctx->flow.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id); break; - case OFPUTIL_NXAST_RESUBMIT_TABLE: - xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia); + case OFPACT_SET_QUEUE: + xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id); break; - case OFPUTIL_NXAST_SET_TUNNEL: - nast = (const struct nx_action_set_tunnel *) ia; - tun_id = htonll(ntohl(nast->tun_id)); - ctx->flow.tun_id = tun_id; - break; - - case OFPUTIL_NXAST_SET_QUEUE: - nasq = (const struct nx_action_set_queue *) ia; - xlate_set_queue_action(ctx, nasq); - break; - - case OFPUTIL_NXAST_POP_QUEUE: + case OFPACT_POP_QUEUE: ctx->flow.skb_priority = ctx->orig_skb_priority; break; - case OFPUTIL_NXAST_REG_MOVE: - nxm_execute_reg_move((const struct nx_action_reg_move *) ia, - &ctx->flow); - break; - - case OFPUTIL_NXAST_REG_LOAD: - nxm_execute_reg_load((const struct nx_action_reg_load *) ia, - &ctx->flow); + case OFPACT_REG_MOVE: + nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow); break; - case OFPUTIL_NXAST_NOTE: - /* Nothing to do. */ + case OFPACT_REG_LOAD: + nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow); break; - case OFPUTIL_NXAST_SET_TUNNEL64: - tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id; - ctx->flow.tun_id = tun_id; + case OFPACT_DEC_TTL: + if (compose_dec_ttl(ctx)) { + goto out; + } break; - case OFPUTIL_NXAST_MULTIPATH: - nam = (const struct nx_action_multipath *) ia; - multipath_execute(nam, &ctx->flow); + case OFPACT_NOTE: + /* Nothing to do. */ break; - case OFPUTIL_NXAST_AUTOPATH: - naa = (const struct nx_action_autopath *) ia; - xlate_autopath(ctx, naa); + case OFPACT_MULTIPATH: + multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow); break; - case OFPUTIL_NXAST_BUNDLE: - ctx->ofproto->has_bundle_action = true; - nab = (const struct nx_action_bundle *) ia; - xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow, - slave_enabled_cb, - ctx->ofproto), 0); + case OFPACT_AUTOPATH: + xlate_autopath(ctx, ofpact_get_AUTOPATH(a)); break; - case OFPUTIL_NXAST_BUNDLE_LOAD: + case OFPACT_BUNDLE: ctx->ofproto->has_bundle_action = true; - nab = (const struct nx_action_bundle *) ia; - bundle_execute_load(nab, &ctx->flow, slave_enabled_cb, - ctx->ofproto); + xlate_bundle_action(ctx, ofpact_get_BUNDLE(a)); break; - case OFPUTIL_NXAST_OUTPUT_REG: - naor = (const struct nx_action_output_reg *) ia; - xlate_output_reg_action(ctx, naor); + case OFPACT_OUTPUT_REG: + xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a)); break; - case OFPUTIL_NXAST_LEARN: + case OFPACT_LEARN: ctx->has_learn = true; if (ctx->may_learn) { - xlate_learn_action(ctx, (const struct nx_action_learn *) ia); + xlate_learn_action(ctx, ofpact_get_LEARN(a)); } break; - case OFPUTIL_NXAST_DEC_TTL: - if (compose_dec_ttl(ctx)) { - goto out; - } - break; - - case OFPUTIL_NXAST_EXIT: + case OFPACT_EXIT: ctx->exit = true; break; - case OFPUTIL_NXAST_FIN_TIMEOUT: + case OFPACT_FIN_TIMEOUT: ctx->has_fin_timeout = true; - xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia); - break; - - case OFPUTIL_NXAST_CONTROLLER: - nac = (const struct nx_action_controller *) ia; - execute_controller_action(ctx, ntohs(nac->max_len), nac->reason, - ntohs(nac->controller_id)); + xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a)); break; } } @@ -5591,11 +5568,11 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx, ctx->resubmit_stats = NULL; } -/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in - * 'odp_actions', using 'ctx'. */ +/* Translates the 'ofpacts_len' bytes of "struct ofpacts" starting at 'ofpacts' + * into datapath actions in 'odp_actions', using 'ctx'. */ static void xlate_actions(struct action_xlate_ctx *ctx, - const union ofp_action *in, size_t n_in, + const struct ofpact *ofpacts, size_t ofpacts_len, struct ofpbuf *odp_actions) { /* Normally false. Set to true if we ever hit MAX_RESUBMIT_RECURSION, so @@ -5665,7 +5642,7 @@ xlate_actions(struct action_xlate_ctx *ctx, ovs_be16 initial_tci = ctx->base_flow.vlan_tci; add_sflow_action(ctx); - do_xlate_actions(in, n_in, ctx); + do_xlate_actions(ofpacts, ofpacts_len, ctx); if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) { if (!hit_resubmit_limit) { @@ -5700,17 +5677,18 @@ xlate_actions(struct action_xlate_ctx *ctx, } } -/* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions, - * using 'ctx', and discards the datapath actions. */ +/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts' + * into datapath actions, using 'ctx', and discards the datapath actions. */ static void xlate_actions_for_side_effects(struct action_xlate_ctx *ctx, - const union ofp_action *in, size_t n_in) + const struct ofpact *ofpacts, + size_t ofpacts_len) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions; ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub); - xlate_actions(ctx, in, n_in, &odp_actions); + xlate_actions(ctx, ofpacts, ofpacts_len, &odp_actions); ofpbuf_uninit(&odp_actions); } @@ -6371,7 +6349,7 @@ set_frag_handling(struct ofproto *ofproto_, static enum ofperr packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, const struct flow *flow, - const union ofp_action *ofp_actions, size_t n_ofp_actions) + const struct ofpact *ofpacts, size_t ofpacts_len) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); enum ofperr error; @@ -6380,8 +6358,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, return OFPERR_NXBRC_BAD_IN_PORT; } - error = validate_actions(ofp_actions, n_ofp_actions, flow, - ofproto->max_ports); + error = ofpacts_check(ofpacts, ofpacts_len, flow, ofproto->max_ports); if (!error) { struct odputil_keybuf keybuf; struct dpif_flow_stats stats; @@ -6403,7 +6380,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet, ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub); - xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions); + xlate_actions(&ctx, ofpacts, ofpacts_len, &odp_actions); dpif_execute(ofproto->dpif, key.data, key.size, odp_actions.data, odp_actions.size, packet); ofpbuf_uninit(&odp_actions); @@ -6560,7 +6537,7 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level, ds_put_char_multiple(result, '\t', level); ds_put_cstr(result, "OpenFlow "); - ofp_print_actions(result, rule->up.actions, rule->up.n_actions); + ofpacts_format(rule->up.ofpacts, rule->up.ofpacts_len, result); ds_put_char(result, '\n'); } @@ -6768,7 +6745,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci, rule, tcp_flags, packet); trace.ctx.resubmit_hook = trace_resubmit; - xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions, + xlate_actions(&trace.ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions); ds_put_char(ds, '\n'); diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 2cbb9ae0..afd17a60 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -29,6 +29,7 @@ #include "shash.h" #include "timeval.h" +struct ofpact; struct ofputil_flow_mod; struct simap; @@ -184,8 +185,8 @@ struct rule { struct heap_node evg_node; /* In eviction_group's "rules" heap. */ struct eviction_group *eviction_group; /* NULL if not in any group. */ - union ofp_action *actions; /* OpenFlow actions. */ - int n_actions; /* Number of elements in actions[]. */ + struct ofpact *ofpacts; /* Sequence of "struct ofpacts". */ + unsigned int ofpacts_len; /* Size of 'ofpacts', in bytes. */ }; static inline struct rule * @@ -781,13 +782,11 @@ struct ofproto_class { * registers, then it is an error if 'rule->cr' does not wildcard all * registers. * - * - Validate that 'rule->actions' and 'rule->n_actions' are well-formed - * OpenFlow actions that the datapath can correctly implement. The - * validate_actions() function (in ofp-util.c) can be useful as a model - * for action validation, but it accepts all of the OpenFlow actions - * that OVS understands. If your ofproto implementation only - * implements a subset of those, then you should implement your own - * action validation. + * - Validate that 'rule->ofpacts' is a sequence of well-formed actions + * that the datapath can correctly implement. If your ofproto + * implementation only implements a subset of the actions that Open + * vSwitch understands, then you should implement your own action + * validation. * * - If the rule is valid, update the datapath flow table, adding the new * rule or replacing the existing one. @@ -918,14 +917,13 @@ struct ofproto_class { enum ofp_config_flags frag_handling); /* Implements the OpenFlow OFPT_PACKET_OUT command. The datapath should - * execute the 'n_actions' in the 'actions' array on 'packet'. + * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'. * - * The caller retains ownership of 'packet', so ->packet_out() should not - * modify or free it. + * The caller retains ownership of 'packet' and of 'ofpacts', so + * ->packet_out() should not modify or free them. * - * This function must validate that the 'n_actions' elements in 'actions' - * are well-formed OpenFlow actions that can be correctly implemented by - * the datapath. If not, then it should return an OpenFlow error code. + * This function must validate that it can implement 'ofpacts'. If not, + * then it should return an OpenFlow error code. * * 'flow' reflects the flow information for 'packet'. All of the * information in 'flow' is extracted from 'packet', except for @@ -957,8 +955,8 @@ struct ofproto_class { * Returns 0 if successful, otherwise an OpenFlow error code. */ enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet, const struct flow *flow, - const union ofp_action *actions, - size_t n_actions); + const struct ofpact *ofpacts, + size_t ofpacts_len); /* ## ------------------------- ## */ /* ## OFPP_NORMAL configuration ## */ @@ -1195,7 +1193,7 @@ BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS); int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *); void ofproto_add_flow(struct ofproto *, const struct cls_rule *, - const union ofp_action *, size_t n_actions); + const struct ofpact *ofpacts, size_t ofpacts_len); bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *); void ofproto_flush_flows(struct ofproto *); diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index ce4da9d3..b23c79b8 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -32,6 +32,7 @@ #include "meta-flow.h" #include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-print.h" #include "ofp-util.h" @@ -118,8 +119,8 @@ struct ofoperation { struct rule *rule; /* Rule being operated upon. */ enum ofoperation_type type; /* Type of operation. */ struct rule *victim; /* OFOPERATION_ADDING: Replaced rule. */ - union ofp_action *actions; /* OFOPERATION_MODIFYING: Replaced actions. */ - int n_actions; /* OFOPERATION_MODIFYING: # of old actions. */ + struct ofpact *ofpacts; /* OFOPERATION_MODIFYING: Replaced actions. */ + size_t ofpacts_len; /* OFOPERATION_MODIFYING: Bytes of ofpacts. */ ovs_be64 flow_cookie; /* Rule's old flow cookie. */ }; @@ -1378,27 +1379,28 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port) * (0...65535, inclusive) then the flow will be visible to OpenFlow * controllers; otherwise, it will be hidden. * - * The caller retains ownership of 'cls_rule' and 'actions'. + * The caller retains ownership of 'cls_rule' and 'ofpacts'. * * This is a helper function for in-band control and fail-open. */ void ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule, - const union ofp_action *actions, size_t n_actions) + const struct ofpact *ofpacts, size_t ofpacts_len) { const struct rule *rule; rule = rule_from_cls_rule(classifier_find_rule_exactly( &ofproto->tables[0].cls, cls_rule)); - if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions, - actions, n_actions)) { + if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len, + ofpacts, ofpacts_len)) { struct ofputil_flow_mod fm; memset(&fm, 0, sizeof fm); fm.cr = *cls_rule; fm.buffer_id = UINT32_MAX; - fm.actions = (union ofp_action *) actions; - fm.n_actions = n_actions; + fm.ofpacts = xmemdup(ofpacts, ofpacts_len); + fm.ofpacts_len = ofpacts_len; add_flow(ofproto, NULL, &fm, NULL); + free(fm.ofpacts); } } @@ -1857,7 +1859,7 @@ static void ofproto_rule_destroy__(struct rule *rule) { if (rule) { - free(rule->actions); + free(rule->ofpacts); rule->ofproto->ofproto_class->rule_dealloc(rule); } } @@ -1879,23 +1881,12 @@ ofproto_rule_destroy(struct rule *rule) } /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action - * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't - * count). */ + * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */ static bool -rule_has_out_port(const struct rule *rule, uint16_t out_port) +rule_has_out_port(const struct rule *rule, uint16_t port) { - const union ofp_action *oa; - size_t left; - - if (out_port == OFPP_NONE) { - return true; - } - OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) { - if (action_outputs_to_port(oa, htons(out_port))) { - return true; - } - } - return false; + return (port == OFPP_NONE + || ofpacts_output_to_port(rule->ofpacts, rule->ofpacts_len, port)); } /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s @@ -2052,6 +2043,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo) struct ofproto *p = ofconn_get_ofproto(ofconn); struct ofputil_packet_out po; struct ofpbuf *payload; + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts; struct flow flow; enum ofperr error; @@ -2059,20 +2052,21 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo) error = reject_slave_controller(ofconn); if (error) { - return error; + goto exit; } /* Decode message. */ - error = ofputil_decode_packet_out(&po, opo); + ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); + error = ofputil_decode_packet_out(&po, opo, &ofpacts); if (error) { - return error; + goto exit_free_ofpacts; } /* Get payload. */ if (po.buffer_id != UINT32_MAX) { error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL); if (error || !payload) { - return error; + goto exit_free_ofpacts; } } else { payload = xmalloc(sizeof *payload); @@ -2082,9 +2076,12 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo) /* Send out packet. */ flow_extract(payload, 0, 0, po.in_port, &flow); error = p->ofproto_class->packet_out(p, payload, &flow, - po.actions, po.n_actions); + po.ofpacts, po.ofpacts_len); ofpbuf_delete(payload); +exit_free_ofpacts: + ofpbuf_uninit(&ofpacts); +exit: return error; } @@ -2486,8 +2483,8 @@ handle_flow_stats_request(struct ofconn *ofconn, fs.hard_age = age_secs(now - rule->modified); ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count, &fs.byte_count); - fs.actions = rule->actions; - fs.n_actions = rule->n_actions; + fs.ofpacts = rule->ofpacts; + fs.ofpacts_len = rule->ofpacts_len; ofputil_append_flow_stats_reply(&fs, &replies); } ofconn_send_replies(ofconn, &replies); @@ -2513,8 +2510,8 @@ flow_stats_ds(struct rule *rule, struct ds *results) ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count); cls_rule_format(&rule->cr, results); ds_put_char(results, ','); - if (rule->n_actions > 0) { - ofp_print_actions(results, rule->actions, rule->n_actions); + if (rule->ofpacts_len > 0) { + ofpacts_format(rule->ofpacts, rule->ofpacts_len, results); } else { ds_put_cstr(results, "drop"); } @@ -2771,6 +2768,9 @@ is_flow_deletion_pending(const struct ofproto *ofproto, * error code on failure, or OFPROTO_POSTPONE if the operation cannot be * initiated now but may be retried later. * + * Upon successful return, takes ownership of 'fm->ofpacts'. On failure, + * ownership remains with the caller. + * * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id, * if any. */ static enum ofperr @@ -2839,8 +2839,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn, rule->hard_timeout = fm->hard_timeout; rule->table_id = table - ofproto->tables; rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0; - rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions); - rule->n_actions = fm->n_actions; + rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len); + rule->ofpacts_len = fm->ofpacts_len; rule->evictable = true; rule->eviction_group = NULL; @@ -2922,14 +2922,14 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn, continue; } - if (!ofputil_actions_equal(fm->actions, fm->n_actions, - rule->actions, rule->n_actions)) { + if (!ofpacts_equal(fm->ofpacts, fm->ofpacts_len, + rule->ofpacts, rule->ofpacts_len)) { ofoperation_create(group, rule, OFOPERATION_MODIFY); - rule->pending->actions = rule->actions; - rule->pending->n_actions = rule->n_actions; - rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions); - rule->n_actions = fm->n_actions; - ofproto->ofproto_class->rule_modify_actions(rule); + rule->pending->ofpacts = rule->ofpacts; + rule->pending->ofpacts_len = rule->ofpacts_len; + rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len); + rule->ofpacts_len = fm->ofpacts_len; + rule->ofproto->ofproto_class->rule_modify_actions(rule); } else { rule->modified = time_msec(); } @@ -3127,30 +3127,35 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) { struct ofproto *ofproto = ofconn_get_ofproto(ofconn); struct ofputil_flow_mod fm; + uint64_t ofpacts_stub[1024 / 8]; + struct ofpbuf ofpacts; enum ofperr error; long long int now; error = reject_slave_controller(ofconn); if (error) { - return error; + goto exit; } - error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn)); + ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); + error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn), + &ofpacts); if (error) { - return error; + goto exit_free_ofpacts; } /* We do not support the OpenFlow 1.0 emergency flow cache, which is not * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */ if (fm.flags & OFPFF_EMERG) { - /* There isn't a good fit for an error code, so just state that the - * flow table is full. */ - return OFPERR_OFPFMFC_ALL_TABLES_FULL; + /* We do not support the emergency flow cache. It will hopefully get + * dropped from OpenFlow in the near future. There is no good error + * code, so just state that the flow table is full. */ + error = OFPERR_OFPFMFC_ALL_TABLES_FULL; + } else { + error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh); } - - error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh); if (error) { - return error; + goto exit_free_ofpacts; } /* Record the operation for logging a summary report. */ @@ -3179,7 +3184,10 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh) } ofproto->last_op = now; - return 0; +exit_free_ofpacts: + ofpbuf_uninit(&ofpacts); +exit: + return error; } static enum ofperr @@ -3615,7 +3623,7 @@ ofoperation_destroy(struct ofoperation *op) hmap_remove(&group->ofproto->deletions, &op->hmap_node); } list_remove(&op->group_node); - free(op->actions); + free(op->ofpacts); free(op); if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) { @@ -3715,10 +3723,10 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error) if (!error) { rule->modified = time_msec(); } else { - free(rule->actions); - rule->actions = op->actions; - rule->n_actions = op->n_actions; - op->actions = NULL; + free(rule->ofpacts); + rule->ofpacts = op->ofpacts; + rule->ofpacts_len = op->ofpacts_len; + op->ofpacts = NULL; } break; diff --git a/tests/automake.mk b/tests/automake.mk index 81d29427..3e42a3ec 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -14,6 +14,7 @@ TESTSUITE_AT = \ tests/check-structs.at \ tests/daemon.at \ tests/daemon-py.at \ + tests/ofp-actions.at \ tests/ofp-print.at \ tests/ofp-errors.at \ tests/ovs-ofctl.at \ diff --git a/tests/learn.at b/tests/learn.at index 9304861b..da82f51f 100644 --- a/tests/learn.at +++ b/tests/learn.at @@ -10,7 +10,7 @@ AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: any chosen protocol: OpenFlow10-table_id OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1) -OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10]) +OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10]) OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31]) ]]) AT_CLEANUP @@ -42,7 +42,7 @@ ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[]) AT_CHECK([ovs-ofctl parse-flows flows.txt], [0], [[usable protocols: any chosen protocol: OpenFlow10-table_id -OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[]) +OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[]) OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[]) OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[]) ]]) diff --git a/tests/ofp-actions.at b/tests/ofp-actions.at new file mode 100644 index 00000000..6f702136 --- /dev/null +++ b/tests/ofp-actions.at @@ -0,0 +1,121 @@ +AT_BANNER([OpenFlow actions]) + +AT_SETUP([OpenFlow 1.0 action translation]) +AT_KEYWORDS([OF1.0]) +AT_DATA([test-data], [dnl +# actions=LOCAL +0000 0008 fffe 04d2 + +# actions=CONTROLLER:1234 +0000 0008 fffd 04d2 + +# actions=mod_vlan_vid:9 +0001 0008 0009 0000 + +# actions=mod_vlan_pcp:6 +0002 0008 06 000000 + +# actions=strip_vlan +0003 0008 00000000 + +# actions=mod_dl_src:00:11:22:33:44:55 +0004 0010 001122334455 000000000000 + +# actions=mod_dl_dst:10:20:30:40:50:60 +0005 0010 102030405060 000000000000 + +# actions=mod_nw_src:1.2.3.4 +0006 0008 01020304 + +# actions=mod_nw_dst:192.168.0.1 +0007 0008 c0a80001 + +# actions=mod_nw_tos:48 +0008 0008 30 000000 + +# actions=mod_tp_src:80 +0009 0008 0050 0000 + +# actions=mod_tp_dst:443 +000a 0008 01bb 0000 + +# actions=enqueue:10q55 +000b 0010 000a 000000000000 00000037 + +# actions=resubmit:5 +ffff 0010 00002320 0001 0005 00000000 + +# actions=set_tunnel:0x12345678 +ffff 0010 00002320 0002 0000 12345678 + +# actions=set_queue:2309737729 +ffff 0010 00002320 0004 0000 89abcd01 + +# actions=pop_queue +ffff 0010 00002320 0005 000000000000 + +# actions=move:NXM_OF_IN_PORT[]->NXM_OF_VLAN_TCI[] +ffff 0018 00002320 0006 0010 0000 0000 00000002 00000802 + +# actions=load:0xf009->NXM_OF_VLAN_TCI[] +ffff 0018 00002320 0007 000f 00000802 000000000000f009 + +# actions=note:11.e9.9a.ad.67.f3 +ffff 0010 00002320 0008 11e99aad67f3 + +# actions=set_tunnel64:0xc426384d49c53d60 +ffff 0018 00002320 0009 000000000000 c426384d49c53d60 + +# actions=set_tunnel64:0x885f3298 +ffff 0018 00002320 0009 000000000000 00000000885f3298 + +# actions=multipath(eth_src,50,modulo_n,1,0,NXM_NX_REG0[]) +ffff 0020 00002320 000a 0000 0032 0000 0000 0000 0000 0000 0000 001f 00010004 + +# actions=autopath(2,NXM_NX_REG0[2..30]) +ffff 0018 00002320 000b 009c 00010004 00000002 00000000 + +# actions=bundle(eth_src,0,hrw,ofport,slaves:4,8) +ffff 0028 00002320 000c 0001 0000 0000 00000002 0002 0000 00000000 00000000 dnl +0004 0008 00000000 + +# actions=bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[],slaves:4,8) +ffff 0028 00002320 000d 0001 0000 0000 00000002 0002 001f 00010004 00000000 dnl +0004 0008 00000000 + +# actions=resubmit(10,5) +ffff 0010 00002320 000e 000a 05 000000 + +# actions=output:NXM_NX_REG1[5..10] +ffff 0018 00002320 000f 0145 00010204 ffff 000000000000 + +# actions=learn(table=2,idle_timeout=10,hard_timeout=20,fin_idle_timeout=2,fin_hard_timeout=4,priority=80,cookie=0x123456789abcdef0,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[]) +ffff 0048 00002320 0010 000a 0014 0050 123456789abcdef0 0000 02 00 0002 0004 dnl +000c 00000802 0000 00000802 0000 dnl +0030 00000406 0000 00000206 0000 dnl +1010 00000002 0000 dnl +00000000 + +# actions=exit +ffff 0010 00002320 0011 000000000000 + +# actions=dec_ttl +ffff 0010 00002320 0012 000000000000 + +# actions=fin_timeout(idle_timeout=10,hard_timeout=20) +ffff 0010 00002320 0013 000a 0014 0000 + +# actions=controller(reason=invalid_ttl,max_len=1234,id=5678) +ffff 0010 00002320 0014 04d2 162e 02 00 + +]) +sed '/^[[#&]]/d' < test-data > input.txt +sed -n 's/^# //p; /^$/p' < test-data > expout +sed -n 's/^& //p' < test-data > experr +AT_CAPTURE_FILE([input.txt]) +AT_CAPTURE_FILE([expout]) +AT_CAPTURE_FILE([experr]) +AT_CHECK( + [ovs-ofctl '-vPATTERN:console:%c|%p|%m' parse-ofp10-actions < input.txt], + [0], [expout], [experr]) +AT_CLEANUP diff --git a/tests/test-bundle.c b/tests/test-bundle.c index 672c426e..f2d9b824 100644 --- a/tests/test-bundle.c +++ b/tests/test-bundle.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 Nicira, Inc. +/* Copyright (c) 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ #include #include "flow.h" +#include "ofp-actions.h" #include "ofpbuf.h" #include "random.h" @@ -64,22 +65,24 @@ slave_enabled_cb(uint16_t slave_id, void *aux) return slave ? slave->enabled : false; } -static struct nx_action_bundle * +static struct ofpact_bundle * parse_bundle_actions(char *actions) { - struct nx_action_bundle *nab; - struct ofpbuf b; + struct ofpact_bundle *bundle; + struct ofpbuf ofpacts; + struct ofpact *action; - ofpbuf_init(&b, 0); - bundle_parse_load(&b, actions); - nab = ofpbuf_steal_data(&b); - ofpbuf_uninit(&b); + ofpbuf_init(&ofpacts, 0); + bundle_parse_load(actions, &ofpacts); + action = ofpacts.data; + bundle = ofpact_get_BUNDLE(xmemdup(action, action->len)); + ofpbuf_uninit(&ofpacts); - if (ntohs(nab->n_slaves) > MAX_SLAVES) { + if (bundle->n_slaves > MAX_SLAVES) { ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES); } - return nab; + return bundle; } static const char * @@ -101,7 +104,7 @@ int main(int argc, char *argv[]) { bool ok = true; - struct nx_action_bundle *nab; + struct ofpact_bundle *bundle; struct flow *flows; size_t i, n_permute, old_n_enabled; struct slave_group sg; @@ -114,12 +117,12 @@ main(int argc, char *argv[]) ovs_fatal(0, "usage: %s bundle_action", program_name); } - nab = parse_bundle_actions(argv[1]); + bundle = parse_bundle_actions(argv[1]); /* Generate 'slaves' array. */ sg.n_slaves = 0; - for (i = 0; i < ntohs(nab->n_slaves); i++) { - uint16_t slave_id = bundle_get_slave(nab, i); + for (i = 0; i < bundle->n_slaves; i++) { + uint16_t slave_id = bundle->slaves[i]; if (slave_lookup(&sg, slave_id)) { ovs_fatal(0, "Redundant slaves are not supported. "); @@ -136,10 +139,6 @@ main(int argc, char *argv[]) flows[i].regs[0] = OFPP_NONE; } - if (bundle_check(nab, 1024, flows)) { - ovs_fatal(0, "Bundle action fails to check."); - } - /* Cycles through each possible liveness permutation for the given * n_slaves. The initial state is equivalent to all slaves down, so we * skip it by starting at i = 1. We do one extra iteration to cover @@ -188,23 +187,19 @@ main(int argc, char *argv[]) uint16_t old_slave_id, ofp_port; old_slave_id = flow->regs[0]; - ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg); - bundle_execute_load(nab, flow, slave_enabled_cb, &sg); - if (flow->regs[0] != ofp_port) { - ovs_fatal(0, "bundle_execute_load() and bundle_execute() " - "disagree"); - } + ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg); + flow->regs[0] = ofp_port; - if (flow->regs[0] != OFPP_NONE) { - slave_lookup(&sg, flow->regs[0])->flow_count++; + if (ofp_port != OFPP_NONE) { + slave_lookup(&sg, ofp_port)->flow_count++; } - if (old_slave_id != flow->regs[0]) { + if (old_slave_id != ofp_port) { changed++; } } - if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) { + if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { perfect = active == old_active ? 0.0 : 1.0; } else { if (old_n_enabled || n_enabled) { @@ -229,7 +224,7 @@ main(int argc, char *argv[]) if (slave->enabled) { double perfect_fp; - if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) { + if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { perfect_fp = j == active ? 1.0 : 0.0; } else { perfect_fp = 1.0 / n_enabled; @@ -262,7 +257,7 @@ main(int argc, char *argv[]) old_n_enabled = n_enabled; } - free(nab); + free(bundle); free(flows); return ok ? 0 : 1; } diff --git a/tests/test-multipath.c b/tests/test-multipath.c index 483eb3db..8a355677 100644 --- a/tests/test-multipath.c +++ b/tests/test-multipath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Nicira, Inc. + * Copyright (c) 2010, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ #include #include "flow.h" +#include "ofp-actions.h" #include "random.h" #include "util.h" @@ -32,7 +33,7 @@ int main(int argc, char *argv[]) { enum { MP_MAX_LINKS = 63 }; - struct nx_action_multipath mp; + struct ofpact_multipath mp; bool ok = true; int n; @@ -60,11 +61,11 @@ main(int argc, char *argv[]) random_bytes(&flow, sizeof flow); - mp.max_link = htons(n - 1); + mp.max_link = n - 1; multipath_execute(&mp, &flow); old_link = flow.regs[0]; - mp.max_link = htons(n); + mp.max_link = n; multipath_execute(&mp, &flow); new_link = flow.regs[0]; @@ -91,7 +92,7 @@ main(int argc, char *argv[]) "stddev/expected=%.4f\n", n, n + 1, disruption, perfect, distribution); - switch (ntohs(mp.algorithm)) { + switch (mp.algorithm) { case NX_MP_ALG_MODULO_N: if (disruption < (n < 2 ? .25 : .5)) { fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n", diff --git a/tests/testsuite.at b/tests/testsuite.at index 9796b036..3ec28d62 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -56,6 +56,7 @@ m4_include([tests/classifier.at]) m4_include([tests/check-structs.at]) m4_include([tests/daemon.at]) m4_include([tests/daemon-py.at]) +m4_include([tests/ofp-actions.at]) m4_include([tests/ofp-print.at]) m4_include([tests/ofp-errors.at]) m4_include([tests/ovs-ofctl.at]) diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index dbc46cd4..b95f5da5 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -35,9 +35,9 @@ #include "compiler.h" #include "dirs.h" #include "dynamic-string.h" -#include "netlink.h" #include "nx-match.h" #include "odp-util.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-parse.h" #include "ofp-print.h" @@ -871,7 +871,7 @@ do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms) struct ofputil_flow_mod *fm = &fms[i]; transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol)); - free(fm->actions); + free(fm->ofpacts); } vconn_close(vconn); } @@ -1243,19 +1243,19 @@ static void do_packet_out(int argc, char *argv[]) { struct ofputil_packet_out po; - struct ofpbuf actions; + struct ofpbuf ofpacts; struct vconn *vconn; int i; - ofpbuf_init(&actions, sizeof(union ofp_action)); - parse_ofp_actions(argv[3], &actions); + ofpbuf_init(&ofpacts, 64); + parse_ofpacts(argv[3], &ofpacts); po.buffer_id = UINT32_MAX; po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE : !strcasecmp(argv[2], "local") ? OFPP_LOCAL : str_to_port_no(argv[1], argv[2])); - po.actions = actions.data; - po.n_actions = actions.size / sizeof(union ofp_action); + po.ofpacts = ofpacts.data; + po.ofpacts_len = ofpacts.size; open_vconn(argv[1], &vconn); for (i = 4; i < argc; i++) { @@ -1274,7 +1274,7 @@ do_packet_out(int argc, char *argv[]) ofpbuf_delete(packet); } vconn_close(vconn); - ofpbuf_uninit(&actions); + ofpbuf_uninit(&ofpacts); } static void @@ -1492,8 +1492,8 @@ struct fte_version { uint16_t idle_timeout; uint16_t hard_timeout; uint16_t flags; - union ofp_action *actions; - size_t n_actions; + struct ofpact *ofpacts; + size_t ofpacts_len; }; /* Frees 'version' and the data that it owns. */ @@ -1501,7 +1501,7 @@ static void fte_version_free(struct fte_version *version) { if (version) { - free(version->actions); + free(version->ofpacts); free(version); } } @@ -1516,9 +1516,8 @@ fte_version_equals(const struct fte_version *a, const struct fte_version *b) return (a->cookie == b->cookie && a->idle_timeout == b->idle_timeout && a->hard_timeout == b->hard_timeout - && a->n_actions == b->n_actions - && !memcmp(a->actions, b->actions, - a->n_actions * sizeof *a->actions)); + && ofpacts_equal(a->ofpacts, a->ofpacts_len, + b->ofpacts, b->ofpacts_len)); } /* Prints 'version' on stdout. Expects the caller to have printed the rule @@ -1539,7 +1538,7 @@ fte_version_print(const struct fte_version *version) } ds_init(&s); - ofp_print_actions(&s, version->actions, version->n_actions); + ofpacts_format(version->ofpacts, version->ofpacts_len, &s); printf(" %s\n", ds_cstr(&s)); ds_destroy(&s); } @@ -1627,8 +1626,8 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index) version->idle_timeout = fm.idle_timeout; version->hard_timeout = fm.hard_timeout; version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG); - version->actions = fm.actions; - version->n_actions = fm.n_actions; + version->ofpacts = fm.ofpacts; + version->ofpacts_len = fm.ofpacts_len; usable_protocols &= ofputil_usable_protocols(&fm.cr); @@ -1694,10 +1693,14 @@ read_flows_from_switch(struct vconn *vconn, for (;;) { struct fte_version *version; struct ofputil_flow_stats fs; + struct ofpbuf ofpacts; int retval; - retval = ofputil_decode_flow_stats_reply(&fs, reply, false); + ofpbuf_init(&ofpacts, 64); + retval = ofputil_decode_flow_stats_reply(&fs, reply, false, + &ofpacts); if (retval) { + ofpbuf_uninit(&ofpacts); if (retval != EOF) { ovs_fatal(0, "parse error in reply"); } @@ -1709,9 +1712,8 @@ read_flows_from_switch(struct vconn *vconn, version->idle_timeout = fs.idle_timeout; version->hard_timeout = fs.hard_timeout; version->flags = 0; - version->n_actions = fs.n_actions; - version->actions = xmemdup(fs.actions, - fs.n_actions * sizeof *fs.actions); + version->ofpacts = ofpbuf_steal_data(&ofpacts); + version->ofpacts_len = ofpacts.size; fte_insert(cls, &fs.rule, version, index); } @@ -1744,11 +1746,11 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command, fm.flags = version->flags; if (command == OFPFC_ADD || command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) { - fm.actions = version->actions; - fm.n_actions = version->n_actions; + fm.ofpacts = version->ofpacts; + fm.ofpacts_len = version->ofpacts_len; } else { - fm.actions = NULL; - fm.n_actions = 0; + fm.ofpacts = NULL; + fm.ofpacts_len = 0; } ofm = ofputil_encode_flow_mod(&fm, protocol); @@ -1901,7 +1903,7 @@ do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms) ofp_print(stdout, msg->data, msg->size, verbosity); ofpbuf_delete(msg); - free(fm->actions); + free(fm->ofpacts); } } @@ -1996,6 +1998,84 @@ do_parse_oxm(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) return do_parse_nxm__(true); } +static void +print_differences(const void *a_, size_t a_len, + const void *b_, size_t b_len) +{ + const uint8_t *a = a_; + const uint8_t *b = b_; + size_t i; + + for (i = 0; i < MIN(a_len, b_len); i++) { + if (a[i] != b[i]) { + printf("%2zu: %02"PRIx8" -> %02"PRIx8"\n", i, a[i], b[i]); + } + } + for (i = a_len; i < b_len; i++) { + printf("%2zu: (none) -> %02"PRIx8"\n", i, b[i]); + } + for (i = b_len; i < a_len; i++) { + printf("%2zu: %02"PRIx8" -> (none)\n", i, a[i]); + } +} + +/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications + * as hex bytes from stdin, converts them to ofpacts, prints them as strings + * on stdout, and then converts them back to hex bytes and prints any + * differences from the input. */ +static void +do_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) +{ + struct ds in; + + ds_init(&in); + while (!ds_get_preprocessed_line(&in, stdin)) { + struct ofpbuf of10_out; + struct ofpbuf of10_in; + struct ofpbuf ofpacts; + enum ofperr error; + size_t size; + struct ds s; + + /* Parse hex bytes. */ + ofpbuf_init(&of10_in, 0); + if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') { + ovs_fatal(0, "Trailing garbage in hex data"); + } + + /* Convert to ofpacts. */ + ofpbuf_init(&ofpacts, 0); + size = of10_in.size; + error = ofpacts_pull_openflow(&of10_in, of10_in.size, &ofpacts); + if (error) { + printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error)); + ofpbuf_uninit(&ofpacts); + ofpbuf_uninit(&of10_in); + continue; + } + ofpbuf_push_uninit(&of10_in, size); + + /* Print cls_rule. */ + ds_init(&s); + ofpacts_format(ofpacts.data, ofpacts.size, &s); + puts(ds_cstr(&s)); + ds_destroy(&s); + + /* Convert back to ofp10 actions and print differences from input. */ + ofpbuf_init(&of10_out, 0); + ofpacts_to_openflow(ofpacts.data, ofpacts.size, &of10_out); + + print_differences(of10_in.data, of10_in.size, + of10_out.data, of10_out.size); + putchar('\n'); + + ofpbuf_uninit(&ofpacts); + ofpbuf_uninit(&of10_in); + ofpbuf_uninit(&of10_out); + } + ds_destroy(&in); +} + /* "parse-ofp11-match": reads a series of ofp11_match specifications as hex * bytes from stdin, converts them to cls_rules, prints them as strings on * stdout, and then converts them back to hex bytes and prints any differences @@ -2129,6 +2209,7 @@ static const struct command all_commands[] = { { "parse-nx-match", 0, 0, do_parse_nxm }, { "parse-nxm", 0, 0, do_parse_nxm }, { "parse-oxm", 0, 0, do_parse_oxm }, + { "parse-ofp10-actions", 0, 0, do_parse_ofp10_actions }, { "parse-ofp11-match", 0, 0, do_parse_ofp11_match }, { "print-error", 1, 1, do_print_error }, { "ofp-print", 1, 2, do_ofp_print }, -- 2.30.2