#include <stdio.h>
#include <err.h>
#include <sysexits.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "bpf.h"
#include "pcap-ng.h"
enum {
SIMPLE_PACKET = 0,
ENHANCED_PACKET = 1,
OBSOLETE_PACKET = 2
};
const char *if_name = NULL;
int type_of_packet = ENHANCED_PACKET;
const char *proc_name = NULL;
uint32_t proc_pid = 0;
int32_t proc_index = -1;
const char *comment = NULL;
pcap_dumper_t *dumper = NULL;
size_t ext_len = 65536;
void *ext_buffer = NULL;
int first_comment = 0;
unsigned char *packet_data = NULL;
size_t packet_length = 0;
unsigned long num_data_blocks = 1;
int copy_data_buffer = 0;
void hex_and_ascii_print(const char *, const void *, unsigned int , const char *);
void
help(const char *str)
{
printf("# usage: %s options...\n", str);
printf("# options in order of dependency:\n");
printf(" %-20s # %s\n", "-4 ipv4:name[:name...]", "IPv4 name resolution record");
printf(" %-20s # %s\n", "-6 ipv6:name[:name...]", "IPv6 name resolution record");
printf(" %-20s # %s\n", "-C", "copy data packet from data buffer");
printf(" %-20s # %s\n", "-c string", "comment option");
printf(" %-20s # %s\n", "-D length", "packet data length");
printf(" %-20s # %s\n", "-d string", "packet data as a string");
printf(" %-20s # %s\n", "-f", "first comment option");
printf(" %-20s # %s\n", "-i name", "interface name");
printf(" %-20s # %s\n", "-n num_data", "number of data blocks");
printf(" %-20s # %s\n", "-p name:pid", "process name and pid");
printf(" %-20s # %s\n", "-e", "enhanced packet block (default)");
printf(" %-20s # %s\n", "-o", "(obsolete) packet block -- not implemented");
printf(" %-20s # %s\n", "-s", "simple packet block");
printf(" %-20s # %s\n", "-w name", "packet capture file name");
printf(" %-20s # %s\n", "-x [buffer_length]", "externalize in buffer of given length");
}
void
write_block(pcapng_block_t block)
{
bpf_u_int32 n;
if (dumper != NULL) {
n = pcap_ng_dump_block(dumper, block);
if (n != pcap_ng_block_get_len(block))
printf("%s: block len %u != pcap_ng_block_get_len() %u\n",
__func__, pcap_ng_block_get_len(block), n);
}
if (ext_buffer != NULL) {
n = pcap_ng_externalize_block(ext_buffer, ext_len, block);
hex_and_ascii_print("", ext_buffer, pcap_ng_block_get_len(block), "");
if (n != pcap_ng_block_get_len(block))
printf("%s: block len %u != pcap_ng_externalize_block() %u\n",
__func__, pcap_ng_block_get_len(block), n);
}
}
pcapng_block_t
make_interface_description_block(pcapng_block_t block, const char *name)
{
struct pcapng_interface_description_fields *idb_fields;
pcap_ng_block_reset(block, PCAPNG_BT_IDB);
if (first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
idb_fields = pcap_ng_get_interface_description_fields(block);
idb_fields->linktype = DLT_RAW;
idb_fields->snaplen = 65536;
if (!first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
pcap_ng_block_add_option_with_string(block, PCAPNG_IF_NAME, name);
write_block(block);
return (block);
}
pcapng_block_t
make_process_information_block(pcapng_block_t block, const char *name, uint32_t pid)
{
struct pcapng_process_information_fields *pib_fields;
pcap_ng_block_reset(block, PCAPNG_BT_PIB);
if (first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
pib_fields = pcap_ng_get_process_information_fields(block);
pib_fields->process_id = pid;
if (!first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
pcap_ng_block_add_option_with_string(block, PCAPNG_PIB_NAME, name);
write_block(block);
return (block);
}
pcapng_block_t
make_data_block(pcapng_block_t block, const void *data, size_t len)
{
unsigned int i;
for (i = 0; i < num_data_blocks; i++) {
switch (type_of_packet) {
case SIMPLE_PACKET: {
pcap_ng_block_reset(block, PCAPNG_BT_SPB);
if (first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
if (copy_data_buffer)
pcap_ng_block_packet_copy_data(block, data, len);
else
pcap_ng_block_packet_set_data(block, data, len);
if (!first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
write_block(block);
break;
}
case ENHANCED_PACKET: {
struct pcapng_enhanced_packet_fields *epb_fields;
pcap_ng_block_reset(block, PCAPNG_BT_EPB);
if (first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
epb_fields = pcap_ng_get_enhanced_packet_fields(block);
epb_fields->caplen = len;
epb_fields->len = len;
epb_fields->interface_id = 0;
epb_fields->timestamp_high = 10000;
epb_fields->timestamp_low = 2000;
if (copy_data_buffer)
pcap_ng_block_packet_copy_data(block, data, len);
else
pcap_ng_block_packet_set_data(block, data, len);
if (proc_name != NULL) {
pcap_ng_block_add_option_with_value(block, PCAPNG_EPB_PIB_INDEX, &proc_index, sizeof(proc_index));
}
if (!first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
write_block(block);
break;
}
default:
break;
}
}
return (block);
}
void
make_section_header_block()
{
pcapng_block_t block = pcap_ng_block_alloc(65536);
pcap_ng_block_reset(block, PCAPNG_BT_SHB);
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT,
"section header block");
write_block(block);
pcap_ng_free_block(block);
return;
}
pcapng_block_t
make_name_resolution_record(pcapng_block_t block, int af, void *addr, char **names)
{
pcap_ng_block_reset(block, PCAPNG_BT_NRB);
if (first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
if (af == AF_INET) {
pcap_ng_block_add_name_record_with_ip4(block, (struct in_addr *)addr, (const char **)names);
} else {
pcap_ng_block_add_name_record_with_ip6(block, (struct in6_addr *)addr, (const char **)names);
}
if (!first_comment && comment)
pcap_ng_block_add_option_with_string(block, PCAPNG_OPT_COMMENT, comment);
write_block(block);
return (block);
}
int
main(int argc, char * const argv[])
{
int ch;
const char *file_name = NULL;
pcapng_block_t block;
while ((ch = getopt(argc, argv, "4:6:Cc:D:d:efhi:n:o:p:sw:x")) != -1) {
switch (ch) {
case 'C':
copy_data_buffer = 1;
break;
case 'c':
comment = optarg;
break;
case 'D': {
int i;
packet_length = strtoul(optarg, NULL, 0);
packet_data = malloc(packet_length);
for (i = 0; i < packet_length; i++) {
packet_data[i] = i % 256;
}
block = pcap_ng_block_alloc(65536);
make_data_block(block, packet_data, packet_length);
pcap_ng_free_block(block);
break;
}
case 'd':
packet_length = strlen(optarg);
packet_data = (unsigned char *)optarg;
block = pcap_ng_block_alloc(65536);
make_data_block(block, packet_data, packet_length);
pcap_ng_free_block(block);
break;
case 'e':
type_of_packet = ENHANCED_PACKET;
break;
case 'f':
first_comment = 1;
break;
case 'h':
help(argv[0]);
return (0);
case 'i':
if_name = optarg;
block = pcap_ng_block_alloc(65536);
make_interface_description_block(block, if_name);
pcap_ng_free_block(block);
break;
case '4':
case '6': {
int af;
int name_count = 0;
char *ptr = strchr(optarg, ':');
char **names = NULL;
int i;
struct in_addr ina;
struct in6_addr in6a;
int retval;
if (ptr == NULL) {
fprintf(stderr, "malformed arguments for option 'n'\n");
help(argv[0]);
return (0);
}
do {
ptr++;
name_count += 1;
} while ((ptr = strchr(ptr, ':')) != NULL);
names = calloc(name_count +1, sizeof(char *));
ptr = strchr(optarg, ':');
*ptr = '\0';
if (ch == '4') {
af = AF_INET;
retval = inet_pton(af, optarg, &ina);
} else {
af = AF_INET6;
retval = inet_pton(af, optarg, &in6a);
}
if (retval == 0)
errx(1, "This is not an %s address: '%s'\n",
af == AF_INET ? "IPv4" : "IPv6",
optarg);
else if (retval == -1)
err(1, "inet_pton(%s) failed\n", optarg);
for (i = 0; i < name_count; i++) {
char *end;
ptr++;
end = strchr(ptr, ':');
if (end != NULL)
*end = '\0';
names[i] = strdup(ptr);
if (end != NULL)
ptr = end;
}
block = pcap_ng_block_alloc(65536);
if (af == AF_INET)
make_name_resolution_record(block, af, &ina, names);
else
make_name_resolution_record(block, af, &in6a, names);
pcap_ng_free_block(block);
break;
}
case 'n': {
num_data_blocks = strtoul(optarg, NULL, 0);
break;
}
case 'o':
type_of_packet = OBSOLETE_PACKET;
break;
case 'p': {
char *ptr = strchr(optarg, ':');
char *endptr;
if (ptr == NULL) {
fprintf(stderr, "malformed arguments for option 'p'\n");
help(argv[0]);
return (0);
}
proc_name = strndup(optarg, (ptr - optarg));
ptr += 1;
if (*ptr == '\0') {
fprintf(stderr, "malformed arguments for option 'p'\n");
help(argv[0]);
return (0);
}
proc_pid = (uint32_t)strtoul(ptr, &endptr, 0);
if (endptr != NULL && *endptr != '\0') {
fprintf(stderr, "malformed arguments for option 'p'\n");
help(argv[0]);
return (0);
}
proc_index += 1;
block = pcap_ng_block_alloc(65536);
make_process_information_block(block, proc_name, proc_pid);
pcap_ng_free_block(block);
break;
}
case 's':
type_of_packet = SIMPLE_PACKET;
break;
case 'w':
file_name = optarg;
if (dumper != NULL)
pcap_ng_dump_close(dumper);
pcap_t *pcap = pcap_open_dead(DLT_PCAPNG, 65536);
if (pcap == NULL)
err(EX_OSERR, "pcap_open_dead(DLT_PCAPNG, 65536) failed\n");
dumper = pcap_ng_dump_open(pcap, file_name);
if (pcap == NULL)
err(EX_OSERR, "pcap_ng_dump_open(%s) failed\n", file_name);
make_section_header_block();
break;
case 'x':
if (optind < argc) {
if (argv[optind][0] != '-') {
char *endptr;
unsigned long num = strtoul(argv[optind], &endptr, 0);
if (endptr != NULL && *endptr == '\0') {
optind++;
ext_len = num;
}
}
}
ext_buffer = malloc(ext_len);
if (ext_buffer == NULL)
errx(EX_OSERR, "malloc(%lu) failed", ext_len);
break;
default:
help(argv[0]);
return (0);
}
}
if (dumper != NULL)
pcap_ng_dump_close(dumper);
return (0);
}