FreeBSD, dhcp, ip unnumbered и все все все…

    Наша небольшая компания подключает абонентов к интернету по технологии vlan-per-user.
    Так исторически сложилось и в этом есть как плюсы, так и минусы, но разговор сейчас не об этом.
    Обычно на каждый vlan выделяется сеть серых адресов, которая потом через NAT выпускается в большой мир. Но иногда абоненты хотят реальный белый адрес, и до недавнего времени им выдавалась /30 сеть. Что в реалиях настоящего очень расточительно и абоненты с реальными адресами переводятся на подключение по технологии SuperVLAN (RFC 3069).



    Все адреса – и серые, и белые выдаются абонентам через DHCP. Сервисы DHCP, NAT и шейпер работают на одном сервере под управлением FreeBSD 9.3. Железо у сервера вполне обычное – Core i7, 8Gb RAM, Intel E1G42ET.

    Задача:

    Выдавать каждому абоненту адрес тот же реальный адрес, что у него был, но уже не с маской /30, а с новой, общей для всех маской /24 и шлюзом x.x.x.1. Обеспечить невозможность работы абонента с чужим реальным адресом.
    Абонентам с серыми адресами продолжать выдавать сеть на абонента.

    Строим SuperVlan ala FreeBSD

    Vlan9 – технологический vlan, к нему ничего не подключено, туда будет уходить трафик для еще не выданных абонентам реальных адресов.
    Vlan10 – абонент с серым адресом
    Vlan11 и vlan12 два абонента с реальными адресами.

    ifconfig vlan9 inet 1.1.1.1/24 
    ifconfig vlan10 inet 192.168.10.1/24
    ifconfig vlan11 inet 1.1.1.1/32
    route add 1.1.1.11 –iface vlan11 
    ifconfig vlan12 inet 1.1.1.1/32
    route add 1.1.1.12 –iface vlan12
    


    Ставим на клиенте vlan11 руками адрес 1.1.1.11/24, gw 1.1.1.1 – интернетик у клиента работает.

    Если требуется хождение трафика между клиентами с реальными адресам включаем ProxyARP

    sysctl net.link.ether.inet.proxyall=1
    


    (Bridge из private sticky vlan (man bridge) в данном случае использовать не получилось, о причинах будет сказано ниже)

    Теперь займемся самым интересным

    Раздача адресов через DHCP

    А конкретно ISC-DHCPD ver 4.3

    Адреса надо выдавать на основании интерфейса, с которого пришел запрос.
    К большому сожалению авторы isc-dhcpd твердо уверены, что они точно знают лучше, что надо сисадмину, чем сам сисадмин. И не дают ему прострелить себе ногу, отрубая сисадмину указательный палец по самый локоть.

    В итоге можно указать список интерфейсов на которых хотелось бы слушать запросы от клиентов, но если в конфиге dhcpd.conf не указать subnet в которую попадает адрес на этом интерфейсе – то он будет проигнорирован.
    А если на нескольких интерфейсах указаны одинаковые адреса – то запросы DHCPREQ будут обрабатываться в одном subnet.
    И нельзя прописать в dhcpd.conf ни subnet, ни pool, ни host которые были бы жестко привязаны к опеределенному интерфейсу.

    Это мечта, на самом деле так не работает:
    subnet 1.1.1.11 netmask 255.255.255.255 {
       option routers 1.1.1.1;
       range 1.1.1.11;
       interface vlan11;
    }
    


    Ну чтож… Есть же DHCP relay. Запускаем релей на нужных портах, который добавит Option 82 Agent Circuit ID, и отдаст запрос dhcpd.

    Попытка 1:

    Так как на лупбек интерфейсе dhcp категорически не желает слушать – заводим дополнительный vlan5

    rc.conf:
    ifconfig_vlan5=”inet 192.168.5.1/24”
    dhcrelay_flags =”-a”
    dhcrelay_servers =”192.168.5.1”
    dhcrelay_ifaces =”vlan11 vlan12”
    dhcpd_ifaces =”vlan5 vlan10”
    


    Запускаем – не запускается…
    dhcpd сообщает, что не может bind socket.
    Хотя прямо указаны разные интерфейсы для релея и для демона, они оба на всякий случай хотят забиндить fallback socket на адрес *:67
    о чем и сообщают вот этой строчкой при старте “Sending on Socket/fallback”.

    Естественно тому, кто стартует вторым система говорит: “Занято!”
    Как и можно ли отключить этот fallback сокет я не смог найти.

    Попытка 2:

    Альтернативные программы – dhcprelya и dhcprelay.
    Опять сообщают, что не могут сделать bind socket. Смотрим в код и понимаем их кардинальное отличие от isc-творений. ISC использует device bpf и выхватывает свои пакеты у системы практически в самом начале сетевого стека, еще до фаерволла и прочих излишеств … Альтернативщики используют стандартный механизм socket, но так как у нас на нескольких интерфейсах одинаковые адреса, то на второй bind на адрес 1.1.1.1:67 система опять сообщает: “Занято!”

    Попытка 3:

    А пусть dhcrelay слушает ВСЕ интерфейсы и пересылает запросы в dhcpd, который будет прибит только к адресу 192.168.5.1
    Пересобираем dhcpd, чтобы он не использовал BPF, а подключался бы через socket.
    Для этого по быстрому подправляем Makefile порта isc-dhcp43-server
    Добавляем в него:
    CONFIGURE_ARGS+=--enable-use-sockets
    Собираем, запускаем – запустилось, но не работает. Dhcpd в логи пишет, что присвоил клиенту адрес, но клиент ответа от dhcpd не получает.

    Смотрим tcpdump как пытается получить адрес клиент на vlan10, тот, что с серыми адресами.
    tcpdump –i vlan10 — Запрос от клиента есть – ответа от сервера нет.
    tcpdump –i vlan5 – тишина
    tcpdump –i lo0 – а вот тут интересно:
    видим udp пакет от релея к демону с адреса 192.168.5.1 на адрес 192.168.5.1
    видим udp ответ от демона к релею с адреса 192.168.5.1 на адрес 192.168.10.1

    И тут становиться понятно, что так как dhcrelay слушает только через BPF – то пакеты, которые проходят через внутренних интерфейс lo0 он просто не видит, пересобирать dhcrelay на использование сокетов смысла нет, тогда мы вернемся в ситуацию “Попытка 2”

    Попытка 4:

    Разносим dhcrelay и dhcpd на ОТДЕЛЬНЫЕ сервера.

    Server dhcpd rc.conf:
    ifconfig_vlan5=”inet 192.168.5.1/24”
    


    Server dhcrelay rc.conf:

    ifconfig_vlan5=”inet 192.168.5.2/24”
    dhcrelay_flags =”-a”
    dhcrelay_servers =”192.168.5.1”
    


    Не работает.
    Ах да….
    Вспоминает tcpdump из “попытки 3”, прописываем на dhcpd -сервере
    route add 192.168.10.0/24 192.168.5.2
    route add 1.1.1.0/24 192.168.5.2

    Теперь работает. Адреса клиенту в vlan10 отдает.
    Теперь добавляем в dhcpd информацию о клиентах с реальными адресами.
    В интернете полно примеров с классами, в которых вырезают подстроки из agent id и circuit-id, создают подклассы, чтобы можно было выделять несколько адресов на порт подключения.

    Мне достаточно одного адреса, поэтому достаточно описать host вот с такой строчкой внутри:
    host-identifier option agent.circuit-id «vlan11»

    Если собрать SuperVLAN с помощью bridge, то dhcrelay будет пересылать запросы со всех интерфейсов объединенных в мост с agent.circuit-id=«bridge0», так что только интерфейсы с одинаковыми адресами и принудительной маршрутизацией.

    Итого:

    Достоинства решения — клиент автоматически получит новые настройки, при этом у него останется его старый адрес.
    Недостатки — Потребовался второй сервер
    ToDo: Проверить как отрабатывается ситуация со сменой мак-адреса у клиента. Выдаст ли dhcpd тот же реальный адрес из host, если у него еще не истек срок выдачи на предыдущий мак.

    И возвращаясь к заботливым и непосредственным авторам isc-dhcpd.
    Чтобы dhcpd перехватывал через bpf запросы, пришедшие на интерфейс надо прописать в конфиг dhcpd.conf секцию subnet в который входит адрес на этом интерфейсе.
    В то же время нет необходимости указывать какие именно subnet использовать для выдачи ответов через relay – он ищет совпадения во всех.

    Вот конфиг dhcpd.conf

    option domain-name "example.org";
    option domain-name-servers 8.8.8.8, 8.8.4.4;
    
    default-lease-time 600;
    max-lease-time 7200;
    one-lease-per-client true;
    stash-agent-options true;
    update-conflict-detection false;
    
    authoritative;
    log-facility local7;
    
    subnet 192.168.5.0 netmask 255.255.255.0 {
    }
    
    subnet 192.168.10.0 netmask 255.255.255.0 {
        range 192.168.10.2 192.168.10.254;
        option routers 192.168.10.1;
    }
    
    subnet 192.168.14.0 netmask 255.255.255.0 {
        range 192.168.14.2 192.168.14.254;
        option routers 192.168.14.1;
    }
    
    subnet 1.1.1.0 netmask 255.255.255.0 {
    }
    
    group realip1 {
      option routers 1.1.1.1;
      host client11 {
        host-identifier option agent.circuit-id "vlan11";
        fixed-address 1.1.1.11;
      }
      host client12 {
        host-identifier option agent.circuit-id "vlan12";
        fixed-address 1.1.1.12;
      }
      host client28 {
        host-identifier option agent.circuit-id "vlan28";
        fixed-address 1.1.1.13;
      }
    }
    


    PS: Неожиданные грабли и решение:
    если кому понадобится поднять isc dhcrelay на 8к интерфейсов
    надо поменять
    значение FD_SETSIZE в файлах
    /usr/src/sys/sys/select.h
    /usr/include/sys/select.h

    с
    #define FD_SETSIZE 1024U
    на
    #define FD_SETSIZE 16384U

    Пересобираем isc-dhcrelay или isc-dhcpd
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 14

      0
      Не забудьте проверить, что будут бегать пакетики между двумя абонентами с белыми адресами.
        +1
        Добавил про включение ProxyARP
          0
          Можно bridge устроить :). Без proxy arp
            0
            Если устроить бридж, то dhcrelay в circuit_id будет писать bridge0,
            а мне надо vlan11
              0
              Не дочел про socketвость релеев.
              Интересно, а вот это чудо сработает static.uguu.ca/projects/staticDHCPd/doc/index.html
                0
                DHCP на питоне…
                С возможностью дописывать функционал в конфиге ???

                Интересная штучка для экспериментов. этакий сферический конь в вакууме.
                  0
                  На nag-е есть ещё два DHCP-сервера на Perl-е.

                  А на FreeRADIUS можно наваять сервер на Perl/Python.
        0
        Как только люди не извращаются, лишь бы не использовать accel-ppp.
          +2
          Использование различных ppp-туннелей было оправдано в начале века.
          Когда на дома ставили неуправляемые мыльницы и скорость в 512 кбит считалась вполне приемлимой.

          в современном мире надо подключать по IPoE
            +3
            Туннели — зло.
            Я имел в виду именно IPoE.
            accel-ppp умеет ip unnumbered с авторизацией из RADIUS, при этом сам же выступает в роли DHCP-сервера для абонента.

            Собственно, там от ppp только название-то и осталось.
              0
              Тут можно парировать и холиварить на тему ISG IPoE, приплести микротики, вопрос в экономической целесообразности и пределе трафика/абонбазы. можно и всё в одном влане только на коммутаторах 2 и 3 уровня сделать, вынеся NAT на отдельную железяку с привязкой мака и ипешника к конкретному порту коммутатора доступа, обслуживать такое правда намучаешься…
                +2
                Не линуксоид, не знал что из него такой комбайн выстроили.
            0
            Не ясно, почему вместо 9 влана не сделали -blackhole на IP?
              0
              Про роут с -blackhole просто в голову не пришло.

              Я с ip unnumbered давно уже разбирался, так что насколько я помню vlan9 появился после перехода на FreeBSD 9

              В ней поменялось что-то в route add, и как мне помниться после создания первого интерфейса для пула unnumbered нельзя было удалить с него route -net -iface, чтобы прописать роут на отдельный ip

              создал левый интерфейс первым и workaround, про создание роута в -iface lo -blackhole даже в голову не пришло ;)
              Надо будет в понедельник на стенде попробовать.

            Only users with full accounts can post comments. Log in, please.