/* pmacct (Promiscuous mode IP Accounting package) pmacct is Copyright (C) 2003-2020 by Paolo Lucente */ /* This program 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 2 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* includes */ #include "pmacct.h" #include "addr.h" #include "pmacct-data.h" #include "pmacct-dlt.h" #include "pretag_handlers.h" #include "plugin_hooks.h" #include "pkt_handlers.h" #include "ip_frag.h" #include "ip_flow.h" #include "net_aggr.h" #include "thread_pool.h" #include "isis/isis.h" #include "bgp/bgp.h" #include "bmp/bmp.h" #if defined (WITH_NDPI) #include "ndpi/ndpi.h" #endif struct tunnel_entry tunnel_handlers_list[] = { {"gtp", gtp_tunnel_func, gtp_tunnel_configurator}, {"", NULL, NULL}, }; void pm_pcap_cb(u_char *user, const struct pcap_pkthdr *pkthdr, const u_char *buf) { struct packet_ptrs pptrs; struct pm_pcap_callback_data *cb_data = (struct pm_pcap_callback_data *) user; struct pm_pcap_device *device = cb_data->device; struct plugin_requests req; u_int32_t iface32 = 0; u_int32_t ifacePresent = 0; memset(&req, 0, sizeof(req)); if (cb_data->sig.is_set) sigprocmask(SIG_BLOCK, &cb_data->sig.set, NULL); /* We process the packet with the appropriate data link layer function */ if (buf) { memset(&pptrs, 0, sizeof(pptrs)); pptrs.pkthdr = (struct pcap_pkthdr *) pkthdr; pptrs.packet_ptr = (u_char *) buf; pptrs.f_agent = cb_data->f_agent; pptrs.bpas_table = cb_data->bpas_table; pptrs.blp_table = cb_data->blp_table; pptrs.bmed_table = cb_data->bmed_table; pptrs.bta_table = cb_data->bta_table; pptrs.flow_type = NF9_FTYPE_TRAFFIC; assert(cb_data); if (cb_data->has_tun_prims) { struct packet_ptrs *tpptrs; pptrs.tun_pptrs = malloc(sizeof(struct packet_ptrs)); memset(pptrs.tun_pptrs, 0, sizeof(struct packet_ptrs)); tpptrs = (struct packet_ptrs *) pptrs.tun_pptrs; tpptrs->pkthdr = malloc(sizeof(struct pcap_pkthdr)); memcpy(&tpptrs->pkthdr, &pptrs.pkthdr, sizeof(struct pcap_pkthdr)); tpptrs->packet_ptr = (u_char *) buf; tpptrs->flow_type = NF9_FTYPE_TRAFFIC; } /* direction */ if (cb_data->device && cb_data->device->pcap_if && cb_data->device->pcap_if->direction) { pptrs.direction = cb_data->device->pcap_if->direction; } else if (config.pcap_direction) { pptrs.direction = config.pcap_direction; } else pptrs.direction = FALSE; /* input interface */ if (cb_data->ifindex_in) { pptrs.ifindex_in = cb_data->ifindex_in; } else if (cb_data->device && cb_data->device->id && cb_data->device->pcap_if && cb_data->device->pcap_if->direction) { if (cb_data->device->pcap_if->direction == PCAP_D_IN) { pptrs.ifindex_in = cb_data->device->id; } } else if (cb_data->device->id && config.pcap_direction == PCAP_D_IN) { pptrs.ifindex_in = cb_data->device->id; } else pptrs.ifindex_in = 0; /* output interface */ if (cb_data->ifindex_out) { pptrs.ifindex_out = cb_data->ifindex_out; } else if (cb_data->device && cb_data->device->id && cb_data->device->pcap_if && cb_data->device->pcap_if->direction) { if (cb_data->device->pcap_if->direction == PCAP_D_OUT) { pptrs.ifindex_out = cb_data->device->id; } } else if (cb_data->device->id && config.pcap_direction == PCAP_D_OUT) { pptrs.ifindex_out = cb_data->device->id; } else pptrs.ifindex_out = 0; if (config.decode_arista_trailer) { memcpy(&ifacePresent, buf + pkthdr->len - 8, 4); if (ifacePresent == 1) { memcpy(&iface32, buf + pkthdr->len - 4, 4); pptrs.ifindex_out = iface32; } } (*device->data->handler)(pkthdr, &pptrs); if (pptrs.iph_ptr) { if ((*pptrs.l3_handler)(&pptrs)) { #if defined (WITH_NDPI) if (config.classifier_ndpi && pm_ndpi_wfl) { pptrs.ndpi_class = pm_ndpi_workflow_process_packet(pm_ndpi_wfl, &pptrs); } #endif if (config.nfacctd_isis) { isis_srcdst_lookup(&pptrs); } if (config.bgp_daemon) { BTA_find_id((struct id_table *)pptrs.bta_table, &pptrs, &pptrs.bta, &pptrs.bta2); bgp_srcdst_lookup(&pptrs, FUNC_TYPE_BGP); } if (config.bgp_daemon_peer_as_src_map) PM_find_id((struct id_table *)pptrs.bpas_table, &pptrs, &pptrs.bpas, NULL); if (config.bgp_daemon_src_local_pref_map) PM_find_id((struct id_table *)pptrs.blp_table, &pptrs, &pptrs.blp, NULL); if (config.bgp_daemon_src_med_map) PM_find_id((struct id_table *)pptrs.bmed_table, &pptrs, &pptrs.bmed, NULL); if (config.bmp_daemon) { BTA_find_id((struct id_table *)pptrs.bta_table, &pptrs, &pptrs.bta, &pptrs.bta2); bmp_srcdst_lookup(&pptrs); } set_index_pkt_ptrs(&pptrs); exec_plugins(&pptrs, &req); } } } if (reload_map) { bta_map_caching = FALSE; sampling_map_caching = FALSE; load_networks(config.networks_file, &nt, &nc); if (config.bgp_daemon && config.bgp_daemon_peer_as_src_map) load_id_file(MAP_BGP_PEER_AS_SRC, config.bgp_daemon_peer_as_src_map, (struct id_table *)cb_data->bpas_table, &req, &bpas_map_allocated); if (config.bgp_daemon && config.bgp_daemon_src_local_pref_map) load_id_file(MAP_BGP_SRC_LOCAL_PREF, config.bgp_daemon_src_local_pref_map, (struct id_table *)cb_data->blp_table, &req, &blp_map_allocated); if (config.bgp_daemon && config.bgp_daemon_src_med_map) load_id_file(MAP_BGP_SRC_MED, config.bgp_daemon_src_med_map, (struct id_table *)cb_data->bmed_table, &req, &bmed_map_allocated); if (config.bgp_daemon) load_id_file(MAP_BGP_TO_XFLOW_AGENT, config.bgp_daemon_to_xflow_agent_map, (struct id_table *)cb_data->bta_table, &req, &bta_map_allocated); reload_map = FALSE; gettimeofday(&reload_map_tstamp, NULL); } if (reload_log) { reload_logs(); reload_log = FALSE; } if (cb_data->has_tun_prims && pptrs.tun_pptrs) { struct packet_ptrs *tpptrs = (struct packet_ptrs *) pptrs.tun_pptrs; if (tpptrs->pkthdr) free(tpptrs->pkthdr); free(pptrs.tun_pptrs); } if (cb_data->sig.is_set) sigprocmask(SIG_UNBLOCK, &cb_data->sig.set, NULL); } int ip_handler(register struct packet_ptrs *pptrs) { register u_int8_t len = 0; register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; register unsigned char *ptr; register u_int16_t off = pptrs->iph_ptr-pptrs->packet_ptr, off_l4; int ret = TRUE, num, is_fragment = 0; /* len: number of 32bit words forming the header */ len = IP_HL(((struct pm_iphdr *) pptrs->iph_ptr)); len <<= 2; ptr = pptrs->iph_ptr+len; off += len; /* check len */ if (off > caplen) return FALSE; /* IP packet truncated */ pptrs->l4_proto = ((struct pm_iphdr *)pptrs->iph_ptr)->ip_p; pptrs->payload_ptr = NULL; off_l4 = off; /* check fragments if needed */ if (config.handle_fragments) { if (pptrs->l4_proto == IPPROTO_TCP || pptrs->l4_proto == IPPROTO_UDP) { if (off+MyTLHdrSz > caplen) { Log(LOG_INFO, "INFO ( %s/core ): short IPv4 packet read (%u/%u/frags). Snaplen issue ?\n", config.name, caplen, off+MyTLHdrSz); return FALSE; } pptrs->tlh_ptr = ptr; if (((struct pm_iphdr *)pptrs->iph_ptr)->ip_off & htons(IP_MF|IP_OFFMASK)) { is_fragment = TRUE; ret = ip_fragment_handler(pptrs); if (!ret) { if (!config.ext_sampling_rate) goto quit; else { pptrs->tlh_ptr = dummy_tlhdr; pptrs->tcp_flags = FALSE; if (off < caplen) pptrs->payload_ptr = ptr; ret = TRUE; goto quit; } } } /* Let's handle both fragments and packets. If we are facing any subsequent frag our pointer is in place; we handle unknown L4 protocols likewise. In case of "entire" TCP/UDP packets we have to jump the L4 header instead */ if (((struct pm_iphdr *)pptrs->iph_ptr)->ip_off & htons(IP_OFFMASK)); else if (pptrs->l4_proto == IPPROTO_UDP) { ptr += UDPHdrSz; off += UDPHdrSz; } else if (pptrs->l4_proto == IPPROTO_TCP) { ptr += ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_off << 2; off += ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_off << 2; } if (off < caplen) { pptrs->payload_ptr = ptr; if (pptrs->l4_proto == IPPROTO_UDP) { u_int16_t dst_port = ntohs(((struct pm_udphdr *)pptrs->tlh_ptr)->uh_dport); if (dst_port == UDP_PORT_VXLAN && (off + sizeof(struct vxlan_hdr) <= caplen)) { struct vxlan_hdr *vxhdr = (struct vxlan_hdr *) pptrs->payload_ptr; if (vxhdr->flags & VXLAN_FLAG_I) pptrs->vxlan_ptr = vxhdr->vni; pptrs->payload_ptr += sizeof(struct vxlan_hdr); if (pptrs->tun_pptrs) { struct packet_ptrs *tpptrs = (struct packet_ptrs *) pptrs->tun_pptrs; tpptrs->pkthdr->caplen = (pptrs->pkthdr->caplen - (pptrs->payload_ptr - pptrs->packet_ptr)); tpptrs->packet_ptr = pptrs->payload_ptr; eth_handler(tpptrs->pkthdr, tpptrs); if (tpptrs->iph_ptr) ((*tpptrs->l3_handler)(tpptrs)); } } } } } else { pptrs->tlh_ptr = dummy_tlhdr; if (off < caplen) pptrs->payload_ptr = ptr; } if (config.handle_flows) { pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP) { if (off_l4+TCPFlagOff+1 > caplen) { Log(LOG_INFO, "INFO ( %s/core ): short IPv4 packet read (%u/%u/flows). Snaplen issue ?\n", config.name, caplen, off_l4+TCPFlagOff+1); return FALSE; } if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_SYN) pptrs->tcp_flags |= TH_SYN; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_FIN) pptrs->tcp_flags |= TH_FIN; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_RST) pptrs->tcp_flags |= TH_RST; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_ACK && pptrs->tcp_flags) pptrs->tcp_flags |= TH_ACK; } ip_flow_handler(pptrs); } /* XXX: optimize/short circuit here! */ pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP && off_l4+TCPFlagOff+1 <= caplen) pptrs->tcp_flags = ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags; /* tunnel handlers here */ if (config.tunnel0 && !pptrs->tun_stack) { for (num = 0; pptrs->payload_ptr && !is_fragment && tunnel_registry[0][num].tf; num++) { if (tunnel_registry[0][num].proto == pptrs->l4_proto) { if (!tunnel_registry[0][num].port || (pptrs->tlh_ptr && tunnel_registry[0][num].port == ntohs(((struct pm_tlhdr *)pptrs->tlh_ptr)->dst_port))) { pptrs->tun_stack = num; ret = (*tunnel_registry[0][num].tf)(pptrs); } } } } else if (pptrs->tun_stack) { if (tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].proto == pptrs->l4_proto) { if (!tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].port || (pptrs->tlh_ptr && tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].port == ntohs(((struct pm_tlhdr *)pptrs->tlh_ptr)->dst_port))) { ret = (*tunnel_registry[pptrs->tun_stack][pptrs->tun_layer].tf)(pptrs); } } } } quit: return ret; } int ip6_handler(register struct packet_ptrs *pptrs) { struct ip6_frag *fhdr = NULL; register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; u_int16_t plen = ntohs(((struct ip6_hdr *)pptrs->iph_ptr)->ip6_plen); u_int16_t off = pptrs->iph_ptr-pptrs->packet_ptr, off_l4; u_int32_t advance; u_int8_t nh; u_char *ptr = pptrs->iph_ptr; int ret = TRUE; /* length checks */ if (off+IP6HdrSz > caplen) return FALSE; /* IP packet truncated */ if (plen == 0 && ((struct ip6_hdr *)pptrs->iph_ptr)->ip6_nxt != IPPROTO_NONE) { Log(LOG_INFO, "INFO ( %s/core ): NULL IPv6 payload length. Jumbo packets are currently not supported.\n", config.name); return FALSE; } pptrs->l4_proto = 0; pptrs->payload_ptr = NULL; nh = ((struct ip6_hdr *)pptrs->iph_ptr)->ip6_nxt; advance = IP6HdrSz; while ((off+advance <= caplen) && advance) { off += advance; ptr += advance; switch(nh) { case IPPROTO_HOPOPTS: case IPPROTO_DSTOPTS: case IPPROTO_ROUTING: case IPPROTO_MOBILITY: nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = (((struct ip6_ext *)ptr)->ip6e_len + 1) << 3; break; case IPPROTO_AH: nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = sizeof(struct ah)+(((struct ah *)ptr)->ah_len << 2); /* hdr + sumlen */ break; case IPPROTO_FRAGMENT: fhdr = (struct ip6_frag *) ptr; nh = ((struct ip6_ext *)ptr)->ip6e_nxt; advance = sizeof(struct ip6_frag); break; /* XXX: case IPPROTO_ESP: */ /* XXX: case IPPROTO_IPCOMP: */ default: pptrs->tlh_ptr = ptr; pptrs->l4_proto = nh; goto end; } } end: off_l4 = off; if (config.handle_fragments) { if (pptrs->l4_proto == IPPROTO_TCP || pptrs->l4_proto == IPPROTO_UDP) { if (off+MyTLHdrSz > caplen) { Log(LOG_INFO, "INFO ( %s/core ): short IPv6 packet read (%u/%u/frags). Snaplen issue ?\n", config.name, caplen, off+MyTLHdrSz); return FALSE; } if (fhdr && (fhdr->ip6f_offlg & htons(IP6F_MORE_FRAG|IP6F_OFF_MASK))) { ret = ip6_fragment_handler(pptrs, fhdr); if (!ret) { if (!config.ext_sampling_rate) goto quit; else { pptrs->tlh_ptr = dummy_tlhdr; pptrs->tcp_flags = FALSE; if (off < caplen) pptrs->payload_ptr = ptr; ret = TRUE; goto quit; } } } /* Let's handle both fragments and packets. If we are facing any subsequent frag our pointer is in place; we handle unknown L4 protocols likewise. In case of "entire" TCP/UDP packets we have to jump the L4 header instead */ if (fhdr && (fhdr->ip6f_offlg & htons(IP6F_OFF_MASK))); else if (pptrs->l4_proto == IPPROTO_UDP) { ptr += UDPHdrSz; off += UDPHdrSz; } else if (pptrs->l4_proto == IPPROTO_TCP) { ptr += ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_off << 2; off += ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_off << 2; } if (off < caplen) { pptrs->payload_ptr = ptr; if (pptrs->l4_proto == IPPROTO_UDP) { u_int16_t dst_port = ntohs(((struct pm_udphdr *)pptrs->tlh_ptr)->uh_dport); if (dst_port == UDP_PORT_VXLAN && (off + sizeof(struct vxlan_hdr) <= caplen)) { struct vxlan_hdr *vxhdr = (struct vxlan_hdr *) pptrs->payload_ptr; if (vxhdr->flags & VXLAN_FLAG_I) pptrs->vxlan_ptr = vxhdr->vni; pptrs->payload_ptr += sizeof(struct vxlan_hdr); if (pptrs->tun_pptrs) { struct packet_ptrs *tpptrs = (struct packet_ptrs *) pptrs->tun_pptrs; tpptrs->pkthdr->caplen = (pptrs->pkthdr->caplen - (pptrs->payload_ptr - pptrs->packet_ptr)); tpptrs->packet_ptr = pptrs->payload_ptr; eth_handler(tpptrs->pkthdr, tpptrs); if (tpptrs->iph_ptr) ((*tpptrs->l3_handler)(tpptrs)); } } } } } else { pptrs->tlh_ptr = dummy_tlhdr; if (off < caplen) pptrs->payload_ptr = ptr; } if (config.handle_flows) { pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP) { if (off_l4+TCPFlagOff+1 > caplen) { Log(LOG_INFO, "INFO ( %s/core ): short IPv6 packet read (%u/%u/flows). Snaplen issue ?\n", config.name, caplen, off_l4+TCPFlagOff+1); return FALSE; } if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_SYN) pptrs->tcp_flags |= TH_SYN; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_FIN) pptrs->tcp_flags |= TH_FIN; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_RST) pptrs->tcp_flags |= TH_RST; if (((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags & TH_ACK && pptrs->tcp_flags) pptrs->tcp_flags |= TH_ACK; } ip_flow6_handler(pptrs); } /* XXX: optimize/short circuit here! */ pptrs->tcp_flags = FALSE; if (pptrs->l4_proto == IPPROTO_TCP && off_l4+TCPFlagOff+1 <= caplen) pptrs->tcp_flags = ((struct pm_tcphdr *)pptrs->tlh_ptr)->th_flags; } quit: return TRUE; } int PM_find_id(struct id_table *t, struct packet_ptrs *pptrs, pm_id_t *tag, pm_id_t *tag2) { int x; pm_id_t ret = 0; if (!t) return 0; pretag_init_vars(pptrs, t); if (tag) *tag = 0; if (tag2) *tag2 = 0; if (pptrs) { pptrs->have_tag = FALSE; pptrs->have_tag2 = FALSE; } /* Giving a first try with index(es) */ if (config.maps_index && pretag_index_have_one(t)) { struct id_entry *index_results[ID_TABLE_INDEX_RESULTS]; u_int32_t iterator; int num_results; num_results = pretag_index_lookup(t, pptrs, index_results, ID_TABLE_INDEX_RESULTS); for (iterator = 0; index_results[iterator] && iterator < num_results; iterator++) { ret = pretag_entry_process(index_results[iterator], pptrs, tag, tag2); if (!(ret & PRETAG_MAP_RCODE_JEQ)) return ret; } /* if we have at least one index we trust we did a good job */ return ret; } for (x = 0; x < t->ipv4_num; x++) { ret = pretag_entry_process(&t->e[x], pptrs, tag, tag2); if (!ret || ret > TRUE) { if (ret & PRETAG_MAP_RCODE_JEQ) { x = t->e[x].jeq.ptr->pos; x--; // yes, it will be automagically incremented by the for() cycle } else break; } } return ret; } void PM_print_stats(time_t now) { int device_idx; Log(LOG_NOTICE, "NOTICE ( %s/%s ): +++\n", config.name, config.type); if (config.pcap_if || config.pcap_interfaces_map) { for (device_idx = 0; device_idx < devices.num; device_idx++) { if (pcap_stats(devices.list[device_idx].dev_desc, &ps) < 0) { Log(LOG_INFO, "INFO ( %s/%s ): stats [%s,%u] time=%ld error='pcap_stats(): %s'\n", config.name, config.type, devices.list[device_idx].str, devices.list[device_idx].id, (long)now, pcap_geterr(devices.list[device_idx].dev_desc)); } Log(LOG_NOTICE, "NOTICE ( %s/%s ): stats [%s,%u] time=%ld received_packets=%u dropped_packets=%u\n", config.name, config.type, devices.list[device_idx].str, devices.list[device_idx].id, (long)now, ps.ps_recv, ps.ps_drop); } } Log(LOG_NOTICE, "NOTICE ( %s/%s ): ---\n", config.name, config.type); } void compute_once() { struct pkt_data dummy; CounterSz = sizeof(dummy.pkt_len); PdataSz = sizeof(struct pkt_data); PpayloadSz = sizeof(struct pkt_payload); PextrasSz = sizeof(struct pkt_extras); PbgpSz = sizeof(struct pkt_bgp_primitives); PlbgpSz = sizeof(struct pkt_legacy_bgp_primitives); PnatSz = sizeof(struct pkt_nat_primitives); PmplsSz = sizeof(struct pkt_mpls_primitives); PtunSz = sizeof(struct pkt_tunnel_primitives); PvhdrSz = sizeof(struct pkt_vlen_hdr_primitives); PmLabelTSz = sizeof(pm_label_t); PtLabelTSz = sizeof(pt_label_t); ChBufHdrSz = sizeof(struct ch_buf_hdr); CharPtrSz = sizeof(char *); IP4HdrSz = sizeof(struct pm_iphdr); MyTLHdrSz = sizeof(struct pm_tlhdr); TCPFlagOff = 13; MyTCPHdrSz = TCPFlagOff+1; PptrsSz = sizeof(struct packet_ptrs); UDPHdrSz = 8; CSSz = sizeof(struct class_st); IpFlowCmnSz = sizeof(struct ip_flow_common); HostAddrSz = sizeof(struct host_addr); IP6HdrSz = sizeof(struct ip6_hdr); IP6AddrSz = sizeof(struct in6_addr); } void tunnel_registry_init() { if (config.tunnel0) { char *tun_string = config.tunnel0, *tun_entry = NULL, *tun_type = NULL; int th_index = 0 /* tunnel handler index */, tr_index = 0 /* tunnel registry index */; while ((tun_entry = extract_token(&tun_string, ';'))) { tun_type = extract_token(&tun_entry, ','); for (th_index = 0; strcmp(tunnel_handlers_list[th_index].type, ""); th_index++) { if (!strcmp(tunnel_handlers_list[th_index].type, tun_type)) { if (tr_index < TUNNEL_REGISTRY_ENTRIES) { (*tunnel_handlers_list[th_index].tc)(&tunnel_registry[0][tr_index], tun_entry); tr_index++; } break; } } } } } int gtp_tunnel_configurator(struct tunnel_handler *th, char *opts) { th->proto = IPPROTO_UDP; th->port = atoi(opts); if (th->port) { th->tf = gtp_tunnel_func; } else { th->tf = NULL; Log(LOG_WARNING, "WARN ( %s/core ): GTP tunnel handler not loaded due to invalid options: '%s'\n", config.name, opts); } return 0; } int gtp_tunnel_func(register struct packet_ptrs *pptrs) { register u_int16_t caplen = ((struct pcap_pkthdr *)pptrs->pkthdr)->caplen; struct pm_gtphdr_v0 *gtp_hdr_v0 = (struct pm_gtphdr_v0 *) pptrs->payload_ptr; u_int16_t off = pptrs->payload_ptr-pptrs->packet_ptr; u_int16_t gtp_hdr_len, gtp_version; u_char *ptr = pptrs->payload_ptr; int ret, trial; gtp_version = (gtp_hdr_v0->flags >> 5) & 0x07; switch (gtp_version) { case 0: gtp_hdr_len = 4; break; case 1: gtp_hdr_len = 8; break; default: Log(LOG_INFO, "INFO ( %s/core ): unsupported GTP version %u\n", config.name, gtp_version); return FALSE; } if (off + gtp_hdr_len < caplen) { off += gtp_hdr_len; ptr += gtp_hdr_len; ret = 0; trial = 0; while (!ret && trial < MAX_GTP_TRIALS) { pptrs->iph_ptr = ptr; pptrs->tlh_ptr = NULL; pptrs->payload_ptr = NULL; pptrs->l4_proto = 0; pptrs->tcp_flags = 0; /* same trick used for MPLS BoS in ll.c: let's look at the first payload byte to guess which protocol we are speaking about */ switch (*pptrs->iph_ptr) { case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: pptrs->tun_layer++; ret = ip_handler(pptrs); break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: pptrs->tun_layer++; ret = ip6_handler(pptrs); break; default: ret = FALSE; break; } /* next loop increment */ off++; ptr++; trial++; } } else { Log(LOG_INFO, "INFO ( %s/core ): short GTP packet read (%u/%u/tunnel). Snaplen issue ?\n", config.name, caplen, off + gtp_hdr_len); return FALSE; } return ret; } void reset_index_pkt_ptrs(struct packet_ptrs *pptrs) { pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_PACKET_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_MAC_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_VLAN_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_MPLS_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_L3_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_L4_PTR] = NULL; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_PAYLOAD_PTR] = NULL; pptrs->pkt_proto[CUSTOM_PRIMITIVE_L3_PTR] = FALSE; pptrs->pkt_proto[CUSTOM_PRIMITIVE_L4_PTR] = FALSE; } void set_index_pkt_ptrs(struct packet_ptrs *pptrs) { pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_PACKET_PTR] = pptrs->packet_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_MAC_PTR] = pptrs->mac_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_VLAN_PTR] = pptrs->vlan_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_MPLS_PTR] = pptrs->mpls_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_L3_PTR] = pptrs->iph_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_L4_PTR] = pptrs->tlh_ptr; pptrs->pkt_data_ptrs[CUSTOM_PRIMITIVE_PAYLOAD_PTR] = pptrs->payload_ptr; pptrs->pkt_proto[CUSTOM_PRIMITIVE_L3_PTR] = pptrs->l3_proto; pptrs->pkt_proto[CUSTOM_PRIMITIVE_L4_PTR] = pptrs->l4_proto; } ssize_t recvfrom_savefile(struct pm_pcap_device *device, void **buf, struct sockaddr *src_addr, struct timeval **ts, int *round, struct packet_ptrs *savefile_pptrs) { ssize_t ret = 0; int pm_pcap_ret; read_packet: pm_pcap_ret = pcap_next_ex(device->dev_desc, &savefile_pptrs->pkthdr, (const u_char **)&savefile_pptrs->packet_ptr); if (pm_pcap_ret == 1 /* all good */) device->errors = FALSE; else if (pm_pcap_ret == -1 /* failed reading next packet */) { device->errors++; if (device->errors == PCAP_SAVEFILE_MAX_ERRORS) { Log(LOG_ERR, "ERROR ( %s/core ): pcap_ext_ex() max errors reached (%u). Exiting.\n", config.name, PCAP_SAVEFILE_MAX_ERRORS); exit_gracefully(1); } else { Log(LOG_WARNING, "WARN ( %s/core ): pcap_ext_ex() failed: %s. Skipping packet.\n", config.name, pcap_geterr(device->dev_desc)); return 0; } } else if (pm_pcap_ret == -2 /* last packet in a pcap_savefile */) { pcap_close(device->dev_desc); if (config.pcap_sf_replay < 0 || (config.pcap_sf_replay > 0 && (*round) < config.pcap_sf_replay)) { (*round)++; open_pcap_savefile(device, config.pcap_savefile); if (config.pcap_sf_delay) sleep(config.pcap_sf_delay); goto read_packet; } if (config.pcap_sf_wait) { fill_pipe_buffer(); Log(LOG_INFO, "INFO ( %s/core ): finished reading PCAP capture file\n", config.name); wait(NULL); } stop_all_childs(); } else { Log(LOG_ERR, "ERROR ( %s/core ): unexpected return code from pcap_next_ex(). Exiting.\n", config.name); exit_gracefully(1); } (*device->data->handler)(savefile_pptrs->pkthdr, savefile_pptrs); if (savefile_pptrs->iph_ptr) { (*savefile_pptrs->l3_handler)(savefile_pptrs); if (savefile_pptrs->payload_ptr) { if (ts) (*ts) = &savefile_pptrs->pkthdr->ts; (*buf) = savefile_pptrs->payload_ptr; ret = savefile_pptrs->pkthdr->caplen - (savefile_pptrs->payload_ptr - savefile_pptrs->packet_ptr); if (savefile_pptrs->l4_proto == IPPROTO_UDP || savefile_pptrs->l4_proto == IPPROTO_TCP) { if (savefile_pptrs->l3_proto == ETHERTYPE_IP) { raw_to_sa((struct sockaddr *)src_addr, (u_char *) &((struct pm_iphdr *)savefile_pptrs->iph_ptr)->ip_src.s_addr, (u_int16_t) ((struct pm_udphdr *)savefile_pptrs->tlh_ptr)->uh_sport, AF_INET); } else if (savefile_pptrs->l3_proto == ETHERTYPE_IPV6) { raw_to_sa((struct sockaddr *)src_addr, (u_char *) &((struct ip6_hdr *)savefile_pptrs->iph_ptr)->ip6_src, (u_int16_t) ((struct pm_udphdr *)savefile_pptrs->tlh_ptr)->uh_sport, AF_INET6); } } } } return ret; } ssize_t recvfrom_rawip(unsigned char *buf, size_t len, struct sockaddr *src_addr, struct packet_ptrs *local_pptrs) { ssize_t ret = 0; local_pptrs->packet_ptr = buf; local_pptrs->pkthdr->caplen = len; raw_handler(local_pptrs->pkthdr, local_pptrs); if (local_pptrs->iph_ptr) { (*local_pptrs->l3_handler)(local_pptrs); if (local_pptrs->payload_ptr) { ret = local_pptrs->pkthdr->caplen - (local_pptrs->payload_ptr - local_pptrs->packet_ptr); if (local_pptrs->l4_proto == IPPROTO_UDP) { if (local_pptrs->l3_proto == ETHERTYPE_IP) { raw_to_sa((struct sockaddr *)src_addr, (u_char *) &((struct pm_iphdr *)local_pptrs->iph_ptr)->ip_src.s_addr, (u_int16_t) ((struct pm_udphdr *)local_pptrs->tlh_ptr)->uh_sport, AF_INET); } else if (local_pptrs->l3_proto == ETHERTYPE_IPV6) { raw_to_sa((struct sockaddr *)src_addr, (u_char *) &((struct ip6_hdr *)local_pptrs->iph_ptr)->ip6_src, (u_int16_t) ((struct pm_udphdr *)local_pptrs->tlh_ptr)->uh_sport, AF_INET6); } } /* last action: cut L3 and L4 off the packet */ memmove(buf, local_pptrs->payload_ptr, ret); } } return ret; } void pm_pcap_add_filter(struct pm_pcap_device *dev_ptr) { /* pcap library stuff */ struct bpf_program filter; memset(&filter, 0, sizeof(filter)); if (pcap_compile(dev_ptr->dev_desc, &filter, config.clbuf, 0, PCAP_NETMASK_UNKNOWN) < 0) { Log(LOG_WARNING, "WARN ( %s/core ): %s (going on without a filter)\n", config.name, pcap_geterr(dev_ptr->dev_desc)); } else { if (pcap_setfilter(dev_ptr->dev_desc, &filter) < 0) { Log(LOG_WARNING, "WARN ( %s/core ): %s (going on without a filter)\n", config.name, pcap_geterr(dev_ptr->dev_desc)); } else pcap_freecode(&filter); } }