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 /*
22 * Purpose: Modify packets in a pcap file based on rules provided by the
23 * user to offload work from tcpreplay and provide a easier means of
24 * reproducing traffic for testing purposes.
25 */
26
27
28 #include "config.h"
29 #include "defines.h"
30 #include "common.h"
31
32 #include <ctype.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 #include <errno.h>
40
41 #include "tcprewrite.h"
42 #include "tcprewrite_opts.h"
43 #include "tcpedit/tcpedit.h"
44 #include "tcpedit/fuzzing.h"
45
46 #ifdef DEBUG
47 int debug;
48 #endif
49
50 #ifdef ENABLE_VERBOSE
51 /* tcpdump handle */
52 tcpdump_t tcpdump;
53 #endif
54
55 tcprewrite_opt_t options;
56 tcpedit_t *tcpedit;
57
58 /* local functions */
59 void tcprewrite_init(void);
60 void post_args(int argc, char *argv[]);
61 int rewrite_packets(tcpedit_t *tcpedit, pcap_t *pin, pcap_dumper_t *pout);
62
63 int
main(int argc,char * argv[])64 main(int argc, char *argv[])
65 {
66 int optct, rcode;
67 pcap_t *dlt_pcap;
68 #ifdef ENABLE_FRAGROUTE
69 char ebuf[FRAGROUTE_ERRBUF_LEN];
70 #endif
71 tcprewrite_init();
72
73 /* call autoopts to process arguments */
74 optct = optionProcess(&tcprewriteOptions, argc, argv);
75 argc -= optct;
76 argv += optct;
77
78 /* parse the tcprewrite args */
79 post_args(argc, argv);
80
81 /* init tcpedit context */
82 if (tcpedit_init(&tcpedit, pcap_datalink(options.pin)) < 0) {
83 errx(-1, "Error initializing tcpedit: %s", tcpedit_geterr(tcpedit));
84 }
85
86 /* parse the tcpedit args */
87 rcode = tcpedit_post_args(tcpedit);
88 if (rcode < 0) {
89 tcpedit_close(&tcpedit);
90 errx(-1, "Unable to parse args: %s", tcpedit_geterr(tcpedit));
91 } else if (rcode == 1) {
92 warnx("%s", tcpedit_geterr(tcpedit));
93 }
94
95 if (tcpedit_validate(tcpedit) < 0) {
96 tcpedit_close(&tcpedit);
97 errx(-1, "Unable to edit packets given options:\n%s",
98 tcpedit_geterr(tcpedit));
99 }
100
101 /* fuzzing init */
102 fuzzing_init(tcpedit->fuzz_seed, tcpedit->fuzz_factor);
103
104 /* open up the output file */
105 options.outfile = safe_strdup(OPT_ARG(OUTFILE));
106 dbgx(1, "Rewriting DLT to %s",
107 pcap_datalink_val_to_name(tcpedit_get_output_dlt(tcpedit)));
108 if ((dlt_pcap = pcap_open_dead(tcpedit_get_output_dlt(tcpedit), 65535)) == NULL) {
109 tcpedit_close(&tcpedit);
110 err(-1, "Unable to open dead pcap handle.");
111 }
112
113 dbgx(1, "DLT of dlt_pcap is %s",
114 pcap_datalink_val_to_name(pcap_datalink(dlt_pcap)));
115
116 #ifdef ENABLE_FRAGROUTE
117 if (options.fragroute_args) {
118 if ((options.frag_ctx = fragroute_init(65535, pcap_datalink(dlt_pcap), options.fragroute_args, ebuf)) == NULL) {
119 tcpedit_close(&tcpedit);
120 errx(-1, "%s", ebuf);
121 }
122 }
123 #endif
124
125 #ifdef ENABLE_VERBOSE
126 if (options.verbose) {
127 tcpdump_open(&tcpdump, dlt_pcap);
128 }
129 #endif
130
131 if ((options.pout = pcap_dump_open(dlt_pcap, options.outfile)) == NULL) {
132 tcpedit_close(&tcpedit);
133 errx(-1, "Unable to open output pcap file: %s", pcap_geterr(dlt_pcap));
134 }
135
136 pcap_close(dlt_pcap);
137
138 /* rewrite packets */
139 if (rewrite_packets(tcpedit, options.pin, options.pout) != 0) {
140 tcpedit_close(&tcpedit);
141 errx(-1, "Error rewriting packets: %s", tcpedit_geterr(tcpedit));
142 }
143
144 /* clean up after ourselves */
145 pcap_dump_close(options.pout);
146 pcap_close(options.pin);
147 tcpedit_close(&tcpedit);
148
149 #ifdef ENABLE_VERBOSE
150 tcpdump_close(&tcpdump);
151 #endif
152
153 #ifdef ENABLE_FRAGROUTE
154 if (options.frag_ctx) {
155 fragroute_close(options.frag_ctx);
156 }
157 #endif
158
159 #ifdef ENABLE_DMALLOC
160 dmalloc_shutdown();
161 #endif
162
163 restore_stdin();
164 return 0;
165 }
166
167 void
tcprewrite_init(void)168 tcprewrite_init(void)
169 {
170
171 memset(&options, 0, sizeof(options));
172
173 #ifdef ENABLE_VERBOSE
174 /* clear out tcpdump struct */
175 memset(&tcpdump, '\0', sizeof(tcpdump_t));
176 #endif
177
178 if (fcntl(STDERR_FILENO, F_SETFL, O_NONBLOCK) < 0)
179 warnx("Unable to set STDERR to non-blocking: %s", strerror(errno));
180 }
181
182 /**
183 * post AutoGen argument processing
184 */
185 void
post_args(_U_ int argc,_U_ char * argv[])186 post_args(_U_ int argc, _U_ char *argv[])
187 {
188 char ebuf[PCAP_ERRBUF_SIZE];
189
190 #ifdef DEBUG
191 if (HAVE_OPT(DBUG))
192 debug = OPT_VALUE_DBUG;
193 #else
194 if (HAVE_OPT(DBUG))
195 warn("not configured with --enable-debug. Debugging disabled.");
196 #endif
197
198
199 #ifdef ENABLE_VERBOSE
200 if (HAVE_OPT(VERBOSE))
201 options.verbose = 1;
202
203 if (HAVE_OPT(DECODE))
204 tcpdump.args = safe_strdup(OPT_ARG(DECODE));
205 #endif
206
207
208 #ifdef ENABLE_FRAGROUTE
209 if (HAVE_OPT(FRAGROUTE))
210 options.fragroute_args = safe_strdup(OPT_ARG(FRAGROUTE));
211
212 options.fragroute_dir = FRAGROUTE_DIR_BOTH;
213 if (HAVE_OPT(FRAGDIR)) {
214 if (strcmp(OPT_ARG(FRAGDIR), "c2s") == 0) {
215 options.fragroute_dir = FRAGROUTE_DIR_C2S;
216 } else if (strcmp(OPT_ARG(FRAGDIR), "s2c") == 0) {
217 options.fragroute_dir = FRAGROUTE_DIR_S2C;
218 } else if (strcmp(OPT_ARG(FRAGDIR), "both") == 0) {
219 options.fragroute_dir = FRAGROUTE_DIR_BOTH;
220 } else {
221 errx(-1, "Unknown --fragdir value: %s", OPT_ARG(FRAGDIR));
222 }
223 }
224 #endif
225
226 /* open up the input file */
227 options.infile = safe_strdup(OPT_ARG(INFILE));
228 if ((options.pin = pcap_open_offline(options.infile, ebuf)) == NULL)
229 errx(-1, "Unable to open input pcap file: %s", ebuf);
230
231 #ifdef HAVE_PCAP_SNAPSHOT
232 if (pcap_snapshot(options.pin) < 65535)
233 warnx("%s was captured using a snaplen of %d bytes. This may mean you have truncated packets.",
234 options.infile, pcap_snapshot(options.pin));
235 #endif
236
237 }
238
239 /**
240 * Main loop to rewrite packets
241 */
242 int
rewrite_packets(tcpedit_t * tcpedit,pcap_t * pin,pcap_dumper_t * pout)243 rewrite_packets(tcpedit_t *tcpedit, pcap_t *pin, pcap_dumper_t *pout)
244 {
245 tcpr_dir_t cache_result = TCPR_DIR_C2S; /* default to primary */
246 struct pcap_pkthdr pkthdr, *pkthdr_ptr; /* packet header */
247 const u_char *pktconst = NULL; /* packet from libpcap */
248 u_char **pktdata = NULL;
249 static u_char *pktdata_buff;
250 static char *frag = NULL;
251 COUNTER packetnum = 0;
252 int rcode;
253 #ifdef ENABLE_FRAGROUTE
254 int frag_len, proto;
255 #endif
256
257 pkthdr_ptr = &pkthdr;
258
259 if (pktdata_buff == NULL)
260 pktdata_buff = (u_char *)safe_malloc(MAXPACKET);
261
262 pktdata = &pktdata_buff;
263
264 if (frag == NULL)
265 frag = (char *)safe_malloc(MAXPACKET);
266
267 /* MAIN LOOP
268 * Keep sending while we have packets or until
269 * we've sent enough packets
270 */
271 while ((pktconst = safe_pcap_next(pin, pkthdr_ptr)) != NULL) {
272 packetnum++;
273 dbgx(2, "packet " COUNTER_SPEC " caplen %d", packetnum, pkthdr.caplen);
274
275 if (pkthdr.caplen > MAX_SNAPLEN)
276 errx(-1, "Frame too big, caplen %d exceeds %d", pkthdr.caplen, MAX_SNAPLEN);
277 /*
278 * copy over the packet so we can pad it out if necessary and
279 * because pcap_next() returns a const ptr
280 */
281 memcpy(*pktdata, pktconst, pkthdr.caplen);
282
283 #ifdef ENABLE_VERBOSE
284 if (options.verbose)
285 tcpdump_print(&tcpdump, pkthdr_ptr, *pktdata);
286 #endif
287
288 /* Dual nic processing? */
289 if (options.cachedata != NULL) {
290 cache_result = check_cache(options.cachedata, packetnum);
291 }
292
293 /* sometimes we should not send the packet, in such cases
294 * no point in editing this packet at all, just write it to the
295 * output file (note, we can't just remove it, or the tcpprep cache
296 * file will loose it's indexing
297 */
298
299 if (cache_result == TCPR_DIR_NOSEND)
300 goto WRITE_PACKET; /* still need to write it so cache stays in sync */
301
302 if ((rcode = tcpedit_packet(tcpedit, &pkthdr_ptr, pktdata, cache_result)) == TCPEDIT_ERROR) {
303 return -1;
304 } else if ((rcode == TCPEDIT_SOFT_ERROR) && HAVE_OPT(SKIP_SOFT_ERRORS)) {
305 /* don't write packet */
306 dbgx(1, "Packet " COUNTER_SPEC " is suppressed from being written due to soft errors", packetnum);
307 continue;
308 }
309
310
311 WRITE_PACKET:
312 #ifdef ENABLE_FRAGROUTE
313 if (options.frag_ctx == NULL) {
314 /* write the packet when there's no fragrouting to be done */
315 pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata);
316 } else {
317 /* get the L3 protocol of the packet */
318 proto = tcpedit_l3proto(tcpedit, AFTER_PROCESS, *pktdata, pkthdr_ptr->caplen);
319
320 /* packet is IPv4/IPv6 AND needs to be fragmented */
321 if ((proto == ETHERTYPE_IP || proto == ETHERTYPE_IP6) &&
322 ((options.fragroute_dir == FRAGROUTE_DIR_BOTH) ||
323 (cache_result == TCPR_DIR_C2S && options.fragroute_dir == FRAGROUTE_DIR_C2S) ||
324 (cache_result == TCPR_DIR_S2C && options.fragroute_dir == FRAGROUTE_DIR_S2C))) {
325 #ifdef DEBUG
326 int i = 0;
327 #endif
328 if (fragroute_process(options.frag_ctx, *pktdata, pkthdr_ptr->caplen) < 0)
329 errx(-1, "Error processing packet via fragroute: %s", options.frag_ctx->errbuf);
330
331 while ((frag_len = fragroute_getfragment(options.frag_ctx, &frag)) > 0) {
332 /* frags get the same timestamp as the original packet */
333 dbgx(1, "processing packet " COUNTER_SPEC " frag: %u (%d)", packetnum, i++, frag_len);
334 pkthdr_ptr->caplen = frag_len;
335 pkthdr_ptr->len = frag_len;
336 pcap_dump((u_char *)pout, pkthdr_ptr, (u_char *)frag);
337 }
338 } else {
339 /* write the packet without fragroute */
340 pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata);
341 }
342 }
343 #else
344 /* write the packet when there's no fragrouting to be done */
345 pcap_dump((u_char *)pout, pkthdr_ptr, *pktdata);
346
347 #endif
348 } /* while() */
349 return 0;
350 }
351