/* * ndpi_util.c * * Copyright (C) 2011-17 - ntop.org * * This file is part of nDPI, an open source deep packet inspection * library based on the OpenDPI and PACE technology by ipoque GmbH * * nDPI is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * nDPI 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with nDPI. If not, see . * */ #include #ifdef WIN32 #include /* winsock.h is included automatically */ #include #include #else #include #include #endif #ifndef ETH_P_IP #define ETH_P_IP 0x0800 /* IPv4 */ #endif #ifndef ETH_P_IPv6 #define ETH_P_IPV6 0x86dd /* IPv6 */ #endif #define SLARP 0x8035 /* Cisco Slarp */ #define CISCO_D_PROTO 0x2000 /* Cisco Discovery Protocol */ #define VLAN 0x8100 #define MPLS_UNI 0x8847 #define MPLS_MULTI 0x8848 #define PPPoE 0x8864 #define SNAP 0xaa #define BSTP 0x42 /* Bridge Spanning Tree Protocol */ /* mask for FCF */ #define WIFI_DATA 0x2 /* 0000 0010 */ #define FCF_TYPE(fc) (((fc) >> 2) & 0x3) /* 0000 0011 = 0x3 */ #define FCF_SUBTYPE(fc) (((fc) >> 4) & 0xF) /* 0000 1111 = 0xF */ #define FCF_TO_DS(fc) ((fc) & 0x0100) #define FCF_FROM_DS(fc) ((fc) & 0x0200) /* mask for Bad FCF presence */ #define BAD_FCS 0x50 /* 0101 0000 */ #define GTP_U_V1_PORT 2152 #define TZSP_PORT 37008 #ifndef DLT_LINUX_SLL #define DLT_LINUX_SLL 113 #endif #include "ndpi_main.h" #include "ndpi_util.h" /* ***************************************************** */ void ndpi_free_flow_info_half(struct ndpi_flow_info *flow) { if(flow->ndpi_flow) { ndpi_flow_free(flow->ndpi_flow); flow->ndpi_flow = NULL; } if(flow->src_id) { ndpi_free(flow->src_id); flow->src_id = NULL; } if(flow->dst_id) { ndpi_free(flow->dst_id); flow->dst_id = NULL; } } /* ***************************************************** */ extern u_int32_t current_ndpi_memory, max_ndpi_memory; /** * @brief malloc wrapper function */ static void *malloc_wrapper(size_t size) { current_ndpi_memory += size; if(current_ndpi_memory > max_ndpi_memory) max_ndpi_memory = current_ndpi_memory; return malloc(size); } /* ***************************************************** */ /** * @brief free wrapper function */ static void free_wrapper(void *freeable) { free(freeable); } /* ***************************************************** */ struct ndpi_workflow * ndpi_workflow_init(const struct ndpi_workflow_prefs * prefs, pcap_t * pcap_handle) { set_ndpi_malloc(malloc_wrapper), set_ndpi_free(free_wrapper); set_ndpi_flow_malloc(NULL), set_ndpi_flow_free(NULL); /* TODO: just needed here to init ndpi malloc wrapper */ struct ndpi_detection_module_struct * module = ndpi_init_detection_module(); struct ndpi_workflow * workflow = ndpi_calloc(1, sizeof(struct ndpi_workflow)); workflow->pcap_handle = pcap_handle; workflow->prefs = *prefs; workflow->ndpi_struct = module; if(workflow->ndpi_struct == NULL) { NDPI_LOG(0, NULL, NDPI_LOG_ERROR, "global structure initialization failed\n"); exit(-1); } workflow->ndpi_flows_root = ndpi_calloc(workflow->prefs.num_roots, sizeof(void *)); return workflow; } /* ***************************************************** */ void ndpi_flow_info_freer(void *node) { struct ndpi_flow_info *flow = (struct ndpi_flow_info*)node; ndpi_free_flow_info_half(flow); ndpi_free(flow); } /* ***************************************************** */ void ndpi_workflow_free(struct ndpi_workflow * workflow) { int i; for(i=0; iprefs.num_roots; i++) ndpi_tdestroy(workflow->ndpi_flows_root[i], ndpi_flow_info_freer); ndpi_exit_detection_module(workflow->ndpi_struct); free(workflow->ndpi_flows_root); free(workflow); } /* ***************************************************** */ int ndpi_workflow_node_cmp(const void *a, const void *b) { struct ndpi_flow_info *fa = (struct ndpi_flow_info*)a; struct ndpi_flow_info *fb = (struct ndpi_flow_info*)b; if(fa->hashval < fb->hashval) return(-1); else if(fa->hashval > fb->hashval) return(1); /* Flows have the same hash */ if(fa->vlan_id < fb->vlan_id ) return(-1); else { if(fa->vlan_id > fb->vlan_id ) return(1); } if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); } if( ( (fa->src_ip == fb->src_ip ) && (fa->src_port == fb->src_port) && (fa->dst_ip == fb->dst_ip ) && (fa->dst_port == fb->dst_port) ) || ( (fa->src_ip == fb->dst_ip ) && (fa->src_port == fb->dst_port) && (fa->dst_ip == fb->src_ip ) && (fa->dst_port == fb->src_port) ) ) return(0); if(fa->src_ip < fb->src_ip ) return(-1); else { if(fa->src_ip > fb->src_ip ) return(1); } if(fa->src_port < fb->src_port) return(-1); else { if(fa->src_port > fb->src_port) return(1); } if(fa->dst_ip < fb->dst_ip ) return(-1); else { if(fa->dst_ip > fb->dst_ip ) return(1); } if(fa->dst_port < fb->dst_port) return(-1); else { if(fa->dst_port > fb->dst_port) return(1); } return(0); /* notreached */ } /* ***************************************************** */ static void patchIPv6Address(char *str) { int i = 0, j = 0; while(str[i] != '\0') { if((str[i] == ':') && (str[i+1] == '0') && (str[i+2] == ':')) { str[j++] = ':'; str[j++] = ':'; i += 3; } else str[j++] = str[i++]; } if(str[j] != '\0') str[j] = '\0'; } /* ***************************************************** */ static struct ndpi_flow_info *get_ndpi_flow_info(struct ndpi_workflow * workflow, const u_int8_t version, u_int16_t vlan_id, const struct ndpi_iphdr *iph, const struct ndpi_ipv6hdr *iph6, u_int16_t ip_offset, u_int16_t ipsize, u_int16_t l4_packet_len, struct ndpi_tcphdr **tcph, struct ndpi_udphdr **udph, u_int16_t *sport, u_int16_t *dport, struct ndpi_id_struct **src, struct ndpi_id_struct **dst, u_int8_t *proto, u_int8_t **payload, u_int16_t *payload_len, u_int8_t *src_to_dst_direction) { u_int32_t idx, l4_offset, hashval; struct ndpi_flow_info flow; void *ret; u_int8_t *l3, *l4; /* Note: to keep things simple (ndpiReader is just a demo app) we handle IPv6 a-la-IPv4. */ if(version == IPVERSION) { if(ipsize < 20) return NULL; if((iph->ihl * 4) > ipsize || ipsize < ntohs(iph->tot_len) /* || (iph->frag_off & htons(0x1FFF)) != 0 */) return NULL; l4_offset = iph->ihl * 4; l3 = (u_int8_t*)iph; } else { l4_offset = sizeof(struct ndpi_ipv6hdr); l3 = (u_int8_t*)iph6; } if(l4_packet_len < 64) workflow->stats.packet_len[0]++; else if(l4_packet_len >= 64 && l4_packet_len < 128) workflow->stats.packet_len[1]++; else if(l4_packet_len >= 128 && l4_packet_len < 256) workflow->stats.packet_len[2]++; else if(l4_packet_len >= 256 && l4_packet_len < 1024) workflow->stats.packet_len[3]++; else if(l4_packet_len >= 1024 && l4_packet_len < 1500) workflow->stats.packet_len[4]++; else if(l4_packet_len >= 1500) workflow->stats.packet_len[5]++; if(l4_packet_len > workflow->stats.max_packet_len) workflow->stats.max_packet_len = l4_packet_len; *proto = iph->protocol; l4 = ((u_int8_t *) l3 + l4_offset); if(iph->protocol == IPPROTO_TCP && l4_packet_len >= 20) { u_int tcp_len; // tcp workflow->stats.tcp_count++; *tcph = (struct ndpi_tcphdr *)l4; *sport = ntohs((*tcph)->source), *dport = ntohs((*tcph)->dest); tcp_len = ndpi_min(4*(*tcph)->doff, l4_packet_len); *payload = &l4[tcp_len]; *payload_len = ndpi_max(0, l4_packet_len-4*(*tcph)->doff); } else if(iph->protocol == IPPROTO_UDP && l4_packet_len >= 8) { // udp workflow->stats.udp_count++; *udph = (struct ndpi_udphdr *)l4; *sport = ntohs((*udph)->source), *dport = ntohs((*udph)->dest); *payload = &l4[sizeof(struct ndpi_udphdr)]; *payload_len = ndpi_max(0, l4_packet_len-sizeof(struct ndpi_udphdr)); } else { // non tcp/udp protocols *sport = *dport = 0; } flow.protocol = iph->protocol, flow.vlan_id = vlan_id; flow.src_ip = iph->saddr, flow.dst_ip = iph->daddr; flow.src_port = htons(*sport), flow.dst_port = htons(*dport); flow.hashval = hashval = flow.protocol + flow.vlan_id + flow.src_ip + flow.dst_ip + flow.src_port + flow.dst_port; idx = hashval % workflow->prefs.num_roots; ret = ndpi_tfind(&flow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); if(ret == NULL) { if(workflow->stats.ndpi_flow_count == workflow->prefs.max_ndpi_flows) { NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "maximum flow count (%u) has been exceeded\n", workflow->prefs.max_ndpi_flows); exit(-1); } else { struct ndpi_flow_info *newflow = (struct ndpi_flow_info*)malloc(sizeof(struct ndpi_flow_info)); if(newflow == NULL) { NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(1): not enough memory\n", __FUNCTION__); return(NULL); } else workflow->num_allocated_flows++; memset(newflow, 0, sizeof(struct ndpi_flow_info)); newflow->hashval = hashval; newflow->protocol = iph->protocol, newflow->vlan_id = vlan_id; newflow->src_ip = iph->saddr, newflow->dst_ip = iph->daddr; newflow->src_port = htons(*sport), newflow->dst_port = htons(*dport); newflow->ip_version = version; if(version == IPVERSION) { inet_ntop(AF_INET, &newflow->src_ip, newflow->src_name, sizeof(newflow->src_name)); inet_ntop(AF_INET, &newflow->dst_ip, newflow->dst_name, sizeof(newflow->dst_name)); } else { inet_ntop(AF_INET6, &iph6->ip6_src, newflow->src_name, sizeof(newflow->src_name)); inet_ntop(AF_INET6, &iph6->ip6_dst, newflow->dst_name, sizeof(newflow->dst_name)); /* For consistency across platforms replace :0: with :: */ patchIPv6Address(newflow->src_name), patchIPv6Address(newflow->dst_name); } if((newflow->ndpi_flow = ndpi_flow_malloc(SIZEOF_FLOW_STRUCT)) == NULL) { NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(2): not enough memory\n", __FUNCTION__); free(newflow); return(NULL); } else memset(newflow->ndpi_flow, 0, SIZEOF_FLOW_STRUCT); if((newflow->src_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(3): not enough memory\n", __FUNCTION__); free(newflow); return(NULL); } else memset(newflow->src_id, 0, SIZEOF_ID_STRUCT); if((newflow->dst_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) { NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_ERROR, "[NDPI] %s(4): not enough memory\n", __FUNCTION__); free(newflow); return(NULL); } else memset(newflow->dst_id, 0, SIZEOF_ID_STRUCT); ndpi_tsearch(newflow, &workflow->ndpi_flows_root[idx], ndpi_workflow_node_cmp); /* Add */ workflow->stats.ndpi_flow_count++; *src = newflow->src_id, *dst = newflow->dst_id; return newflow; } } else { struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)ret; if(flow->src_ip == iph->saddr && flow->dst_ip == iph->daddr && flow->src_port == htons(*sport) && flow->dst_port == htons(*dport) ) *src = flow->src_id, *dst = flow->dst_id, *src_to_dst_direction = 1; else *src = flow->dst_id, *dst = flow->src_id, *src_to_dst_direction = 0, flow->bidirectional = 1; return flow; } } /* ****************************************************** */ static struct ndpi_flow_info *get_ndpi_flow_info6(struct ndpi_workflow * workflow, u_int16_t vlan_id, const struct ndpi_ipv6hdr *iph6, u_int16_t ip_offset, struct ndpi_tcphdr **tcph, struct ndpi_udphdr **udph, u_int16_t *sport, u_int16_t *dport, struct ndpi_id_struct **src, struct ndpi_id_struct **dst, u_int8_t *proto, u_int8_t **payload, u_int16_t *payload_len, u_int8_t *src_to_dst_direction) { struct ndpi_iphdr iph; memset(&iph, 0, sizeof(iph)); iph.version = IPVERSION; iph.saddr = iph6->ip6_src.u6_addr.u6_addr32[2] + iph6->ip6_src.u6_addr.u6_addr32[3]; iph.daddr = iph6->ip6_dst.u6_addr.u6_addr32[2] + iph6->ip6_dst.u6_addr.u6_addr32[3]; iph.protocol = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; if(iph.protocol == IPPROTO_DSTOPTS /* IPv6 destination option */) { u_int8_t *options = (u_int8_t*)iph6 + sizeof(const struct ndpi_ipv6hdr); iph.protocol = options[0]; } return(get_ndpi_flow_info(workflow, 6, vlan_id, &iph, iph6, ip_offset, sizeof(struct ndpi_ipv6hdr), ntohs(iph6->ip6_ctlun.ip6_un1.ip6_un1_plen), tcph, udph, sport, dport, src, dst, proto, payload, payload_len, src_to_dst_direction)); } /* ****************************************************** */ void process_ndpi_collected_info(struct ndpi_workflow * workflow, struct ndpi_flow_info *flow) { if(!flow->ndpi_flow) return; snprintf(flow->host_server_name, sizeof(flow->host_server_name), "%s", flow->ndpi_flow->host_server_name); /* BITTORRENT */ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_BITTORRENT) { int i, j, n = 0; for(i=0, j = 0; j < sizeof(flow->bittorent_hash)-1; i++) { sprintf(&flow->bittorent_hash[j], "%02x", flow->ndpi_flow->protos.bittorrent.hash[i]); j += 2, n += flow->ndpi_flow->protos.bittorrent.hash[i]; } if(n == 0) flow->bittorent_hash[0] = '\0'; } /* MDNS */ else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_MDNS) { snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.mdns.answer); } /* UBNTAC2 */ else if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UBNTAC2) { snprintf(flow->info, sizeof(flow->info), "%s", flow->ndpi_flow->protos.ubntac2.version); } if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_DNS) { /* SSH */ if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSH) { snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", flow->ndpi_flow->protos.ssh.client_signature); snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", flow->ndpi_flow->protos.ssh.server_signature); } /* SSL */ else if((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_SSL) || (flow->detected_protocol.master_protocol == NDPI_PROTOCOL_SSL)) { snprintf(flow->ssh_ssl.client_info, sizeof(flow->ssh_ssl.client_info), "%s", flow->ndpi_flow->protos.ssl.client_certificate); snprintf(flow->ssh_ssl.server_info, sizeof(flow->ssh_ssl.server_info), "%s", flow->ndpi_flow->protos.ssl.server_certificate); } } if(flow->detection_completed && !flow->check_extra_packets) { if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) { if (workflow->__flow_giveup_callback != NULL) workflow->__flow_giveup_callback(workflow, flow, workflow->__flow_giveup_udata); } else { if (workflow->__flow_detected_callback != NULL) workflow->__flow_detected_callback(workflow, flow, workflow->__flow_detected_udata); } ndpi_free_flow_info_half(flow); } } /* ****************************************************** */ /** Function to process the packet: determine the flow of a packet and try to decode it @return: 0 if success; else != 0 @Note: ipsize = header->len - ip_offset ; rawsize = header->len */ static struct ndpi_proto packet_processing(struct ndpi_workflow * workflow, const u_int64_t time, u_int16_t vlan_id, const struct ndpi_iphdr *iph, struct ndpi_ipv6hdr *iph6, u_int16_t ip_offset, u_int16_t ipsize, u_int16_t rawsize) { struct ndpi_id_struct *src, *dst; struct ndpi_flow_info *flow = NULL; struct ndpi_flow_struct *ndpi_flow = NULL; u_int8_t proto; struct ndpi_tcphdr *tcph = NULL; struct ndpi_udphdr *udph = NULL; u_int16_t sport, dport, payload_len; u_int8_t *payload; u_int8_t src_to_dst_direction = 1; struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN }; if(iph) flow = get_ndpi_flow_info(workflow, IPVERSION, vlan_id, iph, NULL, ip_offset, ipsize, ntohs(iph->tot_len) - (iph->ihl * 4), &tcph, &udph, &sport, &dport, &src, &dst, &proto, &payload, &payload_len, &src_to_dst_direction); else flow = get_ndpi_flow_info6(workflow, vlan_id, iph6, ip_offset, &tcph, &udph, &sport, &dport, &src, &dst, &proto, &payload, &payload_len, &src_to_dst_direction); if(flow != NULL) { workflow->stats.ip_packet_count++; workflow->stats.total_wire_bytes += rawsize + 24 /* CRC etc */, workflow->stats.total_ip_bytes += rawsize; ndpi_flow = flow->ndpi_flow; if(src_to_dst_direction) flow->src2dst_packets++, flow->src2dst_bytes += rawsize; else flow->dst2src_packets++, flow->dst2src_bytes += rawsize; flow->last_seen = time; } else { // flow is NULL workflow->stats.total_discarded_bytes++; return(nproto); } /* Protocol already detected */ if(flow->detection_completed) { if(flow->check_extra_packets && ndpi_flow != NULL && ndpi_flow->check_extra_packets) { if(ndpi_flow->num_extra_packets_checked == 0 && ndpi_flow->max_extra_packets_to_check == 0) { /* Protocols can set this, but we set it here in case they didn't */ ndpi_flow->max_extra_packets_to_check = MAX_EXTRA_PACKETS_TO_CHECK; } if(ndpi_flow->num_extra_packets_checked < ndpi_flow->max_extra_packets_to_check) { ndpi_process_extra_packet(workflow->ndpi_struct, ndpi_flow, iph ? (uint8_t *)iph : (uint8_t *)iph6, ipsize, time, src, dst); if (ndpi_flow->check_extra_packets == 0) { flow->check_extra_packets = 0; process_ndpi_collected_info(workflow, flow); } } } else if (ndpi_flow != NULL) { /* If this wasn't NULL we should do the half free */ /* TODO: When half_free is deprecated, get rid of this */ ndpi_free_flow_info_half(flow); } return(flow->detected_protocol); } flow->detected_protocol = ndpi_detection_process_packet(workflow->ndpi_struct, ndpi_flow, iph ? (uint8_t *)iph : (uint8_t *)iph6, ipsize, time, src, dst); if((flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) || ((proto == IPPROTO_UDP) && ((flow->src2dst_packets + flow->dst2src_packets) > 8)) || ((proto == IPPROTO_TCP) && ((flow->src2dst_packets + flow->dst2src_packets) > 10))) { /* New protocol detected or give up */ flow->detection_completed = 1; /* Check if we should keep checking extra packets */ if (ndpi_flow->check_extra_packets) flow->check_extra_packets = 1; if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) flow->detected_protocol = ndpi_detection_giveup(workflow->ndpi_struct, flow->ndpi_flow); process_ndpi_collected_info(workflow, flow); } return(flow->detected_protocol); } /* ****************************************************** */ struct ndpi_proto ndpi_workflow_process_packet (struct ndpi_workflow * workflow, const struct pcap_pkthdr *header, const u_char *packet) { /* * Declare pointers to packet headers */ /* --- Ethernet header --- */ const struct ndpi_ethhdr *ethernet; /* --- LLC header --- */ const struct ndpi_llc_header_snap *llc; /* --- Cisco HDLC header --- */ const struct ndpi_chdlc *chdlc; /* --- Radio Tap header --- */ const struct ndpi_radiotap_header *radiotap; /* --- Wifi header --- */ const struct ndpi_wifi_header *wifi; /* --- MPLS header --- */ struct ndpi_mpls_header *mpls; /** --- IP header --- **/ struct ndpi_iphdr *iph; /** --- IPv6 header --- **/ struct ndpi_ipv6hdr *iph6; struct ndpi_proto nproto = { NDPI_PROTOCOL_UNKNOWN, NDPI_PROTOCOL_UNKNOWN }; /* lengths and offsets */ u_int16_t eth_offset = 0; u_int16_t radio_len; u_int16_t fc; u_int16_t type = 0; int wifi_len = 0; int pyld_eth_len = 0; int check; u_int64_t time; u_int16_t ip_offset = 0, ip_len; u_int16_t frag_off = 0, vlan_id = 0; u_int8_t proto = 0; u_int32_t label; /* counters */ u_int8_t vlan_packet = 0; /* Increment raw packet counter */ workflow->stats.raw_packet_count++; /* setting time */ time = ((uint64_t) header->ts.tv_sec) * TICK_RESOLUTION + header->ts.tv_usec / (1000000 / TICK_RESOLUTION); /* safety check */ if(workflow->last_time > time) { /* printf("\nWARNING: timestamp bug in the pcap file (ts delta: %llu, repairing)\n", ndpi_thread_info[thread_id].last_time - time); */ time = workflow->last_time; } /* update last time value */ workflow->last_time = time; /*** check Data Link type ***/ const int datalink_type = pcap_datalink(workflow->pcap_handle); datalink_check: switch(datalink_type) { case DLT_NULL: if(ntohl(*((u_int32_t*)&packet[eth_offset])) == 2) type = ETH_P_IP; else type = ETH_P_IPV6; ip_offset = 4 + eth_offset; break; /* Cisco PPP in HDLC-like framing - 50 */ case DLT_PPP_SERIAL: chdlc = (struct ndpi_chdlc *) &packet[eth_offset]; ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ type = ntohs(chdlc->proto_code); break; /* Cisco PPP - 9 or 104 */ case DLT_C_HDLC: case DLT_PPP: chdlc = (struct ndpi_chdlc *) &packet[eth_offset]; ip_offset = sizeof(struct ndpi_chdlc); /* CHDLC_OFF = 4 */ type = ntohs(chdlc->proto_code); break; /* IEEE 802.3 Ethernet - 1 */ case DLT_EN10MB: ethernet = (struct ndpi_ethhdr *) &packet[eth_offset]; ip_offset = sizeof(struct ndpi_ethhdr) + eth_offset; check = ntohs(ethernet->h_proto); if(check <= 1500) pyld_eth_len = check; else if (check >= 1536) type = check; if(pyld_eth_len != 0) { llc = (struct ndpi_llc_header_snap *)(&packet[ip_offset]); /* check for LLC layer with SNAP extension */ if(llc->dsap == SNAP || llc->ssap == SNAP) { type = llc->snap.proto_ID; ip_offset += + 8; } /* No SNAP extension - Spanning Tree pkt must be discarted */ else if(llc->dsap == BSTP || llc->ssap == BSTP) { goto v4_warning; } } break; /* Linux Cooked Capture - 113 */ case DLT_LINUX_SLL: type = (packet[eth_offset+14] << 8) + packet[eth_offset+15]; ip_offset = 16 + eth_offset; break; /* Radiotap link-layer - 127 */ case DLT_IEEE802_11_RADIO: radiotap = (struct ndpi_radiotap_header *) &packet[eth_offset]; radio_len = radiotap->len; /* Check Bad FCS presence */ if((radiotap->flags & BAD_FCS) == BAD_FCS) { workflow->stats.total_discarded_bytes += header->len; return(nproto); } /* Calculate 802.11 header length (variable) */ wifi = (struct ndpi_wifi_header*)( packet + eth_offset + radio_len); fc = wifi->fc; /* check wifi data presence */ if(FCF_TYPE(fc) == WIFI_DATA) { if((FCF_TO_DS(fc) && FCF_FROM_DS(fc) == 0x0) || (FCF_TO_DS(fc) == 0x0 && FCF_FROM_DS(fc))) wifi_len = 26; /* + 4 byte fcs */ } else /* no data frames */ break; /* Check ether_type from LLC */ llc = (struct ndpi_llc_header_snap*)(packet + eth_offset + wifi_len + radio_len); if(llc->dsap == SNAP) type = ntohs(llc->snap.proto_ID); /* Set IP header offset */ ip_offset = wifi_len + radio_len + sizeof(struct ndpi_llc_header_snap) + eth_offset; break; case DLT_RAW: ip_offset = eth_offset = 0; break; default: /* printf("Unknown datalink %d\n", datalink_type); */ return(nproto); } /* check ether type */ switch(type) { case VLAN: vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset+1]) & 0xFFF; type = (packet[ip_offset+2] << 8) + packet[ip_offset+3]; ip_offset += 4; vlan_packet = 1; // double tagging for 802.1Q if(type == 0x8100) { vlan_id = ((packet[ip_offset] << 8) + packet[ip_offset+1]) & 0xFFF; type = (packet[ip_offset+2] << 8) + packet[ip_offset+3]; ip_offset += 4; } break; case MPLS_UNI: case MPLS_MULTI: mpls = (struct ndpi_mpls_header *) &packet[ip_offset]; label = ntohl(mpls->label); /* label = ntohl(*((u_int32_t*)&packet[ip_offset])); */ workflow->stats.mpls_count++; type = ETH_P_IP, ip_offset += 4; while((label & 0x100) != 0x100) { ip_offset += 4; label = ntohl(mpls->label); } break; case PPPoE: workflow->stats.pppoe_count++; type = ETH_P_IP; ip_offset += 8; break; default: break; } workflow->stats.vlan_count += vlan_packet; iph_check: /* Check and set IP header size and total packet length */ iph = (struct ndpi_iphdr *) &packet[ip_offset]; /* just work on Ethernet packets that contain IP */ if(type == ETH_P_IP && header->caplen >= ip_offset) { frag_off = ntohs(iph->frag_off); proto = iph->protocol; if(header->caplen < header->len) { static u_int8_t cap_warning_used = 0; if(cap_warning_used == 0) { if(!workflow->prefs.quiet_mode) NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: packet capture size is smaller than packet size, DETECTION MIGHT NOT WORK CORRECTLY\n\n"); cap_warning_used = 1; } } } if(iph->version == IPVERSION) { ip_len = ((u_int16_t)iph->ihl * 4); iph6 = NULL; if(iph->protocol == IPPROTO_IPV6) { ip_offset += ip_len; goto iph_check; } if((frag_off & 0x1FFF) != 0) { static u_int8_t ipv4_frags_warning_used = 0; workflow->stats.fragmented_count++; if(ipv4_frags_warning_used == 0) { if(!workflow->prefs.quiet_mode) NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: IPv4 fragments are not handled by this demo (nDPI supports them)\n"); ipv4_frags_warning_used = 1; } workflow->stats.total_discarded_bytes += header->len; return(nproto); } } else if(iph->version == 6) { iph6 = (struct ndpi_ipv6hdr *)&packet[ip_offset]; proto = iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt; ip_len = sizeof(struct ndpi_ipv6hdr); if(proto == IPPROTO_DSTOPTS /* IPv6 destination option */) { u_int8_t *options = (u_int8_t*)&packet[ip_offset+ip_len]; proto = options[0]; ip_len += 8 * (options[1] + 1); } iph = NULL; } else { static u_int8_t ipv4_warning_used = 0; v4_warning: if(ipv4_warning_used == 0) { if(!workflow->prefs.quiet_mode) NDPI_LOG(0, workflow->ndpi_struct, NDPI_LOG_DEBUG, "\n\nWARNING: only IPv4/IPv6 packets are supported in this demo (nDPI supports both IPv4 and IPv6), all other packets will be discarded\n\n"); ipv4_warning_used = 1; } workflow->stats.total_discarded_bytes += header->len; return(nproto); } if(workflow->prefs.decode_tunnels && (proto == IPPROTO_UDP)) { struct ndpi_udphdr *udp = (struct ndpi_udphdr *)&packet[ip_offset+ip_len]; u_int16_t sport = ntohs(udp->source), dport = ntohs(udp->dest); if((sport == GTP_U_V1_PORT) || (dport == GTP_U_V1_PORT)) { /* Check if it's GTPv1 */ u_int offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr); u_int8_t flags = packet[offset]; u_int8_t message_type = packet[offset+1]; if((((flags & 0xE0) >> 5) == 1 /* GTPv1 */) && (message_type == 0xFF /* T-PDU */)) { ip_offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr)+8; /* GTPv1 header len */ if(flags & 0x04) ip_offset += 1; /* next_ext_header is present */ if(flags & 0x02) ip_offset += 4; /* sequence_number is present (it also includes next_ext_header and pdu_number) */ if(flags & 0x01) ip_offset += 1; /* pdu_number is present */ iph = (struct ndpi_iphdr *) &packet[ip_offset]; if(iph->version != IPVERSION) { // printf("WARNING: not good (packet_id=%u)!\n", (unsigned int)workflow->stats.raw_packet_count); goto v4_warning; } } } else if((sport == TZSP_PORT) || (dport == TZSP_PORT)) { /* https://en.wikipedia.org/wiki/TZSP */ u_int offset = ip_offset+ip_len+sizeof(struct ndpi_udphdr); u_int8_t version = packet[offset]; u_int8_t type = packet[offset+1]; u_int16_t encapsulates = ntohs(*((u_int16_t*)&packet[offset+2])); if((version == 1) && (type == 0) && (encapsulates == 1)) { u_int8_t stop = 0; offset += 4; while((!stop) && (offset < header->caplen)) { u_int8_t tag_type = packet[offset]; u_int8_t tag_len; switch(tag_type) { case 0: /* PADDING Tag */ tag_len = 1; break; case 1: /* END Tag */ tag_len = 1, stop = 1; break; default: tag_len = packet[offset+1]; break; } offset += tag_len; if(offset >= header->caplen) return(nproto); /* Invalid packet */ else { eth_offset = offset; goto datalink_check; } } } } } /* process the packet */ return(packet_processing(workflow, time, vlan_id, iph, iph6, ip_offset, header->caplen - ip_offset, header->caplen)); } /* ********************************************************** */ /* http://home.thep.lu.se/~bjorn/crc/crc32_fast.c */ /* ********************************************************** */ static uint32_t crc32_for_byte(uint32_t r) { int j; for(j = 0; j < 8; ++j) r = (r & 1? 0: (uint32_t)0xEDB88320L) ^ r >> 1; return r ^ (uint32_t)0xFF000000L; } /* Any unsigned integer type with at least 32 bits may be used as * accumulator type for fast crc32-calulation, but unsigned long is * probably the optimal choice for most systems. */ typedef unsigned long accum_t; static void init_tables(uint32_t* table, uint32_t* wtable) { size_t i, j, k, w; for(i = 0; i < 0x100; ++i) table[i] = crc32_for_byte(i); for(k = 0; k < sizeof(accum_t); ++k) for(i = 0; i < 0x100; ++i) { for(j = w = 0; j < sizeof(accum_t); ++j) w = table[(uint8_t)(j == k? w ^ i: w)] ^ w >> 8; wtable[(k << 8) + i] = w ^ (k? wtable[0]: 0); } } static void __crc32(const void* data, size_t n_bytes, uint32_t* crc) { static uint32_t table[0x100], wtable[0x100*sizeof(accum_t)]; size_t n_accum = n_bytes/sizeof(accum_t); size_t i, j; if(!*table) init_tables(table, wtable); for(i = 0; i < n_accum; ++i) { accum_t a = *crc ^ ((accum_t*)data)[i]; for(j = *crc = 0; j < sizeof(accum_t); ++j) *crc ^= wtable[(j << 8) + (uint8_t)(a >> 8*j)]; } for(i = n_accum*sizeof(accum_t); i < n_bytes; ++i) *crc = table[(uint8_t)*crc ^ ((uint8_t*)data)[i]] ^ *crc >> 8; } u_int32_t ethernet_crc32(const void* data, size_t n_bytes) { u_int32_t crc = 0; __crc32(data, n_bytes, &crc); return crc; }