summaryrefslogtreecommitdiffstats
path: root/src/packet_handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/packet_handler.cpp')
-rw-r--r--src/packet_handler.cpp566
1 files changed, 566 insertions, 0 deletions
diff --git a/src/packet_handler.cpp b/src/packet_handler.cpp
new file mode 100644
index 0000000..df21e9f
--- /dev/null
+++ b/src/packet_handler.cpp
@@ -0,0 +1,566 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "packet_handler.h"
+
+#include "dns.h"
+#include "icmp.h"
+#include "output.h"
+#include "packetq.h"
+#include "sql.h"
+#include "tcp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+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<Range> 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<Ident, Fragments> 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_handler*> packet_handlers;
+
+Packet::ParseResult Packet::parse(Packet_handler* handler, const std::vector<int>& 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<int>& 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<int>& columns)
+{
+ acc_src_addr = table->get_accessor<text_column>("src_addr");
+ acc_dst_addr = table->get_accessor<text_column>("dst_addr");
+ acc_ether_type = table->get_accessor<int_column>("ether_type");
+ acc_protocol = table->get_accessor<int_column>("protocol");
+ acc_ip_ttl = table->get_accessor<int_column>("ip_ttl");
+ acc_ip_version = table->get_accessor<int_column>("ip_version");
+ acc_src_port = table->get_accessor<int_column>("src_port");
+ acc_dst_port = table->get_accessor<int_column>("dst_port");
+ acc_s = table->get_accessor<int_column>("s");
+ acc_us = table->get_accessor<int_column>("us");
+ acc_id = table->get_accessor<int_column>("id");
+ acc_fragments = table->get_accessor<int_column>("fragments");
+}
+
+void IP_header_to_table::assign(Row* row, IP_header* head, const std::vector<int>& 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