DHCP 서버가 있는 Freeradius: Perl 모듈 호출 시 오류가 반환됩니다.

DHCP 서버가 있는 Freeradius: Perl 모듈 호출 시 오류가 반환됩니다.

이것은 이전 질문에 이어집니다.Freeradius DHCP 서버 구현에서 고정 경로를 보낼 때Strongswan VPN 서버와 결합.

tcpdump 및 Wireshark를 사용하여 Freeradius를 디버깅할 때 dhcp 서버 구성 파일의 my 및 섹션에 DHCP-Classless-Static-RouteDHCP-Site-specific-25(Microsoft 정적 경로라고도 함) 옵션을 추가하여 Freeradius DHCP 서버에서 클래스 없는 정적 경로를 보낼 수 있다는 것을 알았습니다 .DHCP-DiscoverDHCP-Request

0.0.0.0그러나 기본 게이트웨이를 제안한 대로 설정하면 Microsoft VPN 클라이언트에서 고정 경로를 허용하지 않는 것 같습니다.Strongswan 문서.

적어도 .NET을 사용할 때 Windows 클라이언트에서 광고된 경로를 찾을 수 없습니다 route print -4.

0.0.0.0또한 VPN 인터페이스를 통해 표준 게이트웨이로 사용하는 경우 Windows 클라이언트에서 수동으로 경로를 추가할 수 없습니다 .

하지만:

VPN을 통해 서브넷에 액세스 하고 VPN 서버가 Windows 클라이언트에 192.168.200.0/24주소를 할당한다고 가정해 보겠습니다. 192.168.201.2/24그런 다음 Windows 명령을 사용하여 192.168.201.2를 통해 서브넷 192.168.200.0/24에 액세스할 수 있다고 선언하여 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이는 단지 두 문자열을 연결한 것이기 때문입니다.

update reply그런 다음 다음 코드와 함께 사용해야 합니다 .

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

경로가 더 있으면 모든 경로가 하나의 긴 문자열로 연결됩니다.

까다로운 부분:

파일 에 있는 Freeradius DHCP 서버의 기본 구성이 있다고 가정합니다 freeradius/3.0/sites-available/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 모듈을 호출해야 합니다. 왜냐하면 Perl 모듈은 VPN 클라이언트에 ipaddress를 할당하는 모듈이기 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
}

이것이 작동하게 하려면 폴더 아래에서 Perl을 활성화 하고 Perl 모듈을 가리키도록 freeradius/3.0/mods-enabled파일 이름을 수정 해야 합니다. freeradius/3.0/mods-enabled/perl예를 들어:

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 구성 파일에서 직접 정적 경로를 정의할 수 있습니다.

관련 정보