Jump to content
Калькуляторы

DHCP сервер с базой в памяти и обработкой опции 82 самописный dhcp на с++

Представляю вашему вниманию сервер, который крутится у меня на город, примерно 5к устройств сейчас отрабатывает.

Сервер работает месяц и пока не было ни одного краша и глюка, либо я еще не успел заметить.

Предистория: хотел адаптировать перловый сервер для базы mysql, в результате писанины выяснено: майскул медленный для таких запросов когда есть флуд, а точнее он вообще колом стоит, плюс даже при старте перл 1 из 10 случаев теряет конект с базой. В результате я плюнул и решил с нуля написать именно то, что мне нужно.

 

Спецификация сервера:

- вся база хранится в памяти, скидывается на диск по запросу или при шутдауне сервера

- сервер выдает новые записи удаляя старые при базе в 65к заполненой полностью со скоростью 1к в секунду. В связи с этим мультипроцессорность в код не внедрял.

- сервер стартует двумя процессами, один наблюдает за дочерним, второй сам процесс выдачи адресов клиентам

- сервер выдает айпи только через релей агенты (увы у меня только длинк и циска), циска на отрез отказалась опцию влан айди и порт выдавать, поэтому протестировано только на длинках 3200, 3100, 3526, 3620. Опция 82 не обязательна для работы сервера.

- сервер поддерживает пулы, у меня 128 пока пулов, все зависит от памяти. Весь сервер с 65к записями жрет 8метров памяти

- пул определяется из влана (опции 82) или по релей агенту записанного в виде начальный адрес агента, конечный адрес. Шлюз вычисляется на основе маски и является первым адресом сети.

- пул может содержать мак адреса и ip адреса из других сетей, т.е. один пул может выдавать белые айпи (шлюз вычисляется аналогично). Один мак может находится во всех пулах, чтобы пользоватлель с буком мог регистрироваться в любой точке города. В одном же пуле айпи и мак являются уникальными значениями

- к серверу есть утилита управления, которая: добавляет статику, удаляет, запрашивает статус сервера и для дальнейшей выгрузки в майскул есть функция показа всех записей, а также показа последних добавленных записей.

 

На данный момент прорабатываю возможность выдачи только на одном порту, т.е. закрепление айпи за портом, если порт изменится, то выдаст временный айпи, что можно будет изменить через личный кабинет пользователя.

В общем идея, привязка пользователей к свичам ацесс листами свичей. По снупингу привязка не понравилась ни в какой виде.

 

Прошу сильно не пинать, это первая программа под фряху. Но обоснованная критика приветствуется. Также считаю, что программа без багов не бывает, может вы найдете :), буду только рад. Но особенно буду рад если кому-нибудь это пригодится, т.к. убил почти неделю непрерывной писанины.

Далее в планах написать модуль ядра нарезки скорости, пока выбираю ОСЬ для этого. Linux isg штука хорошая, но кое чем тоже не устраивает.

post-106448-093024000 1386307379_thumb.gif

qdhcp.tar.gz

Edited by nshut

Share this post


Link to post
Share on other sites

Взял на тест. Очень интересна данная разработка!

Share this post


Link to post
Share on other sites

ах да, на скрине S 1:20 это статическая запись, 1 влан, 20 порт, t: время в секундах с последнего обновления айпи, следом айпишник это релей агент.

помимо S - есть D(динамика), B (запись BAD т.е. когда компутер говорит что айпишник занят в сети, удаляется толи через час толи 2), и T это временная запись, когда айпишник сервер подготовил для клиента на его запрос, но клиент не запросил его, т.е. еще не было nack сообщения. Чистка базы от таких записей происходит только при выделении новой записи, т.е. если за сутки никто не запросит айпишник которого нет в базе, записи так и будут висеть. логика такая, ибо не нагружать лишними проверками

Share this post


Link to post
Share on other sites

На данный момент прорабатываю возможность выдачи только на одном порту, т.е. закрепление айпи за портом, если порт изменится, то выдаст временный айпи, что можно будет изменить через личный кабинет пользователя.

В общем идея, привязка пользователей к свичам ацесс листами свичей. По снупингу привязка не понравилась ни в какой виде.

У сейчас примерно так и работает с freeradius+rlm_perl + БД билинга

По идее для того, чтобы всё держать в своей собственной памяти серверу нужно прикрутить какое-то API, чтобы в случае, например, добавления абоненту IP-адреса или, например, изменения привязки абонента с одного порта на другой, билинг мог как-то взаимодействовать с dhcp-сервером, чтобы тот "освежил в памяти" изменённые данные. В случае с работой с БД билинга напрямую этих трудностей нет, хоть и медленнее.

 

p.s. Я сейчас прикрутил ещё и memcached (запоминает на энное время уже "отвеченные" запросы, спасает от флуда), частично помогает снизить нагрузку на бд

Edited by Wingman

Share this post


Link to post
Share on other sites

На данный момент прорабатываю возможность выдачи только на одном порту, т.е. закрепление айпи за портом, если порт изменится, то выдаст временный айпи, что можно будет изменить через личный кабинет пользователя.

В общем идея, привязка пользователей к свичам ацесс листами свичей. По снупингу привязка не понравилась ни в какой виде.

У сейчас примерно так и работает с freeradius+rlm_perl + БД билинга

По идее для того, чтобы всё держать в своей собственной памяти серверу нужно прикрутить какое-то API, чтобы в случае, например, добавления абоненту IP-адреса или, например, изменения привязки абонента с одного порта на другой, билинг мог как-то взаимодействовать с dhcp-сервером, чтобы тот "освежил в памяти" изменённые данные. В случае с работой с БД билинга напрямую этих трудностей нет, хоть и медленнее.

 

p.s. Я сейчас прикрутил ещё и memcached (запоминает на энное время уже "отвеченные" запросы, спасает от флуда), частично помогает снизить нагрузку на бд

так и есть, клиентская тулза через шаред мемори опрашивает дшсп на последние записи перлом каждые 10 сек. нагрузки ноль. От момента получения абонента адреса и момента вход в личный кабинет проходит явно меньше времени чем интервал опроса. Но даже прикрутить апи к порту на котором сервак слушает и отвечает тем же удп ответом делов на час два. Мне просто надо было так, вот и накидал тулзу. т.е. у меня есть веб сервер который слушает от клиентов запросы и может вызывать любые команды на сервере и все это не от привилеге юзера.

просто бывает свич подвисает (длинки любят по питанию) и один запрос идет тысячи раз, ловить это долго, вот и закрыта у меня запись всех изменений в базу.

фактически уже работает у меня: клиент зашел, нажал кнопку это мой айпи и через секунду в дшсп уже статическая запись, ребут и он в сети с нужной скоростью.

идея к порту привязывать пришла вчера, но тоже делов на пол дня максимум. главное ведь логика БД, а скелет сервера подтюнить всегда можно.

цель была не рестартить дшсп при каждых изменениях базы и я ее достиг.

а еще есть глюк, инженер меняет магистральный порт на котором ставится филтер дшсп. клиенты не получают адреса. как изменим настройку а это день может пройти, клиенты с такой скоростью долбят радиус дшсп что и он и база в шоке.

Edited by nshut

Share this post


Link to post
Share on other sites

В случае с работой с БД билинга напрямую этих трудностей нет, хоть и медленнее.

Можно кешировать на стороне сервера все данные, причем кеширование сделать двухуровневым - в памяти для повышения производительности и на диске для того, чтобы сервер работал если пропадет связь с центральной БД.

Share this post


Link to post
Share on other sites

В случае с работой с БД билинга напрямую этих трудностей нет, хоть и медленнее.

Можно кешировать на стороне сервера все данные, причем кеширование сделать двухуровневым - в памяти для повышения производительности и на диске для того, чтобы сервер работал если пропадет связь с центральной БД.

Ну вот примерно к этому мы медленно, но верно подходим :)

Share this post


Link to post
Share on other sites
В случае с работой с БД билинга напрямую этих трудностей нет, хоть и медленнее.

абсолютно согласен, удобней чем с майскулом строить работу билинга еще ничего не видел. обратиться можно с любого языка программирования и т.д. и все связать дело не хитрое. Но прикручивать сам демон к базе и вставить функцию кэша у меня уже нет времени. Активно работаю над скелетом БД и как все это будет шейпится.

пока работает на УТМ, кто админил тот знает :(

Share this post


Link to post
Share on other sites

так и есть, клиентская тулза через шаред мемори опрашивает дшсп на последние записи перлом каждые 10 сек. нагрузки ноль. От момента получения абонента адреса и момента вход в личный кабинет проходит явно меньше времени чем интервал опроса. Но даже прикрутить апи к порту на котором сервак слушает и отвечает тем же удп ответом делов на час два. Мне просто надо было так, вот и накидал тулзу. т.е. у меня есть веб сервер который слушает от клиентов запросы и может вызывать любые команды на сервере и все это не от привилеге юзера.

Это, насколько я понял, касается изменений "dhcp -> billing db", или, грубо говоря, запись выданных лиз. Нужно для авторизации новых юзеров, например.

А я говорил немного о другом - об изменениях "billing -> dhcp" - например, когда юзеру изменили IP-адрес, или добавили второй IP-адрес, или переткнули в другой порт -- тут нужно именно онлайн подхватывание изменённых данных dhcp-сервером, т.е. либо работать с БД билинга напрямую, или дать возможность билингу по событиям пинать кеш dhcp-сервера

 

пока работает на УТМ, кто админил тот знает :(

Мы тоже на нём, перелазим на другой билинг, всё никак не перелезем)

 

а еще есть глюк, инженер меняет магистральный порт на котором ставится филтер дшсп. клиенты не получают адреса. как изменим настройку а это день может пройти, клиенты с такой скоростью долбят радиус дшсп что и он и база в шоке.

А смысл ставить filter dhcp_server на магистральных портах? У нас он стоит на всех 100мбитных, а все гигабитные по умолчанию настроены как магистральные, и монтажникам всё равно, в какой из портов тыркать аплинк.

 

Кроме того, что-то у вас с базой явно не так (индексы кривые? запросы кривые?), если клиенты всего одного свитча могут положить её. У нас трудности возникали, если город тысяч на 6 клиентов отваливался, а потом поднимался :) С прикручиванием memcached они нивелировались

Edited by Wingman

Share this post


Link to post
Share on other sites
Это, насколько я понял, касается изменений "dhcp -> billing db", или, грубо говоря, запись выданных лиз

сейчас так и есть. когда запущу другой билинг он также через тулзу будет напрямую работать с дшсп, (с утм да, даже зафиксифровать что чтото в базе произошло без лога проблематично) или через мемкэш с интервалом раз 10 в секунду будет для любой базы за глаза.

выложил именно скелет, т.к. когда я на него накручу то что мне надо, там черт ногу сломит. я и так не любитель код комментировать :))))

 

А смысл ставить filter dhcp_server на магистральных портах? У нас он стоит на всех 100мбитных, а все гигабитные по умолчанию настроены как магистральные, и монтажникам всё равно, в какой из портов тыркать аплинк.

 

Кроме того, что-то у вас с базой явно не так (индексы кривые? запросы кривые?), если клиенты всего одного свитча могут положить её. У нас трудности возникали, если город тысяч на 6 клиентов отваливался, а потом поднимался :) С прикручиванием memcached они нивелировались

магистральный свич может быть абонентским, и там явно не 24 пользователя. Я только пришел и порядок наводится. я не могу всех вырубить и перетянуть все провода чтобы аплинки и т.д. Пока есть скрипт, ходит по городу и ищет и конфигурит аплинки. Уже третий раз инвентаризацию иницирую, 2 закончились двумя домами :) каждый разгребает свое Г )))

Edited by nshut

Share this post


Link to post
Share on other sites

программа под фряху

 

Порт запилите ;)

рано еще. клиент накидан очень кустарным способом. Плюс еще жду оборудование кроме длинка, чтобы проверить работу по circuit id (по которому вычисляется влан и порт) не нашел ни одной доки из чего это поле должно состоять. Возможно придется отдельным файлом делать шаблон в зависимости от версии коммутатора. А может как биллинг запущу и внедрю код напрямую работы с базой. увы начинаю понимать, что время утекает от нас очень быстро

Share this post


Link to post
Share on other sites
- сервер выдает новые записи удаляя старые при базе в 65к заполненой полностью со скоростью 1к в секунду. В связи с этим мультипроцессорность в код не внедрял.

1к в секунду это вообще ниочём на сях при одном потоке и поиске в памяти. Для примера xbtt http://xbtt.sourceforge.net/tracker/ выдаёт сильно больше.

В перловую реализацию легко добавить кеш, тем более что перл умеет какие то там списки/массивы с хэшем в кач индекса, будет быстрее.

 

По коду:

Ваша реализация кеша крайне не эффективна: перебор всех элементов списка крайне затратен, собственно это худший алгоритм кеша :)

select не нужен когда у вас блокирующий сокет и вызывается recvfrom.

BYTE, WORD, DWORD - это вендовое -> uint8_t, uint16_t, uint32_t.

strcpy + strcat + sprintf +... остальное без контроля размера буфера -> один вызов snprintf(buf, sizeof(buf), "%fmt...", ...);

при этом, inet_ntop(AF_INET,&ip,s,20); можно заменить на что то типа snprintf(buf, sizeof(buf), "%"PRIu8".%"PRIu8".%"PRIu8".%"PRIu8"", ((ip >> 24) & 0xff), ((ip >> 16) & 0xff), ((ip >> 8) & 0xff), (ip & 0xff));

 

 

Мою версию дхцп парсинга можно посмотреть тут:

http://netlab.linkpc...DHCPTool_src.7z - это сама тулса которая ловит дхцп пакеты и переводит всё в текстовый читаемый вид, те она парсит все опции в пакете

http://netlab.linkpc...d/software/SDK/ - всякие доп файлы которые в ней используются, в тч DHCPMessage.h

 

circuit id (по которому вычисляется влан и порт) не нашел ни одной доки из чего это поле должно состоять.

// RFC 3046 DHCP Relay Agent Information Option (sub opt 1-2)

// RFC 3256 The DOCSIS (Data-Over-Cable Service Interface Specifications) Device Class DHCP (Dynamic Host Configuration Protocol) Relay Agent Information Sub-option (add subopt 4 to RFC 3046)

// RFC 3527 Link Selection sub-option (add subopt 5 to RFC 3046)

// RFC 3993 Subscriber-ID Suboption (add subopt 6 to RFC 3046)

// RFC 4014 RADIUS Attributes Suboption (add subopt 7 to RFC 3046)

// RFC 4030 Authentication Suboption (add subopt 8 to RFC 3046)

// RFC 4243 Vendor-Specific Relay Suboption (add subopt 9 to RFC 3046)

// RFC 5010 Relay Agent Flags Suboption (add subopt 10 to RFC 3046)

// RFC 5107 Server ID Override Suboption (add subopt 11 to RFC 3046)

 

Полный список: http://www.iana.org/...cp-parameters-1

Share this post


Link to post
Share on other sites

либо работать с БД билинга напрямую или дать возможность билингу по событиям пинать кеш dhcp-сервера

Если лизы хранить в отдельной БД (что и так в общем логично, к билингу это не относится), то, чтобы не нагружать БД биллинга кучей dhcp серверов, можно их натраваить на read-only реплику.

Edited by vitalyb

Share this post


Link to post
Share on other sites

2 Ivan_83

вот уже по делу. завтра послезавтра изучу приложенные ссылки. Седня уже под пивом, лучше не лезть :)

кэша в моем коде никакого нет, не представляю что за сеть где один дхп обслужит больше 1к запросов, поэтому даже не думал, а так туже таблицу хэш как в айпифв тоже прикрутить можно, но всетаки не тот сервис. Причем тест проводился на обычной тачке пня. причем тест без обработки пакета. Пришел пакет, отправил пакет увеличил скорость всего в 4 раза. я так понял из всего там самое затратное это малоки и калоки, но не утверждаю т.к. на синтетический тест потратил не много времени.

По поводу типов - дибильная привычка с винды, хочешь нужный тип, опиши сам ))

за рфц ссылки еще раз спасибо, завтра буду изучать, ненавижу когда знаю что какойто кусок кода даст сбой.

по поводу strcpy и команд по не лимитированым, набирая каждую команду пытаюсь продумать алгоритм переполнения. в моем сервере, а он тупо для меня основные баги можно вызвать конфиг файлов, т.е. там контроля ноль, в остальном длинну продумывал.

А честно, я вообще думал краши буду ловить каждый день. т.к. до этого инет атон и т.д. а тем более о форке знал только в теории.

а еще скажу. программить под никс системы одно удовольствие, тот же ман. Винда отдыхает. у них апи сложнее асембера местами

Share this post


Link to post
Share on other sites

У меня перловый вариант отдавая статический ответ держал вроде 5к/сек запросов, на Е5300, если правильно помню.

Кеш или база данных в памяти не сильно важно, на самом деле. Учитывая однопоточность можно было намудрить немного и искать очень быстрее.

Полагаю даже в вашей базе, если организовать перемещение записей к которым обращались в начало списка - это и будет кеш наиболее используемых записей очень малой кровью.

 

маллоки на мелких данных вообще смысла не имеют, проще брать из стёка uint8_t buf[4096]; и не думать об этом.

 

strcpy - их у вас много, а snprinf всего одна, это и проще читать, и вызов один и там же проверка буфера. Я раньше для оптимизации шёл ещё дальше и писал типа так:

if ( (*((uint32_t*)buf)) == (*((uint32_t*)"PASS")) ) - те все короткие сравнения и присвоения в 1, 2, 4, 8 байта оптимизировал подобным образом... А потом я собрал такую свою программу под ARM и понял почему так делать нельзя. Теперь такие вещи либо для одного байта либо через mem*** и прочие стандартные функции.

 

То что у вас написано оно вроде полностью на POSIX API и под вендой это собрать и запустить не сложно. Только инициализацию сокетов добавить, заголовочники и какие то мелочи.

А так да, под фрю/линукс оно вроде как проще, легче и приятнее. Чаще потому что есть куча открытых исходников где можно посмотреть как и даже исходники системы доступны. Ещё потому что все названия и тп короткие, в одном регистре, те текста меньше и он адаптирован для чтения и ручного ввода. У венды он адаптирован для копи-пасты из сдк :)

Share this post


Link to post
Share on other sites

либо работать с БД билинга напрямую или дать возможность билингу по событиям пинать кеш dhcp-сервера

Если лизы хранить в отдельной БД (что и так в общем логично, к билингу это не относится), то, чтобы не нагружать БД биллинга кучей dhcp серверов, можно их натраваить на read-only реплику.

 

А привязки "коммутатор + порт -> IP-адрес(а) абонента" вы тоже предлагаете хранить отдельной сущностью, безотносительно билинга? =)

Share this post


Link to post
Share on other sites

Иван, а объявление не extern переменных в хедере это так и должно быть ?

 

алсо размер unsigned long на amd64/emt64

Share this post


Link to post
Share on other sites

Да, глобальные константы/структуры а не переменные.

Вообще, тот проект немножко переформатить бы код.

По поводу размера переменных лучше говорить предметно а не в общем.

Share this post


Link to post
Share on other sites

обновил файл в шапке, изменил кое где отдачу клиенту данных через strncpy

а также изменил вывод влана и порта, теперь выглядит S 1:2:15 1-vlan, 2 стек id (на 3100 результат заметен), ну и 15 порт.

оптимизировать ничего не стал, все же это не тот сервис. Нашел что циска также отдает циркут айди, а вот зюксел по другому. стандарт стандартом, но видимо кроме circut type и id общего стандарта нет.

 

не ссортесь :)

мы же пишем для себя, а не за деньги. Я в хедере много могу чего объявить, увы изучать тонну литературы как красиво написать код не буду, мне хватит устранения ворнингов, а если я буду знать как красиво писать на си, пхп, перл и помнить все их функции какая лучше и красившне, голова лопнет. Мое мнение таково: "ОНО ВЕДЬ РАБОТАЕТ!" )))

Edited by nshut

Share this post


Link to post
Share on other sites

вопрос следующего характера: подумываю прикрутить возможность общения пхп с данным сервером, ну может не пхп, пример: цепляюсь по тсп, отправляю ДайМак00-00-00-00-00-00 и сервак отвечает что имеет. собсно сам вопрос к однопоточному сервису прикрутить в виде асинхронного ожидания как можно?

Читал что не рекомендуется делать по прерываниям, т.к. это некрасиво или инфа устаревшая? в общем на том, что имеем какой красивый способ сделать это? идеи с примерами приветствуются :) Сейчас сервер через семафоры и таймауты ожидает команды, а лучше можно организовать? межпроцессорное взаимодействие плохо знаю, как и сокеты

Share this post


Link to post
Share on other sites

А привязки "коммутатор + порт -> IP-адрес(а) абонента" вы тоже предлагаете хранить отдельной сущностью, безотносительно билинга? =)

Ну, у нас, например, так и есть, но выше я писал о другом. DHCP сервер эти данные не меняет, значит ему не нужен RW доступ к базе биллинга, ему достаточно RO доступа к односторонней реплике - обычная простая схема масштабирования mysql, когда пишем на один мастер, а читаем с N слейвов, только тут писать ничего не надо.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this