Example #1
0
static void
fail_open_recover(struct fail_open *fo)
{
    struct cls_rule rule;

    VLOG_WARN("No longer in fail-open mode");
    fo->last_disconn_secs = 0;
    fo->next_bogus_packet_in = LLONG_MAX;

    cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
    ofproto_delete_flow(fo->ofproto, &rule);
}
Example #2
0
void
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 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);

        cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
        ofproto_add_flow(fo->ofproto, &rule, &action, 1);
    }
}
Example #3
0
void
learn_format(const struct nx_action_learn *learn, struct ds *s)
{
    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->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
        ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
    }
    if (learn->fin_idle_timeout) {
        ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
                      ntohs(learn->fin_idle_timeout));
    }
    if (learn->fin_hard_timeout) {
        ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
                      ntohs(learn->fin_hard_timeout));
    }
    if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
        ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
    }
    if (learn->flags & htons(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 != 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;
        }

        ds_put_char(s, ',');

        switch (src_type | dst_type) {
        case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
            if (dst.field && dst.ofs == 0 && n_bits == 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);
            } 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]);
                }
            }
            break;

        case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
            mf_format_subfield(&dst, s);
            if (src.field != dst.field || src.ofs != dst.ofs) {
                ds_put_char(s, '=');
                mf_format_subfield(&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_cstr(s, "->");
            mf_format_subfield(&dst, s);
            break;

        case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
            ds_put_cstr(s, "load:");
            mf_format_subfield(&src, s);
            ds_put_cstr(s, "->");
            mf_format_subfield(&dst, s);
            break;

        case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
            ds_put_cstr(s, "output:");
            mf_format_subfield(&src, s);
            break;
        }
    }
    if (!is_all_zeros(p, (char *) end - (char *) p)) {
        ds_put_cstr(s, ",***nonzero trailer***");
    }
    ds_put_char(s, ')');
}
Example #4
0
/* 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).
 *
 * Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
 *
 * If 'flow' is nonnull, then it should be the flow from a cls_rule that is
 * the matching rule for the learning action.  This helps to better validate
 * the action's arguments.
 *
 * Modifies 'arg'. */
void
learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
{
    char *orig = xstrdup(arg);
    char *name, *value;
    enum ofperr error;
    size_t learn_ofs;
    size_t len;

    struct nx_action_learn *learn;
    struct cls_rule rule;

    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->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) {
                ovs_fatal(0, "%s: table id 255 not valid for `learn' action",
                          orig);
            }
        } else if (!strcmp(name, "priority")) {
            learn->priority = htons(atoi(value));
        } else if (!strcmp(name, "idle_timeout")) {
            learn->idle_timeout = htons(atoi(value));
        } else if (!strcmp(name, "hard_timeout")) {
            learn->hard_timeout = htons(atoi(value));
        } else if (!strcmp(name, "fin_idle_timeout")) {
            learn->fin_idle_timeout = htons(atoi(value));
        } else if (!strcmp(name, "fin_hard_timeout")) {
            learn->fin_hard_timeout = htons(atoi(value));
        } else if (!strcmp(name, "cookie")) {
            learn->cookie = htonll(strtoull(value, NULL, 0));
        } else {
            struct learn_spec 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)) {
                ovs_fatal(0, "%s: cannot specify source field %s because "
                          "prerequisites are not satisfied",
                          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)) {
                ovs_fatal(0, "%s: cannot specify destination field %s because "
                          "prerequisites are not satisfied",
                          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);
            }
        }
    }

    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);

    /* In theory the above should have caught any errors, but... */
    if (flow) {
        error = learn_check(learn, flow);
        if (error) {
            ovs_fatal(0, "%s: %s", orig, ofperr_to_string(error));
        }
    }
    free(orig);
}
Example #5
0
void
learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
              struct ofputil_flow_mod *fm)
{
    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;

            get_subfield(n_bits, &p, &src);
            mf_read_subfield(&src, flow, &value);
        } else {
            int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16);

            memset(&value, 0, sizeof value);
            bitwise_copy(p, p_bytes, 0,
                         &value, sizeof value, 0,
                         n_bits);
            p = (const uint8_t *) p + p_bytes;
        }

        switch (dst_type) {
        case NX_LEARN_DST_MATCH:
            get_subfield(n_bits, &p, &dst);
            mf_write_subfield(&dst, &value, &fm->cr);
            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);
            }
            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];
            }
            break;
        }
    }

    fm->actions = ofpbuf_steal_data(&actions);
    fm->n_actions = actions.size / sizeof(struct ofp_action_header);
}
Example #6
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);

    if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
        || learn->pad
        || learn->table_id == 0xff) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    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;
        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;
        }

        /* Check the source. */
        if (src_type == NX_LEARN_SRC_FIELD) {
            struct mf_subfield src;

            get_subfield(n_bits, &p, &src);
            error = mf_check_src(&src, flow);
            if (error) {
                return error;
            }
            value = 0;
        } else {
            value = get_bits(n_bits, &p);
        }

        /* Check the destination. */
        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
            struct mf_subfield dst;

            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;
            }

            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 == 11);
                }
            }
        }
    }
    if (!is_all_zeros(p, (char *) end - (char *) p)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return 0;
}
Example #7
0
/* Convert 'string' (as described in the Flow Syntax section of the ovs-ofctl
 * man page) into 'pf'.  If 'actions' is specified, an action must be in
 * 'string' and may be expanded or reallocated. */
void
parse_ofp_str(struct flow_mod *fm, uint8_t *table_idx,
              struct ofpbuf *actions, char *string)
{
    char *save_ptr = NULL;
    char *name;

    if (table_idx) {
        *table_idx = 0xff;
    }
    cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
    fm->cookie = htonll(0);
    fm->command = UINT16_MAX;
    fm->idle_timeout = OFP_FLOW_PERMANENT;
    fm->hard_timeout = OFP_FLOW_PERMANENT;
    fm->buffer_id = UINT32_MAX;
    fm->out_port = OFPP_NONE;
    fm->flags = 0;
    if (actions) {
        char *act_str = strstr(string, "action");
        if (!act_str) {
            ovs_fatal(0, "must specify an action");
        }
        *act_str = '\0';

        act_str = strchr(act_str + 1, '=');
        if (!act_str) {
            ovs_fatal(0, "must specify an action");
        }

        act_str++;

        str_to_action(act_str, actions);
        fm->actions = actions->data;
        fm->n_actions = actions->size / sizeof(union ofp_action);
    } else {
        fm->actions = NULL;
        fm->n_actions = 0;
    }
    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
        const struct protocol *p;

        if (parse_protocol(name, &p)) {
            cls_rule_set_dl_type(&fm->cr, htons(p->dl_type));
            if (p->nw_proto) {
                cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
            }
        } else {
            const struct field *f;
            char *value;

            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
            if (!value) {
                ovs_fatal(0, "field %s missing value", name);
            }

            if (table_idx && !strcmp(name, "table")) {
                *table_idx = atoi(value);
            } else if (!strcmp(name, "out_port")) {
                fm->out_port = atoi(value);
            } else if (!strcmp(name, "priority")) {
                fm->cr.priority = atoi(value);
            } else if (!strcmp(name, "idle_timeout")) {
                fm->idle_timeout = atoi(value);
            } else if (!strcmp(name, "hard_timeout")) {
                fm->hard_timeout = atoi(value);
            } else if (!strcmp(name, "cookie")) {
                fm->cookie = htonll(str_to_u64(value));
            } else if (parse_field_name(name, &f)) {
                if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
                    if (f->wildcard) {
                        fm->cr.wc.wildcards |= f->wildcard;
                        cls_rule_zero_wildcarded_fields(&fm->cr);
                    } else if (f->index == F_NW_SRC) {
                        cls_rule_set_nw_src_masked(&fm->cr, 0, 0);
                    } else if (f->index == F_NW_DST) {
                        cls_rule_set_nw_dst_masked(&fm->cr, 0, 0);
                    } else if (f->index == F_IPV6_SRC) {
                        cls_rule_set_ipv6_src_masked(&fm->cr,
                                &in6addr_any, &in6addr_any);
                    } else if (f->index == F_IPV6_DST) {
                        cls_rule_set_ipv6_dst_masked(&fm->cr,
                                &in6addr_any, &in6addr_any);
                    } else if (f->index == F_DL_VLAN) {
                        cls_rule_set_any_vid(&fm->cr);
                    } else if (f->index == F_DL_VLAN_PCP) {
                        cls_rule_set_any_pcp(&fm->cr);
                    } else {
                        NOT_REACHED();
                    }
                } else {
                    parse_field_value(&fm->cr, f->index, value);
                }
            } else if (!strncmp(name, "reg", 3)
                       && isdigit((unsigned char) name[3])) {
                unsigned int reg_idx = atoi(name + 3);
                if (reg_idx >= FLOW_N_REGS) {
                    ovs_fatal(0, "only %d registers supported", FLOW_N_REGS);
                }
                parse_reg_value(&fm->cr, reg_idx, value);
            } else {
                ovs_fatal(0, "unknown keyword %s", name);
            }
        }
    }
}
Example #8
0
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
 * page) into 'fm' for sending the specified flow_mod 'command' to a switch.
 * If 'actions' is specified, an action must be in 'string' and may be expanded
 * or reallocated.
 *
 * To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
 * constant for 'command'.  To parse syntax for an OFPST_FLOW or
 * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'. */
void
parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
              bool verbose)
{
    enum {
        F_OUT_PORT = 1 << 0,
        F_ACTIONS = 1 << 1,
        F_TIMEOUT = 1 << 3,
        F_PRIORITY = 1 << 4
    } fields;
    char *string = xstrdup(str_);
    char *save_ptr = NULL;
    char *act_str = NULL;
    char *name;

    switch (command) {
    case -1:
        fields = F_OUT_PORT;
        break;

    case OFPFC_ADD:
        fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY;
        break;

    case OFPFC_DELETE:
        fields = F_OUT_PORT;
        break;

    case OFPFC_DELETE_STRICT:
        fields = F_OUT_PORT | F_PRIORITY;
        break;

    case OFPFC_MODIFY:
        fields = F_ACTIONS;
        break;

    case OFPFC_MODIFY_STRICT:
        fields = F_ACTIONS | F_PRIORITY;
        break;

    default:
        NOT_REACHED();
    }

    cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
    fm->cookie = htonll(0);
    fm->cookie_mask = htonll(0);
    if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
        /* For modify, by default, don't update the cookie. */
        fm->new_cookie = htonll(UINT64_MAX);
    } else{
        fm->new_cookie = htonll(0);
    }
    fm->table_id = 0xff;
    fm->command = command;
    fm->idle_timeout = OFP_FLOW_PERMANENT;
    fm->hard_timeout = OFP_FLOW_PERMANENT;
    fm->buffer_id = UINT32_MAX;
    fm->out_port = OFPP_NONE;
    fm->flags = 0;
    if (fields & F_ACTIONS) {
        act_str = strstr(string, "action");
        if (!act_str) {
            ofp_fatal(str_, verbose, "must specify an action");
        }
        *act_str = '\0';

        act_str = strchr(act_str + 1, '=');
        if (!act_str) {
            ofp_fatal(str_, verbose, "must specify an action");
        }

        act_str++;
    }
    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
        const struct protocol *p;

        if (parse_protocol(name, &p)) {
            cls_rule_set_dl_type(&fm->cr, htons(p->dl_type));
            if (p->nw_proto) {
                cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
            }
        } else {
            char *value;

            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
            if (!value) {
                ofp_fatal(str_, verbose, "field %s missing value", name);
            }

            if (!strcmp(name, "table")) {
                fm->table_id = str_to_table_id(value);
            } else if (!strcmp(name, "out_port")) {
                fm->out_port = atoi(value);
            } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
                fm->cr.priority = str_to_u16(value, name);
            } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
                fm->idle_timeout = str_to_u16(value, name);
            } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
                fm->hard_timeout = str_to_u16(value, name);
            } else if (!strcmp(name, "cookie")) {
                char *mask = strchr(value, '/');

                if (mask) {
                    /* A mask means we're searching for a cookie. */
                    if (command == OFPFC_ADD) {
                        ofp_fatal(str_, verbose, "flow additions cannot use "
                                  "a cookie mask");
                    }
                    *mask = '\0';
                    fm->cookie = htonll(str_to_u64(value));
                    fm->cookie_mask = htonll(str_to_u64(mask+1));
                } else {
                    /* No mask means that the cookie is being set. */
                    if (command != OFPFC_ADD && command != OFPFC_MODIFY
                            && command != OFPFC_MODIFY_STRICT) {
                        ofp_fatal(str_, verbose, "cannot set cookie");
                    }
                    fm->new_cookie = htonll(str_to_u64(value));
                }
            } else if (mf_from_name(name)) {
                parse_field(mf_from_name(name), value, &fm->cr);
            } else if (!strcmp(name, "duration")
                       || !strcmp(name, "n_packets")
                       || !strcmp(name, "n_bytes")) {
                /* Ignore these, so that users can feed the output of
                 * "ovs-ofctl dump-flows" back into commands that parse
                 * flows. */
            } else {
                ofp_fatal(str_, verbose, "unknown keyword %s", name);
            }
        }
    }
    if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
            && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
        /* On modifies without a mask, we are supposed to add a flow if
         * one does not exist.  If a cookie wasn't been specified, use a
         * default of zero. */
        fm->new_cookie = htonll(0);
    }
    if (fields & F_ACTIONS) {
        struct ofpbuf actions;

        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);
    } else {
        fm->actions = NULL;
        fm->n_actions = 0;
    }

    free(string);
}
void
learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
              struct ofputil_flow_mod *fm)
{
    const void *p, *end;
    struct ofpbuf actions;

    cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
    fm->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);

    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;
        uint64_t value;

        struct nx_action_reg_load *load;
        ovs_be32 dst_field;
        int dst_ofs;

        if (!header) {
            break;
        }

        if (src_type == NX_LEARN_SRC_FIELD) {
            ovs_be32 src_field = get_be32(&p);
            int src_ofs = ntohs(get_be16(&p));

            value = nxm_read_field_bits(src_field,
                                        nxm_encode_ofs_nbits(src_ofs, n_bits),
                                        flow);
        } else {
            value = get_bits(n_bits, &p);
        }

        switch (dst_type) {
        case NX_LEARN_DST_MATCH:
            dst_field = get_be32(&p);
            dst_ofs = ntohs(get_be16(&p));
            mf_set_subfield(mf_from_nxm_header(ntohl(dst_field)), value,
                            dst_ofs, n_bits, &fm->cr);
            break;

        case NX_LEARN_DST_LOAD:
            dst_field = get_be32(&p);
            dst_ofs = ntohs(get_be16(&p));
            load = ofputil_put_NXAST_REG_LOAD(&actions);
            load->ofs_nbits = nxm_encode_ofs_nbits(dst_ofs, n_bits);
            load->dst = dst_field;
            load->value = htonll(value);
            break;

        case NX_LEARN_DST_OUTPUT:
            ofputil_put_OFPAT_OUTPUT(&actions)->port = htons(value);
            break;
        }
    }

    fm->actions = ofpbuf_steal_data(&actions);
    fm->n_actions = actions.size / sizeof(struct ofp_action_header);
}
/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
 * valid action on 'flow'. */
int
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);

    if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
        || !is_all_zeros(learn->pad, sizeof learn->pad)
        || learn->table_id == 0xff) {
        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
    }

    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;
        int dst_type = header & NX_LEARN_DST_MASK;

        uint64_t value;
        int error;

        if (!header) {
            break;
        }

        error = learn_check_header(header, (char *) end - (char *) p);
        if (error) {
            return error;
        }

        /* Check the source. */
        if (src_type == NX_LEARN_SRC_FIELD) {
            ovs_be32 src_field = get_be32(&p);
            int src_ofs = ntohs(get_be16(&p));

            error = nxm_src_check(src_field, src_ofs, n_bits, flow);
            if (error) {
                return error;
            }
            value = 0;
        } else {
            value = get_bits(n_bits, &p);
        }

        /* Check the destination. */
        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
            ovs_be32 dst_field = get_be32(&p);
            int dst_ofs = ntohs(get_be16(&p));
            int error;

            error = (dst_type == NX_LEARN_DST_LOAD
                     ? nxm_dst_check(dst_field, dst_ofs, n_bits, &rule.flow)
                     : nxm_src_check(dst_field, dst_ofs, n_bits, &rule.flow));
            if (error) {
                return error;
            }

            if (dst_type == NX_LEARN_DST_MATCH
                && src_type == NX_LEARN_SRC_IMMEDIATE) {
                mf_set_subfield(mf_from_nxm_header(ntohl(dst_field)), value,
                                dst_ofs, n_bits, &rule);
            }
        }
    }
    if (!is_all_zeros(p, (char *) end - (char *) p)) {
        return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
    }

    return 0;
}