Coverage Report

Created: 2026-08-31 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tcpreplay/src/common/utils.c
Line
Count
Source
1
/* $Id$ */
2
3
/*
4
 *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
5
 *   Copyright (c) 2013-2026 Fred Klassen <tcpreplay.dev at gmail dot com> - AppNeta by Broadcom
6
 *
7
 *   The Tcpreplay Suite of tools is free software: you can redistribute it
8
 *   and/or modify it under the terms of the GNU General Public License as
9
 *   published by the Free Software Foundation, either version 3 of the
10
 *   License, or with the authors permission any later version.
11
 *
12
 *   The Tcpreplay Suite is distributed in the hope that it will be useful,
13
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *   GNU General Public License for more details.
16
 *
17
 *   You should have received a copy of the GNU General Public License
18
 *   along with the Tcpreplay Suite.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "defines.h"
22
#include "config.h"
23
#include "common.h"
24
#include <stdlib.h>
25
#include <string.h>
26
27
#ifdef HAVE_SYS_IOCTL_H
28
#include <sys/ioctl.h>
29
#endif
30
31
/**
32
 * this is wrapped up in a #define safe_malloc
33
 * This function, detects failures to malloc memory and zeros out the
34
 * memory before returning
35
 */
36
37
void *
38
our_safe_malloc(size_t len, const char *funcname, int line, const char *file)
39
0
{
40
0
    u_char *ptr;
41
42
0
    if ((ptr = malloc(len)) == NULL) {
43
0
        fprintf(stderr, "ERROR in %s:%s() line %d: Unable to malloc() %zu bytes/n", file, funcname, line, len);
44
0
        exit(-1);
45
0
    }
46
47
    /* zero memory */
48
0
    memset(ptr, 0, len);
49
50
    /* wrapped inside an #ifdef for better performance */
51
0
    dbgx(5, "Malloc'd %zu bytes in %s:%s() line %d", len, file, funcname, line);
52
53
0
    return (void *)ptr;
54
0
}
55
56
/**
57
 * this is wrapped up in a #define safe_realloc
58
 * This function, detects failures to realloc memory and zeros
59
 * out the NEW memory if len > current len.  As always, remember
60
 * to use it as:
61
 * ptr = safe_realloc(ptr, size)
62
 */
63
void *
64
our_safe_realloc(void *ptr, size_t len, const char *funcname, int line, const char *file)
65
0
{
66
0
    if ((ptr = realloc(ptr, len)) == NULL) {
67
0
        fprintf(stderr,
68
0
                "ERROR: in %s:%s() line %d: Unable to remalloc() buffer to %zu bytes",
69
0
                file,
70
0
                funcname,
71
0
                line,
72
0
                len);
73
0
        exit(-1);
74
0
    }
75
76
0
    dbgx(5, "Remalloc'd buffer to %zu bytes in %s:%s() line %d", len, file, funcname, line);
77
78
0
    return ptr;
79
0
}
80
81
/**
82
 * this is wrapped up in a #define safe_strdup
83
 * This function, detects failures to realloc memory
84
 */
85
char *
86
our_safe_strdup(const char *str, const char *funcname, int line, const char *file)
87
0
{
88
0
    char *newstr;
89
90
0
    if ((newstr = (char *)malloc(strlen(str) + 1)) == NULL) {
91
0
        fprintf(stderr, "ERROR in %s:%s() line %d: Unable to strdup() %zu bytes\n", file, funcname, line, strlen(str));
92
0
        exit(-1);
93
0
    }
94
95
0
    memcpy(newstr, str, strlen(str) + 1);
96
97
0
    return newstr;
98
0
}
99
100
pcap_t*
101
tcpr_pcap_open(const char *path, char *ebuf)
102
0
{
103
0
#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION
104
0
    return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf);
105
#else
106
    return pcap_open_offline(path, ebuf);
107
#endif
108
0
}
109
110
/**
111
 * Returns the native timestamp precision of a capture file:
112
 * PCAP_TSTAMP_PRECISION_NANO for nanosecond pcap files and for pcapng files
113
 * (whose interfaces may use nanosecond resolution - opening those at
114
 * nanosecond precision never loses accuracy), PCAP_TSTAMP_PRECISION_MICRO
115
 * for everything else, including stdin ("-") which cannot be peeked (#621)
116
 */
117
int
118
tcpr_pcap_file_precision(const char *path)
119
0
{
120
0
#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION
121
0
    FILE *fp;
122
0
    uint32_t magic = 0;
123
0
    size_t nread;
124
125
0
    if (path == NULL || strcmp(path, "-") == 0) {
126
0
        return PCAP_TSTAMP_PRECISION_MICRO;
127
0
    }
128
129
0
    if ((fp = fopen(path, "rb")) == NULL) {
130
0
        return PCAP_TSTAMP_PRECISION_MICRO;
131
0
    }
132
133
0
    nread = fread(&magic, 1, sizeof(magic), fp);
134
0
    fclose(fp);
135
0
    if (nread != sizeof(magic)) {
136
0
        return PCAP_TSTAMP_PRECISION_MICRO;
137
0
    }
138
139
0
    switch (magic) {
140
0
    case 0xa1b23c4d: /* nanosecond pcap */
141
0
    case 0x4d3cb2a1: /* nanosecond pcap, byte-swapped */
142
0
    case 0x0a0d0d0a: /* pcapng section header block */
143
0
        return PCAP_TSTAMP_PRECISION_NANO;
144
0
    default:
145
0
        return PCAP_TSTAMP_PRECISION_MICRO;
146
0
    }
147
#else
148
    (void)path;
149
    return 0;
150
#endif
151
0
}
152
153
/**
154
 * calls free and sets to NULL.
155
 */
156
void
157
our_safe_free(void *ptr, const char *funcname, int line, const char *file)
158
0
{
159
0
    assert(funcname);
160
0
    assert(line);
161
0
    assert(file);
162
163
0
    if (ptr == NULL)
164
0
        return;
165
166
0
    free(ptr);
167
0
}
168
169
/**
170
 * get next packet in pcap file
171
 */
172
u_char *
173
our_safe_pcap_next(pcap_t *pcap, struct pcap_pkthdr *pkthdr, const char *funcname, int line, const char *file)
174
0
{
175
0
    u_char *pktdata = (u_char *)pcap_next(pcap, pkthdr);
176
177
0
    if (pktdata) {
178
0
        if (pkthdr->len > MAX_SNAPLEN) {
179
0
            fprintf(stderr,
180
0
                    "safe_pcap_next ERROR: Invalid packet length in %s:%s() line %d: %u is greater than maximum %u\n",
181
0
                    file,
182
0
                    funcname,
183
0
                    line,
184
0
                    pkthdr->len,
185
0
                    MAX_SNAPLEN);
186
0
            exit(-1);
187
0
        }
188
189
0
        if (!pkthdr->len || !pkthdr->caplen) {
190
0
            fprintf(stderr,
191
0
                    "safe_pcap_next ERROR: Invalid packet length in %s:%s() line %d: packet length=%u capture length=%u\n",
192
0
                    file,
193
0
                    funcname,
194
0
                    line,
195
0
                    pkthdr->len,
196
0
                    pkthdr->caplen);
197
0
            exit(-1);
198
0
        }
199
200
        /* attempt to correct invalid captures */
201
0
        if (pkthdr->len < pkthdr->caplen) {
202
0
            dbgx(1, "Correcting invalid packet capture length %d: packet length=%u", pkthdr->caplen, pkthdr->len);
203
0
            pkthdr->caplen = pkthdr->len;
204
0
        }
205
0
    } else {
206
        /* this will be reported as a failed packet in final report */
207
0
        dbg(1, "No data found in packet");
208
0
    }
209
210
0
    return pktdata;
211
0
}
212
213
/**
214
 * get next packet in pcap file (extended)
215
 */
216
int
217
our_safe_pcap_next_ex(pcap_t *pcap,
218
                      struct pcap_pkthdr **pkthdr,
219
                      const u_char **pktdata,
220
                      const char *funcname,
221
                      int line,
222
                      const char *file)
223
0
{
224
0
    int res = pcap_next_ex(pcap, pkthdr, pktdata);
225
226
0
    if (*pktdata && *pkthdr) {
227
0
        if ((*pkthdr)->len > MAXPACKET) {
228
0
            fprintf(stderr,
229
0
                    "safe_pcap_next_ex ERROR: Invalid packet length in %s:%s() line %d: %u is greater than maximum %u\n",
230
0
                    file,
231
0
                    funcname,
232
0
                    line,
233
0
                    (*pkthdr)->len,
234
0
                    MAXPACKET);
235
0
            exit(-1);
236
0
        }
237
238
0
        if (!(*pkthdr)->len || (*pkthdr)->len < (*pkthdr)->caplen) {
239
0
            fprintf(stderr,
240
0
                    "safe_pcap_next_ex ERROR: Invalid packet length in %s:%s() line %d: packet length=%u capture length=%u\n",
241
0
                    file,
242
0
                    funcname,
243
0
                    line,
244
0
                    (*pkthdr)->len,
245
0
                    (*pkthdr)->caplen);
246
0
            exit(-1);
247
0
        }
248
249
0
        if ((*pkthdr)->len < (*pkthdr)->caplen) {
250
0
            dbgx(1, "Correcting invalid packet capture length %d: packet length=%u", (*pkthdr)->caplen, (*pkthdr)->len);
251
0
            (*pkthdr)->caplen = (*pkthdr)->len;
252
0
        }
253
0
    } else {
254
        /* this will be reported as a failed packet in final report */
255
0
        dbgx(1, "No data found in packet 0x%p and/or header 0x%p", *pktdata, *pkthdr);
256
0
    }
257
258
0
    return res;
259
0
}
260
261
/**
262
 * Print various packet statistics
263
 */
264
void
265
packet_stats(const tcpreplay_stats_t *stats)
266
0
{
267
0
    struct timespec diff;
268
0
    COUNTER diff_us;
269
0
    COUNTER bytes_sec = 0;
270
0
    u_int32_t bytes_sec_10ths = 0;
271
0
    COUNTER mb_sec = 0;
272
0
    u_int32_t mb_sec_100ths = 0;
273
0
    u_int32_t mb_sec_1000ths = 0;
274
0
    COUNTER pkts_sec = 0;
275
0
    u_int32_t pkts_sec_100ths = 0;
276
277
0
    timessub(&stats->end_time, &stats->start_time, &diff);
278
0
    diff_us = TIMESPEC_TO_MICROSEC(&diff);
279
280
0
    if (diff_us && stats->pkts_sent && stats->bytes_sent) {
281
0
        COUNTER bytes_sec_X10;
282
0
        COUNTER pkts_sec_X100;
283
0
        COUNTER mb_sec_X1000;
284
0
        COUNTER mb_sec_X100;
285
286
0
        if (stats->bytes_sent > 1000 * 1000 * 1000 && diff_us > 1000 * 1000) {
287
0
            bytes_sec_X10 = (stats->bytes_sent * 10 * 1000) / (diff_us / 1000);
288
0
            pkts_sec_X100 = (stats->pkts_sent * 100 * 1000) / (diff_us / 1000);
289
0
        } else {
290
0
            bytes_sec_X10 = (stats->bytes_sent * 10 * 1000 * 1000) / diff_us;
291
0
            pkts_sec_X100 = (stats->pkts_sent * 100 * 1000 * 1000) / diff_us;
292
0
        }
293
294
0
        bytes_sec = bytes_sec_X10 / 10;
295
0
        bytes_sec_10ths = bytes_sec_X10 % 10;
296
297
0
        mb_sec_X1000 = (bytes_sec * 8) / 1000;
298
0
        mb_sec_X100 = mb_sec_X1000 / 10;
299
0
        mb_sec = mb_sec_X1000 / 1000;
300
0
        mb_sec_100ths = mb_sec_X100 % 100;
301
0
        mb_sec_1000ths = mb_sec_X1000 % 1000;
302
303
0
        pkts_sec = pkts_sec_X100 / 100;
304
0
        pkts_sec_100ths = pkts_sec_X100 % 100;
305
0
    }
306
307
0
    if (diff_us >= 1000 * 1000) {
308
0
        printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n",
309
0
               stats->pkts_sent,
310
0
               stats->bytes_sent,
311
0
               (ssize_t)diff.tv_sec,
312
0
               (ssize_t)(diff.tv_nsec / (10 * 1000000)));
313
0
    } else {
314
0
        printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n",
315
0
               stats->pkts_sent,
316
0
               stats->bytes_sent,
317
0
               (ssize_t)diff.tv_sec,
318
0
               (ssize_t)diff.tv_nsec / 1000);
319
0
    }
320
321
0
    if (mb_sec >= 1)
322
0
        printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n",
323
0
               bytes_sec,
324
0
               bytes_sec_10ths,
325
0
               mb_sec,
326
0
               mb_sec_100ths,
327
0
               pkts_sec,
328
0
               pkts_sec_100ths);
329
0
    else
330
0
        printf("Rated: %llu.%1u Bps, %llu.%03u Mbps, %llu.%02u pps\n",
331
0
               bytes_sec,
332
0
               bytes_sec_10ths,
333
0
               mb_sec,
334
0
               mb_sec_1000ths,
335
0
               pkts_sec,
336
0
               pkts_sec_100ths);
337
0
    fflush(NULL);
338
339
0
    if (stats->failed)
340
0
        printf("Failed write attempts: " COUNTER_SPEC "\n", stats->failed);
341
0
}
342
343
/**
344
 * fills a buffer with a string representing the given time
345
 *
346
 * @param when: the time that should be formatted
347
 * @param buf: a buffer to write to
348
 * @param len: length of the buffer
349
 * @return: string containing date, or -1 on error
350
 */
351
int
352
format_date_time(struct timespec *when, char *buf, size_t len)
353
0
{
354
0
    struct tm *tm;
355
0
    char tmp[64];
356
357
0
    assert(len);
358
359
0
    tm = localtime(&when->tv_sec);
360
0
    if (!tm)
361
0
        return -1;
362
363
0
    strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%09u", tm);
364
0
    return snprintf(buf, len, tmp, when->tv_nsec);
365
0
}
366
367
/**
368
 * reads a hexstring in the format of xx,xx,xx,xx spits it back into *hex
369
 * up to hexlen bytes.  Returns actual number of bytes returned.  On error
370
 * it just calls errx() since all errors are fatal.
371
 */
372
int
373
read_hexstring(const char *l2string, u_char *hex, int hexlen)
374
0
{
375
0
    int numbytes = 0;
376
0
    unsigned int value;
377
0
    char *l2byte;
378
0
    u_char databyte;
379
0
    char *token = NULL;
380
0
    char *string;
381
382
0
    string = safe_strdup(l2string);
383
384
0
    if (hexlen <= 0)
385
0
        err(-1, "Hex buffer must be > 0");
386
387
0
    memset(hex, '\0', hexlen);
388
389
    /* data is hex, comma separated, byte by byte */
390
391
    /* get the first byte */
392
0
    l2byte = strtok_r(string, ",", &token);
393
0
    if (l2byte == NULL)
394
0
        err(-1, "Hex buffer must contain something");
395
0
    value = strtol(l2byte, NULL, 16);
396
0
    if (value > 0xff)
397
0
        errx(-1, "Invalid hex string byte: %s", l2byte);
398
0
    databyte = (u_char)value;
399
0
    memcpy(&hex[numbytes], &databyte, 1);
400
401
    /* get remaining bytes */
402
0
    while ((l2byte = strtok_r(NULL, ",", &token)) != NULL) {
403
0
        numbytes++;
404
0
        if (numbytes + 1 > hexlen) {
405
0
            warn("Hex buffer too small for data- skipping data");
406
0
            goto done;
407
0
        }
408
0
        value = strtol(l2byte, NULL, 16);
409
0
        if (value > 0xff)
410
0
            errx(-1, "Invalid hex string byte: %s", l2byte);
411
0
        databyte = (u_char)value;
412
0
        memcpy(&hex[numbytes], &databyte, 1);
413
0
    }
414
415
0
    numbytes++;
416
417
0
done:
418
0
    safe_free(string);
419
420
0
    dbgx(1, "Read %d bytes of hex data", numbytes);
421
0
    return (numbytes);
422
0
}
423
424
#ifdef USE_CUSTOM_INET_ATON
425
int
426
inet_aton(const char *name, struct in_addr *addr)
427
{
428
    in_addr_t a = inet_addr(name);
429
    addr->s_addr = a;
430
    return a != (in_addr_t)-1;
431
}
432
#endif
433
434
#if SIZEOF_LONG == 4
435
uint32_t
436
__div64_32(uint64_t *n, uint32_t base)
437
{
438
    uint64_t rem = *n;
439
    uint64_t b = base;
440
    uint64_t res, d = 1;
441
    uint32_t high = rem >> 32;
442
443
    /* Reduce the thing a bit first */
444
    res = 0;
445
    if (high >= base) {
446
        high /= base;
447
        res = (uint64_t)high << 32;
448
        rem -= (uint64_t)(high * base) << 32;
449
    }
450
451
    while ((int64_t)b > 0 && b < rem) {
452
        b = b + b;
453
        d = d + d;
454
    }
455
456
    do {
457
        if (rem >= b) {
458
            rem -= b;
459
            res += d;
460
        }
461
        b >>= 1;
462
        d >>= 1;
463
    } while (d);
464
465
    *n = res;
466
    return rem;
467
}
468
#endif /* SIZEOF_LONG  == 4 */
469
470
/**
471
 * Implementation of rand_r that is consistent across all platforms
472
 * This algorithm is mentioned in the ISO C standard, here extended
473
 * for 32 bits.
474
 * @param: seed
475
 * @return: random number
476
 */
477
uint32_t
478
tcpr_random(uint32_t *seed)
479
0
{
480
0
    unsigned int next = *seed;
481
0
    unsigned int result;
482
483
0
    next *= 1103515245;
484
0
    next += 12345;
485
0
    result = (int)(next / 65536) % 2048;
486
487
0
    next *= 1103515245;
488
0
    next += 12345;
489
0
    result <<= 10;
490
0
    result ^= (int)(next / 65536) % 1024;
491
492
0
    next *= 1103515245;
493
0
    next += 12345;
494
0
    result <<= 10;
495
0
    result ^= (int)(next / 65536) % 1024;
496
497
0
    *seed = next;
498
499
0
    return result;
500
0
}
501
502
/**
503
 * #416 - Ensure STDIN is not left in non-blocking mode after closing
504
 * a program. BSD and Unix derivatives should utilize `FIONBIO` due to known
505
 * issues with reading from tty with a 0 byte read returning -1 opposed to 0.
506
 */
507
void
508
restore_stdin(void)
509
0
{
510
0
#ifdef FIONBIO
511
0
    int nb = 0;
512
513
0
    ioctl(0, FIONBIO, &nb);
514
#else
515
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
516
#endif /* FIONBIO */
517
0
}