/* 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 dp_packet. The new data is left uninitialized. */ void * dp_packet_push_uninit(struct dp_packet *b, size_t size) { dp_packet_prealloc_headroom(b, size); dp_packet_set_data(b, (char*)dp_packet_data(b) - size); dp_packet_set_size(b, dp_packet_size(b) + size); return dp_packet_data(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 dp_packet_shift(struct dp_packet *b, int delta) { ovs_assert(delta > 0 ? delta <= dp_packet_tailroom(b) : delta < 0 ? -delta <= dp_packet_headroom(b) : true); if (delta != 0) { char *dst = (char *) dp_packet_data(b) + delta; memmove(dst, dp_packet_data(b), dp_packet_size(b)); dp_packet_set_data(b, dst); } }
/* Reallocates 'b' so that it has exactly 'new_headroom' and 'new_tailroom' * bytes of headroom and tailroom, respectively. */ static void dp_packet_resize__(struct dp_packet *b, size_t new_headroom, size_t new_tailroom) { void *new_base, *new_data; size_t new_allocated; new_allocated = new_headroom + dp_packet_size(b) + new_tailroom; switch (b->source) { case DPBUF_DPDK: OVS_NOT_REACHED(); case DPBUF_MALLOC: if (new_headroom == dp_packet_headroom(b)) { new_base = xrealloc(dp_packet_base(b), new_allocated); } else { new_base = xmalloc(new_allocated); dp_packet_copy__(b, new_base, new_headroom, new_tailroom); free(dp_packet_base(b)); } break; case DPBUF_STACK: OVS_NOT_REACHED(); case DPBUF_STUB: b->source = DPBUF_MALLOC; new_base = xmalloc(new_allocated); dp_packet_copy__(b, new_base, new_headroom, new_tailroom); break; default: OVS_NOT_REACHED(); } b->allocated = new_allocated; dp_packet_set_base(b, new_base); new_data = (char *) new_base + new_headroom; if (dp_packet_data(b) != new_data) { if (b->frame) { uintptr_t data_delta = (char *) new_data - (char *) dp_packet_data(b); b->frame = (char *) b->frame + data_delta; } dp_packet_set_data(b, new_data); } }
/* Adjust the size of the l2 portion of the dp_packet, updating the l2 * pointer and the layer offsets. The caller is responsible for * modifying the contents. */ void * dp_packet_resize_l2(struct dp_packet *b, int increment) { dp_packet_resize_l2_5(b, increment); dp_packet_adjust_layer_offset(&b->l2_5_ofs, increment); return dp_packet_data(b); }
/* Reserves 'size' bytes of headroom so that they can be later allocated with * dp_packet_push_uninit() without reallocating the dp_packet. */ void dp_packet_reserve(struct dp_packet *b, size_t size) { ovs_assert(!dp_packet_size(b)); dp_packet_prealloc_tailroom(b, size); dp_packet_set_data(b, (char*)dp_packet_data(b) + size); }
/* Creates and returns a new dp_packet whose data are copied from 'buffer'. The * returned dp_packet will additionally have 'headroom' bytes of headroom. */ struct dp_packet * dp_packet_clone_with_headroom(const struct dp_packet *buffer, size_t headroom) { struct dp_packet *new_buffer; new_buffer = dp_packet_clone_data_with_headroom(dp_packet_data(buffer), dp_packet_size(buffer), headroom); new_buffer->l2_pad_size = buffer->l2_pad_size; new_buffer->l2_5_ofs = buffer->l2_5_ofs; new_buffer->l3_ofs = buffer->l3_ofs; new_buffer->l4_ofs = buffer->l4_ofs; new_buffer->md = buffer->md; new_buffer->cutlen = buffer->cutlen; new_buffer->packet_type = buffer->packet_type; #ifdef DPDK_NETDEV new_buffer->mbuf.ol_flags = buffer->mbuf.ol_flags; #else new_buffer->rss_hash_valid = buffer->rss_hash_valid; #endif if (dp_packet_rss_valid(new_buffer)) { #ifdef DPDK_NETDEV new_buffer->mbuf.hash.rss = buffer->mbuf.hash.rss; #else new_buffer->rss_hash = buffer->rss_hash; #endif } return new_buffer; }
/* Reserves 'headroom' bytes at the head and 'tailroom' at the end so that * they can be later allocated with dp_packet_push_uninit() or * dp_packet_put_uninit() without reallocating the dp_packet. */ void dp_packet_reserve_with_tailroom(struct dp_packet *b, size_t headroom, size_t tailroom) { ovs_assert(!dp_packet_size(b)); dp_packet_prealloc_tailroom(b, headroom + tailroom); dp_packet_set_data(b, (char*)dp_packet_data(b) + headroom); }
/* 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 * dp_packet_new(), then it should still be freed with, e.g., dp_packet_delete().) */ void * dp_packet_steal_data(struct dp_packet *b) { void *p; ovs_assert(b->source != DPBUF_DPDK); if (b->source == DPBUF_MALLOC && dp_packet_data(b) == dp_packet_base(b)) { p = dp_packet_data(b); } else { p = xmemdup(dp_packet_data(b), dp_packet_size(b)); if (b->source == DPBUF_MALLOC) { free(dp_packet_base(b)); } } dp_packet_set_base(b, NULL); dp_packet_set_data(b, NULL); return p; }
/* Returns a string that describes some of 'b''s metadata plus a hex dump of up * to 'maxbytes' from the start of the buffer. */ char * dp_packet_to_string(const struct dp_packet *b, size_t maxbytes) { struct ds s; ds_init(&s); ds_put_format(&s, "size=%"PRIu32", allocated=%"PRIu32", head=%"PRIuSIZE", tail=%"PRIuSIZE"\n", dp_packet_size(b), b->allocated, dp_packet_headroom(b), dp_packet_tailroom(b)); ds_put_hex_dump(&s, dp_packet_data(b), MIN(dp_packet_size(b), maxbytes), 0, false); return ds_cstr(&s); }
/* Creates and returns a new dp_packet whose data are copied from 'buffer'. The * returned dp_packet will additionally have 'headroom' bytes of headroom. */ struct dp_packet * dp_packet_clone_with_headroom(const struct dp_packet *buffer, size_t headroom) { struct dp_packet *new_buffer; new_buffer = dp_packet_clone_data_with_headroom(dp_packet_data(buffer), dp_packet_size(buffer), headroom); if (buffer->frame) { uintptr_t data_delta = (char *)dp_packet_data(new_buffer) - (char *)dp_packet_data(buffer); new_buffer->frame = (char *) buffer->frame + data_delta; } new_buffer->l2_pad_size = buffer->l2_pad_size; new_buffer->l2_5_ofs = buffer->l2_5_ofs; new_buffer->l3_ofs = buffer->l3_ofs; new_buffer->l4_ofs = buffer->l4_ofs; new_buffer->md = buffer->md; return new_buffer; }
/* Adjust the size of the l2_5 portion of the dp_packet, updating the l2 * pointer and the layer offsets. The caller is responsible for * modifying the contents. */ void * dp_packet_resize_l2_5(struct dp_packet *b, int increment) { if (increment >= 0) { dp_packet_push_uninit(b, increment); } else { dp_packet_pull(b, -increment); } /* Adjust layer offsets after l2_5. */ dp_packet_adjust_layer_offset(&b->l3_ofs, increment); dp_packet_adjust_layer_offset(&b->l4_ofs, increment); return dp_packet_data(b); }
/* Creates and returns a new dp_packet whose data are copied from 'buffer'. The * returned dp_packet will additionally have 'headroom' bytes of headroom. */ struct dp_packet * dp_packet_clone_with_headroom(const struct dp_packet *buffer, size_t headroom) { struct dp_packet *new_buffer; new_buffer = dp_packet_clone_data_with_headroom(dp_packet_data(buffer), dp_packet_size(buffer), headroom); new_buffer->l2_pad_size = buffer->l2_pad_size; new_buffer->l2_5_ofs = buffer->l2_5_ofs; new_buffer->l3_ofs = buffer->l3_ofs; new_buffer->l4_ofs = buffer->l4_ofs; new_buffer->md = buffer->md; return new_buffer; }
void * netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl, unsigned int *hlen) { void *nh; struct ip_header *ip; struct ovs_16aligned_ip6_hdr *ip6; void *l4; int l3_size; nh = dp_packet_l3(packet); ip = nh; ip6 = nh; l4 = dp_packet_l4(packet); if (!nh || !l4) { return NULL; } *hlen = sizeof(struct eth_header); l3_size = dp_packet_size(packet) - ((char *)nh - (char *)dp_packet_data(packet)); if (IP_VER(ip->ip_ihl_ver) == 4) { ovs_be32 ip_src, ip_dst; if (csum(ip, IP_IHL(ip->ip_ihl_ver) * 4)) { VLOG_WARN_RL(&err_rl, "ip packet has invalid checksum"); return NULL; } if (ntohs(ip->ip_tot_len) > l3_size) { VLOG_WARN_RL(&err_rl, "ip packet is truncated (IP length %d, actual %d)", ntohs(ip->ip_tot_len), l3_size); return NULL; } if (IP_IHL(ip->ip_ihl_ver) * 4 > sizeof(struct ip_header)) { VLOG_WARN_RL(&err_rl, "ip options not supported on tunnel packets " "(%d bytes)", IP_IHL(ip->ip_ihl_ver) * 4); return NULL; } ip_src = get_16aligned_be32(&ip->ip_src); ip_dst = get_16aligned_be32(&ip->ip_dst); tnl->ip_src = ip_src; tnl->ip_dst = ip_dst; tnl->ip_tos = ip->ip_tos; tnl->ip_ttl = ip->ip_ttl; *hlen += IP_HEADER_LEN; } else if (IP_VER(ip->ip_ihl_ver) == 6) { ovs_be32 tc_flow = get_16aligned_be32(&ip6->ip6_flow); memcpy(tnl->ipv6_src.s6_addr, ip6->ip6_src.be16, sizeof ip6->ip6_src); memcpy(tnl->ipv6_dst.s6_addr, ip6->ip6_dst.be16, sizeof ip6->ip6_dst); tnl->ip_tos = ntohl(tc_flow) >> 20; tnl->ip_ttl = ip6->ip6_hlim; *hlen += IPV6_HEADER_LEN; } else {
static void test_parse_actions(const char *input) { struct shash symtab; struct hmap dhcp_opts; struct hmap dhcpv6_opts; struct hmap nd_ra_opts; struct simap ports; create_symtab(&symtab); create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts); /* Initialize group ids. */ struct ovn_extend_table group_table; ovn_extend_table_init(&group_table); /* Initialize meter ids for QoS. */ struct ovn_extend_table meter_table; ovn_extend_table_init(&meter_table); simap_init(&ports); simap_put(&ports, "eth0", 5); simap_put(&ports, "eth1", 6); simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL)); struct ofpbuf ovnacts; struct expr *prereqs; char *error; puts(input); ofpbuf_init(&ovnacts, 0); const struct ovnact_parse_params pp = { .symtab = &symtab, .dhcp_opts = &dhcp_opts, .dhcpv6_opts = &dhcpv6_opts, .nd_ra_opts = &nd_ra_opts, .n_tables = 24, .cur_ltable = 10, }; error = ovnacts_parse_string(input, &pp, &ovnacts, &prereqs); if (!error) { /* Convert the parsed representation back to a string and print it, * if it's different from the input. */ struct ds ovnacts_s = DS_EMPTY_INITIALIZER; ovnacts_format(ovnacts.data, ovnacts.size, &ovnacts_s); if (strcmp(input, ds_cstr(&ovnacts_s))) { printf(" formats as %s\n", ds_cstr(&ovnacts_s)); } /* Encode the actions into OpenFlow and print. */ const struct ovnact_encode_params ep = { .lookup_port = lookup_port_cb, .aux = &ports, .is_switch = true, .group_table = &group_table, .meter_table = &meter_table, .pipeline = OVNACT_P_INGRESS, .ingress_ptable = 8, .egress_ptable = 40, .output_ptable = 64, .mac_bind_ptable = 65, }; struct ofpbuf ofpacts; ofpbuf_init(&ofpacts, 0); ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts); struct ds ofpacts_s = DS_EMPTY_INITIALIZER; struct ofpact_format_params fp = { .s = &ofpacts_s }; ofpacts_format(ofpacts.data, ofpacts.size, &fp); printf(" encodes as %s\n", ds_cstr(&ofpacts_s)); ds_destroy(&ofpacts_s); ofpbuf_uninit(&ofpacts); /* Print prerequisites if any. */ if (prereqs) { struct ds prereqs_s = DS_EMPTY_INITIALIZER; expr_format(prereqs, &prereqs_s); printf(" has prereqs %s\n", ds_cstr(&prereqs_s)); ds_destroy(&prereqs_s); } /* Now re-parse and re-format the string to verify that it's * round-trippable. */ struct ofpbuf ovnacts2; struct expr *prereqs2; ofpbuf_init(&ovnacts2, 0); error = ovnacts_parse_string(ds_cstr(&ovnacts_s), &pp, &ovnacts2, &prereqs2); if (!error) { struct ds ovnacts2_s = DS_EMPTY_INITIALIZER; ovnacts_format(ovnacts2.data, ovnacts2.size, &ovnacts2_s); if (strcmp(ds_cstr(&ovnacts_s), ds_cstr(&ovnacts2_s))) { printf(" bad reformat: %s\n", ds_cstr(&ovnacts2_s)); } ds_destroy(&ovnacts2_s); } else { printf(" reparse error: %s\n", error); free(error); } expr_destroy(prereqs2); ovnacts_free(ovnacts2.data, ovnacts2.size); ofpbuf_uninit(&ovnacts2); ds_destroy(&ovnacts_s); } else { printf(" %s\n", error); free(error); } expr_destroy(prereqs); ovnacts_free(ovnacts.data, ovnacts.size); ofpbuf_uninit(&ovnacts); simap_destroy(&ports); expr_symtab_destroy(&symtab); shash_destroy(&symtab); dhcp_opts_destroy(&dhcp_opts); dhcp_opts_destroy(&dhcpv6_opts); nd_ra_opts_destroy(&nd_ra_opts); ovn_extend_table_destroy(&group_table); ovn_extend_table_destroy(&meter_table); } static void test_parse_expr(const char *input) { struct shash symtab; struct shash addr_sets; struct shash port_groups; struct simap ports; struct expr *expr; char *error; create_symtab(&symtab); create_addr_sets(&addr_sets); create_port_groups(&port_groups); simap_init(&ports); simap_put(&ports, "eth0", 5); simap_put(&ports, "eth1", 6); simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL)); simap_put(&ports, "lsp1", 0x11); simap_put(&ports, "lsp2", 0x12); simap_put(&ports, "lsp3", 0x13); expr = expr_parse_string(input, &symtab, &addr_sets, &port_groups, &error); if (!error) { expr = expr_annotate(expr, &symtab, &error); } if (!error) { expr = expr_simplify(expr, is_chassis_resident_cb, &ports); expr = expr_normalize(expr); ovs_assert(expr_is_normalized(expr)); } if (!error) { struct hmap matches; expr_to_matches(expr, lookup_port_cb, &ports, &matches); expr_matches_print(&matches, stdout); expr_matches_destroy(&matches); } else { puts(error); free(error); } expr_destroy(expr); simap_destroy(&ports); expr_symtab_destroy(&symtab); shash_destroy(&symtab); expr_const_sets_destroy(&addr_sets); shash_destroy(&addr_sets); expr_const_sets_destroy(&port_groups); shash_destroy(&port_groups); } static bool lookup_atoi_cb(const void *aux OVS_UNUSED, const char *port_name, unsigned int *portp) { *portp = atoi(port_name); return true; } static void test_expr_to_packets(const char *input) { struct shash symtab; create_symtab(&symtab); struct flow uflow; char *error = expr_parse_microflow(input, &symtab, NULL, NULL, lookup_atoi_cb, NULL, &uflow); if (error) { puts(error); free(error); expr_symtab_destroy(&symtab); shash_destroy(&symtab); return; } uint64_t packet_stub[128 / 8]; struct dp_packet packet; dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub); flow_compose(&packet, &uflow, NULL, 64); struct ds output = DS_EMPTY_INITIALIZER; const uint8_t *buf = dp_packet_data(&packet); for (int i = 0; i < dp_packet_size(&packet); i++) { uint8_t val = buf[i]; ds_put_format(&output, "%02"PRIx8, val); } puts(ds_cstr(&output)); ds_destroy(&output); dp_packet_uninit(&packet); expr_symtab_destroy(&symtab); shash_destroy(&symtab); } int LLVMFuzzerTestOneInput(const uint8_t *input_, size_t size) { /* Bail out if we cannot construct at least a 1 char string. */ const char *input = (const char *) input_; if (size < 2 || input[size - 1] != '\0' || strchr(input, '\n') || strlen(input) != size - 1) { return 0; } /* Disable logging to avoid write to disk. */ static bool isInit = false; if (!isInit) { vlog_set_verbosity("off"); isInit = true; } /* Parse, annotate, simplify, normalize expr and convert to flows. */ test_parse_expr(input); /* Parse actions. */ test_parse_actions(input); /* Test OVN lexer. */ test_lex(input); /* Expr to packets. */ test_expr_to_packets(input); return 0; }
void *dpp_data(const struct dp_packet* pkt) { return dp_packet_data(pkt); }