X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-actions.c;h=6dabc5a4920e6210c2aecad8ddd9b56430e5e2ab;hb=0f3f3c3db0a997ddb512bf9f0a9a1df7d5252f84;hp=12cc0b0bfd8b482f397bba54156f4b439c52164d;hpb=f25d0cf3c366d92042269a4f787f19c741c2530c;p=sliver-openvswitch.git diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c index 12cc0b0bf..6dabc5a49 100644 --- a/lib/ofp-actions.c +++ b/lib/ofp-actions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ #include #include "ofp-actions.h" -#include "autopath.h" #include "bundle.h" #include "byte-order.h" #include "compiler.h" @@ -27,6 +26,7 @@ #include "nx-match.h" #include "ofp-util.h" #include "ofpbuf.h" +#include "util.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(ofp_actions); @@ -36,7 +36,7 @@ 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, +output_from_openflow10(const struct ofp10_action_output *oao, struct ofpbuf *out) { struct ofpact_output *output; @@ -49,7 +49,7 @@ output_from_openflow10(const struct ofp_action_output *oao, } static enum ofperr -enqueue_from_openflow10(const struct ofp_action_enqueue *oae, +enqueue_from_openflow10(const struct ofp10_action_enqueue *oae, struct ofpbuf *out) { struct ofpact_enqueue *enqueue; @@ -135,6 +135,23 @@ controller_from_openflow(const struct nx_action_controller *nac, oc->reason = nac->reason; } +static enum ofperr +metadata_from_nxast(const struct nx_action_write_metadata *nawm, + struct ofpbuf *out) +{ + struct ofpact_metadata *om; + + if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + om = ofpact_put_WRITE_METADATA(out); + om->metadata = nawm->metadata; + om->mask = nawm->mask; + + return 0; +} + static void note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) { @@ -148,6 +165,58 @@ note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out) memcpy(note->data, nan->note, length); } +static enum ofperr +dec_ttl_from_openflow(struct ofpbuf *out, enum ofputil_action_code compat) +{ + uint16_t id = 0; + struct ofpact_cnt_ids *ids; + enum ofperr error = 0; + + ids = ofpact_put_DEC_TTL(out); + ids->ofpact.compat = compat; + ids->n_controllers = 1; + ofpbuf_put(out, &id, sizeof id); + ids = out->l2; + ofpact_update_len(out, &ids->ofpact); + return error; +} + +static enum ofperr +dec_ttl_cnt_ids_from_openflow(const struct nx_action_cnt_ids *nac_ids, + struct ofpbuf *out) +{ + struct ofpact_cnt_ids *ids; + size_t ids_size; + int i; + + ids = ofpact_put_DEC_TTL(out); + ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS; + ids->n_controllers = ntohs(nac_ids->n_controllers); + ids_size = ntohs(nac_ids->len) - sizeof *nac_ids; + + if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + if (ids_size < ids->n_controllers * sizeof(ovs_be16)) { + VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %zu bytes " + "allocated for controller ids. %zu bytes are required for " + "%"PRIu16" controllers.", ids_size, + ids->n_controllers * sizeof(ovs_be16), ids->n_controllers); + return OFPERR_OFPBAC_BAD_LEN; + } + + for (i = 0; i < ids->n_controllers; i++) { + uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]); + ofpbuf_put(out, &id, sizeof id); + } + + ids = out->l2; + ofpact_update_len(out, &ids->ofpact); + + return 0; +} + static enum ofperr decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code) { @@ -216,14 +285,153 @@ decode_openflow10_action(const union ofp_action *a, } static enum ofperr -ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out) +ofpact_from_nxast(const union ofp_action *a, enum ofputil_action_code code, + 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; + const struct nx_action_write_metadata *nawm; struct ofpact_tunnel *tunnel; + enum ofperr error = 0; + + switch (code) { + case OFPUTIL_ACTION_INVALID: +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM: +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: +#include "ofp-util.def" + NOT_REACHED(); + + 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_WRITE_METADATA: + nawm = (const struct nx_action_write_metadata *) a; + error = metadata_from_nxast(nawm, out); + 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_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: + error = dec_ttl_from_openflow(out, code); + break; + + case OFPUTIL_NXAST_DEC_TTL_CNT_IDS: + error = dec_ttl_cnt_ids_from_openflow( + (const struct nx_action_cnt_ids *) a, 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; + + case OFPUTIL_NXAST_PUSH_MPLS: { + struct nx_action_push_mpls *nxapm = (struct nx_action_push_mpls *)a; + if (!eth_type_mpls(nxapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_PUSH_MPLS(out)->ethertype = nxapm->ethertype; + break; + } + + case OFPUTIL_NXAST_SET_MPLS_TTL: { + struct nx_action_mpls_ttl *nxamt = (struct nx_action_mpls_ttl *)a; + ofpact_put_SET_MPLS_TTL(out)->ttl = nxamt->ttl; + break; + } + + case OFPUTIL_NXAST_DEC_MPLS_TTL: + ofpact_put_DEC_MPLS_TTL(out); + break; + + case OFPUTIL_NXAST_POP_MPLS: { + struct nx_action_pop_mpls *nxapm = (struct nx_action_pop_mpls *)a; + if (eth_type_mpls(nxapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_POP_MPLS(out)->ethertype = nxapm->ethertype; + break; + } + } + + return error; +} + +static enum ofperr +ofpact_from_openflow10(const union ofp_action *a, struct ofpbuf *out) +{ enum ofputil_action_code code; enum ofperr error; @@ -234,11 +442,12 @@ ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out) switch (code) { case OFPUTIL_ACTION_INVALID: +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: +#include "ofp-util.def" NOT_REACHED(); case OFPUTIL_OFPAT10_OUTPUT: - return output_from_openflow10((const struct ofp_action_output *) a, - out); + return output_from_openflow10(&a->output10, out); case OFPUTIL_OFPAT10_SET_VLAN_VID: if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { @@ -293,191 +502,609 @@ ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out) break; case OFPUTIL_OFPAT10_ENQUEUE: - error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a, + error = enqueue_from_openflow10((const struct ofp10_action_enqueue *) a, out); break; - case OFPUTIL_NXAST_RESUBMIT: - resubmit_from_openflow((const struct nx_action_resubmit *) a, out); - break; +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: +#include "ofp-util.def" + return ofpact_from_nxast(a, code, out); + } - 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); + 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 void +log_bad_action(const union ofp_action *actions, size_t n_actions, size_t ofs, + enum ofperr error) +{ + if (!VLOG_DROP_WARN(&rl)) { + struct ds s; + + ds_init(&s); + ds_put_hex_dump(&s, actions, n_actions * sizeof *actions, 0, false); + VLOG_WARN("bad action at offset %#zx (%s):\n%s", + ofs * sizeof *actions, ofperr_get_name(error), ds_cstr(&s)); + ds_destroy(&s); + } +} + +static enum ofperr +ofpacts_from_openflow(const union ofp_action *in, size_t n_in, + struct ofpbuf *out, + enum ofperr (*ofpact_from_openflow)( + const union ofp_action *a, struct ofpbuf *out)) +{ + const union ofp_action *a; + size_t left; + + ACTION_FOR_EACH (a, left, in, n_in) { + enum ofperr error = ofpact_from_openflow(a, out); + if (error) { + log_bad_action(in, n_in, a - in, error); + return error; + } + } + if (left) { + enum ofperr error = OFPERR_OFPBAC_BAD_LEN; + log_bad_action(in, n_in, n_in - left, error); + return error; + } + + ofpact_pad(out); + return 0; +} + +static enum ofperr +ofpacts_from_openflow10(const union ofp_action *in, size_t n_in, + struct ofpbuf *out) +{ + return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow10); +} + +static enum ofperr +ofpacts_pull_actions(struct ofpbuf *openflow, unsigned int actions_len, + struct ofpbuf *ofpacts, + enum ofperr (*translate)(const union ofp_action *actions, + size_t n_actions, + 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 = translate(actions, actions_len / OFP_ACTION_ALIGN, ofpacts); + if (error) { + ofpbuf_clear(ofpacts); + return error; + } + + error = ofpacts_verify(ofpacts->data, ofpacts->size); + if (error) { + ofpbuf_clear(ofpacts); + } + return error; +} + +/* Attempts to convert 'actions_len' bytes of OpenFlow 1.0 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. + * + * The parsed actions are valid generically, but they may not be valid in a + * specific context. For example, port numbers up to OFPP_MAX are valid + * generically, but specific datapaths may only support port numbers in a + * smaller range. Use ofpacts_check() to additional check whether actions are + * valid in a specific context. */ +enum ofperr +ofpacts_pull_openflow10(struct ofpbuf *openflow, unsigned int actions_len, + struct ofpbuf *ofpacts) +{ + return ofpacts_pull_actions(openflow, actions_len, ofpacts, + ofpacts_from_openflow10); +} + +/* OpenFlow 1.1 actions. */ + +/* 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_openflow11_action(const union ofp_action *a, + enum ofputil_action_code *code) +{ + uint16_t len; + + switch (a->type) { + case CONSTANT_HTONS(OFPAT11_EXPERIMENTER): + return decode_nxast_action(a, code); + +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ + case CONSTANT_HTONS(ENUM): \ + len = ntohs(a->header.len); \ + 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" + + default: + return OFPERR_OFPBAC_BAD_TYPE; + } +} + +static enum ofperr +output_from_openflow11(const struct ofp11_action_output *oao, + struct ofpbuf *out) +{ + struct ofpact_output *output; + enum ofperr error; + + output = ofpact_put_OUTPUT(out); + output->max_len = ntohs(oao->max_len); + + error = ofputil_port_from_ofp11(oao->port, &output->port); + if (error) { + return error; + } + + return ofputil_check_output_port(output->port, OFPP_MAX); +} + +static enum ofperr +ofpact_from_openflow11(const union ofp_action *a, struct ofpbuf *out) +{ + enum ofputil_action_code code; + enum ofperr error; + + error = decode_openflow11_action(a, &code); + if (error) { + return error; + } + + switch (code) { + case OFPUTIL_ACTION_INVALID: +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM: +#include "ofp-util.def" + NOT_REACHED(); + + case OFPUTIL_OFPAT11_OUTPUT: + return output_from_openflow11((const struct ofp11_action_output *) a, + out); + + case OFPUTIL_OFPAT11_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_NXAST_SET_QUEUE: - nasq = (const struct nx_action_set_queue *) a; - ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id); + case OFPUTIL_OFPAT11_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_NXAST_POP_QUEUE: - ofpact_put_POP_QUEUE(out); + case OFPUTIL_OFPAT11_PUSH_VLAN: + if (((const struct ofp11_action_push *)a)->ethertype != + htons(ETH_TYPE_VLAN_8021Q)) { + /* XXX 802.1AD(QinQ) isn't supported at the moment */ + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_PUSH_VLAN(out); break; - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_reg_move_from_openflow( - (const struct nx_action_reg_move *) a, out); + case OFPUTIL_OFPAT11_POP_VLAN: + ofpact_put_STRIP_VLAN(out); break; - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_reg_load_from_openflow( - (const struct nx_action_reg_load *) a, out); + case OFPUTIL_OFPAT11_SET_QUEUE: + ofpact_put_SET_QUEUE(out)->queue_id = + ntohl(((const struct ofp11_action_set_queue *)a)->queue_id); break; - case OFPUTIL_NXAST_NOTE: - nan = (const struct nx_action_note *) a; - note_from_openflow(nan, out); + case OFPUTIL_OFPAT11_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_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); + case OFPUTIL_OFPAT11_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_NXAST_MULTIPATH: - error = multipath_from_openflow((const struct nx_action_multipath *) a, - ofpact_put_MULTIPATH(out)); + case OFPUTIL_OFPAT11_DEC_NW_TTL: + dec_ttl_from_openflow(out, code); break; - case OFPUTIL_NXAST_AUTOPATH: - error = autopath_from_openflow((const struct nx_action_autopath *) a, - ofpact_put_AUTOPATH(out)); + case OFPUTIL_OFPAT11_SET_NW_SRC: + ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr; break; - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_from_openflow((const struct nx_action_bundle *) a, out); + case OFPUTIL_OFPAT11_SET_NW_DST: + ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr; break; - case OFPUTIL_NXAST_OUTPUT_REG: - error = output_reg_from_openflow( - (const struct nx_action_output_reg *) a, out); + case OFPUTIL_OFPAT11_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_NXAST_RESUBMIT_TABLE: - nar = (const struct nx_action_resubmit *) a; - error = resubmit_table_from_openflow(nar, out); + case OFPUTIL_OFPAT11_SET_TP_SRC: + ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port); break; - case OFPUTIL_NXAST_LEARN: - error = learn_from_openflow((const struct nx_action_learn *) a, out); + case OFPUTIL_OFPAT11_SET_TP_DST: + ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port); break; - case OFPUTIL_NXAST_EXIT: - ofpact_put_EXIT(out); + case OFPUTIL_OFPAT12_SET_FIELD: + return nxm_reg_load_from_openflow12_set_field( + (const struct ofp12_action_set_field *)a, out); + + case OFPUTIL_OFPAT11_SET_MPLS_TTL: { + struct ofp11_action_mpls_ttl *oamt = (struct ofp11_action_mpls_ttl *)a; + ofpact_put_SET_MPLS_TTL(out)->ttl = oamt->mpls_ttl; break; + } - case OFPUTIL_NXAST_DEC_TTL: - ofpact_put_DEC_TTL(out); + case OFPUTIL_OFPAT11_DEC_MPLS_TTL: + ofpact_put_DEC_MPLS_TTL(out); break; - case OFPUTIL_NXAST_FIN_TIMEOUT: - fin_timeout_from_openflow( - (const struct nx_action_fin_timeout *) a, out); + case OFPUTIL_OFPAT11_PUSH_MPLS: { + struct ofp11_action_push *oap = (struct ofp11_action_push *)a; + if (!eth_type_mpls(oap->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_PUSH_MPLS(out)->ethertype = oap->ethertype; break; + } - case OFPUTIL_NXAST_CONTROLLER: - controller_from_openflow((const struct nx_action_controller *) a, out); + case OFPUTIL_OFPAT11_POP_MPLS: { + struct ofp11_action_pop_mpls *oapm = (struct ofp11_action_pop_mpls *)a; + if (eth_type_mpls(oapm->ethertype)) { + return OFPERR_OFPBAC_BAD_ARGUMENT; + } + ofpact_put_POP_MPLS(out)->ethertype = oapm->ethertype; break; } +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM: +#include "ofp-util.def" + return ofpact_from_nxast(a, code, out); + } + return error; } -static inline union ofp_action * -action_next(const union ofp_action *a) +static enum ofperr +ofpacts_from_openflow11(const union ofp_action *in, size_t n_in, + struct ofpbuf *out) { - return ((union ofp_action *) (void *) - ((uint8_t *) a + ntohs(a->header.len))); + return ofpacts_from_openflow(in, n_in, out, ofpact_from_openflow11); +} + +/* OpenFlow 1.1 instructions. */ + +#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ + static inline const struct STRUCT * \ + instruction_get_##ENUM(const struct ofp11_instruction *inst)\ + { \ + ovs_assert(inst->type == htons(ENUM)); \ + return (struct STRUCT *)inst; \ + } \ + \ + static inline void \ + instruction_init_##ENUM(struct STRUCT *s) \ + { \ + memset(s, 0, sizeof *s); \ + s->type = htons(ENUM); \ + s->len = htons(sizeof *s); \ + } \ + \ + static inline struct STRUCT * \ + instruction_put_##ENUM(struct ofpbuf *buf) \ + { \ + struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \ + instruction_init_##ENUM(s); \ + return s; \ + } +OVS_INSTRUCTIONS +#undef DEFINE_INST + +struct instruction_type_info { + enum ovs_instruction_type type; + const char *name; +}; + +static const struct instruction_type_info inst_info[] = { +#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) {OVSINST_##ENUM, NAME}, +OVS_INSTRUCTIONS +#undef DEFINE_INST +}; + +const char * +ofpact_instruction_name_from_type(enum ovs_instruction_type type) +{ + return inst_info[type].name; +} + +int +ofpact_instruction_type_from_name(const char *name) +{ + const struct instruction_type_info *p; + for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) { + if (!strcasecmp(name, p->name)) { + return p->type; + } + } + return -1; +} + +static inline struct ofp11_instruction * +instruction_next(const struct ofp11_instruction *inst) +{ + return ((struct ofp11_instruction *) (void *) + ((uint8_t *) inst + ntohs(inst->len))); } static inline bool -action_is_valid(const union ofp_action *a, size_t n_actions) +instruction_is_valid(const struct ofp11_instruction *inst, + size_t n_instructions) { - uint16_t len = ntohs(a->header.len); - return (!(len % OFP_ACTION_ALIGN) - && len >= sizeof *a - && len / sizeof *a <= n_actions); + uint16_t len = ntohs(inst->len); + return (!(len % OFP11_INSTRUCTION_ALIGN) + && len >= sizeof *inst + && len / sizeof *inst <= n_instructions); } -/* 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))) +/* This macro is careful to check for instructions with bad lengths. */ +#define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS) \ + for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS); \ + (LEFT) > 0 && instruction_is_valid(ITER, LEFT); \ + ((LEFT) -= (ntohs((ITER)->len) \ + / sizeof(struct ofp11_instruction)), \ + (ITER) = instruction_next(ITER))) static enum ofperr -ofpact_from_openflow10(const union ofp_action *in, size_t n_in, - struct ofpbuf *out) +decode_openflow11_instruction(const struct ofp11_instruction *inst, + enum ovs_instruction_type *type) { - const union ofp_action *a; + uint16_t len = ntohs(inst->len); + + switch (inst->type) { + case CONSTANT_HTONS(OFPIT11_EXPERIMENTER): + return OFPERR_OFPBIC_BAD_EXPERIMENTER; + +#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \ + case CONSTANT_HTONS(ENUM): \ + if (EXTENSIBLE \ + ? len >= sizeof(struct STRUCT) \ + : len == sizeof(struct STRUCT)) { \ + *type = OVSINST_##ENUM; \ + return 0; \ + } else { \ + return OFPERR_OFPBIC_BAD_LEN; \ + } +OVS_INSTRUCTIONS +#undef DEFINE_INST + + default: + return OFPERR_OFPBIC_UNKNOWN_INST; + } +} + +static enum ofperr +decode_openflow11_instructions(const struct ofp11_instruction insts[], + size_t n_insts, + const struct ofp11_instruction *out[]) +{ + const struct ofp11_instruction *inst; size_t left; - ACTION_FOR_EACH (a, left, in, n_in) { - enum ofperr error = ofpact_from_openflow10__(a, out); + memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out); + INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) { + enum ovs_instruction_type type; + enum ofperr error; + + error = decode_openflow11_instruction(inst, &type); if (error) { - VLOG_WARN_RL(&rl, "bad action at offset %td (%s)", - (a - in) * sizeof *a, ofperr_get_name(error)); return error; } + + if (out[type]) { + return OFPERR_OFPBAC_UNSUPPORTED_ORDER; /* No specific code for + * a duplicate instruction + * exist */ + } + out[type] = inst; } + if (left) { - VLOG_WARN_RL(&rl, "bad action format at offset %zu", - (n_in - left) * sizeof *a); - return OFPERR_OFPBAC_BAD_LEN; + VLOG_WARN_RL(&rl, "bad instruction format at offset %zu", + (n_insts - left) * sizeof *inst); + return OFPERR_OFPBIC_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. +static void +get_actions_from_instruction(const struct ofp11_instruction *inst, + const union ofp_action **actions, + size_t *n_actions) +{ + *actions = (const union ofp_action *) (inst + 1); + *n_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN; +} + +/* Attempts to convert 'actions_len' bytes of OpenFlow 1.1 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. + * + * In most places in OpenFlow 1.1 and 1.2, actions appear encapsulated in + * instructions, so you should call ofpacts_pull_openflow11_instructions() + * instead of this function. * - * This function does not check that the actions are valid in a given context. - * The caller should do so, with ofpacts_check(). */ + * The parsed actions are valid generically, but they may not be valid in a + * specific context. For example, port numbers up to OFPP_MAX are valid + * generically, but specific datapaths may only support port numbers in a + * smaller range. Use ofpacts_check() to additional check whether actions are + * valid in a specific context. */ +enum ofperr +ofpacts_pull_openflow11_actions(struct ofpbuf *openflow, + unsigned int actions_len, + struct ofpbuf *ofpacts) +{ + return ofpacts_pull_actions(openflow, actions_len, ofpacts, + ofpacts_from_openflow11); +} + enum ofperr -ofpacts_pull_openflow(struct ofpbuf *openflow, unsigned int actions_len, - struct ofpbuf *ofpacts) +ofpacts_pull_openflow11_instructions(struct ofpbuf *openflow, + unsigned int instructions_len, + struct ofpbuf *ofpacts) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); - const union ofp_action *actions; + const struct ofp11_instruction *instructions; + const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS]; 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; + if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) { + VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a " + "multiple of %d", + instructions_len, OFP11_INSTRUCTION_ALIGN); + error = OFPERR_OFPBIC_BAD_LEN; + goto exit; } - actions = ofpbuf_try_pull(openflow, actions_len); - if (actions == NULL) { - VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds " + instructions = ofpbuf_try_pull(openflow, instructions_len); + if (instructions == NULL) { + VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds " "remaining message length (%zu)", - actions_len, openflow->size); - return OFPERR_OFPBRC_BAD_LEN; + instructions_len, openflow->size); + error = OFPERR_OFPBIC_BAD_LEN; + goto exit; + } + + error = decode_openflow11_instructions( + instructions, instructions_len / OFP11_INSTRUCTION_ALIGN, + insts); + if (error) { + goto exit; + } + + if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) { + const union ofp_action *actions; + size_t n_actions; + + get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS], + &actions, &n_actions); + error = ofpacts_from_openflow11(actions, n_actions, ofpacts); + if (error) { + goto exit; + } + } + if (insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) { + instruction_get_OFPIT11_CLEAR_ACTIONS( + insts[OVSINST_OFPIT11_CLEAR_ACTIONS]); + ofpact_put_CLEAR_ACTIONS(ofpacts); + } + /* XXX Write-Actions */ + if (insts[OVSINST_OFPIT11_WRITE_METADATA]) { + const struct ofp11_instruction_write_metadata *oiwm; + struct ofpact_metadata *om; + + oiwm = (const struct ofp11_instruction_write_metadata *) + insts[OVSINST_OFPIT11_WRITE_METADATA]; + + om = ofpact_put_WRITE_METADATA(ofpacts); + om->metadata = oiwm->metadata; + om->mask = oiwm->metadata_mask; + } + if (insts[OVSINST_OFPIT11_GOTO_TABLE]) { + const struct ofp11_instruction_goto_table *oigt; + struct ofpact_goto_table *ogt; + + oigt = instruction_get_OFPIT11_GOTO_TABLE( + insts[OVSINST_OFPIT11_GOTO_TABLE]); + ogt = ofpact_put_GOTO_TABLE(ofpacts); + ogt->table_id = oigt->table_id; + } + + if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) { + error = OFPERR_OFPBIC_UNSUP_INST; + goto exit; } - error = ofpact_from_openflow10(actions, actions_len / OFP_ACTION_ALIGN, - ofpacts); + error = ofpacts_verify(ofpacts->data, ofpacts->size); +exit: if (error) { ofpbuf_clear(ofpacts); } - return 0; + return error; } static enum ofperr -ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) +ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports, + ovs_be16 *dl_type) { const struct ofpact_enqueue *enqueue; @@ -506,6 +1133,7 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: + case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: @@ -519,9 +1147,17 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) 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); + if (*dl_type != flow->dl_type) { + struct flow updated_flow = *flow; + updated_flow.dl_type = *dl_type; + return nxm_reg_load_check(ofpact_get_REG_LOAD(a), &updated_flow); + } else { + return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow); + } case OFPACT_DEC_TTL: + case OFPACT_SET_MPLS_TTL: + case OFPACT_DEC_MPLS_TTL: case OFPACT_SET_TUNNEL: case OFPACT_SET_QUEUE: case OFPACT_POP_QUEUE: @@ -535,13 +1171,23 @@ ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports) 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; + case OFPACT_PUSH_MPLS: + *dl_type = ofpact_get_PUSH_MPLS(a)->ethertype; + return 0; + + case OFPACT_POP_MPLS: + *dl_type = ofpact_get_POP_MPLS(a)->ethertype; + return 0; + + case OFPACT_CLEAR_ACTIONS: + case OFPACT_WRITE_METADATA: + case OFPACT_GOTO_TABLE: + return 0; + default: NOT_REACHED(); } @@ -555,9 +1201,10 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, const struct flow *flow, int max_ports) { const struct ofpact *a; + ovs_be16 dl_type = flow->dl_type; OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { - enum ofperr error = ofpact_check__(a, flow, max_ports); + enum ofperr error = ofpact_check__(a, flow, max_ports, &dl_type); if (error) { return error; } @@ -565,6 +1212,49 @@ ofpacts_check(const struct ofpact ofpacts[], size_t ofpacts_len, return 0; } + +/* Verifies that the 'ofpacts_len' bytes of actions in 'ofpacts' are + * in the appropriate order as defined by the OpenFlow spec. */ +enum ofperr +ofpacts_verify(const struct ofpact ofpacts[], size_t ofpacts_len) +{ + const struct ofpact *a; + enum ovs_instruction_type inst; + + inst = OVSINST_OFPIT11_APPLY_ACTIONS; + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + enum ovs_instruction_type next; + + if (a->type == OFPACT_CLEAR_ACTIONS) { + next = OVSINST_OFPIT11_CLEAR_ACTIONS; + } else if (a->type == OFPACT_WRITE_METADATA) { + next = OVSINST_OFPIT11_WRITE_METADATA; + } else if (a->type == OFPACT_GOTO_TABLE) { + next = OVSINST_OFPIT11_GOTO_TABLE; + } else { + next = OVSINST_OFPIT11_APPLY_ACTIONS; + } + + if (inst != OVSINST_OFPIT11_APPLY_ACTIONS && next <= inst) { + const char *name = ofpact_instruction_name_from_type(inst); + const char *next_name = ofpact_instruction_name_from_type(next); + + if (next == inst) { + VLOG_WARN("duplicate %s instruction not allowed, for OpenFlow " + "1.1+ compatibility", name); + } else { + VLOG_WARN("invalid instruction ordering: %s must appear " + "before %s, for OpenFlow 1.1+ compatibility", + next_name, name); + } + return OFPERR_OFPBAC_UNSUPPORTED_ORDER; + } + + inst = next; + } + + return 0; +} /* Converting ofpacts to Nicira OpenFlow extensions. */ @@ -610,6 +1300,17 @@ ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel, } } +static void +ofpact_write_metadata_to_nxast(const struct ofpact_metadata *om, + struct ofpbuf *out) +{ + struct nx_action_write_metadata *nawm; + + nawm = ofputil_put_NXAST_WRITE_METADATA(out); + nawm->metadata = om->metadata; + nawm->mask = om->mask; +} + static void ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out) { @@ -644,6 +1345,29 @@ ofpact_controller_to_nxast(const struct ofpact_controller *oc, nac->reason = oc->reason; } +static void +ofpact_dec_ttl_to_nxast(const struct ofpact_cnt_ids *oc_ids, + struct ofpbuf *out) +{ + if (oc_ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL) { + ofputil_put_NXAST_DEC_TTL(out); + } else { + struct nx_action_cnt_ids *nac_ids = + ofputil_put_NXAST_DEC_TTL_CNT_IDS(out); + int ids_len = ROUND_UP(2 * oc_ids->n_controllers, OFP_ACTION_ALIGN); + ovs_be16 *ids; + size_t i; + + nac_ids->len = htons(ntohs(nac_ids->len) + ids_len); + nac_ids->n_controllers = htons(oc_ids->n_controllers); + + ids = ofpbuf_put_zeros(out, ids_len); + for (i = 0; i < oc_ids->n_controllers; i++) { + ids[i] = htons(oc_ids->cnt_ids[i]); + } + } +} + static void ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout, struct ofpbuf *out) @@ -678,13 +1402,26 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) break; case OFPACT_DEC_TTL: - ofputil_put_NXAST_DEC_TTL(out); + ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out); + break; + + case OFPACT_SET_MPLS_TTL: + ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl + = ofpact_get_SET_MPLS_TTL(a)->ttl; + break; + + case OFPACT_DEC_MPLS_TTL: + ofputil_put_NXAST_DEC_MPLS_TTL(out); break; case OFPACT_SET_TUNNEL: ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out); break; + case OFPACT_WRITE_METADATA: + ofpact_write_metadata_to_nxast(ofpact_get_WRITE_METADATA(a), out); + break; + case OFPACT_SET_QUEUE: ofputil_put_NXAST_SET_QUEUE(out)->queue_id = htonl(ofpact_get_SET_QUEUE(a)->queue_id); @@ -710,10 +1447,6 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) 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; @@ -722,11 +1455,22 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) ofputil_put_NXAST_EXIT(out); break; + case OFPACT_PUSH_MPLS: + ofputil_put_NXAST_PUSH_MPLS(out)->ethertype = + ofpact_get_PUSH_MPLS(a)->ethertype; + break; + + case OFPACT_POP_MPLS: + ofputil_put_NXAST_POP_MPLS(out)->ethertype = + ofpact_get_POP_MPLS(a)->ethertype; + break; + case OFPACT_OUTPUT: case OFPACT_ENQUEUE: case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: + case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: @@ -734,6 +1478,8 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out) case OFPACT_SET_IPV4_DSCP: case OFPACT_SET_L4_SRC_PORT: case OFPACT_SET_L4_DST_PORT: + case OFPACT_CLEAR_ACTIONS: + case OFPACT_GOTO_TABLE: NOT_REACHED(); } } @@ -744,7 +1490,7 @@ static void ofpact_output_to_openflow10(const struct ofpact_output *output, struct ofpbuf *out) { - struct ofp_action_output *oao; + struct ofp10_action_output *oao; oao = ofputil_put_OFPAT10_OUTPUT(out); oao->port = htons(output->port); @@ -755,7 +1501,7 @@ static void ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue, struct ofpbuf *out) { - struct ofp_action_enqueue *oae; + struct ofp10_action_enqueue *oae; oae = ofputil_put_OFPAT10_ENQUEUE(out); oae->port = htons(enqueue->port); @@ -823,33 +1569,43 @@ ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out) = htons(ofpact_get_SET_L4_DST_PORT(a)->port); break; + case OFPACT_PUSH_VLAN: + case OFPACT_CLEAR_ACTIONS: + case OFPACT_GOTO_TABLE: + /* XXX */ + 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_MPLS_TTL: + case OFPACT_DEC_MPLS_TTL: case OFPACT_SET_TUNNEL: + case OFPACT_WRITE_METADATA: 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: + case OFPACT_PUSH_MPLS: + case OFPACT_POP_MPLS: ofpact_to_nxast(a, out); break; } } -/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow +/* Converts the 'ofpacts_len' bytes of ofpacts in 'ofpacts' into OpenFlow 1.0 * 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) +ofpacts_put_openflow10(const struct ofpact ofpacts[], size_t ofpacts_len, + struct ofpbuf *openflow) { const struct ofpact *a; @@ -858,6 +1614,232 @@ ofpacts_to_openflow(const struct ofpact ofpacts[], size_t ofpacts_len, } } +/* Converting ofpacts to OpenFlow 1.1. */ + +static void +ofpact_output_to_openflow11(const struct ofpact_output *output, + struct ofpbuf *out) +{ + struct ofp11_action_output *oao; + + oao = ofputil_put_OFPAT11_OUTPUT(out); + oao->port = ofputil_port_to_ofp11(output->port); + oao->max_len = htons(output->max_len); +} + +static void +ofpact_dec_ttl_to_openflow11(const struct ofpact_cnt_ids *dec_ttl, + struct ofpbuf *out) +{ + if (dec_ttl->n_controllers == 1 && dec_ttl->cnt_ids[0] == 0 + && (!dec_ttl->ofpact.compat || + dec_ttl->ofpact.compat == OFPUTIL_OFPAT11_DEC_NW_TTL)) { + ofputil_put_OFPAT11_DEC_NW_TTL(out); + } else { + ofpact_dec_ttl_to_nxast(dec_ttl, out); + } +} + +static void +ofpact_to_openflow11(const struct ofpact *a, struct ofpbuf *out) +{ + switch (a->type) { + case OFPACT_OUTPUT: + return ofpact_output_to_openflow11(ofpact_get_OUTPUT(a), out); + + case OFPACT_ENQUEUE: + /* XXX */ + break; + + case OFPACT_SET_VLAN_VID: + ofputil_put_OFPAT11_SET_VLAN_VID(out)->vlan_vid + = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid); + break; + + case OFPACT_SET_VLAN_PCP: + ofputil_put_OFPAT11_SET_VLAN_PCP(out)->vlan_pcp + = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp; + break; + + case OFPACT_STRIP_VLAN: + ofputil_put_OFPAT11_POP_VLAN(out); + break; + + case OFPACT_PUSH_VLAN: + /* XXX ETH_TYPE_VLAN_8021AD case */ + ofputil_put_OFPAT11_PUSH_VLAN(out)->ethertype = + htons(ETH_TYPE_VLAN_8021Q); + break; + + case OFPACT_SET_QUEUE: + ofputil_put_OFPAT11_SET_QUEUE(out)->queue_id + = htonl(ofpact_get_SET_QUEUE(a)->queue_id); + break; + + case OFPACT_SET_ETH_SRC: + memcpy(ofputil_put_OFPAT11_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_OFPAT11_SET_DL_DST(out)->dl_addr, + ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN); + break; + + case OFPACT_SET_IPV4_SRC: + ofputil_put_OFPAT11_SET_NW_SRC(out)->nw_addr + = ofpact_get_SET_IPV4_SRC(a)->ipv4; + break; + + case OFPACT_SET_IPV4_DST: + ofputil_put_OFPAT11_SET_NW_DST(out)->nw_addr + = ofpact_get_SET_IPV4_DST(a)->ipv4; + break; + + case OFPACT_SET_IPV4_DSCP: + ofputil_put_OFPAT11_SET_NW_TOS(out)->nw_tos + = ofpact_get_SET_IPV4_DSCP(a)->dscp; + break; + + case OFPACT_SET_L4_SRC_PORT: + ofputil_put_OFPAT11_SET_TP_SRC(out)->tp_port + = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); + break; + + case OFPACT_SET_L4_DST_PORT: + ofputil_put_OFPAT11_SET_TP_DST(out)->tp_port + = htons(ofpact_get_SET_L4_DST_PORT(a)->port); + break; + + case OFPACT_DEC_TTL: + ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out); + break; + + case OFPACT_SET_MPLS_TTL: + ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl + = ofpact_get_SET_MPLS_TTL(a)->ttl; + break; + + case OFPACT_DEC_MPLS_TTL: + ofputil_put_OFPAT11_DEC_MPLS_TTL(out); + break; + + case OFPACT_WRITE_METADATA: + /* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */ + break; + + case OFPACT_PUSH_MPLS: + ofputil_put_OFPAT11_PUSH_MPLS(out)->ethertype = + ofpact_get_PUSH_MPLS(a)->ethertype; + break; + + case OFPACT_POP_MPLS: + ofputil_put_OFPAT11_POP_MPLS(out)->ethertype = + ofpact_get_POP_MPLS(a)->ethertype; + + break; + + case OFPACT_CLEAR_ACTIONS: + case OFPACT_GOTO_TABLE: + NOT_REACHED(); + + case OFPACT_CONTROLLER: + case OFPACT_OUTPUT_REG: + case OFPACT_BUNDLE: + case OFPACT_REG_MOVE: + case OFPACT_REG_LOAD: + case OFPACT_SET_TUNNEL: + case OFPACT_POP_QUEUE: + case OFPACT_FIN_TIMEOUT: + case OFPACT_RESUBMIT: + case OFPACT_LEARN: + case OFPACT_MULTIPATH: + case OFPACT_NOTE: + case OFPACT_EXIT: + ofpact_to_nxast(a, out); + break; + } +} + +/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow + * 1.1 actions in 'openflow', appending the actions to any existing data in + * 'openflow'. */ +size_t +ofpacts_put_openflow11_actions(const struct ofpact ofpacts[], + size_t ofpacts_len, struct ofpbuf *openflow) +{ + const struct ofpact *a; + size_t start_size = openflow->size; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + ofpact_to_openflow11(a, openflow); + } + + return openflow->size - start_size; +} + +static void +ofpacts_update_instruction_actions(struct ofpbuf *openflow, size_t ofs) +{ + struct ofp11_instruction_actions *oia; + + /* Update the instruction's length (or, if it's empty, delete it). */ + oia = ofpbuf_at_assert(openflow, ofs, sizeof *oia); + if (openflow->size > ofs + sizeof *oia) { + oia->len = htons(openflow->size - ofs); + } else { + openflow->size = ofs; + } +} + +void +ofpacts_put_openflow11_instructions(const struct ofpact ofpacts[], + size_t ofpacts_len, + struct ofpbuf *openflow) +{ + const struct ofpact *a; + + OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) { + /* XXX Write-Actions */ + + if (a->type == OFPACT_CLEAR_ACTIONS) { + instruction_put_OFPIT11_CLEAR_ACTIONS(openflow); + } else if (a->type == OFPACT_GOTO_TABLE) { + struct ofp11_instruction_goto_table *oigt; + + oigt = instruction_put_OFPIT11_GOTO_TABLE(openflow); + oigt->table_id = ofpact_get_GOTO_TABLE(a)->table_id; + memset(oigt->pad, 0, sizeof oigt->pad); + } else if (a->type == OFPACT_WRITE_METADATA) { + const struct ofpact_metadata *om; + struct ofp11_instruction_write_metadata *oiwm; + + om = ofpact_get_WRITE_METADATA(a); + oiwm = instruction_put_OFPIT11_WRITE_METADATA(openflow); + oiwm->metadata = om->metadata; + oiwm->metadata_mask = om->mask; + } else if (!ofpact_is_instruction(a)) { + /* Apply-actions */ + const size_t ofs = openflow->size; + const size_t ofpacts_len_left = + (uint8_t*)ofpact_end(ofpacts, ofpacts_len) - (uint8_t*)a; + const struct ofpact *action; + const struct ofpact *processed = a; + + instruction_put_OFPIT11_APPLY_ACTIONS(openflow); + OFPACT_FOR_EACH(action, a, ofpacts_len_left) { + if (ofpact_is_instruction(action)) { + break; + } + ofpact_to_openflow11(action, openflow); + processed = action; + } + ofpacts_update_instruction_actions(openflow, ofs); + a = processed; + } + } +} + /* Returns true if 'action' outputs to 'port', false otherwise. */ static bool ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) @@ -875,6 +1857,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_SET_VLAN_VID: case OFPACT_SET_VLAN_PCP: case OFPACT_STRIP_VLAN: + case OFPACT_PUSH_VLAN: case OFPACT_SET_ETH_SRC: case OFPACT_SET_ETH_DST: case OFPACT_SET_IPV4_SRC: @@ -885,16 +1868,22 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port) case OFPACT_REG_MOVE: case OFPACT_REG_LOAD: case OFPACT_DEC_TTL: + case OFPACT_SET_MPLS_TTL: + case OFPACT_DEC_MPLS_TTL: case OFPACT_SET_TUNNEL: + case OFPACT_WRITE_METADATA: 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: + case OFPACT_PUSH_MPLS: + case OFPACT_POP_MPLS: + case OFPACT_CLEAR_ACTIONS: + case OFPACT_GOTO_TABLE: default: return false; } @@ -940,6 +1929,25 @@ print_note(const struct ofpact_note *note, struct ds *string) } } +static void +print_dec_ttl(const struct ofpact_cnt_ids *ids, + struct ds *s) +{ + size_t i; + + ds_put_cstr(s, "dec_ttl"); + if (ids->ofpact.compat == OFPUTIL_NXAST_DEC_TTL_CNT_IDS) { + ds_put_cstr(s, "("); + for (i = 0; i < ids->n_controllers; i++) { + if (i) { + ds_put_cstr(s, ","); + } + ds_put_format(s, "%"PRIu16, ids->cnt_ids[i]); + } + ds_put_cstr(s, ")"); + } +} + static void print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout, struct ds *s) @@ -962,8 +1970,8 @@ 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_metadata *metadata; const struct ofpact_tunnel *tunnel; uint16_t port; @@ -1035,6 +2043,11 @@ ofpact_format(const struct ofpact *a, struct ds *s) ds_put_cstr(s, "strip_vlan"); break; + case OFPACT_PUSH_VLAN: + /* XXX 802.1AD case*/ + ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q); + 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)); @@ -1047,12 +2060,12 @@ ofpact_format(const struct ofpact *a, struct ds *s) case OFPACT_SET_IPV4_SRC: ds_put_format(s, "mod_nw_src:"IP_FMT, - IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4)); + 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)); + IP_ARGS(ofpact_get_SET_IPV4_DST(a)->ipv4)); break; case OFPACT_SET_IPV4_DSCP: @@ -1076,7 +2089,16 @@ ofpact_format(const struct ofpact *a, struct ds *s) break; case OFPACT_DEC_TTL: - ds_put_cstr(s, "dec_ttl"); + print_dec_ttl(ofpact_get_DEC_TTL(a), s); + break; + + case OFPACT_SET_MPLS_TTL: + ds_put_format(s, "set_mpls_ttl(%"PRIu8")", + ofpact_get_SET_MPLS_TTL(a)->ttl); + break; + + case OFPACT_DEC_MPLS_TTL: + ds_put_cstr(s, "dec_mpls_ttl"); break; case OFPACT_SET_TUNNEL: @@ -1103,7 +2125,8 @@ ofpact_format(const struct ofpact *a, struct ds *s) 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); + ds_put_cstr(s, "resubmit:"); + ofputil_format_port(resubmit->in_port, s); } else { ds_put_format(s, "resubmit("); if (resubmit->in_port != OFPP_IN_PORT) { @@ -1125,20 +2148,47 @@ ofpact_format(const struct ofpact *a, struct ds *s) 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_PUSH_MPLS: + ds_put_format(s, "push_mpls:0x%04"PRIx16, + ntohs(ofpact_get_PUSH_MPLS(a)->ethertype)); + break; + + case OFPACT_POP_MPLS: + ds_put_format(s, "pop_mpls:0x%04"PRIx16, + ntohs(ofpact_get_POP_MPLS(a)->ethertype)); + break; + case OFPACT_EXIT: ds_put_cstr(s, "exit"); break; + + case OFPACT_CLEAR_ACTIONS: + ds_put_format(s, "%s", + ofpact_instruction_name_from_type( + OVSINST_OFPIT11_CLEAR_ACTIONS)); + break; + + case OFPACT_WRITE_METADATA: + metadata = ofpact_get_WRITE_METADATA(a); + ds_put_format(s, "%s:%#"PRIx64, + ofpact_instruction_name_from_type( + OVSINST_OFPIT11_WRITE_METADATA), + ntohll(metadata->metadata)); + if (metadata->mask != htonll(UINT64_MAX)) { + ds_put_format(s, "/%#"PRIx64, ntohll(metadata->mask)); + } + break; + + case OFPACT_GOTO_TABLE: + ds_put_format(s, "%s:%"PRIu8, + ofpact_instruction_name_from_type( + OVSINST_OFPIT11_GOTO_TABLE), + ofpact_get_GOTO_TABLE(a)->table_id); + break; } } @@ -1158,6 +2208,8 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len, if (a != ofpacts) { ds_put_cstr(string, ","); } + + /* XXX write-actions */ ofpact_format(a, string); } } @@ -1194,7 +2246,7 @@ ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len) void ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact) { - assert(ofpact == ofpacts->l2); + ovs_assert(ofpact == ofpacts->l2); ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact; } @@ -1216,3 +2268,15 @@ ofpact_pad(struct ofpbuf *ofpacts) ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem); } } + +void +ofpact_set_field_init(struct ofpact_reg_load *load, const struct mf_field *mf, + const void *src) +{ + load->ofpact.compat = OFPUTIL_OFPAT12_SET_FIELD; + load->dst.field = mf; + load->dst.ofs = 0; + load->dst.n_bits = mf->n_bits; + bitwise_copy(src, mf->n_bytes, load->dst.ofs, + &load->subvalue, sizeof load->subvalue, 0, mf->n_bits); +}