void ProcessPacket(NodeList_t *NodeList, pcap_dev_t *pcap_dev, const struct pcap_pkthdr *hdr, const u_char *data) { struct FlowNode *Node; struct ip *ip; void *payload, *defragmented; uint32_t size_ip, offset, data_len, payload_len, bytes; uint16_t version, ethertype, proto; #ifdef DEVEL char s1[64]; char s2[64]; #endif static unsigned pkg_cnt = 0; pkg_cnt++; dbg_printf("\nNext Packet: %u\n", pkg_cnt); pcap_dev->proc_stat.packets++; offset = pcap_dev->linkoffset; Node = New_Node(); if ( !Node ) { pcap_dev->proc_stat.skipped++; LogError("Node allocation error - skip packet"); return; } if ( pcap_dev->linktype == DLT_EN10MB ) { ethertype = data[12] << 0x08 | data[13]; int IEEE802 = ethertype <= 1500; if ( IEEE802 ) { pcap_dev->proc_stat.skipped++; Free_Node(Node); return; } REDO_LINK: switch (ethertype) { case 0x800: // IPv4 case 0x86DD: // IPv6 break; case 0x8100: { // VLAN do { vlan_hdr_t *vlan_hdr = (vlan_hdr_t *)(data + offset); // offset points to end of link layer dbg_printf("VLAN ID: %u, type: 0x%x\n", ntohs(vlan_hdr->vlan_id), ntohs(vlan_hdr->type) ); ethertype = ntohs(vlan_hdr->type); /* pkt->vlans[pkt->vlan_count].pcp = (p[0] >> 5) & 7; pkt->vlans[pkt->vlan_count].cfi = (p[0] >> 4) & 1; pkt->vlans[pkt->vlan_count].vid = uint_16_be(p) & 0xfff; */ offset += 4; } while ( ethertype == 0x8100 ); // redo ethertype evaluation goto REDO_LINK; } break; case 0x806: // skip ARP // silently skip ARP pcap_dev->proc_stat.skipped++; return; break; case 0x26: // ?? multicast router termination ?? case 0x4305: // B.A.T.M.A.N. BATADV case 0x886f: // MS NLB heartbeat case 0x88a2: // ATA over ethernet case 0x88cc: // CISCO LLDP case 0x9000: // Loop case 0x880b: // PPP - rfc 7042 pcap_dev->proc_stat.skipped++; if ( Node->proto ) { // if it's an encap which we do not understand yet - push tunnel Push_Node(NodeList, Node); } else { pcap_dev->proc_stat.skipped++; dbg_printf("Skip Ethertype 0x%x", ethertype); Free_Node(Node); } return; break; default: pcap_dev->proc_stat.unknown++; LogError("Unsupported link type: 0x%x, packet: %u", ethertype, pkg_cnt); Free_Node(Node); return; } } if (hdr->caplen < offset) { pcap_dev->proc_stat.short_snap++; LogError("Short packet: %u/%u", hdr->caplen, offset); Free_Node(Node); return; } Node->t_first.tv_sec = hdr->ts.tv_sec; Node->t_first.tv_usec = hdr->ts.tv_usec; Node->t_last.tv_sec = hdr->ts.tv_sec; Node->t_last.tv_usec = hdr->ts.tv_usec; data = data + offset; data_len = hdr->caplen - offset; offset = 0; defragmented = NULL; // IP decoding REDO_IPPROTO: // IP decoding if ( defragmented ) { // data is sitting on a defragmented IPv4 packet memory region // REDO loop could result in a memory leak, if again IP is fragmented // XXX memory leak to be fixed LogError("Fragmentation memory leak triggered!"); } ip = (struct ip *)(data + offset); // offset points to end of link layer version = ip->ip_v; // ip version if ( version == 6 ) { uint64_t *addr; struct ip6_hdr *ip6 = (struct ip6_hdr *) (data + offset); size_ip = sizeof(struct ip6_hdr); offset = size_ip; // offset point to end of IP header if ( data_len < size_ip ) { LogError("Packet: %u Length error: data_len: %u < size IPV6: %u, captured: %u, hdr len: %u", pkg_cnt, data_len, size_ip, hdr->caplen, hdr->len); pcap_dev->proc_stat.short_snap++; Free_Node(Node); return; } // XXX Extension headers not processed proto = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; payload_len = bytes = ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); if (data_len < (payload_len + size_ip) ) { // capture len was limited - so adapt payload_len payload_len = data_len - size_ip; } dbg_printf("Packet IPv6, SRC %s, DST %s, ", inet_ntop(AF_INET6, &ip6->ip6_src, s1, sizeof(s1)), inet_ntop(AF_INET6, &ip6->ip6_dst, s2, sizeof(s2))); payload = (void *)ip + size_ip; addr = (uint64_t *)&ip6->ip6_src; Node->src_addr.v6[0] = ntohll(addr[0]); Node->src_addr.v6[1] = ntohll(addr[1]); addr = (uint64_t *)&ip6->ip6_dst; Node->dst_addr.v6[0] = ntohll(addr[0]); Node->dst_addr.v6[1] = ntohll(addr[1]); Node->version = AF_INET6; } else if ( version == 4 ) { uint16_t ip_off = ntohs(ip->ip_off); uint32_t frag_offset = (ip_off & IP_OFFMASK) << 3; size_ip = (ip->ip_hl << 2); offset = size_ip; // offset point to end of IP header if ( data_len < size_ip ) { LogError("Packet: %u Length error: data_len: %u < size IPV4: %u, captured: %u, hdr len: %u", pkg_cnt, data_len, size_ip, hdr->caplen, hdr->len); pcap_dev->proc_stat.short_snap++; Free_Node(Node); return; } payload_len = ntohs(ip->ip_len); dbg_printf("size IP hader: %u, len: %u, %u\n", size_ip, ip->ip_len, payload_len); payload_len -= size_ip; // ajust length compatibel IPv6 bytes = payload_len; payload = (void *)ip + size_ip; proto = ip->ip_p; if (data_len < (payload_len + size_ip) ) { // capture len was limited - so adapt payload_len payload_len = data_len - size_ip; pcap_dev->proc_stat.short_snap++; } dbg_printf("Packet IPv4 SRC %s, DST %s, ", inet_ntop(AF_INET, &ip->ip_src, s1, sizeof(s1)), inet_ntop(AF_INET, &ip->ip_dst, s2, sizeof(s2))); // IPv4 defragmentation if ( (ip_off & IP_MF) || frag_offset ) { uint16_t ip_id = ntohs(ip->ip_id); #ifdef DEVEL if ( frag_offset == 0 ) printf("Fragmented packet: first segement: ip_off: %u, frag_offset: %u\n", ip_off, frag_offset); if (( ip_off & IP_MF ) && frag_offset ) printf("Fragmented packet: middle segement: ip_off: %u, frag_offset: %u\n", ip_off, frag_offset); if (( ip_off & IP_MF ) == 0 ) printf("Fragmented packet: last segement: ip_off: %u, frag_offset: %u\n", ip_off, frag_offset); #endif // fragmented packet defragmented = IPFrag_tree_Update(ip->ip_src.s_addr, ip->ip_dst.s_addr, ip_id, &payload_len, ip_off, payload); if ( defragmented == NULL ) { // not yet complete dbg_printf("Fragmentation not yet completed\n"); return; } dbg_printf("Fragmentation assembled\n"); // packet defragmented - set payload to defragmented data payload = defragmented; } Node->src_addr.v6[0] = 0; Node->src_addr.v6[1] = 0; Node->src_addr.v4 = ntohl(ip->ip_src.s_addr); Node->dst_addr.v6[0] = 0; Node->dst_addr.v6[1] = 0; Node->dst_addr.v4 = ntohl(ip->ip_dst.s_addr); Node->version = AF_INET; } else { LogError("ProcessPacket() Unsupprted protocol version: %i", version); pcap_dev->proc_stat.unknown++; Free_Node(Node); return; } Node->packets = 1; Node->bytes = bytes; Node->proto = proto; dbg_printf("Payload: %u bytes, Full packet: %u bytes\n", payload_len, bytes); // TCP/UDP decoding switch (proto) { case IPPROTO_UDP: { struct udphdr *udp = (struct udphdr *)payload; uint16_t UDPlen = ntohs(udp->uh_ulen); if ( UDPlen < 8 ) { LogError("UDP payload legth error: %u bytes < 8\n", UDPlen); Free_Node(Node); break; } uint32_t size_udp_payload = ntohs(udp->uh_ulen) - 8; if ( (bytes == payload_len ) && (payload_len - sizeof(struct udphdr)) != size_udp_payload ) { LogError("UDP payload legth error: Expected %u, have %u bytes\n", size_udp_payload, (payload_len - (unsigned)sizeof(struct udphdr))); Free_Node(Node); break; } payload = payload + sizeof(struct udphdr); payload_len -= sizeof(struct udphdr); dbg_printf("UDP: size: %u, SRC: %i, DST: %i\n", size_udp_payload, ntohs(udp->uh_sport), ntohs(udp->uh_dport)); Node->flags = 0; Node->src_port = ntohs(udp->uh_sport); Node->dst_port = ntohs(udp->uh_dport); if ( hdr->caplen == hdr->len ) { // process payload of full packets if ( (bytes == payload_len) && (Node->src_port == 53 || Node->dst_port == 53) ) content_decode_dns(Node, payload, payload_len); } Push_Node(NodeList, Node); } break; case IPPROTO_TCP: { struct tcphdr *tcp = (struct tcphdr *)payload; uint32_t size_tcp; size_tcp = tcp->th_off << 2; if ( payload_len < size_tcp ) { LogError("TCP header length error: len: %u < size TCP header: %u", payload_len, size_tcp); pcap_dev->proc_stat.short_snap++; Free_Node(Node); break; } payload = payload + size_tcp; payload_len -= size_tcp; dbg_printf("Size TCP header: %u, size TCP payload: %u ", size_tcp, payload_len); dbg_printf("src %i, DST %i, flags %i : ", ntohs(tcp->th_sport), ntohs(tcp->th_dport), tcp->th_flags); #ifdef DEVEL if ( tcp->th_flags & TH_SYN ) printf("SYN "); if ( tcp->th_flags & TH_ACK ) printf("ACK "); if ( tcp->th_flags & TH_URG ) printf("URG "); if ( tcp->th_flags & TH_PUSH ) printf("PUSH "); if ( tcp->th_flags & TH_FIN ) printf("FIN "); if ( tcp->th_flags & TH_RST ) printf("RST "); printf("\n"); #endif Node->flags = tcp->th_flags; Node->src_port = ntohs(tcp->th_sport); Node->dst_port = ntohs(tcp->th_dport); Push_Node(NodeList, Node); } break; case IPPROTO_ICMP: { struct icmp *icmp = (struct icmp *)payload; Node->dst_port = (icmp->icmp_type << 8 ) + icmp->icmp_code; dbg_printf("IPv%d ICMP proto: %u, type: %u, code: %u\n", version, ip->ip_p, icmp->icmp_type, icmp->icmp_code); Push_Node(NodeList, Node); } break; case IPPROTO_ICMPV6: { struct icmp6_hdr *icmp6 = (struct icmp6_hdr *)payload; Node->dst_port = (icmp6->icmp6_type << 8 ) + icmp6->icmp6_code; dbg_printf("IPv%d ICMP proto: %u, type: %u, code: %u\n", version, ip->ip_p, icmp6->icmp6_type, icmp6->icmp6_code); Push_Node(NodeList, Node); } break; case IPPROTO_IPV6: { uint32_t size_inner_ip = sizeof(struct ip6_hdr); if ( payload_len < size_inner_ip ) { LogError("IPIPv6 tunnel header length error: len: %u < size inner IP: %u", payload_len, size_inner_ip); pcap_dev->proc_stat.short_snap++; if ( defragmented ) { free(defragmented); defragmented = NULL; } Free_Node(Node); return; } offset = 0; data = payload; data_len = payload_len; // // move IP to tun IP Node->tun_src_addr = Node->src_addr; Node->tun_dst_addr = Node->dst_addr; Node->tun_proto = IPPROTO_IPIP; dbg_printf("IPIPv6 tunnel - inner IPv6:\n"); // redo proto evaluation goto REDO_IPPROTO; } break; case IPPROTO_IPIP: { struct ip *inner_ip = (struct ip *)payload; uint32_t size_inner_ip = (inner_ip->ip_hl << 2); if ( payload_len < size_inner_ip ) { LogError("IPIP tunnel header length error: len: %u < size inner IP: %u", payload_len, size_inner_ip); pcap_dev->proc_stat.short_snap++; Free_Node(Node); break; } offset = 0; data = payload; data_len = payload_len; // move IP to tun IP Node->tun_src_addr = Node->src_addr; Node->tun_dst_addr = Node->dst_addr; Node->tun_proto = IPPROTO_IPIP; dbg_printf("IPIP tunnel - inner IP:\n"); // redo proto evaluation goto REDO_IPPROTO; } break; case IPPROTO_GRE: { gre_hdr_t *gre_hdr = (gre_hdr_t *)payload; uint32_t gre_hdr_size = sizeof(gre_hdr_t); // offset points to end of inner IP if ( payload_len < gre_hdr_size ) { LogError("GRE tunnel header length error: len: %u < size GRE hdr: %u", payload_len, gre_hdr_size); pcap_dev->proc_stat.short_snap++; Free_Node(Node); break; } dbg_printf("GRE proto encapsulation: type: 0x%x\n", ethertype); ethertype = ntohs(gre_hdr->type); offset = gre_hdr_size; data = payload; data_len = payload_len; // move IP to tun IP Node->tun_src_addr = Node->src_addr; Node->tun_dst_addr = Node->dst_addr; Node->tun_proto = IPPROTO_GRE; // redo IP proto evaluation goto REDO_LINK; } break; default: // not handled protocol - simply save node Push_Node(NodeList, Node); pcap_dev->proc_stat.unknown++; break; } if ( defragmented ) { free(defragmented); defragmented = NULL; dbg_printf("Defragmented buffer freed for proto %u", proto); } } // End of ProcessPacket
__attribute__((noreturn)) static void *p_packet_thread(void *thread_data) { // argument dispatching p_packet_thread_args_t *args = (p_packet_thread_args_t *)thread_data; pcap_dev_t *pcap_dev = args->pcap_dev; time_t t_win = args->t_win; char *pcap_datadir = args->pcap_datadir; int subdir_index = args->subdir_index; int live = args->live; // locals p_pcap_flush_thread_args_t p_flush_thread_args; pcapfile_t *pcapfile; time_t t_start; int err; dbg_printf("New packet thread[%lu]\n", (long unsigned)args->tid); args->done = 0; args->exit = 0; err = pthread_setspecific( buffer_key, (void *)args ); if ( err ) { LogError("[%lu] pthread_setspecific() error in %s line %d: %s\n", (long unsigned)args->tid, __FILE__, __LINE__, strerror(errno) ); args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); /* NOTREACHED */ } /* start flush and pcap file handler thread */ if ( pcap_datadir ) { // just allocate pcapfile and buffers - we need to share pcapfile pcapfile = OpenNewPcapFile(pcap_dev->handle, NULL, NULL); if ( !pcapfile ) { args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); /* NOTREACHED */ } p_flush_thread_args.done = 0; p_flush_thread_args.exit = 0; p_flush_thread_args.parent = args->tid; p_flush_thread_args.pcap_dev = args->pcap_dev; p_flush_thread_args.subdir_index = subdir_index; p_flush_thread_args.pcap_datadir = pcap_datadir; p_flush_thread_args.pcapfile = pcapfile; err = pthread_create(&p_flush_thread_args.tid, NULL, p_pcap_flush_thread, (void *)&p_flush_thread_args); if ( err ) { LogError("pthread_create() error in %s line %d: %s\n", __FILE__, __LINE__, strerror(errno) ); args->done = 1; args->exit = 255; pthread_kill(args->parent, SIGUSR1); pthread_exit((void *)args); } dbg_printf("Started flush thread[%lu]\n", (long unsigned)p_flush_thread_args.tid); } else { pcapfile = NULL; } err = 0; t_start = 0; while ( 1 ) { struct pcap_pkthdr *hdr; const u_char *data; struct timeval tv; time_t t_clock; int ret; if ( !args->done ) { ret = pcap_next_ex(pcap_dev->handle, &hdr, &data); t_clock = 0; switch (ret) { case 1: { // packet read ok t_clock = hdr->ts.tv_sec; // process packet for flow cache ProcessPacket(args->NodeList, pcap_dev, hdr, data); if ( pcap_datadir ) { // keep the packet if (((t_clock - t_start) >= t_win)) { // first packet or rotate file if ( t_start != 0 ) { RotateFile(pcapfile, t_start, live); } // if first packet - set t_start here t_start = t_clock - (t_clock % t_win); } PcapDump(pcapfile, hdr, data); } } break; case 0: { // live capture idle cycle dbg_printf("pcap_next_ex() read live - timeout\n"); gettimeofday(&tv, NULL); t_clock = tv.tv_sec; if ((t_clock - t_start) >= t_win) { /* rotate file */ if ( t_start ) { // if not first packet, where t_start = 0 struct FlowNode *Node = New_Node(); Node->t_first = tv; Node->t_last = tv; Node->fin = SIGNAL_NODE; Push_Node(args->NodeList, Node); if ( pcap_datadir ) // keep the packet RotateFile(pcapfile, t_start, live); LogInfo("Packet processing stats: Total: %u, Skipped: %u, Unknown: %u, Short snaplen: %u", pcap_dev->proc_stat.packets, pcap_dev->proc_stat.skipped, pcap_dev->proc_stat.unknown, pcap_dev->proc_stat.short_snap); } t_start = t_clock - (t_clock % t_win); memset((void *)&(pcap_dev->proc_stat), 0, sizeof(proc_stat_t)); } } break; case -1: // signal error reading the packet err = 1; LogError("pcap_next_ex() read error: '%s'", pcap_geterr(pcap_dev->handle)); args->done = 1; continue; break; case -2: // End of packet file // signal parent, job is done err = 1; LogInfo("pcap_next_ex() end of file"); args->done = 1; LogInfo("Packet processing stats: Total: %u, Skipped: %u, Unknown: %u, Short snaplen: %u", pcap_dev->proc_stat.packets, pcap_dev->proc_stat.skipped, pcap_dev->proc_stat.unknown, pcap_dev->proc_stat.short_snap); continue; break; default: err = 1; pcap_breakloop(pcap_dev->handle); LogError("Unexpected pcap_next_ex() return value: %i", ret); args->done = 1; continue; } } if ( args->done ) break; } if ( pcap_datadir ) { dbg_printf("Wait for flush thread to complete\n"); pthread_mutex_lock(&pcapfile->m_pbuff); while ( pcapfile->alternate_size ) { pthread_cond_wait(&pcapfile->c_pbuff, &pcapfile->m_pbuff); } pcapfile->t_CloseRename = t_start; pthread_mutex_unlock(&pcapfile->m_pbuff); dbg_printf("Wait done.\n"); LogInfo("Signal flush thread[%lu] to terminate", p_flush_thread_args.tid); SignalThreadTerminate((thread_info_t *)&p_flush_thread_args, &pcapfile->c_pbuff); } if ( err ) pthread_kill(args->parent, SIGUSR1); LogInfo("Packet processing stats: Total: %u, Skipped: %u, Unknown: %u, Short snaplen: %u", pcap_dev->proc_stat.packets, pcap_dev->proc_stat.skipped, pcap_dev->proc_stat.unknown, pcap_dev->proc_stat.short_snap); LogInfo("Terminating packet dumping: exit: %i", args->exit); dbg_printf("End packet thread[%lu]\n", (long unsigned)args->tid); pthread_exit((void *)args); /* NOTREACHED */ } /* End of p_packet_thread */