/* * Copyright (c) 2017-2024 OARC, Inc. * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden * All rights reserved. * * This file is part of PacketQ. * * PacketQ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PacketQ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PacketQ. If not, see . */ #include "packet_handler.h" #include "dns.h" #include "icmp.h" #include "output.h" #include "packetq.h" #include "sql.h" #include "tcp.h" #include #include namespace packetq { Payload g_payload; Output g_output; Str_conv converter; class Fragments { private: Fragments& operator=(const Fragments& other); Fragments(Fragments&& other) noexcept; Fragments const& operator=(Fragments&& other); public: class Range { public: bool operator<(const Range& r) const { if (begin < r.begin) return true; return false; } Range(int s, int e1) { begin = s; endp1 = e1; } int begin; int endp1; }; Fragments(const Fragments& f) { // printf( "copy Fragments\n" ); m_first = f.m_first; m_complete = f.m_complete; m_frags = f.m_frags; } Fragments() { m_complete = 0; m_frags = 0; } ~Fragments() { // printf( "delete Fragments\n", m_first.offset ); } bool add(IP_header& head, unsigned char* data, int len) { if (head.offset == 0) m_first = head; if (head.offset + len > 0x10000 || len < 0) return false; m_frags++; if (head.fragments == 0) m_complete = head.offset + len; bool complete = add_range(head.offset, head.offset + len); memcpy((void*)&m_buffer[head.offset], data, len); if (complete) { m_complete = head.offset + len; m_first.fragments = m_frags; return true; } return false; } bool add_range(int start, int end) { m_ranges.push_back(Range(start, end)); m_ranges.sort(); bool merged = true; // this is algorithmically horrid (hope there wont be tonnes of fragments) while (merged) { merged = false; auto it = m_ranges.begin(); auto last = it; if (last == m_ranges.end()) break; it++; for (; it != m_ranges.end(); it++) { if (last->endp1 == it->begin) { merged = true; last->endp1 = it->endp1; m_ranges.erase(it); break; } } } if (m_ranges.size() == 1 && m_ranges.begin()->endp1 == m_complete && m_ranges.begin()->begin == 0) return true; return false; } std::list m_ranges; int m_complete; int m_frags; IP_header m_first; unsigned char m_buffer[0x10000]; }; class Ident { public: bool operator<(const Ident& rhs) const { if (m_ident < rhs.m_ident) return true; if (m_ident > rhs.m_ident) return false; for (int i = 0; i < 4; i++) { if (m_src_ip.__in6_u.__u6_addr32[i] < rhs.m_src_ip.__in6_u.__u6_addr32[i]) return true; if (m_src_ip.__in6_u.__u6_addr32[i] > rhs.m_src_ip.__in6_u.__u6_addr32[i]) return false; } if (m_protocol < rhs.m_protocol) return true; if (m_protocol > rhs.m_protocol) return false; for (int i = 0; i < 4; i++) { if (m_dst_ip.__in6_u.__u6_addr32[i] < rhs.m_dst_ip.__in6_u.__u6_addr32[i]) return true; if (m_dst_ip.__in6_u.__u6_addr32[i] > rhs.m_dst_ip.__in6_u.__u6_addr32[i]) return false; } return false; } in6addr_t m_dst_ip; in6addr_t m_src_ip; int m_ident; int m_protocol; }; class FragmentHandler { public: void add_fragment(IP_header& head, unsigned char* data, int len, Packet& p) { Ident i; i.m_src_ip = head.src_ip; i.m_dst_ip = head.dst_ip; i.m_protocol = head.proto; i.m_ident = head.ident; Fragments& frag = m_fragments[i]; if (frag.add(head, data, len)) { p.m_ip_header = frag.m_first; p.parse_transport(frag.m_buffer, frag.m_complete); m_fragments.erase(i); } } std::map m_fragments; }; FragmentHandler m_fraghandler; void IP_header::reset() { memset(&src_ip, 0, sizeof(in6addr_t)); memset(&dst_ip, 0, sizeof(in6addr_t)); fragments = 0; offset = 0; ident = 0; s = 0; us = 0; ethertype = 0; src_port = 0; dst_port = 0; proto = 0; ip_ttl = 0; ip_version = 0; id = 0; length = 0; } int IP_header::decode(unsigned char* data, int itype, int i_id) { reset(); ethertype = itype; id = i_id; int len = 0; // ether frame done (ignored mac's) // ip ip_version = data[0] >> 4; proto = 0; if (ip_version == 4) { if (ethertype == 0) ethertype = 0x800; int header_len = (data[0] & 0xf) * 4; proto = data[9]; ip_ttl = data[8]; src_ip.__in6_u.__u6_addr32[3] = get_int(&data[12]); dst_ip.__in6_u.__u6_addr32[3] = get_int(&data[16]); int totallen = get_short(&data[2]); length = totallen - header_len; int flags = get_short(&data[6]); offset = (flags & 0x1fff) << 3; flags >>= 13; if (flags & 1) fragments = 1; data += header_len; len += header_len; } else if (ip_version == 6) { if (ethertype == 0) ethertype = 0x86DD; proto = data[6]; ip_ttl = data[7]; src_ip.__in6_u.__u6_addr32[3] = get_int(&data[8]); src_ip.__in6_u.__u6_addr32[2] = get_int(&data[12]); src_ip.__in6_u.__u6_addr32[1] = get_int(&data[16]); src_ip.__in6_u.__u6_addr32[0] = get_int(&data[20]); dst_ip.__in6_u.__u6_addr32[3] = get_int(&data[24]); dst_ip.__in6_u.__u6_addr32[2] = get_int(&data[28]); dst_ip.__in6_u.__u6_addr32[1] = get_int(&data[32]); dst_ip.__in6_u.__u6_addr32[0] = get_int(&data[36]); data += 40; len += 40; // process next headers - NOTE: there are 6 not 4 while (proto == 0 || proto == 43 || proto == 44 || proto == 60) { if (proto == 44) { offset = get_short(&data[2]) & 0xfff8; fragments = get_short(&data[2]) & 1; } proto = data[0]; int hdr_len = data[1] + 8; data += hdr_len; len += hdr_len; } } else { return 0; } return len; } std::vector packet_handlers; Packet::ParseResult Packet::parse(Packet_handler* handler, const std::vector& columns, Row& destination_row, bool sample) { bool base_layers_parsed; if (m_link_layer_type == 1) base_layers_parsed = parse_ethernet(); else if (m_link_layer_type == 113) base_layers_parsed = parse_sll(); else base_layers_parsed = parse_ip(m_data, m_len, 0); if (!base_layers_parsed) return ERROR; // do the application layer return handler->parse(*this, columns, destination_row, sample); } bool Packet::parse_ethernet() { unsigned char* data = m_data; int len = m_len; if (len < 14) return false; // check for etherframe size int ethertype = data[13] | (data[12] << 8); if (ethertype == 0x8100) { if (len < 18) return false; // check for etherframe size + VLAN tag ethertype = data[17] | (data[16] << 8); data += 18; len -= 18; } else { data += 14; len -= 14; } return parse_ip(data, len, ethertype); } bool Packet::parse_sll() { unsigned char* data = m_data; int len = m_len; if (len < 16) return false; // check for LINUX_SLL size int ethertype = data[15] | (data[14] << 8); if (ethertype == 0x8100) { if (len < 20) return false; // check for etherframe size + VLAN tag ethertype = data[19] | (data[18] << 8); data += 20; len -= 20; } else { data += 16; len -= 16; } return parse_ip(data, len, ethertype); } bool Packet::parse_ip(unsigned char* data, int len, int ethertype) { if (len < 5 * 4) return false; // check for etherframe size + ipv4 header int consumed = m_ip_header.decode(data, ethertype, m_id); m_ip_header.s = m_s; m_ip_header.us = m_us; data += consumed; len -= consumed; if (m_ip_header.fragments > 0 || m_ip_header.offset > 0) { m_fraghandler.add_fragment(m_ip_header, data, len, *this); return false; } return parse_transport(data, len); } bool Packet::parse_transport(unsigned char* data, int len) { // tcp/udp if (m_ip_header.proto == IPPROTO_TCP) { if (len < 14) return false; m_ip_header.src_port = get_short(data); m_ip_header.dst_port = get_short(&data[2]); int seq = get_int(&data[4]); int ack = get_int(&data[8]); int dataoffs = 4 * (data[12] >> 4); unsigned char bits = data[13]; char syn = (bits >> 1) & 1; char fin = (bits >> 0) & 1; char rst = (bits >> 2) & 1; // get the assembled TCP packet and remove the individual segments. data += dataoffs; len -= dataoffs; if (len < 0) { fprintf(stderr, "Warning: Found TCP packet with bad length\n"); return false; } unsigned int rest = len; data = assemble_tcp(g_payload, &m_ip_header.src_ip, &m_ip_header.dst_ip, m_ip_header.src_port, m_ip_header.dst_port, &rest, seq, data, rest, syn, fin, rst, ack); len = rest; } else if (m_ip_header.proto == IPPROTO_UDP) { if (len < 4) return false; m_ip_header.src_port = get_short(data); m_ip_header.dst_port = get_short(&data[2]); data += 8; len -= 8; if (len < 0) { fprintf(stderr, "Warning: Found UDP packet with bad length\n"); return false; } } if (data) { m_data = data; m_len = len; return true; } return false; } Table* Packet_handler::create_table(const std::vector& columns) { Table* table = g_db.create_table(table_name); for (auto i = packet_columns.begin(); i != packet_columns.end(); ++i) if (std::find(columns.begin(), columns.end(), i->id) != columns.end()) table->add_column(i->name, i->type, i->id); on_table_created(table, columns); return table; } void Packet_handler::add_packet_column(const char* name, const char* description, Coltype::Type type, int id) { Packet_column c; c.name = name; c.description = description; c.id = id; c.type = type; packet_columns.push_back(c); } void init_packet_handlers(bool escape_dnsnames) { packet_handlers.push_back(new Parse_dns(escape_dnsnames)); packet_handlers.push_back(new Parse_icmp()); } void destroy_packet_handlers() { for (auto i = packet_handlers.begin(); i != packet_handlers.end(); ++i) delete *i; packet_handlers.clear(); } Packet_handler* get_packet_handler(std::string table_name) { for (auto i = packet_handlers.begin(); i != packet_handlers.end(); ++i) { if (table_name == (*i)->table_name) return *i; } return 0; } void IP_header_to_table::add_packet_columns(Packet_handler& packet_handler) { packet_handler.add_packet_column("id", "ID", Coltype::_int, COLUMN_ID); packet_handler.add_packet_column("s", "Seconds", Coltype::_int, COLUMN_S); packet_handler.add_packet_column("us", "Milliseconds", Coltype::_int, COLUMN_US); packet_handler.add_packet_column("ether_type", "", Coltype::_int, COLUMN_ETHER_TYPE); packet_handler.add_packet_column("src_port", "", Coltype::_int, COLUMN_SRC_PORT); // this is really tcp/udp but accidents do happen packet_handler.add_packet_column("dst_port", "", Coltype::_int, COLUMN_DST_PORT); packet_handler.add_packet_column("src_addr", "", Coltype::_text, COLUMN_SRC_ADDR); packet_handler.add_packet_column("dst_addr", "", Coltype::_text, COLUMN_DST_ADDR); packet_handler.add_packet_column("protocol", "", Coltype::_int, COLUMN_PROTOCOL); packet_handler.add_packet_column("ip_ttl", "", Coltype::_int, COLUMN_IP_TTL); packet_handler.add_packet_column("ip_version", "", Coltype::_int, COLUMN_IP_VERSION); packet_handler.add_packet_column("fragments", "", Coltype::_int, COLUMN_FRAGMENTS); } void IP_header_to_table::on_table_created(Table* table, const std::vector& columns) { acc_src_addr = table->get_accessor("src_addr"); acc_dst_addr = table->get_accessor("dst_addr"); acc_ether_type = table->get_accessor("ether_type"); acc_protocol = table->get_accessor("protocol"); acc_ip_ttl = table->get_accessor("ip_ttl"); acc_ip_version = table->get_accessor("ip_version"); acc_src_port = table->get_accessor("src_port"); acc_dst_port = table->get_accessor("dst_port"); acc_s = table->get_accessor("s"); acc_us = table->get_accessor("us"); acc_id = table->get_accessor("id"); acc_fragments = table->get_accessor("fragments"); } void IP_header_to_table::assign(Row* row, IP_header* head, const std::vector& columns) { if (!head) return; for (auto i = columns.begin(), end = columns.end(); i != end; ++i) { switch (*i) { case COLUMN_ID: acc_id.value(row) = head->id; break; case COLUMN_S: acc_s.value(row) = head->s; break; case COLUMN_US: acc_us.value(row) = head->us; break; case COLUMN_ETHER_TYPE: acc_ether_type.value(row) = head->ethertype; break; case COLUMN_PROTOCOL: acc_protocol.value(row) = head->proto; break; case COLUMN_IP_TTL: acc_ip_ttl.value(row) = head->ip_ttl; break; case COLUMN_IP_VERSION: acc_ip_version.value(row) = head->ip_version; break; case COLUMN_SRC_PORT: acc_src_port.value(row) = head->src_port; break; case COLUMN_DST_PORT: acc_dst_port.value(row) = head->dst_port; break; case COLUMN_FRAGMENTS: acc_fragments.value(row) = head->fragments; break; case COLUMN_SRC_ADDR: if (head->ethertype == 2048) acc_src_addr.value(row) = v4_addr2str(head->src_ip); else acc_src_addr.value(row) = v6_addr2str(head->src_ip); break; case COLUMN_DST_ADDR: if (head->ethertype == 2048) acc_dst_addr.value(row) = v4_addr2str(head->dst_ip); else acc_dst_addr.value(row) = v6_addr2str(head->dst_ip); break; } } } RefCountString* v4_addr2str(in6addr_t& addr) { converter.reset(); converter.add_attr_ipv4(addr.__in6_u.__u6_addr32[3]); return RefCountString::construct(converter.get()); } RefCountString* v6_addr2str(in6addr_t& addr) { converter.reset(); converter.add_attr_ipv6(&addr.__in6_u.__u6_addr8[0]); return RefCountString::construct(converter.get()); } } // namespace packetq