Initial commit

This commit is contained in:
Pavel Odintsov 2013-10-18 14:16:55 +04:00
parent ad439d8605
commit f6a11792a6
5 changed files with 1071 additions and 0 deletions

2
INSTALL Normal file

@ -0,0 +1,2 @@
# https://git.netfilter.org/ulogd2/plain/include/libipulog/libipulog.h
# https://git.netfilter.org/ulogd2/plain/libipulog/libipulog.c

4
build.sh Executable file

@ -0,0 +1,4 @@
#!/bin/bash
g++ libipulog.c -c -o libipulog.o -Wno-write-strings
g++ fastnetmon.cpp libipulog.o -lpcap

736
fastnetmon.cpp Normal file

@ -0,0 +1,736 @@
/*
TODO:
1) ДОбавить среднюю нагрузку за 30 секунд/минуту/5 минут, хз как ее сделать :)
2) Добавить проверку на существование конфигам с сетями
3) Подумать на тему выноса всех параметров в конфиг
4) Сделать трейсер 100-200 пакетов при бане
*/
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <vector>
#include <utility>
#include <sstream>
#include <time.h>
// for boost split
#include <boost/algorithm/string.hpp>
// ULOG
#include "libipulog.h"
// Мы используем механизмы ULOG2
#define ULOG2
/*
Installing guide:
apt-get install -y libpcap-dev g++ gcc libboost-all-dev
yum install -y libpcap-devel gcc-c++ boost-devel boost
Compile it:
g++ fastsniffer.c -lpcap
Custom pcap:
Install PCAP from sources: http://www.stableit.ru/2013/10/lib-pcap-debian-squeeze.html
g++ sniffer.cpp -lpcap -I/opt/libpcap140/include -L/opt/libpcap140/lib
g++ sniffer.cpp -Linclude
LD_LIBRARY_PATH=/opt/libpcap140/lib ./a.out
Pcap docs:
http://www.linuxforu.com/2011/02/capturing-packets-c-program-libpcap/
http://vichargrave.com/develop-a-packet-sniffer-with-libpcap/ парсер отсюда
*/
using namespace std;
// not a good idea
//using namespace boost;
enum ulog_keys {
ULOG_KEY_RAW_MAC = 0,
ULOG_KEY_RAW_PCKT,
ULOG_KEY_RAW_PCKTLEN,
ULOG_KEY_RAW_PCKTCOUNT,
ULOG_KEY_OOB_PREFIX,
ULOG_KEY_OOB_TIME_SEC,
ULOG_KEY_OOB_TIME_USEC,
ULOG_KEY_OOB_MARK,
ULOG_KEY_OOB_IN,
ULOG_KEY_OOB_OUT,
ULOG_KEY_OOB_HOOK,
ULOG_KEY_RAW_MAC_LEN,
ULOG_KEY_OOB_FAMILY,
ULOG_KEY_OOB_PROTOCOL,
ULOG_KEY_RAW_LABEL,
};
// main data structure for storing traffic data for all our IPs
typedef map <uint32_t, int> map_for_counters;
map_for_counters PacketsCounterIncoming;
map_for_counters PacketsCounterOutgoing;
map_for_counters TrafficCounterIncoming;
map_for_counters TrafficCounterOutgoing;
enum direction {INCOMING, OUTGOING, INTERNAL, OTHER};
#ifdef ULOG2
// для подсчета числа ошибок буфера при работе по netlink
int netlink_error_counter = 0;
int netlink_packets_counter = 0;
#endif
string get_direction_name(direction direction_value) {
string direction_name;
switch (direction_value) {
case INCOMING: direction_name = "incoming"; break;
case OUTGOING: direction_name = "outgoing"; break;
case INTERNAL: direction_name = "internal"; break;
case OTHER: direction_name = "other"; break;
default: direction_name = "unknown"; break;
}
}
// делаем глобальной, так как нам нужно иметь к ней доступ из обработчика сигнала
pcap_t* descr;
int total_count_of_incoming_packets = 0;
int total_count_of_outgoing_packets = 0;
int total_count_of_other_packets = 0;
int total_count_of_internal_packets = 0;
int total_count_of_incoming_bytes = 0;
int total_count_of_outgoing_bytes = 0;
int total_count_of_other_bytes = 0;
int total_count_of_internal_bytes = 0;
map<uint32_t,int> ban_list;
time_t start_time;
int DEBUG = 0;
// Период, через который мы пересчитываем pps/трафик
int check_period = 3;
// Увеличиваем буфер, чтобы минимизировать потери пакетов
int pcap_buffer_size_mbytes = 10;
// Нас не интересуют запросы IP, у которых менее XXX pps в секунду
int threshhold = 2000;
// Баним IP, если он превысил данный порог
int ban_threshhold = 10000;
// data structure for storing data in Vector
typedef pair<uint32_t, int> pair_of_map_elements;
/*
Тут кроется огромный баго-фич:
В случае прослушивания any интерфейсов мы ловим фичу-баг, вместо эзернет хидера у нас тип 113, который LINUX SLL, а следовательно размер хидера не 14, а 16 байт!
Если мы сниффим один интерфейсе - у нас хидер эзернет, 14 байт, а если ANY, то хидер у нас 16 !!!
packetptr += 14; // Ethernet
packetptr += 16; // LINUX SLL, только в случае указания any интерфейса
Подробнее:
https://github.com/the-tcpdump-group/libpcap/issues/324
http://comments.gmane.org/gmane.network.tcpdump.devel/5043
http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
https://github.com/the-tcpdump-group/libpcap/issues/163
*/
// стандартно у нас смещение для типа DLT_EN10MB, Ethernet
int DATA_SHIFT_VALUE = 14;
typedef pair<uint32_t, uint32_t> subnet;
vector<subnet> our_networks;
vector<subnet> whitelist_networks;
// prototypes
void signal_handler(int signal_number);
uint32_t convert_cidr_to_binary_netmask(int cidr);
bool belongs_to_networks(vector<subnet> networks_list, uint32_t ip);
// Function for sorting Vector of pairs
bool compare_function (pair_of_map_elements a, pair_of_map_elements b) {
return a.second > b.second;
}
uint32_t convert_ip_as_string_to_uint(string ip) {
struct in_addr ip_addr;
inet_aton(ip.c_str(), &ip_addr);
// in network byte order
return ip_addr.s_addr;
}
string convert_ip_as_uint_to_string(uint32_t ip_as_string) {
struct in_addr ip_addr;
ip_addr.s_addr = ip_as_string;
return (string)inet_ntoa(ip_addr);
}
vector<string> exec(string cmd) {
vector<string> output_list;
FILE* pipe = popen(cmd.c_str(), "r");
if (!pipe) return output_list;
char buffer[256];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 256, pipe) != NULL) {
size_t newbuflen = strlen(buffer);
// remove newline at the end
if (buffer[newbuflen - 1] == '\n') {
buffer[newbuflen - 1] = '\0';
}
output_list.push_back(buffer);
}
}
pclose(pipe);
return output_list;
}
void draw_table(map_for_counters& my_map_packets, map_for_counters& my_map_traffic, string data_direction) {
std::vector<pair_of_map_elements> vector_for_sort;
/* Вобщем-то весь код ниже зависит лишь от входных векторов и порядка сортировки данных */
for( map<uint32_t,int>::iterator ii=my_map_packets.begin(); ii!=my_map_packets.end(); ++ii) {
// кладем все наши элементы в массив для последующей сортировки при отображении
pair_of_map_elements current_pair;
current_pair.first = (*ii).first;
current_pair.second = (*ii).second;
vector_for_sort.push_back(current_pair);
}
std::sort( vector_for_sort.begin(), vector_for_sort.end(), compare_function);
for( vector<pair_of_map_elements>::iterator ii=vector_for_sort.begin(); ii!=vector_for_sort.end(); ++ii) {
int pps = (*ii).second / check_period;
uint32_t client_ip = (*ii).first;
string client_ip_as_string = convert_ip_as_uint_to_string((*ii).first);
if (pps >= threshhold) {
string pps_as_string;
std::stringstream out;
out << pps;
pps_as_string = out.str();
// еси клиента еще нету в бан листе
if (pps > ban_threshhold) {
if (belongs_to_networks(whitelist_networks, client_ip)) {
// IP в белом списке
} else {
cout<<"!!!ALARM!!! WE MUST BAN THIS IP!!! ";
// add IP to BAN list
if (ban_list.count(client_ip) == 0) {
ban_list[client_ip] = pps;
cout << "*BAN EXECUTED* ";
exec("echo 'Please execute reglaments and notify client' | mail -s \"Myflower Guard: IP " + client_ip_as_string +" was locked, " + pps_as_string + " pps/" + data_direction + "\" odintsov@fastvps.ru,hohryakov@fastvps.ru,ziltsov@fastvps.ee");
} else {
cout << "*BAN EXECUTED* ";
// already in ban list
}
}
}
// determine attack speed
int bps = my_map_traffic[ (*ii).first ] / check_period;
// convert to mbps
int mbps = bps / 1024 / 1024 * 8;
cout << client_ip_as_string << "\t\t" << pps << " pps " << mbps << " Mbps" << endl;
}
}
}
#include <boost/assign/std/vector.hpp>
// bring 'operator+=()' into scope
using namespace boost::assign;
bool load_our_networks_list() {
// вносим в белый список, IP из этой сети мы не баним
subnet white_subnet = std::make_pair(convert_ip_as_string_to_uint("159.253.17.0"), convert_cidr_to_binary_netmask(24));
whitelist_networks.push_back(white_subnet);
vector<string> networks_list_as_string;
// если мы на openvz ноде, то "свои" IP мы можем получить из спец-файла в /proc
FILE *detect_openvz_file = fopen("/proc/vz/version", "r");
string our_networks_netmask;
if (detect_openvz_file) {
fclose(detect_openvz_file);
cout<<"We found OpenVZ"<<endl;
// тут искусствено добавляем суффикс 32
networks_list_as_string = exec("cat /proc/vz/veip | awk '{print $1\"/32\"}' |grep -vi version |grep -v ':'");
}
vector<string> network_list_from_config = exec("cat /etc/networks_list");
networks_list_as_string.insert(networks_list_as_string.end(), network_list_from_config.begin(), network_list_from_config.end());
// если это ложь, то в моих функциях косяк
assert( convert_ip_as_string_to_uint("255.255.255.0") == convert_cidr_to_binary_netmask(24) );
assert( convert_ip_as_string_to_uint("255.255.255.255") == convert_cidr_to_binary_netmask(32) );
for( vector<string>::iterator ii=networks_list_as_string.begin(); ii!=networks_list_as_string.end(); ++ii) {
vector<string> subnet_as_string;
split( subnet_as_string, *ii, boost::is_any_of("/"), boost::token_compress_on );
int cidr = atoi(subnet_as_string[1].c_str());
uint32_t subnet_as_int = convert_ip_as_string_to_uint(subnet_as_string[0]);
uint32_t netmask_as_int = convert_cidr_to_binary_netmask(cidr);
subnet current_subnet = std::make_pair(subnet_as_int, netmask_as_int);
//current_subnet.first = subnet_as_int;
//current_subnet.second = netmask_as_int;
our_networks.push_back(current_subnet);
}
return true;
}
uint32_t convert_cidr_to_binary_netmask(int cidr) {
uint32_t binary_netmask = 0xFFFFFFFF;
binary_netmask = binary_netmask << ( 32 - cidr );
// htonl from host byte order to network
// ntohl from network byte order to host
// поидее, на выходе тут нужен network byte order
return htonl(binary_netmask);
}
bool belongs_to_networks(vector<subnet> networks_list, uint32_t ip) {
for( vector<subnet>::iterator ii=networks_list.begin(); ii!=networks_list.end(); ++ii) {
if ( (ip & (*ii).second) == ((*ii).first & (*ii).second) ) {
return true;
}
}
return false;
}
// в случае прямого вызова скрипта колбэка - нужно конст, напрямую в хендлере - конст не нужно
void parse_packet(u_char *user, struct pcap_pkthdr *packethdr, const u_char *packetptr) {
struct ip* iphdr;
struct icmphdr* icmphdr;
struct tcphdr* tcphdr;
struct udphdr* udphdr;
char iphdrInfo[256], srcip_char[256], dstip_char[256];
unsigned short id, seq;
int packet_length;
// Skip the datalink layer header and get the IP header fields.
packetptr += DATA_SHIFT_VALUE;
iphdr = (struct ip*)packetptr;
// исходящий/входящий айпи это in_addr, http://man7.org/linux/man-pages/man7/ip.7.html
strcpy(srcip_char, inet_ntoa(iphdr->ip_src));
strcpy(dstip_char, inet_ntoa(iphdr->ip_dst));
uint32_t src_ip = iphdr->ip_src.s_addr;
uint32_t dst_ip = iphdr->ip_dst.s_addr;
//cout<<srcip_char<<" > "<<dstip_char<<endl;
// The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order
packet_length = ntohs(iphdr->ip_len);
direction packet_direction;
if (belongs_to_networks(our_networks, src_ip) && belongs_to_networks(our_networks, dst_ip)) {
packet_direction = INTERNAL;
total_count_of_internal_packets ++;
total_count_of_internal_bytes += packet_length;
} else if (belongs_to_networks(our_networks, src_ip)) {
packet_direction = OUTGOING;
total_count_of_outgoing_packets ++;
total_count_of_outgoing_bytes += packet_length;
PacketsCounterOutgoing[ src_ip ]++;
TrafficCounterOutgoing[ src_ip ] += packet_length;
} else if (belongs_to_networks(our_networks, dst_ip)) {
packet_direction = INCOMING;
total_count_of_incoming_packets++;
total_count_of_incoming_bytes += packet_length;
PacketsCounterIncoming[ dst_ip ]++;
TrafficCounterIncoming[ dst_ip ] += packet_length;
} else {
packet_direction = OTHER;
total_count_of_other_packets ++;
total_count_of_other_bytes += packet_length;
}
time_t current_time;
time(&current_time);
if ( difftime(current_time, start_time) >= check_period ) {
// clean up screen
system("clear");
cout<<"Below you can see all clients with more than "<<threshhold<<" pps"<<endl<<endl;
cout<<"Incoming Traffic"<<"\t"<<total_count_of_incoming_packets/check_period<<" pps "<<total_count_of_incoming_bytes/check_period/1024/1024*8<<" mbps"<<endl;
draw_table(PacketsCounterIncoming, TrafficCounterIncoming, "incoming");
cout<<endl;
cout<<"Outgoing traffic"<<"\t"<<total_count_of_outgoing_packets/check_period<<" pps "<<total_count_of_outgoing_bytes/check_period/1024/1024*8<<" mbps"<<endl;
draw_table(PacketsCounterOutgoing, TrafficCounterOutgoing, "outgoing");
cout<<endl;
cout<<"Internal traffic"<<"\t"<<total_count_of_internal_packets/check_period<<" pps"<<endl;
cout<<endl;
cout<<"Other traffic"<<"\t\t"<<total_count_of_other_packets/check_period<<" pps"<<endl;
cout<<endl;
#ifdef PCAP
struct pcap_stat current_pcap_stats;
if (pcap_stats(descr, &current_pcap_stats) == 0) {
cout<<"PCAP statistics"<<endl<<"Received packets: "<<current_pcap_stats.ps_recv<<endl
<<"Dropped packets: "<<current_pcap_stats.ps_drop
<<" ("<<int((double)current_pcap_stats.ps_drop/current_pcap_stats.ps_recv*100)<<"%)"<<endl
<<"Dropped by driver or interface: "<<current_pcap_stats.ps_ifdrop<<endl;
}
#endif
#ifdef ULOG2
cout<<"ULOG buffer errors: " << netlink_error_counter<<" ("<<int((double)netlink_error_counter/netlink_packets_counter)<<"%)"<<endl;
cout<<"ULOG packets received: "<< netlink_packets_counter<<endl;
#endif
if (ban_list.size() > 0) {
cout<<endl<<"Ban list:"<<endl;
for( map<uint32_t,int>::iterator ii=ban_list.begin(); ii!=ban_list.end(); ++ii) {
cout<<convert_ip_as_uint_to_string((*ii).first)<<"/"<<(*ii).second<<" pps"<<endl;
}
}
// переустанавливаем время запуска
time(&start_time);
// зануляем счетчики пакетов
PacketsCounterIncoming.clear();
PacketsCounterOutgoing.clear();
TrafficCounterIncoming.clear();
TrafficCounterOutgoing.clear();
/* вот здесь можно сбросить данные в Redis */
total_count_of_incoming_bytes = 0;
total_count_of_outgoing_bytes = 0;
total_count_of_other_packets = 0;
total_count_of_other_bytes = 0;
total_count_of_internal_packets = 0;
total_count_of_internal_bytes = 0;
total_count_of_incoming_packets = 0;
total_count_of_outgoing_packets = 0;
}
// Advance to the transport layer header then parse and display
// the fields based on the type of hearder: tcp, udp or icmp.
packetptr += 4*iphdr->ip_hl;
switch (iphdr->ip_p) {
case IPPROTO_TCP:
tcphdr = (struct tcphdr*)packetptr;
if (DEBUG) {
printf("TCP %s:%d -> %s:%d\n", srcip_char, ntohs(tcphdr->source), dstip_char, ntohs(tcphdr->dest));
}
//printf("%s\n", iphdrInfo);
/*
printf("%c%c%c%c%c%c Seq: 0x%x Ack: 0x%x Win: 0x%x TcpLen: %d\n",
(tcphdr->urg ? 'U' : '*'),
(tcphdr->ack ? 'A' : '*'),
(tcphdr->psh ? 'P' : '*'),
(tcphdr->rst ? 'R' : '*'),
(tcphdr->syn ? 'S' : '*'),
(tcphdr->fin ? 'F' : '*'),
ntohl(tcphdr->seq), ntohl(tcphdr->ack_seq),
ntohs(tcphdr->window), 4*tcphdr->doff);
*/
break;
case IPPROTO_UDP:
udphdr = (struct udphdr*)packetptr;
if (DEBUG) {
printf("UDP %s:%d -> %s:%d\n", srcip_char, ntohs(udphdr->source), dstip_char, ntohs(udphdr->dest));
}
//printf("%s\n", iphdrInfo);
break;
case IPPROTO_ICMP:
icmphdr = (struct icmphdr*)packetptr;
if (DEBUG) {
printf("ICMP %s -> %s\n", srcip_char, dstip_char);
}
break;
}
}
int main(int argc,char **argv) {
int i;
char *dev;
char errbuf[PCAP_ERRBUF_SIZE];
const u_char *packet;
struct pcap_pkthdr hdr;
struct ether_header *eptr; /* net/ethernet.h */
struct bpf_program fp; /* hold compiled program */
time(&start_time);
printf("I need few seconds for collecting data, please wait. Thank you!\n");
if (argc != 2) {
fprintf(stdout, "Usage: %s \"eth0\" or \"any\"\n", argv[0]);
cout<< "We must automatically select interface"<<endl;
/* Now get a device */
dev = pcap_lookupdev(errbuf);
if(dev == NULL) {
fprintf(stderr, "%s\n", errbuf);
exit (1);
}
printf("Automatically selected %s device\n", dev);
} else {
dev = argv[1];
}
// загружаем наши сети
load_our_networks_list();
/* open device for reading in promiscuous mode */
int promisc = 1;
int pcap_read_timeout = -1;
bpf_u_int32 maskp; /* subnet mask */
bpf_u_int32 netp; /* ip */
#ifdef PCAP
cout<<"Start listening on "<<dev<<endl;
/* Get the network address and mask */
pcap_lookupnet(dev, &netp, &maskp, errbuf);
descr = pcap_create(dev, errbuf);
if (descr == NULL) {
printf("pcap_create was failed with error: %s", errbuf);
exit(0);
}
int set_buffer_size_res = pcap_set_buffer_size(descr, pcap_buffer_size_mbytes * 1024 * 1024);
if (set_buffer_size_res != 0 ) { // выставляем буфер в 1 мегабайт
if (set_buffer_size_res == PCAP_ERROR_ACTIVATED) {
printf("Can't set buffer size because pcap already activated\n");
exit(1);
} else {
printf("Can't set buffer size due to error %d\n", set_buffer_size_res);
exit(1);
}
}
/*
Вот через этот спец механизм можно собирать лишь хидеры!
If you don't need the entire contents of the packet - for example, if you are only interested in the TCP headers of packets - you can set the "snapshot length" for the capture to an appropriate value.
*/
/*
if (pcap_set_snaplen(descr, 32 ) != 0 ) {
printf("Can't set snap len\n");
exit(1);
}
*/
if (pcap_set_promisc(descr, promisc) != 0) {
printf("Can't activate promisc mode for interface: %s\n", dev);
exit(1);
}
if (pcap_activate(descr) != 0) {
printf("Call pcap_activate was failed: %s\n", pcap_geterr(descr));
exit(1);
}
/*
descr = pcap_open_live(dev, BUFSIZ, promisc, pcap_read_timeout, errbuf);
if(descr == NULL) {
printf("pcap_open_live(): %s\n", errbuf);
exit(1);
}
*/
// В общем-то можно фильтровать то, что нам падает от PCAP, но в моем случае это совершенно не требуется
// тут было argv[1], но я убрал фильтрацию
/* Now we'll compile the filter expression*/
//if(pcap_compile(descr, &fp, "", 0, netp) == -1) {
// fprintf(stderr, "Error calling pcap_compile\n");
// exit(1);
//}
/* set the filter */
//if(pcap_setfilter(descr, &fp) == -1) {
// fprintf(stderr, "Error setting filter\n");
// exit(1);
//}
/* loop for callback function */
// pcap_setnonblock(descr, 1, NULL);
signal(SIGINT, signal_handler);
// man pcap-linktype
int link_layer_header_type = pcap_datalink(descr);
if (link_layer_header_type == DLT_EN10MB) {
DATA_SHIFT_VALUE = 14;
} else if (link_layer_header_type == DLT_LINUX_SLL) {
DATA_SHIFT_VALUE = 16;
} else {
printf("We did not support link type %d\n", link_layer_header_type);
exit(0);
}
#endif
#ifdef PCAP
// пока деактивируем pcap, начинаем интегрировать ULOG
pcap_loop(descr, -1, (pcap_handler)parse_packet, NULL);
#endif
/* Size of the socket receive memory. Should be at least the same size as the 'nlbufsiz' module loadtime parameter of ipt_ULOG.o If you have _big_ in-kernel queues, you may have to increase this number. (
* --qthreshold 100 * 1500 bytes/packet = 150kB */
int ULOGD_RMEM_DEFAULT = 131071;
// В загрузке модуля есть параметры: modprobe ipt_ULOG nlbufsiz=131072
// Увеличиваем размер буфера в ядре, так как стандартно он всего-то 3712
// Так задать нельзя, только при запуске модуля ядром
//exec("echo '131072' > /sys/module/ipt_ULOG/parameters/nlbufsiz");
/* Size of the receive buffer for the netlink socket. Should be at least of RMEM_DEFAULT size. */
int ULOGD_BUFSIZE_DEFAULT = 150000;
int ULOGD_NLGROUP_DEFAULT = 1;
struct ipulog_handle *libulog_h;
unsigned char *libulog_buf;
libulog_buf = (unsigned char*)malloc(ULOGD_BUFSIZE_DEFAULT);
if (!libulog_buf) {
printf("Can't allocate buffer");
exit(1);
}
libulog_h = ipulog_create_handle(ipulog_group2gmask(ULOGD_NLGROUP_DEFAULT), ULOGD_RMEM_DEFAULT);
if (!libulog_h) {
printf("Can't create ipulog handle");
exit(0);
}
int len;
while ( len = ipulog_read(libulog_h, libulog_buf, ULOGD_BUFSIZE_DEFAULT) ) {
if (len <= 0) {
if (errno == EAGAIN) {
break;
}
if (errno == 105) {
// Наш уютный бажик: errno = '105' ('No buffer space available'
netlink_error_counter++;
continue;
}
// поймали ошибку - зафиксируем ее при расчетах
printf("ipulog_read = '%d'! "
"ipulog_errno = '%d' ('%s'), "
"errno = '%d' ('%s')\n",
len, ipulog_errno,
ipulog_strerror(ipulog_errno),
errno, strerror(errno));
continue;
}
// успешний прием пакета
netlink_packets_counter++;
ulog_packet_msg_t *upkt;
while ((upkt = ipulog_get_packet(libulog_h, libulog_buf, len))) {
// вот такой хитрый хак, так как данные начинаются без ethernet хидера
DATA_SHIFT_VALUE = 0;
parse_packet(NULL, NULL, upkt->payload);
}
}
free(libulog_buf);
/*
Альтернативный парсер, пока не совсем корректно работает, так как возвращает NULL
const u_char* packetptr;
struct pcap_pkthdr packethdr;
while ( (packetptr = pcap_next(descr, &packethdr) ) != NULL) {
parse_packet(NULL, &packethdr, packetptr);
}
*/
return 0;
}
// для корректной останвоки программы
void signal_handler(int signal_number) {
#ifdef PCAP
// останавливаем PCAP цикл
pcap_breakloop(descr);
#endif
exit(1);
}

271
libipulog.c Normal file

@ -0,0 +1,271 @@
/*
* libipulog.c
*
* netfilter ULOG userspace library.
*
* (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* 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
*
* This library is still under development, so be aware of sudden interface
* changes
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include "libipulog.h"
struct ipulog_handle
{
int fd;
u_int8_t blocking;
struct sockaddr_nl local;
struct sockaddr_nl peer;
struct nlmsghdr* last_nlhdr;
};
/* internal */
int ipulog_errno = IPULOG_ERR_NONE;
struct ipulog_errmap_t
{
int errcode;
char *message;
} ipulog_errmap[] =
{
{ IPULOG_ERR_NONE, "No error" },
{ IPULOG_ERR_IMPL, "Not implemented yet" },
{ IPULOG_ERR_HANDLE, "Unable to create netlink handle" },
{ IPULOG_ERR_SOCKET, "Unable to create netlink socket" },
{ IPULOG_ERR_BIND, "Unable to bind netlink socket" },
{ IPULOG_ERR_RECVBUF, "Receive buffer size invalid" },
{ IPULOG_ERR_RECV, "Error during netlink receive" },
{ IPULOG_ERR_NLEOF, "Received EOF on netlink socket" },
{ IPULOG_ERR_TRUNC, "Receive message truncated" },
{ IPULOG_ERR_INVGR, "Invalid group specified" },
{ IPULOG_ERR_INVNL, "Invalid netlink message" },
};
static ssize_t
ipulog_netlink_recvfrom(const struct ipulog_handle *h,
unsigned char *buf, size_t len)
{
socklen_t addrlen;
int status;
struct nlmsghdr *nlh;
if (len < sizeof(struct nlmsgerr)) {
ipulog_errno = IPULOG_ERR_RECVBUF;
return -1;
}
addrlen = sizeof(h->peer);
status = recvfrom(h->fd, buf, len, 0, (struct sockaddr *)&h->peer, &addrlen);
if (status < 0) {
//printf("status < 0: %d\n", status);
// траблы тут!!! Поймал
ipulog_errno = IPULOG_ERR_RECV;
return status;
}
if (addrlen != sizeof (h->peer)) {
//printf("addrlen != sizeof (h->peer)");
ipulog_errno = IPULOG_ERR_RECV;
return -1;
}
if (h->peer.nl_pid != 0) {
//printf("h->peer.nl_pid != 0\n");
ipulog_errno = IPULOG_ERR_RECV;
return -1;
}
if (status == 0) {
ipulog_errno = IPULOG_ERR_NLEOF;
return -1;
}
nlh = (struct nlmsghdr *)buf;
if (nlh->nlmsg_flags & MSG_TRUNC || (size_t) status > len) {
ipulog_errno = IPULOG_ERR_TRUNC;
return -1;
}
return status;
}
/* public */
char *ipulog_strerror(int errcode)
{
if (errcode < 0 || errcode > IPULOG_MAXERR)
errcode = IPULOG_ERR_IMPL;
return ipulog_errmap[errcode].message;
}
/* convert a netlink group (1-32) to a group_mask suitable for create_handle */
u_int32_t ipulog_group2gmask(u_int32_t group)
{
if (group < 1 || group > 32)
{
ipulog_errno = IPULOG_ERR_INVGR;
return 0;
}
return (1 << (group - 1));
}
/* create a ipulog handle for the reception of packets sent to gmask */
struct ipulog_handle *ipulog_create_handle(u_int32_t gmask,
u_int32_t rcvbufsize)
{
struct ipulog_handle *h;
int status;
h = (struct ipulog_handle *) malloc(sizeof(struct ipulog_handle));
if (h == NULL)
{
ipulog_errno = IPULOG_ERR_HANDLE;
return NULL;
}
memset(h, 0, sizeof(struct ipulog_handle));
h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
if (h->fd == -1)
{
ipulog_errno = IPULOG_ERR_SOCKET;
close(h->fd);
free(h);
return NULL;
}
memset(&h->local, 0, sizeof(struct sockaddr_nl));
h->local.nl_family = AF_NETLINK;
h->local.nl_pid = getpid();
h->local.nl_groups = gmask;
status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
if (status == -1)
{
ipulog_errno = IPULOG_ERR_BIND;
close(h->fd);
free(h);
return NULL;
}
memset(&h->peer, 0, sizeof(struct sockaddr_nl));
h->peer.nl_family = AF_NETLINK;
h->peer.nl_pid = 0;
h->peer.nl_groups = gmask;
//printf("Allocated %d bytes buffer size\n", rcvbufsize);
//rcvbufsize = 4;
//void* socket_buffer = malloc(rcvbufsize);
status = setsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
sizeof(rcvbufsize));
//status = setsockopt(h->fd, SOL_SOCKET, SO_RCVBUF, socket_buffer, rcvbufsize);
if (status == -1)
{
ipulog_errno = IPULOG_ERR_RECVBUF;
close(h->fd);
free(h);
return NULL;
}
return h;
}
/* destroy a ipulog handle */
void ipulog_destroy_handle(struct ipulog_handle *h)
{
close(h->fd);
free(h);
}
#if 0
int ipulog_set_mode()
{
}
#endif
/* do a BLOCKING read on an ipulog handle */
ssize_t ipulog_read(struct ipulog_handle *h, unsigned char *buf,
size_t len)
{
return ipulog_netlink_recvfrom(h, buf, len);
}
/* get a pointer to the actual start of the ipulog packet,
use this to strip netlink header */
ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
const unsigned char *buf,
size_t len)
{
struct nlmsghdr *nlh;
size_t remain_len;
/* if last header in handle not inside this buffer,
* drop reference to last header */
if ((unsigned char *)h->last_nlhdr > (buf + len) ||
(unsigned char *)h->last_nlhdr < buf) {
h->last_nlhdr = NULL;
}
if (!h->last_nlhdr) {
/* fist message in buffer */
nlh = (struct nlmsghdr *) buf;
if (!NLMSG_OK(nlh, len)) {
/* ERROR */
ipulog_errno = IPULOG_ERR_INVNL;
return NULL;
}
} else {
/* we are in n-th part of multilink message */
if (h->last_nlhdr->nlmsg_type == NLMSG_DONE ||
!(h->last_nlhdr->nlmsg_flags & NLM_F_MULTI)) {
/* if last part in multilink message,
* or no multipart message at all: return */
h->last_nlhdr = NULL;
return NULL;
}
/* calculate remaining lenght from lasthdr to end of buffer */
remain_len = (len -
((unsigned char *)h->last_nlhdr - buf));
nlh = NLMSG_NEXT(h->last_nlhdr, remain_len);
}
h->last_nlhdr = nlh;
return (ulog_packet_msg_t*)NLMSG_DATA(nlh);
}
/* print a human readable description of the last error to stderr */
void ipulog_perror(const char *s)
{
if (s)
fputs(s, stderr);
else
fputs("ERROR", stderr);
if (ipulog_errno)
fprintf(stderr, ": %s", ipulog_strerror(ipulog_errno));
if (errno)
fprintf(stderr, ": %s", strerror(errno));
fputc('\n', stderr);
}
int ipulog_get_fd(struct ipulog_handle *h)
{
return h->fd;
}

58
libipulog.h Normal file

@ -0,0 +1,58 @@
#ifndef _LIBIPULOG_H
#define _LIBIPULOG_H
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <net/if.h>
#include <linux/netfilter_ipv4/ipt_ULOG.h>
/* FIXME: glibc sucks */
#ifndef MSG_TRUNC
#define MSG_TRUNC 0x20
#endif
struct ipulog_handle;
extern int ipulog_errno;
u_int32_t ipulog_group2gmask(u_int32_t group);
struct ipulog_handle *ipulog_create_handle(u_int32_t gmask, u_int32_t rmem);
void ipulog_destroy_handle(struct ipulog_handle *h);
ssize_t ipulog_read(struct ipulog_handle *h,
unsigned char *buf, size_t len);
ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
const unsigned char *buf,
size_t len);
char *ipulog_strerror(int errcode);
int ipulog_get_fd(struct ipulog_handle *h);
void ipulog_perror(const char *s);
enum
{
IPULOG_ERR_NONE = 0,
IPULOG_ERR_IMPL,
IPULOG_ERR_HANDLE,
IPULOG_ERR_SOCKET,
IPULOG_ERR_BIND,
IPULOG_ERR_RECVBUF,
IPULOG_ERR_RECV,
IPULOG_ERR_NLEOF,
IPULOG_ERR_TRUNC,
IPULOG_ERR_INVGR,
IPULOG_ERR_INVNL,
};
#define IPULOG_MAXERR IPULOG_ERR_INVNL
#endif /* _LIBULOG_H */