diff options
36 files changed, 3356 insertions, 2088 deletions
@@ -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 =========== @@ -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 = "<unknown>"; } - switch (ntohl(nab->slave_type)) { - case NXM_OF_IN_PORT: - slave_type = "ofport"; - break; - default: - slave_type = "<unknown>"; - } - - 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 <sys/types.h> #include <netinet/in.h> #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 <sys/types.h> #include <netinet/in.h> #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 <config.h> +#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_<ENUM>() 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 <stdint.h> +#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. <ENUM>, used below in the enum definition of OFPACT_<ENUM>, and + * elsewhere. + * + * 2. <STRUCT> corresponding to a structure "struct <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. <MEMBER>, which has one of two possible values: + * + * - If "struct <STRUCT>" is fixed-length, it must be "ofpact". + * + * - If "struct <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_<ENUM> 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_<ENUM> with a corresponding struct <STRUCT>, this defines + * the following commonly useful functions: + * + * struct <STRUCT> *ofpact_put_<ENUM>(struct ofpbuf *ofpacts); + * + * Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts', + * initializes it with ofpact_init_<ENUM>(), 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 <STRUCT> *ofpact_get_<ENUM>(const struct ofpact *ofpact); + * + * Returns 'ofpact' cast to "struct <STRUCT> *". 'ofpact->type' must be + * OFPACT_<ENUM>. + * + * as well as the following more rarely useful definitions: + * + * void ofpact_init_<ENUM>(struct <STRUCT> *ofpact); + * + * Initializes the parts of 'ofpact' that identify it as having type + * OFPACT_<ENUM> and length OFPACT_<ENUM>_RAW_SIZE and zeros the rest. + * + * <ENUM>_RAW_SIZE + * + * The size of the action structure. For a fixed-length action, this is + * sizeof(struct <STRUCT>). For a variable-length action, this is the + * offset to the variable-length part. + * + * <ENUM>_SIZE + * + * An integer constant, the value of OFPACT_<ENUM>_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" @@ -154,278 +155,17 @@ 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 <stddef.h> #include <stdint.h> #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 @@ -5297,14 +5296,31 @@ 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 <stdlib.h> #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 <stdlib.h> #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 }, |