static int route_table_reset(void) { struct nl_dump dump; struct rtgenmsg *rtmsg; uint64_t reply_stub[NL_DUMP_BUFSIZE / 8]; struct ofpbuf request, reply, buf; route_map_clear(); netdev_get_addrs_list_flush(); route_table_valid = true; rt_change_seq++; ofpbuf_init(&request, 0); nl_msg_put_nlmsghdr(&request, sizeof *rtmsg, RTM_GETROUTE, NLM_F_REQUEST); rtmsg = ofpbuf_put_zeros(&request, sizeof *rtmsg); rtmsg->rtgen_family = AF_UNSPEC; nl_dump_start(&dump, NETLINK_ROUTE, &request); ofpbuf_uninit(&request); ofpbuf_use_stub(&buf, reply_stub, sizeof reply_stub); while (nl_dump_next(&dump, &reply, &buf)) { struct route_table_msg msg; if (route_table_parse(&reply, &msg)) { route_table_handle_msg(&msg); } } ofpbuf_uninit(&buf); return nl_dump_done(&dump); }
/* If 'b' is shorter than 'length' bytes, pads its tail out with zeros to that * length. */ void ofpbuf_padto(struct ofpbuf *b, size_t length) { if (b->size < length) { ofpbuf_put_zeros(b, length - b->size); } }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_load_immediate(union mf_subvalue *imm, const char *s, const char *full_s, struct ofpact_learn_spec *spec, struct ofpbuf *ofpacts) { struct mf_subfield dst; char *error; error = mf_parse_subfield(&dst, s); if (error) { return error; } if (!mf_nxm_header(dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", full_s, s); } if (!bitwise_is_all_zeros(imm, sizeof *imm, dst.n_bits, (8 * sizeof *imm) - dst.n_bits)) { return xasprintf("%s: value does not fit into %u bits", full_s, dst.n_bits); } spec->n_bits = dst.n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = dst; /* Push value last, as this may reallocate 'spec'! */ unsigned int n_bytes = DIV_ROUND_UP(dst.n_bits, 8); uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(n_bytes)); memcpy(src_imm, &imm->u8[sizeof imm->u8 - n_bytes], n_bytes); return NULL; }
struct ofpbuf * ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version, struct ofputil_bundle_ctrl_msg *bc) { struct ofpbuf *request; struct ofp14_bundle_ctrl_msg *m; switch (ofp_version) { case OFP10_VERSION: case OFP11_VERSION: case OFP12_VERSION: ovs_fatal(0, "bundles need OpenFlow 1.3 or later " "(\'-O OpenFlow14\')"); case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: request = ofpraw_alloc(ofp_version == OFP13_VERSION ? OFPRAW_ONFT13_BUNDLE_CONTROL : OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0); m = ofpbuf_put_zeros(request, sizeof *m); m->bundle_id = htonl(bc->bundle_id); m->type = htons(bc->type); m->flags = htons(bc->flags); break; default: OVS_NOT_REACHED(); } return request; }
void bundle_to_nxast(const struct ofpact_bundle *bundle, struct ofpbuf *openflow) { int slaves_len = ROUND_UP(2 * 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(ofp_to_u16(bundle->slaves[i])); } }
/* If 'b' is shorter than 'length' bytes, pads its tail out with zeros to that * length. */ void ofpbuf_padto(struct ofpbuf *b, size_t length) { if (ofpbuf_size(b) < length) { ofpbuf_put_zeros(b, length - ofpbuf_size(b)); } }
/* Returns a buffer owned by the caller that encodes 'features' in the format * required by 'protocol' with the given 'xid'. The caller should append port * information to the buffer with subsequent calls to * ofputil_put_switch_features_port(). */ struct ofpbuf * ofputil_encode_switch_features(const struct ofputil_switch_features *features, enum ofputil_protocol protocol, ovs_be32 xid) { struct ofp_switch_features *osf; struct ofpbuf *b; enum ofp_version version; enum ofpraw raw; version = ofputil_protocol_to_ofp_version(protocol); switch (version) { case OFP10_VERSION: raw = OFPRAW_OFPT10_FEATURES_REPLY; break; case OFP11_VERSION: case OFP12_VERSION: raw = OFPRAW_OFPT11_FEATURES_REPLY; break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: raw = OFPRAW_OFPT13_FEATURES_REPLY; break; default: OVS_NOT_REACHED(); } b = ofpraw_alloc_xid(raw, version, xid, 0); osf = ofpbuf_put_zeros(b, sizeof *osf); osf->datapath_id = htonll(features->datapath_id); osf->n_buffers = htonl(features->n_buffers); osf->n_tables = features->n_tables; osf->capabilities = htonl(features->capabilities & ofputil_capabilities_mask(version)); switch (version) { case OFP10_VERSION: if (features->capabilities & OFPUTIL_C_STP) { osf->capabilities |= htonl(OFPC10_STP); } osf->actions = ofpact_bitmap_to_openflow(features->ofpacts, OFP10_VERSION); break; case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: osf->auxiliary_id = features->auxiliary_id; /* fall through */ case OFP11_VERSION: case OFP12_VERSION: if (features->capabilities & OFPUTIL_C_GROUP_STATS) { osf->capabilities |= htonl(OFPC11_GROUP_STATS); } break; default: OVS_NOT_REACHED(); } return b; }
/* 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); }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * WARN_UNUSED_RESULT learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; 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; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = strtoull(value, NULL, 0); } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->frame; learn->n_specs++; error = learn_parse_spec(orig, name, value, spec); if (error) { return error; } /* Update 'match' 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, &match); } } } ofpact_update_len(ofpacts, &learn->ofpact); return NULL; }
/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id * extension on or off (according to 'enable'). */ struct ofpbuf * ofputil_encode_nx_flow_mod_table_id(bool enable) { struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION, 0); uint8_t *p = ofpbuf_put_zeros(msg, 8); *p = enable; return msg; }
void ofputil_append_queue_get_config_reply(const struct ofputil_queue_config *qc, struct ovs_list *replies) { enum ofp_version ofp_version = ofpmp_version(replies); struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies)); size_t start_ofs = reply->size; size_t len_ofs; ovs_be16 *len; if (ofp_version < OFP14_VERSION) { if (ofp_version < OFP12_VERSION) { struct ofp10_packet_queue *opq10; opq10 = ofpbuf_put_zeros(reply, sizeof *opq10); opq10->queue_id = htonl(qc->queue); len_ofs = (char *) &opq10->len - (char *) reply->data; } else { struct ofp12_packet_queue *opq12; opq12 = ofpbuf_put_zeros(reply, sizeof *opq12); opq12->port = ofputil_port_to_ofp11(qc->port); opq12->queue_id = htonl(qc->queue); len_ofs = (char *) &opq12->len - (char *) reply->data; } put_ofp10_queue_rate(reply, OFPQT10_MIN_RATE, qc->min_rate); put_ofp10_queue_rate(reply, OFPQT11_MAX_RATE, qc->max_rate); } else { struct ofp14_queue_desc *oqd = ofpbuf_put_zeros(reply, sizeof *oqd); oqd->port_no = ofputil_port_to_ofp11(qc->port); oqd->queue_id = htonl(qc->queue); len_ofs = (char *) &oqd->len - (char *) reply->data; put_ofp14_queue_rate(reply, OFPQDPT14_MIN_RATE, qc->min_rate); put_ofp14_queue_rate(reply, OFPQDPT14_MAX_RATE, qc->max_rate); } len = ofpbuf_at(reply, len_ofs, sizeof *len); *len = htons(reply->size - start_ofs); if (ofp_version >= OFP14_VERSION) { ofpmp_postappend(replies, start_ofs); } }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts) { struct ofpact_learn *learn; struct match match; char *name, *value; 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; match_init_catchall(&match); while (ofputil_parse_key_value(&arg, &name, &value)) { if (!strcmp(name, "table")) { learn->table_id = atoi(value); if (learn->table_id == 255) { return xasprintf("%s: table id 255 not valid for `learn' " "action", orig); } } else if (!strcmp(name, "priority")) { learn->priority = atoi(value); } else if (!strcmp(name, "idle_timeout")) { learn->idle_timeout = atoi(value); } else if (!strcmp(name, "hard_timeout")) { learn->hard_timeout = atoi(value); } else if (!strcmp(name, "fin_idle_timeout")) { learn->fin_idle_timeout = atoi(value); } else if (!strcmp(name, "fin_hard_timeout")) { learn->fin_hard_timeout = atoi(value); } else if (!strcmp(name, "cookie")) { learn->cookie = htonll(strtoull(value, NULL, 0)); } else if (!strcmp(name, "send_flow_rem")) { learn->flags |= NX_LEARN_F_SEND_FLOW_REM; } else if (!strcmp(name, "delete_learned")) { learn->flags |= NX_LEARN_F_DELETE_LEARNED; } else { struct ofpact_learn_spec *spec; char *error; spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); error = learn_parse_spec(orig, name, value, spec, ofpacts, &match); if (error) { return error; } learn = ofpacts->header; } } ofpact_finish_LEARN(ofpacts, &learn); return NULL; }
/* Constructs and returns the beginning of a reply to * OFPT_QUEUE_GET_CONFIG_REQUEST or OFPMP_QUEUE_DESC request 'oh'. The caller * may append information about individual queues with * ofputil_append_queue_get_config_reply(). */ void ofputil_start_queue_get_config_reply(const struct ofp_header *request, struct ovs_list *replies) { struct ofpbuf *reply; ofp_port_t port; uint32_t queue; ovs_assert(!ofputil_decode_queue_get_config_request(request, &port, &queue)); enum ofpraw raw = ofpraw_decode_assert(request); switch ((int) raw) { case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY, request, 0); struct ofp10_queue_get_config_reply *qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10); qgcr10->port = htons(ofp_to_u16(port)); break; case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY, request, 0); struct ofp11_queue_get_config_reply *qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11); qgcr11->port = ofputil_port_to_ofp11(port); break; case OFPRAW_OFPST14_QUEUE_DESC_REQUEST: reply = ofpraw_alloc_stats_reply(request, 0); break; default: OVS_NOT_REACHED(); } ovs_list_init(replies); ovs_list_push_back(replies, &reply->list_node); }
static struct ofpbuf * ofputil_put_switch_config(const struct ofputil_switch_config *config, struct ofpbuf *b) { const struct ofp_header *oh = b->data; struct ofp_switch_config *osc = ofpbuf_put_zeros(b, sizeof *osc); osc->flags = htons(config->frag); if (config->invalid_ttl_to_controller > 0 && oh->version < OFP13_VERSION) { osc->flags |= htons(OFPC_INVALID_TTL_TO_CONTROLLER); } osc->miss_send_len = htons(config->miss_send_len); return b; }
static void put_ofp10_queue_rate(struct ofpbuf *reply, enum ofp10_queue_properties property, uint16_t rate) { if (rate != UINT16_MAX) { struct ofp10_queue_prop_rate *oqpr; oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr); oqpr->prop_header.property = htons(property); oqpr->prop_header.len = htons(sizeof *oqpr); oqpr->rate = htons(rate); } }
/* Try connecting and sending an extra-long hello, which should succeed (since * the specification says that implementations must accept and ignore extra * data). */ static void test_send_long_hello(int argc OVS_UNUSED, char *argv[]) { const char *type = argv[1]; struct ofpbuf *hello; enum { EXTRA_BYTES = 8 }; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION, htonl(0x12345678), EXTRA_BYTES); ofpbuf_put_zeros(hello, EXTRA_BYTES); ofpmsg_update_length(hello); test_send_hello(type, ofpbuf_data(hello), ofpbuf_size(hello), 0); ofpbuf_delete(hello); }
/* Try connecting and sending an extra-long hello, which should succeed (since * the specification says that implementations must accept and ignore extra * data). */ static void test_send_long_hello(struct ovs_cmdl_context *ctx) { const char *type = ctx->argv[1]; struct ofpbuf *hello; enum { EXTRA_BYTES = 8 }; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION, htonl(0x12345678), EXTRA_BYTES); ofpbuf_put_zeros(hello, EXTRA_BYTES); ofpmsg_update_length(hello); test_send_hello(type, hello->data, hello->size, 0); ofpbuf_delete(hello); }
/* Encode a queue stats request for 'oqsr', the encoded message * will be for OpenFlow version 'ofp_version'. Returns message * as a struct ofpbuf. Returns encoded message on success, NULL on error. */ struct ofpbuf * ofputil_encode_queue_stats_request( enum ofp_version ofp_version, const struct ofputil_queue_stats_request *oqsr) { struct ofpbuf *request; switch (ofp_version) { case OFP11_VERSION: case OFP12_VERSION: case OFP13_VERSION: case OFP14_VERSION: case OFP15_VERSION: case OFP16_VERSION: { struct ofp11_queue_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); req->port_no = ofputil_port_to_ofp11(oqsr->port_no); req->queue_id = htonl(oqsr->queue_id); break; } case OFP10_VERSION: { struct ofp10_queue_stats_request *req; request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0); req = ofpbuf_put_zeros(request, sizeof *req); /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */ req->port_no = htons(ofp_to_u16(oqsr->port_no == OFPP_ANY ? OFPP_ALL : oqsr->port_no)); req->queue_id = htonl(oqsr->queue_id); break; } default: OVS_NOT_REACHED(); } return request; }
static void send_controller_id(struct lswitch *sw) { struct ofpbuf *b; int ofp_version = rconn_get_version(sw->rconn); ovs_assert(ofp_version > 0 && ofp_version < 0xff); b = ofpraw_alloc(OFPRAW_NXT_SET_CONTROLLER_ID, ofp_version, 0); struct nx_controller_id *nci = ofpbuf_put_zeros(b, sizeof *nci); nci->controller_id = htons(100); queue_tx(sw, b); }
struct ofpbuf * ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh, struct ofputil_bundle_ctrl_msg *msg) { struct ofpbuf *buf; struct ofp14_bundle_ctrl_msg *m; buf = ofpraw_alloc_reply(oh->version == OFP13_VERSION ? OFPRAW_ONFT13_BUNDLE_CONTROL : OFPRAW_OFPT14_BUNDLE_CONTROL, oh, 0); m = ofpbuf_put_zeros(buf, sizeof *m); m->bundle_id = htonl(msg->bundle_id); m->type = htons(msg->type); m->flags = htons(msg->flags); return buf; }
struct ofpbuf * ofputil_encode_bundle_add(enum ofp_version ofp_version, struct ofputil_bundle_add_msg *msg) { struct ofpbuf *request; struct ofp14_bundle_ctrl_msg *m; /* Must use the same xid as the embedded message. */ request = ofpraw_alloc_xid(ofp_version == OFP13_VERSION ? OFPRAW_ONFT13_BUNDLE_ADD_MESSAGE : OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version, msg->msg->xid, ntohs(msg->msg->length)); m = ofpbuf_put_zeros(request, sizeof *m); m->bundle_id = htonl(msg->bundle_id); m->flags = htons(msg->flags); ofpbuf_put(request, msg->msg, ntohs(msg->msg->length)); ofpmsg_update_length(request); return request; }
static void parse_note(struct ofpbuf *b, const char *arg) { size_t start_ofs = b->size; struct nx_action_note *nan; int remainder; size_t len; nan = ofputil_put_NXAST_NOTE(b); b->size -= sizeof nan->note; while (*arg != '\0') { uint8_t byte; bool ok; if (*arg == '.') { arg++; } if (*arg == '\0') { break; } byte = hexits_value(arg, 2, &ok); if (!ok) { ovs_fatal(0, "bad hex digit in `note' argument"); } ofpbuf_put(b, &byte, 1); arg += 2; } len = b->size - start_ofs; remainder = len % OFP_ACTION_ALIGN; if (remainder) { ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder); } nan = (struct nx_action_note *)((char *)b->data + start_ofs); nan->len = htons(b->size - start_ofs); }
/* Puts into 'b' a packet that flow_extract() would parse as having the given * 'flow'. * * (This is useful only for testing, obviously, and the packet isn't really * valid. It hasn't got some checksums filled in, for one, and lots of fields * are just zeroed.) */ void flow_compose(struct ofpbuf *b, const struct flow *flow) { eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0); if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) { struct eth_header *eth = b->l2; eth->eth_type = htons(b->size); return; } if (flow->vlan_tci & htons(VLAN_CFI)) { eth_push_vlan(b, flow->vlan_tci); } if (flow->dl_type == htons(ETH_TYPE_IP)) { struct ip_header *ip; b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip); ip->ip_ihl_ver = IP_IHL_VER(5, 4); ip->ip_tos = flow->nw_tos; ip->ip_ttl = flow->nw_ttl; ip->ip_proto = flow->nw_proto; put_16aligned_be32(&ip->ip_src, flow->nw_src); put_16aligned_be32(&ip->ip_dst, flow->nw_dst); if (flow->nw_frag & FLOW_NW_FRAG_ANY) { ip->ip_frag_off |= htons(IP_MORE_FRAGMENTS); if (flow->nw_frag & FLOW_NW_FRAG_LATER) { ip->ip_frag_off |= htons(100); } } if (!(flow->nw_frag & FLOW_NW_FRAG_ANY) || !(flow->nw_frag & FLOW_NW_FRAG_LATER)) { if (flow->nw_proto == IPPROTO_TCP) { struct tcp_header *tcp; b->l4 = tcp = ofpbuf_put_zeros(b, sizeof *tcp); tcp->tcp_src = flow->tp_src; tcp->tcp_dst = flow->tp_dst; tcp->tcp_ctl = TCP_CTL(0, 5); } else if (flow->nw_proto == IPPROTO_UDP) { struct udp_header *udp; b->l4 = udp = ofpbuf_put_zeros(b, sizeof *udp); udp->udp_src = flow->tp_src; udp->udp_dst = flow->tp_dst; } else if (flow->nw_proto == IPPROTO_SCTP) { struct sctp_header *sctp; b->l4 = sctp = ofpbuf_put_zeros(b, sizeof *sctp); sctp->sctp_src = flow->tp_src; sctp->sctp_dst = flow->tp_dst; } else if (flow->nw_proto == IPPROTO_ICMP) { struct icmp_header *icmp; b->l4 = icmp = ofpbuf_put_zeros(b, sizeof *icmp); icmp->icmp_type = ntohs(flow->tp_src); icmp->icmp_code = ntohs(flow->tp_dst); icmp->icmp_csum = csum(icmp, ICMP_HEADER_LEN); } } ip = b->l3; ip->ip_tot_len = htons((uint8_t *) b->data + b->size - (uint8_t *) b->l3); ip->ip_csum = csum(ip, sizeof *ip); } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) { /* XXX */ } else if (flow->dl_type == htons(ETH_TYPE_ARP) || flow->dl_type == htons(ETH_TYPE_RARP)) { struct arp_eth_header *arp; b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp); arp->ar_hrd = htons(1); arp->ar_pro = htons(ETH_TYPE_IP); arp->ar_hln = ETH_ADDR_LEN; arp->ar_pln = 4; arp->ar_op = htons(flow->nw_proto); if (flow->nw_proto == ARP_OP_REQUEST || flow->nw_proto == ARP_OP_REPLY) { put_16aligned_be32(&arp->ar_spa, flow->nw_src); put_16aligned_be32(&arp->ar_tpa, flow->nw_dst); memcpy(arp->ar_sha, flow->arp_sha, ETH_ADDR_LEN); memcpy(arp->ar_tha, flow->arp_tha, ETH_ADDR_LEN); } } if (eth_type_mpls(flow->dl_type)) { b->l2_5 = b->l3; push_mpls(b, flow->dl_type, flow->mpls_lse); } }
/* 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) { struct ofpact_learn *learn; const void *p, *end; if (nal->pad) { return OFPERR_OFPBAC_BAD_ARGUMENT; } learn = ofpact_put_LEARN(ofpacts); 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 & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) { return OFPERR_OFPBAC_BAD_ARGUMENT; } end = (char *) nal + ntohs(nal->len); for (p = nal + 1; p != end; ) { struct ofpact_learn_spec *spec; uint16_t header = ntohs(get_be16(&p)); if (!header) { break; } spec = ofpbuf_put_zeros(ofpacts, sizeof *spec); learn = ofpacts->l2; learn->n_specs++; 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; /* 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 { return OFPERR_OFPBAC_BAD_ARGUMENT; } /* 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 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); 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; } return 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); }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_spec(const char *orig, char *name, char *value, const struct ofputil_port_map *port_map, struct ofpact_learn_spec *spec, struct ofpbuf *ofpacts, struct match *match) { /* Parse destination and check prerequisites. */ struct mf_subfield dst; char *error = mf_parse_subfield(&dst, name); bool parse_error = error != NULL; free(error); if (!parse_error) { if (!mf_nxm_header(dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } spec->dst = dst; spec->n_bits = dst.n_bits; spec->dst_type = NX_LEARN_DST_MATCH; /* Parse source and check prerequisites. */ if (value[0] != '\0') { struct mf_subfield src; error = mf_parse_subfield(&src, value); if (error) { union mf_value imm; char *imm_error = NULL; /* Try an immediate value. */ if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) { /* Full field value. */ imm_error = mf_parse_value(dst.field, value, port_map, &imm); } else { char *tail; /* Partial field value. */ if (parse_int_string(value, (uint8_t *)&imm, dst.field->n_bytes, &tail) || *tail != 0) { imm_error = xasprintf("%s: cannot parse integer value", orig); } if (!imm_error && !bitwise_is_all_zeros(&imm, dst.field->n_bytes, dst.n_bits, dst.field->n_bytes * 8 - dst.n_bits)) { struct ds ds; ds_init(&ds); mf_format(dst.field, &imm, NULL, NULL, &ds); imm_error = xasprintf("%s: value %s does not fit into %d bits", orig, ds_cstr(&ds), dst.n_bits); ds_destroy(&ds); } } if (imm_error) { char *err = xasprintf("%s: %s value %s cannot be parsed as a subfield (%s) or an immediate value (%s)", orig, name, value, error, imm_error); free(error); free(imm_error); return err; } spec->src_type = NX_LEARN_SRC_IMMEDIATE; /* Update 'match' to allow for satisfying destination * prerequisites. */ mf_write_subfield_value(&dst, &imm, match); /* Push value last, as this may reallocate 'spec'! */ unsigned int imm_bytes = DIV_ROUND_UP(dst.n_bits, 8); uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(imm_bytes)); memcpy(src_imm, &imm, imm_bytes); free(error); return NULL; } spec->src = src; if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->src_type = NX_LEARN_SRC_FIELD; } else if (!strcmp(name, "load")) { union mf_subvalue imm; char *tail; char *dst_value = strstr(value, "->"); if (dst_value == value) { return xasprintf("%s: missing source before `->' in `%s'", name, value); } if (!dst_value) { return xasprintf("%s: missing `->' in `%s'", name, value); } if (!parse_int_string(value, imm.u8, sizeof imm.u8, (char **) &tail) && tail != value) { if (tail != dst_value) { return xasprintf("%s: garbage before `->' in `%s'", name, value); } error = learn_parse_load_immediate(&imm, dst_value + 2, value, spec, ofpacts); if (error) { return error; } } else { struct ofpact_reg_move move; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; }
/* Helper for bundle_parse and bundle_parse_load. */ static void bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr, const char *fields, const char *basis, const char *algorithm, const char *slave_type, const char *dst, const char *slave_delim) { enum ofputil_action_code code; struct nx_action_bundle *nab; uint16_t n_slaves; if (!slave_delim) { ovs_fatal(0, "%s: not enough arguments to bundle action", s); } if (strcasecmp(slave_delim, "slaves")) { ovs_fatal(0, "%s: missing slave delimiter, expected `slaves' got `%s'", s, slave_delim); } code = dst ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE; b->l2 = ofputil_put_action(code, b); n_slaves = 0; for (;;) { ovs_be16 slave_be; char *slave; slave = strtok_r(NULL, ", ", save_ptr); if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) { break; } slave_be = htons(atoi(slave)); ofpbuf_put(b, &slave_be, sizeof slave_be); n_slaves++; } /* 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)); if (!strcasecmp(fields, "eth_src")) { nab->fields = htons(NX_HASH_FIELDS_ETH_SRC); } else if (!strcasecmp(fields, "symmetric_l4")) { nab->fields = htons(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); } else if (!strcasecmp(algorithm, "hrw")) { nab->algorithm = htons(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 { ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type); } if (dst) { uint32_t reg; int ofs, n_bits; nxm_parse_field_bits(dst, ®, &ofs, &n_bits); nab->dst = htonl(reg); nab->ofs_nbits = nxm_encode_ofs_nbits(ofs, n_bits); } b->l2 = NULL; }
update_instruction_length(struct ofpbuf *buffer, size_t oia_offset) { struct ofp_header *oh = ofpbuf_at_assert(buffer, 0, sizeof *oh); struct ofp_instruction *ih = ofpbuf_at_assert(buffer, oia_offset, sizeof *ih); ih->len = htons(buffer->size - oia_offset); } struct ofpbuf * make_flow_mod(uint8_t command, uint8_t table_id, const struct flow *flow UNUSED, 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 = OFP_VERSION; ofm->header.type = OFPT_FLOW_MOD; ofm->header.length = htons(size); ofm->cookie = 0; /*TODO fill match ofm->match.in_port = flow->in_port; memcpy(ofm->match.dl_src, flow->dl_src, sizeof ofm->match.dl_src); memcpy(ofm->match.dl_dst, flow->dl_dst, sizeof ofm->match.dl_dst); ofm->match.dl_vlan = flow->dl_vlan; ofm->match.dl_vlan_pcp = flow->dl_vlan_pcp; ofm->match.dl_type = flow->dl_type; ofm->match.nw_src = flow->nw_src; ofm->match.nw_dst = flow->nw_dst; ofm->match.nw_proto = flow->nw_proto; ofm->match.nw_tos = flow->nw_tos;
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man * page) into 'mm' for sending the specified meter_mod 'command' to a switch. */ void parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_, int command, bool verbose) { enum { F_METER = 1 << 0, F_FLAGS = 1 << 1, F_BANDS = 1 << 2, } fields; char *string = xstrdup(str_); char *save_ptr = NULL; char *band_str = NULL; char *name; switch (command) { case -1: fields = F_METER; break; case OFPMC13_ADD: fields = F_METER | F_FLAGS | F_BANDS; break; case OFPMC13_DELETE: fields = F_METER; break; case OFPMC13_MODIFY: fields = F_METER | F_FLAGS | F_BANDS; break; default: NOT_REACHED(); } mm->command = command; mm->meter.meter_id = 0; mm->meter.flags = 0; if (fields & F_BANDS) { band_str = strstr(string, "band"); if (!band_str) { ofp_fatal(str_, verbose, "must specify bands"); } *band_str = '\0'; band_str = strchr(band_str + 1, '='); if (!band_str) { ofp_fatal(str_, verbose, "must specify bands"); } band_str++; } for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { if (fields & F_FLAGS && !strcmp(name, "kbps")) { mm->meter.flags |= OFPMF13_KBPS; } else if (fields & F_FLAGS && !strcmp(name, "pktps")) { mm->meter.flags |= OFPMF13_PKTPS; } else if (fields & F_FLAGS && !strcmp(name, "burst")) { mm->meter.flags |= OFPMF13_BURST; } else if (fields & F_FLAGS && !strcmp(name, "stats")) { mm->meter.flags |= OFPMF13_STATS; } 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, "meter")) { if (!strcmp(value, "all")) { mm->meter.meter_id = OFPM13_ALL; } else if (!strcmp(value, "controller")) { mm->meter.meter_id = OFPM13_CONTROLLER; } else if (!strcmp(value, "slowpath")) { mm->meter.meter_id = OFPM13_SLOWPATH; } else { mm->meter.meter_id = str_to_u32(value); if (mm->meter.meter_id > OFPM13_MAX) { ofp_fatal(str_, verbose, "invalid value for %s", name); } } } else { ofp_fatal(str_, verbose, "unknown keyword %s", name); } } } if (fields & F_METER && !mm->meter.meter_id) { ofp_fatal(str_, verbose, "must specify 'meter'"); } if (fields & F_FLAGS && !mm->meter.flags) { ofp_fatal(str_, verbose, "meter must specify either 'kbps' or 'pktps'"); } if (fields & F_BANDS) { struct ofpbuf bands; uint16_t n_bands = 0; struct ofputil_meter_band *band = NULL; int i; ofpbuf_init(&bands, 64); for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name; name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) { 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, "type")) { /* Start a new band */ band = ofpbuf_put_zeros(&bands, sizeof *band); n_bands++; if (!strcmp(value, "drop")) { band->type = OFPMBT13_DROP; } else if (!strcmp(value, "dscp_remark")) { band->type = OFPMBT13_DSCP_REMARK; } else { ofp_fatal(str_, verbose, "field %s unknown value %s", name, value); } } else if (!band || !band->type) { ofp_fatal(str_, verbose, "band must start with the 'type' keyword"); } else if (!strcmp(name, "rate")) { band->rate = str_to_u32(value); } else if (!strcmp(name, "burst_size")) { band->burst_size = str_to_u32(value); } else if (!strcmp(name, "prec_level")) { band->prec_level = str_to_u8(value, name); } else { ofp_fatal(str_, verbose, "unknown keyword %s", name); } } /* validate bands */ if (!n_bands) { ofp_fatal(str_, verbose, "meter must have bands"); } mm->meter.n_bands = n_bands; mm->meter.bands = ofpbuf_steal_data(&bands); for (i = 0; i < n_bands; ++i) { band = &mm->meter.bands[i]; if (!band->type) { ofp_fatal(str_, verbose, "band must have 'type'"); } if (band->type == OFPMBT13_DSCP_REMARK) { if (!band->prec_level) { ofp_fatal(str_, verbose, "'dscp_remark' band must have" " 'prec_level'"); } } else { if (band->prec_level) { ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have" " 'prec_level'"); } } if (!band->rate) { ofp_fatal(str_, verbose, "band must have 'rate'"); } if (mm->meter.flags & OFPMF13_BURST) { if (!band->burst_size) { ofp_fatal(str_, verbose, "band must have 'burst_size' " "when 'burst' flag is set"); } } else { if (band->burst_size) { ofp_fatal(str_, verbose, "band may have 'burst_size' only " "when 'burst' flag is set"); } } } } else { mm->meter.n_bands = 0; mm->meter.bands = NULL; } free(string); }
/* Returns NULL if successful, otherwise a malloc()'d string describing the * error. The caller is responsible for freeing the returned string. */ static char * OVS_WARN_UNUSED_RESULT learn_parse_spec(const char *orig, char *name, char *value, struct ofpact_learn_spec *spec, struct ofpbuf *ofpacts, struct match *match) { if (mf_from_name(name)) { const struct mf_field *dst = mf_from_name(name); union mf_value imm; char *error; error = mf_parse_value(dst, value, &imm); if (error) { return error; } spec->n_bits = dst->n_bits; spec->src_type = NX_LEARN_SRC_IMMEDIATE; spec->dst_type = NX_LEARN_DST_MATCH; spec->dst.field = dst; spec->dst.ofs = 0; spec->dst.n_bits = dst->n_bits; /* Update 'match' to allow for satisfying destination * prerequisites. */ mf_set_value(dst, &imm, match, NULL); /* Push value last, as this may reallocate 'spec'! */ uint8_t *src_imm = ofpbuf_put_zeros(ofpacts, OFPACT_ALIGN(dst->n_bytes)); memcpy(src_imm, &imm, dst->n_bytes); } else if (strchr(name, '[')) { /* Parse destination and check prerequisites. */ char *error; error = mf_parse_subfield(&spec->dst, name); if (error) { return error; } if (!mf_nxm_header(spec->dst.field->id)) { return xasprintf("%s: experimenter OXM field '%s' not supported", orig, name); } /* Parse source and check prerequisites. */ if (value[0] != '\0') { error = mf_parse_subfield(&spec->src, value); if (error) { return error; } if (spec->src.n_bits != spec->dst.n_bits) { return xasprintf("%s: bit widths of %s (%u) and %s (%u) " "differ", orig, name, spec->src.n_bits, value, spec->dst.n_bits); } } else { spec->src = spec->dst; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_MATCH; } else if (!strcmp(name, "load")) { if (value[strcspn(value, "[-")] == '-') { char *error = learn_parse_load_immediate(value, spec, ofpacts); if (error) { return error; } } else { struct ofpact_reg_move move; char *error; error = nxm_parse_reg_move(&move, value); if (error) { return error; } spec->n_bits = move.src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->src = move.src; spec->dst_type = NX_LEARN_DST_LOAD; spec->dst = move.dst; } } else if (!strcmp(name, "output")) { char *error = mf_parse_subfield(&spec->src, value); if (error) { return error; } spec->n_bits = spec->src.n_bits; spec->src_type = NX_LEARN_SRC_FIELD; spec->dst_type = NX_LEARN_DST_OUTPUT; } else { return xasprintf("%s: unknown keyword %s", orig, name); } return NULL; }