Рейтинг:1

Freeradius with dhcp server: Calls to perl module returns error

флаг us

This is a continuation of my previous question in sending static routes from Freeradius DHCP server implementation in combination with Strongswan VPN server.

When debugging Freeradius using tcpdump and Wireshark I found out that I can send classless static routes from Freeradius DHCP server by through adding DHCP-Classless-Static-Route and DHCP-Site-specific-25 (aka Microsoft static route) options to my DHCP-Discover and DHCP-Request sections of the dhcp server configuration file.

However: It appears that the static routes are not accepted by Microsoft VPN client if I set the default gateway to be 0.0.0.0 as suggested by Strongswan documentation.

At least I cannot find the advetised routes on my Windows client when using route print -4.

Also I cannot add the routes manually on Windows client when I am using 0.0.0.0 as standard gateway over VPN interface.

However:

Lets say I want to access the subnet 192.168.200.0/24 over VPN and my VPN server assign the address 192.168.201.2/24 to my Windows client. Then it is actually possible to create a static route on windows client side by declaring that the subnet 192.168.200.0/24 is accessible via 192.168.201.2 using the windows command:

route add 192.168.200.0 mask 255.255.255.0 192.168.201.2

I know it looks a bit weird, but I can ping any host on the 192.168.200.0 subnet, so as long as it works I am happy. :-)

But: I would be more happy if I could do the same thing by advertising the routes from my VPN server instead of doing it manually on all VPN clients. :-)

That means I have to do a bit of dynamic programming to the DHCP configuration in Freeradius. I my case it means I have to make a reference to a perl module in DHCP-Discover and DHCP-request that grabs the assigned client vpn ip address, convert it into octets and combine it with the static routes that is also given as octets.

An example:

The subnet 192.168.200.0/24 will be encoded as 0x18c0a8c8 as subnet mask is encoded first.

The client 192.168.201.2/24 will be encoded as 0xc0a8c902 as it is just converting each number in the ip address to hex.

The final encoding for the route will be: 0x18c0a8c8c0a8c902 as it is just a concatination of the two strings.

I then have to use update reply with the following code:

  update reply {
    &DHCP-Classless-Static-Route = 0x18c0a8c8c0a8c902
    &DHCP-Site-specific-25 = 0x18c0a8c8c0a8c902
  }

If there are any more routes then all the routes will be concatenated into one long string.

The tricky part:

Assume you have the default configuration of Freeradius DHCP server as found in freeradius/3.0/sites-available/dhcp file.

The general structure of the file for DHCP-Discover and DHCP-Request is as follows:

dhcp DHCP-Request {
  update reply {
    &DHCP-Message-Type = DHCP-Ack
  }

  update reply {
    # General DHCP options, such as default GW, DNS, IP-address lease time etc.
  }

  update control {
    &Pool-Name := "vpn_pool"
  }

  dhcp_sqlippool

  ok
}

Then as far as I have gathered I need to call my perl module after dhcp_sqlippool has been called and before returning ok, because dhcp_sqlippool is the module that assigns the ipaddress to the VPN client.

That means my version would be something like:

dhcp DHCP-Request {
  update reply {
    &DHCP-Message-Type = DHCP-Ack
  }

  update reply {
    # General DHCP options, such as default GW, DNS, IP-address lease time etc.
  }

  update control {
    &Pool-Name := "vpn_pool"
  }

  dhcp_sqlippool

  perl

  # If perl module returned no error
  if(ok) {
    update reply {
      # Perl-Route contains a hex encoded string with all routes.
      &DHCP-Classless-Static-Route = Perl-Route
      &DHCP-Site-specific-25 = Perl-Route      
    }
  }

  # Not sure if this one is needed?
  update reply {
    &DHCP-End-Of-Options = 255
  }

  ok
}

In order to make it work, I have to enable perl under the freeradius/3.0/mods-enabled folder and modify the filename in freeradius/3.0/mods-enabled/perl to point it to my perl module. Like for instance:

filename = ${modconfdir}/${.:instance}/dhcp/Options.pm

But how do I do reference the call to perl the right way?

I thought I had to enable the line func_post_auth = post_auth in freeradius/3.0/mods-enabled/perl and create a sub post_auth section in my perl module to handle calls from Freeradius, but as far as I can see in my log I get the following error in Freeradius:

(8) perl: perl_embed:: module = /etc/freeradius/3.0/mods-config/perl/dhcp/Options.pm , 
func = post_auth exit status= Undefined subroutine &main::post_auth called.
...
(8)     [perl] = fail
(8)   } # dhcp DHCP-Discover = fail

So what is it that I am not seeing?

Рейтинг:0
флаг us

Я несколько раз ударился головой о стену, но по крайней мере у меня заработал модуль perl, правда я не совсем там, где хочу быть, так как статическая маршрутизация по DHCP не проходит от DHCP-сервера Freeradius к VPN-клиенту через Strongswan , но отладка пакетов UDP с DHCP-сервера Freeradius подразумевает, что проблема в другом.

Во всяком случае, вот что я сделал:

  1. Включить модуль perl в freeradius/3.0/моды с поддержкой и установите как минимум следующие строки:
перл {
  # Расположение кода Perl: ("freeradius/3.0/mods-config/dhcp/Options.pm")
  имя файла = ${modconfdir}/${.:instance}/dhcp/Options.pm

  # DHCP-модуль вызывается во время работы freeradius post_auth
  func_post_auth = пост_аутентификация
}
  1. Изменить freeradius/3.0/сайты с поддержкой/dhcp Соответствующие места DHCP-обнаружение и DHCP-запрос:
dhcp DHCP-обнаружение {

        обновить ответ {
               DHCP-Message-Type = DHCP-предложение
        }

        # Содержимое здесь придумано. Измени их!
        обновить ответ {
                & DHCP-сервер доменных имен = 192.168.200.1
                & DHCP-маска подсети = 255.255.255.0
                & DHCP-IP-адрес-Lease-Time = 86400
                & DHCP-идентификатор DHCP-сервера = 192.168.200.4
        }

        # Или выделите IP-адреса из пула DHCP в SQL. Вам может понадобиться
        # установите имя пула здесь, если вы не установили его где-либо еще.
        управление обновлением {
                &Имя_пула := "vpn_pool"
        }

        dhcp_sqlippool

        # Вызов генерации статического маршрута.
        перл

        хорошо
}

dhcp DHCP-запрос {

        # Тип ответного пакета.См. раздел DHCP-Discover выше.
        обновить ответ {
               &DHCP-Message-Type = DHCP-Ack
        }

        # Содержимое здесь придумано. Измени их!
        обновить ответ {
                & DHCP-сервер доменных имен = 192.168.200.1
                & DHCP-маска подсети = 255.255.255.0
                & DHCP-IP-адрес-Lease-Time = 86400
                & DHCP-идентификатор DHCP-сервера = 192.168.200.4
        }

        # Или выделите IP-адреса из пула DHCP в SQL. Вам может понадобиться
        # установите имя пула здесь, если вы не установили его где-либо еще.
        управление обновлением {
                &Имя_пула := "vpn_pool"
        }
 
        dhcp_sqlippool

        # Вызов генерации статического маршрута.
        перл

        хорошо
}
  1. Создайте Perl-код, расположенный по адресу freeradius/3.0/mods-config/perl/dhcp/Options.pm:
использовать строгий;
использовать предупреждения;
использовать Data::Dumper;
использовать Net::IP;

# Вносим глобальные хэши в область действия пакета
наш (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);

#
# Это переназначение возвращаемых значений
#
использовать константу {
    RLM_MODULE_REJECT => 0, # немедленно отклонить запрос
    RLM_MODULE_OK => 2, # модуль в порядке, продолжаем
    RLM_MODULE_HANDLED => 3, # модуль обработал запрос, так что стоп
    RLM_MODULE_INVALID => 4, # модуль считает запрос некорректным
    RLM_MODULE_USERLOCK => 5, # отклонить запрос (пользователь заблокирован)
    RLM_MODULE_NOTFOUND => 6, # пользователь не найден
    RLM_MODULE_NOOP => 7, # модуль прошел успешно, ничего не делая
    RLM_MODULE_UPDATED => 8, # OK (пары изменены)
    RLM_MODULE_NUMCODES => 9 # Сколько существует кодов возврата
};

# То же, что и src/include/radiusd.h
использовать константу L_DBG=>1;
использовать константу L_AUTH=>2;
использовать константу L_INFO=>3;
использовать константу L_ERR=>4;
использовать константу L_PROXY=> 5;
использовать константу L_ACCT=>6;

# Функция для обработки post_auth

под post_auth {

    # Получить IP-адрес VPN-клиента с DHCP-сервера Freeradius.
    my $client_ip = new Net::IP ( $RAD_REQUEST{'DHCP-Requested-IP-Address'} ) или die (Net::IP::Error());

    # Пример отправки 2 правил маршрутизации ('192.168.20.0/24' и '192.168.200.0/24') 
    my @routes = (новая сеть::IP('192.168.20/24'), новая сеть::IP('192.168.200/24'));

    # Измерить количество элементов в массиве маршрутов.
    мой $size = @маршруты;

    # Преобразование IP-адреса клиента в шестнадцатеричный код.
    мои $client_octets = get_ip_octets ($client_ip->ip(),$client_ip->prefixlen());

    # Freeradius хочет, чтобы закодированная строка начиналась с '0x'
    # за которым следуют закодированные октеты в шестнадцатеричном виде.
    мой $octet_str = "0x";

    for(my $i = 0; $i < $size; $i++)
    {
        # Преобразовать подсеть в октеты, пропуская конечные нули.
        мои $route_octets = get_ip_octets ($routes[$i]->ip(),$routes[$i]->prefixlen());

        # Преобразовать сетевой префикс в октеты
        мой $hex_prefix = sprintf("%02x", $routes[$i]->prefixlen());

        # Маршрут кодируется октетами сети, за которыми следуют октеты подсети
        $route_octets = $hex_prefix . $route_octets;

        # Вся строка маршрута представляет собой октеты маршрута, за которыми следуют октеты шлюза («клиент vpn ip»).
        мой $route_str = $route_octets . $client_octets;

        $octet_str = $октет_str . $маршрут_стр;
    }

    # Бесклассовая статическая маршрутизация (опция DHCP 121)
    $RAD_REPLY{'DHCP-бесклассовый-статический-маршрут'} = $octet_str;

    # Бесклассовая статическая маршрутизация Microsoft (опция DHCP 249)
    $RAD_REPLY{'DHCP-Site-specific-25'} = $octet_str;

    вернуть RLM_MODULE_OK;

}

суб get_ip_octets {
    # Первый параметр: Исходный IP-адрес
    мой $sip = $_[0];

    # Второй параметр: длина сети в битах (нотация CIDR).
    мой $cidr = $_[1];

    мой @decimals = split('\.', $sip);
    мой $index = int($cidr/8);

    мой $результат = '';
    for(my $i = 0; $i < $index; $i++)
    {
        # Преобразование каждого числа в IP-адресе в шестнадцатеричный формат и форматирование с начальным
        # ноль, если преобразованное число меньше 16.
        $результат = $результат. sprintf("%02x", $decimals[$i]);
    }

    вернуть $результат;
}

Код perl можно настроить отсюда, поэтому опция 121 или же опция 249 отправляется в зависимости от клиентской операционной системы.

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

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.