/*- * Copyright (c) 2017 Michael Tuexen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* * Compile: cc -Wall -Werror -pedantic pcap2corpus.c -lpcap -o pcap2corpus * * Usage: pcap2corpus infile outfile_prefix [expression] * if no expression, a pcap filter, is provided, sctp is used. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static unsigned long nr_read = 0; static unsigned long nr_decaps = 0; #define PRE_PADDING 1 struct args { struct bpf_program bpf_prog; char *filename_prefix; int (*is_ipv4)(const void *); int (*is_ipv6)(const void *); int linktype; unsigned int offset; }; /* * SCTP protocol - RFC4960. */ struct sctphdr { uint16_t src_port; /* source port */ uint16_t dest_port; /* destination port */ uint32_t v_tag; /* verification tag of packet */ uint32_t checksum; /* CRC32C checksum */ /* chunks follow... */ } __attribute__((packed)); static int loopback_is_ipv4(const void *bytes) { uint32_t family; family = *(const uint32_t *)bytes; return (family == 2); } static int loopback_is_ipv6(const void *bytes) { uint32_t family; family = *(const uint32_t *)bytes; return (family == 24 || family == 28 || family == 30); } static int ethernet_is_ipv4(const void *bytes) { const struct ether_header *ether_hdr; ether_hdr = (const struct ether_header *)bytes; return (ntohs(ether_hdr->ether_type) == ETHERTYPE_IP); } static int ethernet_is_ipv6(const void *bytes) { const struct ether_header *ether_hdr; ether_hdr = (const struct ether_header *)bytes; return (ntohs(ether_hdr->ether_type) == ETHERTYPE_IPV6); } static void packet_handler(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *bytes_in) { struct args *args; const u_char *bytes_out; FILE *file; char *filename; const struct ip *ip4_hdr_in; const struct ip6_hdr *ip6_hdr_in; size_t offset, length; int null = 0; args = (struct args *)(void *)user; bytes_out = NULL; if (pcap_offline_filter(&args->bpf_prog, pkthdr, bytes_in) == 0) { goto out; } if (pkthdr->caplen < args->offset) { goto out; } if (args->is_ipv4(bytes_in)) { offset = args->offset + sizeof(struct ip) + sizeof(struct sctphdr); if (pkthdr->caplen < offset) { goto out; } ip4_hdr_in = (const struct ip *)(const void *)(bytes_in + args->offset); if (ip4_hdr_in->ip_p == IPPROTO_SCTP) { unsigned int ip4_hdr_len; ip4_hdr_len = ip4_hdr_in->ip_hl << 2; offset = args->offset + ip4_hdr_len + sizeof(struct sctphdr); if (pkthdr->caplen < offset) { goto out; } bytes_out = bytes_in + offset; length = pkthdr->caplen - offset; } } if (args->is_ipv6(bytes_in)) { offset = args->offset + sizeof(struct ip6_hdr) + sizeof(struct sctphdr); if (pkthdr->caplen < offset) { goto out; } ip6_hdr_in = (const struct ip6_hdr *)(bytes_in + args->offset); if (ip6_hdr_in->ip6_nxt == IPPROTO_SCTP) { bytes_out = bytes_in + offset; length = pkthdr->caplen - offset; } } out: nr_read++; if (bytes_out != NULL) { asprintf(&filename, "%s-%06lu", args->filename_prefix, nr_decaps); file = fopen(filename, "w"); fwrite(&null, 1, PRE_PADDING, file); fwrite(bytes_out, length, 1, file); fclose(file); free(filename); nr_decaps++; } } static char * get_filter(int argc, char *argv[]) { char *result, *c; size_t len; int i; if (argc == 3) { asprintf(&result, "%s", "sctp"); } else { len = 0; for (i = 3; i < argc; i++) { len += strlen(argv[i]) + 1; } len -= 1; result = malloc(len); c = result; for (i = 3; i < argc; i++) { size_t arg_len; arg_len = strlen(argv[i]); memcpy(c, argv[i], arg_len); c += arg_len; if (i < argc - 1) { *c++ = ' '; } } } return (result); } int main(int argc, char *argv[]) { char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *pcap_reader; char *filter; struct args args; if (argc < 3) { fprintf(stderr, "Usage: %s infile outfile_prefix [expression]\n", argv[0]); return (-1); } args.filename_prefix = argv[2]; pcap_reader = pcap_open_offline(argv[1], errbuf); if (pcap_reader == NULL) { fprintf(stderr, "Can't open input file %s: %s\n", argv[1], errbuf); return (-1); } args.linktype = pcap_datalink(pcap_reader); switch (args.linktype) { case DLT_NULL: args.is_ipv4 = loopback_is_ipv4; args.is_ipv6 = loopback_is_ipv6; args.offset = sizeof(uint32_t); break; case DLT_EN10MB: args.is_ipv4 = ethernet_is_ipv4; args.is_ipv6 = ethernet_is_ipv6; args.offset = sizeof(struct ether_header); break; default: fprintf(stderr, "Datalink type %d not supported\n", args.linktype); pcap_close(pcap_reader); return (-1); } filter = get_filter(argc, argv); if (pcap_compile(pcap_reader, &args.bpf_prog, filter, 0, PCAP_NETMASK_UNKNOWN) < 0) { fprintf(stderr, "Can't compile filter %s: %s\n", filter, pcap_geterr(pcap_reader)); free(filter); pcap_close(pcap_reader); return (-1); } free(filter); pcap_dispatch(pcap_reader, 0, packet_handler, (u_char *)&args); pcap_close(pcap_reader); fprintf(stderr, "%lu packets processed\n", nr_read); fprintf(stderr, "%lu packets decapsulated\n", nr_decaps); return (0); }