/* Reserves 'size' bytes of headroom so that they can be later allocated with * ofpbuf_push_uninit() without reallocating the ofpbuf. */ void ofpbuf_reserve(struct ofpbuf *b, size_t size) { ovs_assert(!ofpbuf_size(b)); ofpbuf_prealloc_tailroom(b, size); ofpbuf_set_data(b, (char*)ofpbuf_data(b) + size); }
static int parse_actions(void) { struct ds in; ds_init(&in); vlog_set_levels_from_string_assert("odp_util:console:dbg"); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf odp_actions; struct ds out; int error; /* Convert string to OVS DP actions. */ ofpbuf_init(&odp_actions, 0); error = odp_actions_from_string(ds_cstr(&in), NULL, &odp_actions); if (error) { printf("odp_actions_from_string: error\n"); goto next; } /* Convert odp_actions back to string. */ ds_init(&out); format_odp_actions(&out, ofpbuf_data(&odp_actions), ofpbuf_size(&odp_actions)); puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_actions); } ds_destroy(&in); return 0; }
/* Reserves 'headroom' bytes at the head and 'tailroom' at the end so that * they can be later allocated with ofpbuf_push_uninit() or * ofpbuf_put_uninit() without reallocating the ofpbuf. */ void ofpbuf_reserve_with_tailroom(struct ofpbuf *b, size_t headroom, size_t tailroom) { ovs_assert(!ofpbuf_size(b)); ofpbuf_prealloc_tailroom(b, headroom + tailroom); ofpbuf_set_data(b, (char*)ofpbuf_data(b) + headroom); }
/* Prefixes 'size' bytes to the head end of 'b', reallocating and copying its * data if necessary. Returns a pointer to the first byte of the data's * location in the ofpbuf. The new data is left uninitialized. */ void * ofpbuf_push_uninit(struct ofpbuf *b, size_t size) { ofpbuf_prealloc_headroom(b, size); ofpbuf_set_data(b, (char*)ofpbuf_data(b) - size); ofpbuf_set_size(b, ofpbuf_size(b) + size); return ofpbuf_data(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 = ofpbuf_size(openflow); 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 ((ofpbuf_size(openflow) - start_ofs) % 8) { ofpbuf_put_zeros(openflow, 8 - (ofpbuf_size(openflow) - start_ofs) % 8); } nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal); nal->len = htons(ofpbuf_size(openflow) - start_ofs); }
/* Appends 'size' bytes of data to the tail end of 'b', reallocating and * copying its data if necessary. Returns a pointer to the first byte of the * new data, which is left uninitialized. */ void * ofpbuf_put_uninit(struct ofpbuf *b, size_t size) { void *p; ofpbuf_prealloc_tailroom(b, size); p = ofpbuf_tail(b); ofpbuf_set_size(b, ofpbuf_size(b) + size); return p; }
/* Try connecting and sending a normal hello, which should succeed. */ static void test_send_plain_hello(int argc OVS_UNUSED, char *argv[]) { const char *type = argv[1]; struct ofpbuf *hello; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION, htonl(0x12345678), 0); test_send_hello(type, ofpbuf_data(hello), ofpbuf_size(hello), 0); ofpbuf_delete(hello); }
/* Try connecting and sending an echo request instead of a hello, which should * fail with EPROTO. */ static void test_send_echo_hello(int argc OVS_UNUSED, char *argv[]) { const char *type = argv[1]; struct ofpbuf *echo; echo = ofpraw_alloc_xid(OFPRAW_OFPT_ECHO_REQUEST, OFP13_VERSION, htonl(0x12345678), 0); test_send_hello(type, ofpbuf_data(echo), ofpbuf_size(echo), EPROTO); ofpbuf_delete(echo); }
/* Parses as many pairs of hex digits as possible (possibly separated by * spaces) from the beginning of 's', appending bytes for their values to 'b'. * Returns the first character of 's' that is not the first of a pair of hex * digits. If 'n' is nonnull, stores the number of bytes added to 'b' in * '*n'. */ char * ofpbuf_put_hex(struct ofpbuf *b, const char *s, size_t *n) { size_t initial_size = ofpbuf_size(b); for (;;) { uint8_t byte; bool ok; s += strspn(s, " \t\r\n"); byte = hexits_value(s, 2, &ok); if (!ok) { if (n) { *n = ofpbuf_size(b) - initial_size; } return CONST_CAST(char *, s); } ofpbuf_put(b, &byte, 1); s += 2; } }
/* Try connecting and sending a hello packet that has a bad version, which * should fail with EPROTO. */ static void test_send_invalid_version_hello(int argc OVS_UNUSED, char *argv[]) { const char *type = argv[1]; struct ofpbuf *hello; hello = ofpraw_alloc_xid(OFPRAW_OFPT_HELLO, OFP13_VERSION, htonl(0x12345678), 0); ((struct ofp_header *) ofpbuf_data(hello))->version = 0; test_send_hello(type, ofpbuf_data(hello), ofpbuf_size(hello), EPROTO); ofpbuf_delete(hello); }
static void send_bogus_packet_ins(struct fail_open *fo) { struct ofproto_packet_in pin; uint8_t mac[ETH_ADDR_LEN]; struct ofpbuf b; ofpbuf_init(&b, 128); eth_addr_nicira_random(mac); compose_rarp(&b, mac); memset(&pin, 0, sizeof pin); pin.up.packet = ofpbuf_data(&b); pin.up.packet_len = ofpbuf_size(&b); pin.up.reason = OFPR_NO_MATCH; pin.up.fmd.in_port = OFPP_LOCAL; pin.send_len = ofpbuf_size(&b); pin.miss_type = OFPROTO_PACKET_IN_NO_MISS; connmgr_send_packet_in(fo->connmgr, &pin); ofpbuf_uninit(&b); }
/* Shifts all of the data within the allocated space in 'b' by 'delta' bytes. * For example, a 'delta' of 1 would cause each byte of data to move one byte * forward (from address 'p' to 'p+1'), and a 'delta' of -1 would cause each * byte to move one byte backward (from 'p' to 'p-1'). */ void ofpbuf_shift(struct ofpbuf *b, int delta) { ovs_assert(delta > 0 ? delta <= ofpbuf_tailroom(b) : delta < 0 ? -delta <= ofpbuf_headroom(b) : true); if (delta != 0) { char *dst = (char *) ofpbuf_data(b) + delta; memmove(dst, ofpbuf_data(b), ofpbuf_size(b)); ofpbuf_set_data(b, dst); } }
static void ofpbuf_copy__(struct ofpbuf *b, uint8_t *new_base, size_t new_headroom, size_t new_tailroom) { const uint8_t *old_base = ofpbuf_base(b); size_t old_headroom = ofpbuf_headroom(b); size_t old_tailroom = ofpbuf_tailroom(b); size_t copy_headroom = MIN(old_headroom, new_headroom); size_t copy_tailroom = MIN(old_tailroom, new_tailroom); memcpy(&new_base[new_headroom - copy_headroom], &old_base[old_headroom - copy_headroom], copy_headroom + ofpbuf_size(b) + copy_tailroom); }
/* 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); }
/* Reallocates 'b' so that it has exactly 'new_headroom' and 'new_tailroom' * bytes of headroom and tailroom, respectively. */ static void ofpbuf_resize__(struct ofpbuf *b, size_t new_headroom, size_t new_tailroom) { void *new_base, *new_data; size_t new_allocated; new_allocated = new_headroom + ofpbuf_size(b) + new_tailroom; switch (b->source) { case OFPBUF_DPDK: OVS_NOT_REACHED(); case OFPBUF_MALLOC: if (new_headroom == ofpbuf_headroom(b)) { new_base = xrealloc(ofpbuf_base(b), new_allocated); } else { new_base = xmalloc(new_allocated); ofpbuf_copy__(b, new_base, new_headroom, new_tailroom); free(ofpbuf_base(b)); } break; case OFPBUF_STACK: OVS_NOT_REACHED(); case OFPBUF_STUB: b->source = OFPBUF_MALLOC; new_base = xmalloc(new_allocated); ofpbuf_copy__(b, new_base, new_headroom, new_tailroom); break; default: OVS_NOT_REACHED(); } b->allocated = new_allocated; ofpbuf_set_base(b, new_base); new_data = (char *) new_base + new_headroom; if (ofpbuf_data(b) != new_data) { if (b->frame) { uintptr_t data_delta = (char *) new_data - (char *) ofpbuf_data(b); b->frame = (char *) b->frame + data_delta; } ofpbuf_set_data(b, new_data); } }
/* Returns the data in 'b' as a block of malloc()'d memory and frees the buffer * within 'b'. (If 'b' itself was dynamically allocated, e.g. with * ofpbuf_new(), then it should still be freed with, e.g., ofpbuf_delete().) */ void * ofpbuf_steal_data(struct ofpbuf *b) { void *p; ovs_assert(b->source != OFPBUF_DPDK); if (b->source == OFPBUF_MALLOC && ofpbuf_data(b) == ofpbuf_base(b)) { p = ofpbuf_data(b); } else { p = xmemdup(ofpbuf_data(b), ofpbuf_size(b)); if (b->source == OFPBUF_MALLOC) { free(ofpbuf_base(b)); } } ofpbuf_set_base(b, NULL); ofpbuf_set_data(b, NULL); return p; }
static enum ofperr nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { uint8_t *p = NULL; if (match_len) { p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); if (!p) { VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a " "multiple of 8, is longer than space in message (max " "length %"PRIu32")", match_len, ofpbuf_size(b)); return OFPERR_OFPBMC_BAD_LEN; } } return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask); }
/* Creates and returns a new ofpbuf whose data are copied from 'buffer'. The * returned ofpbuf will additionally have 'headroom' bytes of headroom. */ struct ofpbuf * ofpbuf_clone_with_headroom(const struct ofpbuf *buffer, size_t headroom) { struct ofpbuf *new_buffer; new_buffer = ofpbuf_clone_data_with_headroom(ofpbuf_data(buffer), ofpbuf_size(buffer), headroom); if (buffer->frame) { uintptr_t data_delta = (char *)ofpbuf_data(new_buffer) - (char *)ofpbuf_data(buffer); new_buffer->frame = (char *) buffer->frame + data_delta; } new_buffer->l2_5_ofs = buffer->l2_5_ofs; new_buffer->l3_ofs = buffer->l3_ofs; new_buffer->l4_ofs = buffer->l4_ofs; return new_buffer; }
/* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = learn->cookie; fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->importance = 0; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = 0; if (learn->flags & NX_LEARN_F_SEND_FLOW_REM) { fm->flags |= OFPUTIL_FF_SEND_FLOW_REM; } fm->ofpacts = NULL; fm->ofpacts_len = 0; fm->delete_reason = OFPRR_DELETE; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { struct ofpact_set_field *sf; union mf_subvalue value; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { value = spec->src_imm; } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); break; case NX_LEARN_DST_LOAD: sf = ofpact_put_reg_load(ofpacts); sf->field = spec->dst.field; bitwise_copy(&value, sizeof value, 0, &sf->value, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); bitwise_one(&sf->mask, spec->dst.field->n_bytes, spec->dst.ofs, spec->n_bits); break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ovs_be16 *last_be16 = &value.be16[ARRAY_SIZE(value.be16) - 1]; ofp_port_t port = u16_to_ofp(ntohs(*last_be16)); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } ofpact_pad(ofpacts); fm->ofpacts = ofpbuf_data(ofpacts); fm->ofpacts_len = ofpbuf_size(ofpacts); }
static void print_netflow(struct ofpbuf *buf) { const struct netflow_v5_header *hdr; int i; hdr = ofpbuf_try_pull(buf, sizeof *hdr); if (!hdr) { printf("truncated NetFlow packet header\n"); return; } printf("header: v%"PRIu16", " "uptime %"PRIu32", " "now %"PRIu32".%09"PRIu32", " "seq %"PRIu32", " "engine %"PRIu8",%"PRIu8, ntohs(hdr->version), ntohl(hdr->sysuptime), ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs), ntohl(hdr->flow_seq), hdr->engine_type, hdr->engine_id); if (hdr->sampling_interval != htons(0)) { printf(", interval %"PRIu16, ntohs(hdr->sampling_interval)); } putchar('\n'); for (i = 0; i < ntohs(hdr->count); i++) { struct netflow_v5_record *rec; rec = ofpbuf_try_pull(buf, sizeof *rec); if (!rec) { printf("truncated NetFlow records\n"); return; } printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq), IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr)); printf(", if %"PRIu16" > %"PRIu16, ntohs(rec->input), ntohs(rec->output)); printf(", %"PRIu32" pkts, %"PRIu32" bytes", ntohl(rec->packet_count), ntohl(rec->byte_count)); switch (rec->ip_proto) { case IPPROTO_TCP: printf(", TCP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); if (rec->tcp_flags) { struct ds s = DS_EMPTY_INITIALIZER; packet_format_tcp_flags(&s, rec->tcp_flags); printf(" %s", ds_cstr(&s)); ds_destroy(&s); } break; case IPPROTO_UDP: printf(", UDP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); break; case IPPROTO_SCTP: printf(", SCTP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); break; case IPPROTO_ICMP: printf(", ICMP %"PRIu16":%"PRIu16, ntohs(rec->dst_port) >> 8, ntohs(rec->dst_port) & 0xff); if (rec->src_port != htons(0)) { printf(", src_port=%"PRIu16, ntohs(rec->src_port)); } break; default: printf(", proto %"PRIu8, rec->ip_proto); break; } if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) { printf(", flags %"PRIx8, rec->tcp_flags); } if (rec->ip_proto != IPPROTO_TCP && rec->ip_proto != IPPROTO_UDP && rec->ip_proto != IPPROTO_SCTP && rec->ip_proto != IPPROTO_ICMP) { if (rec->src_port != htons(0)) { printf(", src_port %"PRIu16, ntohs(rec->src_port)); } if (rec->dst_port != htons(0)) { printf(", dst_port %"PRIu16, ntohs(rec->dst_port)); } } if (rec->ip_tos) { printf(", TOS %"PRIx8, rec->ip_tos); } printf(", time %"PRIu32"...%"PRIu32, ntohl(rec->init_time), ntohl(rec->used_time)); if (rec->nexthop != htonl(0)) { printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop)); } if (rec->src_as != htons(0) || rec->dst_as != htons(0)) { printf(", AS %"PRIu16" > %"PRIu16, ntohs(rec->src_as), ntohs(rec->dst_as)); } if (rec->src_mask != 0 || rec->dst_mask != 0) { printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask); } if (rec->pad1) { printf(", pad1 %"PRIu8, rec->pad1); } if (rec->pad[0] || rec->pad[1]) { printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]); } putchar('\n'); } if (ofpbuf_size(buf)) { printf("%"PRIu32" extra bytes after last record\n", ofpbuf_size(buf)); } }
static int parse_filter(char *filter_parse) { struct ds in; struct flow flow_filter; struct flow_wildcards wc_filter; char *error, *filter = NULL; vlog_set_levels_from_string_assert("odp_util:console:dbg"); if (filter_parse && !strncmp(filter_parse, "filter=", 7)) { filter = strdup(filter_parse+7); memset(&flow_filter, 0, sizeof(flow_filter)); memset(&wc_filter, 0, sizeof(wc_filter)); error = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks, filter, NULL); if (error) { ovs_fatal(0, "Failed to parse filter (%s)", error); } } else { ovs_fatal(0, "No filter to parse."); } ds_init(&in); while (!ds_get_test_line(&in, stdin)) { struct ofpbuf odp_key; struct ofpbuf odp_mask; struct ds out; int error; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); error = odp_flow_from_string(ds_cstr(&in), NULL, &odp_key, &odp_mask); if (error) { printf("odp_flow_from_string: error\n"); goto next; } if (filter) { struct flow flow; struct flow_wildcards wc; struct match match, match_filter; struct minimatch minimatch; odp_flow_key_to_flow(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), &flow); odp_flow_key_to_mask(ofpbuf_data(&odp_mask), ofpbuf_size(&odp_mask), &wc.masks, &flow); match_init(&match, &flow, &wc); match_init(&match_filter, &flow_filter, &wc); match_init(&match_filter, &match_filter.flow, &wc_filter); minimatch_init(&minimatch, &match_filter); if (!minimatch_matches_flow(&minimatch, &match.flow)) { minimatch_destroy(&minimatch); goto next; } minimatch_destroy(&minimatch); } /* Convert odp_key to string. */ ds_init(&out); odp_flow_format(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), ofpbuf_data(&odp_mask), ofpbuf_size(&odp_mask), NULL, &out, false); puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_key); ofpbuf_uninit(&odp_mask); } ds_destroy(&in); free(filter); return 0; }
static int parse_keys(bool wc_keys) { int exit_code = 0; struct ds in; ds_init(&in); vlog_set_levels_from_string_assert("odp_util:console:dbg"); while (!ds_get_test_line(&in, stdin)) { enum odp_key_fitness fitness; struct ofpbuf odp_key; struct ofpbuf odp_mask; struct flow flow; struct ds out; int error; /* Convert string to OVS DP key. */ ofpbuf_init(&odp_key, 0); ofpbuf_init(&odp_mask, 0); error = odp_flow_from_string(ds_cstr(&in), NULL, &odp_key, &odp_mask); if (error) { printf("odp_flow_from_string: error\n"); goto next; } if (!wc_keys) { /* Convert odp_key to flow. */ fitness = odp_flow_key_to_flow(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), &flow); switch (fitness) { case ODP_FIT_PERFECT: break; case ODP_FIT_TOO_LITTLE: printf("ODP_FIT_TOO_LITTLE: "); break; case ODP_FIT_TOO_MUCH: printf("ODP_FIT_TOO_MUCH: "); break; case ODP_FIT_ERROR: printf("odp_flow_key_to_flow: error\n"); goto next; } /* Convert cls_rule back to odp_key. */ ofpbuf_uninit(&odp_key); ofpbuf_init(&odp_key, 0); odp_flow_key_from_flow(&odp_key, &flow, NULL, flow.in_port.odp_port); if (ofpbuf_size(&odp_key) > ODPUTIL_FLOW_KEY_BYTES) { printf ("too long: %"PRIu32" > %d\n", ofpbuf_size(&odp_key), ODPUTIL_FLOW_KEY_BYTES); exit_code = 1; } } /* Convert odp_key to string. */ ds_init(&out); if (wc_keys) { odp_flow_format(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), ofpbuf_data(&odp_mask), ofpbuf_size(&odp_mask), NULL, &out, false); } else { odp_flow_key_format(ofpbuf_data(&odp_key), ofpbuf_size(&odp_key), &out); } puts(ds_cstr(&out)); ds_destroy(&out); next: ofpbuf_uninit(&odp_key); } ds_destroy(&in); return exit_code; }
static int nl_sock_recv__(struct nl_sock *sock, struct ofpbuf *buf, bool wait) { /* We can't accurately predict the size of the data to be received. The * caller is supposed to have allocated enough space in 'buf' to handle the * "typical" case. To handle exceptions, we make available enough space in * 'tail' to allow Netlink messages to be up to 64 kB long (a reasonable * figure since that's the maximum length of a Netlink attribute). */ struct nlmsghdr *nlmsghdr; #ifdef _WIN32 #define MAX_STACK_LENGTH 81920 uint8_t tail[MAX_STACK_LENGTH]; #else uint8_t tail[65536]; #endif struct iovec iov[2]; struct msghdr msg; ssize_t retval; int error; ovs_assert(buf->allocated >= sizeof *nlmsghdr); ofpbuf_clear(buf); iov[0].iov_base = ofpbuf_base(buf); iov[0].iov_len = buf->allocated; iov[1].iov_base = tail; iov[1].iov_len = sizeof tail; memset(&msg, 0, sizeof msg); msg.msg_iov = iov; msg.msg_iovlen = 2; /* Receive a Netlink message from the kernel. * * This works around a kernel bug in which the kernel returns an error code * as if it were the number of bytes read. It doesn't actually modify * anything in the receive buffer in that case, so we can initialize the * Netlink header with an impossible message length and then, upon success, * check whether it changed. */ nlmsghdr = ofpbuf_base(buf); do { nlmsghdr->nlmsg_len = UINT32_MAX; #ifdef _WIN32 boolean result = false; DWORD last_error = 0; result = ReadFile(sock->handle, tail, MAX_STACK_LENGTH, &retval, NULL); last_error = GetLastError(); if (last_error != ERROR_SUCCESS && !result) { retval = -1; errno = EAGAIN; } else { ofpbuf_put(buf, tail, retval); } #else retval = recvmsg(sock->fd, &msg, wait ? 0 : MSG_DONTWAIT); #endif error = (retval < 0 ? errno : retval == 0 ? ECONNRESET /* not possible? */ : nlmsghdr->nlmsg_len != UINT32_MAX ? 0 : retval); } while (error == EINTR); if (error) { if (error == ENOBUFS) { /* Socket receive buffer overflow dropped one or more messages that * the kernel tried to send to us. */ COVERAGE_INC(netlink_overflow); } return error; } if (msg.msg_flags & MSG_TRUNC) { VLOG_ERR_RL(&rl, "truncated message (longer than %"PRIuSIZE" bytes)", sizeof tail); return E2BIG; } if (retval < sizeof *nlmsghdr || nlmsghdr->nlmsg_len < sizeof *nlmsghdr || nlmsghdr->nlmsg_len > retval) { VLOG_ERR_RL(&rl, "received invalid nlmsg (%"PRIuSIZE" bytes < %"PRIuSIZE")", retval, sizeof *nlmsghdr); return EPROTO; } #ifndef _WIN32 ofpbuf_set_size(buf, MIN(retval, buf->allocated)); if (retval > buf->allocated) { COVERAGE_INC(netlink_recv_jumbo); ofpbuf_put(buf, tail, retval - buf->allocated); } #endif log_nlmsg(__func__, 0, ofpbuf_data(buf), ofpbuf_size(buf), sock->protocol); COVERAGE_INC(netlink_received); return 0; }
static int nl_sock_transact_multiple__(struct nl_sock *sock, struct nl_transaction **transactions, size_t n, size_t *done) { uint64_t tmp_reply_stub[1024 / 8]; struct nl_transaction tmp_txn; struct ofpbuf tmp_reply; uint32_t base_seq; struct iovec iovs[MAX_IOVS]; struct msghdr msg; int error; int i; base_seq = nl_sock_allocate_seq(sock, n); *done = 0; for (i = 0; i < n; i++) { struct nl_transaction *txn = transactions[i]; struct nlmsghdr *nlmsg = nl_msg_nlmsghdr(txn->request); nlmsg->nlmsg_len = ofpbuf_size(txn->request); nlmsg->nlmsg_seq = base_seq + i; nlmsg->nlmsg_pid = sock->pid; iovs[i].iov_base = ofpbuf_data(txn->request); iovs[i].iov_len = ofpbuf_size(txn->request); } memset(&msg, 0, sizeof msg); msg.msg_iov = iovs; msg.msg_iovlen = n; do { #ifdef _WIN32 DWORD last_error = 0; bool result = FALSE; for (i = 0; i < n; i++) { result = WriteFile((HANDLE)sock->handle, iovs[i].iov_base, iovs[i].iov_len, &error, NULL); last_error = GetLastError(); if (last_error != ERROR_SUCCESS && !result) { error = EAGAIN; errno = EAGAIN; } else { error = 0; } } #else error = sendmsg(sock->fd, &msg, 0) < 0 ? errno : 0; #endif } while (error == EINTR); for (i = 0; i < n; i++) { struct nl_transaction *txn = transactions[i]; log_nlmsg(__func__, error, ofpbuf_data(txn->request), ofpbuf_size(txn->request), sock->protocol); } if (!error) { COVERAGE_ADD(netlink_sent, n); } if (error) { return error; } ofpbuf_use_stub(&tmp_reply, tmp_reply_stub, sizeof tmp_reply_stub); tmp_txn.request = NULL; tmp_txn.reply = &tmp_reply; tmp_txn.error = 0; while (n > 0) { struct nl_transaction *buf_txn, *txn; uint32_t seq; /* Find a transaction whose buffer we can use for receiving a reply. * If no such transaction is left, use tmp_txn. */ buf_txn = &tmp_txn; for (i = 0; i < n; i++) { if (transactions[i]->reply) { buf_txn = transactions[i]; break; } } /* Receive a reply. */ error = nl_sock_recv__(sock, buf_txn->reply, false); if (error) { if (error == EAGAIN) { nl_sock_record_errors__(transactions, n, 0); *done += n; error = 0; } break; } /* Match the reply up with a transaction. */ seq = nl_msg_nlmsghdr(buf_txn->reply)->nlmsg_seq; if (seq < base_seq || seq >= base_seq + n) { VLOG_DBG_RL(&rl, "ignoring unexpected seq %#"PRIx32, seq); continue; } i = seq - base_seq; txn = transactions[i]; /* Fill in the results for 'txn'. */ if (nl_msg_nlmsgerr(buf_txn->reply, &txn->error)) { if (txn->reply) { ofpbuf_clear(txn->reply); } if (txn->error) { VLOG_DBG_RL(&rl, "received NAK error=%d (%s)", error, ovs_strerror(txn->error)); } } else { txn->error = 0; if (txn->reply && txn != buf_txn) { /* Swap buffers. */ struct ofpbuf *reply = buf_txn->reply; buf_txn->reply = txn->reply; txn->reply = reply; } } /* Fill in the results for transactions before 'txn'. (We have to do * this after the results for 'txn' itself because of the buffer swap * above.) */ nl_sock_record_errors__(transactions, i, 0); /* Advance. */ *done += i + 1; transactions += i + 1; n -= i + 1; base_seq += i + 1; } ofpbuf_uninit(&tmp_reply); return error; }
/* Composes 'fm' so that executing it will implement 'learn' given that the * packet being processed has 'flow' as its flow. * * Uses 'ofpacts' to store the flow mod's actions. The caller must initialize * 'ofpacts' and retains ownership of it. 'fm->ofpacts' will point into the * 'ofpacts' buffer. * * The caller has to actually execute 'fm'. */ void learn_execute(const struct ofpact_learn *learn, const struct flow *flow, struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts) { const struct ofpact_learn_spec *spec; match_init_catchall(&fm->match); fm->priority = learn->priority; fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = htonll(learn->cookie); fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; fm->table_id = learn->table_id; fm->command = OFPFC_MODIFY_STRICT; fm->idle_timeout = learn->idle_timeout; fm->hard_timeout = learn->hard_timeout; fm->buffer_id = UINT32_MAX; fm->out_port = OFPP_NONE; fm->flags = learn->flags; fm->ofpacts = NULL; fm->ofpacts_len = 0; if (learn->fin_idle_timeout || learn->fin_hard_timeout) { struct ofpact_fin_timeout *oft; oft = ofpact_put_FIN_TIMEOUT(ofpacts); oft->fin_idle_timeout = learn->fin_idle_timeout; oft->fin_hard_timeout = learn->fin_hard_timeout; } for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { union mf_subvalue value; int chunk, ofs; if (spec->src_type == NX_LEARN_SRC_FIELD) { mf_read_subfield(&spec->src, flow, &value); } else { value = spec->src_imm; } switch (spec->dst_type) { case NX_LEARN_DST_MATCH: mf_write_subfield(&spec->dst, &value, &fm->match); break; case NX_LEARN_DST_LOAD: for (ofs = 0; ofs < spec->n_bits; ofs += chunk) { struct ofpact_reg_load *load; chunk = MIN(spec->n_bits - ofs, 64); load = ofpact_put_REG_LOAD(ofpacts); load->dst.field = spec->dst.field; load->dst.ofs = spec->dst.ofs + ofs; load->dst.n_bits = chunk; bitwise_copy(&value, sizeof value, ofs, &load->subvalue, sizeof load->subvalue, 0, chunk); } break; case NX_LEARN_DST_OUTPUT: if (spec->n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) { ofp_port_t port = u16_to_ofp(ntohs(value.be16[7])); if (ofp_to_u16(port) < ofp_to_u16(OFPP_MAX) || port == OFPP_IN_PORT || port == OFPP_FLOOD || port == OFPP_LOCAL || port == OFPP_ALL) { ofpact_put_OUTPUT(ofpacts)->port = port; } } break; } } ofpact_pad(ofpacts); fm->ofpacts = ofpbuf_data(ofpacts); fm->ofpacts_len = ofpbuf_size(ofpacts); }