Esempio n. 1
0
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);
}
Esempio n. 2
0
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");
}
Esempio n. 3
0
/**
 * \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;
}
Esempio n. 4
0
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);
}