pcapng: write packet and Netflix custom blocks the same as other blocks.

Use compute_options_size() to get the total size of all the options, and
use write_options() to write out the options for those blocks, as we do
for other blocks.

Get rid of wtap_block_option_get_value_size() and
wtap_block_get_options_size_padded(); they're no longer needed, and
their notion of an option's "size" is "size in a pcapng file", so that
doesn't belong in code that's intended to support all file types.
This commit is contained in:
Guy Harris 2021-08-30 14:36:44 -07:00
parent 510c088ce6
commit 030b06ba3c
4 changed files with 449 additions and 283 deletions

View File

@ -36,6 +36,7 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_block_add_ipv4_option@Base 2.1.2
wtap_block_add_ipv6_option@Base 2.1.2
wtap_block_add_nflx_custom_option@Base 3.5.0
wtap_block_add_packet_verdict_option@Base 3.5.1
wtap_block_add_string_option@Base 2.1.2
wtap_block_add_string_option_format@Base 2.1.2
wtap_block_add_uint32_option@Base 3.5.0
@ -53,15 +54,14 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_block_get_mandatory_data@Base 2.1.2
wtap_block_get_nflx_custom_option@Base 3.5.0
wtap_block_get_nth_bytes_option_value@Base 3.5.0
wtap_block_get_nth_packet_verdict_option_value@Base 3.5.1
wtap_block_get_nth_string_option_value@Base 2.1.2
wtap_block_get_options_size_padded@Base 3.5.0
wtap_block_get_string_option_value@Base 2.1.2
wtap_block_get_type@Base 3.5.0
wtap_block_get_uint32_option_value@Base 3.5.0
wtap_block_get_uint64_option_value@Base 2.1.2
wtap_block_get_uint8_option_value@Base 2.1.2
wtap_block_make_copy@Base 3.3.2
wtap_block_option_get_value_size@Base 3.5.0
wtap_block_ref@Base 3.5.0
wtap_block_remove_nth_option_instance@Base 2.2.0
wtap_block_remove_option@Base 2.2.0
@ -70,6 +70,7 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_block_set_ipv4_option_value@Base 2.1.2
wtap_block_set_ipv6_option_value@Base 2.1.2
wtap_block_set_nth_bytes_option_value@Base 3.5.0
wtap_block_set_nth_packet_verdict_option_value@Base 3.5.1
wtap_block_set_nth_string_option_value@Base 2.1.2
wtap_block_set_nth_string_option_value_format@Base 3.5.0
wtap_block_set_string_option_value@Base 2.1.2
@ -151,6 +152,7 @@ libwiretap.so.0 libwiretap0 #MINVER#
wtap_open_offline@Base 1.9.1
wtap_opttypes_initialize@Base 2.1.2
wtap_opttypes_cleanup@Base 2.3.0
wtap_packet_verdict_free@Base 3.5.1
wtap_pcap_encap_to_wtap_encap@Base 1.9.1
wtap_pcap_file_type_subtype@Base 3.5.0
wtap_pcap_nsec_file_type_subtype@Base 3.5.0

View File

@ -1639,6 +1639,7 @@ pcapng_process_packet_block_option(wtapng_block_t *wblock,
int *err, gchar **err_info)
{
guint64 tmp64;
packet_verdict_opt_t packet_verdict;
/*
* Handle option content.
@ -1725,7 +1726,11 @@ pcapng_process_packet_block_option(wtapng_block_t *wblock,
switch (option_content[0]) {
case(OPT_VERDICT_TYPE_HW):
/* No byte swapping needed */
packet_verdict.type = packet_verdict_hardware;
packet_verdict.data.verdict_bytes =
g_byte_array_new_take((guint8 *)g_memdup2(&option_content[1],
option_length - 1),
option_length - 1);
break;
case(OPT_VERDICT_TYPE_TC):
@ -1736,11 +1741,15 @@ pcapng_process_packet_block_option(wtapng_block_t *wblock,
/* XXX - free anything? */
return FALSE;
}
if (section_info->byte_swapped) {
memcpy(&tmp64, option_content + 1, sizeof(tmp64));
/* Don't cast a guint8 * into a guint64 *--the
* guint8 * may not point to something that's
* aligned correctly.
*/
memcpy(&tmp64, &option_content[1], sizeof(guint64));
if (section_info->byte_swapped)
tmp64 = GUINT64_SWAP_LE_BE(tmp64);
memcpy((void *)(option_content + 1), &tmp64, sizeof(tmp64));
}
packet_verdict.type = packet_verdict_linux_ebpf_tc;
packet_verdict.data.verdict_linux_ebpf_tc = tmp64;
break;
case(OPT_VERDICT_TYPE_XDP):
@ -1751,24 +1760,32 @@ pcapng_process_packet_block_option(wtapng_block_t *wblock,
/* XXX - free anything? */
return FALSE;
}
if (section_info->byte_swapped) {
memcpy(&tmp64, option_content + 1, sizeof(tmp64));
/* Don't cast a guint8 * into a guint64 *--the
* guint8 * may not point to something that's
* aligned correctly.
*/
memcpy(&tmp64, &option_content[1], sizeof(guint64));
if (section_info->byte_swapped)
tmp64 = GUINT64_SWAP_LE_BE(tmp64);
memcpy((void*)(option_content + 1), &tmp64, sizeof(tmp64));
}
packet_verdict.type = packet_verdict_linux_ebpf_xdp;
packet_verdict.data.verdict_linux_ebpf_xdp = tmp64;
break;
default:
/* Silently ignore unknown verdict types */
return TRUE;
}
pcapng_process_bytes_option(wblock, option_code, option_length, (void*)option_content);
wtap_block_add_packet_verdict_option(wblock->block, option_code, &packet_verdict);
wtap_packet_verdict_free(&packet_verdict);
ws_debug("verdict type %u, data len %u",
option_content[0], option_length - 1);
break;
default:
ws_debug("unknown option %u - ignoring %u bytes",
option_code, option_length);
if (!pcapng_process_unhandled_option(wblock, BT_INDEX_PBS,
section_info, option_code,
option_length, option_content,
err, err_info))
return FALSE;
break;
}
return TRUE;
@ -3829,7 +3846,7 @@ static guint32 pcapng_compute_custom_option_size(wtap_optval_t *optval)
size = sizeof(guint32);
switch (optval->custom_opt.pen) {
case PEN_NFLX:
/* PEN */
/* NFLX type */
size += sizeof(guint32);
size += optval->custom_opt.data.nflx_data.custom_data_len;
break;
@ -3851,6 +3868,39 @@ static guint32 pcapng_compute_custom_option_size(wtap_optval_t *optval)
return (guint32)size;
}
static guint32 pcapng_compute_packet_verdict_option_size(wtap_optval_t *optval)
{
packet_verdict_opt_t* verdict = &optval->packet_verdictval;
guint32 size;
guint32 pad;
switch (verdict->type) {
case packet_verdict_hardware:
size = verdict->data.verdict_bytes->len;
break;
case packet_verdict_linux_ebpf_tc:
size = 9;
break;
case packet_verdict_linux_ebpf_xdp:
size = 9;
break;
default:
size = 0;
break;
}
if ((size % 4)) {
pad = 4 - (size % 4);
} else {
pad = 0;
}
size += pad;
return size;
}
static gboolean
compute_block_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type, wtap_optval_t *optval, void *user_data)
{
@ -4097,6 +4147,7 @@ static gboolean pcapng_write_string_option(wtap_dumper *wdh, guint option_id, wt
return TRUE;
}
#if 0
static gboolean pcapng_write_bytes_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
{
struct pcapng_option_header option_hdr;
@ -4177,6 +4228,7 @@ static gboolean pcapng_write_ipv6_option(wtap_dumper *wdh, guint option_id, wtap
return TRUE;
}
#endif
static gboolean pcapng_write_if_filter_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
{
@ -4363,6 +4415,99 @@ static gboolean pcapng_write_custom_option(wtap_dumper *wdh, guint option_id, wt
return TRUE;
}
static gboolean pcapng_write_packet_verdict_option(wtap_dumper *wdh, guint option_id, wtap_optval_t *optval, int *err)
{
packet_verdict_opt_t* verdict = &optval->packet_verdictval;
struct pcapng_option_header option_hdr;
guint8 type;
size_t size;
const guint32 zero_pad = 0;
guint32 pad;
switch (verdict->type) {
case packet_verdict_hardware:
size = verdict->data.verdict_bytes->len;
if (size > 65535) {
/*
* Too big to fit in the option.
* Don't write anything.
*
* XXX - truncate it? Report an error?
*/
return TRUE;
}
option_hdr.type = option_id;
option_hdr.value_length = (guint16)size;
if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
return FALSE;
wdh->bytes_dumped += 4;
type = packet_verdict_hardware;
if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
return FALSE;
wdh->bytes_dumped += 1;
if (!wtap_dump_file_write(wdh, verdict->data.verdict_bytes->data, size,
err))
return FALSE;
wdh->bytes_dumped += size;
break;
case packet_verdict_linux_ebpf_tc:
size = 9;
option_hdr.type = option_id;
option_hdr.value_length = (guint16)size;
if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
return FALSE;
wdh->bytes_dumped += 4;
type = packet_verdict_linux_ebpf_tc;
if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
return FALSE;
wdh->bytes_dumped += 1;
if (!wtap_dump_file_write(wdh, &verdict->data.verdict_linux_ebpf_tc,
sizeof(guint64), err))
return FALSE;
wdh->bytes_dumped += 8;
break;
case packet_verdict_linux_ebpf_xdp:
size = 9;
option_hdr.type = option_id;
option_hdr.value_length = (guint16)size;
if (!wtap_dump_file_write(wdh, &option_hdr, 4, err))
return FALSE;
wdh->bytes_dumped += 4;
type = packet_verdict_linux_ebpf_xdp;
if (!wtap_dump_file_write(wdh, &type, sizeof(guint8), err))
return FALSE;
wdh->bytes_dumped += 1;
if (!wtap_dump_file_write(wdh, &verdict->data.verdict_linux_ebpf_xdp,
sizeof(guint64), err))
return FALSE;
wdh->bytes_dumped += 8;
break;
default:
/* Unknown - don't write it out. */
return TRUE;
}
/* write padding (if any) */
if ((size % 4)) {
pad = 4 - (size % 4);
if (!wtap_dump_file_write(wdh, &zero_pad, pad, err))
return FALSE;
wdh->bytes_dumped += pad;
}
return TRUE;
}
static gboolean write_block_option(wtap_block_t block, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, void* user_data)
{
write_options_t* write_options = (write_options_t*)user_data;
@ -4393,8 +4538,9 @@ static gboolean write_block_option(wtap_block_t block, guint option_id, wtap_opt
*/
break;
default:
/* Block-type dependent; call the callback. */
if (!(*write_options->write_option)(write_options->wdh, block, option_id, option_type, optval, write_options->err))
/* Block-type dependent; call the callback, if we have one. */
if (write_options->write_option != NULL &&
!(*write_options->write_option)(write_options->wdh, block, option_id, option_type, optval, write_options->err))
return FALSE;
break;
}
@ -4485,8 +4631,8 @@ pcapng_write_section_header_block(wtap_dumper *wdh, int *err)
wdh->bytes_dumped += sizeof shb;
if (wdh_shb) {
/* Write options, if we have any */
if (options_size != 0) {
/* Write options */
if (!write_options(wdh, wdh_shb, write_wtap_shb_option, err))
return FALSE;
}
@ -4501,67 +4647,114 @@ pcapng_write_section_header_block(wtap_dumper *wdh, int *err)
return TRUE;
}
typedef struct pcapng_write_block_s
{
wtap_dumper *wdh;
int *err;
}
pcapng_write_block_t;
/* Helper function used in pcapng_write_enhanced_packet_block().
* Meant to be generic enough to be used elsewhere too, but currently,
* only WTAP_OPTTYPE_STRING and WTAP_OPTTYPE_BYTES are exercised (and thus tested).
* This is a wtap_block_foreach_func.
/* options defined in Section 2.5 (Options)
* Name Code Length Description
* opt_comment 1 variable A UTF-8 string containing a comment that is associated to the current block.
*
* Enhanced Packet Block options
* epb_flags 2 4 A flags word containing link-layer information. A complete specification of
* the allowed flags can be found in Appendix A (Packet Block Flags Word).
* epb_hash 3 variable This option contains a hash of the packet. The first byte specifies the hashing algorithm,
* while the following bytes contain the actual hash, whose size depends on the hashing algorithm,
* and hence from the value in the first bit. The hashing algorithm can be: 2s complement
* (algorithm byte = 0, size=XXX), XOR (algorithm byte = 1, size=XXX), CRC32 (algorithm byte = 2, size = 4),
* MD-5 (algorithm byte = 3, size=XXX), SHA-1 (algorithm byte = 4, size=XXX).
* The hash covers only the packet, not the header added by the capture driver:
* this gives the possibility to calculate it inside the network card.
* The hash allows easier comparison/merging of different capture files, and reliable data transfer between the
* data acquisition system and the capture library.
* epb_dropcount 4 8 A 64bit integer value specifying the number of packets lost (by the interface and the operating system)
* between this packet and the preceding one.
* epb_packetid 5 8 The epb_packetid option is a 64-bit unsigned integer that
* uniquely identifies the packet. If the same packet is seen
* by multiple interfaces and there is a way for the capture
* application to correlate them, the same epb_packetid value
* must be used. An example could be a router that captures
* packets on all its interfaces in both directions. When a
* packet hits interface A on ingress, an EPB entry gets
* created, TTL gets decremented, and right before it egresses
* on interface B another EPB entry gets created in the trace
* file. In this case, two packets are in the capture file,
* which are not identical but the epb_packetid can be used to
* correlate them.
* epb_queue 6 4 The epb_queue option is a 32-bit unsigned integer that
* identifies on which queue of the interface the specific
* packet was received.
* epb_verdict 7 variable The epb_verdict option stores a verdict of the packet. The
* verdict indicates what would be done with the packet after
* processing it. For example, a firewall could drop the
* packet. This verdict can be set by various components, i.e.
* Hardware, Linux's eBPF TC or XDP framework, etc. etc. The
* first octet specifies the verdict type, while the following
* octets contain the actual verdict data, whose size depends on
* the verdict type, and hence from the value in the first
* octet. The verdict type can be: Hardware (type octet = 0,
* size = variable), Linux_eBPF_TC (type octet = 1, size = 8
* (64-bit unsigned integer), value = TC_ACT_* as defined in the
* Linux pck_cls.h include), Linux_eBPF_XDP (type octet = 2,
* size = 8 (64-bit unsigned integer), value = xdp_action as
* defined in the Linux pbf.h include).
* opt_endofopt 0 0 It delimits the end of the optional fields. This block cannot be repeated within a given list of options.
*/
static gboolean
pcapng_write_option_cb(wtap_block_t block _U_, guint option_id _U_, wtap_opttype_e option_type, wtap_optval_t *option, void *user_data)
static guint32
compute_epb_option_size(wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t* optval)
{
pcapng_write_block_t* write_block = (pcapng_write_block_t*)user_data;
gboolean ok = TRUE;
guint32 size;
/* The functions below write their option type and length fields, and any needed padding. */
switch(option_type)
switch(option_id)
{
case WTAP_OPTTYPE_UINT8:
ok = pcapng_write_uint8_option(write_block->wdh, option_id, option, write_block->err);
case OPT_EPB_FLAGS:
size = 4;
break;
case WTAP_OPTTYPE_UINT32:
ok = pcapng_write_uint32_option(write_block->wdh, option_id, option, write_block->err);
case OPT_EPB_DROPCOUNT:
size = 8;
break;
case WTAP_OPTTYPE_UINT64:
ok = pcapng_write_uint64_option(write_block->wdh, option_id, option, write_block->err);
case OPT_EPB_PACKETID:
size = 8;
break;
case WTAP_OPTTYPE_IPv4:
// wtap_opttypes.h implies this is stored in network byte order
ok = pcapng_write_ipv4_option(write_block->wdh, option_id, option, write_block->err);
case OPT_EPB_QUEUE:
size = 4;
break;
case WTAP_OPTTYPE_IPv6:
ok = pcapng_write_ipv6_option(write_block->wdh, option_id, option, write_block->err);
case OPT_EPB_VERDICT:
size = pcapng_compute_packet_verdict_option_size(optval);
break;
case WTAP_OPTTYPE_STRING:
ok = pcapng_write_string_option(write_block->wdh, option_id, option, write_block->err);
break;
case WTAP_OPTTYPE_BYTES:
ok = pcapng_write_bytes_option(write_block->wdh, option_id, option, write_block->err);
break;
case WTAP_OPTTYPE_IF_FILTER:
ok = pcapng_write_if_filter_option(write_block->wdh, option_id, option, write_block->err);
break;
case WTAP_OPTTYPE_CUSTOM:
ok = pcapng_write_custom_option(write_block->wdh, option_id, option, write_block->err);
default:
/* Unknown options - size by datatype? */
size = 0;
break;
}
return size;
}
return ok;
static gboolean write_wtap_epb_option(wtap_dumper *wdh, wtap_block_t block _U_, guint option_id, wtap_opttype_e option_type _U_, wtap_optval_t *optval, int *err)
{
switch(option_id)
{
case OPT_PKT_FLAGS:
if (!pcapng_write_uint32_option(wdh, OPT_EPB_FLAGS, optval, err))
return FALSE;
break;
case OPT_PKT_DROPCOUNT:
if (!pcapng_write_uint64_option(wdh, OPT_EPB_DROPCOUNT, optval, err))
return FALSE;
break;
case OPT_PKT_PACKETID:
if (!pcapng_write_uint64_option(wdh, OPT_EPB_PACKETID, optval, err))
return FALSE;
break;
case OPT_PKT_QUEUE:
if (!pcapng_write_uint32_option(wdh, OPT_EPB_QUEUE, optval, err))
return FALSE;
break;
case OPT_PKT_VERDICT:
if (!pcapng_write_packet_verdict_option(wdh, OPT_EPB_QUEUE, optval,
err))
break;
default:
/* Unknown options - write by datatype? */
break;
}
return TRUE; /* success */
}
static gboolean
@ -4571,19 +4764,14 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec,
const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
pcapng_block_header_t bh;
pcapng_enhanced_packet_block_t epb;
guint32 options_size = 0;
guint64 ts;
const guint32 zero_pad = 0;
guint32 pad_len;
guint32 phdr_len;
gboolean have_options = FALSE;
guint32 options_total_length = 0;
gsize options_len = 0;
wtap_block_t int_data;
wtapng_if_descr_mandatory_t *int_data_mand;
pcapng_write_block_t block_data;
block_data.wdh = wdh;
block_data.err = err;
/* Don't write anything we're not willing to read. */
if (rec->rec_header.packet_header.caplen > wtap_max_snaplen_for_encap(wdh->encap)) {
@ -4597,24 +4785,15 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec,
} else {
pad_len = 0;
}
if (rec->block != NULL) {
// Remember to also add newly-supported option types to packet_block_options_supported
// below.
options_len = wtap_block_get_options_size_padded(rec->block);
if (options_len > 0) {
have_options = TRUE;
options_total_length += (guint32)options_len;
}
}
if (have_options) {
/* End-of options tag */
options_total_length += 4;
if (rec->block != NULL) {
/* Compute size of all the options */
options_size = compute_options_size(rec->block, compute_epb_option_size);
}
/* write (enhanced) packet block header */
bh.block_type = BLOCK_TYPE_EPB;
bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + rec->rec_header.packet_header.caplen + pad_len + options_total_length + 4;
bh.block_total_length = (guint32)sizeof(bh) + (guint32)sizeof(epb) + phdr_len + rec->rec_header.packet_header.caplen + pad_len + options_total_length + options_size + 4;
if (!wtap_dump_file_write(wdh, &bh, sizeof bh, err))
return FALSE;
@ -4687,65 +4866,9 @@ pcapng_write_enhanced_packet_block(wtap_dumper *wdh, const wtap_rec *rec,
wdh->bytes_dumped += pad_len;
}
/* XXX - write (optional) block options */
/* options defined in Section 2.5 (Options)
* Name Code Length Description
* opt_comment 1 variable A UTF-8 string containing a comment that is associated to the current block.
* opt_custom 2988 variable A UTF-8 string which can be copied.
* opt_custom 2989 variable Binary octets which can be copied.
* opt_custom 19372 variable A UTF-8 string which should not be copied.
* opt_custom 19373 variable Binary octets which should not be copied.
* Enhanced Packet Block options
* epb_flags 2 4 A flags word containing link-layer information. A complete specification of
* the allowed flags can be found in Appendix A (Packet Block Flags Word).
* epb_hash 3 variable This option contains a hash of the packet. The first byte specifies the hashing algorithm,
* while the following bytes contain the actual hash, whose size depends on the hashing algorithm,
* and hence from the value in the first bit. The hashing algorithm can be: 2s complement
* (algorithm byte = 0, size=XXX), XOR (algorithm byte = 1, size=XXX), CRC32 (algorithm byte = 2, size = 4),
* MD-5 (algorithm byte = 3, size=XXX), SHA-1 (algorithm byte = 4, size=XXX).
* The hash covers only the packet, not the header added by the capture driver:
* this gives the possibility to calculate it inside the network card.
* The hash allows easier comparison/merging of different capture files, and reliable data transfer between the
* data acquisition system and the capture library.
* epb_dropcount 4 8 A 64bit integer value specifying the number of packets lost (by the interface and the operating system)
* between this packet and the preceding one.
* epb_packetid 5 8 The epb_packetid option is a 64-bit unsigned integer that
* uniquely identifies the packet. If the same packet is seen
* by multiple interfaces and there is a way for the capture
* application to correlate them, the same epb_packetid value
* must be used. An example could be a router that captures
* packets on all its interfaces in both directions. When a
* packet hits interface A on ingress, an EPB entry gets
* created, TTL gets decremented, and right before it egresses
* on interface B another EPB entry gets created in the trace
* file. In this case, two packets are in the capture file,
* which are not identical but the epb_packetid can be used to
* correlate them.
* epb_queue 6 4 The epb_queue option is a 32-bit unsigned integer that
* identifies on which queue of the interface the specific
* packet was received.
* epb_verdict 7 variable The epb_verdict option stores a verdict of the packet. The
* verdict indicates what would be done with the packet after
* processing it. For example, a firewall could drop the
* packet. This verdict can be set by various components, i.e.
* Hardware, Linux's eBPF TC or XDP framework, etc. etc. The
* first octet specifies the verdict type, while the following
* octets contain the actual verdict data, whose size depends on
* the verdict type, and hence from the value in the first
* octet. The verdict type can be: Hardware (type octet = 0,
* size = variable), Linux_eBPF_TC (type octet = 1, size = 8
* (64-bit unsigned integer), value = TC_ACT_* as defined in the
* Linux pck_cls.h include), Linux_eBPF_XDP (type octet = 2,
* size = 8 (64-bit unsigned integer), value = xdp_action as
* defined in the Linux pbf.h include).
* opt_endofopt 0 0 It delimits the end of the optional fields. This block cannot be repeated within a given list of options.
*/
if (!wtap_block_foreach_option(rec->block, pcapng_write_option_cb, &block_data)) {
return FALSE;
}
/* Write end of options if we have options */
if (have_options) {
if (!pcapng_write_option_eofopt(wdh, err))
/* Write options, if we have any */
if (options_size != 0) {
if (!write_options(wdh, rec->block, write_wtap_epb_option, err))
return FALSE;
}
@ -4994,15 +5117,15 @@ pcapng_write_bblog_block(wtap_dumper *wdh, const wtap_rec *rec,
const guint8 *pd _U_, int *err)
{
pcapng_block_header_t bh;
pcapng_write_block_t block_data;
gsize options_len;
guint32 options_size = 0;
guint32 pen, skipped, type;
options_len = wtap_block_get_options_size_padded(rec->block) + 4;
/* Compute size of all the options */
options_size = compute_options_size(rec->block, compute_epb_option_size);
/* write block header */
bh.block_type = BLOCK_TYPE_CB_COPY;
bh.block_total_length = (guint32)(sizeof(bh) + sizeof(guint32) + sizeof(guint32) + options_len + 4);
bh.block_total_length = (guint32)(sizeof(bh) + sizeof(guint32) + sizeof(guint32) + options_size + 4);
if (rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.type == BBLOG_TYPE_SKIPPED_BLOCK) {
bh.block_total_length += (guint32)sizeof(guint32);
}
@ -5038,14 +5161,14 @@ pcapng_write_bblog_block(wtap_dumper *wdh, const wtap_rec *rec,
ws_debug("wrote skipped = %u", rec->rec_header.custom_block_header.custom_data_header.nflx_custom_data_header.skipped);
}
block_data.wdh = wdh;
block_data.err = err;
if (!wtap_block_foreach_option(rec->block, pcapng_write_option_cb, &block_data)) {
return FALSE;
}
/* Write end of options if we have options */
if (!pcapng_write_option_eofopt(wdh, err)) {
return FALSE;
/* Write options, if we have any */
if (options_size != 0) {
/*
* This block type supports only comments and custom options,
* so it doesn't need a callback.
*/
if (!write_options(wdh, rec->block, NULL, err))
return FALSE;
}
/* write block footer */

View File

@ -143,6 +143,57 @@ static void if_filter_free(if_filter_opt_t* filter_src)
}
}
static packet_verdict_opt_t
packet_verdict_dup(packet_verdict_opt_t* verdict_src)
{
packet_verdict_opt_t verdict_dest;
memset(&verdict_dest, 0, sizeof(verdict_dest));
/* Deep copy. */
verdict_dest.type = verdict_src->type;
switch (verdict_src->type) {
case packet_verdict_hardware:
/* array of octets */
verdict_dest.data.verdict_bytes =
g_byte_array_new_take((guint8 *)g_memdup2(verdict_src->data.verdict_bytes->data,
verdict_src->data.verdict_bytes->len),
verdict_src->data.verdict_bytes->len);
break;
case packet_verdict_linux_ebpf_tc:
/* eBPF TC_ACT_ value */
verdict_dest.data.verdict_linux_ebpf_tc =
verdict_src->data.verdict_linux_ebpf_tc;
break;
case packet_verdict_linux_ebpf_xdp:
/* xdp_action value */
verdict_dest.data.verdict_linux_ebpf_xdp =
verdict_src->data.verdict_linux_ebpf_xdp;
break;
default:
break;
}
return verdict_dest;
}
void wtap_packet_verdict_free(packet_verdict_opt_t* verdict)
{
switch (verdict->type) {
case packet_verdict_hardware:
/* array of bytes */
g_byte_array_free(verdict->data.verdict_bytes, TRUE);
break;
default:
break;
}
}
static void wtap_opttype_block_register(wtap_blocktype_t *blocktype)
{
wtap_block_type_t block_type;
@ -309,6 +360,10 @@ static void wtap_block_free_option(wtap_block_t block, wtap_option_t *opt)
}
break;
case WTAP_OPTTYPE_PACKET_VERDICT:
wtap_packet_verdict_free(&opt->value.packet_verdictval);
break;
default:
break;
}
@ -449,6 +504,10 @@ wtap_block_copy(wtap_block_t dest_block, wtap_block_t src_block)
break;
}
break;
case WTAP_OPTTYPE_PACKET_VERDICT:
wtap_block_add_packet_verdict_option(dest_block, src_opt->option_id, &src_opt->value.packet_verdictval);
break;
}
}
}
@ -462,109 +521,6 @@ wtap_block_t wtap_block_make_copy(wtap_block_t block)
return block_copy;
}
/*
* Get the (un-padded) size of the given option value, by its type.
*/
gsize
wtap_block_option_get_value_size(wtap_opttype_e option_type, wtap_optval_t *option)
{
gsize ret_val = 0;
switch(option_type) {
case WTAP_OPTTYPE_UINT8:
ret_val += 1;
break;
case WTAP_OPTTYPE_UINT32:
ret_val += 4;
break;
case WTAP_OPTTYPE_UINT64:
ret_val += 8;
break;
case WTAP_OPTTYPE_IPv4:
ret_val += 4;
break;
case WTAP_OPTTYPE_IPv6:
ret_val += IPv6_ADDR_SIZE;
break;
case WTAP_OPTTYPE_STRING:
ret_val += strlen(option->stringval);
break;
case WTAP_OPTTYPE_BYTES:
ret_val += g_bytes_get_size(option->byteval);
break;
case WTAP_OPTTYPE_IF_FILTER:
switch(option->if_filterval.type) {
case if_filter_pcap:
ret_val += 1 + strlen(option->if_filterval.data.filter_str);
break;
case if_filter_bpf:
ret_val += 1 + (8 * option->if_filterval.data.bpf_prog.bpf_prog_len);
break;
}
break;
case WTAP_OPTTYPE_CUSTOM:
/* PEN */
ret_val += sizeof(guint32);
switch (option->custom_opt.pen) {
case PEN_NFLX:
/* NFLX type */
ret_val += sizeof(guint32);
ret_val += option->custom_opt.data.nflx_data.custom_data_len;
break;
default:
ret_val += option->custom_opt.data.generic_data.custom_data_len;
break;
}
break;
}
return ret_val;
}
/*
* Get the size of all options, padded to a 32-bit boundary.
*/
gsize
wtap_block_get_options_size_padded(wtap_block_t block)
{
gsize ret_val = 0;
gsize opt_size = 0;
guint i;
wtap_option_t *opt;
const wtap_opttype_t *opttype;
if (block == NULL) {
return 0;
}
for (i = 0; i < block->options->len; i++)
{
opt = &g_array_index(block->options, wtap_option_t, i);
opttype = GET_OPTION_TYPE(block->info->options, opt->option_id);
opt_size = ROUND_TO_4BYTE(wtap_block_option_get_value_size(opttype->data_type, &opt->value));
/* pcapng.c silently skips over data that's too big to fit in an option.
* Reflect that when calculating the size of our options.
*/
if (opt_size <= 0xffff) {
ret_val += opt_size + 4; // 4 for size of type and length fields themselves
}
}
return ret_val;
}
guint
wtap_block_count_option(wtap_block_t block, guint option_id)
{
@ -1428,6 +1384,50 @@ wtap_block_add_custom_option(wtap_block_t block, guint option_id, guint32 pen, c
return WTAP_OPTTYPE_SUCCESS;
}
wtap_opttype_return_val
wtap_block_add_packet_verdict_option(wtap_block_t block, guint option_id, packet_verdict_opt_t* value)
{
wtap_opttype_return_val ret;
wtap_option_t *opt;
ret = wtap_block_add_option_common(block, option_id, WTAP_OPTTYPE_PACKET_VERDICT, &opt);
if (ret != WTAP_OPTTYPE_SUCCESS)
return ret;
opt->value.packet_verdictval = packet_verdict_dup(value);
return WTAP_OPTTYPE_SUCCESS;
}
wtap_opttype_return_val
wtap_block_set_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value)
{
wtap_opttype_return_val ret;
wtap_optval_t *optval;
packet_verdict_opt_t prev_value;
ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_PACKET_VERDICT, idx, &optval);
if (ret != WTAP_OPTTYPE_SUCCESS)
return ret;
prev_value = optval->packet_verdictval;
optval->packet_verdictval = packet_verdict_dup(value);
/* Free after memory is duplicated in case structure was manipulated with a "get then set" */
wtap_packet_verdict_free(&prev_value);
return WTAP_OPTTYPE_SUCCESS;
}
wtap_opttype_return_val
wtap_block_get_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value)
{
wtap_opttype_return_val ret;
wtap_optval_t *optval;
ret = wtap_block_get_nth_option_common(block, option_id, WTAP_OPTTYPE_STRING, idx, &optval);
if (ret != WTAP_OPTTYPE_SUCCESS)
return ret;
*value = optval->packet_verdictval;
return WTAP_OPTTYPE_SUCCESS;
}
wtap_opttype_return_val
wtap_block_remove_option(wtap_block_t block, guint option_id)
{
@ -1849,8 +1849,7 @@ void wtap_opttypes_initialize(void)
static const wtap_opttype_t pkt_verdict = {
"verdict",
"Packet Verdict",
WTAP_OPTTYPE_BYTES, // maybe replace with a pkt_verdict_opt_t
// (or maybe not, packet-frame.c reads the raw bytes)
WTAP_OPTTYPE_PACKET_VERDICT,
WTAP_OPTTYPE_FLAG_MULTIPLE_ALLOWED
};

View File

@ -281,7 +281,8 @@ typedef enum {
WTAP_OPTTYPE_IPv4,
WTAP_OPTTYPE_IPv6,
WTAP_OPTTYPE_IF_FILTER,
WTAP_OPTTYPE_CUSTOM
WTAP_OPTTYPE_CUSTOM,
WTAP_OPTTYPE_PACKET_VERDICT
} wtap_opttype_e;
typedef enum {
@ -346,6 +347,26 @@ typedef struct custom_opt_s {
} data;
} custom_opt_t;
/* Packet - packet_verdict option structure */
/*
* Type of verdict.
*/
typedef enum {
packet_verdict_hardware = 0, /* array of octets */
packet_verdict_linux_ebpf_tc = 1, /* 64-bit unsigned integer TC_ACT_ value */
packet_verdict_linux_ebpf_xdp = 2 /* 64-bit unsigned integer xdp_action value */
} packet_verdict_type_e;
typedef struct packet_verdict_opt_s {
packet_verdict_type_e type;
union {
GByteArray *verdict_bytes;
guint64 verdict_linux_ebpf_tc;
guint64 verdict_linux_ebpf_xdp;
} data;
} packet_verdict_opt_t;
/*
* Structure describing a NFLX custom option.
*/
@ -370,6 +391,7 @@ typedef union {
GBytes *byteval;
if_filter_opt_t if_filterval;
custom_opt_t custom_opt;
packet_verdict_opt_t packet_verdictval;
} wtap_optval_t;
/*
@ -977,6 +999,46 @@ wtap_block_get_nflx_custom_option(wtap_block_t block, guint32 nflx_type, char *n
WS_DLL_PUBLIC wtap_opttype_return_val
wtap_block_add_custom_option(wtap_block_t block, guint option_id, guint32 pen, const char *custom_data, gsize custom_data_len);
/** Add an packet_verdict option value to a block
*
* @param[in] block Block to which to add the option
* @param[in] option_id Identifier value for option
* @param[in] value Value of option
* @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
* error code otherwise
*/
WS_DLL_PUBLIC wtap_opttype_return_val
wtap_block_add_packet_verdict_option(wtap_block_t block, guint option_id, packet_verdict_opt_t* value);
/** Set packet_verdict option value for the nth instsance of a particular
* option in a block
*
* @param[in] block Block in which to set the option value
* @param[in] option_id Identifier value for option
* @param[in] idx Instance number of option with that ID
* @param[in] value New value of option
* @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
* error code otherwise
*/
WS_DLL_PUBLIC wtap_opttype_return_val
wtap_block_set_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value);
/** Get packet_verdict option value for the nth instance of a particular
* option in a block
*
* @param[in] block Block from which to get the option value
* @param[in] option_id Identifier value for option
* @param[in] idx Instance number of option with that ID
* @param[out] value Returned value of option value
* @return wtap_opttype_return_val - WTAP_OPTTYPE_SUCCESS if successful,
* error code otherwise
*/
WS_DLL_PUBLIC wtap_opttype_return_val
wtap_block_get_nth_packet_verdict_option_value(wtap_block_t block, guint option_id, guint idx, packet_verdict_opt_t* value) G_GNUC_WARN_UNUSED_RESULT;
WS_DLL_PUBLIC void
wtap_packet_verdict_free(packet_verdict_opt_t* verdict);
/** Remove an option from a block
*
* @param[in] block Block from which to remove the option
@ -998,26 +1060,6 @@ wtap_block_remove_option(wtap_block_t block, guint option_id);
WS_DLL_PUBLIC wtap_opttype_return_val
wtap_block_remove_nth_option_instance(wtap_block_t block, guint option_id, guint idx);
/**
* Get the original (unpadded) length of an option's value
*
* @param[in] option_type The `wtap_opttype_e` for this option
* @param[in] option The option's value to measure
* @return gsize - the number of bytes occupied by the option's value
*/
WS_DLL_PUBLIC gsize
wtap_block_option_get_value_size(wtap_opttype_e option_type, wtap_optval_t *option);
/** Get the padded length of all options in the block
*
* @param[in] block Block from which to remove the option instance
* @return gsize - size in bytes of all options, each padded to 32 bits
* @note The size of any options with values larger than can be held in an option
* is NOT included, because pcapng.c skips over such option values.
*/
WS_DLL_PUBLIC gsize
wtap_block_get_options_size_padded(wtap_block_t block);
/** Copy a block to another.
*
* Any options that are in the destination but not the source are not removed.