1 /* $Id$ */
2 
3 /*
4  *   Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
5  *   Copyright (c) 2013-2018 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
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 "config.h"
22 #include "defines.h"
23 #include "common.h"
24 
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 
31 #ifdef HAVE_SYS_IOCTL_H
32 #include <sys/ioctl.h>
33 #endif
34 
35 #ifdef DEBUG
36 extern int debug;
37 #endif
38 
39 /**
40  * this is wrapped up in a #define safe_malloc
41  * This function, detects failures to malloc memory and zeros out the
42  * memory before returning
43  */
44 
45 void *
_our_safe_malloc(size_t len,const char * funcname,const int line,const char * file)46 _our_safe_malloc(size_t len, const char *funcname, const int line, const char *file)
47 {
48     u_char *ptr;
49 
50     if ((ptr = malloc(len)) == NULL) {
51         fprintf(stderr, "ERROR in %s:%s() line %d: Unable to malloc() %zu bytes/n",
52                 file, funcname, line, len);
53         exit(-1);
54     }
55 
56     /* zero memory */
57     memset(ptr, 0, len);
58 
59     /* wrapped inside an #ifdef for better performance */
60     dbgx(5, "Malloc'd %zu bytes in %s:%s() line %d", len, file, funcname, line);
61 
62     return (void *)ptr;
63 }
64 
65 /**
66  * this is wrapped up in a #define safe_realloc
67  * This function, detects failures to realloc memory and zeros
68  * out the NEW memory if len > current len.  As always, remember
69  * to use it as:
70  * ptr = safe_realloc(ptr, size)
71  */
72 void *
_our_safe_realloc(void * ptr,size_t len,const char * funcname,const int line,const char * file)73 _our_safe_realloc(void *ptr, size_t len, const char *funcname, const int line, const char *file)
74 {
75 
76     if ((ptr = realloc(ptr, len)) == NULL) {
77         fprintf(stderr, "ERROR: in %s:%s() line %d: Unable to remalloc() buffer to %zu bytes", file, funcname, line, len);
78         exit(-1);
79     }
80 
81     dbgx(5, "Remalloc'd buffer to %zu bytes in %s:%s() line %d", len, file, funcname, line);
82 
83     return ptr;
84 }
85 
86 /**
87  * this is wrapped up in a #define safe_strdup
88  * This function, detects failures to realloc memory
89  */
90 char *
_our_safe_strdup(const char * str,const char * funcname,const int line,const char * file)91 _our_safe_strdup(const char *str, const char *funcname, const int line, const char *file)
92 {
93     char *newstr;
94 
95     if ((newstr = (char *)malloc(strlen(str) + 1)) == NULL) {
96         fprintf(stderr, "ERROR in %s:%s() line %d: Unable to strdup() %zu bytes\n", file, funcname, line, strlen(str));
97         exit(-1);
98     }
99 
100     memcpy(newstr, str, strlen(str) + 1);
101 
102     return newstr;
103 
104 }
105 
106 /**
107  * calls free and sets to NULL.
108  */
109 void
_our_safe_free(void * ptr,const char * funcname,const int line,const char * file)110 _our_safe_free(void *ptr, const char *funcname, const int line, const char *file)
111 {
112     assert(funcname);
113     assert(line);
114     assert(file);
115 
116     if (ptr == NULL)
117         return;
118 
119     free(ptr);
120 }
121 
122 /**
123  * get next packet in pcap file
124  */
_our_safe_pcap_next(pcap_t * pcap,struct pcap_pkthdr * pkthdr,const char * funcname,const int line,const char * file)125 u_char *_our_safe_pcap_next(pcap_t *pcap,  struct pcap_pkthdr *pkthdr,
126         const char *funcname, const int line, const char *file)
127 {
128     u_char *pktdata = (u_char *)pcap_next(pcap, pkthdr);
129 
130     if (pktdata) {
131         if (pkthdr->len > MAX_SNAPLEN) {
132             fprintf(stderr, "safe_pcap_next ERROR: Invalid packet length in %s:%s() line %d: %u is greater than maximum %u\n",
133                     file, funcname, line, pkthdr->len, MAX_SNAPLEN);
134             exit(-1);
135         }
136 
137         if (!pkthdr->len || !pkthdr->caplen) {
138             fprintf(stderr, "safe_pcap_next ERROR: Invalid packet length in %s:%s() line %d: packet length=%u capture length=%u\n",
139                     file, funcname, line, pkthdr->len, pkthdr->caplen);
140             exit(-1);
141         }
142 
143         /* attempt to correct invalid captures */
144         if (pkthdr->len < pkthdr->caplen) {
145             dbgx(1, "Correcting invalid packet capture length %d: packet length=%u",
146                     pkthdr->caplen, pkthdr->len);
147             pkthdr->caplen = pkthdr->len;
148         }
149     } else {
150         /* this will be reported as a failed packet in final report */
151         dbg(1, "No data found in packet");
152     }
153 
154     return pktdata;
155 }
156 
157 /**
158  * get next packet in pcap file (extended)
159  */
_our_safe_pcap_next_ex(pcap_t * pcap,struct pcap_pkthdr ** pkthdr,const u_char ** pktdata,const char * funcname,const int line,const char * file)160 int _our_safe_pcap_next_ex(pcap_t *pcap, struct pcap_pkthdr **pkthdr,
161         const u_char **pktdata, const char *funcname,
162         const int line, const char *file)
163 {
164     int res = pcap_next_ex(pcap, pkthdr, pktdata);
165 
166     if (*pktdata && *pkthdr) {
167         if ((*pkthdr)->len > MAXPACKET) {
168             fprintf(stderr, "safe_pcap_next_ex ERROR: Invalid packet length in %s:%s() line %d: %u is greater than maximum %u\n",
169                     file, funcname, line, (*pkthdr)->len, MAXPACKET);
170             exit(-1);
171         }
172 
173         if (!(*pkthdr)->len || (*pkthdr)->len < (*pkthdr)->caplen) {
174             fprintf(stderr, "safe_pcap_next_ex ERROR: Invalid packet length in %s:%s() line %d: packet length=%u capture length=%u\n",
175                     file, funcname, line, (*pkthdr)->len, (*pkthdr)->caplen);
176             exit(-1);
177         }
178 
179         if ((*pkthdr)->len < (*pkthdr)->caplen) {
180             dbgx(1, "Correcting invalid packet capture length %d: packet length=%u",
181                     (*pkthdr)->caplen, (*pkthdr)->len);
182             (*pkthdr)->caplen = (*pkthdr)->len;
183         }
184     } else {
185         /* this will be reported as a failed packet in final report */
186         dbgx(1, "No data found in packet 0x%p and/or header 0x%p",
187                 *pktdata, *pkthdr);
188     }
189 
190     return res;
191 }
192 
193 /**
194  * Print various packet statistics
195  */
196 void
packet_stats(const tcpreplay_stats_t * stats)197 packet_stats(const tcpreplay_stats_t *stats)
198 {
199     struct timeval diff;
200     COUNTER diff_us;
201     COUNTER bytes_sec = 0;
202     u_int32_t bytes_sec_10ths = 0;
203     COUNTER mb_sec = 0;
204     u_int32_t mb_sec_100ths = 0;
205     u_int32_t mb_sec_1000ths = 0;
206     COUNTER pkts_sec = 0;
207     u_int32_t pkts_sec_100ths = 0;
208 
209     timersub(&stats->end_time, &stats->start_time, &diff);
210     diff_us = TIMEVAL_TO_MICROSEC(&diff);
211 
212     if (diff_us && stats->pkts_sent && stats->bytes_sent) {
213         COUNTER bytes_sec_X10;
214         COUNTER pkts_sec_X100;
215         COUNTER mb_sec_X1000;
216         COUNTER mb_sec_X100;
217 
218         if (stats->bytes_sent > 1000 * 1000 * 1000 && diff_us > 1000 * 1000) {
219             bytes_sec_X10 = (stats->bytes_sent * 10 * 1000) / (diff_us / 1000);
220             pkts_sec_X100 = (stats->pkts_sent * 100 * 1000) / (diff_us / 1000);
221          } else {
222             bytes_sec_X10 = (stats->bytes_sent * 10 * 1000 * 1000) / diff_us;
223             pkts_sec_X100 = (stats->pkts_sent * 100 * 1000 * 1000) / diff_us;
224          }
225 
226         bytes_sec = bytes_sec_X10 / 10;
227         bytes_sec_10ths = bytes_sec_X10 % 10;
228 
229         mb_sec_X1000 = (bytes_sec * 8) / 1000;
230         mb_sec_X100 = mb_sec_X1000 / 10;
231         mb_sec = mb_sec_X1000 / 1000;
232         mb_sec_100ths = mb_sec_X100 % 100;
233         mb_sec_1000ths = mb_sec_X1000 % 1000;
234 
235         pkts_sec = pkts_sec_X100 / 100;
236         pkts_sec_100ths = pkts_sec_X100 % 100;
237     }
238 
239     if (diff_us >= 1000 * 1000)
240         printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n",
241                 stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_usec / (10 * 1000)));
242     else
243         printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n",
244                 stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_usec);
245 
246 
247     if (mb_sec >= 1)
248         printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n",
249                bytes_sec, bytes_sec_10ths, mb_sec, mb_sec_100ths, pkts_sec, pkts_sec_100ths);
250     else
251         printf("Rated: %llu.%1u Bps, %llu.%03u Mbps, %llu.%02u pps\n",
252                bytes_sec, bytes_sec_10ths, mb_sec, mb_sec_1000ths, pkts_sec, pkts_sec_100ths);
253     fflush(NULL);
254 
255     if (stats->failed)
256         printf("Failed write attempts: " COUNTER_SPEC "\n",
257                 stats->failed);
258 }
259 
260 /**
261  * fills a buffer with a string representing the given time
262  *
263  * @param when: the time that should be formatted
264  * @param buf: a buffer to write to
265  * @param len: length of the buffer
266  * @return: string containing date, or -1 on error
267  */
format_date_time(struct timeval * when,char * buf,size_t len)268 int format_date_time(struct timeval *when, char *buf, size_t len)
269 {
270     struct tm *tm;
271     char tmp[64];
272 
273     assert(len);
274 
275     tm = localtime(&when->tv_sec);
276     if (!tm)
277         return -1;
278 
279     strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%06u", tm);
280     return snprintf(buf, len, tmp, when->tv_usec);
281 }
282 
283 /**
284  * reads a hexstring in the format of xx,xx,xx,xx spits it back into *hex
285  * up to hexlen bytes.  Returns actual number of bytes returned.  On error
286  * it just calls errx() since all errors are fatal.
287  */
288 int
read_hexstring(const char * l2string,u_char * hex,const int hexlen)289 read_hexstring(const char *l2string, u_char *hex, const int hexlen)
290 {
291     int numbytes = 0;
292     unsigned int value;
293     char *l2byte;
294     u_char databyte;
295     char *token = NULL;
296     char *string;
297 
298     string = safe_strdup(l2string);
299 
300     if (hexlen <= 0)
301         err(-1, "Hex buffer must be > 0");
302 
303     memset(hex, '\0', hexlen);
304 
305     /* data is hex, comma separated, byte by byte */
306 
307     /* get the first byte */
308     l2byte = strtok_r(string, ",", &token);
309     sscanf(l2byte, "%x", &value);
310     if (value > 0xff)
311         errx(-1, "Invalid hex string byte: %s", l2byte);
312     databyte = (u_char) value;
313     memcpy(&hex[numbytes], &databyte, 1);
314 
315     /* get remaining bytes */
316     while ((l2byte = strtok_r(NULL, ",", &token)) != NULL) {
317         numbytes++;
318         if (numbytes + 1 > hexlen) {
319             warn("Hex buffer too small for data- skipping data");
320             goto done;
321         }
322         sscanf(l2byte, "%x", &value);
323         if (value > 0xff)
324             errx(-1, "Invalid hex string byte: %s", l2byte);
325         databyte = (u_char) value;
326         memcpy(&hex[numbytes], &databyte, 1);
327     }
328 
329     numbytes++;
330 
331 done:
332     safe_free(string);
333 
334     dbgx(1, "Read %d bytes of hex data", numbytes);
335     return (numbytes);
336 }
337 
338 #ifdef USE_CUSTOM_INET_ATON
339 int
inet_aton(const char * name,struct in_addr * addr)340 inet_aton(const char *name, struct in_addr *addr)
341 {
342     in_addr_t a = inet_addr(name);
343     addr->s_addr = a;
344     return a != (in_addr_t)-1;
345 }
346 #endif
347 
348 #if SIZEOF_LONG  == 4
__div64_32(uint64_t * n,uint32_t base)349 uint32_t __div64_32(uint64_t *n, uint32_t base)
350 {
351     uint64_t rem = *n;
352     uint64_t b = base;
353     uint64_t res, d = 1;
354     uint32_t high = rem >> 32;
355 
356     /* Reduce the thing a bit first */
357     res = 0;
358     if (high >= base) {
359         high /= base;
360         res = (uint64_t) high << 32;
361         rem -= (uint64_t) (high*base) << 32;
362     }
363 
364     while ((int64_t)b > 0 && b < rem) {
365         b = b+b;
366         d = d+d;
367     }
368 
369     do {
370         if (rem >= b) {
371             rem -= b;
372             res += d;
373         }
374         b >>= 1;
375         d >>= 1;
376     } while (d);
377 
378     *n = res;
379     return rem;
380 }
381 #endif /* SIZEOF_LONG  == 4 */
382 
383 /**
384  * Implementation of rand_r that is consistent across all platforms
385  * This algorithm is mentioned in the ISO C standard, here extended
386  * for 32 bits.
387  * @param: seed
388  * @return: random number
389  */
tcpr_random(uint32_t * seed)390 uint32_t tcpr_random(uint32_t *seed)
391 {
392   unsigned int next = *seed;
393   int result;
394 
395   next *= 1103515245;
396   next += 12345;
397   result = (unsigned int) (next / 65536) % 2048;
398 
399   next *= 1103515245;
400   next += 12345;
401   result <<= 10;
402   result ^= (unsigned int) (next / 65536) % 1024;
403 
404   next *= 1103515245;
405   next += 12345;
406   result <<= 10;
407   result ^= (unsigned int) (next / 65536) % 1024;
408 
409   *seed = next;
410 
411   return result;
412 }
413 
414 /**
415  * #416 - Ensure STDIN is not left in non-blocking mode after closing
416  * a program. BSD and Unix derivatives should utilize `FIONBIO` due to known
417  * issues with reading from tty with a 0 byte read returning -1 opposed to 0.
418  */
restore_stdin(void)419 void restore_stdin(void)
420 {
421 #ifdef FIONBIO
422     int nb = 0;
423 
424     ioctl(0, FIONBIO, &nb);
425 #else
426     fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
427 #endif /* FIONBIO */
428 }
429