DHCP サーバーを使用した Freeradius: Perl モジュールの呼び出しでエラーが返される

DHCP サーバーを使用した Freeradius: Perl モジュールの呼び出しでエラーが返される

これは前回の質問の続きですFreeradius DHCPサーバ実装から静的ルートを送信する場合Strongswan VPN サーバーと組み合わせて使用​​します。

tcpdump と Wireshark を使用して Freeradius をデバッグしているときに、dhcp サーバー構成ファイルのセクションとセクションにオプションDHCP-Classless-Static-RouteDHCP-Site-specific-25(別名 Microsoft 静的ルート) を追加することで、Freeradius DHCP サーバーからクラスレス静的ルートを送信できることが分かりました。DHCP-DiscoverDHCP-Request

0.0.0.0しかし、デフォルトゲートウェイを次のように設定すると、静的ルートがMicrosoft VPNクライアントによって受け入れられないようです。Strongswan ドキュメント

少なくとも、 を使用しているときに、Windows クライアントでアドバタイズされたルートを見つけることができませんroute print -4

0.0.0.0また、 VPN インターフェイス経由の標準ゲートウェイとして使用している場合、Windows クライアントにルートを手動で追加することはできません。

しかし:

たとえば、192.168.200.0/24VPN 経由でサブネットにアクセスし、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/perlperl モジュールを指すようにする必要があります。たとえば、次のようになります。

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

しかし、どうすれば perl への呼び出しを正しく参照できるのでしょうか?

func_post_auth = post_authFreeradius からの呼び出しを処理するには、この行を有効にし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 パッケージをデバッグすると、問題は別の場所にあることが示唆されるため、まだ完全に目的を達成したわけではありません。

とにかく、私がやったことは次のとおりです:

  1. 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
}
  1. 変更する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
}
  1. 次の場所にある 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 構成ファイルで直接定義できるようにする可能性も残しています。

関連情報