diff options
Diffstat (limited to 'nDPId.c')
-rw-r--r-- | nDPId.c | 377 |
1 files changed, 268 insertions, 109 deletions
@@ -59,6 +59,9 @@ #define DLT_DSA_TAG_EDSA 285 #endif +#define PPP_P_IP 0x0021 +#define PPP_P_IPV6 0x0057 + #define NDPI_VERSION_CHECK ((NDPI_MAJOR == 4 && NDPI_MINOR < 9) || NDPI_MAJOR < 4) #if NDPI_VERSION_CHECK @@ -384,18 +387,17 @@ enum error_event PACKET_TYPE_UNKNOWN, PACKET_HEADER_INVALID, IP4_PACKET_TOO_SHORT, - IP4_SIZE_SMALLER_THAN_HEADER, IP4_L4_PAYLOAD_DETECTION_FAILED, - IP6_PACKET_TOO_SHORT, // 10 - IP6_SIZE_SMALLER_THAN_HEADER, - IP6_L4_PAYLOAD_DETECTION_FAILED, + IP6_PACKET_TOO_SHORT, + IP6_L4_PAYLOAD_DETECTION_FAILED, // 10 + TUNNEL_DECODE_FAILED, TCP_PACKET_TOO_SHORT, UDP_PACKET_TOO_SHORT, CAPTURE_SIZE_SMALLER_THAN_PACKET_SIZE, MAX_FLOW_TO_TRACK, - FLOW_MEMORY_ALLOCATION_FAILED, + FLOW_MEMORY_ALLOCATION_FAILED, // 16 - ERROR_EVENT_COUNT // 17 + ERROR_EVENT_COUNT }; enum daemon_event @@ -435,11 +437,10 @@ static char const * const error_event_name_table[ERROR_EVENT_COUNT] = { [PACKET_TYPE_UNKNOWN] = "Unknown packet type", [PACKET_HEADER_INVALID] = "Packet header invalid", [IP4_PACKET_TOO_SHORT] = "IP4 packet too short", - [IP4_SIZE_SMALLER_THAN_HEADER] = "Packet smaller than IP4 header", [IP4_L4_PAYLOAD_DETECTION_FAILED] = "nDPI IPv4/L4 payload detection failed", [IP6_PACKET_TOO_SHORT] = "IP6 packet too short", - [IP6_SIZE_SMALLER_THAN_HEADER] = "Packet smaller than IP6 header", [IP6_L4_PAYLOAD_DETECTION_FAILED] = "nDPI IPv6/L4 payload detection failed", + [TUNNEL_DECODE_FAILED] = "Tunnel decoding failed", [TCP_PACKET_TOO_SHORT] = "TCP packet smaller than expected", [UDP_PACKET_TOO_SHORT] = "UDP packet smaller than expected", [CAPTURE_SIZE_SMALLER_THAN_PACKET_SIZE] = "Captured packet size is smaller than expected packet size", @@ -482,6 +483,7 @@ static struct struct cmdarg config_file; struct cmdarg pcap_file_or_interface; struct cmdarg bpf_str; + struct cmdarg decode_tunnel; struct cmdarg pidfile; struct cmdarg user; struct cmdarg group; @@ -535,6 +537,7 @@ static struct } nDPId_options = {.config_file = CMDARG_STR(NULL), .pcap_file_or_interface = CMDARG_STR(NULL), .bpf_str = CMDARG_STR(NULL), + .decode_tunnel = CMDARG_BOOL(0), .pidfile = CMDARG_STR(nDPId_PIDFILE), .user = CMDARG_STR(DEFAULT_CHUSER), .group = CMDARG_STR(NULL), @@ -596,6 +599,7 @@ static struct .error_event_threshold_time = CMDARG_ULL(nDPId_ERROR_EVENT_THRESHOLD_TIME)}; struct confopt general_config_map[] = {CONFOPT("netif", &nDPId_options.pcap_file_or_interface), CONFOPT("bpf", &nDPId_options.bpf_str), + CONFOPT("decode-tunnel", &nDPId_options.decode_tunnel), CONFOPT("pidfile", &nDPId_options.pidfile), CONFOPT("user", &nDPId_options.user), CONFOPT("group", &nDPId_options.group), @@ -3529,12 +3533,12 @@ static uint32_t calculate_ndpi_flow_struct_hash(struct ndpi_flow_struct const * // future) hash += ndpi_flow->confidence; - const size_t protocol_bitmask_size = sizeof(ndpi_flow->excluded_protocol_bitmask.fds_bits) / - sizeof(ndpi_flow->excluded_protocol_bitmask.fds_bits[0]); + const size_t protocol_bitmask_size = sizeof(ndpi_flow->excluded_dissectors_bitmask.fds_bits) / + sizeof(ndpi_flow->excluded_dissectors_bitmask.fds_bits[0]); for (size_t i = 0; i < protocol_bitmask_size; ++i) { - hash += ndpi_flow->excluded_protocol_bitmask.fds_bits[i]; - hash += ndpi_flow->excluded_protocol_bitmask.fds_bits[i]; + hash += ndpi_flow->excluded_dissectors_bitmask.fds_bits[i]; + hash += ndpi_flow->excluded_dissectors_bitmask.fds_bits[i]; } size_t host_server_name_len = @@ -3590,7 +3594,21 @@ static int process_datalink_layer(struct nDPId_reader_thread * const reader_thre case DLT_NULL: { /* DLT header values can be stored as big or little endian. */ - + if (header->caplen < sizeof(uint32_t)) + { + if (is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, + PACKET_TOO_SHORT, + "%s%u %s%zu", + "size", + header->caplen, + "expected", + sizeof(uint32_t)); + jsonize_packet_event(reader_thread, header, packet, 0, 0, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + return 1; + } uint32_t dlt_hdr = *((uint32_t const *)&packet[eth_offset]); if (dlt_hdr == 0x02000000 || dlt_hdr == 0x02) @@ -3862,54 +3880,8 @@ static int process_datalink_layer(struct nDPId_reader_thread * const reader_thre switch (*layer3_type) { case ETH_P_IP: /* IPv4 */ - if (header->caplen < sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_iphdr)) - { - if (is_error_event_threshold(reader_thread->workflow) == 0) - { - jsonize_error_eventf(reader_thread, - IP4_PACKET_TOO_SHORT, - "%s%u %s%zu", - "size", - header->caplen, - "expected", - sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_iphdr)); - jsonize_packet_event(reader_thread, - header, - packet, - *layer3_type, - *ip_offset, - 0, - 0, - NULL, - PACKET_EVENT_PAYLOAD); - } - return 1; - } break; case ETH_P_IPV6: /* IPV6 */ - if (header->caplen < sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_ipv6hdr)) - { - if (is_error_event_threshold(reader_thread->workflow) == 0) - { - jsonize_error_eventf(reader_thread, - IP6_PACKET_TOO_SHORT, - "%s%u %s%zu", - "size", - header->caplen, - "expected", - sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_ipv6hdr)); - jsonize_packet_event(reader_thread, - header, - packet, - *layer3_type, - *ip_offset, - 0, - 0, - NULL, - PACKET_EVENT_PAYLOAD); - } - return 1; - } break; case ETHERTYPE_PAE: /* 802.1X Authentication */ return 1; @@ -4031,6 +4003,119 @@ static int distribute_single_packet(struct nDPId_reader_thread * const reader_th reader_thread->array_index); } +/* See libnDPI: `ndpi_is_valid_gre_tunnel()` in example/reader_util.c */ +static uint32_t is_valid_gre_tunnel(struct pcap_pkthdr const * const header, + uint8_t const * const packet, + uint8_t const * const l4_ptr) +{ + + if (header->caplen < (l4_ptr - packet) + sizeof(struct ndpi_gre_basehdr)) + { + return 0; /* Too short for GRE header*/ + } + uint32_t offset = (l4_ptr - packet); + struct ndpi_gre_basehdr * grehdr = (struct ndpi_gre_basehdr *)&packet[offset]; + offset += sizeof(struct ndpi_gre_basehdr); + + /* + * The GRE flags are encoded in the first two octets. Bit 0 is the + * most significant bit, bit 15 is the least significant bit. Bits + * 13 through 15 are reserved for the Version field. Bits 9 through + * 12 are reserved for future use and MUST be transmitted as zero. + */ + if (NDPI_GRE_IS_FLAGS(grehdr->flags)) + { + return 0; + } + if (NDPI_GRE_IS_REC(grehdr->flags)) + { + return 0; + } + + /* GRE rfc 2890 that update 1701 */ + if (NDPI_GRE_IS_VERSION_0(grehdr->flags)) + { + if (NDPI_GRE_IS_CSUM(grehdr->flags)) + { + if (header->caplen < offset + 4) + { + return 0; + } + /* checksum field and offset field */ + offset += 4; + } + if (NDPI_GRE_IS_KEY(grehdr->flags)) + { + if (header->caplen < offset + 4) + { + return 0; + } + offset += 4; + } + if (NDPI_GRE_IS_SEQ(grehdr->flags)) + { + if (header->caplen < offset + 4) + { + return 0; + } + offset += 4; + } + } + else if (NDPI_GRE_IS_VERSION_1(grehdr->flags)) + { + /* rfc-2637 section 4.1 enhanced gre */ + if (NDPI_GRE_IS_CSUM(grehdr->flags)) + { + return 0; + } + if (NDPI_GRE_IS_ROUTING(grehdr->flags)) + { + return 0; + } + if (!NDPI_GRE_IS_KEY(grehdr->flags)) + { + return 0; + } + if (NDPI_GRE_IS_STRICT(grehdr->flags)) + { + return 0; + } + if (grehdr->protocol != NDPI_GRE_PROTO_PPP) + { + return 0; + } + /* key field */ + if (header->caplen < offset + 4) + { + return 0; + } + offset += 4; + if (NDPI_GRE_IS_SEQ(grehdr->flags)) + { + if (header->caplen < offset + 4) + { + return 0; + } + offset += 4; + } + if (NDPI_GRE_IS_ACK(grehdr->flags)) + { + if (header->caplen < offset + 4) + { + return 0; + } + offset += 4; + } + } + else + { + /* support only ver 0, 1 */ + return 0; + } + + return offset; +} + static void ndpi_process_packet(uint8_t * const args, struct pcap_pkthdr const * const header, uint8_t const * const packet) @@ -4092,64 +4177,74 @@ static void ndpi_process_packet(uint8_t * const args, return; } +process_layer3_again: if (type == ETH_P_IP) { ip = (struct ndpi_iphdr *)&packet[ip_offset]; ip6 = NULL; - } - else if (type == ETH_P_IPV6) - { - ip = NULL; - ip6 = (struct ndpi_ipv6hdr *)&packet[ip_offset]; - } - else - { - if (distribute_single_packet(reader_thread) != 0 && is_error_event_threshold(reader_thread->workflow) == 0) - { - jsonize_error_eventf(reader_thread, UNKNOWN_L3_PROTOCOL, "%s%u", "protocol", type); - jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); - } - return; - } - ip_size = header->caplen - ip_offset; - - if (type == ETH_P_IP && header->caplen >= ip_offset) - { - if (header->caplen < header->len) + if (header->caplen < ip_offset + sizeof(*ip)) { if (distribute_single_packet(reader_thread) != 0 && is_error_event_threshold(reader_thread->workflow) == 0) { jsonize_error_eventf(reader_thread, - CAPTURE_SIZE_SMALLER_THAN_PACKET_SIZE, - "%s%u %s%u", + IP4_PACKET_TOO_SHORT, + "%s%u %s%zu", "size", header->caplen, "expected", - header->len); + sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_iphdr)); jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); } + return; } } - - /* process layer3 e.g. IPv4 / IPv6 */ - if (ip != NULL && ip->version == 4) + else if (type == ETH_P_IPV6) { - if (ip_size < sizeof(*ip)) + ip = NULL; + ip6 = (struct ndpi_ipv6hdr *)&packet[ip_offset]; + if (header->caplen < ip_offset + sizeof(*ip6)) { if (distribute_single_packet(reader_thread) != 0 && is_error_event_threshold(reader_thread->workflow) == 0) { jsonize_error_eventf(reader_thread, - IP4_SIZE_SMALLER_THAN_HEADER, + IP4_PACKET_TOO_SHORT, "%s%u %s%zu", "size", - ip_size, + header->caplen, "expected", - sizeof(*ip)); + sizeof(struct ndpi_ethhdr) + sizeof(struct ndpi_iphdr)); jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); } return; } + } + else + { + if (distribute_single_packet(reader_thread) != 0 && is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, UNKNOWN_L3_PROTOCOL, "%s%u", "protocol", type); + jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + return; + } + ip_size = header->caplen - ip_offset; + + if (header->caplen >= ip_offset && header->caplen < header->len && distribute_single_packet(reader_thread) != 0 && + is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, + CAPTURE_SIZE_SMALLER_THAN_PACKET_SIZE, + "%s%u %s%u", + "size", + header->caplen, + "expected", + header->len); + jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + /* process layer3 e.g. IPv4 / IPv6 */ + if (ip != NULL && ip->version == 4) + { flow_basic.l3_type = L3_IP; if (ndpi_detection_get_l4( @@ -4167,26 +4262,10 @@ static void ndpi_process_packet(uint8_t * const args, flow_basic.src.v4.ip = ip->saddr; flow_basic.dst.v4.ip = ip->daddr; uint32_t min_addr = (flow_basic.src.v4.ip > flow_basic.dst.v4.ip ? flow_basic.dst.v4.ip : flow_basic.src.v4.ip); - thread_index = min_addr + ip->protocol; + thread_index += min_addr + ip->protocol; } else if (ip6 != NULL) { - if (ip_size < sizeof(ip6->ip6_hdr)) - { - if (distribute_single_packet(reader_thread) != 0 && is_error_event_threshold(reader_thread->workflow) == 0) - { - jsonize_error_eventf(reader_thread, - IP6_SIZE_SMALLER_THAN_HEADER, - "%s%u %s%zu", - "size", - ip_size, - "expected", - sizeof(ip6->ip6_hdr)); - jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); - } - return; - } - flow_basic.l3_type = L3_IP6; if (ndpi_detection_get_l4( (uint8_t *)ip6, ip_size, &l4_ptr, &l4_len, &flow_basic.l4_protocol, NDPI_DETECTION_ONLY_IPV6) != 0) @@ -4217,7 +4296,7 @@ static void ndpi_process_packet(uint8_t * const args, min_addr[0] = flow_basic.src.v6.ip[0]; min_addr[1] = flow_basic.src.v6.ip[1]; } - thread_index = min_addr[0] + min_addr[1] + ip6->ip6_hdr.ip6_un1_nxt; + thread_index += min_addr[0] + min_addr[1] + ip6->ip6_hdr.ip6_un1_nxt; } else { @@ -4229,6 +4308,79 @@ static void ndpi_process_packet(uint8_t * const args, return; } + /* process intermediate protocols i.e. layer4 tunnel protocols */ + if (IS_CMDARG_SET(nDPId_options.decode_tunnel) != 0 && flow_basic.l4_protocol == IPPROTO_GRE) + { + uint32_t const offset = is_valid_gre_tunnel(header, packet, l4_ptr); + + if (offset == 0) + { + if (is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, TUNNEL_DECODE_FAILED, "%s%u", "protocol", flow_basic.l4_protocol); + jsonize_packet_event(reader_thread, header, packet, type, ip_offset, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + return; + } + else + { + struct ndpi_gre_basehdr const * const grehdr = (struct ndpi_gre_basehdr const *)l4_ptr; + + if (grehdr->protocol == ntohs(ETH_P_IP) || grehdr->protocol == ntohs(ETH_P_IPV6)) + { + type = ntohs(grehdr->protocol); + ip_offset = offset; + goto process_layer3_again; + } + else if (grehdr->protocol == NDPI_GRE_PROTO_PPP) + { + /* Point to Point Protocol */ + if (header->caplen < offset + sizeof(struct ndpi_chdlc)) + { + if (is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, + TUNNEL_DECODE_FAILED, + "%s%u %s%u %s%zu", + "protocol", + flow_basic.l4_protocol, + "size", + header->caplen, + "expected", + offset + sizeof(struct ndpi_chdlc)); + jsonize_packet_event(reader_thread, header, packet, 0, 0, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + return; + } + + struct ndpi_chdlc const * const chdlc = (struct ndpi_chdlc const *)&packet[offset]; + type = ntohs(chdlc->proto_code); + switch (type) + { + case PPP_P_IP: + type = ETH_P_IP; + break; + case PPP_P_IPV6: + type = ETH_P_IPV6; + break; + default: + if (is_error_event_threshold(reader_thread->workflow) == 0) + { + jsonize_error_eventf(reader_thread, TUNNEL_DECODE_FAILED, "%s%u", "ppp-protocol", type); + jsonize_packet_event(reader_thread, header, packet, 0, 0, 0, 0, NULL, PACKET_EVENT_PAYLOAD); + } + return; + } + ip_offset = offset + sizeof(*chdlc); + goto process_layer3_again; + } + else + { + // TODO: Check Layer1 / Layer2 again? + } + } + } + /* process layer4 e.g. TCP / UDP */ if (flow_basic.l4_protocol == IPPROTO_TCP) { @@ -4666,7 +4818,9 @@ static void ndpi_process_packet(uint8_t * const args, flow_to_process->flow_extended.packets_processed[FD_DST2SRC] == 1) { - ndpi_unset_risk(&flow_to_process->info.detection_data->flow, NDPI_UNIDIRECTIONAL_TRAFFIC); + ndpi_unset_risk(workflow->ndpi_struct, + &flow_to_process->info.detection_data->flow, + NDPI_UNIDIRECTIONAL_TRAFFIC); } jsonize_flow_detection_event(reader_thread, flow_to_process, FLOW_EVENT_DETECTED); flow_to_process->info.detection_data->last_ndpi_flow_struct_hash = @@ -5401,6 +5555,8 @@ static void print_usage(char const * const arg0) "\t \tDefault: disabled\n" "\t-B\tSet an optional PCAP filter string. (BPF format)\n" "\t \tDefault: empty\n" + "\t-t\tEnable tunnel decapsulation. Supported protocols: GRE\n" + "\t \tDefault: disabled\n" "\t-l\tLog all messages to stderr.\n" "\t \tDefault: disabled\n" "\t-L\tLog all messages to a log file.\n" @@ -5531,7 +5687,7 @@ static int nDPId_parse_options(int argc, char ** argv) { int opt; - while ((opt = getopt(argc, argv, "f:i:rIEB:lL:c:k:K:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1) + while ((opt = getopt(argc, argv, "f:i:rIEB:tlL:c:k:K:edp:u:g:R:P:C:J:S:a:U:Azo:vh")) != -1) { switch (opt) { @@ -5558,6 +5714,9 @@ static int nDPId_parse_options(int argc, char ** argv) case 'B': set_cmdarg_string(&nDPId_options.bpf_str, optarg); break; + case 't': + set_cmdarg_boolean(&nDPId_options.decode_tunnel, 1); + break; case 'l': enable_console_logger(); break; |