/* * Author Jerry Lundström * Copyright (c) 2016-2017, OARC, Inc. * 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. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 * COPYRIGHT HOLDER 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. */ #include "config.h" #include "pcap_thread.h" #include #include #include #include #ifndef PCAP_THREAD_LAYER_TRACE #define PCAP_THREAD_LAYER_TRACE 0 #endif /* * Forward declares for layer callbacks */ static void pcap_thread_callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt, const char* name, int dlt); static void pcap_thread_callback_linux_sll(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_ether(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_null(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_loop(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_ieee802(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_gre(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_ip(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_ipv4(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_ipv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_icmp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_icmpv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_udp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); static void pcap_thread_callback_tcp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length); /* * Version */ static const char* _version = PCAP_THREAD_VERSION_STR; const char* pcap_thread_version_str(void) { return _version; } int pcap_thread_version_major(void) { return PCAP_THREAD_VERSION_MAJOR; } int pcap_thread_version_minor(void) { return PCAP_THREAD_VERSION_MINOR; } int pcap_thread_version_patch(void) { return PCAP_THREAD_VERSION_PATCH; } /* * Create/Free */ static pcap_thread_t _pcap_thread_defaults = PCAP_THREAD_T_INIT; pcap_thread_t* pcap_thread_create(void) { pcap_thread_t* pcap_thread = calloc(1, sizeof(pcap_thread_t)); if (pcap_thread) { memcpy(pcap_thread, &_pcap_thread_defaults, sizeof(pcap_thread_t)); } return pcap_thread; } void pcap_thread_free(pcap_thread_t* pcap_thread) { if (!pcap_thread) { return; } pcap_thread_close(pcap_thread); if (pcap_thread->filter) { free(pcap_thread->filter); } free(pcap_thread); } /* * Get/Set */ int pcap_thread_use_threads(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->use_threads; } int pcap_thread_set_use_threads(pcap_thread_t* pcap_thread, const int use_threads) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->use_threads = use_threads; return PCAP_THREAD_OK; } int pcap_thread_use_layers(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->use_layers; } int pcap_thread_set_use_layers(pcap_thread_t* pcap_thread, const int use_layers) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->use_layers = use_layers; return PCAP_THREAD_OK; } pcap_thread_queue_mode_t pcap_thread_queue_mode(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->queue_mode; } int pcap_thread_set_queue_mode(pcap_thread_t* pcap_thread, const pcap_thread_queue_mode_t queue_mode) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } switch (queue_mode) { case PCAP_THREAD_QUEUE_MODE_COND: case PCAP_THREAD_QUEUE_MODE_DIRECT: break; case PCAP_THREAD_QUEUE_MODE_YIELD: case PCAP_THREAD_QUEUE_MODE_WAIT: case PCAP_THREAD_QUEUE_MODE_DROP: return PCAP_THREAD_EOBSOLETE; default: return PCAP_THREAD_EINVAL; } pcap_thread->queue_mode = queue_mode; return PCAP_THREAD_OK; } struct timeval pcap_thread_queue_wait(const pcap_thread_t* pcap_thread) { static struct timeval tv = { 0, 0 }; return tv; } int pcap_thread_set_queue_wait(pcap_thread_t* pcap_thread, const struct timeval queue_wait) { return PCAP_THREAD_EOBSOLETE; } pcap_thread_queue_mode_t pcap_thread_callback_queue_mode(const pcap_thread_t* pcap_thread) { return PCAP_THREAD_EOBSOLETE; } int pcap_thread_set_callback_queue_mode(pcap_thread_t* pcap_thread, const pcap_thread_queue_mode_t callback_queue_mode) { return PCAP_THREAD_EOBSOLETE; } struct timeval pcap_thread_callback_queue_wait(const pcap_thread_t* pcap_thread) { static struct timeval tv = { 0, 0 }; return tv; } int pcap_thread_set_callback_queue_wait(pcap_thread_t* pcap_thread, const struct timeval callback_queue_wait) { return PCAP_THREAD_EOBSOLETE; } int pcap_thread_snapshot(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->snapshot; } int pcap_thread_snaplen(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->snaplen; } int pcap_thread_set_snaplen(pcap_thread_t* pcap_thread, const int snaplen) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->snaplen = snaplen; return PCAP_THREAD_OK; } int pcap_thread_promiscuous(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->promiscuous; } int pcap_thread_set_promiscuous(pcap_thread_t* pcap_thread, const int promiscuous) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->promiscuous = promiscuous; return PCAP_THREAD_OK; } int pcap_thread_monitor(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->monitor; } int pcap_thread_set_monitor(pcap_thread_t* pcap_thread, const int monitor) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->monitor = monitor; return PCAP_THREAD_OK; } int pcap_thread_timeout(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->timeout; } int pcap_thread_set_timeout(pcap_thread_t* pcap_thread, const int timeout) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->timeout = timeout; return PCAP_THREAD_OK; } int pcap_thread_buffer_size(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->buffer_size; } int pcap_thread_set_buffer_size(pcap_thread_t* pcap_thread, const int buffer_size) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->buffer_size = buffer_size; return PCAP_THREAD_OK; } int pcap_thread_timestamp_type(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->timestamp_type; } int pcap_thread_set_timestamp_type(pcap_thread_t* pcap_thread, const int timestamp_type) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->have_timestamp_type = 1; pcap_thread->timestamp_type = timestamp_type; return PCAP_THREAD_OK; } int pcap_thread_timestamp_precision(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->timestamp_precision; } int pcap_thread_set_timestamp_precision(pcap_thread_t* pcap_thread, const int timestamp_precision) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->have_timestamp_precision = 1; pcap_thread->timestamp_precision = timestamp_precision; return PCAP_THREAD_OK; } int pcap_thread_immediate_mode(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->immediate_mode; } int pcap_thread_set_immediate_mode(pcap_thread_t* pcap_thread, const int immediate_mode) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->immediate_mode = immediate_mode; return PCAP_THREAD_OK; } pcap_direction_t pcap_thread_direction(const pcap_thread_t* pcap_thread) { #ifdef HAVE_PCAP_DIRECTION_T if (!pcap_thread) { return -1; } return pcap_thread->direction; #else return 0; #endif } int pcap_thread_set_direction(pcap_thread_t* pcap_thread, const pcap_direction_t direction) { #ifdef HAVE_PCAP_DIRECTION_T if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->have_direction = 1; pcap_thread->direction = direction; return PCAP_THREAD_OK; #else return PCAP_THREAD_ENODIR; #endif } const char* pcap_thread_filter(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return 0; } return pcap_thread->filter; } int pcap_thread_set_filter(pcap_thread_t* pcap_thread, const char* filter, const size_t filter_len) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!filter) { return PCAP_THREAD_EINVAL; } if (!filter_len) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->filter) { free(pcap_thread->filter); } if (!(pcap_thread->filter = strndup(filter, filter_len))) { return PCAP_THREAD_ENOMEM; } pcap_thread->filter_len = filter_len; return PCAP_THREAD_OK; } int pcap_thread_clear_filter(pcap_thread_t* pcap_thread) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->filter) { free(pcap_thread->filter); pcap_thread->filter = 0; pcap_thread->filter_len = 0; } return PCAP_THREAD_OK; } int pcap_thread_filter_errno(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->filter_errno; } int pcap_thread_filter_optimize(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->filter_optimize; } int pcap_thread_set_filter_optimize(pcap_thread_t* pcap_thread, const int filter_optimize) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->filter_optimize = filter_optimize; return PCAP_THREAD_OK; } bpf_u_int32 pcap_thread_filter_netmask(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->filter_netmask; } int pcap_thread_set_filter_netmask(pcap_thread_t* pcap_thread, const bpf_u_int32 filter_netmask) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->filter_netmask = filter_netmask; return PCAP_THREAD_OK; } struct timeval pcap_thread_timedrun(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { static struct timeval tv = { 0, 0 }; return tv; } return pcap_thread->timedrun; } int pcap_thread_set_timedrun(pcap_thread_t* pcap_thread, const struct timeval timedrun) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->timedrun = timedrun; return PCAP_THREAD_OK; } struct timeval pcap_thread_timedrun_to(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { static struct timeval tv = { 0, 0 }; return tv; } return pcap_thread->timedrun_to; } int pcap_thread_set_timedrun_to(pcap_thread_t* pcap_thread, const struct timeval timedrun_to) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->timedrun_to = timedrun_to; return PCAP_THREAD_OK; } pcap_thread_activate_mode_t pcap_thread_activate_mode(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return PCAP_THREAD_DEFAULT_ACTIVATE_MODE; } return pcap_thread->activate_mode; } int pcap_thread_set_activate_mode(pcap_thread_t* pcap_thread, const pcap_thread_activate_mode_t activate_mode) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->activate_mode = activate_mode; return PCAP_THREAD_OK; } int pcap_thread_was_stopped(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } return pcap_thread->was_stopped; } /* * Queue */ size_t pcap_thread_queue_size(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return -1; } return pcap_thread->queue_size; } int pcap_thread_set_queue_size(pcap_thread_t* pcap_thread, const size_t queue_size) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!queue_size) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->queue_size = queue_size; return PCAP_THREAD_OK; } int pcap_thread_set_callback(pcap_thread_t* pcap_thread, pcap_thread_callback_t callback) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback = callback; return PCAP_THREAD_OK; } int pcap_thread_set_dropback(pcap_thread_t* pcap_thread, pcap_thread_callback_t dropback) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->dropback = dropback; return PCAP_THREAD_OK; } /* * Layers */ int pcap_thread_set_callback_linux_sll(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_linux_sll) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_linux_sll = callback_linux_sll; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ether(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ether) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ether = callback_ether; return PCAP_THREAD_OK; } int pcap_thread_set_callback_null(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_null) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_null = callback_null; return PCAP_THREAD_OK; } int pcap_thread_set_callback_loop(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_loop) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_loop = callback_loop; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ieee802(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ieee802) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ieee802 = callback_ieee802; return PCAP_THREAD_OK; } int pcap_thread_set_callback_gre(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_gre) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_gre = callback_gre; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ip(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ip) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ip = callback_ip; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ipv4(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ipv4) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ipv4 = callback_ipv4; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ipv4_frag(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_frag_t callback_ipv4_frag) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!callback_ipv4_frag.new || !callback_ipv4_frag.free || !callback_ipv4_frag.reassemble || !callback_ipv4_frag.release) { if (callback_ipv4_frag.new || callback_ipv4_frag.free || callback_ipv4_frag.reassemble || callback_ipv4_frag.release) { return PCAP_THREAD_EINVAL; } } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ipv4_frag = callback_ipv4_frag; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ipv6(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_ipv6) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ipv6 = callback_ipv6; return PCAP_THREAD_OK; } int pcap_thread_set_callback_ipv6_frag(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_frag_t callback_ipv6_frag) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!callback_ipv6_frag.new || !callback_ipv6_frag.free || !callback_ipv6_frag.reassemble || !callback_ipv6_frag.release) { if (callback_ipv6_frag.new || callback_ipv6_frag.free || callback_ipv6_frag.reassemble || callback_ipv6_frag.release) { return PCAP_THREAD_EINVAL; } } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_ipv6_frag = callback_ipv6_frag; return PCAP_THREAD_OK; } int pcap_thread_set_callback_icmp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_icmp) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_icmp = callback_icmp; return PCAP_THREAD_OK; } int pcap_thread_set_callback_icmpv6(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_icmpv6) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_icmpv6 = callback_icmpv6; return PCAP_THREAD_OK; } int pcap_thread_set_callback_udp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_udp) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_udp = callback_udp; return PCAP_THREAD_OK; } int pcap_thread_set_callback_tcp(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_tcp) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6) { return PCAP_THREAD_ELAYERCB; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_tcp = callback_tcp; return PCAP_THREAD_OK; } int pcap_thread_set_callback_invalid(pcap_thread_t* pcap_thread, pcap_thread_layer_callback_t callback_invalid) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } pcap_thread->callback_invalid = callback_invalid; return PCAP_THREAD_OK; } #define need4x2(v1, v2, p, l) \ if (l < 1) { \ break; \ } \ v1 = (*p) >> 4; \ v2 = (*p) & 0xf; \ p += 1; \ l -= 1 #define need8(v, p, l) \ if (l < 1) { \ break; \ } \ v = *p; \ p += 1; \ l -= 1 #define need16(v, p, l) \ if (l < 2) { \ break; \ } \ v = (*p << 8) + *(p + 1); \ p += 2; \ l -= 2 #define need32(v, p, l) \ if (l < 4) { \ break; \ } \ v = (*p << 24) + (*(p + 1) << 16) + (*(p + 2) << 8) + *(p + 3); \ p += 4; \ l -= 4 #define needxb(b, x, p, l) \ if (l < x) { \ break; \ } \ memcpy(b, p, x); \ p += x; \ l -= x #define advancexb(x, p, l) \ if (l < x) { \ break; \ } \ p += x; \ l -= x #if PCAP_THREAD_LAYER_TRACE #define layer_trace(msg) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__) #define layer_tracef(msg, args...) printf("LT %s:%d: " msg "\n", __FILE__, __LINE__, args) #else #define layer_trace(msg) #define layer_tracef(msg, args...) #endif static void pcap_thread_callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt, const char* name, int dlt) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; size_t length; pcap_thread_packet_t packet; const u_char* orig = pkt; size_t origlength; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!pkthdr) { return; } if (!pkt) { return; } if (!name) { return; } memset(&packet, 0, sizeof(packet)); packet.name = name; packet.dlt = dlt; packet.pkthdr = *pkthdr; packet.have_pkthdr = 1; length = pkthdr->caplen; origlength = length; layer_tracef("packet, length %lu", length); switch (dlt) { case DLT_NULL: layer_trace("dlt_null"); { uint8_t hdr[4]; packet.state = PCAP_THREAD_PACKET_INVALID_NULL; need8(hdr[0], pkt, length); need8(hdr[1], pkt, length); need8(hdr[2], pkt, length); need8(hdr[3], pkt, length); packet.state = PCAP_THREAD_PACKET_OK; /* * The header for null is in host byte order but may not be * in the same endian as host if coming from a savefile */ if (pcaplist->is_offline && pcap_is_swapped(pcaplist->pcap)) { #if __BYTE_ORDER == __LITTLE_ENDIAN packet.nullhdr.family = hdr[3] + (hdr[2] << 8) + (hdr[1] << 16) + (hdr[0] << 24); #elif __BYTE_ORDER == __BIG_ENDIAN packet.nullhdr.family = hdr[0] + (hdr[1] << 8) + (hdr[2] << 16) + (hdr[3] << 24); #else #error "Please fix " #endif } else { #if __BYTE_ORDER == __LITTLE_ENDIAN packet.nullhdr.family = hdr[0] + (hdr[1] << 8) + (hdr[2] << 16) + (hdr[3] << 24); #elif __BYTE_ORDER == __BIG_ENDIAN packet.nullhdr.family = hdr[3] + (hdr[2] << 8) + (hdr[1] << 16) + (hdr[0] << 24); #else #error "Please fix " #endif } packet.have_nullhdr = 1; if (pcaplist->pcap_thread->callback_null) pcaplist->pcap_thread->callback_null(pcaplist->user, &packet, pkt, length); else pcap_thread_callback_null((void*)pcaplist, &packet, pkt, length); return; } break; case DLT_EN10MB: layer_trace("dlt_en10mb"); packet.state = PCAP_THREAD_PACKET_INVALID_ETHER; needxb(packet.ethhdr.ether_dhost, sizeof(packet.ethhdr.ether_dhost), pkt, length); needxb(packet.ethhdr.ether_shost, sizeof(packet.ethhdr.ether_shost), pkt, length); need16(packet.ethhdr.ether_type, pkt, length); packet.state = PCAP_THREAD_PACKET_OK; packet.have_ethhdr = 1; if (pcaplist->pcap_thread->callback_ether) pcaplist->pcap_thread->callback_ether(pcaplist->user, &packet, pkt, length); else pcap_thread_callback_ether((void*)pcaplist, &packet, pkt, length); return; case DLT_LOOP: layer_trace("dlt_loop"); packet.state = PCAP_THREAD_PACKET_INVALID_LOOP; need32(packet.loophdr.family, pkt, length); packet.state = PCAP_THREAD_PACKET_OK; packet.have_loophdr = 1; if (pcaplist->pcap_thread->callback_loop) pcaplist->pcap_thread->callback_loop(pcaplist->user, &packet, pkt, length); else pcap_thread_callback_loop((void*)pcaplist, &packet, pkt, length); return; case DLT_RAW: #ifdef DLT_IPV4 case DLT_IPV4: #endif #ifdef DLT_IPV6 case DLT_IPV6: #endif layer_trace("dlt_raw/ipv4/ipv6"); if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, &packet, pkt, length); else pcap_thread_callback_ip((void*)pcaplist, &packet, pkt, length); return; case DLT_LINUX_SLL: layer_trace("dlt_linux_sll"); packet.state = PCAP_THREAD_PACKET_INVALID_LINUX_SLL; need16(packet.linux_sll.packet_type, pkt, length); need16(packet.linux_sll.arp_hardware, pkt, length); need16(packet.linux_sll.link_layer_address_length, pkt, length); needxb(packet.linux_sll.link_layer_address, 8, pkt, length); need16(packet.linux_sll.ether_type, pkt, length); packet.state = PCAP_THREAD_PACKET_OK; packet.have_linux_sll = 1; if (pcaplist->pcap_thread->callback_linux_sll) pcaplist->pcap_thread->callback_linux_sll(pcaplist->user, &packet, pkt, length); else pcap_thread_callback_linux_sll((void*)pcaplist, &packet, pkt, length); return; /* TODO: These might be interesting to implement case DLT_IPNET: case DLT_PKTAP: */ default: packet.state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } if (pcaplist->pcap_thread->callback_invalid) { if (packet.state == PCAP_THREAD_PACKET_OK) packet.state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, &packet, orig, origlength); } } static void pcap_thread_callback_linux_sll(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_linux_sll) { layer_trace("have_linux_sll"); switch (packet->linux_sll.ether_type) { case 0x8100: /* 802.1q */ case 0x88a8: /* 802.1ad */ case 0x9100: /* 802.1 QinQ non-standard */ if (packet->have_ieee802hdr) break; { uint16_t tci; packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802; need16(tci, payload, length); packet->ieee802hdr.pcp = (tci & 0xe000) >> 13; packet->ieee802hdr.dei = (tci & 0x1000) >> 12; packet->ieee802hdr.vid = tci & 0x0fff; need16(packet->ieee802hdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_ieee802hdr = 1; } if (pcaplist->pcap_thread->callback_ieee802) pcaplist->pcap_thread->callback_ieee802(pcaplist->user, packet, payload, length); else pcap_thread_callback_ieee802((void*)pcaplist, packet, payload, length); return; case ETHERTYPE_IP: case ETHERTYPE_IPV6: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length); else pcap_thread_callback_ip((void*)pcaplist, packet, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_ether(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_ethhdr) { layer_trace("have_ethhdr"); switch (packet->ethhdr.ether_type) { case 0x8100: /* 802.1q */ case 0x88a8: /* 802.1ad */ case 0x9100: /* 802.1 QinQ non-standard */ if (packet->have_ieee802hdr) break; { uint16_t tci; packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802; need16(tci, payload, length); packet->ieee802hdr.pcp = (tci & 0xe000) >> 13; packet->ieee802hdr.dei = (tci & 0x1000) >> 12; packet->ieee802hdr.vid = tci & 0x0fff; need16(packet->ieee802hdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_ieee802hdr = 1; } if (pcaplist->pcap_thread->callback_ieee802) pcaplist->pcap_thread->callback_ieee802(pcaplist->user, packet, payload, length); else pcap_thread_callback_ieee802((void*)pcaplist, packet, payload, length); return; case ETHERTYPE_IP: case ETHERTYPE_IPV6: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length); else pcap_thread_callback_ip((void*)pcaplist, packet, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_null(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_nullhdr) { layer_trace("have_nullhdr"); /* From libpcap link types documentation: * containing a value of 2 for IPv4 packets, a value of either 24, 28, * or 30 for IPv6 packets, a value of 7 for OSI packets, or a value of 23 * for IPX packets. All of the IPv6 values correspond to IPv6 packets; * code reading files should check for all of them. */ switch (packet->nullhdr.family) { case 2: case 24: case 28: case 30: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length); else pcap_thread_callback_ip((void*)pcaplist, packet, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_loop(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_loophdr) { layer_trace("have_loophdr"); /* From libpcap link types documentation: * containing a value of 2 for IPv4 packets, a value of either 24, 28, * or 30 for IPv6 packets, a value of 7 for OSI packets, or a value of 23 * for IPX packets. All of the IPv6 values correspond to IPv6 packets; * code reading files should check for all of them. */ switch (packet->loophdr.family) { case 2: case 24: case 28: case 30: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length); else pcap_thread_callback_ip((void*)pcaplist, packet, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_ieee802(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_ieee802hdr) { layer_trace("have_ieee802hdr"); switch (packet->ieee802hdr.ether_type) { case 0x88a8: /* 802.1ad */ case 0x9100: /* 802.1 QinQ non-standard */ { pcap_thread_packet_t ieee802pkt; uint16_t tci; memset(&ieee802pkt, 0, sizeof(ieee802pkt)); ieee802pkt.prevpkt = packet; ieee802pkt.have_prevpkt = 1; packet->state = PCAP_THREAD_PACKET_INVALID_IEEE802; need16(tci, payload, length); ieee802pkt.ieee802hdr.pcp = (tci & 0xe000) >> 13; ieee802pkt.ieee802hdr.dei = (tci & 0x1000) >> 12; ieee802pkt.ieee802hdr.vid = tci & 0x0fff; need16(ieee802pkt.ieee802hdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_OK; ieee802pkt.have_ieee802hdr = 1; if (pcaplist->pcap_thread->callback_ieee802) pcaplist->pcap_thread->callback_ieee802(pcaplist->user, &ieee802pkt, payload, length); else pcap_thread_callback_ieee802((void*)pcaplist, &ieee802pkt, payload, length); return; } case ETHERTYPE_IP: case ETHERTYPE_IPV6: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, packet, payload, length); else pcap_thread_callback_ip((void*)pcaplist, packet, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_gre(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_grehdr) { pcap_thread_packet_t grepkt; layer_trace("have_grehdr"); memset(&grepkt, 0, sizeof(grepkt)); grepkt.prevpkt = packet; grepkt.have_prevpkt = 1; for (;;) { packet->state = PCAP_THREAD_PACKET_INVALID_GRE; if (packet->grehdr.gre_flags & 0x1) { need16(packet->gre.checksum, payload, length); } if (packet->grehdr.gre_flags & 0x4) { need16(packet->gre.key, payload, length); } if (packet->grehdr.gre_flags & 0x8) { need16(packet->gre.sequence, payload, length); } packet->state = PCAP_THREAD_PACKET_OK; packet->have_gre = 1; switch (packet->grehdr.ether_type) { case ETHERTYPE_IP: case ETHERTYPE_IPV6: if (pcaplist->pcap_thread->callback_ip) pcaplist->pcap_thread->callback_ip(pcaplist->user, &grepkt, payload, length); else pcap_thread_callback_ip((void*)pcaplist, &grepkt, payload, length); return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_ip(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (!packet->have_iphdr && !packet->have_ip6hdr) { layer_trace("checking for ip"); for (;;) { packet->state = PCAP_THREAD_PACKET_INVALID_IP; need4x2(packet->iphdr.ip_v, packet->iphdr.ip_hl, payload, length); if (packet->iphdr.ip_v == 4) { packet->state = PCAP_THREAD_PACKET_INVALID_IPV4; need8(packet->iphdr.ip_tos, payload, length); need16(packet->iphdr.ip_len, payload, length); need16(packet->iphdr.ip_id, payload, length); need16(packet->iphdr.ip_off, payload, length); need8(packet->iphdr.ip_ttl, payload, length); need8(packet->iphdr.ip_p, payload, length); need16(packet->iphdr.ip_sum, payload, length); needxb(&(packet->iphdr.ip_src.s_addr), 4, payload, length); needxb(&(packet->iphdr.ip_dst.s_addr), 4, payload, length); /* TODO: IPv4 options */ if (packet->iphdr.ip_hl < 5) break; if (packet->iphdr.ip_hl > 5) { advancexb((packet->iphdr.ip_hl - 5) * 4, payload, length); } packet->state = PCAP_THREAD_PACKET_OK; packet->have_iphdr = 1; if (pcaplist->pcap_thread->callback_ipv4) pcaplist->pcap_thread->callback_ipv4(pcaplist->user, packet, payload, length); else pcap_thread_callback_ipv4((void*)pcaplist, packet, payload, length); return; } else if (packet->iphdr.ip_v == 6) { /* * Clear IPv4 headers and reverse reading one byte */ packet->iphdr.ip_v = 0; packet->iphdr.ip_hl = 0; payload--; length++; packet->state = PCAP_THREAD_PACKET_INVALID_IPV6; need32(packet->ip6hdr.ip6_flow, payload, length); need16(packet->ip6hdr.ip6_plen, payload, length); need8(packet->ip6hdr.ip6_nxt, payload, length); need8(packet->ip6hdr.ip6_hlim, payload, length); needxb(&(packet->ip6hdr.ip6_src), 16, payload, length); needxb(&(packet->ip6hdr.ip6_dst), 16, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_ip6hdr = 1; if (pcaplist->pcap_thread->callback_ipv6) pcaplist->pcap_thread->callback_ipv6(pcaplist->user, packet, payload, length); else pcap_thread_callback_ipv6((void*)pcaplist, packet, payload, length); return; } packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_ipv4(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; int release_frag = 0; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_iphdr) { layer_trace("have_iphdr"); for (;;) { /* Check reported length for missing payload or padding */ if (packet->iphdr.ip_len < (packet->iphdr.ip_hl * 4)) { layer_trace("ip_len < ip header"); packet->state = PCAP_THREAD_PACKET_INVALID_IPV4; break; } if (length < (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4))) { layer_trace("length < (ip_len - ip header)"); packet->state = PCAP_THREAD_PACKET_INVALID_IPV4; break; } if (length > (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4))) { layer_trace("have_ippadding"); packet->ippadding = length - (packet->iphdr.ip_len - (packet->iphdr.ip_hl * 4)); packet->have_ippadding = 1; length -= packet->ippadding; } /* Check if packet wants more fragments or has an offset */ if (packet->iphdr.ip_off & 0x2000 || packet->iphdr.ip_off & 0x1fff) { layer_trace("is_v4_frag"); if (pcaplist->pcap_thread->callback_ipv4_frag.reassemble) { pcap_thread_packet_t* whole_packet = 0; const u_char* whole_payload = 0; size_t whole_length = 0; packet->state = pcaplist->pcap_thread->callback_ipv4_frag.reassemble(pcaplist->ipv4_frag_ctx, packet, payload, length, &whole_packet, &whole_payload, &whole_length); /* Defragmentation failed some how, we return packet as invalid */ if (packet->state != PCAP_THREAD_PACKET_OK) { break; } /* No whole/defragmented packet return, need more */ if (!whole_packet || !whole_payload || !whole_length) { return; } layer_tracef("v4_reasm %p %p %lu", whole_packet, whole_payload, whole_length); packet = whole_packet; payload = whole_payload; length = whole_length; release_frag = 1; } else { /* * Mark packet as fragment and send it to the next user * layer (if any) or return it as invalid. */ packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; switch (packet->iphdr.ip_p) { case IPPROTO_GRE: layer_trace("ipproto_gre frag"); if (!(packet->iphdr.ip_off & 0x1fff)) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_GREHDR; need16(packet->grehdr.gre_flags, payload, length); need16(packet->grehdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_grehdr = 1; break; } } if (pcaplist->pcap_thread->callback_gre) { pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_ICMP: layer_trace("ipproto_icmp frag"); if (!(packet->iphdr.ip_off & 0x1fff)) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_ICMPHDR; need8(packet->icmphdr.type, payload, length); need8(packet->icmphdr.code, payload, length); need16(packet->icmphdr.checksum, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_icmphdr = 1; break; } } if (pcaplist->pcap_thread->callback_icmp) { pcaplist->pcap_thread->callback_icmp(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_UDP: layer_trace("ipproto_udp frag"); if (!(packet->iphdr.ip_off & 0x1fff)) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_UDPHDR; need16(packet->udphdr.uh_sport, payload, length); need16(packet->udphdr.uh_dport, payload, length); need16(packet->udphdr.uh_ulen, payload, length); need16(packet->udphdr.uh_sum, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_udphdr = 1; break; } } if (pcaplist->pcap_thread->callback_udp) { pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_TCP: layer_trace("ipproto_tcp frag"); if (!(packet->iphdr.ip_off & 0x1fff)) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_TCPHDR; need16(packet->tcphdr.th_sport, payload, length); need16(packet->tcphdr.th_dport, payload, length); need32(packet->tcphdr.th_seq, payload, length); need32(packet->tcphdr.th_ack, payload, length); need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length); need8(packet->tcphdr.th_flags, payload, length); need16(packet->tcphdr.th_win, payload, length); need16(packet->tcphdr.th_sum, payload, length); need16(packet->tcphdr.th_urp, payload, length); if (packet->tcphdr.th_off > 5) { packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4; needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length); packet->have_tcpopts = 1; } packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_tcphdr = 1; break; } } if (pcaplist->pcap_thread->callback_tcp) { pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length); return; } break; default: break; } break; } } switch (packet->iphdr.ip_p) { case IPPROTO_GRE: layer_trace("ipproto_gre"); if (packet->have_grehdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_GRE; need16(packet->grehdr.gre_flags, payload, length); need16(packet->grehdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_grehdr = 1; if (pcaplist->pcap_thread->callback_gre) pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length); else pcap_thread_callback_gre((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length); } return; case IPPROTO_ICMP: layer_trace("ipproto_icmp"); if (packet->have_icmphdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_ICMP; need8(packet->icmphdr.type, payload, length); need8(packet->icmphdr.code, payload, length); need16(packet->icmphdr.checksum, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_icmphdr = 1; if (pcaplist->pcap_thread->callback_icmp) pcaplist->pcap_thread->callback_icmp(pcaplist->user, packet, payload, length); else pcap_thread_callback_icmp((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length); } return; case IPPROTO_UDP: layer_trace("ipproto_udp"); if (packet->have_udphdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_UDP; need16(packet->udphdr.uh_sport, payload, length); need16(packet->udphdr.uh_dport, payload, length); need16(packet->udphdr.uh_ulen, payload, length); need16(packet->udphdr.uh_sum, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_udphdr = 1; if (pcaplist->pcap_thread->callback_udp) pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length); else pcap_thread_callback_udp((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length); } return; case IPPROTO_TCP: layer_trace("ipproto_tcp"); if (packet->have_tcphdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_TCP; need16(packet->tcphdr.th_sport, payload, length); need16(packet->tcphdr.th_dport, payload, length); need32(packet->tcphdr.th_seq, payload, length); need32(packet->tcphdr.th_ack, payload, length); need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length); need8(packet->tcphdr.th_flags, payload, length); need16(packet->tcphdr.th_win, payload, length); need16(packet->tcphdr.th_sum, payload, length); need16(packet->tcphdr.th_urp, payload, length); if (packet->tcphdr.th_off > 5) { packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4; needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length); packet->have_tcpopts = 1; } packet->state = PCAP_THREAD_PACKET_OK; packet->have_tcphdr = 1; if (pcaplist->pcap_thread->callback_tcp) pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length); else pcap_thread_callback_tcp((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length); } return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; if (release_frag) pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, payload, length); else pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } if (release_frag) { pcaplist->pcap_thread->callback_ipv4_frag.release(pcaplist->ipv4_frag_ctx, packet, payload, length); } } static void pcap_thread_callback_ipv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; int release_frag = 0; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } if (packet->have_ip6hdr) { struct ip6_ext ext; size_t already_advanced = 0; layer_trace("have_ip6hdr"); /* Check reported length for missing payload or padding */ if (length < packet->ip6hdr.ip6_plen) { layer_trace("length < ip6_plen"); packet->state = PCAP_THREAD_PACKET_INVALID_IPV6; if (pcaplist->pcap_thread->callback_invalid) { pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } return; } if (length > packet->ip6hdr.ip6_plen) { layer_trace("have_ip6padding"); packet->ip6padding = length - packet->ip6hdr.ip6_plen; packet->have_ip6padding = 1; length -= packet->ip6padding; } ext.ip6e_nxt = packet->ip6hdr.ip6_nxt; ext.ip6e_len = 0; while (ext.ip6e_nxt != IPPROTO_NONE && ext.ip6e_nxt != IPPROTO_GRE && ext.ip6e_nxt != IPPROTO_ICMPV6 && ext.ip6e_nxt != IPPROTO_UDP && ext.ip6e_nxt != IPPROTO_TCP) { packet->state = PCAP_THREAD_PACKET_INVALID_IPV6HDR; /* * Advance to the start of next header, this may not be needed * if it's the first header or if the header is supported. */ if (ext.ip6e_len) { if (ext.ip6e_len < already_advanced) { /* Header length is invalid */ layer_trace("ip6hdr invalid"); break; } /* Advance if not already there */ else if (ext.ip6e_len > already_advanced) { advancexb((ext.ip6e_len - already_advanced) * 8, payload, length); } already_advanced = 0; } else if (already_advanced) { /* Already advanced but header has no length */ layer_trace("ip6hdr already advanced"); break; } /* TODO: Store IPv6 headers? */ /* Handle supported headers */ if (ext.ip6e_nxt == IPPROTO_FRAGMENT) { if (packet->have_ip6frag) { layer_trace("dup ip6frag"); break; } layer_trace("ip6frag"); need8(ext.ip6e_nxt, payload, length); need8(packet->ip6frag.ip6f_reserved, payload, length); need16(packet->ip6frag.ip6f_offlg, payload, length); need32(packet->ip6frag.ip6f_ident, payload, length); packet->have_ip6frag = 1; ext.ip6e_len = 1; already_advanced = 1; } else if (ext.ip6e_nxt == IPPROTO_ROUTING) { struct ip6_rthdr rthdr; struct in6_addr rt[255]; if (packet->have_ip6rtdst) { layer_trace("dup ip6rtdst"); break; } need8(ext.ip6e_nxt, payload, length); need8(ext.ip6e_len, payload, length); need8(rthdr.ip6r_type, payload, length); need8(rthdr.ip6r_segleft, payload, length); if (!rthdr.ip6r_type) { if (rthdr.ip6r_segleft > ext.ip6e_len) break; for (rthdr.ip6r_len = 0; rthdr.ip6r_len < ext.ip6e_len; rthdr.ip6r_len++, already_advanced += 2) { needxb(&rt[rthdr.ip6r_len], 16, payload, length); } if (!rthdr.ip6r_len || rthdr.ip6r_len != ext.ip6e_len) { break; } if (rthdr.ip6r_segleft) { packet->ip6rtdst = rt[rthdr.ip6r_segleft]; packet->have_ip6rtdst = 1; } } } else { /* Nonsupported header */ layer_trace("ip6hdr?"); need8(ext.ip6e_nxt, payload, length); need8(ext.ip6e_len, payload, length); } packet->state = PCAP_THREAD_PACKET_OK; if (!ext.ip6e_len) break; } for (; packet->state == PCAP_THREAD_PACKET_OK;) { if (packet->have_ip6frag) { packet->ip6frag_payload = ext.ip6e_nxt; layer_trace("is_v6_frag"); if (pcaplist->pcap_thread->callback_ipv6_frag.reassemble) { pcap_thread_packet_t* whole_packet = 0; const u_char* whole_payload = 0; size_t whole_length = 0; packet->state = pcaplist->pcap_thread->callback_ipv6_frag.reassemble(pcaplist->ipv6_frag_ctx, packet, payload, length, &whole_packet, &whole_payload, &whole_length); /* Defragmentation failed some how, we return packet as invalid */ if (packet->state != PCAP_THREAD_PACKET_OK) { break; } /* No whole/defragmented packet return, need more */ if (!whole_packet || !whole_payload || !whole_length) { return; } layer_tracef("v6_reasm %p %p %lu", whole_packet, whole_payload, whole_length); packet = whole_packet; payload = whole_payload; length = whole_length; release_frag = 1; } else { /* * Mark packet as fragment and send it to the next user * layer (if any) or return it as invalid. */ packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; switch (ext.ip6e_nxt) { case IPPROTO_GRE: layer_trace("ipproto_gre frag"); if (!packet->ip6frag.ip6f_offlg) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_GREHDR; need16(packet->grehdr.gre_flags, payload, length); need16(packet->grehdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_grehdr = 1; break; } } if (pcaplist->pcap_thread->callback_gre) { pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_ICMPV6: layer_trace("ipproto_icmpv6 frag"); if (!packet->ip6frag.ip6f_offlg) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_ICMPV6HDR; need8(packet->icmpv6hdr.icmp6_type, payload, length); need8(packet->icmpv6hdr.icmp6_code, payload, length); need16(packet->icmpv6hdr.icmp6_cksum, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_icmpv6hdr = 1; break; } } if (pcaplist->pcap_thread->callback_icmpv6) { pcaplist->pcap_thread->callback_icmpv6(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_UDP: layer_trace("ipproto_udp frag"); if (!packet->ip6frag.ip6f_offlg) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_UDPHDR; need16(packet->udphdr.uh_sport, payload, length); need16(packet->udphdr.uh_dport, payload, length); need16(packet->udphdr.uh_ulen, payload, length); need16(packet->udphdr.uh_sum, payload, length); packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_udphdr = 1; break; } } if (pcaplist->pcap_thread->callback_udp) { pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length); return; } break; case IPPROTO_TCP: layer_trace("ipproto_tcp frag"); if (!packet->ip6frag.ip6f_offlg) { for (;;) { packet->state = PCAP_THREAD_PACKET_FRAGMENTED_TCPHDR; need16(packet->tcphdr.th_sport, payload, length); need16(packet->tcphdr.th_dport, payload, length); need32(packet->tcphdr.th_seq, payload, length); need32(packet->tcphdr.th_ack, payload, length); need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length); need8(packet->tcphdr.th_flags, payload, length); need16(packet->tcphdr.th_win, payload, length); need16(packet->tcphdr.th_sum, payload, length); need16(packet->tcphdr.th_urp, payload, length); if (packet->tcphdr.th_off > 5) { packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4; needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length); packet->have_tcpopts = 1; } packet->state = PCAP_THREAD_PACKET_IS_FRAGMENT; packet->have_tcphdr = 1; break; } } if (pcaplist->pcap_thread->callback_tcp) { pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length); return; } break; default: break; } break; } } switch (ext.ip6e_nxt) { case IPPROTO_GRE: if (packet->have_grehdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_GRE; need16(packet->grehdr.gre_flags, payload, length); need16(packet->grehdr.ether_type, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_grehdr = 1; if (pcaplist->pcap_thread->callback_gre) pcaplist->pcap_thread->callback_gre(pcaplist->user, packet, payload, length); else pcap_thread_callback_gre((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length); } return; case IPPROTO_ICMPV6: layer_trace("ipproto_icmpv6"); if (packet->have_icmpv6hdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_ICMPV6; need8(packet->icmpv6hdr.icmp6_type, payload, length); need8(packet->icmpv6hdr.icmp6_code, payload, length); need16(packet->icmpv6hdr.icmp6_cksum, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_icmpv6hdr = 1; if (pcaplist->pcap_thread->callback_icmpv6) pcaplist->pcap_thread->callback_icmpv6(pcaplist->user, packet, payload, length); else pcap_thread_callback_icmpv6((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length); } return; case IPPROTO_UDP: if (packet->have_udphdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_UDP; need16(packet->udphdr.uh_sport, payload, length); need16(packet->udphdr.uh_dport, payload, length); need16(packet->udphdr.uh_ulen, payload, length); need16(packet->udphdr.uh_sum, payload, length); packet->state = PCAP_THREAD_PACKET_OK; packet->have_udphdr = 1; if (pcaplist->pcap_thread->callback_udp) pcaplist->pcap_thread->callback_udp(pcaplist->user, packet, payload, length); else pcap_thread_callback_udp((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length); } return; case IPPROTO_TCP: if (packet->have_tcphdr) break; packet->state = PCAP_THREAD_PACKET_INVALID_TCP; need16(packet->tcphdr.th_sport, payload, length); need16(packet->tcphdr.th_dport, payload, length); need32(packet->tcphdr.th_seq, payload, length); need32(packet->tcphdr.th_ack, payload, length); need4x2(packet->tcphdr.th_off, packet->tcphdr.th_x2, payload, length); need8(packet->tcphdr.th_flags, payload, length); need16(packet->tcphdr.th_win, payload, length); need16(packet->tcphdr.th_sum, payload, length); need16(packet->tcphdr.th_urp, payload, length); if (packet->tcphdr.th_off > 5) { packet->tcpopts_len = (packet->tcphdr.th_off - 5) * 4; needxb(&(packet->tcpopts[0]), packet->tcpopts_len, payload, length); packet->have_tcpopts = 1; } packet->state = PCAP_THREAD_PACKET_OK; packet->have_tcphdr = 1; if (pcaplist->pcap_thread->callback_tcp) pcaplist->pcap_thread->callback_tcp(pcaplist->user, packet, payload, length); else pcap_thread_callback_tcp((void*)pcaplist, packet, payload, length); if (release_frag) { pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length); } return; default: packet->state = PCAP_THREAD_PACKET_UNSUPPORTED; break; } break; } } if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; if (release_frag) pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, payload, length); else pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } if (release_frag) { pcaplist->pcap_thread->callback_ipv6_frag.release(pcaplist->ipv6_frag_ctx, packet, payload, length); } } static void pcap_thread_callback_icmp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } /* TODO: Higher layer support? */ packet->state = PCAP_THREAD_PACKET_UNPROCESSED; if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_icmpv6(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } /* TODO: Higher layer support? */ packet->state = PCAP_THREAD_PACKET_UNPROCESSED; if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_udp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } /* TODO: Higher layer support? */ packet->state = PCAP_THREAD_PACKET_UNPROCESSED; if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } static void pcap_thread_callback_tcp(u_char* user, pcap_thread_packet_t* packet, const u_char* payload, size_t length) { pcap_thread_pcaplist_t* pcaplist = (pcap_thread_pcaplist_t*)user; const u_char* orig = payload; size_t origlength = length; if (!pcaplist) { return; } if (!pcaplist->pcap_thread) { return; } if (!packet) { return; } if (!payload) { return; } if (!length) { return; } /* TODO: Higher layer support? */ packet->state = PCAP_THREAD_PACKET_UNPROCESSED; if (pcaplist->pcap_thread->callback_invalid) { if (packet->state == PCAP_THREAD_PACKET_OK) packet->state = PCAP_THREAD_PACKET_INVALID; pcaplist->pcap_thread->callback_invalid(pcaplist->user, packet, orig, origlength); } } /* * Open/Close */ static pcap_thread_pcaplist_t _pcaplist_defaults = PCAP_THREAD_PCAPLIST_T_INIT; int pcap_thread_open(pcap_thread_t* pcap_thread, const char* device, void* user) { pcap_t* pcap; pcap_thread_pcaplist_t* pcaplist; int snapshot; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!device) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; if (!(pcaplist = malloc(sizeof(pcap_thread_pcaplist_t)))) { return PCAP_THREAD_ENOMEM; } memcpy(pcaplist, &_pcaplist_defaults, sizeof(pcap_thread_pcaplist_t)); if (!(pcaplist->name = strdup(device))) { free(pcaplist); return PCAP_THREAD_ENOMEM; } #ifdef HAVE_PCAP_CREATE if (!(pcap = pcap_create(pcaplist->name, pcap_thread->errbuf))) { free(pcaplist->name); free(pcaplist); return PCAP_THREAD_EPCAP; } if (pcap_thread->monitor) { pcap_thread->status = pcap_can_set_rfmon(pcap); if (pcap_thread->status == 0) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); return PCAP_THREAD_ENOMON; } if (pcap_thread->status != 1) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_can_set_rfmon()"); return PCAP_THREAD_EPCAP; } } #ifdef HAVE_PCAP_SET_TSTAMP_PRECISION if (pcap_thread->have_timestamp_precision && (pcap_thread->status = pcap_set_tstamp_precision(pcap, pcap_thread->timestamp_precision))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_tstamp_precision()"); return PCAP_THREAD_EPCAP; } #endif #ifdef HAVE_PCAP_SET_IMMEDIATE_MODE if (pcap_thread->immediate_mode && (pcap_thread->status = pcap_set_immediate_mode(pcap, 1))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_immediate_mode()"); return PCAP_THREAD_EPCAP; } #endif if (pcap_thread->monitor && (pcap_thread->status = pcap_set_rfmon(pcap, 1))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_rfmon()"); return PCAP_THREAD_EPCAP; } if (pcap_thread->snaplen && (pcap_thread->status = pcap_set_snaplen(pcap, pcap_thread->snaplen))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_snaplen()"); return PCAP_THREAD_EPCAP; } if (pcap_thread->promiscuous && (pcap_thread->status = pcap_set_promisc(pcap, pcap_thread->promiscuous))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_promisc()"); return PCAP_THREAD_EPCAP; } if (pcap_thread->timeout && (pcap_thread->status = pcap_set_timeout(pcap, pcap_thread->timeout))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_timeout()"); return PCAP_THREAD_EPCAP; } if (pcap_thread->buffer_size && (pcap_thread->status = pcap_set_buffer_size(pcap, pcap_thread->buffer_size))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_buffer_size()"); return PCAP_THREAD_EPCAP; } #ifdef HAVE_PCAP_SET_TSTAMP_TYPE if (pcap_thread->have_timestamp_type && (pcap_thread->status = pcap_set_tstamp_type(pcap, pcap_thread->timestamp_type))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_set_tstamp_type()"); return PCAP_THREAD_EPCAP; } #endif if (pcap_thread->activate_mode == PCAP_THREAD_ACTIVATE_MODE_IMMEDIATE) { if ((pcap_thread->status = pcap_activate(pcap))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_activate()"); return PCAP_THREAD_EPCAP; } #ifdef HAVE_PCAP_SETDIRECTION #ifdef HAVE_PCAP_DIRECTION_T if (pcap_thread->have_direction && (pcap_thread->status = pcap_setdirection(pcap, pcap_thread->direction))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setdirection()"); return PCAP_THREAD_EPCAP; } #endif #endif } #else /* HAVE_PCAP_CREATE */ if (!(pcap = pcap_open_live(pcaplist->name, pcap_thread->snaplen, pcap_thread->promiscuous, pcap_thread->timeout, pcap_thread->errbuf))) { free(pcaplist->name); free(pcaplist); return PCAP_THREAD_EPCAP; } #endif if (pcap_thread->activate_mode == PCAP_THREAD_ACTIVATE_MODE_IMMEDIATE) { if (pcap_thread->filter) { if ((pcap_thread->status = pcap_compile(pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()"); return PCAP_THREAD_EPCAP; } pcaplist->have_bpf = 1; pcap_thread->filter_errno = 0; errno = 0; if ((pcap_thread->status = pcap_setfilter(pcap, &(pcaplist->bpf)))) { pcap_freecode(&(pcaplist->bpf)); pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()"); return PCAP_THREAD_EPCAP; } pcap_thread->filter_errno = errno; } if ((snapshot = pcap_snapshot(pcap)) < 0) { pcap_thread->status = snapshot; if (pcaplist->have_bpf) pcap_freecode(&(pcaplist->bpf)); pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()"); return PCAP_THREAD_EPCAP; } if (snapshot > pcap_thread->snapshot) { pcap_thread->snapshot = snapshot; } } pcaplist->pcap = pcap; pcaplist->user = user; if (pcap_thread->callback_ipv4_frag.new) { pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, user); pcaplist->have_ipv4_frag_ctx = 1; } if (pcap_thread->callback_ipv6_frag.new) { pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, user); pcaplist->have_ipv6_frag_ctx = 1; } if (pcap_thread->pcaplist) { pcaplist->next = pcap_thread->pcaplist; } pcap_thread->pcaplist = pcaplist; return PCAP_THREAD_OK; } int pcap_thread_open_offline(pcap_thread_t* pcap_thread, const char* file, void* user) { pcap_t* pcap; pcap_thread_pcaplist_t* pcaplist; int snapshot; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!file) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; if (!(pcaplist = malloc(sizeof(pcap_thread_pcaplist_t)))) { return PCAP_THREAD_ENOMEM; } memcpy(pcaplist, &_pcaplist_defaults, sizeof(pcap_thread_pcaplist_t)); pcaplist->is_offline = 1; if (!(pcaplist->name = strdup(file))) { free(pcaplist); return PCAP_THREAD_ENOMEM; } #ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION if (pcap_thread->have_timestamp_precision) { if (!(pcap = pcap_open_offline_with_tstamp_precision(pcaplist->name, pcap_thread->timestamp_precision, pcap_thread->errbuf))) { free(pcaplist->name); free(pcaplist); return PCAP_THREAD_EPCAP; } } else #endif { if (!(pcap = pcap_open_offline(pcaplist->name, pcap_thread->errbuf))) { free(pcaplist->name); free(pcaplist); return PCAP_THREAD_EPCAP; } } if (pcap_thread->filter) { if ((pcap_thread->status = pcap_compile(pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) { pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()"); return PCAP_THREAD_EPCAP; } pcaplist->have_bpf = 1; pcap_thread->filter_errno = 0; errno = 0; if ((pcap_thread->status = pcap_setfilter(pcap, &(pcaplist->bpf)))) { pcap_freecode(&(pcaplist->bpf)); pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()"); return PCAP_THREAD_EPCAP; } pcap_thread->filter_errno = errno; } if ((snapshot = pcap_snapshot(pcap)) < 0) { pcap_thread->status = snapshot; if (pcaplist->have_bpf) pcap_freecode(&(pcaplist->bpf)); pcap_close(pcap); free(pcaplist->name); free(pcaplist); PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()"); return PCAP_THREAD_EPCAP; } if (snapshot > pcap_thread->snapshot) { pcap_thread->snapshot = snapshot; } pcaplist->pcap = pcap; pcaplist->user = user; if (pcap_thread->callback_ipv4_frag.new) { pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, user); pcaplist->have_ipv4_frag_ctx = 1; } if (pcap_thread->callback_ipv6_frag.new) { pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, user); pcaplist->have_ipv6_frag_ctx = 1; } if (pcap_thread->pcaplist) { pcaplist->next = pcap_thread->pcaplist; } pcap_thread->pcaplist = pcaplist; return PCAP_THREAD_OK; } int pcap_thread_add(pcap_thread_t* pcap_thread, const char* name, pcap_t* pcap, void* user) { (void)pcap_thread; (void)name; (void)pcap; (void)user; if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; return PCAP_THREAD_EOBSOLETE; } int pcap_thread_activate(pcap_thread_t* pcap_thread) { pcap_thread_pcaplist_t* pcaplist; int snapshot; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; pcap_thread->filter_errno = 0; for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { if (pcaplist->is_offline) { continue; } #ifdef HAVE_PCAP_ACTIVATE if ((pcap_thread->status = pcap_activate(pcaplist->pcap))) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_activate()"); return PCAP_THREAD_EPCAP; } #endif #ifdef HAVE_PCAP_SETDIRECTION #ifdef HAVE_PCAP_DIRECTION_T if (pcap_thread->have_direction && (pcap_thread->status = pcap_setdirection(pcaplist->pcap, pcap_thread->direction))) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setdirection()"); return PCAP_THREAD_EPCAP; } #endif #endif if (pcap_thread->filter) { if (pcaplist->have_bpf) pcap_freecode(&(pcaplist->bpf)); if ((pcap_thread->status = pcap_compile(pcaplist->pcap, &(pcaplist->bpf), pcap_thread->filter, pcap_thread->filter_optimize, pcap_thread->filter_netmask))) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_compile()"); return PCAP_THREAD_EPCAP; } pcaplist->have_bpf = 1; errno = 0; if ((pcap_thread->status = pcap_setfilter(pcaplist->pcap, &(pcaplist->bpf)))) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_setfilter()"); return PCAP_THREAD_EPCAP; } if (errno && !pcap_thread->filter_errno) pcap_thread->filter_errno = errno; } if ((snapshot = pcap_snapshot(pcaplist->pcap)) < 0) { pcap_thread->status = snapshot; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_snapshot()"); return PCAP_THREAD_EPCAP; } if (snapshot > pcap_thread->snapshot) { pcap_thread->snapshot = snapshot; } } return PCAP_THREAD_OK; } int pcap_thread_close(pcap_thread_t* pcap_thread) { pcap_thread_pcaplist_t* pcaplist; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } while (pcap_thread->pcaplist) { pcaplist = pcap_thread->pcaplist; pcap_thread->pcaplist = pcaplist->next; if (pcap_thread->callback_ipv4_frag.free && pcaplist->have_ipv4_frag_ctx) { pcap_thread->callback_ipv4_frag.free(pcaplist->ipv4_frag_ctx); } if (pcap_thread->callback_ipv6_frag.free && pcaplist->have_ipv6_frag_ctx) { pcap_thread->callback_ipv6_frag.free(pcaplist->ipv6_frag_ctx); } if (pcaplist->pcap) { pcap_close(pcaplist->pcap); } if (pcaplist->have_bpf) { pcap_freecode(&(pcaplist->bpf)); } if (pcaplist->name) { free(pcaplist->name); } free(pcaplist); } pcap_thread->step = 0; #ifdef HAVE_PTHREAD if (pcap_thread->pkthdr) { free(pcap_thread->pkthdr); pcap_thread->pkthdr = 0; } if (pcap_thread->pkt) { free(pcap_thread->pkt); pcap_thread->pkt = 0; } if (pcap_thread->pcaplist_pkt) { free(pcap_thread->pcaplist_pkt); pcap_thread->pcaplist_pkt = 0; } #endif return PCAP_THREAD_OK; } /* * Engine */ #ifdef HAVE_PTHREAD static void _callback(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt) { pcap_thread_pcaplist_t* pcaplist; pcap_thread_t* pcap_thread; pthread_testcancel(); if (!user) { return; } pcaplist = (pcap_thread_pcaplist_t*)user; if (!pcaplist->pcap_thread) { pcaplist->running = 0; return; } pcap_thread = pcaplist->pcap_thread; if (pkthdr->caplen > pcap_thread->snapshot) { if (pcap_thread->dropback) { pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } return; } if (pcap_thread->queue_mode == PCAP_THREAD_QUEUE_MODE_DIRECT) { if (pcap_thread->callback) { pcap_thread->callback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } else if (pcaplist->layer_callback) { pcaplist->layer_callback((void*)pcaplist, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } else if (pcap_thread->dropback) { pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } return; } if (pthread_mutex_lock(&(pcap_thread->mutex))) { if (pcap_thread->dropback) { pcap_thread->dropback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } return; } while (pcaplist->running && pcap_thread->running) { if (pcap_thread->pkts < pcap_thread->queue_size) { pcap_thread->pcaplist_pkt[pcap_thread->write_pos] = pcaplist; memcpy(&(pcap_thread->pkthdr[pcap_thread->write_pos]), pkthdr, sizeof(struct pcap_pkthdr)); memcpy(&(pcap_thread->pkt[pcap_thread->write_pos * pcap_thread->snapshot]), pkt, pkthdr->caplen); pcap_thread->write_pos++; if (pcap_thread->write_pos == pcap_thread->queue_size) { pcap_thread->write_pos = 0; } pcap_thread->pkts++; pthread_cond_signal(&(pcap_thread->have_packets)); break; } if (pthread_cond_wait(&(pcap_thread->can_write), &(pcap_thread->mutex))) { pcaplist->running = 0; pcap_breakloop(pcaplist->pcap); return; } continue; } if (pthread_mutex_unlock(&(pcap_thread->mutex))) { pcaplist->running = 0; pcap_breakloop(pcaplist->pcap); return; } } static void* _thread(void* vp) { pcap_thread_pcaplist_t* pcaplist; int ret = 0; /*pthread_detach(pthread_self());*/ if (!vp) { return 0; } pcaplist = (pcap_thread_pcaplist_t*)vp; if (!pcaplist->pcap_thread) { pcaplist->running = 0; return 0; } /* * pcap_loop() might return -2 to indicate pcap_breakloop() was called * but we do not need to act on that because either this thread has * been cancelled or running has been cleared */ while (pcaplist->running) { pthread_testcancel(); ret = pcap_loop(pcaplist->pcap, -1, _callback, (u_char*)pcaplist); if (ret == -1) { /* TODO: Store pcap_loop() error */ break; } if (!ret) break; } pcaplist->running = 0; pthread_mutex_lock(&(pcaplist->pcap_thread->mutex)); pthread_cond_signal(&(pcaplist->pcap_thread->have_packets)); pthread_mutex_unlock(&(pcaplist->pcap_thread->mutex)); return 0; } #endif static void _callback2(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* pkt) { pcap_thread_pcaplist_t* pcaplist; if (!user) { return; } pcaplist = (pcap_thread_pcaplist_t*)user; if (!pcaplist->pcap_thread) { pcaplist->running = 0; return; } if (pcaplist->pcap_thread->callback) { pcaplist->pcap_thread->callback(pcaplist->user, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } else if (pcaplist->layer_callback) { pcaplist->layer_callback((void*)pcaplist, pkthdr, pkt, pcaplist->name, pcap_datalink(pcaplist->pcap)); } else { pcaplist->running = 0; } } int pcap_thread_run(pcap_thread_t* pcap_thread) { pcap_thread_pcaplist_t* pcaplist; int run = 1, timedrun = 0; struct timeval start = { 0, 0 }; struct timespec end = { 0, 0 }; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!pcap_thread->pcaplist) { return PCAP_THREAD_NOPCAPS; } if (!pcap_thread->callback && !pcap_thread->use_layers) { return PCAP_THREAD_NOCALLBACK; } if (pcap_thread->use_layers && !(pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp)) { return PCAP_THREAD_NOCALLBACK; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; if (pcap_thread->timedrun.tv_sec || pcap_thread->timedrun.tv_usec) { timedrun = 1; if (gettimeofday(&start, 0)) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()"); return PCAP_THREAD_ERRNO; } end.tv_sec = start.tv_sec + pcap_thread->timedrun.tv_sec + ((start.tv_usec + pcap_thread->timedrun.tv_usec) / 1000000); end.tv_nsec = ((start.tv_usec + pcap_thread->timedrun.tv_usec) % 1000000) * 1000; } else if (pcap_thread->timedrun_to.tv_sec) { timedrun = 1; end.tv_sec = pcap_thread->timedrun_to.tv_sec; end.tv_nsec = pcap_thread->timedrun_to.tv_usec * 1000; } #ifdef HAVE_PTHREAD if (pcap_thread->use_threads) { int err, all_offline; switch (pcap_thread->queue_mode) { case PCAP_THREAD_QUEUE_MODE_COND: case PCAP_THREAD_QUEUE_MODE_DIRECT: if ((err = pthread_mutex_lock(&(pcap_thread->mutex)))) { errno = err; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_mutex_lock()"); return PCAP_THREAD_ERRNO; } break; case PCAP_THREAD_QUEUE_MODE_WAIT: case PCAP_THREAD_QUEUE_MODE_YIELD: case PCAP_THREAD_QUEUE_MODE_DROP: return PCAP_THREAD_EOBSOLETE; default: return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { pthread_mutex_unlock(&(pcap_thread->mutex)); return PCAP_THREAD_ERUNNING; } if (pcap_thread->pkthdr) { free(pcap_thread->pkthdr); } if (!(pcap_thread->pkthdr = calloc(pcap_thread->queue_size, sizeof(struct pcap_pkthdr)))) { pthread_mutex_unlock(&(pcap_thread->mutex)); return PCAP_THREAD_ENOMEM; } if (pcap_thread->pkt) { free(pcap_thread->pkt); } if (!(pcap_thread->pkt = calloc(pcap_thread->queue_size, pcap_thread->snapshot))) { pthread_mutex_unlock(&(pcap_thread->mutex)); return PCAP_THREAD_ENOMEM; } if (pcap_thread->pcaplist_pkt) { free(pcap_thread->pcaplist_pkt); } if (!(pcap_thread->pcaplist_pkt = calloc(pcap_thread->queue_size, sizeof(pcap_thread_pcaplist_t*)))) { pthread_mutex_unlock(&(pcap_thread->mutex)); return PCAP_THREAD_ENOMEM; } pcap_thread->read_pos = 0; pcap_thread->write_pos = 0; pcap_thread->pkts = 0; all_offline = 1; for (pcaplist = pcap_thread->pcaplist; all_offline && pcaplist; pcaplist = pcaplist->next) { if (!pcaplist->is_offline) { all_offline = 0; break; } } pcap_thread->running = 1; pcap_thread->was_stopped = 0; err = PCAP_THREAD_OK; for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { pcaplist->pcap_thread = pcap_thread; if (pcap_thread->use_layers) { pcaplist->layer_callback = &pcap_thread_callback; } if (pcap_thread->callback_ipv4_frag.new && !pcaplist->have_ipv4_frag_ctx) { pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcaplist->user); pcaplist->have_ipv4_frag_ctx = 1; } if (pcap_thread->callback_ipv6_frag.new && !pcaplist->have_ipv6_frag_ctx) { pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcaplist->user); pcaplist->have_ipv6_frag_ctx = 1; } pcaplist->running = 1; if ((err = pthread_create(&(pcaplist->thread), 0, _thread, (void*)pcaplist))) { errno = err; err = PCAP_THREAD_ERRNO; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_create()"); break; } } while (err == PCAP_THREAD_OK && run && pcap_thread->running) { while (pcap_thread->pkts) { if (!pcap_thread->pcaplist_pkt[pcap_thread->read_pos]) { err = PCAP_THREAD_ENOPCAPLIST; break; } if (pcap_thread->callback) { pcap_thread->callback( pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->user, &(pcap_thread->pkthdr[pcap_thread->read_pos]), &(pcap_thread->pkt[pcap_thread->read_pos * pcap_thread->snapshot]), pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->name, pcap_datalink(pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->pcap)); } else { pcap_thread_callback( (void*)pcap_thread->pcaplist_pkt[pcap_thread->read_pos], &(pcap_thread->pkthdr[pcap_thread->read_pos]), &(pcap_thread->pkt[pcap_thread->read_pos * pcap_thread->snapshot]), pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->name, pcap_datalink(pcap_thread->pcaplist_pkt[pcap_thread->read_pos]->pcap)); } pcap_thread->pcaplist_pkt[pcap_thread->read_pos] = 0; pcap_thread->read_pos++; if (pcap_thread->read_pos == pcap_thread->queue_size) { pcap_thread->read_pos = 0; } pcap_thread->pkts--; } if (err != PCAP_THREAD_OK) break; if ((err = pthread_cond_broadcast(&(pcap_thread->can_write)))) { errno = err; err = PCAP_THREAD_ERRNO; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_broadcast()"); break; } run = 0; for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { if (pcaplist->running) { run = 1; } } if (!run) break; if (timedrun) { struct timeval now; if (gettimeofday(&now, 0)) { err = PCAP_THREAD_ERRNO; PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()"); break; } if (now.tv_sec > end.tv_sec || (now.tv_sec == end.tv_sec && (now.tv_usec * 1000) >= end.tv_nsec)) { break; } err = pthread_cond_timedwait(&(pcap_thread->have_packets), &(pcap_thread->mutex), &end); if (err == ETIMEDOUT) { err = PCAP_THREAD_OK; } else if (err) { errno = err; err = PCAP_THREAD_ERRNO; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_timedwait()"); break; } } else { if ((err = pthread_cond_wait(&(pcap_thread->have_packets), &(pcap_thread->mutex)))) { errno = err; err = PCAP_THREAD_ERRNO; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pthread_cond_wait()"); break; } } } for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { pcaplist->running = 0; pcap_breakloop(pcaplist->pcap); if (pcaplist->thread) { pthread_cancel(pcaplist->thread); } } pthread_mutex_unlock(&(pcap_thread->mutex)); for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { if (pcaplist->thread) { pthread_join(pcaplist->thread, 0); pcaplist->thread = 0; } } pcap_thread->running = 0; return err; } else #endif { fd_set fds, rfds; int max_fd = 0; struct timeval t1, t2; pcap_thread->running = 1; pcap_thread->was_stopped = 0; FD_ZERO(&fds); for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { int fd = pcap_get_selectable_fd(pcaplist->pcap); FD_SET(fd, &fds); if (fd > max_fd) max_fd = fd; if (!pcaplist->is_offline && (pcap_thread->status = pcap_setnonblock(pcaplist->pcap, 1, pcap_thread->errbuf))) { pcap_thread->running = 0; return PCAP_THREAD_EPCAP; } pcaplist->pcap_thread = pcap_thread; if (pcap_thread->use_layers) { pcaplist->layer_callback = &pcap_thread_callback; } if (pcap_thread->callback_ipv4_frag.new && !pcaplist->have_ipv4_frag_ctx) { pcaplist->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcaplist->user); pcaplist->have_ipv4_frag_ctx = 1; } if (pcap_thread->callback_ipv6_frag.new && !pcaplist->have_ipv6_frag_ctx) { pcaplist->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcaplist->user); pcaplist->have_ipv6_frag_ctx = 1; } pcaplist->running = 1; } t1.tv_sec = pcap_thread->timeout / 1000; t1.tv_usec = (pcap_thread->timeout % 1000) * 1000; max_fd++; while (run) { rfds = fds; t2 = t1; if (timedrun) { struct timeval now; struct timeval diff; if (gettimeofday(&now, 0)) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "gettimeofday()"); pcap_thread->running = 0; return PCAP_THREAD_ERRNO; } if (now.tv_sec > end.tv_sec || (now.tv_sec == end.tv_sec && (now.tv_usec * 1000) >= end.tv_nsec)) { break; } if (end.tv_sec > now.tv_sec) { diff.tv_sec = end.tv_sec - now.tv_sec - 1; diff.tv_usec = 1000000 - now.tv_usec; diff.tv_usec += end.tv_nsec / 1000; if (diff.tv_usec > 1000000) { diff.tv_sec += diff.tv_usec / 1000000; diff.tv_usec %= 1000000; } } else { diff.tv_sec = 0; if (end.tv_sec == now.tv_sec && (end.tv_nsec / 1000) > now.tv_usec) { diff.tv_usec = (end.tv_nsec / 1000) - now.tv_usec; } else { diff.tv_usec = 0; } } if (diff.tv_sec < t1.tv_sec || (diff.tv_sec == t1.tv_sec && diff.tv_usec < t1.tv_usec)) { t2 = diff; } } if (select(max_fd, &rfds, 0, 0, &t2) == -1) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "select()"); pcap_thread->running = 0; return PCAP_THREAD_ERRNO; } run = 0; for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { int packets; if (!pcaplist->running) { continue; } else { run = 1; } packets = pcap_dispatch(pcaplist->pcap, -1, _callback2, (u_char*)pcaplist); if (packets == -1) { pcap_thread->status = -1; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_dispatch()"); pcap_thread->running = 0; return PCAP_THREAD_EPCAP; } else if (packets == -2 || (pcaplist->is_offline && !packets)) { pcaplist->running = 0; } } } pcap_thread->running = 0; } return PCAP_THREAD_OK; } int pcap_thread_next(pcap_thread_t* pcap_thread) { const u_char* pkt; struct pcap_pkthdr pkthdr; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!pcap_thread->callback && !pcap_thread->use_layers) { return PCAP_THREAD_NOCALLBACK; } if (pcap_thread->use_layers && !(pcap_thread->callback_linux_sll || pcap_thread->callback_ether || pcap_thread->callback_null || pcap_thread->callback_loop || pcap_thread->callback_ieee802 || pcap_thread->callback_gre || pcap_thread->callback_ip || pcap_thread->callback_ipv4 || pcap_thread->callback_ipv6 || pcap_thread->callback_icmp || pcap_thread->callback_icmpv6 || pcap_thread->callback_udp || pcap_thread->callback_tcp)) { return PCAP_THREAD_NOCALLBACK; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (!pcap_thread->pcaplist) { return PCAP_THREAD_NOPCAPS; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; if (!pcap_thread->step) { pcap_thread->step = pcap_thread->pcaplist; } if (!pcap_thread->step) { return PCAP_THREAD_OK; } pcap_thread->step->pcap_thread = pcap_thread; if (pcap_thread->callback_ipv4_frag.new && !pcap_thread->step->have_ipv4_frag_ctx) { pcap_thread->step->ipv4_frag_ctx = pcap_thread->callback_ipv4_frag.new(pcap_thread->callback_ipv4_frag.conf, pcap_thread->step->user); pcap_thread->step->have_ipv4_frag_ctx = 1; } if (pcap_thread->callback_ipv6_frag.new && !pcap_thread->step->have_ipv6_frag_ctx) { pcap_thread->step->ipv6_frag_ctx = pcap_thread->callback_ipv6_frag.new(pcap_thread->callback_ipv6_frag.conf, pcap_thread->step->user); pcap_thread->step->have_ipv6_frag_ctx = 1; } if (!(pkt = pcap_next(pcap_thread->step->pcap, &pkthdr))) { pcap_thread->status = -1; PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_next()"); return PCAP_THREAD_EPCAP; } if (pcap_thread->callback) { pcap_thread->callback(pcap_thread->step->user, &pkthdr, pkt, pcap_thread->step->name, pcap_datalink(pcap_thread->step->pcap)); } else { pcap_thread_callback((void*)pcap_thread->step, &pkthdr, pkt, pcap_thread->step->name, pcap_datalink(pcap_thread->step->pcap)); } pcap_thread->step = pcap_thread->step->next; return PCAP_THREAD_OK; } int pcap_thread_next_reset(pcap_thread_t* pcap_thread) { if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (pcap_thread->running) { return PCAP_THREAD_ERUNNING; } if (!pcap_thread->pcaplist) { return PCAP_THREAD_NOPCAPS; } pcap_thread->step = 0; return PCAP_THREAD_OK; } int pcap_thread_stop(pcap_thread_t* pcap_thread) { pcap_thread_pcaplist_t* pcaplist; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!pcap_thread->pcaplist) { return PCAP_THREAD_NOPCAPS; } for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { pcaplist->running = 0; pcap_breakloop(pcaplist->pcap); } pcap_thread->running = 0; pcap_thread->was_stopped = 1; #ifdef HAVE_PTHREAD pthread_cond_broadcast(&(pcap_thread->have_packets)); pthread_cond_broadcast(&(pcap_thread->can_write)); #endif return PCAP_THREAD_OK; } /* * Stats */ int pcap_thread_stats(pcap_thread_t* pcap_thread, pcap_thread_stats_callback_t callback, u_char* user) { pcap_thread_pcaplist_t* pcaplist; struct pcap_stat stats; if (!pcap_thread) { return PCAP_THREAD_EINVAL; } if (!callback) { return PCAP_THREAD_NOCALLBACK; } if (!pcap_thread->pcaplist) { return PCAP_THREAD_NOPCAPS; } if (pcap_thread->errbuf[0]) { memset(pcap_thread->errbuf, 0, sizeof(pcap_thread->errbuf)); } pcap_thread->status = 0; for (pcaplist = pcap_thread->pcaplist; pcaplist; pcaplist = pcaplist->next) { if (pcaplist->is_offline) continue; if ((pcap_thread->status = pcap_stats(pcaplist->pcap, &stats))) { PCAP_THREAD_SET_ERRBUF(pcap_thread, "pcap_stats()"); return PCAP_THREAD_EPCAP; } callback(user, &stats, pcaplist->name, pcap_datalink(pcaplist->pcap)); } return PCAP_THREAD_OK; } /* * Error handling */ int pcap_thread_status(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return 0; } return pcap_thread->status; } const char* pcap_thread_errbuf(const pcap_thread_t* pcap_thread) { if (!pcap_thread) { return 0; } return pcap_thread->errbuf; } const char* pcap_thread_strerr(int error) { switch (error) { case PCAP_THREAD_OK: return 0; case PCAP_THREAD_EPCAP: return PCAP_THREAD_EPCAP_STR; case PCAP_THREAD_ENOMEM: return PCAP_THREAD_ENOMEM_STR; case PCAP_THREAD_ENOMON: return PCAP_THREAD_ENOMON_STR; case PCAP_THREAD_ENODIR: return PCAP_THREAD_ENODIR_STR; case PCAP_THREAD_EINVAL: return PCAP_THREAD_EINVAL_STR; case PCAP_THREAD_EWOULDBLOCK: return PCAP_THREAD_EWOULDBLOCK_STR; case PCAP_THREAD_NOPCAPS: return PCAP_THREAD_NOPCAPS_STR; case PCAP_THREAD_NOCALLBACK: return PCAP_THREAD_NOCALLBACK_STR; case PCAP_THREAD_ERRNO: return PCAP_THREAD_ERRNO_STR; case PCAP_THREAD_NOYIELD: return PCAP_THREAD_NOYIELD_STR; case PCAP_THREAD_EOBSOLETE: return PCAP_THREAD_EOBSOLETE_STR; case PCAP_THREAD_ERUNNING: return PCAP_THREAD_ERUNNING_STR; case PCAP_THREAD_ENOPCAPLIST: return PCAP_THREAD_ENOPCAPLIST_STR; case PCAP_THREAD_ELAYERCB: return PCAP_THREAD_ELAYERCB_STR; } return "UNKNOWN"; }