これは前回の質問の続きですFreeradius DHCPサーバ実装から静的ルートを送信する場合Strongswan VPN サーバーと組み合わせて使用します。
tcpdump と Wireshark を使用して Freeradius をデバッグしているときに、dhcp サーバー構成ファイルのセクションとセクションにオプションDHCP-Classless-Static-Route
とDHCP-Site-specific-25
(別名 Microsoft 静的ルート) を追加することで、Freeradius DHCP サーバーからクラスレス静的ルートを送信できることが分かりました。DHCP-Discover
DHCP-Request
0.0.0.0
しかし、デフォルトゲートウェイを次のように設定すると、静的ルートがMicrosoft VPNクライアントによって受け入れられないようです。Strongswan ドキュメント。
少なくとも、 を使用しているときに、Windows クライアントでアドバタイズされたルートを見つけることができませんroute print -4
。
0.0.0.0
また、 VPN インターフェイス経由の標準ゲートウェイとして使用している場合、Windows クライアントにルートを手動で追加することはできません。
しかし:
たとえば、192.168.200.0/24
VPN 経由でサブネットにアクセスし、VPN サーバーが Windows クライアントにアドレスを割り当てるとします192.168.201.2/24
。この場合、Windows コマンドを使用してサブネット 192.168.200.0/24 が 192.168.201.2 経由でアクセス可能であることを宣言することで、Windows クライアント側で静的ルートを作成することが可能です。
route add 192.168.200.0 mask 255.255.255.0 192.168.201.2
少し奇妙に見えるかもしれませんが、192.168.200.0
サブネット上のどのホストにも ping できるので、動作している限りは満足です。 :-)
しかし、すべての VPN クライアントで手動で行うのではなく、VPN サーバーからルートをアドバタイズすることで同じことを実行できればもっとうれしいです。 :-)
つまり、Freeradius の DHCP 構成に少し動的プログラミングを行う必要があります。私の場合、割り当てられたクライアント VPN IP アドレスを取得し、それをオクテットに変換して、同じくオクテットとして指定される静的ルートと組み合わせる、DHCP-Discover および DHCP-request 内の Perl モジュールへの参照を作成する必要があります。
例:
サブネットは、サブネット マスクが最初にエンコードされるのと192.168.200.0/24
同じようにエンコードされます。0x18c0a8c8
クライアントは、IP アドレス内の各数字を 16 進数に変換するだけで192.168.201.2/24
エンコードされます。0xc0a8c902
ルートの最終的なエンコーディングは次のようになります。0x18c0a8c8c0a8c902
これは、2 つの文字列を連結しただけだからです。
update reply
次に、次のコードを使用する必要があります。
update reply {
&DHCP-Classless-Static-Route = 0x18c0a8c8c0a8c902
&DHCP-Site-specific-25 = 0x18c0a8c8c0a8c902
}
さらにルートがある場合は、すべてのルートが 1 つの長い文字列に連結されます。
難しい部分:
freeradius/3.0/sites-available/dhcp
ファイルに記載されている Freeradius DHCP サーバーのデフォルト構成があると仮定します。
DHCP-Discover および DHCP-Request のファイルの一般的な構造は次のとおりです。
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
}
次に、私が収集した限りでは、 がdhcp_sqlippool
呼び出された後、 が返される前に、 perl モジュールを呼び出す必要があります。これは、 が VPN クライアントに IP アドレスを割り当てるモジュールであるok
ためです。dhcp_sqlippool
つまり、私のバージョンは次のようになります。
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
}
これを動作させるには、freeradius/3.0/mods-enabled
フォルダーの下で perl を有効にし、ファイル名を変更してfreeradius/3.0/mods-enabled/perl
perl モジュールを指すようにする必要があります。たとえば、次のようになります。
filename = ${modconfdir}/${.:instance}/dhcp/Options.pm
しかし、どうすれば perl への呼び出しを正しく参照できるのでしょうか?
func_post_auth = post_auth
Freeradius からの呼び出しを処理するには、この行を有効にしfreeradius/3.0/mods-enabled/perl
て perl モジュールにセクションを作成する必要があると思いましたsub post_auth
が、ログで確認する限り、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
それで、私が見ていないものは何でしょうか?
答え1
何度か壁に頭をぶつけましたが、少なくとも Perl モジュールは動作しました。ただし、DHCP 経由の静的ルーティングは Freeradius DHCP サーバーから Strongswan 経由で VPN クライアントに渡されず、Freeradius DHCP サーバーから UDP パッケージをデバッグすると、問題は別の場所にあることが示唆されるため、まだ完全に目的を達成したわけではありません。
とにかく、私がやったことは次のとおりです:
- perl モジュールを有効にし
freeradius/3.0/mods-enabled
、少なくとも次の行を設定します。
perl {
# Perl code location: ("freeradius/3.0/mods-config/dhcp/Options.pm")
filename = ${modconfdir}/${.:instance}/dhcp/Options.pm
# DHCP module is called during freeradius post_auth
func_post_auth = post_auth
}
- 変更する
freeradius/3.0/sites-enabled/dhcp
関連箇所はDHCP-Discover
、およびですDHCP-Request
。
dhcp DHCP-Discover {
update reply {
DHCP-Message-Type = DHCP-Offer
}
# The contents here are invented. Change them!
update reply {
&DHCP-Domain-Name-Server = 192.168.200.1
&DHCP-Subnet-Mask = 255.255.255.0
&DHCP-IP-Address-Lease-Time = 86400
&DHCP-DHCP-Server-Identifier = 192.168.200.4
}
# Or, allocate IPs from the DHCP pool in SQL. You may need to
# set the pool name here if you haven't set it elsewhere.
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
# Call static route generation.
perl
ok
}
dhcp DHCP-Request {
# Response packet type. See DHCP-Discover section above.
update reply {
&DHCP-Message-Type = DHCP-Ack
}
# The contents here are invented. Change them!
update reply {
&DHCP-Domain-Name-Server = 192.168.200.1
&DHCP-Subnet-Mask = 255.255.255.0
&DHCP-IP-Address-Lease-Time = 86400
&DHCP-DHCP-Server-Identifier = 192.168.200.4
}
# Or, allocate IPs from the DHCP pool in SQL. You may need to
# set the pool name here if you haven't set it elsewhere.
update control {
&Pool-Name := "vpn_pool"
}
dhcp_sqlippool
# Call static route generation.
perl
ok
}
- 次の場所にある perl コードを作成します
freeradius/3.0/mods-config/perl/dhcp/Options.pm
:
use strict;
use warnings;
use Data::Dumper;
use Net::IP;
# Bring the global hashes into the package scope
our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);
#
# This the remapping of return values
#
use constant {
RLM_MODULE_REJECT => 0, # immediately reject the request
RLM_MODULE_OK => 2, # the module is OK, continue
RLM_MODULE_HANDLED => 3, # the module handled the request, so stop
RLM_MODULE_INVALID => 4, # the module considers the request invalid
RLM_MODULE_USERLOCK => 5, # reject the request (user is locked out)
RLM_MODULE_NOTFOUND => 6, # user not found
RLM_MODULE_NOOP => 7, # module succeeded without doing anything
RLM_MODULE_UPDATED => 8, # OK (pairs modified)
RLM_MODULE_NUMCODES => 9 # How many return codes there are
};
# Same as src/include/radiusd.h
use constant L_DBG=> 1;
use constant L_AUTH=> 2;
use constant L_INFO=> 3;
use constant L_ERR=> 4;
use constant L_PROXY=> 5;
use constant L_ACCT=> 6;
# Function to handle post_auth
sub post_auth {
# Get VPN Client IP from Freeradius DHCP server.
my $client_ip = new Net::IP ( $RAD_REQUEST{'DHCP-Requested-IP-Address'} ) or die (Net::IP::Error());
# An example of 2 routing rules sent ('192.168.20.0/24' and '192.168.200.0/24')
my @routes = (new Net::IP('192.168.20/24'), new Net::IP('192.168.200/24'));
# Measure how many elements there is in the routes array.
my $size = @routes;
# Convert client ip into hex code.
my $client_octets = get_ip_octets ($client_ip->ip(),$client_ip->prefixlen());
# Freeradius want the encoded string start with '0x'
# followed by the encoded octets as hex.
my $octet_str = "0x";
for(my $i = 0; $i < $size; $i++)
{
# Convert subnet into octets, skipping ending zeroes.
my $route_octets = get_ip_octets ($routes[$i]->ip(),$routes[$i]->prefixlen());
# Convert network prefix into octets
my $hex_prefix = sprintf("%02x", $routes[$i]->prefixlen());
# Route is encoded by network octets followed by subnet octets
$route_octets = $hex_prefix . $route_octets;
# The entire route string is the route octets followed by gateway octets ('the client vpn ip').
my $route_str = $route_octets . $client_octets;
$octet_str = $octet_str . $route_str;
}
# Classless static routing (dhcp option 121)
$RAD_REPLY{'DHCP-Classless-Static-Route'} = $octet_str;
# Microsoft classless static routing (dhcp option 249)
$RAD_REPLY{'DHCP-Site-specific-25'} = $octet_str;
return RLM_MODULE_OK;
}
sub get_ip_octets {
# First parameter: Source ip address
my $sip = $_[0];
# Second parameter: Bitlength of network (aka CIDR notation).
my $cidr = $_[1];
my @decimals = split('\.', $sip);
my $index = int($cidr / 8) ;
my $result = '';
for(my $i = 0; $i < $index; $i++)
{
# Convert each number in ip address to hex and format with leading
# zero in case converted number is less than 16.
$result = $result . sprintf("%02x", $decimals[$i]);
}
return $result;
}
Perlコードはここから調整できるので、オプション121またはオプション 249 は、クライアントのオペレーティング システムに応じて送信されます。
また、コードをより汎用的にして、静的ルートを Freeradius 構成ファイルで直接定義できるようにする可能性も残しています。