diff options
Diffstat (limited to 'freebsd/contrib/libpcap/sf-pcap-ng.c')
-rw-r--r-- | freebsd/contrib/libpcap/sf-pcap-ng.c | 647 |
1 files changed, 481 insertions, 166 deletions
diff --git a/freebsd/contrib/libpcap/sf-pcap-ng.c b/freebsd/contrib/libpcap/sf-pcap-ng.c index 0cc595d8..209c1424 100644 --- a/freebsd/contrib/libpcap/sf-pcap-ng.c +++ b/freebsd/contrib/libpcap/sf-pcap-ng.c @@ -32,9 +32,9 @@ static const char rcsid[] _U_ = #include "config.h" #endif -#ifdef WIN32 +#ifdef _WIN32 #include <pcap-stdinc.h> -#else /* WIN32 */ +#else /* _WIN32 */ #if HAVE_INTTYPES_H #include <inttypes.h> #elif HAVE_STDINT_H @@ -44,7 +44,7 @@ static const char rcsid[] _U_ = #include <sys/bitypes.h> #endif #include <sys/types.h> -#endif /* WIN32 */ +#endif /* _WIN32 */ #include <errno.h> #include <memory.h> @@ -123,6 +123,7 @@ struct section_header_block { * that means that this code can't read the file. */ #define PCAP_NG_VERSION_MAJOR 1 +#define PCAP_NG_VERSION_MINOR 0 /* * Interface Description Block. @@ -203,6 +204,32 @@ struct block_cursor { bpf_u_int32 block_type; }; +typedef enum { + PASS_THROUGH, + SCALE_UP_DEC, + SCALE_DOWN_DEC, + SCALE_UP_BIN, + SCALE_DOWN_BIN +} tstamp_scale_type_t; + +/* + * Per-interface information. + */ +struct pcap_ng_if { + u_int tsresol; /* time stamp resolution */ + tstamp_scale_type_t scale_type; /* how to scale */ + u_int scale_factor; /* time stamp scale factor for power-of-10 tsresol */ + u_int64_t tsoffset; /* time stamp offset */ +}; + +struct pcap_ng_sf { + u_int user_tsresol; /* time stamp resolution requested by the user */ + bpf_u_int32 ifcount; /* number of interfaces seen in this capture */ + bpf_u_int32 ifaces_size; /* size of array below */ + struct pcap_ng_if *ifaces; /* array of interface information */ +}; + +static void pcap_ng_cleanup(pcap_t *p); static int pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data); @@ -215,13 +242,13 @@ read_bytes(FILE *fp, void *buf, size_t bytes_to_read, int fail_on_eof, amt_read = fread(buf, 1, bytes_to_read, fp); if (amt_read != bytes_to_read) { if (ferror(fp)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); } else { if (amt_read == 0 && !fail_on_eof) return (0); /* EOF */ - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "truncated dump file; tried to read %lu bytes, only got %lu", (unsigned long)bytes_to_read, (unsigned long)amt_read); @@ -236,12 +263,14 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) { int status; struct block_header bhdr; + u_char *bdata; + size_t data_remaining; status = read_bytes(fp, &bhdr, sizeof(bhdr), 0, errbuf); if (status <= 0) return (status); /* error or EOF */ - if (p->sf.swapped) { + if (p->swapped) { bhdr.block_type = SWAPLONG(bhdr.block_type); bhdr.total_length = SWAPLONG(bhdr.total_length); } @@ -254,7 +283,7 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) * memory if we read a malformed file. */ if (bhdr.total_length > 16*1024*1024) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "pcap-ng block size %u > maximum %u", bhdr.total_length, 16*1024*1024); return (-1); @@ -266,7 +295,7 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) */ if (bhdr.total_length < sizeof(struct block_header) + sizeof(struct block_trailer)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "block in pcap-ng dump file has a length of %u < %lu", bhdr.total_length, (unsigned long)(sizeof(struct block_header) + sizeof(struct block_trailer))); @@ -280,11 +309,14 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) /* * No - make it big enough. */ - p->buffer = realloc(p->buffer, bhdr.total_length); - if (p->buffer == NULL) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + void *bigger_buffer; + + bigger_buffer = realloc(p->buffer, bhdr.total_length); + if (bigger_buffer == NULL) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); return (-1); } + p->buffer = bigger_buffer; } /* @@ -292,16 +324,16 @@ read_block(FILE *fp, pcap_t *p, struct block_cursor *cursor, char *errbuf) * of the block. */ memcpy(p->buffer, &bhdr, sizeof(bhdr)); - if (read_bytes(fp, p->buffer + sizeof(bhdr), - bhdr.total_length - sizeof(bhdr), 1, errbuf) == -1) + bdata = (u_char *)p->buffer + sizeof(bhdr); + data_remaining = bhdr.total_length - sizeof(bhdr); + if (read_bytes(fp, bdata, data_remaining, 1, errbuf) == -1) return (-1); /* * Initialize the cursor. */ - cursor->data = p->buffer + sizeof(bhdr); - cursor->data_remaining = bhdr.total_length - sizeof(bhdr) - - sizeof(struct block_trailer); + cursor->data = bdata; + cursor->data_remaining = data_remaining - sizeof(struct block_trailer); cursor->block_type = bhdr.block_type; return (1); } @@ -317,7 +349,7 @@ get_from_block_data(struct block_cursor *cursor, size_t chunk_size, * the block data. */ if (cursor->data_remaining < chunk_size) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "block of type %u in pcap-ng dump file is too short", cursor->block_type); return (NULL); @@ -348,7 +380,7 @@ get_opthdr_from_block_data(pcap_t *p, struct block_cursor *cursor, char *errbuf) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { opthdr->option_code = SWAPSHORT(opthdr->option_code); opthdr->option_length = SWAPSHORT(opthdr->option_length); } @@ -380,7 +412,7 @@ get_optvalue_from_block_data(struct block_cursor *cursor, static int process_idb_options(pcap_t *p, struct block_cursor *cursor, u_int *tsresol, - u_int64_t *tsoffset, char *errbuf) + u_int64_t *tsoffset, int *is_binary, char *errbuf) { struct option_header *opthdr; void *optvalue; @@ -418,7 +450,7 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, u_int *tsresol, case OPT_ENDOFOPT: if (opthdr->option_length != 0) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has opt_endofopt option with length %u != 0", opthdr->option_length); return (-1); @@ -427,27 +459,29 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, u_int *tsresol, case IF_TSRESOL: if (opthdr->option_length != 1) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has if_tsresol option with length %u != 1", opthdr->option_length); return (-1); } if (saw_tsresol) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has more than one if_tsresol option"); return (-1); } saw_tsresol = 1; - tsresol_opt = *(u_int *)optvalue; + memcpy(&tsresol_opt, optvalue, sizeof(tsresol_opt)); if (tsresol_opt & 0x80) { /* * Resolution is negative power of 2. */ + *is_binary = 1; *tsresol = 1 << (tsresol_opt & 0x7F); } else { /* * Resolution is negative power of 10. */ + *is_binary = 0; *tsresol = 1; for (i = 0; i < tsresol_opt; i++) *tsresol *= 10; @@ -457,11 +491,11 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, u_int *tsresol, * Resolution is too high. */ if (tsresol_opt & 0x80) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block if_tsresol option resolution 2^-%u is too high", tsresol_opt & 0x7F); } else { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block if_tsresol option resolution 10^-%u is too high", tsresol_opt); } @@ -471,19 +505,19 @@ process_idb_options(pcap_t *p, struct block_cursor *cursor, u_int *tsresol, case IF_TSOFFSET: if (opthdr->option_length != 8) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has if_tsoffset option with length %u != 8", opthdr->option_length); return (-1); } if (saw_tsoffset) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Interface Description Block has more than one if_tsoffset option"); return (-1); } saw_tsoffset = 1; memcpy(tsoffset, optvalue, sizeof(*tsoffset)); - if (p->sf.swapped) + if (p->swapped) *tsoffset = SWAPLL(*tsoffset); break; @@ -496,25 +530,204 @@ done: return (0); } +static int +add_interface(pcap_t *p, struct block_cursor *cursor, char *errbuf) +{ + struct pcap_ng_sf *ps; + u_int tsresol; + u_int64_t tsoffset; + int is_binary; + + ps = p->priv; + + /* + * Count this interface. + */ + ps->ifcount++; + + /* + * Grow the array of per-interface information as necessary. + */ + if (ps->ifcount > ps->ifaces_size) { + /* + * We need to grow the array. + */ + bpf_u_int32 new_ifaces_size; + struct pcap_ng_if *new_ifaces; + + if (ps->ifaces_size == 0) { + /* + * It's currently empty. + * + * (The Clang static analyzer doesn't do enough, + * err, umm, dataflow *analysis* to realize that + * ps->ifaces_size == 0 if ps->ifaces == NULL, + * and so complains about a possible zero argument + * to realloc(), so we check for the former + * condition to shut it up. + * + * However, it doesn't complain that one of the + * multiplications below could overflow, which is + * a real, albeit extremely unlikely, problem (you'd + * need a pcap-ng file with tens of millions of + * interfaces).) + */ + new_ifaces_size = 1; + new_ifaces = malloc(sizeof (struct pcap_ng_if)); + } else { + /* + * It's not currently empty; double its size. + * (Perhaps overkill once we have a lot of interfaces.) + * + * Check for overflow if we double it. + */ + if (ps->ifaces_size * 2 < ps->ifaces_size) { + /* + * The maximum number of interfaces before + * ps->ifaces_size overflows is the largest + * possible 32-bit power of 2, as we do + * size doubling. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "more than %u interfaces in the file", + 0x80000000U); + return (0); + } + + /* + * ps->ifaces_size * 2 doesn't overflow, so it's + * safe to multiply. + */ + new_ifaces_size = ps->ifaces_size * 2; + + /* + * Now make sure that's not so big that it overflows + * if we multiply by sizeof (struct pcap_ng_if). + * + * That can happen on 32-bit platforms, with a 32-bit + * size_t; it shouldn't happen on 64-bit platforms, + * with a 64-bit size_t, as new_ifaces_size is + * 32 bits. + */ + if (new_ifaces_size * sizeof (struct pcap_ng_if) < new_ifaces_size) { + /* + * As this fails only with 32-bit size_t, + * the multiplication was 32x32->32, and + * the largest 32-bit value that can safely + * be multiplied by sizeof (struct pcap_ng_if) + * without overflow is the largest 32-bit + * (unsigned) value divided by + * sizeof (struct pcap_ng_if). + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "more than %u interfaces in the file", + 0xFFFFFFFFU / ((u_int)sizeof (struct pcap_ng_if))); + return (0); + } + new_ifaces = realloc(ps->ifaces, new_ifaces_size * sizeof (struct pcap_ng_if)); + } + if (new_ifaces == NULL) { + /* + * We ran out of memory. + * Give up. + */ + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "out of memory for per-interface information (%u interfaces)", + ps->ifcount); + return (0); + } + ps->ifaces_size = new_ifaces_size; + ps->ifaces = new_ifaces; + } + + /* + * Set the default time stamp resolution and offset. + */ + tsresol = 1000000; /* microsecond resolution */ + is_binary = 0; /* which is a power of 10 */ + tsoffset = 0; /* absolute timestamps */ + + /* + * Now look for various time stamp options, so we know + * how to interpret the time stamps for this interface. + */ + if (process_idb_options(p, cursor, &tsresol, &tsoffset, &is_binary, + errbuf) == -1) + return (0); + + ps->ifaces[ps->ifcount - 1].tsresol = tsresol; + ps->ifaces[ps->ifcount - 1].tsoffset = tsoffset; + + /* + * Determine whether we're scaling up or down or not + * at all for this interface. + */ + if (tsresol == ps->user_tsresol) { + /* + * The resolution is the resolution the user wants, + * so we don't have to do scaling. + */ + ps->ifaces[ps->ifcount - 1].scale_type = PASS_THROUGH; + } else if (tsresol > ps->user_tsresol) { + /* + * The resolution is greater than what the user wants, + * so we have to scale the timestamps down. + */ + if (is_binary) + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_DOWN_BIN; + else { + /* + * Calculate the scale factor. + */ + ps->ifaces[ps->ifcount - 1].scale_factor = tsresol/ps->user_tsresol; + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_DOWN_DEC; + } + } else { + /* + * The resolution is less than what the user wants, + * so we have to scale the timestamps up. + */ + if (is_binary) + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_UP_BIN; + else { + /* + * Calculate the scale factor. + */ + ps->ifaces[ps->ifcount - 1].scale_factor = ps->user_tsresol/tsresol; + ps->ifaces[ps->ifcount - 1].scale_type = SCALE_UP_DEC; + } + } + return (1); +} + /* * Check whether this is a pcap-ng savefile and, if it is, extract the * relevant information from the header. */ -int -pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) +pcap_t * +pcap_ng_check_header(bpf_u_int32 magic, FILE *fp, u_int precision, char *errbuf, + int *err) { size_t amt_read; bpf_u_int32 total_length; bpf_u_int32 byte_order_magic; struct block_header *bhdrp; struct section_header_block *shbp; + pcap_t *p; + int swapped = 0; + struct pcap_ng_sf *ps; int status; struct block_cursor cursor; struct interface_description_block *idbp; /* + * Assume no read errors. + */ + *err = 0; + + /* * Check whether the first 4 bytes of the file are the block - * type for a pcap-ng savefile. + * type for a pcap-ng savefile. */ if (magic != BT_SHB) { /* @@ -526,7 +739,7 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) * this as possibly being a pcap-ng file transferred * between UN*X and Windows in text file format? */ - return (0); /* nope */ + return (NULL); /* nope */ } /* @@ -543,32 +756,34 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) amt_read = fread(&total_length, 1, sizeof(total_length), fp); if (amt_read < sizeof(total_length)) { if (ferror(fp)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); - return (-1); /* fail */ + *err = 1; + return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcap-ng". */ - return (0); + return (NULL); } amt_read = fread(&byte_order_magic, 1, sizeof(byte_order_magic), fp); if (amt_read < sizeof(byte_order_magic)) { if (ferror(fp)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "error reading dump file: %s", pcap_strerror(errno)); - return (-1); /* fail */ + *err = 1; + return (NULL); /* fail */ } /* * Possibly a weird short text file, so just say * "not pcap-ng". */ - return (0); + return (NULL); } if (byte_order_magic != BYTE_ORDER_MAGIC) { byte_order_magic = SWAPLONG(byte_order_magic); @@ -576,9 +791,9 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) /* * Not a pcap-ng file. */ - return (0); + return (NULL); } - p->sf.swapped = 1; + swapped = 1; total_length = SWAPLONG(total_length); } @@ -586,12 +801,49 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) * Check the sanity of the total length. */ if (total_length < sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer)) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Section Header Block in pcap-ng dump file has a length of %u < %lu", total_length, (unsigned long)(sizeof(*bhdrp) + sizeof(*shbp) + sizeof(struct block_trailer))); - return (-1); + *err = 1; + return (NULL); + } + + /* + * OK, this is a good pcap-ng file. + * Allocate a pcap_t for it. + */ + p = pcap_open_offline_common(errbuf, sizeof (struct pcap_ng_sf)); + if (p == NULL) { + /* Allocation failed. */ + *err = 1; + return (NULL); } + p->swapped = swapped; + ps = p->priv; + + /* + * What precision does the user want? + */ + switch (precision) { + + case PCAP_TSTAMP_PRECISION_MICRO: + ps->user_tsresol = 1000000; + break; + + case PCAP_TSTAMP_PRECISION_NANO: + ps->user_tsresol = 1000000000; + break; + + default: + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "unknown time stamp resolution %u", precision); + free(p); + *err = 1; + return (NULL); + } + + p->opt.tstamp_precision = precision; /* * Allocate a buffer into which to read blocks. We default to @@ -610,8 +862,10 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) p->bufsize = total_length; p->buffer = malloc(p->bufsize); if (p->buffer == NULL) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); - return (-1); + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "out of memory"); + free(p); + *err = 1; + return (NULL); } /* @@ -619,17 +873,17 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) * of the SHB. */ bhdrp = (struct block_header *)p->buffer; - shbp = (struct section_header_block *)(p->buffer + sizeof(struct block_header)); + shbp = (struct section_header_block *)((u_char *)p->buffer + sizeof(struct block_header)); bhdrp->block_type = magic; bhdrp->total_length = total_length; shbp->byte_order_magic = byte_order_magic; if (read_bytes(fp, - p->buffer + (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), + (u_char *)p->buffer + (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), total_length - (sizeof(magic) + sizeof(total_length) + sizeof(byte_order_magic)), 1, errbuf) == -1) goto fail; - if (p->sf.swapped) { + if (p->swapped) { /* * Byte-swap the fields we've read. */ @@ -640,21 +894,21 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) * XXX - we don't care about the section length. */ } - if (shbp->major_version != PCAP_NG_VERSION_MAJOR) { - snprintf(errbuf, PCAP_ERRBUF_SIZE, - "unknown pcap-ng savefile major version number %u", - shbp->major_version); + /* currently only SHB version 1.0 is supported */ + if (! (shbp->major_version == PCAP_NG_VERSION_MAJOR && + shbp->minor_version == PCAP_NG_VERSION_MINOR)) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "unsupported pcap-ng savefile version %u.%u", + shbp->major_version, shbp->minor_version); goto fail; } - p->sf.version_major = shbp->major_version; - p->sf.version_minor = shbp->minor_version; + p->version_major = shbp->major_version; + p->version_minor = shbp->minor_version; /* - * Set the default time stamp resolution and offset. + * Save the time stamp resolution the user requested. */ - p->sf.tsresol = 1000000; /* microsecond resolution */ - p->sf.tsscale = 1; /* multiply by 1 to scale to microseconds */ - p->sf.tsoffset = 0; /* absolute timestamps */ + p->opt.tstamp_precision = precision; /* * Now start looking for an Interface Description Block. @@ -666,7 +920,7 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) status = read_block(fp, p, &cursor, errbuf); if (status == 0) { /* EOF - no IDB in this file */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has no Interface Description Blocks"); goto fail; } @@ -687,42 +941,28 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { idbp->linktype = SWAPSHORT(idbp->linktype); idbp->snaplen = SWAPLONG(idbp->snaplen); } /* - * Count this interface. + * Interface capture length sanity check */ - p->sf.ifcount++; + if (idbp->snaplen > MAXIMUM_SNAPLEN) { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "invalid interface capture length %u, " + "bigger than maximum of %u", + idbp->snaplen, MAXIMUM_SNAPLEN); + goto fail; + } /* - * Now look for various time stamp options, so - * we know how to interpret the time stamps. + * Try to add this interface. */ - if (process_idb_options(p, &cursor, &p->sf.tsresol, - &p->sf.tsoffset, errbuf) == -1) + if (!add_interface(p, &cursor, errbuf)) goto fail; - /* - * Compute the scaling factor to convert the - * sub-second part of the time stamp to - * microseconds. - */ - if (p->sf.tsresol > 1000000) { - /* - * Higher than microsecond resolution; - * scale down to microseconds. - */ - p->sf.tsscale = (p->sf.tsresol / 1000000); - } else { - /* - * Lower than microsecond resolution; - * scale up to microseconds. - */ - p->sf.tsscale = (1000000 / p->sf.tsresol); - } goto done; case BT_EPB: @@ -733,7 +973,7 @@ pcap_ng_check_header(pcap_t *p, bpf_u_int32 magic, FILE *fp, char *errbuf) * not valid, as we don't know what link-layer * encapsulation the packet has. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "the capture file has a packet block before any Interface Description Blocks"); goto fail; @@ -751,13 +991,26 @@ done: p->linktype = linktype_to_dlt(idbp->linktype); p->linktype_ext = 0; - p->sf.next_packet_op = pcap_ng_next_packet; + p->next_packet_op = pcap_ng_next_packet; + p->cleanup_op = pcap_ng_cleanup; - return (1); + return (p); fail: + free(ps->ifaces); free(p->buffer); - return (-1); + free(p); + *err = 1; + return (NULL); +} + +static void +pcap_ng_cleanup(pcap_t *p) +{ + struct pcap_ng_sf *ps = p->priv; + + free(ps->ifaces); + sf_cleanup(p); } /* @@ -768,6 +1021,7 @@ fail: static int pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) { + struct pcap_ng_sf *ps = p->priv; struct block_cursor cursor; int status; struct enhanced_packet_block *epbp; @@ -776,9 +1030,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) bpf_u_int32 interface_id = 0xFFFFFFFF; struct interface_description_block *idbp; struct section_header_block *shbp; - FILE *fp = p->sf.rfile; - u_int tsresol; - u_int64_t tsoffset; + FILE *fp = p->rfile; u_int64_t t, sec, frac; /* @@ -810,7 +1062,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { /* these were written in opposite byte order */ interface_id = SWAPLONG(epbp->interface_id); hdr->caplen = SWAPLONG(epbp->caplen); @@ -825,7 +1077,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) epbp->timestamp_low; } goto found; - + case BT_SPB: /* * Get a pointer to the fixed-length portion of the @@ -845,7 +1097,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { /* these were written in opposite byte order */ hdr->len = SWAPLONG(spbp->len); } else @@ -857,7 +1109,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * and the packet length. */ hdr->caplen = hdr->len; - if (hdr->caplen > p->snapshot) + if (hdr->caplen > (bpf_u_int32)p->snapshot) hdr->caplen = p->snapshot; t = 0; /* no time stamps */ goto found; @@ -875,7 +1127,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { /* these were written in opposite byte order */ interface_id = SWAPSHORT(pbp->interface_id); hdr->caplen = SWAPLONG(pbp->caplen); @@ -904,7 +1156,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte-swap it if necessary. */ - if (p->sf.swapped) { + if (p->swapped) { idbp->linktype = SWAPSHORT(idbp->linktype); idbp->snaplen = SWAPLONG(idbp->snaplen); } @@ -918,50 +1170,23 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * interfaces? */ if (p->linktype != idbp->linktype) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "an interface has a type %u different from the type of the first interface", idbp->linktype); return (-1); } - if (p->snapshot != idbp->snaplen) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + if ((bpf_u_int32)p->snapshot != idbp->snaplen) { + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "an interface has a snapshot length %u different from the type of the first interface", idbp->snaplen); return (-1); } /* - * Count this interface. + * Try to add this interface. */ - p->sf.ifcount++; - - /* - * Set the default time stamp resolution and offset. - */ - tsresol = 1000000; /* microsecond resolution */ - tsoffset = 0; /* absolute timestamps */ - - /* - * Now look for various time stamp options, to - * make sure they're the same. - * - * XXX - we could, in theory, handle multiple - * different resolutions and offsets, but we - * don't do so for now. - */ - if (process_idb_options(p, &cursor, &tsresol, &tsoffset, - p->errbuf) == -1) - return (-1); - if (tsresol != p->sf.tsresol) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "an interface has a time stamp resolution different from the time stamp resolution of the first interface"); + if (!add_interface(p, &cursor, p->errbuf)) return (-1); - } - if (tsoffset != p->sf.tsoffset) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, - "an interface has a time stamp offset different from the time stamp offset of the first interface"); - return (-1); - } break; case BT_SHB: @@ -979,7 +1204,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * the same as that of the previous section. * We'll check for that later. */ - if (p->sf.swapped) { + if (p->swapped) { shbp->byte_order_magic = SWAPLONG(shbp->byte_order_magic); shbp->major_version = @@ -1003,7 +1228,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Byte order changes. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "the file has sections with different byte orders"); return (-1); @@ -1011,7 +1236,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) /* * Not a valid SHB. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "the file has a section with a bad byte order magic field"); return (-1); } @@ -1021,7 +1246,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * we handle. */ if (shbp->major_version != PCAP_NG_VERSION_MAJOR) { - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "unknown pcap-ng savefile major version number %u", shbp->major_version); return (-1); @@ -1036,7 +1261,7 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * any IDBs, we'll fail when we see a packet * block.) */ - p->sf.ifcount = 0; + ps->ifcount = 0; break; default: @@ -1044,43 +1269,148 @@ pcap_ng_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data) * Not a packet block, IDB, or SHB; ignore it. */ break; - } + } } found: /* * Is the interface ID an interface we know? */ - if (interface_id >= p->sf.ifcount) { + if (interface_id >= ps->ifcount) { /* * Yes. Fail. */ - snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + pcap_snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "a packet arrived on interface %u, but there's no Interface Description Block for that interface", interface_id); return (-1); } /* - * Convert the time stamp to a struct timeval. + * Convert the time stamp to seconds and fractions of a second, + * with the fractions being in units of the file-supplied resolution. */ - sec = t / p->sf.tsresol + p->sf.tsoffset; - frac = t % p->sf.tsresol; - if (p->sf.tsresol > 1000000) { + sec = t / ps->ifaces[interface_id].tsresol + ps->ifaces[interface_id].tsoffset; + frac = t % ps->ifaces[interface_id].tsresol; + + /* + * Convert the fractions from units of the file-supplied resolution + * to units of the user-requested resolution. + */ + switch (ps->ifaces[interface_id].scale_type) { + + case PASS_THROUGH: /* - * Higher than microsecond resolution; scale down to - * microseconds. + * The interface resolution is what the user wants, + * so we're done. */ - frac /= p->sf.tsscale; - } else { + break; + + case SCALE_UP_DEC: + /* + * The interface resolution is less than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * Those resolutions are both powers of 10, and the user- + * requested resolution is greater than the file-supplied + * resolution, so the quotient in question is an integer. + * We've calculated that quotient already, so we just + * multiply by it. + */ + frac *= ps->ifaces[interface_id].scale_factor; + break; + + case SCALE_UP_BIN: /* - * Lower than microsecond resolution; scale up to - * microseconds. + * The interface resolution is less than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * The file-supplied resolution is a power of 2, so the + * quotient is not an integer, so, in order to do this + * entirely with integer arithmetic, we multiply by the + * user-requested resolution and divide by the file- + * supplied resolution. + * + * XXX - Is there something clever we could do here, + * given that we know that the file-supplied resolution + * is a power of 2? Doing a multiplication followed by + * a division runs the risk of overflowing, and involves + * two non-simple arithmetic operations. */ - frac *= p->sf.tsscale; + frac *= ps->user_tsresol; + frac /= ps->ifaces[interface_id].tsresol; + break; + + case SCALE_DOWN_DEC: + /* + * The interface resolution is greater than what the user + * wants; scale the fractional part up to the units of + * the resolution the user requested by multiplying by + * the quotient of the user-requested resolution and the + * file-supplied resolution. + * + * Those resolutions are both powers of 10, and the user- + * requested resolution is less than the file-supplied + * resolution, so the quotient in question isn't an + * integer, but its reciprocal is, and we can just divide + * by the reciprocal of the quotient. We've calculated + * the reciprocal of that quotient already, so we must + * divide by it. + */ + frac /= ps->ifaces[interface_id].scale_factor; + break; + + + case SCALE_DOWN_BIN: + /* + * The interface resolution is greater than what the user + * wants; convert the fractional part to units of the + * resolution the user requested by multiplying by the + * quotient of the user-requested resolution and the + * file-supplied resolution. We do that by multiplying + * by the user-requested resolution and dividing by the + * file-supplied resolution, as the quotient might not + * fit in an integer. + * + * The file-supplied resolution is a power of 2, so the + * quotient is not an integer, and neither is its + * reciprocal, so, in order to do this entirely with + * integer arithmetic, we multiply by the user-requested + * resolution and divide by the file-supplied resolution. + * + * XXX - Is there something clever we could do here, + * given that we know that the file-supplied resolution + * is a power of 2? Doing a multiplication followed by + * a division runs the risk of overflowing, and involves + * two non-simple arithmetic operations. + */ + frac *= ps->user_tsresol; + frac /= ps->ifaces[interface_id].tsresol; + break; } - hdr->ts.tv_sec = sec; - hdr->ts.tv_usec = frac; +#ifdef _WIN32 + /* + * tv_sec and tv_used in the Windows struct timeval are both + * longs. + */ + hdr->ts.tv_sec = (long)sec; + hdr->ts.tv_usec = (long)frac; +#else + /* + * tv_sec in the UN*X struct timeval is a time_t; tv_usec is + * suseconds_t in UN*Xes that work the way the current Single + * UNIX Standard specify - but not all older UN*Xes necessarily + * support that type, so just cast to int. + */ + hdr->ts.tv_sec = (time_t)sec; + hdr->ts.tv_usec = (int)frac; +#endif /* * Get a pointer to the packet data. @@ -1089,23 +1419,8 @@ found: if (*data == NULL) return (-1); - if (p->sf.swapped) { - /* - * Convert pseudo-headers from the byte order of - * the host on which the file was saved to our - * byte order, as necessary. - */ - switch (p->linktype) { - - case DLT_USB_LINUX: - swap_linux_usb_header(hdr, *data, 0); - break; - - case DLT_USB_LINUX_MMAPPED: - swap_linux_usb_header(hdr, *data, 1); - break; - } - } + if (p->swapped) + swap_pseudo_headers(p->linktype, hdr, *data); return (0); } |