summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Volkov <pva@gentoo.org>2010-08-30 11:26:55 +0000
committerPeter Volkov <pva@gentoo.org>2010-08-30 11:26:55 +0000
commitd1c3a641a485a1612fd413ba50332fb0a4849dd4 (patch)
treecf8ff83f80acff06e1b4978a1d895ecb653c016c
parentapp-text/kbib: drop kde3 package. (diff)
downloadpva-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.patch2998
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 },
+