diff options
author | Peter Volkov <pva@gentoo.org> | 2010-08-30 11:26:55 +0000 |
---|---|---|
committer | Peter Volkov <pva@gentoo.org> | 2010-08-30 11:26:55 +0000 |
commit | d1c3a641a485a1612fd413ba50332fb0a4849dd4 (patch) | |
tree | cf8ff83f80acff06e1b4978a1d895ecb653c016c | |
parent | app-text/kbib: drop kde3 package. (diff) | |
download | pva-d1c3a641a485a1612fd413ba50332fb0a4849dd4.tar.gz pva-d1c3a641a485a1612fd413ba50332fb0a4849dd4.tar.bz2 pva-d1c3a641a485a1612fd413ba50332fb0a4849dd4.zip |
net-misc/dhcp: missed patch added.
svn path=/; revision=450
-rw-r--r-- | net-misc/dhcp/files/dhcp-3.0.7-radius-gentoo-v3.patch | 2998 |
1 files changed, 2998 insertions, 0 deletions
diff --git a/net-misc/dhcp/files/dhcp-3.0.7-radius-gentoo-v3.patch b/net-misc/dhcp/files/dhcp-3.0.7-radius-gentoo-v3.patch new file mode 100644 index 0000000..e5ddfca --- /dev/null +++ b/net-misc/dhcp/files/dhcp-3.0.7-radius-gentoo-v3.patch @@ -0,0 +1,2998 @@ +=== modified file 'common/Makefile.dist' +--- common/Makefile.dist 2010-08-05 10:07:34 +0000 ++++ common/Makefile.dist 2010-08-05 10:07:56 +0000 +@@ -25,11 +25,11 @@ + SRC = raw.c parse.c nit.c icmp.c dispatch.c conflex.c upf.c bpf.c socket.c \ + lpf.c dlpi.c packet.c tr.c ethernet.c memory.c print.c options.c \ + inet.c tree.c tables.c alloc.c fddi.c ctrace.c dns.c resolv.c \ +- execute.c discover.c comapi.c ++ execute.c discover.c comapi.c dhcp2radius.c + OBJ = raw.o parse.o nit.o icmp.o dispatch.o conflex.o upf.o bpf.o socket.o \ + lpf.o dlpi.o packet.o tr.o ethernet.o memory.o print.o options.o \ + inet.o tree.o tables.o alloc.o fddi.o ctrace.o dns.o resolv.o \ +- execute.o discover.o comapi.o ++ execute.o discover.o comapi.o dhcp2radius.o + MAN = dhcp-options.5 dhcp-eval.5 + + INCLUDES = -I$(TOP) $(BINDINC) -I$(TOP)/includes + +=== added file 'common/dhcp2radius.c' +--- common/dhcp2radius.c 1970-01-01 00:00:00 +0000 ++++ common/dhcp2radius.c 2010-08-05 10:07:56 +0000 +@@ -0,0 +1,2011 @@ ++/* START PATCH CODE */ ++#include <stdio.h> ++#include <string.h> ++#include <stdint.h> ++#include <stdlib.h> ++#include <openssl/md5.h> ++#include <signal.h> ++#include <sys/ioctl.h> ++#include <sys/socket.h> ++#include <sys/types.h> ++#include <netinet/in.h> ++#include <net/if_arp.h> ++#include <arpa/inet.h> ++#include <time.h> ++ ++#include "dhcpd.h" ++#include "dhcp2radius.h" ++ ++int32_t use_dhcp2radius; ++ ++XID_NODE * xid_list_top = 0; /* Указатель на начало списка кэша DHCP */ ++XID_NODE * xid_list_ptr = 0; /* указатель на текущий элемент списка кэша DHCP */ ++struct counter_node * counter = 0; /* Указатель на нулевой элемент массива-счётчика частоты запросов */ ++uint16_t counter_end = 0; /* Содержит индекс последующего элемента за последним ++ используемым элементом счётчика */ ++uint16_t counter_size = MIN_COUNTER_SIZE; /* Указывает текущий размер массива счётчика частоты запросов */ ++time_t xid_ttl = DEFAULT_XID_TTL; /* Время в течение которого хранятся узлы кэша */ ++uint16_t radius_port = RADIUS_PORT; /* Порт на который релей отправляет RADIUS запросы. По умолчанию равен 1812 */ ++float max_qps = MAX_DHCP_QPS; /* Максимально допустимая частота отправки DHCP запросов ++ всеми клиентами суммарно */ ++float max_qps_from_host = MAX_QPS_FROM_HOST; /* Максимально допустимая частота ++ отправки запросов DHCP одним клиентом */ ++uint32_t cache_list_len = MAX_CACHE_LIST_LEN; /* Максимальная длина кэша DHCP. Переменная нужна для возможности ++ задания этого параметра из командной строки. ++ В данной версии - не реализовано */ ++int16_t dhcp_cache_len = 0; /* Текущая длина кэша */ ++time_t normal_clients_counter[QUERY_TIME_RING_LEN]; /* Массив используемый как кольцевой буфер для ++ подсчёта частоты запросов в секунду */ ++uint16_t ring_ptr; /* Указатель на последний элемент кольцевого буфера для подсчёта частоты DHCP запросов */ ++struct sockaddr_in * radius_servers; /* Указатель на список массив хранящий адреса RADIUS серверов */ ++int radius_servers_count; /* Число доступных RADIUS серверов */ ++int unfinished_requests; /* Переменная хранящая число необработанных RADIUS-сервером запросов. ++ Т.е. число DHCP запросов для которых не получены ответы RADIUS сервера. ++ Переменная используется для обнаружения "падения" RADIUS сервера и ++ переключения на резервный сервер, если таковой указан */ ++int server_index = BASIC_SERVER_INDEX; /* Индекс массива серверов, указывающий на RADIUS сервер ++ к которому в текущий момент времени пересылаются запросы */ ++uint32_t time_to_restore_primary_server_index; /* Время (в секундах) через которое производится попытка переключиться ++ на первичный RADIUS сервера, если на данный момент осуществлено ++ переключение на резервный */ ++time_t primary_server_down_time; /* Фиксируется время (секунд), в которое "упал" PRIMARY RADIUS сервер */ ++#ifdef __linux__ ++int updating_arp_cache_perm; /* Указывает - обновлять ли ARP кэш при получении RADIUS сообщений с cached == 0 */ ++int arp_cache_perm_len = DEF_ARP_CACHE_PERM_LEN; /* Длина массива хранящего кэш статических (флаг PERM) ARP записей */ ++int arp_entry_count; /* Число зафиксированных ARP в текущий момент времени */ ++struct arp_entry * arp_cache_perm; /* Указатель на начало динамического массива содержащего ARP записи */ ++#endif ++struct ignored_dhcp_interface * start_dhcp_ignor_list = 0; /* Указатель на список интерфейсов на которых ++ игнорируются DHCP запросы */ ++struct ignored_dhcp_interface * end_dhcp_ignor_list = 0; /* Указатель на последний элемент списка */ ++const uint8_t magic_cookie[] = {99, 130, 83, 99}; ++ ++unsigned char rad_secret[RAD_SECRET_SIZE]; /* Пароль для доступа на Radius-сервер */ ++unsigned char rad_users_passwd[RAD_PASSWD_SIZE]; /* Пароль пользователя, в качестве имени применяется MAC адрес клиента */ ++ ++unsigned long forward_client_packets_errors; /* Хранит число неудачных попыток передать запрос на RADIUS сервер */ ++uint16_t radius_dhcp_relay_port; /* Порт с которого отправляет запрос DHCP relay агент */ ++int radius_allow_cache; /* Переменная указывающая на возможность кэширования ответов RADIUS */ ++int radius_allow_dynamic_cache; /* Переменная указывающая на возможность кэширования ответов для клиентов ++ не имеющих привязки MAC -> IP */ ++int radius_use_mac_delim; /* Использовать ":" в качестве разделителя MAC адреса в запросах к ++ RADIUS серверу, если true */ ++uint8_t * opts_send_to_srv; /* Указатель на массив опций отправляемых на RADIUS сервер при запросе ++ клиентом адреса. Инициализируется в случае использования ++ опции radius-send-opts-to-srv */ ++ ++isc_result_t got_radius_packet(uint8_t * raw_packet, uint32_t length) ++{ ++ RAD_MESSAGE auth_pack; ++ int rad_msg_len = 0; ++ char str_dhcp_type[20] = ""; /* Строка используемая для вывода в лог строкового представления ++ типа пакета, например: DHCPDISCOVER, DHCPACK и т.д.*/ ++ struct interface_info * out = 0; /* Указатель определяющий выходной интерфейс для отправляемого пакета */ ++ struct sockaddr_in to; /* IP информация о хосте назначения пакета */ ++ struct hardware hto, *htop; /* Физический адрес хоста назначения */ ++ struct in_addr user_if; /* Интерфейс хоста на котором запущен dhcpd находящийся в подсети клиента */ ++ ++ static struct rad_header prev_rad_pack; ++ if(memcmp(&prev_rad_pack, raw_packet, sizeof(prev_rad_pack))) ++ memcpy(&prev_rad_pack, raw_packet, sizeof(prev_rad_pack)); /* Новый пакет, сохраняем его копию */ ++ else ++ return ISC_R_UNEXPECTED; /* Дубликат */ ++ ++ if (length < sizeof(struct rad_header)) ++ { ++ log_error("PATCH: received RADIUS packet with invalid length: %d, minimum lenght is: %d", ++ length, sizeof(struct rad_header)); ++ return ISC_R_UNEXPECTED; ++ } ++ /* Обрабатываем полученный ответ RADIUS сервера преобразовывая его в DHCP */ ++ if( !(length = dispatch_radius_packet((struct dhcp_packet*) raw_packet, length, &user_if, &hto.hbuf[1]))) ++ return ISC_R_UNEXPECTED; ++ ++ /* Ищем среди списка обслуживаемых интерфейсов интерфейс расположенный в подсети ++ клиента запросившего адрес что бы отправить ответ через него */ ++ for (out = interfaces; out; out = out -> next) ++ if (!memcmp (&out -> primary_address, &user_if, sizeof (user_if))) ++ break; ++ ++ if(!out) ++ { ++ log_info("No such interface for sending BOOTREPLY (user interface %s)", inet_ntoa(user_if)); ++ return ISC_R_UNEXPECTED; ++ } ++ ++ bzero(&to, sizeof(to)); ++ ++ struct dhcp_packet * dhcp_pack = (struct dhcp_packet *) raw_packet; ++ ++ if ( ( !(dhcp_pack -> flags & htons (BOOTP_BROADCAST) )/* Отправляем юникастом если нет флага BROADCAST */ ++ || dhcp_pack -> giaddr.s_addr ) ) /* Или если пакет должен быть отправлен через агента пересылки */ ++ { ++ to.sin_addr = dhcp_pack -> giaddr.s_addr ? dhcp_pack -> giaddr : dhcp_pack -> yiaddr; ++ to.sin_port = dhcp_pack -> giaddr.s_addr ? radius_dhcp_relay_port : remote_port; ++ htop = &hto; ++ } ++ else ++ { ++ to.sin_addr.s_addr = htonl (INADDR_BROADCAST); ++ to.sin_port = remote_port; ++ /* hardware address is broadcast */ ++ htop = NULL; ++ } ++ to.sin_family = AF_INET; ++#ifdef HAVE_SA_LEN ++ to.sin_len = sizeof to; ++#endif ++ ++ /* Задаём тип и длину физического адреса хоста назначения */ ++ hto.hbuf [0] = dhcp_pack -> htype; ++ hto.hlen = dhcp_pack -> hlen + 1; ++ ++ /* Определяем тип DHCP сообщения отправляемого клиенту */ ++ bzero(str_dhcp_type, sizeof(str_dhcp_type)); ++ get_dhcp_type(dhcp_pack, str_dhcp_type); ++ ++ if (send_packet (out, ++ (struct packet *)0, ++ dhcp_pack, length, out -> primary_address, ++ &to, htop) < 0) { ++ ++forward_client_packets_errors; ++ } ++ else ++ { ++ static char to_str[16]; ++ strncpy(to_str, inet_ntoa(to.sin_addr), sizeof(to_str) - 1); ++ log_info ("Convert RADIUS-reply for %s in %s (IP: %s) and %s to %s", ++ print_hw_addr (dhcp_pack -> htype, dhcp_pack -> hlen, dhcp_pack -> chaddr), ++ str_dhcp_type, ++ inet_ntoa(dhcp_pack -> yiaddr), ++ dhcp_pack -> giaddr.s_addr ? "forwarded" : "sended", ++ to_str); ++ } ++ return ISC_R_SUCCESS; ++} ++ ++isc_result_t got_dhcp_packet(uint8_t * raw_packet, uint32_t length, const struct sockaddr_in * from, ++ struct interface_info * ip, struct hardware * hfrom) ++{ ++ RAD_MESSAGE auth_pack; ++ time_t now; ++ char str_dhcp_type[20] = ""; /* Строка используемая для вывода в лог строкового представления ++ типа пакета, например: DHCPDISCOVER, DHCPACK и т.д.*/ ++ uint8_t from_cache = 0; ++ struct interface_info * out = 0; /* Указатель определяющий выходной интерфейс для отправляемого пакета */ ++ struct sockaddr_in to; /* IP информация о хосте назначения пакета */ ++ struct hardware hto, *htop; /* Физический адрес хоста назначения */ ++ ++ int ret = 0; ++ ++ if(time(&now) == (time_t) -1) ++ { ++ log_error("time() failed! Processing packet aborted."); ++ return ISC_R_UNEXPECTED; ++ } ++ ++ ++ /* Иначе это запрос от DHCP клиента или агента пересылки. ++ Сперва проверяем пакет на соответствие минимальной длине */ ++ if (length < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN) ++ return ISC_R_UNEXPECTED; ++ ++ struct dhcp_packet * dhcp_pack = (struct dhcp_packet*) raw_packet; ++ ++ /* Проверяем не получен-ли запрос с интерфейса на котором игнорируются DHCP запросы */ ++ if(from -> sin_port == remote_port && found_ignored_interfase(ip -> name)) ++ { ++ bzero(str_dhcp_type, sizeof(str_dhcp_type)); ++ get_dhcp_type(dhcp_pack, str_dhcp_type); ++ log_info("INFO: Ignore %s from %s on interface %s", ++ str_dhcp_type, ++ print_hw_addr (dhcp_pack -> htype, dhcp_pack -> hlen, dhcp_pack -> chaddr), ++ ip -> name); ++ return ISC_R_UNEXPECTED; ++ } ++ /* Обрабатываем DHCP запрос, в результате выполнения этой функции ++ либо получаем сформированный RADIUS запрос (в структуре auth_pack), ++ либо если для данного клиента найдена информация в кэше - получаем ++ сформированный BOOTREPLY (в структуре на которую указывает raw_packet). ++ Длина выходной информации сохраняется в переменной length */ ++ ret = dispatch_dhcp_packet((struct dhcp_packet*)raw_packet, &length, ip, hfrom, ++ &from_cache, &auth_pack); ++ ++ if(!ret) ++ return ISC_R_UNEXPECTED; ++ ++ if(ret < 0) ++ { ++ log_info("INFO: Can't processed DHCP packet."); ++ return ISC_R_UNEXPECTED; ++ } ++ ++ /* Если задано время через которое производится попытка ++ тестирования упавшего первичного RADIUS сервера на ++ работоспособность, то пробуем переключиться на него */ ++ if(time_to_restore_primary_server_index ++ && ++ (server_index != PRIMARY_SERVER_INDEX) ++ ) ++ { ++ if( (now = time(0)) == ((time_t) -1)) ++ { ++ log_fatal("FATAL: time() error! Abort execution. Quit."); ++ exit (-1); ++ } ++ if( (now - primary_server_down_time) > ++ time_to_restore_primary_server_index) ++ { ++ server_index = PRIMARY_SERVER_INDEX; ++ log_info("INFO: Primary server down %u seconds earlier. Attempting change RADIUS server to primary: %s", ++ now - primary_server_down_time, ++ inet_ntoa(radius_servers[server_index].sin_addr)); ++ unfinished_requests = 0; ++ } ++ } ++ ++ /* Проверяем - не ушёл ли RADIUS-сервер в down */ ++ if(!from_cache && ++ is_unfinished_requests_overflow(&unfinished_requests, INC, MAX_UNFINISHED_REQ, ZEROING_UNFIN_REQ)) ++ { ++ log_info("ERROR: RADIUS server %s down!", inet_ntoa(radius_servers[server_index].sin_addr)); ++ /* Если упал первичный сервер, то отмечаем последнее время падения */ ++ if(server_index == PRIMARY_SERVER_INDEX) ++ { ++ primary_server_down_time = time(0); ++ if(primary_server_down_time == ((time_t) -1)) ++ { ++ log_fatal("FATAL: time() error! Abort execution. Quit."); ++ exit (-1); ++ } ++ } ++ /* Если есть дублирующие сервера, то переключаемся на следующий */ ++ if(radius_servers_count > 1) ++ { ++ if(server_index == radius_servers_count - 1) ++ server_index = 0; ++ else ++server_index; ++ log_info("WARN: Changing RADIUS server to %s (#%d)", ++ inet_ntoa(radius_servers[server_index].sin_addr), server_index + 1 ); ++ unfinished_requests = 0; ++ } ++ else log_info("ERROR: can't change the server, because there are no duplicating servers."); ++ } ++ ++ /* Если переменная from_cache != 0, это значит что в структуре на которую указывает ++ dhcp_pack содержится BOOTREPLY из кэша dhcpd и нет необходимости отправлять ++ запрос на RADIUS сервер. Потому сразу отправляем ответ DHCP клиенту */ ++ bzero(&to, sizeof(to)); ++ to.sin_family = AF_INET; ++ if(from_cache) ++ { ++ out = ip; ++ /* Задаём физический адрес хоста назначения */ ++ if(dhcp_pack -> giaddr.s_addr) ++ memcpy(&hto.hbuf[1], &hfrom -> hbuf[1], hfrom -> hlen - 1); ++ else ++ memcpy(&hto.hbuf [1], dhcp_pack -> chaddr, dhcp_pack -> hlen); ++ hto.hbuf [0] = dhcp_pack -> htype; ++ hto.hlen = dhcp_pack -> hlen + 1; ++ ++ if (!(dhcp_pack -> flags & htons (BOOTP_BROADCAST)) && ++ can_unicast_without_arp (out)) ++ { ++ to.sin_addr = dhcp_pack -> giaddr.s_addr ? dhcp_pack -> giaddr : dhcp_pack -> yiaddr; ++ to.sin_port = dhcp_pack -> giaddr.s_addr ? radius_dhcp_relay_port : remote_port; ++ htop = &hto; ++ } ++ else ++ { ++ to.sin_addr.s_addr = htonl (INADDR_BROADCAST); ++ to.sin_port = remote_port; ++ /* hardware address is broadcast */ ++ htop = NULL; ++ } ++ } ++ else /* from_cache == 0 - запрос от клиента не найден в кэше, потому */ ++ { /* пересылаем его на RADIUS сервер */ ++ to = radius_servers[server_index]; ++ out = ((fallback_interface) ? fallback_interface : interfaces); ++ } ++ ++ if (send_packet ++ ( ++ out, ++ (struct packet *)0, ++ ((from_cache)? dhcp_pack : (struct dhcp_packet *)&auth_pack), ++ length, out -> primary_address, &to, (struct hardware *)htop ++ ) < 0 ++ ) ++ { ++ log_error("PATCH: Error - can't send packet to %s (%s/%s)", ++ inet_ntoa (to.sin_addr), ++ print_hw_addr (dhcp_pack -> htype, dhcp_pack -> hlen, dhcp_pack -> chaddr), out -> name); ++ ++forward_client_packets_errors; ++ } ++ else ++ { ++ static char to_str[16]; ++ strncpy(to_str, inet_ntoa(to.sin_addr), sizeof(to_str) - 1); ++ ++ static char from_str[16]; /* Строка используемая для сохранения строкового представления ++ IP адреса агента пересылки если таковой используется */ ++ if(dhcp_pack -> giaddr.s_addr) ++ strncpy(from_str, inet_ntoa(dhcp_pack -> giaddr), sizeof(from_str)); ++ ++ bzero(str_dhcp_type, sizeof(str_dhcp_type)); ++ get_dhcp_type(dhcp_pack, str_dhcp_type); ++ ++ if(from_cache) ++ log_info("Creating %s (IP: %s) for %s/%s and %s to %s", ++ str_dhcp_type, ++ inet_ntoa(dhcp_pack -> yiaddr), ++ print_hw_addr (dhcp_pack -> htype, dhcp_pack -> hlen, dhcp_pack -> chaddr), ++ ip -> name, ++ (dhcp_pack -> giaddr.s_addr) ? "forwarded" : "sended", ++ to_str); ++ else ++ log_info ("Convert %s %s/%s to RADIUS and %s to %s", ++ str_dhcp_type, ++ print_hw_addr (dhcp_pack -> htype, dhcp_pack -> hlen, dhcp_pack -> chaddr), ++ dhcp_pack -> giaddr.s_addr ? from_str : ip -> name, /* Если агент пересылки не используется - ++ указываем имя интерфейса на котором получен запрос */ ++ "forwarded", ++ to_str); ++ } ++ return ISC_R_SUCCESS; ++} ++ ++/* ++ Функция удаляющая кэшированные xid узлы при изменении соответствия IP - MAC клиента. ++ Такое возможно в случае смены MAC-адреса клиента: старому IP адресу будет соответстовать ++ новый MAC адрес, старая информация из кэша должна быть удалёна. ++*/ ++int delete_xid_if_mac_not_equal(uint32_t ip, uint8_t * mac) ++{ ++ XID_NODE * xid_ptr = xid_list_top; ++ while(xid_ptr) ++ { ++ if( xid_ptr -> out_packet && ++ (xid_ptr -> out_packet -> yiaddr.s_addr == ip) && ++ memcmp(mac, xid_ptr -> out_packet -> chaddr, MAC_ADDR_LEN) ++ ) ++ xid_ptr = delete_xid(xid_ptr); ++ else ++ xid_ptr = xid_ptr -> next; ++ } ++ return 1; ++} ++ ++#ifdef __linux__ ++/* ++ Получаем ARP-кэш статически привязанных MAC адресов из ядра ОС. ++ ДАННЫЙ ВАРИАНТ ПОДХОДИТ ТОЛЬКО ДЛЯ LINUX!!! ++*/ ++int get_arp_cache_perm(void) ++{ ++ ++ int type, flags, num; ++ char ip[16]; ++ char string[256]; ++ char mask[20]; ++ char device[20]; ++ unsigned char hw[MAC_ADDR_LEN]; ++ FILE * proc_fd = fopen(PROC_ARP, "r"); ++ if(!proc_fd) ++ log_fatal("FATAL: Can't open system ARP-table in: %s", PROC_ARP); ++ arp_entry_count = 0; ++ bzero(string, sizeof(string)); ++ bzero(ip, sizeof(ip)); ++ while(fgets(string, sizeof(string), proc_fd)) ++ { ++ if(arp_entry_count == arp_cache_perm_len) ++ { ++ arp_cache_perm_len *= 2; ++ log_info("INFO: Reallocating memory for ARP cache. New size is: %d", arp_cache_perm_len); ++ arp_cache_perm = (struct arp_entry*) realloc(arp_cache_perm, arp_cache_perm_len * sizeof(struct arp_entry)); ++ if(!arp_cache_perm) ++ return 0; ++ } ++ num = sscanf(string, "%s 0x%x 0x%x %X:%X:%X:%X:%X:%X %100s %100s\n", ++ ip, &type, &flags, ++ &hw[0], &hw[1], &hw[2], &hw[3], &hw[4], &hw[5], ++ mask, device); ++ if(num < 4) ++ continue; ++ if(flags & ATF_PERM) ++ { ++ if(!inet_aton(ip, &arp_cache_perm[arp_entry_count].ip)) ++ { ++ log_error("ERROR: Can't convert IP from ARP cache to binary format."); ++ continue; ++ } ++ memcpy(arp_cache_perm[arp_entry_count].mac, hw, MAC_ADDR_LEN); ++ ++ arp_entry_count; ++ } ++ } ++ return 1; ++} ++ ++/* ++ Функция обновляющая ARP таблицу ОС согласно информации полученной с RADIUS сервера. ++ Выполняется при смене физического адреса клиента в базе RADIUS. ++*/ ++int update_arp_cache_perm(uint32_t ip, unsigned char * mac) ++{ ++ int i; ++ int ip_found = 0; ++ /* Обновляем информацию в собственной ARP таблице dhcpd */ ++ for(i = 0; i < arp_entry_count; ++i) ++ { ++ if(arp_cache_perm[i].ip.s_addr == ip) ++ { ++ if(!memcmp(arp_cache_perm[i].mac, mac, MAC_ADDR_LEN)) /* Если переданный и найденный MAC адреса равны, */ ++ return 0; /* то выходим, т.к. нечего обновлять */ ++ ip_found = 1; ++ break; ++ } ++ } ++ if(!ip_found) /* Если IP адрес не найден в текущей ARP таблице, */ ++ { /* то добавляем его в таблицу */ ++ if(arp_entry_count == arp_cache_perm_len) /* Если ARP таблица dhcpd заполнена на 100%, */ ++ { /* выделяем для неё дополнительную память */ ++ arp_cache_perm_len *= 2; ++ arp_cache_perm = (struct arp_entry*) realloc(arp_cache_perm, arp_cache_perm_len * sizeof(struct arp_entry)); ++ if(!arp_cache_perm) ++ return -1; ++ } ++ arp_cache_perm[i].ip.s_addr = ip; /* Заполняем поле IP адрес в новой строке ARP таблицы dhcpd */ ++ ++ arp_entry_count; ++ } ++ memcpy(arp_cache_perm[i].mac, mac, MAC_ADDR_LEN); /* Копируем новый MAC адрес в ARP таблицу dhcpd */ ++ ++ /* Обновляем ARP таблицу ядра ОС */ ++ struct arpreq req; ++ uint32_t sockfd; ++ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) ++ { ++ log_error("ERROR: Create socket for update ARP cache failed."); ++ return -1; ++ } ++ bzero((char *) &req, sizeof(req)); ++ req.arp_pa.sa_family = PF_INET; ++ memcpy((char *)&req.arp_pa.sa_data + 2, &ip, sizeof(ip)); ++ req.arp_ha.sa_family = PF_LOCAL; ++ memcpy(req.arp_ha.sa_data, mac, MAC_ADDR_LEN); ++ req.arp_flags = ATF_PERM | ATF_COM; ++ if (ioctl(sockfd, SIOCSARP, &req) < 0) ++ { ++ log_error("ERROR: ioctl for set ARP entry failed."); ++ return -1; ++ } ++ ++ return 1; ++} ++#endif ++ ++/* ++ Функция выполняет подсчёт общего усреднённого числа DHCP запросов от всех клиентов ++ за единицу времени TIME_DELTA, а так же - подсчёт усреднённого числа запросов ++ от конкретного хоста указанного в mac_addr. Любой из параметров ++ функции может быть равен нулю. Вызов функции с обоими параметрами равными нулю ++ можно применить для принудительного уменьшения размера счётчика, ++ без внешнего запроса от DHCP клиента - если в счётчике будут обнаружены ++ устаревшие элементы. ++*/ ++float qps_hosts(uint8_t * mac_addr, float * qps_summary) ++{ ++ float hits = 0; ++ time_t now = time(0); ++ int i = 0; ++ ++ if(qps_summary) ++ *qps_summary = 0; ++ /* Проверяем достаточно-ли места для добавления нового элемента в счётчик */ ++ if(mac_addr && (counter_size <= counter_end) ) ++ { /* Перераспределяем память для счётчика */ ++ counter_size *= 2; /* Увеличиваем размер массива счётчика в 2 раза*/ ++ counter = (struct counter_node*) realloc(counter, sizeof(struct counter_node) * counter_size); ++ if(!counter) ++ { ++ log_fatal("FATAL: realloc for resizing QPS counter failed!"); ++ } ++ log_info("INFO: Realloc memory for QPS counter. New counter size is: %u", counter_size); ++ } ++ if(mac_addr) ++ { /* Добавляем новый элемент в счётчик */ ++ memcpy(counter[counter_end].mac_addr, mac_addr, MAC_ADDR_LEN); ++ counter[counter_end].timestamp = now; ++ } ++ ++ ++ counter_end; ++ ++ /* Начало обработки статистической информации */ ++ for(;i < counter_end; i++) ++ { /* Это условие выполняется если узел устарел */ ++ if( (now - counter[i].timestamp) > TIME_DELTA ) ++ { /* Удаляем устаревший узел */ ++ counter[i] = counter[counter_end - 1]; /* Просто переносим в него ++ значение последнего элемента счётчика */ ++ -- counter_end; /* И укорачиваем счётчик на 1 элемент */ ++ -- i; ++ continue; ++ } ++ ++ /* Производим подсчёт запросов для конкретного хоста */ ++ if( mac_addr /* Считаем только если в функцию передан адрес хоста-источника */ ++ && ++ !memcmp(counter[i].mac_addr, mac_addr, MAC_ADDR_LEN) ++ && ++ (now - counter[i].timestamp <= TIME_DELTA_HOST) ++ ) ++ { /* Найден узел содержащий информацию о хосте пославшем данный запрос */ ++ ++hits; ++ } ++ } ++ /* Производим подсчёт общего числа запросов за TIME_DELTA. Т.к. все устаревшие ++ узлы уже удалены, то общее число запросов равно числу значимых элементов счётчика */ ++ if(qps_summary) ++ *qps_summary = counter_end; ++ /* Проверяем, если значение размера счётчика больше минимально допустимого ++ и хотя бы 60% массива счётчика не используется, то перераспределяем память ++ освобождая не используемые участки */ ++ if(counter_size > MIN_COUNTER_SIZE ++ && ++ (counter_end - 1) < ((counter_size / 10) * 4) /* Во второй части сравнения вычисляется 40% размера счётчика */ ++ ) ++ { ++ counter_size /= 2; ++ counter = (struct counter_node*) realloc(counter, sizeof(struct counter_node) * counter_size); ++ if(!counter) ++ log_fatal("FATAL: realloc failed for QPS counter!"); ++ log_info("INFO: Realloc memory for QPS counter. New counter size is: %u", counter_size); ++ } ++ /* Вычисляем среднее число запросов в секунду за TIME_DELTA */ ++ if(qps_summary) ++ (*qps_summary) /= TIME_DELTA; ++ return hits / TIME_DELTA_HOST; ++} ++ ++/* ++ Подсчитывает среднее число DHCP запросов в секунду за время TIME_DELTA секунд от всех клиентов ++*/ ++float count_queries_per_sec(void) ++{ ++ uint16_t queries_count = 0; ++ time_t now = time(0); ++ int i = 0; ++ ++ time_t oldest_time = 0, newest_time = 0; ++ ++ if(ring_ptr == QUERY_TIME_RING_LEN) ++ oldest_time = normal_clients_counter [0]; ++ else ++ oldest_time = normal_clients_counter [ring_ptr]; ++ if(ring_ptr == 0) ++ newest_time = normal_clients_counter [QUERY_TIME_RING_LEN - 1]; ++ else ++ newest_time = normal_clients_counter [ring_ptr - 1]; ++ ++ if( (now - newest_time) > TIME_DELTA) ++ return 0; ++ ++ if(newest_time - oldest_time > 0) ++ return QUERY_TIME_RING_LEN / (newest_time - oldest_time) ; ++ return (float)0xFFFFFFFF; ++} ++ ++/* ++ Функция сменяющая текущий используемый RADIUS сервер ++ при получении внешнего сигнала SIGNAL_CHANGE_SERV ++*/ ++void change_server(int sign) ++{ ++ log_info("INFO: Recv SIGNAL_CHANGE_SERV signal."); ++ if(radius_servers_count > 1) ++ { ++ if(server_index == PRIMARY_SERVER_INDEX) ++ primary_server_down_time = time(0); ++ if(server_index == radius_servers_count - 1) ++ server_index = 0; ++ else ++server_index; ++ log_info("INFO: Changing RADIUS server to %s (#%d)", ++ inet_ntoa(radius_servers[server_index].sin_addr), server_index + 1 ); ++ unfinished_requests = 0; ++ } ++ else log_info("ERROR: can't change the server, because there are no duplicating servers."); ++ return; ++} ++ ++/* ++ Функция устанавливающая текущим RADIUS сервером - первичный сервер, т.е. сервер ++ заданный первым в параметрах командной строки при получении сигнала SIGNAL_SET_PRI_SERVER ++*/ ++inline void set_default_server(int sign) ++{ ++ log_info("INFO: receiving SIGNAL_SET_PRI_SERVER signal. Current server is: %s", ++ inet_ntoa(radius_servers[server_index].sin_addr)); ++ server_index = BASIC_SERVER_INDEX; ++ log_info("INFO: Set RADIUS server to: %s", inet_ntoa(radius_servers[server_index].sin_addr)); ++ return; ++} ++ ++/* ++ Функция выводящая в лог статистическую информацию и содержимое DHCP кэша релея ++ при получении сигнала SIGSTAT ++*/ ++void print_stat(int sign) ++{ ++ XID_NODE * tmp_ptr = xid_list_top; ++ int numb = 0; ++ struct in_addr ip; ++ time_t ttl; ++ float qps = 0; ++ char dhcp_info[1024]; ++ ++ log_info ("#*************************************************************************#"); ++ log_info ("dhcpd patch version: %s", PATCH_VERSION); ++ log_info ("Current XID's list:"); ++ log_info ("---"); ++ for(;tmp_ptr; ++numb) ++ { ++ log_info("XID number: %u", numb + 1); ++ log_info("XID value: %u", ntohl(tmp_ptr -> xid)); ++ log_info("XID rad_req.id: %u", tmp_ptr -> req_header.id); ++ ip.s_addr = tmp_ptr -> relay_if_index; ++ log_info("XID relay_if_index: %u (%s)", ntohl(tmp_ptr -> relay_if_index), inet_ntoa(ip)); ++ ip.s_addr = tmp_ptr -> if_index; ++ log_info("XID if_index: %u (%s)", ntohl(tmp_ptr -> if_index), inet_ntoa(ip)); ++ log_info("XID DHCP client MAC address: %s", print_hw_addr (1, MAC_ADDR_LEN, tmp_ptr-> chaddr)); ++ log_info("XID hw_from MAC address: %s", print_hw_addr (1, MAC_ADDR_LEN, tmp_ptr-> hw_from)); ++ switch(tmp_ptr -> server_msg_type) ++ { ++ case DHCPOFFER: log_info("Last server response: DHCPOFFER"); break; ++ case DHCPACK: log_info("Last server response: DHCPACK"); break; ++ case DHCPNAK: log_info("Last server response: DHCPNAK"); break; ++ case 0: log_info("Last server response: Server not respond yet"); break; ++ default: log_info("Unknown server response: %u", tmp_ptr -> server_msg_type); break; ++ } ++ switch(tmp_ptr -> client_msg_type) ++ { ++ case DHCPDISCOVER: log_info("Last client request: DHCPDISCOVER"); break; ++ case DHCPREQUEST: log_info("Last client request: DHCPREQUEST"); break; ++ default: log_info("Last client request: unknown client request type"); break; ++ } ++ ++ if(tmp_ptr -> dhcp_flags & htons (BOOTP_BROADCAST)) ++ log_info("XID flags: BROADCAST"); ++ ttl = xid_ttl - (time(0) - tmp_ptr -> timestamp); ++ log_info("XID ttl: %u", (ttl > 0)? ttl: 0); ++ ++ if(tmp_ptr -> out_packet && (tmp_ptr -> out_packet -> op == BOOTREPLY) && tmp_ptr -> allow_cache) ++ { ++ bzero(dhcp_info, sizeof(dhcp_info)); ++ memcpy(&ip, &tmp_ptr -> out_packet -> yiaddr, sizeof(struct in_addr)); ++ strcat(dhcp_info, "you addr: "); ++ strcat(dhcp_info, inet_ntoa(ip)); ++ if(get_dhcp_option(tmp_ptr -> out_packet, tmp_ptr -> out_pack_len, DHO_SUBNET_MASK, &ip.s_addr, sizeof(ip.s_addr))) ++ strcat(dhcp_info, " subnet mask: "); ++ strcat(dhcp_info, inet_ntoa(ip)); ++ if(get_dhcp_option(tmp_ptr -> out_packet, tmp_ptr -> out_pack_len, DHO_ROUTERS, &ip.s_addr, sizeof(ip.s_addr))) ++ strcat(dhcp_info, " default gw: "); ++ strcat(dhcp_info, inet_ntoa(ip)); ++ log_info("DHCP info: '%s'", dhcp_info); ++ } ++ log_info("---"); ++ ++ tmp_ptr = tmp_ptr -> next; ++ } ++ log_info ("End XID list. Summary XID count: %u.", numb); ++ qps_hosts(0, &qps); ++ log_info ("QPS: %g", count_queries_per_sec()); ++ log_info ("DHCP cache lenght: %u", dhcp_cache_len); ++ if(server_index != PRIMARY_SERVER_INDEX) ++ { ++ char * down_time; ++ down_time = ctime(&primary_server_down_time); ++ down_time[strlen(down_time) - 1] = 0; ++ log_info("Primary server down time: %s", down_time); ++ } ++ log_info ("Current RADIUS server: %s%s", ++ inet_ntoa(radius_servers[server_index].sin_addr), ++ (server_index == PRIMARY_SERVER_INDEX)? " (primary)": " (backup server)"); ++ log_info ("Unfinished RADIUS requests: %u", unfinished_requests); ++ ++ log_info("Hits counter lenght: %u", counter_end); ++ log_info("Hits counter size: %u", counter_size); ++#ifdef __linux__ ++ if(updating_arp_cache_perm) ++ log_info("ARP entry count: %d", arp_entry_count); ++#endif ++ log_info ("#*************************************************************************#"); ++ return; ++} ++ ++/* ++ Функция шифрующая пароль для доступа к RADIUS-серверу. ++ INCOMPLETE: Текущий вариант не соответствует требованиям безопасности предъявляемым ++ к RADIUS клиентам. В данной ситуации это не слишком опасно, т.к. протокол ++ DHCP в своём чистом виде в принципе не поддерживает шифрование передаваемой информации. ++*/ ++int encrypt_passwd(char * passwd, ++ uint8_t * encr_passwd, char *shared_key, ++ uint8_t * auth) ++{ ++ unsigned char full_passwd[16]; ++ uint8_t md5_hash[16]; ++ unsigned int i = 0; ++ MD5_CTX context; ++ ++ /* Verify input data */ ++ if(!passwd || (strlen(passwd) > 16))return 0; ++ if(!encr_passwd) return 1; ++ if(!shared_key) return 2; ++ if(!auth) return 3; ++ ++ bzero(full_passwd, sizeof(full_passwd)); ++ bzero(encr_passwd, ENCR_PWD_LEN); ++ ++ memcpy(full_passwd, passwd, strlen(passwd)); /* Дополняем пароль до 16ти символов */ ++ ++ /* Generate MD5 sum for encrypting password */ ++ MD5_Init(&context); ++ MD5_Update(&context, shared_key, strlen(shared_key)); ++ MD5_Update(&context, auth, MD5_LEN); ++ MD5_Final(md5_hash, &context); ++ ++ /* Encrypting password */ ++ for(i = 0; i < sizeof(full_passwd); i++) ++ encr_passwd[i] = full_passwd[i] ^ md5_hash[i]; ++ ++ return ENCR_PWD_LEN; ++} ++ ++uint8_t make_radius_vsa(uint8_t * out_buffer, uint8_t ob_len, const uint32_t vendor_id, ++ const uint16_t vsa, const uint8_t vsa_len, uint8_t * vsa_value) ++{ ++ uint8_t *buf_ptr = out_buffer; ++ uint8_t const *ob_end = out_buffer + ob_len; ++ ++ if(buf_ptr + sizeof(vendor_id) > ob_end) ++ return 0; ++ memcpy(buf_ptr, &vendor_id, sizeof(vendor_id)); ++ buf_ptr += sizeof(vendor_id); ++ ++ if(buf_ptr + sizeof(vsa) > ob_end) ++ return 0; ++ memcpy(buf_ptr, &vsa, sizeof(vsa)); ++ buf_ptr += sizeof(vsa); ++ ++ uint8_t full_len = sizeof(vsa) + sizeof(vsa_len) + vsa_len; ++ if(buf_ptr + sizeof(full_len) > ob_end) ++ return 0; ++ memcpy(buf_ptr, &full_len, sizeof(full_len)); ++ buf_ptr += sizeof(full_len); ++ ++ if(buf_ptr + vsa_len > ob_end) ++ return 0; ++ memcpy(buf_ptr, vsa_value, vsa_len); ++ buf_ptr += vsa_len; ++ ++ return buf_ptr - out_buffer; ++} ++ ++/* ++ Функция добавляющая RADIUS атрибут rad_attr (значение по адресу * value) в буфер message_buffer. ++ Возвращает число байт на которое увеличился размер буфера занимаемый RADIUS атрибутами ++*/ ++inline uint8_t add_radius_attribute(uint8_t * message_buffer, uint8_t const * mb_end, ++ uint8_t rad_attr, void * value, uint8_t value_len) ++{ ++ if(message_buffer + VALUE_OFFSET + value_len > mb_end) /* Проверка на переполнение буфера */ ++ { ++ log_error("PATCH: Can't add RADIUS attribute - buffer overflow."); ++ return 0; ++ } ++ /* Формат информационного поля RADIUS сообщения. ++ 1 байт - имя атрибута ++ 1 байт - длина атрибута и значения формируется из длин полей: имя атрибута, длина, значение атрибута ++ X байт - значение атрибута ++ */ ++ message_buffer[ATTR_OFFSET] = rad_attr; ++ message_buffer[LEN_OFFSET] = value_len + 2; ++ memcpy(&message_buffer[VALUE_OFFSET], value, value_len); ++ return VALUE_OFFSET + value_len; ++} ++/* ++ Функция формирующая RADIUS-Access-Request ++*/ ++int assemble_auth_message( RAD_MESSAGE *access_req, const struct dhcp_packet * packet, unsigned length, ++ char *user_name, char *passwd, uint32_t subnet) ++{ ++ union ++ { ++ uint8_t auth[MD5_LEN]; ++ uint32_t digits[4]; ++ }auth_union; ++ ++ static uint8_t encr_passwd[ENCR_PWD_LEN]; ++ int32_t ret = 0; ++ uint32_t attr_ptr = 0; ++ int32_t i; ++ ++ bzero(access_req, sizeof(RAD_MESSAGE)); ++ access_req -> header.code = RAD_ACCESS_REQ; ++ access_req -> header.id = (uint8_t) random(); ++ uint8_t const * msg_end = access_req -> message + sizeof(access_req -> message); ++ ++ for(i = 0; i < 4; i++) ++ auth_union.digits[i] = random(); /* Генерируем случайный идентификатор RADIUS запроса */ ++ memcpy(access_req->header.auth, auth_union.auth, MD5_LEN); ++ ++ ret = encrypt_passwd(passwd, encr_passwd, rad_secret, auth_union.auth); ++ if(ret != ENCR_PWD_LEN) ++ { ++ log_error ("PATCH: error - encrypt_passwd() failed. Return value %d . Exiting from assemble_auth_message()!", ret); ++ return 0; ++ } ++ ++ ret = add_radius_attribute(access_req -> message + attr_ptr, msg_end, ++ USER_NAME_ATTR, user_name, strlen(user_name)); ++ if(!ret) ++ return 0; ++ attr_ptr += ret; ++ ++ ret = add_radius_attribute(access_req -> message + attr_ptr, msg_end, ++ USER_PASS_ATTR, encr_passwd, ENCR_PWD_LEN); ++ if(!ret) ++ return 0; ++ attr_ptr += ret; ++ ++ /* В качестве атрибута NAS_PORT используется IP адрес интерфейса на котором ++ получен DHCP запрос представленный как число типа int32_t */ ++ ret = add_radius_attribute(access_req -> message + attr_ptr, msg_end, ++ NAS_PORT, &subnet, sizeof(subnet)); ++ if(!ret) ++ return 0; ++ attr_ptr += ret; ++ ++ if(opts_send_to_srv) /* Если какие-либо DHCP опции нужно отправить на RADIUS сервер */ ++ { ++ uint8_t option_buffer[256]; ++ uint8_t option_len; ++ for(i = 0; opts_send_to_srv[i]; ++i) ++ { ++ uint8_t vsa_buffer[254]; ++ ++ if( (option_len = get_dhcp_option(packet, length, opts_send_to_srv[i], option_buffer, sizeof(option_buffer)) ) ) ++ { ++ ret = make_radius_vsa(vsa_buffer, sizeof(vsa_buffer), htonl(VSA_DHCP), ++ htons((const uint16_t) opts_send_to_srv[i]), option_len, option_buffer); ++ if(!ret) ++ return 0; ++ ++ ret = add_radius_attribute(access_req -> message + attr_ptr, msg_end, RAD_VSA, vsa_buffer , ret); ++ if(!ret) ++ return 0; ++ attr_ptr += ret; ++ } ++ } ++ } ++ ++ int full_packet_len = sizeof(RAD_HEADER) + attr_ptr; ++ if(full_packet_len < RADIUS_MIN_PACK_LEN) ++ full_packet_len = 20; ++ ++ access_req->header.pack_len = htons(full_packet_len); ++ ++ return full_packet_len; ++} ++ ++/* ++ Функция проверяющая соответствие значения аутентификатора в ответе сервера ++ значению аутентификатора в запросе сохранённом в списке кэша. ++*/ ++int calc_reply_auth(RAD_MESSAGE *packet, uint8_t *original_auth, ++ const char *secret) ++{ ++ uint8_t repl_checksumm[MD5_LEN]; ++ uint8_t auth[MD5_LEN]; ++ MD5_CTX context; ++ ++ if (!original_auth) ++ return -1; ++ ++ memcpy(auth, packet->header.auth, MD5_LEN); ++ memcpy(packet->header.auth, original_auth, MD5_LEN); ++ ++ MD5_Init(&context); ++ MD5_Update(&context, (uint8_t *)packet, ntohs(packet->header.pack_len)); ++ MD5_Update(&context, secret, strlen(secret)); ++ MD5_Final(repl_checksumm, &context); ++ ++ memcpy(packet->header.auth, auth, MD5_LEN); ++ ++ return memcmp(auth, repl_checksumm, MD5_LEN); ++} ++ ++/* ++ Функция удаляющая устаревшие элементы кэша DHCP ++*/ ++int delete_old_xid(XID_NODE * start_xid_list) ++{ ++ XID_NODE * xid_ptr = start_xid_list; ++ int deleted_xid_count = 0; ++ static time_t now; ++ if(time(&now) == (time_t)-1) ++ { ++ log_error("time() failed! Adding xid aborted."); ++ return 0; ++ } ++ while(xid_ptr) ++ { ++ if((now - xid_ptr->timestamp) > xid_ttl) ++ { ++ xid_ptr = delete_xid(xid_ptr); ++ deleted_xid_count++; ++ } else xid_ptr = xid_ptr->next; ++ } ++ return deleted_xid_count; ++} ++ ++/* ++ Функция добавляющая новый элемент в DHCP кэш. ++*/ ++XID_NODE * add_xid(struct dhcp_packet *packet, uint8_t dhcp_type, uint32_t if_index, struct hardware * hw_from) ++{ ++ static time_t now; ++ if(dhcp_type <= 0) ++ { ++ log_info ("INFO Invalid DHCP message type %d for xid: %d hw addr: %s - add_xid aborted.", ++ dhcp_type, ntohl( packet -> xid), print_hw_addr(1, MAC_ADDR_LEN, packet -> chaddr)); ++ return 0; ++ } ++ ++ if((int)search_xid(packet -> chaddr, if_index, packet -> giaddr.s_addr)) ++ return 0; /* Если подобный клиент уже есть в кэше, ++ то не добавляем новый узел */ ++ ++ if( time(&now) == (time_t) -1 ) ++ { ++ log_error("time() failed! Adding xid aborted."); ++ return 0; ++ } ++ ++ ++ XID_NODE * xid; ++ xid = malloc(sizeof(XID_NODE)); ++ if(!xid) ++ { ++ log_error("Creating DHCP cache failed!"); ++ return 0; ++ } ++ bzero(xid, sizeof(XID_NODE)); ++ xid -> client_msg_type = dhcp_type; ++ xid -> timestamp = now; ++ xid -> if_index = if_index; ++ xid -> relay_if_index = packet -> giaddr.s_addr; ++ xid -> xid = packet -> xid; ++ xid -> dhcp_flags = packet -> flags; ++ memcpy(xid -> chaddr, packet -> chaddr, MAC_ADDR_LEN); ++ memcpy(xid -> hw_from, hw_from -> hbuf + 1, MAC_ADDR_LEN); ++ ++ if(!xid_list_top) /* Если в кэше нет ни одного элемента */ ++ xid_list_top = xid; ++ else ++ { ++ xid_list_ptr -> next = xid; ++ xid_list_ptr -> next -> prev = xid_list_ptr; ++ } ++ ++ xid_list_ptr = xid; ++ ++ return xid_list_ptr; ++} ++ ++/* ++ Устанавливает указанный тип DHCP сообщения, возвращает предыдущее значение типа ++*/ ++uint16_t set_dhcp_type(struct dhcp_packet *request, uint16_t new_type) ++{ ++ uint8_t *option = (uint8_t *)request + sizeof (struct dhcp_packet) - DHCP_MAX_OPTION_LEN; ++ const uint8_t * opt_end = (const uint8_t *)request + sizeof(struct dhcp_packet); ++ uint8_t opt_len, old_type; ++ if(memcmp(option, magic_cookie, sizeof(magic_cookie))) /* Выходим если не найдено magic_cookie - */ ++ return -1; /* начало поля опций */ ++ option += sizeof(magic_cookie); ++ while((option < opt_end) && (*option != 255)) ++ { ++ if(*option == DHO_DHCP_MESSAGE_TYPE) ++ { ++ old_type = *(option + 2); ++ *(option + 2) = new_type; ++ if(new_type == DHCPNAK) ++ request -> flags |= htons (BOOTP_BROADCAST); /* Флаг broadcast */ ++ return old_type; ++ } ++ else option += *(option + 1) + 2; ++ } ++ return 0; ++} ++ ++/* ++ Функция получающая значение заданной опции из DHCP пакета ++*/ ++uint8_t get_dhcp_option(const struct dhcp_packet *request, uint16_t packet_len, uint8_t req_option, ++ void * option_value, const size_t value_size) ++{ ++ /* Calculate start address for field "options" in DHCP packet */ ++ uint8_t *option = (uint8_t *)request + sizeof (struct dhcp_packet) - DHCP_MAX_OPTION_LEN; ++ /* End options equal end packet */ ++ const uint8_t * opt_end = (const uint8_t *)request + packet_len; ++ if(memcmp(option, magic_cookie, sizeof(magic_cookie)))/* Check "Magic cookie" in first 4 bytes options-field */ ++ { ++ log_info("magic cookie not found!"); ++ return -1; ++ } ++ option += sizeof(magic_cookie); ++ ++ int opt_len; ++ while((option < opt_end) && (*option != DHO_END)) ++ { ++ opt_len = *(option + 1); ++ if((option + opt_len) > opt_end) ++ { ++ log_error("WARN: Invalid value in DHCP-option length. Attempting DoS?"); ++ return -1; ++ } ++ /* Возможно что первые некоторые байты поля опций пусты, например ++ в случае применения функции до задания типа DHCP сообщения ++ в функции translate_rad_to_dhcp(), либо в следсвии возможных ошибок протокола */ ++ if(!*option) ++ { ++ option += 3; /* Минимальный размер одного поля 3 байта: атрибут(1 байт)|длина (1 байт)|значение(минимум 1 байт). */ ++ continue; /* Перескакиваем на следующую опцию. */ ++ } ++ if(*option == req_option) ++ { ++ if(opt_len > value_size) ++ { ++ printf("\nWARNING! Option's length is more than was expected (opcode: %d op_len: %d > expected_len: %d). Attempting DoS?\n", ++ *option, opt_len, value_size); ++ return -1; ++ } ++ ++ if(option_value) ++ memcpy(option_value, option + 2, opt_len); ++ ++ return *(option + 1); ++ } ++ else ++ option += *(option + 1) + 2; ++ } ++ return 0; ++} ++ ++/* ++ Функция возвращающая тип DHCP сообщения, а так же строковую информацию о нём через указатель str_dhcp_type ++*/ ++uint16_t get_dhcp_type(struct dhcp_packet *request, char * str_dhcp_type) ++{ ++ uint8_t *option = (uint8_t *)request + sizeof (struct dhcp_packet) - DHCP_MAX_OPTION_LEN; ++ const uint8_t * opt_end = (const uint8_t *)request + sizeof(struct dhcp_packet); ++ uint8_t opt_len, dhcp_type; ++ if(memcmp(option, magic_cookie, sizeof(magic_cookie)))return -1; ++ option += sizeof(magic_cookie); ++ while((option < opt_end) && (*option != 255)) ++ { ++ if(*option == DHO_DHCP_MESSAGE_TYPE) ++ { ++ if(str_dhcp_type) ++ { /*Возвращаем строку указывающую тип*/ ++ switch(*(option + 2)) ++ { ++ case DHCPDISCOVER: memcpy(str_dhcp_type, "DHCPDISCOVER", strlen("DHCPDISCOVER")); break; ++ case DHCPOFFER: memcpy(str_dhcp_type, "DHCPOFFER", strlen("DHCPOFFER")); break; ++ case DHCPREQUEST: memcpy(str_dhcp_type, "DHCPREQUEST", strlen("DHCPREQUEST")); break; ++ case DHCPDECLINE: memcpy(str_dhcp_type, "DHCPDECLINE", strlen("DHCPDECLINE")); break; ++ case DHCPACK: memcpy(str_dhcp_type, "DHCPACK", strlen("DHCPACK")); break; ++ case DHCPNAK: memcpy(str_dhcp_type, "DHCPNAK", strlen("DHCPNAK")); break; ++ case DHCPRELEASE: memcpy(str_dhcp_type, "DHCPRELEASE", strlen("DHCPRELEASE")); break; ++ case DHCPINFORM: memcpy(str_dhcp_type, "DHCPINFORM", strlen("DHCPINFORM")); break; ++ default: memcpy(str_dhcp_type, "UNKNOWN", strlen("UNKNOWN")); break; ++ } ++ } ++ return *(option + 2); ++ } ++ else option += *(option + 1) + 2; ++ } ++ return 0; ++} ++ ++/* ++ Функция получающая основную запрашиваемую клиентом информацию из DHCP сообщения ++*/ ++int get_dhcp_request_info(struct dhcp_packet *request, DHCP_REQ_INFO * request_info) ++{ ++ uint8_t *option = (uint8_t*)request->options; ++ const uint8_t * opt_end = (const uint8_t *)request + sizeof(struct dhcp_packet); ++ uint8_t opt_len, dhcp_type; ++ if(memcmp(option, magic_cookie, sizeof(magic_cookie))) ++ return 0; /* Выходим, если не найдена последовательность magic_cookie */ ++ option += sizeof(magic_cookie); ++ ++ short options_found = 0; ++ while((option < opt_end) && (options_found < 2) && (*option != 255)) ++ { ++ switch((uint8_t)*option) ++ { ++ case DHO_DHCP_SERVER_IDENTIFIER: ++ memcpy(&request_info->server_id, option + 2, *(option + 1)); ++ options_found++; ++ break; ++ case DHO_DHCP_REQUESTED_ADDRESS: ++ memcpy(&request_info->req_addr, option + 2, *(option + 1)); ++ options_found++; ++ break; ++ } ++ if(options_found == 2) ++ { ++ return 1; ++ } ++ else option += *(option + 1) + 2; ++ } ++ if(request_info->client_addr.s_addr == 0) ++ request_info->client_addr = request->ciaddr; ++ return 1; ++} ++ ++/* ++ Функция удаляющая элемент кэша на который ссылается указатель xid_n ++*/ ++inline XID_NODE * delete_xid(XID_NODE *xid_n) ++{ ++ if(xid_n == xid_list_top) ++ { ++ if(xid_list_ptr == xid_list_top) ++ { ++ xid_list_top = 0; ++ xid_list_ptr = 0; ++ } ++ else xid_list_top = xid_n->next; ++ } ++ else if(!xid_n->next) ++ { ++ xid_n->prev->next = 0; ++ xid_list_ptr = xid_n->prev; ++ } ++ else ++ { ++ xid_n->prev->next = xid_n->next; ++ xid_n->next->prev = xid_n->prev; ++ } ++ XID_NODE * next_xid = xid_n->next; ++ ++ if(xid_n -> out_packet) ++ free(xid_n -> out_packet); ++ free(xid_n); ++ ++ -- dhcp_cache_len; ++ return next_xid; ++} ++ ++/* ++ Функция удаляющая дублирующиеся по MAC адресу клиента и значению xid элементы кэша. ++ Такое бывает необходимо в случае повторной инициализации клиентом процесса получения ++ IP адреса - в последующих пакетах поле xid будет иметь другое значение. ++ В таком случае все предыдущие запросы являются устаревшими и будут удалены. ++ Кроме MAC адреса сверяется индекс интерфейса на котором получен запрос от клиента. ++ Индексом интерфейса является его основной (не алиасный!) IP адрес ++ представленный как число типа uint32_t ++*/ ++int del_duplicate_xid(uint32_t xid, uint8_t * hw_addr, uint32_t if_index, uint32_t relay_if_index) ++{ ++ XID_NODE * xid_ptr = xid_list_top; ++ int ret_code = 0; ++ if(relay_if_index) ++ { ++ while(xid_ptr) ++ { ++ if( !memcmp(xid_ptr -> chaddr, hw_addr, MAC_ADDR_LEN) && ++ (xid_ptr -> xid != xid) && ++ (xid_ptr -> relay_if_index == relay_if_index) ++ ) ++ { ++ xid_ptr = delete_xid(xid_ptr); ++ ++ret_code; ++ } ++ else xid_ptr = xid_ptr -> next; ++ } ++ } ++ else ++ { ++ while(xid_ptr) ++ { ++ if( !memcmp(xid_ptr -> chaddr, hw_addr, MAC_ADDR_LEN) && ++ (xid_ptr -> xid != xid) && ++ (xid_ptr -> if_index == if_index) ++ ) ++ { ++ xid_ptr = delete_xid(xid_ptr); ++ ++ret_code; ++ } ++ else xid_ptr = xid_ptr -> next; ++ } ++ } ++ return ret_code; ++} ++ ++/* ++ Функция производящая поиск нужного узла кэша и возвращающая в случае успеха указатель на него. ++*/ ++XID_NODE * search_xid(uint8_t * hw_addr, uint32_t if_index, uint32_t relay_if_index) ++{ ++ XID_NODE * tmp_ptr = xid_list_top; ++ ++ if(relay_if_index) ++ { ++ while(tmp_ptr) ++ { ++ if(!memcmp(tmp_ptr -> chaddr, hw_addr, MAC_ADDR_LEN) && ++ (tmp_ptr -> relay_if_index == relay_if_index) ) ++ return tmp_ptr; ++ tmp_ptr = tmp_ptr -> next; ++ } ++ } ++ else ++ { ++ /* Иначе производим поиск по MAC-адресу клиента и ID интерфейса на котором получен запрос. ++ Запрос может быть получен локально от клиента, и тогда ID равен tmp_ptr->if_index, ++ либо запрос может быть переслан от клиента агентом пересылки. ++ Тогда ID равен tmp_ptr -> out_packet -> giaddr.s_addr */ ++ while(tmp_ptr) ++ { ++ if(!memcmp(tmp_ptr -> chaddr, hw_addr, MAC_ADDR_LEN) && ++ (tmp_ptr -> if_index == if_index) ) ++ return tmp_ptr; ++ tmp_ptr = tmp_ptr -> next; ++ } ++ } ++ return 0; ++} ++ ++/* ++ Возвращает смещение с байтах поля указанного option ++ относительно начала заголовка DHCP пакета. ++*/ ++int dhcp_option_offset(const uint16_t option) ++{ ++ static struct dhcp_packet * pack; ++ switch(option) ++ { ++ case dhcp_op: ++ return (uint8_t*)(&pack -> op) - (uint8_t *)pack; ++ case dhcp_hwtype: ++ return (uint8_t*)(&pack -> htype) - (uint8_t *)pack; ++ case dhcp_hwlen: ++ return (uint8_t*)(&pack -> hlen) - (uint8_t *)pack; ++ case dhcp_hops: ++ return (uint8_t*)(&pack -> hops) - (uint8_t *)pack; ++ case dhcp_xid: ++ return (uint8_t*)(&pack -> xid) - (uint8_t *)pack; ++ case dhcp_secs: ++ return (uint8_t*)(&pack -> secs) - (uint8_t *)pack; ++ case dhcp_flags: ++ return (uint8_t*)(&pack -> flags) - (uint8_t *)pack; ++ case dhcp_ciaddr: ++ return (uint8_t*)(&pack -> ciaddr) - (uint8_t *)pack; ++ case dhcp_yiaddr: ++ return (uint8_t*)&pack -> yiaddr - (uint8_t *)pack; ++ case dhcp_siaddr: ++ return (uint8_t*)(&pack -> siaddr) - (uint8_t *)pack; ++ case dhcp_giaddr: ++ return (uint8_t*)(&pack -> giaddr) - (uint8_t *)pack; ++ case dhcp_chaddr: ++ return (uint8_t*)(&pack -> chaddr) - (uint8_t *)pack; ++ case dhcp_sname: ++ return (uint8_t*)(&pack -> sname) - (uint8_t *)pack; ++ case dhcp_bootfile: ++ return (uint8_t*)(&pack -> file) - (uint8_t *)pack; ++ default: ++ return OPTIONAL_OFFSET; ++ } ++} ++ ++/* ++ Функция производящая разбор RADIUS VSA ++*/ ++int dispatch_radius_vsa(const int length, const uint8_t * attr_cont, struct dhcp_packet * out_packet, ++ unsigned int * opt_len, AUX_ATTRS *aux_attrs) ++{ ++ uint32_t vendor_id = ntohl(*((uint32_t *) attr_cont)); ++ if(vendor_id != VSA_DHCP) ++ { ++ log_info("PATCH: found unknown RADIUS VSA vendor ID: %u.", vendor_id); ++ return INVALID_VSA; ++ } ++ ++ ++ uint16_t option = ntohs(((PVSA_DHCP_HEADER) attr_cont) -> type); ++ uint8_t data_len = ((PVSA_DHCP_HEADER) attr_cont) -> length - 3; /* 3 - суммарная длина полей length & type */ ++ uint8_t *data = (uint8_t*) &((PVSA_DHCP_HEADER) attr_cont) -> data; ++ int offset = OPTIONAL_OFFSET; ++ ++ if(option > MAX_STD_DHCP_OPT) ++ offset = dhcp_option_offset(option); ++ ++ if(offset != OPTIONAL_OFFSET) /* Поле из DHCP заголовка */ ++ memcpy(((uint8_t *) out_packet) + offset, data, data_len); ++ else if(option < MAX_STD_DHCP_OPT) /* Опция из стандартного набора DHCP опций */ ++ { ++ *((out_packet -> options) + (*opt_len)++) = option; ++ *((out_packet -> options) + (*opt_len)++) = data_len; ++ memcpy((uint8_t*)(out_packet -> options) + *opt_len, data, data_len); ++ *opt_len += data_len; ++ } else /* Служебная опция нуждающаяся в дополнительной обработке перед записью в DHCP пакет */ ++ { ++ switch(option) ++ { ++ case RAD_IFINDEX: ++ aux_attrs -> if_index = *((uint32_t*)data); ++ break; ++ case RAD_DYN_CLIENT: ++ /* Данный атрибут свидетельствует о том что для этого клиента нет привязки MAC -> IP */ ++ /* Устанавливаем разрешение для кэширования, если оно разрешено глобальными настройками */ ++ /* См. опцию "radius-allow-dynamic-cache" */ ++ aux_attrs -> allow_cache = radius_allow_dynamic_cache; ++ break; ++ default: ++ return INVALID_VSA; ++ break; ++ } ++ } ++ return offset; ++} ++ ++/* Функция создающая DHCPNAK сообщение в буфере на который указывает out_packet */ ++void make_dhcp_nak(struct dhcp_packet * out_packet, XID_NODE * cur_xid, const uint32_t server_id) ++{ ++ *(out_packet->options + sizeof(magic_cookie) + 2) = DHCPNAK; ++ out_packet->flags |= htons (BOOTP_BROADCAST); /* Флаг broadcast */ ++ out_packet->ciaddr.s_addr = 0; ++ out_packet->yiaddr.s_addr = 0; ++ out_packet -> siaddr.s_addr = server_id; ++ *(out_packet->options + sizeof(magic_cookie) + 3) = DHO_DHCP_SERVER_IDENTIFIER; ++ *(out_packet->options + sizeof(magic_cookie) + 4) = sizeof(server_id); ++ memcpy(out_packet->options + sizeof(magic_cookie) + 5, &server_id, sizeof(server_id)); ++ cur_xid -> server_msg_type = DHCPNAK; ++} ++ ++/* Функция возвращающая классовую маску */ ++inline uint32_t make_default_netmask(const uint32_t addr) ++{ ++ if(addr <= 0x7FFFFFFF) ++ return 0xFF000000; ++ if(addr <= 0xBFFF0000) ++ return 0xFFFF0000; ++ if(addr <= 0xDFFFFF00) ++ return 0xFFFFFF00; ++ return 0xFFFFFFFF; ++} ++ ++/* ++ Функция устанавливающая тип DHCP сообщения в исходящем пакете клиенту. ++*/ ++uint8_t set_dhcp_type_of_out_packet(struct dhcp_packet * out_packet, const int out_pack_len, ++ XID_NODE * cur_xid, const server_id) ++{ ++ uint8_t out_packet_type = DHCPACK; ++ /* Выбираем тип сообщения который будет отправлен в DHCP пакете к клиенту */ ++ *(out_packet->options + sizeof(magic_cookie)) = DHO_DHCP_MESSAGE_TYPE; ++ *(out_packet->options + sizeof(magic_cookie) + 1) = 1; ++ switch (cur_xid -> client_msg_type) ++ { ++ case DHCPDISCOVER: ++ *(out_packet->options + sizeof(magic_cookie) + 2) = DHCPOFFER; ++ cur_xid -> server_msg_type = DHCPOFFER; ++ /* Записываем в XID предлагаемый пользователю адрес */ ++ memcpy(&cur_xid -> request_info.client_addr, &out_packet -> yiaddr, sizeof(struct in_addr)); ++ break; ++ case DHCPREQUEST: ++ { ++ uint32_t subnet_mask; ++ if(!get_dhcp_option(out_packet, out_pack_len, DHO_SUBNET_MASK, &subnet_mask, sizeof(subnet_mask))) ++ subnet_mask = make_default_netmask(cur_xid -> request_info.client_addr.s_addr); ++ ++ if( cur_xid -> request_info.client_addr.s_addr && ++ ( (cur_xid -> request_info.client_addr.s_addr & subnet_mask) != ++ (out_packet -> yiaddr.s_addr & subnet_mask) ) ++ ) /* Игнорируем запрос если клиент запрашивает иную подсеть */ ++ { ++ log_info("Ignore DHCPREQUEST because the IP address of the client (%s) is incorrect.", ++ inet_ntoa(cur_xid -> request_info.client_addr)); ++ return 0; ++ } ++ ++ if(cur_xid -> request_info.req_addr && ++ ( (cur_xid -> request_info.req_addr & subnet_mask) != ++ (out_packet -> yiaddr.s_addr & subnet_mask) ) ) ++ { ++ make_dhcp_nak(out_packet, cur_xid, server_id); ++ out_packet_type = DHCPNAK; ++ } ++ else /* Проверяем адрес с которого клиент послал запрос на соответсвие предлагаемому ++ клиенту адресу. Если они не совпадают - отвергаем запрос */ ++ if(cur_xid -> request_info.client_addr.s_addr != 0) ++ { ++ if(out_packet -> yiaddr.s_addr != cur_xid -> request_info.client_addr.s_addr) ++ { ++ make_dhcp_nak(out_packet, cur_xid, server_id); ++ out_packet_type = DHCPNAK; ++ }else ++ { ++ *(out_packet->options + sizeof(magic_cookie) + 2) = DHCPACK; ++ cur_xid -> server_msg_type = DHCPACK; ++ } ++ } ++ else if(cur_xid->request_info.req_addr != 0) ++ { ++ if(cur_xid->request_info.req_addr != out_packet->yiaddr.s_addr) ++ { ++ make_dhcp_nak(out_packet, cur_xid, server_id); ++ out_packet_type = DHCPNAK; ++ } ++ else ++ { ++ *(out_packet->options + sizeof(magic_cookie) + 2) = DHCPACK; ++ cur_xid -> server_msg_type = DHCPACK; ++ } ++ }else ++ { ++ struct in_addr ip; ++ ip.s_addr = cur_xid->request_info.req_addr; ++ log_error("WARN: Found invalid DHCP-request \"required address\":%s \"client address\" %s (hw: %s), state: %d", ++ inet_ntoa(cur_xid->request_info.client_addr), ++ inet_ntoa(ip), ++ print_hw_addr ( 1, MAC_ADDR_LEN, cur_xid -> chaddr), ++ cur_xid -> server_msg_type); ++ return 0; ++ } ++ } ++ break; ++ default: ++ break; ++ } /* End switch () */ ++ return out_packet_type; ++} ++ ++/* ++ Функция преобразующая полученный от сервера RADIUS-Access-Accept в DHCP-сообщение клиенту ++ запросившему IP адрес ++*/ ++int translate_rad_to_dhcp(RAD_MESSAGE *rad_pack, struct dhcp_packet * out_packet, uint16_t received_length, ++ uint32_t * user_interface ) /* Параметр необходим для корректной обработки */ ++{ /* запросов с xid == 0 */ ++ if(!out_packet) ++ { ++ log_error("NULL pointer *out_packet in function translate_rad_to_dhcp(). Exiting whith error code 0."); ++ return 0; ++ } ++ ++ bzero(out_packet, sizeof(struct dhcp_packet)); ++ ++ uint8_t * attr_ptr = (uint8_t *)rad_pack + sizeof(RAD_HEADER); /* Указатель на начало ++ обрабатываемого RADIUS атрибута */ ++ uint8_t * attr_cont = 0; /* Указатель на начало значения атрибута */ ++ unsigned int opt_len = 0; ++ /* Вставляем "magic cookie" в начало DHCP опций: 99.130.83.99 */ ++ memcpy(out_packet->options, magic_cookie, sizeof(magic_cookie)); ++ opt_len += sizeof(magic_cookie) + /* Задаём начальную длину пакета равной длине */ ++ 3;/* "magic cookie" + длина DHCP-Message-type */ ++ if( received_length != ntohs(rad_pack -> header.pack_len)) ++ { ++ log_error("ERROR: Invalid length in RADIUS header." ++ "Received %u bytes, but length = %u in RADIUS header.", ++ received_length, ntohs(rad_pack -> header.pack_len)); ++ return 0; ++ } ++ ++ /* Начинаем разбор переданных атрибутов */ ++ uint8_t rad_attr = 0; ++ uint8_t attr_len = 0; ++ AUX_ATTRS aux_attrs; ++ bzero(&aux_attrs, sizeof(aux_attrs)); ++ aux_attrs.allow_cache = radius_allow_cache; ++ /* Цикл пока не достигнем конца пакета */ ++ while( attr_ptr < (uint8_t *) rad_pack + ntohs(rad_pack -> header.pack_len) ) ++ { ++ rad_attr = *attr_ptr; ++ attr_len = *(attr_ptr + 1); ++ attr_cont = attr_ptr + 2; ++ ++ /* Проверка на неправильную длину атрибута */ ++ if( (uint8_t *)(rad_attr + attr_len) > (uint8_t *)(rad_pack + received_length)) ++ { ++ log_error("ERROR: Invalid RADIUS attribute length: %u. " ++ "End of option exceed the bounds of packet.", ++ attr_len); ++ return 0; ++ } ++ /* Проверка на переполнение поля опций DHCP пакета */ ++ if(opt_len + attr_len > DHCP_MAX_OPTION_LEN) ++ { ++ log_error("ERROR: DHCP_OPTION owerflow! Exiting from translate_rad_to_dhcp() with error code 0."); ++ return 0; ++ } ++ ++ if(rad_attr == RAD_VSA) ++ { ++ if(dispatch_radius_vsa(attr_len, attr_cont, out_packet, &opt_len, &aux_attrs) == INVALID_VSA) ++ log_info("PATCH: Skip this attribute."); ++ }else ++ log_info ("WARN: dhcp2radius for dhcpd patch support only RADIUS VSA. Attribute: %u, len: %u not supported.", ++ rad_attr, attr_len); ++ attr_ptr += attr_len; ++ } /* End while(...)*/ ++ ++ XID_NODE * cur_xid; ++ cur_xid = search_xid(out_packet -> chaddr, aux_attrs.if_index, out_packet -> giaddr.s_addr); ++ if(!cur_xid) ++ { ++ struct in_addr ip; ++ ip.s_addr = aux_attrs.if_index; ++ log_info("WARN: Can't find request info for client %s from interface %s (%u)", ++ print_hw_addr ( 1, MAC_ADDR_LEN, out_packet -> chaddr), inet_ntoa(ip), aux_attrs.if_index); ++ return 0; ++ } ++ ++ *user_interface = cur_xid -> if_index; ++ ++ cur_xid -> allow_cache = aux_attrs.allow_cache; ++ out_packet -> flags = cur_xid -> dhcp_flags; ++ ++ int out_pack_len = sizeof(struct dhcp_packet) - DHCP_MAX_OPTION_LEN + opt_len; ++ uint32_t server_id; ++ if(!get_dhcp_option(out_packet, out_pack_len, DHO_DHCP_SERVER_IDENTIFIER, &server_id, sizeof(server_id))) ++ return -1; ++ ++ /* Выбираем тип сообщения который будет отправлен в DHCP пакете к клиенту */ ++ uint8_t out_packet_type = set_dhcp_type_of_out_packet(out_packet, out_pack_len, cur_xid, server_id); ++ if(!out_packet_type) ++ return 0; ++ if(out_packet_type == DHCPNAK) ++ { ++ *(out_packet->options + sizeof(magic_cookie) + 5 + sizeof(server_id)) = DHO_END; ++ out_pack_len = sizeof(struct dhcp_packet) - DHCP_MAX_OPTION_LEN + /* Общая длина пакета складывается из: */ ++ sizeof(magic_cookie) + /* размера обязательного поля magic_cookie, */ ++ 3 + /* размера поля указывающего тип DHCP сообщения ++ включая длину полей указыающих имя и длину ++ DHCP атрибута (1 + 1 + 1 = 3), */ ++ 2 + sizeof(server_id) + /* размера идентификатора сервера, так же ++ включая длину служебных полей */ ++ 1; /* и длины опции-завершителя DHO_END */ ++ } ++ else ++ { ++ *(out_packet->options + opt_len++) = DHO_END; ++ ++out_pack_len; ++ } ++ ++ out_packet -> op = BOOTREPLY; ++ out_packet -> htype = HTYPE_ETHER; ++ out_packet -> hlen = MAC_ADDR_LEN; ++ out_packet -> xid = cur_xid -> xid; ++#ifdef __linux__ ++ /* Обновляем, если это необходимо статическую ARP таблицу ОС */ ++ if(updating_arp_cache_perm && !out_packet -> giaddr.s_addr && /* Должно быть разрешено обновление, пакет не должен */ ++ aux_attrs.allow_cache && out_packet -> yiaddr.s_addr) /* быть отправлен через агента пересылки, должно быть */ ++ { /* разрешено кэширование данного клиента и должен быть */ ++ int ret_code = update_arp_cache_perm(out_packet -> yiaddr.s_addr, out_packet -> chaddr); /* известен его адрес */ ++ if(ret_code < 0) ++ log_error("ERROR: Can't updating ARP cache for %s (hw: %s)", ++ inet_ntoa(out_packet -> yiaddr), print_hw_addr(1, MAC_ADDR_LEN, out_packet -> chaddr)); ++ if(ret_code > 0) ++ log_info("INFO: Updating ARP entry for %s (new hw address: %s)", ++ inet_ntoa(out_packet -> yiaddr), print_hw_addr(1, MAC_ADDR_LEN, out_packet -> chaddr)); ++ } ++#endif ++ return out_pack_len; ++} ++ ++uint16_t update_xid_node(XID_NODE * xid, struct dhcp_packet * packet, unsigned pack_len) ++{ ++ uint16_t msg_type = DHCPNAK; ++ struct in_addr requested_addr; ++ if( !get_dhcp_option(packet, pack_len, ++ DHO_DHCP_REQUESTED_ADDRESS, &requested_addr.s_addr, sizeof(requested_addr.s_addr)) ) ++ requested_addr.s_addr = 0; ++ if(!xid -> out_packet) ++ { ++ log_error("PATCH: NULL argument for update_xid_node() found!"); ++ return 0; ++ } ++ /* Сюда попадают пакеты обязательно имеющие тип DHCPREQUEST, ++ следовательно у них обязательно должно быть ++ заполнено одно из ниже указанных полей */ ++ if(requested_addr.s_addr) ++ { ++ if(requested_addr.s_addr != xid -> out_packet -> yiaddr.s_addr) ++ { ++ xid -> out_packet -> yiaddr.s_addr = 0; ++ xid -> out_packet -> ciaddr.s_addr = 0; ++ } ++ else ++ msg_type = DHCPACK; ++ } ++ else if(packet -> ciaddr.s_addr) ++ { ++ if(packet -> ciaddr.s_addr != xid -> out_packet -> yiaddr.s_addr) ++ { ++ xid -> out_packet -> yiaddr.s_addr = 0; ++ xid -> out_packet -> ciaddr.s_addr = 0; ++ } ++ else msg_type = DHCPACK; ++ } ++ else ++ { ++ log_error("INFO: Invalid DHCPREQUEST for xid: %d hw addr: %s.", ++ ntohl( packet -> xid), print_hw_addr(1, MAC_ADDR_LEN, packet -> chaddr)); ++ return 0; ++ } ++ ++ xid -> out_packet -> xid = packet -> xid; ++ xid -> xid = packet -> xid; ++ ++ set_dhcp_type( xid -> out_packet, msg_type); ++ xid -> server_msg_type = msg_type; ++ return msg_type; ++} ++ ++/* ++ Функция удаляющая самый старый запрос в DHCP кэше ++*/ ++inline int delete_oldest_cache_node(XID_NODE * cache_node) ++{ ++ delete_xid(xid_list_top); ++ return dhcp_cache_len; ++} ++ ++/* ++ Функция проверяющая не превышено-ли максимально допустимое число запросов на которые не ++ получены ответы от сервера. Применяется для переключения на резервный сервер в случае ++ падения основного. ++*/ ++inline int is_unfinished_requests_overflow(int * count, int inc, int max_count, int zeroing) ++{ ++ static int counter = 0; ++ if(++counter == zeroing) ++ *count = counter = 0; ++ if(!inc && *count > 0) ++ -- *count; ++ else ++ if(inc) ++ ++ *count; ++ if(*count > max_count) ++ return *count; ++ return 0; ++} ++ ++/* ++ Функция анализирующая RADIUS-Response ++*/ ++int dispatch_radius_packet(struct dhcp_packet * packet, unsigned length, ++ struct in_addr * user_if, uint8_t * hw_from) ++{ ++ RAD_MESSAGE *rad_ptr; ++ rad_ptr = (RAD_MESSAGE *) packet; ++ struct dhcp_packet tmp_dhcp_pack; ++ XID_NODE * xid; ++ ++ is_unfinished_requests_overflow(&unfinished_requests, DEC, MAX_UNFINISHED_REQ, ZEROING_UNFIN_REQ); ++ ++ if(rad_ptr->header.code != RAD_ACCESS_ACK) ++ { ++ if(rad_ptr -> header.code == RAD_ACCESS_NAK) /* Access Reject приходит в случае отсутствия */ ++ { /* пула адресов для данного сегмента */ ++ XID_NODE * xid = xid_list_top; ++ while(xid) ++ { ++ if(xid -> req_header.id == rad_ptr -> header.id && ++ calc_reply_auth(rad_ptr, xid -> req_header.auth, rad_secret) == 0) ++ { ++ static struct in_addr ip; ++ static char srv_addr[16]; ++ strncpy(srv_addr, inet_ntoa(radius_servers[server_index].sin_addr), sizeof(srv_addr)); ++ ip.s_addr = xid -> relay_if_index ? xid -> relay_if_index : xid -> if_index; ++ log_info("RADIUS server %s reject DHCP client %s from %s", ++ srv_addr, ++ print_hw_addr(HTYPE_ETHER, MAC_ADDR_LEN, xid -> chaddr), ++ inet_ntoa(ip)); ++ goto rejected_ok; ++ } ++ xid = xid -> next; ++ } ++ log_info("RADIUS-Access-Reject found, but I can't found DHCP info for him."); ++rejected_ok:; ++ } ++ else log_info ("INFO: Recv RADIUS, code: %u - this is not Request-Accept. Drop this packet.", ++ rad_ptr -> header.code); ++ return 0; ++ } ++ ++ length = translate_rad_to_dhcp((RAD_MESSAGE *)packet, &tmp_dhcp_pack, length, &user_if -> s_addr); ++ ++ if(!length) ++ return 0; ++ if((signed int)length < 0) ++ { ++ log_debug("WARN: invalid client IP address. BOOTREPLY aborted."); ++ return 0; ++ } ++ ++ xid = search_xid(tmp_dhcp_pack.chaddr, user_if -> s_addr, tmp_dhcp_pack.giaddr.s_addr); ++ ++ memcpy(hw_from, xid -> hw_from, MAC_ADDR_LEN); ++ ++ if(!xid) ++ { ++ log_info ("INFO: Get unknown xid %d with unknown user interface: %u in RADIUS. BOOTREPLY aborted.", ++ tmp_dhcp_pack.xid, user_if -> s_addr); ++ return 0; ++ } ++ ++ if(calc_reply_auth(rad_ptr, xid -> req_header.auth, rad_secret) != 0) ++ { ++ log_info ("WARN: Invalid autentificator in RADIUS. BOOTREPLY aborted."); ++ return 0; ++ } ++ ++ if(rad_ptr->header.id != xid->req_header.id) ++ { ++ log_info ("INFO: Invalid RADIUS-id in RADIUS-reply. BOOTREPLY aborted."); ++ return 0; ++ } ++ ++ bzero(packet, sizeof(struct dhcp_packet)); ++ memcpy(packet, &tmp_dhcp_pack, sizeof(struct dhcp_packet)); ++ ++ if(xid -> allow_cache) ++ { /* Если разрешено кэширование то */ ++ /* копируем DHCPREPLY в поле out_packet для хранения в кэше */ ++ xid -> out_packet = (struct dhcp_packet *) calloc(1, length); ++ if(!xid -> out_packet) ++ log_fatal("PATCH: Can't allocate memory to cache for DHCP packet."); ++ memcpy(xid -> out_packet, packet, length); ++ xid -> out_pack_len = length; ++ } ++ ++ delete_xid_if_mac_not_equal(packet -> yiaddr.s_addr, packet -> chaddr); ++ ++ return length; ++} ++ ++/* ++ Функция анализирующая полученный от клиента DHCP запрос ++*/ ++int dispatch_dhcp_packet(struct dhcp_packet * packet, ++ unsigned * length, ++ struct interface_info *ip, ++ struct hardware * hw_from, ++ uint8_t * from_cache, ++ RAD_MESSAGE * auth_pack) ++{ ++ XID_NODE * xid_ptr; ++ uint16_t dhcp_type = 0; ++ static char str_dhcp_type[20]; ++ float qps = 0; /* Общее усреднённое число запросов в секунду */ ++ float qps_host = 0; /* Усреднённое число запросов от конкретного хоста */ ++ ++ /*log_info("hw_from: %s", print_hw_addr(hw_from -> hbuf[0], hw_from -> hlen - 1, hw_from -> hbuf + 1));*/ ++ ++ /* Определяем тип DHCP запроса */ ++ bzero(str_dhcp_type, sizeof(str_dhcp_type)); ++ dhcp_type = get_dhcp_type(packet, str_dhcp_type); ++ ++ if(dhcp_type == DHCPDISCOVER || dhcp_type == DHCPREQUEST) ++ { ++ qps = count_queries_per_sec(); ++ ++ /* Начинаем вычисления числа запросов для каждого отдельно взятого хоста в секунду */ ++ qps_host = qps_hosts(packet -> chaddr, 0); ++ if(qps_host > max_qps_from_host) ++ { ++ log_info("ERROR: Exceed maximum DHCP queries per second (%g/sec) for host %s/%s. Maximum: %g/sec. Drop request.", ++ qps_host, ++ print_hw_addr (packet -> htype, packet -> hlen, packet -> chaddr), ++ ip -> name, ++ max_qps_from_host); ++ return -1; ++ } ++ /* Если клиент не блокирован по числу запросов в секунду, ++ то добавляем временной штамп в кольцевой буфер */ ++ if(ring_ptr >= RING_MAX) ++ ring_ptr = 0; ++ normal_clients_counter[ring_ptr] = time(0); ++ ++ring_ptr; ++ ++ delete_old_xid(xid_list_top); /* Удаляем старые узлы dhcp-транзакций */ ++ static DHCP_REQ_INFO new_req_info; ++ bzero(&new_req_info, sizeof(new_req_info)); ++ if(dhcp_type == DHCPREQUEST) /* Если тип запроса DHCPREQUEST то возможно мы уже имеем */ ++ { /* кэшированный ответ */ ++ /* Возвращает структуру содержащую адрес клиента, запрашиваемый адрес */ ++ get_dhcp_request_info(packet, &new_req_info); /* и идентификатор сервера */ ++ xid_ptr = search_xid(packet -> chaddr, ip -> primary_address.s_addr, packet -> giaddr.s_addr); ++ ++ if( xid_ptr /* Если соответствующий узел найден */ ++ && xid_ptr -> out_packet /* и существует кэшированный пакет */ ++ && xid_ptr -> allow_cache /* и разрешено его кэширование */ ++ && xid_ptr -> out_packet -> op) /* и в нём уже имеется ответ сервера */ ++ { ++ uint32_t subnet_mask; ++ if(!get_dhcp_option(xid_ptr -> out_packet, xid_ptr -> out_pack_len, DHO_SUBNET_MASK, ++ &subnet_mask, sizeof(subnet_mask))) ++ subnet_mask = make_default_netmask(xid_ptr -> request_info.client_addr.s_addr); ++ ++ if( packet -> ciaddr.s_addr && ++ ( (packet -> ciaddr.s_addr & subnet_mask) != ++ (xid_ptr -> out_packet -> yiaddr.s_addr & subnet_mask) ) ) ++ return 0; /* Игнорируем запрос если клиент запрашивает иную подсеть */ ++ ++ /* Информация для этого адреса уже есть в кэше */ ++ /* Отправляем клиенту кэшированный ответ заменив поле типа */ ++ switch(xid_ptr -> server_msg_type) ++ { /* В этих случаях XID содержит ответный пакет */ ++ case DHCPOFFER: ++ case DHCPACK: ++ case DHCPNAK: ++ if(!( dhcp_type = update_xid_node(xid_ptr, packet, *length)) ) ++ { ++ log_info("Can't update cache node."); ++ return -1; ++ } ++ /* Отдаём клиенту кэшированный пакет */ ++ memcpy(packet, xid_ptr -> out_packet, xid_ptr -> out_pack_len); ++ memcpy(hw_from -> hbuf + 1, xid_ptr -> hw_from, MAC_ADDR_LEN); ++ *length = xid_ptr -> out_pack_len; ++ ++ static char from_str[16]; /* Строка используемая для сохранения строкового представления ++ IP адреса агента пересылки если таковой используется */ ++ if(packet -> giaddr.s_addr) ++ strncpy(from_str, inet_ntoa(packet -> giaddr), sizeof(from_str)); ++ ++ log_info ("Received %s (%s/%s) found in dhcpd cache.", str_dhcp_type, ++ print_hw_addr (packet -> htype, MAC_ADDR_LEN , packet -> chaddr), ++ packet -> giaddr.s_addr ? inet_ntoa(packet -> giaddr) : ip -> name); ++ *from_cache = 1; ++ if(qps > max_qps) ++ log_error("WARN: Exceed maximum DHCP queries per second - %g/sec (%g/sec max)." ++ " But DHCP-response found in cache. Sending packet...", ++ qps, max_qps); ++ xid_ptr -> client_msg_type = DHCPREQUEST; ++ return 1; ++ break; ++ } ++ } ++ } ++ ++ /* Тип DHCP запроса - DHCPDISCOVER, либо запрещено кэширование для данного клиента, ++ значит необходима пересылка данных на RADIUS сервер. */ ++ /* Проверяем не превышено ли пороговое значение числа запросов в секунду */ ++ if(qps > max_qps) ++ { /* Если превышен, то дропаем новый запрос */ ++ log_error("ERROR: Exceed maximum DHCP queries per second - %g/sec (%g/sec)", ++ qps, max_qps); ++ log_error("ERROR: Drop %s from %s/%s !", str_dhcp_type, ++ print_hw_addr (packet -> htype, packet -> hlen, packet -> chaddr), ++ ip -> name); ++ return -1; ++ } ++ ++ /* Если наш запрос не найден в кэше, то проверяем не достиг-ли кэш максимального размера */ ++ if( dhcp_cache_len >= MAX_CACHE_LIST_LEN && ++ !search_xid(packet -> chaddr, ip -> primary_address.s_addr, packet -> giaddr.s_addr)/* Если запрос найден в кэше */ ++ ) /* то нет нужды удалять старый узел, либо дропать запрос, т.к. старый узел будет удалён в ходе */ ++ { /* добавления нового и размер кэша не увеличится */ ++ if(delete_oldest_cache_node(xid_list_top) < 0) ++ { ++ log_error("ERROR: Can't delete oldest cache node! Drop %s from %s/%s", str_dhcp_type, ++ print_hw_addr (packet -> htype, packet -> hlen, packet -> chaddr), ++ ip -> name); ++ return -1; ++ } ++ else ++ log_info("WARN: Cache overflow. The oldest cache node has been dropped. Cache length now is: %u", ++ dhcp_cache_len); ++ } ++ /* Добавляем новый узел в кэш */ ++ del_duplicate_xid(packet -> xid, packet -> chaddr, ip -> primary_address.s_addr, packet -> giaddr.s_addr); ++ xid_ptr = add_xid(packet, dhcp_type, ip -> primary_address.s_addr, hw_from); ++ uint8_t mac_addr[STR_MAC_ADDR_LEN]; ++ bzero(mac_addr, sizeof(mac_addr)); ++ sprintf(mac_addr, ++ (radius_use_mac_delim)? ++ "%02x:%02x:%02x:%02x:%02x:%02x" : ++ "%02x%02x%02x%02x%02x%02x", ++ packet -> chaddr[0], packet -> chaddr[1], packet -> chaddr[2], ++ packet -> chaddr[3], packet -> chaddr[4], packet -> chaddr[5]); ++ *length = assemble_auth_message(auth_pack, packet, *length, ++ mac_addr, rad_users_passwd, ++ packet -> giaddr.s_addr ? ++ packet -> giaddr.s_addr : ++ ip -> primary_address.s_addr); ++ if(!*length) ++ { ++ log_error("PATCH: Can't create RADIUS-ACCESS-REQUEST"); ++ return -1; ++ } ++ ++ if(!xid_ptr) /* Если не добавлен новый узел в список, значит ответ */ ++ { /* сервера уже есть в кэше */ ++ xid_ptr = search_xid(packet->chaddr, ip -> primary_address.s_addr, packet -> giaddr.s_addr); ++ if(!xid_ptr) ++ { ++ log_error("WARN: search_xid() in dispatch_dhcp_packet() return zero."); ++ return -1; ++ } ++ xid_ptr -> req_header.id = auth_pack -> header.id; ++ xid_ptr -> client_msg_type = dhcp_type; ++ memcpy(xid_ptr -> req_header.auth, &auth_pack -> header.auth, MD5_LEN); ++ } ++ else ++ memcpy(&xid_ptr -> req_header, &auth_pack -> header, sizeof(RAD_HEADER)); ++ ++ if(new_req_info.client_addr.s_addr || new_req_info.req_addr) ++ memcpy(&xid_ptr -> request_info, &new_req_info, sizeof(new_req_info)); ++ } ++ else ++ { ++ if(dhcp_type != DHCPOFFER && dhcp_type != DHCPACK && dhcp_type != DHCPNAK) /* Пакеты отправляемые агентам пересылки */ ++ log_info ("Ignore BOOTREQUEST: type - %s from client %s (%s/%s)", ++ (strlen(str_dhcp_type))? str_dhcp_type: "'unknown'", ++ (strlen(str_dhcp_type))? inet_ntoa(packet -> ciaddr): "'unknown'", ++ (strlen(str_dhcp_type))? ++ print_hw_addr (packet -> htype, packet -> hlen, packet -> chaddr):"'unknown'", ++ ip -> name); ++ return 0; ++ } ++ return 1; ++} ++ ++/* ++ Функция добавляющая интерфейс в список интерфейсов игнорируемых для DHCP запросов. ++*/ ++int32_t add_dhcp_ignored_interface(char * if_name) ++{ ++ if(!if_name) ++ return 0; ++ if(!start_dhcp_ignor_list) ++ { ++ start_dhcp_ignor_list = malloc(sizeof(struct ignored_dhcp_interface)); ++ start_dhcp_ignor_list -> if_name = malloc(strlen(if_name) + 1); ++ strcpy(start_dhcp_ignor_list -> if_name, if_name); ++ start_dhcp_ignor_list -> next = 0; ++ end_dhcp_ignor_list = start_dhcp_ignor_list; ++ } ++ else ++ { ++ end_dhcp_ignor_list -> next = malloc(sizeof(struct ignored_dhcp_interface)); ++ end_dhcp_ignor_list = end_dhcp_ignor_list -> next; ++ end_dhcp_ignor_list -> if_name = malloc(strlen(if_name) + 1); ++ strcpy(end_dhcp_ignor_list -> if_name, if_name); ++ end_dhcp_ignor_list -> next = 0; ++ } ++ return 1; ++} ++ ++int32_t found_ignored_interfase(char * if_name) ++{ ++ if(!if_name) ++ return 0; ++ struct ignored_dhcp_interface * ign_ptr = start_dhcp_ignor_list; ++ while(ign_ptr) ++ { ++ if(!strcmp(ign_ptr -> if_name, if_name)) ++ return 1; ++ ign_ptr = ign_ptr -> next; ++ } ++ return 0; ++} ++ ++/* END PATCH CODE */ + +=== modified file 'common/discover.c' +--- common/discover.c 2010-08-05 10:07:34 +0000 ++++ common/discover.c 2010-08-05 10:07:56 +0000 +@@ -40,6 +40,12 @@ + #include "dhcpd.h" + #include <sys/ioctl.h> + ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++#include "dhcp2radius.h" ++#endif ++/* END PATCH CODE */ ++ + struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; + int interfaces_invalidated; + int quiet_interface_discovery; +@@ -604,7 +610,13 @@ + sizeof tmp -> ifp -> ifr_addr); + + /* We must have a subnet declaration for each interface. */ ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ if (!tmp -> shared_network && (state == DISCOVER_SERVER) && !use_dhcp2radius) { ++#else + if (!tmp -> shared_network && (state == DISCOVER_SERVER)) { ++#endif ++/* END PATCH CODE */ + log_error ("%s", ""); + log_error ("No subnet declaration for %s (%s).", + tmp -> name, inet_ntoa (foo.sin_addr)); +@@ -764,6 +776,8 @@ + struct hardware hfrom; + struct iaddr ifrom; + int result; ++ ++ + union { + unsigned char packbuf [4095]; /* Packet input buffer. + Must be as large as largest +@@ -782,16 +796,51 @@ + log_error ("receive_packet failed on %s: %m", ip -> name); + return ISC_R_UNEXPECTED; + } ++ + if (result == 0) + return ISC_R_UNEXPECTED; + ++ + /* If we didn't at least get the fixed portion of the BOOTP + packet, drop the packet. We're allowing packets with no + sname or filename, because we're aware of at least one + client that sends such packets, but this definitely falls + into the category of being forgiving. */ ++/* START PATCH CODE */ ++#ifndef PATCHED_SERVER + if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN) + return ISC_R_UNEXPECTED; ++#endif ++ ++#ifdef PATCHED_SERVER ++ if(use_dhcp2radius) /* Если в конфигурационном файле: use-dhcp2radius true */ ++ { ++ /* Определяем какого типа полученный пакет: DHCP/RADIUS/??? */ ++ if(from.sin_port == radius_port) ++ { /* Recieved RADIUS packet */ ++ return got_radius_packet((uint8_t *)&u.packet, result); ++ } ++ else if( (from.sin_port == remote_port) || (from.sin_port == radius_dhcp_relay_port) ) ++ { ++ return got_dhcp_packet((uint8_t *)&u.packet, result, &from, ip, &hfrom); ++ } else ++ { /* Если порт отправителя пакета не равен порту RADIUS сервера, не равен ++ порту DHCP клиентов (remote_port) и не равен ++ порту агента пересылки - не обрабатываем этот пакет */ ++ char addr_str[16]; ++ bzero(addr_str, sizeof(addr_str)); ++ if(!inet_ntop(AF_INET, &from.sin_addr, addr_str, sizeof(addr_str))) ++ { ++ log_info("Can't do inet_ntop()."); ++ } ++ ++ log_info("Received packet from: %s hw: %s. Port is unexpected: %d - I'm don't know what is this!", ++ addr_str, print_hw_addr (hfrom.hbuf [0], hfrom.hlen - 1, &hfrom.hbuf [1]), ntohs(from.sin_port)); ++ return ISC_R_UNEXPECTED; ++ } ++ } ++#endif ++/* END PATCH CODE */ + + if (bootp_packet_handler) { + ifrom.len = 4; + +=== modified file 'common/packet.c' +--- common/packet.c 2010-08-05 10:07:34 +0000 ++++ common/packet.c 2010-08-05 10:07:56 +0000 +@@ -325,7 +325,13 @@ + 8, IPPROTO_UDP + ulen)))); + + udp_packets_seen++; ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ if (usum && usum != sum && interface -> primary_address.s_addr != htonl(INADDR_LOOPBACK)) { ++#else + if (usum && usum != sum) { ++#endif ++/* END PATCH CODE */ + udp_packets_bad_checksum++; + if (udp_packets_seen > 4 && + (udp_packets_seen / udp_packets_bad_checksum) < 2) { + +=== added file 'includes/dhcp2radius.h' +--- includes/dhcp2radius.h 1970-01-01 00:00:00 +0000 ++++ includes/dhcp2radius.h 2010-08-05 10:07:56 +0000 +@@ -0,0 +1,342 @@ ++ ++#ifndef RAD2DHCP_H ++ ++#define RAD2DHCP_H ++#pragma pack(1) ++/* ++#define FULL_DEBUG_OUT ++#define DEBUG_TR_RAD_TO_DHCP ++*/ ++ ++#define PATCH_VERSION "RADIUS to DHCP patch (dhcp2radius) v 0.1.1 2009.06.24" ++#define HOME_PAGE "http://www.netpatch.ru/dhcp2radius.html" ++ ++#define RADIUS_PORT 1812 ++#define RELAY_PORT 67 ++#define DHCP_CLIENT_PORT 68 ++#define RADIUS_MIN_PACK_LEN 20 /* Минимальная длина RADIUS сообщения */ ++#define IPV4_ALEN 4 ++ ++/* ++ Смещения в RADIUS пакете ++*/ ++#define ATTR_OFFSET 0 ++#define LEN_OFFSET 1 ++#define VALUE_OFFSET 2 ++ ++ ++#define OPTIONAL_OFFSET -1 ++#define INVALID_VSA -2 ++ ++#define MAX_STD_DHCP_OPT 0xFF ++ ++/* Коды пакетов протокола Radius */ ++#define RAD_ACCESS_REQ 1 /* Запрос доступа (отправка MAC адреса на сервер) */ ++#define RAD_ACCESS_ACK 2 /* Доступ подтверждён (успешное получение конфигурационной информации от сервера) */ ++#define RAD_ACCESS_NAK 3 /* Доступ запрещён (не удалось получить конфигурацию от сервера) */ ++ ++/* Значения атрибутов протокола Radius ++используемые в пакете Access-Request */ ++#define USER_NAME_ATTR 1 ++#define USER_PASS_ATTR 2 ++#define NAS_PORT 5 ++ ++/* Значения атрибутов протокола Radius ++используемые в пакете Access-Accept */ ++#define RAD_IFINDEX 0x0F01 ++#define RAD_DYN_CLIENT 0x0F02 ++ ++/* Определяем работу протокола DHCP через Vendor-Specific атрибуты */ ++#define RAD_VSA 0x1A ++/* Vendor ID == DHCP. Используется файл dictionary.dhcp из ++ стандартной поставки FreeRADIUS. См. подробности описания ++ атрибутов в этом файле */ ++#define VSA_DHCP 0x36 ++ ++#define SIGSTAT 64 /* Сигнал при получении которого dhcrelay выдаёт статистику работы в лог */ ++#define SIGNAL_SET_PRI_SERVER 63 /* Сигнал, получив который dhcrelay устанавливает server_index */ ++ /* в значение по умолчанию, т.е. на основной сервер */ ++#define SIGNAL_CHANGE_SERV 62 /* При получении этого сигнала dhcrelay устанавливает server_index */ ++ /* на следующий элемент в массиве серверов если таковой имеется */ ++ ++#ifdef OLD ++/* Опция используемая в dhcp-пакете для ++указания статических маршрутов в сети */ ++#define DHO_STATIC_ROUTE 249 ++#endif ++ ++/* Используется в функциях аутентификации RADIUS */ ++#define MD5_LEN 16 ++#define MAC_ADDR_LEN 6 ++#define ENCR_PWD_LEN 16 ++#define RAD_SECRET_SIZE 17 ++#define RAD_PASSWD_SIZE 17 ++ ++#define DEFAULT_XID_TTL 86400 /* Одни сутки */ ++#define MAX_CACHE_LIST_LEN 3000 /* Максимальная длина списка кэша запросов */ ++#define QUERY_TIME_RING_LEN 350 /* Длина массива служащего для подсчёта частоты запросов */ ++#define RING_MAX QUERY_TIME_RING_LEN ++#define TIME_DELTA 10.0 /* Время (сек.) за которое производится подсчёт */ ++ /* среднего числа DHCP запросов в секунду */ ++#define TIME_DELTA_HOST 5.0 /* То же что и TIME_DELTA, только для отдельно взятого хоста */ ++#define MAX_DHCP_QPS 10.0 /* Суммарное максимально допустимое число DHCP запросов в секунду */ ++#define MAX_QPS_FROM_HOST 4.0 /* Максимально допустимое число DHCP запросов в секунду от одного хоста */ ++#define MIN_COUNTER_SIZE 100 /* Минимальная длина динамического массива счётчика запросов */ ++ ++#define DEFAULT_SERV_ARR_SIZE 10 /* Размер массива серверов по умолчанию */ ++#define BASIC_SERVER_INDEX 0 /* Индекс массива серверов обозначающий основной сервер, */ ++ /* запросы к которому отправляются в штатном режиме работы релея */ ++#define PRIMARY_SERVER_INDEX BASIC_SERVER_INDEX ++#define INC 1 ++#define DEC 0 ++#define MAX_UNFINISHED_REQ 30 ++#define ZEROING_UNFIN_REQ 50 ++ ++#define DEF_ARP_CACHE_PERM_LEN 1024 ++#ifdef __linux__ ++#define PROC_ARP "/proc/net/arp" ++#endif ++#define STR_MAC_ADDR_LEN 18 ++ ++/* Используется что бы определить начало поля dhcp-options */ ++extern const uint8_t magic_cookie[]; ++extern int32_t use_dhcp2radius; ++extern struct sockaddr_in * radius_servers; /* Указатель на список массив хранящий адреса RADIUS серверов */ ++extern int radius_servers_count; /* Число доступных RADIUS серверов */ ++extern unsigned char rad_secret[]; /* Пароль для доступа на Radius-сервер */ ++extern unsigned char rad_users_passwd[]; /* Пароль пользователя, в качестве имени применяется MAC адрес клиента */ ++extern uint32_t time_to_restore_primary_server_index; /* Время (в секундах) через которое производится попытка переключиться ++ на первичный RADIUS сервера, если на данный момент осуществлено ++ переключение на резервный */ ++extern time_t xid_ttl; /* Время в течение которого хранятся узлы кэша */ ++extern uint16_t radius_port; /* Порт на который релей отправляет RADIUS запросы. По умолчанию равен 1812 */ ++extern float max_qps; /* Максимально допустимая частота отправки DHCP запросов ++ всеми клиентами суммарно */ ++extern float max_qps_from_host; /* Максимально допустимая частота ++ отправки запросов DHCP одним клиентом */ ++extern uint32_t cache_list_len; /* Максимальная длина кэша DHCP. Переменная нужна для возможности ++ задания этого параметра из командной строки. ++ В данной версии - не реализовано */ ++extern int updating_arp_cache_perm; /* Указывает - обновлять ли ARP кэш при получении RADIUS сообщений с cached == 0 */ ++extern struct counter_node * counter; /* Указатель на нулевой элемент массива-счётчика частоты запросов */ ++extern time_t normal_clients_counter[QUERY_TIME_RING_LEN]; /* Массив используемый как кольцевой буфер для ++ подсчёта частоты запросов в секунду */ ++extern struct arp_entry * arp_cache_perm; /* Указатель на начало динамического массива содержащего ARP записи */ ++extern int arp_cache_perm_len; /* Длина массива хранящего кэш статических (флаг PERM) ARP записей */ ++extern int server_index; /* Индекс массива серверов, указывающий на RADIUS сервер ++ к которому в текущий момент времени пересылаются запросы */ ++extern time_t primary_server_down_time; /* Фиксируется время (секунд), в которое "упал" PRIMARY RADIUS сервер */ ++extern int unfinished_requests; /* Переменная хранящая число необработанных RADIUS-сервером запросов. ++ Т.е. число DHCP запросов для которых не получены ответы RADIUS сервера. ++ Переменная используется для обнаружения "падения" RADIUS сервера и ++ переключения на резервный сервер, если таковой указан */ ++extern unsigned long forward_client_packets_errors; /* Хранит число неудачных попыток передать запрос на RADIUS сервер */ ++extern uint16_t radius_dhcp_relay_port; /* Порт с которого отправляет запрос DHCP relay агент */ ++extern int radius_allow_cache; /* Переменная указывающая на возможность кэширования ответов RADIUS */ ++extern int radius_allow_dynamic_cache; /* Переменная указывающая на возможность кэширования ответов для клиентов ++ не имеющих привязки MAC -> IP */ ++extern int radius_use_mac_delim; ++extern uint8_t * opts_send_to_srv; /* Указатель на массив опций отправляемых на RADIUS сервер при запросе ++ клиентом адреса. Инициализируется в случае использования ++ опции radius-send-opts-to-srv */ ++ ++/* Структура описывающая заголовок пакета Radius */ ++typedef struct rad_header ++{ ++ uint8_t code; ++ uint8_t id; ++ uint16_t pack_len; ++ uint8_t auth[16]; ++}RAD_HEADER; ++ ++/* Полный формат пакета Radius */ ++typedef struct rad_message ++{ ++ RAD_HEADER header; ++ uint8_t message[4096 - sizeof(RAD_HEADER)]; ++}RAD_MESSAGE; ++ ++/* Структура входящая в состав xid_list_node и описывающая ++DHCP-запрос полученный от клиента */ ++typedef struct dhcp_req_info ++{ ++ struct in_addr client_addr; ++ uint32_t server_id; ++ uint32_t req_addr; ++}DHCP_REQ_INFO; ++ ++/* Структура узла служащего для обработки DHCP ответов - определения ++типа DHCP ответа отправляемого клиенту. Одновременно служит кэшем DHCP ответов. */ ++typedef struct xid_list_node ++{ ++ uint8_t allow_cache; /* Булева переменная, принимает значение TRUE если разрешено кэширование DHCP ответов */ ++ struct dhcp_packet * out_packet; /* Указатель на структуру для кэширования BOOTREPLY */ ++ uint32_t out_pack_len; /* Хранит длину кэшированного пакета что бы не высчитывать её перед повторной ++ отправкой при использовании кэша */ ++ uint32_t if_index; /* Индекс интерфейса на котором был получен запрос от клиента */ ++ uint32_t relay_if_index; /* Индекс интерфейса на котором DHCPRELAY получил запрос от клиента */ ++ uint8_t client_msg_type; ++ uint8_t server_msg_type; /* Возможные состояния транзакции: XID_DISCOVER|XID_OFFER|XID_REUQEST|XID_ACK */ ++ uint32_t xid; /* Копируется из dhcp-пакета запроса конфигурации */ ++ uint8_t hw_from[MAC_ADDR_LEN]; /* MAC адрес хоста с которого получен DHCP запрос. Равен либо MAC адресу клиента ++ (поле chaddr), либо MAC адресу агента пересылки ++ (промежуточного маршрутизатора) */ ++ uint8_t chaddr[MAC_ADDR_LEN]; /* MAC адрес клиента запросившего конфигурацию */ ++ uint16_t dhcp_flags; /* Флаги из dhcp-пакета запроса конфигурации. Необходимо для учёта BROADCAST */ ++ DHCP_REQ_INFO request_info; /* Служит для определения валидности запроса клиента, т.е. соотвествия ++ запрашиваемого адреса предлагаемому */ ++ RAD_HEADER req_header; /* Заголовок запроса Radius отправленного на сервер для получения конфигурации */ ++ time_t timestamp; /* Используется для вычисления устаревших узлов кэша */ ++ struct xid_list_node *prev; /* Указатель на предыдущий узел списка транзакций */ ++ struct xid_list_node *next; /* Указатель на последующий узел списка транзакций */ ++} XID_NODE; ++ ++typedef struct counter_node ++{ ++ uint8_t mac_addr[MAC_ADDR_LEN]; ++ time_t timestamp; ++} COUNTER_NODE; ++ ++/* Структура для хранения информации о статическом ARP кэше сервера */ ++typedef struct arp_entry ++{ ++ struct in_addr ip; ++ unsigned char mac[MAC_ADDR_LEN]; ++} ARP_ENTRY; ++ ++/* Структура для хранения имени игнорируемого интерфейса. Структуры объединяются в список */ ++ ++typedef struct ignored_dhcp_interface ++{ ++ char *if_name; ++ struct ignored_dhcp_interface * next; ++} IGN_DHCP_ENTRY; ++ ++/* Структура описывающая заголовок RADIUS VSA */ ++typedef struct radius_vsa_dhcp_header ++{ ++ uint32_t vendor_id; ++ uint16_t type; ++ uint8_t length; /* Длина VSA данных включая поле type && length */ ++ uint8_t *data; ++} VSA_DHCP_HEADER, *PVSA_DHCP_HEADER; ++ ++/* Коды полей в DHCP заголовке */ ++typedef enum dhcp_header_codes ++{ ++ dhcp_op = 256, /* 0: Message opcode/type */ ++ dhcp_hwtype, /* 1: Hardware addr type (net/if_types.h) */ ++ dhcp_hwlen, /* 2: Hardware addr length */ ++ dhcp_hops, /* 3: Number of relay agent hops from client */ ++ dhcp_xid, /* 4: Transaction ID */ ++ dhcp_secs, /* 8: Seconds since client started looking */ ++ dhcp_flags, /* 10: Flag bits */ ++ dhcp_ciaddr, /* 12: Client IP address (if already in use) */ ++ dhcp_yiaddr, /* 16: Client IP address */ ++ dhcp_siaddr, /* 18: IP address of next server to talk to */ ++ dhcp_giaddr, /* 20: DHCP relay agent IP address */ ++ dhcp_chaddr, /* 24: Client hardware address */ ++ dhcp_sname, /* 40: Server name */ ++ dhcp_bootfile /* 104: Boot filename */ ++}DHCP_HCODES; ++ ++typedef struct auxiliary_attributes ++{ ++ uint32_t if_index; ++ int allow_cache; ++}AUX_ATTRS; ++ ++/* Функции применяемые для работы с Radius-сервером */ ++/* Обработчик DHCP сообщений от клиентов */ ++isc_result_t got_dhcp_packet(uint8_t * raw_packet, uint32_t length, const struct sockaddr_in * from, ++ struct interface_info * ip, struct hardware * hfrom); ++/* Функция анализирующая полученный от клиента DHCP запрос */ ++isc_result_t got_dhcp_packet(uint8_t * raw_packet, uint32_t length, const struct sockaddr_in * from, ++ struct interface_info * ip, struct hardware * hfrom); ++/* Обработчик RADIUS сообщений от RADIUS сервер от RADIUS сервераа */ ++isc_result_t got_radius_packet(uint8_t * raw_packet, uint32_t length); ++/* Функция анализирующая RADIUS-Response */ ++int dispatch_radius_packet(struct dhcp_packet * packet, unsigned length, ++ struct in_addr * user_if, uint8_t * hw_from); ++/* Функция производящая разбор RADIUS VSA */ ++int dispatch_radius_vsa(const int length, const uint8_t * attr_cont, struct dhcp_packet * out_packet, ++ unsigned int * opt_len, AUX_ATTRS *aux_attrs); ++/* Возвращает смещение с байтах поля указанного option относительно начала заголовка DHCP пакета. */ ++int dhcp_option_offset(const uint16_t option); ++/* Функция создающая RADIUS VSA и помещающая его по адресу out_bufffer */ ++uint8_t make_radius_vsa(uint8_t * out_buffer, uint8_t ob_len, const uint32_t vendor_id, ++ const uint16_t vsa, const uint8_t vsa_len, uint8_t * vsa_value); ++/* Функция добавляющая RADIUS атрибут */ ++inline uint8_t add_radius_attribute(uint8_t * message_buffer, uint8_t const * mb_end, ++ uint8_t rad_attr, void * value, uint8_t value_len); ++/* Функция устанавливающая тип DHCP сообщения в исходящем пакете клиенту */ ++uint8_t set_dhcp_type_of_out_packet(struct dhcp_packet * out_packet, const int out_pack_len, ++ XID_NODE * cur_xid, const server_id); ++/* Функция возвращающая классовую маску */ ++inline uint32_t make_default_netmask(const uint32_t addr); ++/* Добавляем интефейс в список игнорируемых */ ++int32_t add_dhcp_ignored_interface(char * if_name); ++/* Производим поиск по списку игнорируемых интерфейсов, возвращаем != 0 если найдено */ ++int32_t found_ignored_interfase(char * if_name); ++/* Проверка на переполнение не обработанных DHCP запросов. Такое переполнение может случиться ++ в случае падения RADIUS сервера */ ++inline int is_unfinished_requests_overflow(int * count, int inc, int max_count, int zeroing); ++/* Печатам текущий список узлов dhcp-транзакций */ ++void print_stat(int sign); ++/* Шифруем пароль для передачи в radius-пакете */ ++int encrypt_passwd(char * passwd, uint8_t * encr_passwd, ++ char *shared_key, uint8_t * auth); ++/* Сборка пакета radius-Access-Request для отправки на radius-сервер */ ++int assemble_auth_message( RAD_MESSAGE *access_req, const struct dhcp_packet * packet, unsigned length, ++ char *user_name, char *passwd, uint32_t subnet); ++/* Подсчёт контрольной суммы ответа radius-Access-Accept */ ++static int calc_reply_auth(RAD_MESSAGE *packet, uint8_t *original_auth, const char *secret); ++/* Добавляем новый узел в список узлов dhcp-транзакций */ ++XID_NODE * add_xid(struct dhcp_packet *packet, uint8_t dhcp_type, uint32_t if_index, struct hardware * hw_from); ++/* Функция получающая основную запрашиваемую клиентом информацию из DHCP сообщения */ ++int get_dhcp_request_info(struct dhcp_packet *request, DHCP_REQ_INFO * request_info); ++/* Функция создающая DHCPNAK сообщение в буфере на который указывает out_packet */ ++void make_dhcp_nak(struct dhcp_packet * out_packet, XID_NODE * cur_xid, const uint32_t server_id); ++/* Определение dhcp-типа пакета (DHCPDISCOVER/DHCPREQUEST/etc...) */ ++uint16_t get_dhcp_type(struct dhcp_packet *request, char * str_dhcp_type); ++/* Присваивает option_value значение запрошенной опции находящейся в DHCP пакете */ ++uint8_t get_dhcp_option(const struct dhcp_packet *request, uint16_t packet_len, uint8_t req_option, ++ void * option_value, const size_t option_size); ++/* Удаление узла dhcp-транзакции */ ++inline XID_NODE * delete_xid(XID_NODE *xid_n); ++/* Удаление старых узлов dhcp-транзакций, у которых поле истекло время ожидания */ ++int delete_old_xid(XID_NODE * start_xid_list); ++/* Удаление дублирующихся по MAC-адресу узлов dhcp-транзакций */ ++int del_duplicate_xid(uint32_t xid, uint8_t * hw_addr, uint32_t if_index, uint32_t relay_if_index); ++/* Поиск узла dhcp-транзакции либо только по MAC-адресу, либо по MAC-адресу и значению xid */ ++XID_NODE * search_xid(uint8_t * hw_addr, uint32_t if_index, uint32_t relay_if_index); ++/* Преобразование пакета radius в dhcp-пакет готовый для отправки клиенту запросившему конфигурационную информацию */ ++int translate_rad_to_dhcp(RAD_MESSAGE *rad_pack, struct dhcp_packet * out_packet, uint16_t received_length, ++ uint32_t * user_interface ); /* Параметр необходим для корректной обработки */ ++/* Устанавливает указанный тип DHCP сообщения, возвращает предыдущее значение типа */ ++uint16_t set_dhcp_type(struct dhcp_packet *request, uint16_t new_type); ++/* Обновляет поля DHCP пакета в кэше перед отправкой пакета клиенту */ ++uint16_t update_xid_node(XID_NODE * xid, struct dhcp_packet * packet, unsigned pack_len); ++/* Функция удаляющая самый старый запрос в DHCP кэше */ ++int delete_oldest_cache_node(XID_NODE * cache_node); ++/* Подсчитывает среднее число DHCP запросов в секунду за время TIME_DELTA секунд */ ++float count_queries_per_sec(void); ++/* Устанавливаем обработчик для смены индекса массива RADIUS серверов на значение по умолчанию */ ++inline void set_default_server(int sign); ++/* Устанавливаем обработчик для смены индекса массива */ ++void change_server(int sign); ++/* Берём ARP-кэш статически привязанных MAC адресов. ДАННЫЙ ВАРИАНТ ПОДХОДИТ ТОЛЬКО ДЛЯ LINUX!!! */ ++int get_arp_cache_perm(void); ++#ifdef __linux__ ++/* Функция обновляющая статический ARP кэш на роутере, если для IP сменился MAC */ ++int update_arp_cache_perm(uint32_t ip, unsigned char * mac); ++#endif ++/* Функция удаляющая кэшированные xid узлы при изменении соответствия IP - MACклиента */ ++int delete_xid_if_mac_not_equal(uint32_t ip, uint8_t * mac); ++/* Печтает help-info для ключей патча*/ ++static void patch_help (void); ++/* Функция выполняет подсчёт общего усреднённого числа DHCP запросов */ ++float qps_hosts(uint8_t * mac_addr, float * qps_summary); ++ ++#endif ++ + +=== modified file 'includes/dhcpd.h' +--- includes/dhcpd.h 2010-08-05 10:07:34 +0000 ++++ includes/dhcpd.h 2010-08-05 10:07:56 +0000 +@@ -32,6 +32,10 @@ + * ``http://www.nominum.com''. + */ + ++ ++/* START PATCH CODE */ ++#define PATCHED_SERVER ++/* END PATCH CODE */ + #ifndef __CYGWIN32__ + #include <sys/types.h> + #include <netinet/in.h> +@@ -75,7 +79,6 @@ + #include "tree.h" + #include "inet.h" + #include "dhctoken.h" +- + #include <isc-dhcp/result.h> + #include <omapip/omapip_p.h> + +@@ -428,6 +431,45 @@ + #define SV_DO_FORWARD_UPDATES 45 + #define SV_PING_TIMEOUT 46 + ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ ++/* { "use-dhcp2radius", "f", &server_universe, 100 },*/ ++#define SV_RAD2DHCP 100 ++/* { "radius-servers", "IA", &server_universe, 101 },*/ ++#define SV_RADIUS_SERVERS 101 ++/* { "radius-secret", "t", &server_universe, 102 },*/ ++#define SV_RADIUS_SECRET 102 ++/* { "radius-password", "t", &server_universe, 103 },*/ ++#define SV_RADIUS_PASSWORD 103 ++/* { "radius-server-restore", "L", &server_universe, 104 },*/ ++#define SV_RADIUS_SRV_RESTORE 104 ++/* { "radius-port", "S", &server_universe, 105 },*/ ++#define SV_RADIUS_SRV_PORT 105 ++/* { "radius-client-freq", "L", &server_universe, 106 },*/ ++#define SV_RADIUS_CLIENT_FREQ 106 ++/* { "radius-cache-ttl", "L", &server_universe, 107 },*/ ++#define SV_RADIUS_CACHE_TTL 107 ++/* { "radius-freq-summ", "L", &server_universe, 108 },*/ ++#define SV_RADIUS_FREQ_SUM 108 ++/* { "radius-update-arp", "f", &server_universe, 109 },*/ ++#define SV_RADIUS_UPDATE_ARP 109 ++/* { "radius-cache-maxlen", "L", &server_universe, 110 },*/ ++#define SV_RADIUS_CACHE_MAXLEN 110 ++/* { "radius-dhcp-relay-port", "S", &server_universe, 111 },*/ ++#define SV_RADIUS_DHCP_RELAY_PORT 111 ++/* { "radius-allow-cache", "f", &server_universe, 112 },*/ ++#define SV_RADIUS_ALLOW_CACHE 112 ++/* { "radius-allow-dynamic-cache", "f",&server_universe, 113 },*/ ++#define SV_RADIUS_ALLOW_DYN_CACHE 113 ++/* { "radius-send-opts-to-srv", "X", &server_universe, 114 },*/ ++#define SV_RADIUS_SEND_OPTS_TO_SRV 114 ++/* { "radius-use-mac-delimiter", "f", &server_universe, 115 },*/ ++#define SV_RADIUS_USE_MAC_DELIM 115 ++ ++#endif /* #ifdef PATCHED_SERVER */ ++/* END PATCH CODE */ ++ + #if !defined (DEFAULT_PING_TIMEOUT) + # define DEFAULT_PING_TIMEOUT 1 + #endif +@@ -2650,3 +2692,4 @@ + #endif /* FAILOVER_PROTOCOL */ + + const char *binding_state_print (enum failover_state); ++ + +=== modified file 'server/dhcpd.c' +--- server/dhcpd.c 2010-08-05 10:07:34 +0000 ++++ server/dhcpd.c 2010-08-05 10:11:18 +0000 +@@ -57,6 +57,16 @@ + # undef group + #endif /* PARANOIA */ + ++/* START PATCH CODE */ ++ ++#ifdef PATCHED_SERVER ++ ++#include "dhcp2radius.h" ++const char patch_info[] = "Patched by Chebotarev Roman. Home page: " HOME_PAGE "\n" ++ "Patch version info: " PATCH_VERSION; ++#endif /* PATCHED_SERVER */ ++/* END PATCH CODE */ ++ + static void usage PROTO ((void)); + + struct iaddr server_identifier; +@@ -367,6 +377,14 @@ + cftest = 1; + lftest = 1; + log_perror = -1; ++#ifdef PATCHED_SERVER ++ } else if( !strcmp (argv [i], "-I")) ++ { ++ if(++i == argc) ++ usage(); ++ if(!add_dhcp_ignored_interface(argv[i])) ++ log_fatal("PATCH: Can't add interface '%s' to ignored DHCP interfaces list.", argv[i]); ++#endif + } else if (!strcmp (argv [i], "-q")) { + quiet = 1; + quiet_interface_discovery = 1; +@@ -421,6 +439,11 @@ + log_info ("%s", copyright); + log_info ("%s", arr); + log_info ("%s", url); ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ log_info (patch_info); ++#endif ++/* END PATCH CODE */ + } else { + quiet = 0; + log_perror = 0; +@@ -705,6 +728,16 @@ + omapi_set_int_value ((omapi_object_t *)dhcp_control_object, + (omapi_object_t *)0, "state", server_running); + ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ if(signal(SIGSTAT, print_stat) == SIG_ERR) /* Для вывода статистики */ ++ log_error("ERROR: signal() for handling SIGSTAT failed. Runtime statistic not available."); ++ if(signal(SIGNAL_SET_PRI_SERVER, set_default_server) == SIG_ERR) /* Для принудительного включения первичного */ ++ log_error("ERROR: signal() for handling SIGNAL_SET_PRI_SERVER failed."); /* RADIUS-сервера */ ++ if(signal(SIGNAL_CHANGE_SERV, change_server) == SIG_ERR) /* Для принудительной смены RADIUS сервера */ ++ log_error("ERROR: signal() for handling SIGNAL_CHANGE_SERV failed."); /* на следующий по списку */ ++#endif ++/* END PATCH CODE */ + /* Receive packets and dispatch them... */ + dispatch (); + +@@ -712,6 +745,293 @@ + return 0; + } + ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ ++void patch_configure(struct option_state *options) ++{ ++ struct parse *parse; ++ char *s; ++ struct data_string db; ++ struct option_cache *oc; ++ bzero(&db, sizeof(db)); ++ oc = lookup_option (config_universe, options, SV_RAD2DHCP); ++ if (oc) ++ { ++ int ignorep; ++ if((use_dhcp2radius = evaluate_boolean_option_cache (&ignorep, (struct packet *)0, ++ (struct lease *)0, 0, ++ options, ++ (struct option_state *)0, ++ &global_scope, oc, MDL))) ++ { ++ data_string_forget (&db, MDL); ++ log_info("PATCH: dhcp2radius ability is enabled."); ++ int i; ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_SRV_PORT); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(radius_port)) ++ log_fatal("PATCH ERROR: invalid 'RADIUS port' length: %d must be: %d", ++ db.len, sizeof(radius_port)); ++ radius_port = htons(getUShort(db.data)); ++ data_string_forget (&db, MDL); ++ } ++ else ++ radius_port = htons(RADIUS_PORT); ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_SERVERS); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len % IPV4_ALEN) ++ log_fatal("PATCH ERROR: Invalid RADIUS servers address length."); ++ radius_servers_count = (int)(db.len / IPV4_ALEN); ++ radius_servers = calloc(radius_servers_count, sizeof(struct sockaddr_in)); ++ if(!radius_servers) ++ log_fatal("FATAL: Can't allocate memory for RADIUS servers list."); ++ for(i = 0; i < radius_servers_count; ++i) ++ { ++ memcpy(&radius_servers[i].sin_addr.s_addr, db.data + IPV4_ALEN * i, IPV4_ALEN); ++ radius_servers[i].sin_port = radius_port; ++ radius_servers[i].sin_family = AF_INET; ++#ifdef HAVE_SA_LEN ++ radius_servers[i].sin_len = sizeof(radius_servers[i]); ++#endif ++ } ++ data_string_forget (&db, MDL); ++ } ++ else ++ log_fatal("PATCH ERROR: RADIUS servers not found in configuration file!"); ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_SECRET); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len > (RAD_SECRET_SIZE - 1) ) ++ log_fatal("PATCH ERROR: RADIUS secret too long. Maximum length - %d.", RAD_SECRET_SIZE - 1); ++ bzero(rad_secret, RAD_SECRET_SIZE); ++ strncpy(rad_secret, db.data, RAD_SECRET_SIZE - 1); ++ data_string_forget (&db, MDL); ++ } ++ else ++ log_fatal("PATCH ERROR: RADIUS secret not found in configuration file!"); ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_PASSWORD); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len > (RAD_PASSWD_SIZE - 1) ) ++ log_fatal("PATCH ERROR: RADIUS password too long. Maximum length - %d.", RAD_PASSWD_SIZE - 1); ++ bzero(rad_users_passwd, RAD_PASSWD_SIZE); ++ strncpy(rad_users_passwd, db.data, RAD_PASSWD_SIZE - 1); ++ data_string_forget (&db, MDL); ++ } ++ else ++ log_fatal("PATCH ERROR: RADIUS password not found in configuration file!"); ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_SRV_RESTORE); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(time_to_restore_primary_server_index)) ++ log_fatal("PATCH ERROR: invalid 'time to restore primary server' length: %d must be: %d", ++ db.len, sizeof(time_to_restore_primary_server_index)); ++ time_to_restore_primary_server_index = getULong(db.data); ++ data_string_forget (&db, MDL); ++ } ++ else ++ time_to_restore_primary_server_index = 0; ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_CACHE_TTL); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(xid_ttl)) ++ log_fatal("PATCH ERROR: invalid 'cache ttl' length: %d must be: %d", ++ db.len, sizeof(xid_ttl)); ++ xid_ttl = getULong(db.data); ++ data_string_forget (&db, MDL); ++ } ++ else ++ xid_ttl = DEFAULT_XID_TTL; ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_DHCP_RELAY_PORT); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(xid_ttl)) ++ log_fatal("PATCH ERROR: invalid 'cache ttl' length: %d must be: %d", ++ db.len, sizeof(radius_dhcp_relay_port)); ++ radius_dhcp_relay_port = htons(getUShort(db.data)); ++ data_string_forget (&db, MDL); ++ } ++ else ++ radius_dhcp_relay_port = htons(RELAY_PORT); ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_CLIENT_FREQ); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(unsigned long)) ++ log_fatal("PATCH ERROR: invalid 'RADIUS maximum frequency queries from one host' length:" ++ " %d must be: %d", db.len, sizeof(unsigned long)); ++ max_qps_from_host = (float) getULong(db.data); ++ data_string_forget (&db, MDL); ++ } ++ else ++ max_qps_from_host = MAX_QPS_FROM_HOST; ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_FREQ_SUM); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(unsigned long)) ++ log_fatal("PATCH ERROR: invalid 'RADIUS summary frequency of DHCP queries' length: %d must be: %d", ++ db.len, sizeof(unsigned long)); ++ max_qps = (float) getULong(db.data); ++ data_string_forget (&db, MDL); ++ } ++ else ++ max_qps = MAX_DHCP_QPS; ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_CACHE_MAXLEN); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ if(db.len != sizeof(cache_list_len)) ++ log_fatal("PATCH ERROR: invalid 'RADIUS cache maximum length' length: %d must be: %d", ++ db.len, sizeof(cache_list_len)); ++ cache_list_len = getULong(db.data); ++ data_string_forget (&db, MDL); ++ } ++ else ++ cache_list_len = MAX_CACHE_LIST_LEN; ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_USE_MAC_DELIM); ++ if(oc) ++ radius_use_mac_delim = evaluate_boolean_option_cache (&ignorep, (struct packet *)0, ++ (struct lease *)0, 0, options, ++ (struct option_state *)0, ++ &global_scope, oc, MDL); ++ ++#ifdef __linux__ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_UPDATE_ARP); ++ if(oc) ++ updating_arp_cache_perm = evaluate_boolean_option_cache (&ignorep, (struct packet *)0, ++ (struct lease *)0, 0, options, ++ (struct option_state *)0, ++ &global_scope, oc, MDL); ++#endif ++ ++ oc = lookup_option (config_universe, options, SV_RADIUS_ALLOW_CACHE); ++ if(oc) ++ { ++ radius_allow_cache = evaluate_boolean_option_cache (&ignorep, (struct packet *)0, ++ (struct lease *)0, 0, options, ++ (struct option_state *)0, ++ &global_scope, oc, MDL); ++ } ++ else /* По умолчанию кэш для клиентов имеющих привязку MAC -> IP разрешён */ ++ radius_allow_cache = 1; ++ ++ ++ if(radius_allow_cache && ++ (oc = lookup_option (config_universe, options, SV_RADIUS_ALLOW_DYN_CACHE)) ) ++ radius_allow_dynamic_cache = evaluate_boolean_option_cache (&ignorep, (struct packet *)0, ++ (struct lease *)0, 0, options, ++ (struct option_state *)0, ++ &global_scope, oc, MDL); ++ else /* По умолчанию кэш для клиентов не имеющих привязки MAC -> IP отключен */ ++ radius_allow_dynamic_cache = 0; ++ ++ ++ oc = lookup_option (&server_universe, options, SV_RADIUS_SEND_OPTS_TO_SRV); ++ if (oc && ++ evaluate_option_cache (&db, (struct packet *)0, ++ (struct lease *)0, (struct client_state *)0, ++ options, (struct option_state *)0, ++ &global_scope, oc, MDL)) ++ { ++ opts_send_to_srv = malloc(db.len + 1); ++ if(!opts_send_to_srv) ++ log_fatal("FATAL: Can't allocate memory for array of options that are sent to the server."); ++ opts_send_to_srv[db.len] = 0; /* отмечаем конец опций */ ++ memcpy(opts_send_to_srv, db.data, db.len); ++ data_string_forget (&db, MDL); ++ } ++ ++ /* Инициализируем глобальные структуры данных необходимые для работы */ ++ bzero(normal_clients_counter, sizeof(normal_clients_counter)); /* Обнуляем кольцевой буфер */ ++ /* Создаём динамический массив для счётчика запросов */ ++ counter = malloc(sizeof(struct counter_node) * MIN_COUNTER_SIZE); ++ ++ if(!counter) ++ log_fatal("FATAL: malloc error for counter"); ++ ++ bzero(counter, MIN_COUNTER_SIZE); ++#ifdef __linux__ ++ if(updating_arp_cache_perm) ++ { ++ if( !(arp_cache_perm = calloc(arp_cache_perm_len, sizeof(struct arp_entry))) ) ++ log_fatal("FATAL: Can't allocate memory for copy ARP cache."); ++ if(! get_arp_cache_perm()) ++ log_fatal("FATAL: Can't get ARP cache. Try launch dhcrelay without updating ARP cache option.\n"); ++ } ++#endif ++ } ++ else ++ log_info("PATCH: DHCP to RADIUS ability is disabled."); ++ } ++ else ++ log_info("PATCH: DHCP to RADIUS ability is disabled."); ++ ++ return; ++} ++ ++#endif /* PATCHED_SERVER */ ++/* END PATCH CODE */ ++ + void postconf_initialization (int quiet) + { + struct option_state *options = (struct option_state *)0; +@@ -907,6 +1227,11 @@ + log_info ("%s", copyright); + log_info ("%s", arr); + log_info ("%s", url); ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ log_info (patch_info); ++#endif ++/* END PATCH CODE */ + log_perror = tmp; + } + } else +@@ -915,6 +1240,12 @@ + } + } + ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ patch_configure(options); ++#endif ++/* END PATCH CODE */ ++ + /* Don't need the options anymore. */ + option_state_dereference (&options, MDL); + +@@ -980,6 +1311,11 @@ + log_info ("%s %s", message, DHCP_VERSION); + log_info ("%s", copyright); + log_info ("%s", arr); ++/* START PATCH CODE */ ++#ifdef PATCHED_SERVER ++ log_info (patch_info); ++#endif ++/* END PATCH CODE */ + + log_fatal ("Usage: dhcpd [-p <UDP port #>] [-d] [-f]%s%s%s%s", + "\n [-cf config-file] [-lf lease-file]", + +=== modified file 'server/stables.c' +--- server/stables.c 2010-08-05 10:07:34 +0000 ++++ server/stables.c 2010-08-05 10:07:56 +0000 +@@ -536,6 +536,24 @@ + { "unknown-97", "X", &server_universe, 97 }, + { "unknown-98", "X", &server_universe, 98 }, + { "unknown-99", "X", &server_universe, 99 }, ++#ifdef PATCHED_SERVER ++ { "use-dhcp2radius", "f", &server_universe, 100 }, ++ { "radius-servers", "IA", &server_universe, 101 }, ++ { "radius-secret", "t", &server_universe, 102 }, ++ { "radius-password", "t", &server_universe, 103 }, ++ { "radius-server-restore", "L", &server_universe, 104 }, ++ { "radius-port", "S", &server_universe, 105 }, ++ { "radius-client-freq", "L", &server_universe, 106 }, ++ { "radius-cache-ttl", "L", &server_universe, 107 }, ++ { "radius-freq-summ", "L", &server_universe, 108 }, ++ { "radius-update-arp", "f", &server_universe, 109 }, ++ { "radius-cache-maxlen", "L", &server_universe, 110 }, ++ { "radius-dhcp-relay-port", "S", &server_universe, 111 }, ++ { "radius-allow-cache", "f", &server_universe, 112 }, ++ { "radius-allow-dynamic-cache", "f",&server_universe, 113 }, ++ { "radius-send-opts-to-srv", "X", &server_universe, 114 }, ++ { "radius-use-mac-delimiter", "f", &server_universe, 115 }, ++#else + { "unknown-100", "X", &server_universe, 100 }, + { "unknown-101", "X", &server_universe, 101 }, + { "unknown-102", "X", &server_universe, 102 }, +@@ -552,6 +570,7 @@ + { "unknown-113", "X", &server_universe, 113 }, + { "unknown-114", "X", &server_universe, 114 }, + { "unknown-115", "X", &server_universe, 115 }, ++#endif + { "unknown-116", "X", &server_universe, 116 }, + { "unknown-117", "X", &server_universe, 117 }, + { "unknown-118", "X", &server_universe, 118 }, + |