void NetherNetlink::setVerdict(const u_int32_t packetId, const NetherVerdict verdict, int32_t mark) { int ret = 0; LOGD("id=" << packetId << " verdict=" << verdictToString(verdict) << " mark=" << mark); if(verdict == NetherVerdict::allow) { if (mark >= 0) { ret = nfq_set_verdict2(queueHandle, packetId, NF_ACCEPT, mark, 0, NULL); } else { ret = nfq_set_verdict(queueHandle, packetId, NF_ACCEPT, 0, NULL); } } if(verdict == NetherVerdict::deny) { ret = nfq_set_verdict2(queueHandle, packetId, NF_ACCEPT, /* if we're relaxed, let's not stress out */ /* if we get a mark from the verdict caster */ /* let's use it, maybe it knows better */ mark > 0 ? mark : (netherConfig.relaxed ? netherConfig.markAllowAndLog : netherConfig.markDeny), 0, NULL); } if(verdict == NetherVerdict::allowAndLog) ret = nfq_set_verdict2(queueHandle, packetId, NF_ACCEPT, netherConfig.markAllowAndLog, 0, NULL); if(ret == -1) LOGW("can't set verdict for packetId=" << packetId); }
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) { struct nfqnl_msg_packet_hdr *ph; u_int32_t id; ph = nfq_get_msg_packet_hdr(nfa); assert (ph); id = ntohl(ph->packet_id); uint8_t *pkt; int len = nfq_get_payload(nfa, &pkt); printf("id %d: got %d bytes payload\n", id, len); u_int32_t verdict = NF_REPEAT; /* by default, just mark so we can REJECT */ if (len >= (signed)sizeof(struct iphdr)) { struct iphdr *iph = (struct iphdr *)pkt; if (iph->version == 4) { struct in_addr haddr = {.s_addr = iph->saddr}; printf("host: %s\n", inet_ntoa(haddr)); for (const char **host = hosts; *host; ++host) { printf("trying %s\n", *host); struct hostent *he = gethostbyname(*host); if (he) { for (char **alp = he->h_addr_list; *alp; ++alp) { if (((struct in_addr *)*alp)->s_addr == haddr.s_addr) { printf("ok\n"); verdict = NF_ACCEPT; break; } } } if (verdict == NF_ACCEPT) break; } } } int rv; rv = nfq_set_verdict2(qh, id, verdict, 1, 0, NULL); fprintf(stderr, "set_verdict2: %d\n", rv); assert (rv >= 0); return 0; } static void usage() { fprintf(stderr, "Usage: namematch [-h dest_hostname ...]\n"); }
/** * \brief NFQ verdict function */ TmEcode NFQSetVerdict(Packet *p) { int iter = 0; int ret = 0; uint32_t verdict = NF_ACCEPT; /* we could also have a direct pointer but we need to have a ref counf in this case */ NFQQueueVars *t = nfq_q + p->nfq_v.nfq_index; /** \todo add a test on validity of the entry NFQQueueVars could have been * wipeout */ /* can't verdict a "fake" packet */ if (p->flags & PKT_PSEUDO_STREAM_END) { return TM_ECODE_OK; } //printf("%p verdicting on queue %" PRIu32 "\n", t, t->queue_num); NFQMutexLock(t); if (t->qh == NULL) { /* Somebody has started a clean-up, we leave */ NFQMutexUnlock(t); return TM_ECODE_OK; } if (p->action & ACTION_DROP) { verdict = NF_DROP; #ifdef COUNTERS t->dropped++; #endif /* COUNTERS */ } else { switch (nfq_config.mode) { default: case NFQ_ACCEPT_MODE: verdict = NF_ACCEPT; break; case NFQ_REPEAT_MODE: verdict = NF_REPEAT; break; case NFQ_ROUTE_MODE: verdict = ((uint32_t) NF_QUEUE) | nfq_config.next_queue; break; } if (p->flags & PKT_STREAM_MODIFIED) { #ifdef COUNTERS t->replaced++; #endif /* COUNTERS */ } #ifdef COUNTERS t->accepted++; #endif /* COUNTERS */ } do { switch (nfq_config.mode) { default: case NFQ_ACCEPT_MODE: case NFQ_ROUTE_MODE: if (p->flags & PKT_MARK_MODIFIED) { #ifdef HAVE_NFQ_SET_VERDICT2 if (p->flags & PKT_STREAM_MODIFIED) { ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, p->nfq_v.mark, GET_PKT_LEN(p), GET_PKT_DATA(p)); } else { ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, p->nfq_v.mark, 0, NULL); } #else /* fall back to old function */ if (p->flags & PKT_STREAM_MODIFIED) { ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, htonl(p->nfq_v.mark), GET_PKT_LEN(p), GET_PKT_DATA(p)); } else { ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, htonl(p->nfq_v.mark), 0, NULL); } #endif /* HAVE_NFQ_SET_VERDICT2 */ } else { if (p->flags & PKT_STREAM_MODIFIED) { ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, GET_PKT_LEN(p), GET_PKT_DATA(p)); } else { ret = nfq_set_verdict(t->qh, p->nfq_v.id, verdict, 0, NULL); } } break; case NFQ_REPEAT_MODE: #ifdef HAVE_NFQ_SET_VERDICT2 if (p->flags & PKT_STREAM_MODIFIED) { ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask), GET_PKT_LEN(p), GET_PKT_DATA(p)); } else { ret = nfq_set_verdict2(t->qh, p->nfq_v.id, verdict, (nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask), 0, NULL); } #else /* fall back to old function */ if (p->flags & PKT_STREAM_MODIFIED) { ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)), GET_PKT_LEN(p), GET_PKT_DATA(p)); } else { ret = nfq_set_verdict_mark(t->qh, p->nfq_v.id, verdict, htonl((nfq_config.mark & nfq_config.mask) | (p->nfq_v.mark & ~nfq_config.mask)), 0, NULL); } #endif /* HAVE_NFQ_SET_VERDICT2 */ break; } } while ((ret < 0) && (iter++ < NFQ_VERDICT_RETRY_TIME)); NFQMutexUnlock(t); if (ret < 0) { SCLogWarning(SC_ERR_NFQ_SET_VERDICT, "nfq_set_verdict of %p failed %" PRId32 "", p, ret); return TM_ECODE_FAILED; } return TM_ECODE_OK; }
void PktAnalyzer::analyzer(Packet &pkt) { Poco::Stopwatch sw; _logger.debug("Got packet from queue"); unsigned char *full_packet=pkt.get_payload(); int id = pkt.get_id(); uint32_t size = pkt.get_size(); struct nfq_q_handle *qh=pkt.get_qh(); struct ip *iph = (struct ip *)full_packet; struct ip6_hdr *iph6 = (struct ip6_hdr *)full_packet; // определяем версию протокола int ip_version=0; if(iph->ip_v == 6) ip_version = 6; else if (iph->ip_v == 4) ip_version = 4; if(!ip_version) { _logger.error("Unsupported IP protocol version %d for packet id %d",(int) iph->ip_v, id); nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); dump_file(full_packet,size,id); return ; } unsigned char *pkt_data_ptr = NULL; struct tcphdr* tcph; pkt_data_ptr = full_packet + (ip_version == 4 ? sizeof(struct ip) : sizeof(struct ip6_hdr)); tcph = (struct tcphdr *) pkt_data_ptr; // длина ip заголовка int iphlen = iphdr(full_packet)->ihl*4; // ipv4 if(ip_version == 6) iphlen = sizeof(struct ip6_hdr); // длина tcp заголовка int tcphlen = tcphdr(full_packet+iphlen)->doff*4; // общая длина всех заголовков uint32_t hlen = iphlen + tcphlen; _parent->inc_total_bytes_packets(size); // пропускаем пакет без данных if(hlen == size) { nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); return ; } int tcp_src_port=ntohs(tcph->source); int tcp_dst_port=ntohs(tcph->dest); std::unique_ptr<Poco::Net::IPAddress> src_ip; std::unique_ptr<Poco::Net::IPAddress> dst_ip; if(ip_version == 4) { src_ip.reset(new Poco::Net::IPAddress(&iph->ip_src,sizeof(in_addr))); dst_ip.reset(new Poco::Net::IPAddress(&iph->ip_dst,sizeof(in_addr))); } else { src_ip.reset(new Poco::Net::IPAddress(&iph6->ip6_src,sizeof(in6_addr))); dst_ip.reset(new Poco::Net::IPAddress(&iph6->ip6_dst,sizeof(in6_addr))); } uint8_t ip_protocol=(ip_version == 4 ? iph->ip_p : iph6->ip6_ctlun.ip6_un1.ip6_un1_nxt); { Poco::ScopedReadRWLock lock(nfqFilter::_ipportMapMutex); IPPortMap::iterator it_ip=nfqFilter::_ipportMap->find(*dst_ip.get()); if(it_ip != nfqFilter::_ipportMap->end()) { unsigned short port=tcp_dst_port; if (it_ip->second.size() == 0 || it_ip->second.find(port) != it_ip->second.end()) { _parent->inc_matched_ip_port(); if(_config.send_rst) { _logger.debug("HostList: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); std::string empty_str; SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); _parent->inc_sended_rst(); nfq_set_verdict(qh,id,NF_DROP,0,NULL); } else { _logger.debug("HostList: Set mark %d to packet no %d port %hu",_config.mark_value,id,port); _parent->inc_marked_hosts(); nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); } return ; } } } // nDPI usage sw.reset(); sw.start(); nDPIWrapper nw; struct ndpi_flow_struct *flow=nw.get_flow(); uint32_t current_tickt = 0; ndpi_protocol protocol = ndpi_detection_process_packet(nfqFilter::my_ndpi_struct, flow, full_packet, size, current_tickt, nw.get_src(), nw.get_dst()); if(protocol.protocol == NDPI_PROTOCOL_UNKNOWN) { _logger.debug("Guessing protocol..."); protocol = ndpi_guess_undetected_protocol(nfqFilter::my_ndpi_struct, ip_protocol, 0,//ip tcp_src_port, // sport 0, tcp_dst_port); // dport } _logger.debug("Protocol is %hu/%hu ",protocol.master_protocol,protocol.protocol); sw.stop(); _logger.debug("nDPI protocol detection occupied %ld us",sw.elapsed()); if(protocol.master_protocol == NDPI_PROTOCOL_SSL || protocol.protocol == NDPI_PROTOCOL_SSL || protocol.protocol == NDPI_PROTOCOL_TOR) { if(flow->l4.tcp.ssl_seen_client_cert == 1) { std::string ssl_client; _logger.debug("Analysing SSL protocol"); if(flow->protos.ssl.client_certificate[0] != '\0') { ssl_client=flow->protos.ssl.client_certificate; _logger.debug("SSL client is: %s",ssl_client); } if(!ssl_client.empty()) { sw.reset(); sw.start(); if(_config.lower_host) std::transform(ssl_client.begin(), ssl_client.end(), ssl_client.begin(), ::tolower); AhoCorasickPlus::Match match; std::size_t host_len=ssl_client.length(); bool found=false; { Poco::Mutex::ScopedLock lock(nfqFilter::_sslMutex); nfqFilter::atm_ssl->search(ssl_client,false); while(nfqFilter::atm_ssl->findNext(match) && !found) { if(match.pattern.length != host_len) { DomainsMatchType::Iterator it=nfqFilter::_SSLdomainsMatchType->find(match.id); bool exact_match=false; if(it != nfqFilter::_SSLdomainsMatchType->end()) exact_match = it->second; if(exact_match) continue; if(ssl_client[host_len-match.pattern.length-1] != '.') continue; } found=true; } } sw.stop(); _logger.debug("SSL Host seek occupied %ld us, host: %s",sw.elapsed(),ssl_client); if(found) { _parent->inc_matched_ssl(); if(_config.send_rst) { _logger.debug("SSLHostList: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); std::string empty_str; SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); _parent->inc_sended_rst(); nfq_set_verdict(qh,id,NF_DROP,0,NULL); } else { _logger.debug("SSLHostList: Set mark %d to packet no %d, ssl host name: %s",_config.mark_value,id,ssl_client); _parent->inc_marked_ssl(); nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); } return ; } else { nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); return ; } } else { if(_config.block_undetected_ssl) { Poco::ScopedReadRWLock lock(nfqFilter::_sslIpsSetMutex); if(nfqFilter::_sslIps->try_search_exact_ip(*dst_ip.get())) { _parent->inc_matched_ssl_ip(); _logger.debug("Blocking/Marking SSL client hello packet from %s:%d to %s:%d", src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); if(_config.send_rst) { _logger.debug("SSLClientHello: Send RST to the client (%s) and server (%s) (packet no %d)",src_ip->toString(),dst_ip->toString(),id); std::string empty_str; SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),empty_str,true)); _parent->inc_sended_rst(); nfq_set_verdict(qh,id,NF_DROP,0,NULL); } else { _logger.debug("SSLClientHello: Set mark %d to packet no %d",_config.mark_value,id); _parent->inc_marked_ssl(); nfq_set_verdict2(qh,id,NF_ACCEPT,_config.mark_value,0,NULL); } return ; } } _logger.debug("No ssl client certificate found! Accept packet from %s:%d to %s:%d.",src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); } } nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); return ; } if(protocol.master_protocol != NDPI_PROTOCOL_HTTP && protocol.protocol != NDPI_PROTOCOL_HTTP && protocol.protocol != NDPI_PROTOCOL_DIRECT_DOWNLOAD_LINK) { _logger.debug("Not http protocol. Protocol is %hu/%hu from %s:%d to %s:%d",protocol.master_protocol,protocol.protocol,src_ip->toString(),tcp_src_port,dst_ip->toString(),tcp_dst_port); nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); return ; } _logger.debug("Got HTTP protocol"); std::string host((char *)&flow->host_server_name[0]); if((flow->http.method == HTTP_METHOD_GET || flow->http.method == HTTP_METHOD_POST || flow->http.method == HTTP_METHOD_HEAD) && !host.empty()) { int dot_del=0; if(host[host.length()-1] == '.') { dot_del=host.length()-1; host.erase(dot_del,1); } if(_config.lower_host) std::transform(host.begin(), host.end(), host.begin(), ::tolower); sw.reset(); sw.start(); AhoCorasickPlus::Match match; bool found=false; { Poco::Mutex::ScopedLock lock(nfqFilter::_domainMapMutex); nfqFilter::atm_domains->search(host,false); std::size_t host_len=host.length(); while(nfqFilter::atm_domains->findNext(match) && !found) { if(match.pattern.length != host_len) { DomainsMatchType::Iterator it=nfqFilter::_domainsMatchType->find(match.id); bool exact_match=false; if(it != nfqFilter::_domainsMatchType->end()) exact_match = it->second; if(exact_match) continue; if(host[host_len-match.pattern.length-1] != '.') continue; } found=true; } } sw.stop(); _logger.debug("Host seek occupied %ld us",sw.elapsed()); if(found) { _logger.debug("Host %s present in domain (file line %d) list from ip %s", host, match.id, src_ip->toString()); std::string add_param; switch (_config.add_p_type) { case A_TYPE_ID: add_param="id="+std::to_string(match.id); break; case A_TYPE_URL: add_param="url="+host; break; default: break; } SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port, src_ip.get(), dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),add_param)); _parent->inc_redirected_domains(); nfq_set_verdict(qh,id,NF_DROP,0,NULL); return ; } sw.reset(); sw.start(); found=false; std::string uri_o(flow->http.url ? flow->http.url : ""); if(flow->http.url) { std::string uri; if(dot_del) uri_o.erase(dot_del+7,1); try { Poco::URI uri_p(uri_o); uri_p.normalize(); uri.assign(uri_p.toString()); if(_config.url_decode) { #ifdef __USE_POCO_URI_DECODE Poco::URI::decode(uri_p.toString(),uri); #else uri=url_decode(uri); #endif } } catch (Poco::SyntaxException &ex) { _logger.debug("An SyntaxException occured: '%s' on URI: '%s'",ex.displayText(), uri_o); uri.assign(flow->http.url); } { Poco::Mutex::ScopedLock lock(nfqFilter::_urlMapMutex); nfqFilter::atm->search(uri,false); while(nfqFilter::atm->findNext(match) && !found) { if(_config.match_url_exactly && uri.length() != match.pattern.length) continue; found=true; } } sw.stop(); _logger.debug("URL seek occupied %ld us for uri %s",sw.elapsed(),uri); if(found) { _logger.debug("URL %s present in url (file pos %u) list from ip %s",uri,match.id,src_ip->toString()); std::string add_param; switch (_config.add_p_type) { case A_TYPE_ID: add_param="id="+std::to_string(match.id); break; case A_TYPE_URL: add_param="url="+uri; break; default: break; } SenderTask::queue.enqueueNotification(new RedirectNotification(tcp_src_port, tcp_dst_port,src_ip.get(),dst_ip.get(),/*acknum*/ tcph->ack_seq, /*seqnum*/ tcph->seq,/* flag psh */ (tcph->psh ? 1 : 0 ),add_param)); _parent->inc_redirected_urls(); nfq_set_verdict(qh,id,NF_DROP,0,NULL); return ; } } } nfq_set_verdict(qh,id,NF_ACCEPT,0,NULL); }