1/*
2 * Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <stdio.h>
30#include <err.h>
31#include <sysexits.h>
32#include <string.h>
33#include <strings.h>
34#include <stdlib.h>
35#include <fcntl.h>
36#include <unistd.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40
41#include "bpf.h"
42
43#include "pcap-ng.h"
44
45enum {
46	SIMPLE_PACKET = 0,
47	ENHANCED_PACKET = 1,
48	OBSOLETE_PACKET = 2
49};
50
51const char *if_name = NULL;
52int type_of_packet = ENHANCED_PACKET;
53const char *proc_name = NULL;
54uint32_t proc_pid = 0;
55int32_t proc_index = -1;
56const char *comment = NULL;
57pcap_dumper_t *dumper = NULL;
58size_t ext_len = 65536;
59void *ext_buffer = NULL;
60int first_comment = 0;
61unsigned char *packet_data = NULL;
62size_t packet_length = 0;
63unsigned long num_data_blocks = 1;
64int copy_data_buffer = 0;
65
66void hex_and_ascii_print(const char *, const void *, size_t, const char *);
67
68void
69help(const char *str)
70{
71	printf("# usage: %s options...\n", str);
72	printf("# options in order of dependency:\n");
73	printf(" %-20s # %s\n", "-4 ipv4:name[:name...]", "IPv4 name resolution record");
74	printf(" %-20s # %s\n", "-6 ipv6:name[:name...]", "IPv6 name resolution record");
75	printf(" %-20s # %s\n", "-C", "copy data packet from data buffer");
76	printf(" %-20s # %s\n", "-c string", "comment option");
77	printf(" %-20s # %s\n", "-D length", "packet data length");
78	printf(" %-20s # %s\n", "-d string", "packet data as a string");
79	printf(" %-20s # %s\n", "-f", "first comment option");
80	printf(" %-20s # %s\n", "-i name", "interface name");
81	printf(" %-20s # %s\n", "-n num_data", "number of data blocks");
82	printf(" %-20s # %s\n", "-p name:pid", "process name and pid");
83	printf(" %-20s # %s\n", "-e", "enhanced packet block (default)");
84	printf(" %-20s # %s\n", "-o", "(obsolete) packet block -- not implemented");
85	printf(" %-20s # %s\n", "-s", "simple packet block");
86	printf(" %-20s # %s\n", "-w name", "packet capture file name");
87	printf(" %-20s # %s\n", "-x [buffer_length]", "externalize in buffer of given length");
88}
89
90void
91write_block(pcapng_block_t block)
92{
93	bpf_u_int32 n;
94
95	if (dumper != NULL) {
96		n = pcap_ng_dump_block(dumper, block);
97		if (n != pcap_ng_block_get_len(block))
98			printf("%s: block len %u != pcap_ng_block_get_len() %u\n",
99				   __func__, pcap_ng_block_get_len(block), n);
100	}
101	if (ext_buffer != NULL) {
102		n = pcap_ng_externalize_block(ext_buffer, ext_len, block);
103		hex_and_ascii_print("", ext_buffer, pcap_ng_block_get_len(block), "");
104		if (n != pcap_ng_block_get_len(block))
105			printf("%s: block len %u != pcap_ng_externalize_block() %u\n",
106				   __func__, pcap_ng_block_get_len(block), n);
107	}
108}
109
110pcapng_block_t
111make_interface_description_block(pcapng_block_t block, const char *name)
112{
113	struct pcapng_interface_description_fields *idb_fields;
114
115	pcap_ng_block_reset(block, PCAPNG_BT_IDB);
116
117	if (first_comment && comment)
118		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
119
120	idb_fields = pcap_ng_get_interface_description_fields(block);
121	idb_fields->linktype = DLT_RAW;
122	idb_fields->snaplen = 65536;
123
124	if (!first_comment && comment)
125		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
126
127	pcap_ng_block_add_option_with_string(block, PCAPNG_IF_NAME, name);
128
129	write_block(block);
130
131	return (block);
132}
133
134pcapng_block_t
135make_process_information_block(pcapng_block_t block, const char *name, uint32_t pid)
136{
137	struct pcapng_process_information_fields *pib_fields;
138
139	pcap_ng_block_reset(block, PCAPNG_BT_PIB);
140
141	if (first_comment && comment)
142		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
143
144	pib_fields = pcap_ng_get_process_information_fields(block);
145	pib_fields->process_id = pid;
146
147	if (!first_comment && comment)
148		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
149
150	pcap_ng_block_add_option_with_string(block, PCAPNG_PIB_NAME, name);
151
152	write_block(block);
153
154	return (block);
155}
156
157pcapng_block_t
158make_data_block(pcapng_block_t block, const void *data, size_t len)
159{
160	unsigned int i;
161
162	for (i = 0; i < num_data_blocks; i++) {
163		switch (type_of_packet) {
164			case SIMPLE_PACKET: {
165				pcap_ng_block_reset(block, PCAPNG_BT_SPB);
166
167				if (first_comment && comment)
168					pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
169
170				if (copy_data_buffer)
171					pcap_ng_block_packet_copy_data(block, data, len);
172				else
173					pcap_ng_block_packet_set_data(block, data, len);
174
175				if (!first_comment && comment)
176					pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
177
178				write_block(block);
179				break;
180			}
181			case ENHANCED_PACKET: {
182				struct pcapng_enhanced_packet_fields *epb_fields;
183
184				pcap_ng_block_reset(block, PCAPNG_BT_EPB);
185
186				if (first_comment && comment)
187					pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
188
189				epb_fields = pcap_ng_get_enhanced_packet_fields(block);
190				epb_fields->caplen = len;
191				epb_fields->len = len;
192				epb_fields->interface_id = 0;
193				epb_fields->timestamp_high = 10000;
194				epb_fields->timestamp_low = 2000;
195
196				if (copy_data_buffer)
197					pcap_ng_block_packet_copy_data(block, data, len);
198				else
199					pcap_ng_block_packet_set_data(block, data, len);
200
201				if (proc_name != NULL) {
202					pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PIB_INDEX, &proc_index, sizeof(proc_index));
203				}
204				if (!first_comment && comment)
205					pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
206
207				write_block(block);
208				break;
209			}
210			default:
211				break;
212		}
213	}
214	return (block);
215}
216
217void
218make_section_header_block()
219{
220	pcapng_block_t block = pcap_ng_block_alloc(65536);
221
222	pcap_ng_block_reset(block, PCAPNG_BT_SHB);
223
224	pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT,
225												  "section header block");
226
227	write_block(block);
228
229	pcap_ng_free_block(block);
230
231	return;
232}
233
234pcapng_block_t
235make_name_resolution_record(pcapng_block_t block, int af, void *addr, char **names)
236{
237	pcap_ng_block_reset(block, PCAPNG_BT_NRB);
238
239	if (first_comment && comment)
240		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
241
242	if (af == AF_INET) {
243		pcap_ng_block_add_name_record_with_ip4(block, (struct in_addr *)addr, (const char **)names);
244	} else {
245		pcap_ng_block_add_name_record_with_ip6(block, (struct in6_addr *)addr, (const char **)names);
246	}
247
248	if (!first_comment && comment)
249		pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
250
251	write_block(block);
252
253	return (block);
254}
255
256int
257main(int argc, char * const argv[])
258{
259	int ch;
260	const char *file_name = NULL;
261	pcapng_block_t block;
262
263	/*
264	 * Loop through argument to build PCAP-NG block
265	 * Optionally write to file
266	 */
267	while ((ch = getopt(argc, argv, "4:6:Cc:D:d:efhi:n:o:p:sw:x")) != -1) {
268		switch (ch) {
269			case 'C':
270				copy_data_buffer = 1;
271				break;
272
273			case 'c':
274				comment = optarg;
275				break;
276
277			case 'D': {
278				int i;
279
280				packet_length = strtoul(optarg, NULL, 0);
281				packet_data = malloc(packet_length);
282				for (i = 0; i < packet_length; i++) {
283					packet_data[i] = i % 256;
284				}
285
286				block = pcap_ng_block_alloc(65536);
287				make_data_block(block, packet_data, packet_length);
288				pcap_ng_free_block(block);
289				break;
290			}
291			case 'd':
292				packet_length  = strlen(optarg);
293				packet_data = (unsigned char *)optarg;
294				block = pcap_ng_block_alloc(65536);
295				make_data_block(block, packet_data, packet_length);
296				pcap_ng_free_block(block);
297				break;
298
299			case 'e':
300				type_of_packet = ENHANCED_PACKET;
301				break;
302
303			case 'f':
304				first_comment = 1;
305				break;
306
307			case 'h':
308				help(argv[0]);
309				return (0);
310
311			case 'i':
312				if_name = optarg;
313
314				block = pcap_ng_block_alloc(65536);
315				make_interface_description_block(block, if_name);
316				pcap_ng_free_block(block);
317
318				break;
319
320			case '4':
321			case '6': {
322				int af;
323				int name_count = 0;
324				char *ptr = strchr(optarg, ':');
325				char **names = NULL;
326				int i;
327				struct in_addr ina;
328				struct in6_addr in6a;
329				int retval;
330
331
332				if (ptr == NULL) {
333					fprintf(stderr, "malformed arguments for option 'n'\n");
334					help(argv[0]);
335					return (0);
336				}
337				do {
338					ptr++;
339					name_count += 1;
340				} while ((ptr = strchr(ptr, ':')) != NULL);
341
342				names = calloc(name_count +1, sizeof(char *));
343
344				ptr = strchr(optarg, ':');
345				*ptr = '\0';
346
347				if (ch == '4') {
348					af = AF_INET;
349					retval = inet_pton(af, optarg, &ina);
350				} else {
351					af = AF_INET6;
352					retval = inet_pton(af, optarg, &in6a);
353				}
354				if (retval == 0)
355					errx(1, "This is not an %s address: '%s'\n",
356						 af == AF_INET ? "IPv4" : "IPv6",
357						 optarg);
358				else if (retval == -1)
359					err(1, "inet_pton(%s) failed\n", optarg);
360
361				for (i = 0; i < name_count; i++) {
362					char *end;
363					ptr++;
364					end = strchr(ptr, ':');
365					if (end != NULL)
366						*end = '\0';
367
368					names[i] = strdup(ptr);
369					if (end != NULL)
370						ptr = end;
371				}
372
373				block = pcap_ng_block_alloc(65536);
374				if (af == AF_INET)
375					make_name_resolution_record(block, af,  &ina, names);
376				else
377					make_name_resolution_record(block, af, &in6a, names);
378				pcap_ng_free_block(block);
379				break;
380			}
381
382			case 'n': {
383				num_data_blocks = strtoul(optarg, NULL, 0);
384				break;
385			}
386
387			case 'o':
388				type_of_packet = OBSOLETE_PACKET;
389				break;
390
391			case 'p': {
392				char *ptr = strchr(optarg, ':');
393				char *endptr;
394
395				if (ptr == NULL) {
396					fprintf(stderr, "malformed arguments for option 'p'\n");
397					help(argv[0]);
398					return (0);
399				}
400				proc_name = strndup(optarg, (ptr - optarg));
401				ptr += 1;
402				if (*ptr == '\0') {
403					fprintf(stderr, "malformed arguments for option 'p'\n");
404					help(argv[0]);
405					return (0);
406				}
407				proc_pid = (uint32_t)strtoul(ptr, &endptr, 0);
408				if (endptr != NULL && *endptr != '\0') {
409					fprintf(stderr, "malformed arguments for option 'p'\n");
410					help(argv[0]);
411					return (0);
412				}
413				proc_index += 1;
414
415				block = pcap_ng_block_alloc(65536);
416				make_process_information_block(block, proc_name, proc_pid);
417				pcap_ng_free_block(block);
418				break;
419			}
420			case 's':
421				type_of_packet = SIMPLE_PACKET;
422				break;
423
424			case 'w':
425				file_name = optarg;
426
427				if (dumper != NULL)
428					pcap_ng_dump_close(dumper);
429
430				pcap_t *pcap = pcap_open_dead(DLT_PCAPNG, 65536);
431				if (pcap == NULL)
432					err(EX_OSERR, "pcap_open_dead(DLT_PCAPNG, 65536) failed\n");
433
434				dumper = pcap_ng_dump_open(pcap, file_name);
435				if (pcap == NULL)
436					err(EX_OSERR,  "pcap_ng_dump_open(%s) failed\n", file_name);
437
438
439				make_section_header_block();
440				break;
441
442			case 'x':
443				if (optind < argc) {
444					if (argv[optind][0] != '-') {
445						char *endptr;
446						unsigned long num = strtoul(argv[optind], &endptr, 0);
447						if (endptr != NULL && *endptr == '\0') {
448							optind++;
449							ext_len = num;
450						}
451					}
452				}
453				ext_buffer = malloc(ext_len);
454				if (ext_buffer == NULL)
455					errx(EX_OSERR, "malloc(%lu) failed", ext_len);
456				break;
457			default:
458				help(argv[0]);
459				return (0);
460		}
461	}
462
463	if (dumper != NULL)
464		pcap_ng_dump_close(dumper);
465
466	return (0);
467}