VPP和StrongSwan搭建IPSec


1、strongswan+vpp簡介

使用VPP 20.01 版本 + strongswan 5.8.3版本編譯。

目前strongswan+VPP方案主要是使用strongswan的插件機制,替換strongswan的兩個默認插件。

  1. socket-default 該插件是IKE報文的socket backend。
  2. kernel-netlink 該插件是IPSec 數通backend

將默認的socket-default連接替換為VPP的punt socket方式,punt socket會將ike協議報文通過VPP上送到strongswan中,strongswan也會將回應的報文通過punt socket傳輸回vpp,IKE的協商層面是通過strongswan完成。
ike協商完成之后,strongswan通過vpp的C語言 API向VPP下發IPSEC的配置,sa spd 路由等等,下發完成配置之后,VPP的IPSEC隧道就完成了建立。
用strongswan替換VPP自身的IKE功能,是因為VPP本身的IKE只支持IKE V2而且功能的豐富度不如strongswan完善。

已有的開源項目簡介

作者matfabia

https://github.com/matfabia/strongswan/tree/vpp
該項目是strongswan+vpp這個方案的最初的項目,確定了strongswan與vpp結合的大致方向,完成度也比較高,后續的其他開源項目都是在此基礎上修修補補。
該項目在上傳最初代碼后,就停止更新了,代碼基於的VPP版本應該是v18.01左右。

作者mestery

https://github.com/mestery/strongswan
該項目是基於上面原始項目進行修改,支持VPP 的1810版本,進行了小幅度的API適配整體和上面項目相差不大。隨后該項目也停止更新了,但是該項目中有一個pull request比較關鍵,在4500 UDP端口上支持了NAT-T IKE,增加了一些VPP新支持的加密算法例如GCM的支持。但是該pull request並沒有合入到該項目,因為作者可能已經忘記這個項目了。

作者rayshi-10

https://github.com/rayshi-10/Strongswan-Vpp2001
該項目是基於第二個項目做的,而且把第二個項目中pull request合入了進來,支持了VPP后來加入的更多加密和認證算法,而且支持NAT-T IKE。然后支持了VPP v20.01版本。這個版本的代碼修改量還是比較大的。因為VPP v20.01版本API和數據結構的改動是相當大的,大部分原有的IPSEC配置API都發生的變化,進行了多次重構,但是而且設置還刪除了一些配置屬性,導致原有的流程可以需要改動比較大才能適配。
v20.01的VPP ipv4 ipv6的配置需要顯示下發兩條,而以前的版本是使用any屬性標志下發一條就可以了。這部分的改動需要特別關注下,該項目目測這部分可能會有BUG。可以特別關注下該項目的manage_policy函數,例如下面的部分,is_anyaddr的情況只下發了一條policy。可能會出現問題

if (src->is_anyaddr(src) && dst->is_anyaddr(dst))
    {
        memset(mp->entry.local_address_stop.un.ip6, 0xFF, 16);
        memset(mp->entry.remote_address_stop.un.ip6, 0xFF, 16);
    } 

2、基於rayshi-10的代碼和strongswan release5.8.3進行修改

下載源碼

下載strongswan主線代碼,切換到5.8.3分支

git clone https://github.com/strongswan/strongswan.git
git checkout 5.8.3

下載rayshi-10 strongswan + vpp 20.01代碼

git clone https://github.com/rayshi-10/Strongswan-Vpp2001.git

替換文件

將該項目的

src/libcharon/plugins/kernel_vpp/
src/libcharon/plugins/socket_vpp/

兩個目錄替換到strongswan 5.8.3對應目錄下,然后將該項目configure.ac目錄下kernel-vpp socket-vpp相關的內存,添加到strongswan 5.8.3對應的文件里。
注,該項目的configure.ac里面缺少下面兩條配置

ADD_PLUGIN([kernel-vpp],           [c charon])
ADD_PLUGIN([socket-vpp],           [c charon])

需要將這兩條配置自行添加到configure.ac中的合適位置,
例如向下面的方式添加

ADD_PLUGIN([kernel-iph],           [c charon])
ADD_PLUGIN([kernel-vpp],           [c charon])
ADD_PLUGIN([kernel-pfkey],         [c charon starter nm cmd])
ADD_PLUGIN([kernel-pfroute],       [c charon starter nm cmd])
ADD_PLUGIN([kernel-netlink],       [c charon starter nm cmd])
ADD_PLUGIN([resolve],              [c charon cmd])
ADD_PLUGIN([save-keys],            [c])
ADD_PLUGIN([socket-default],       [c charon nm cmd])
ADD_PLUGIN([socket-dynamic],       [c charon cmd])
ADD_PLUGIN([socket-win],           [c charon])
ADD_PLUGIN([socket-vpp],           [c charon])
ADD_PLUGIN([bypass-lan],           [c charon nm cmd])

注意dnssec_status_t的修改

dnssec_status_t枚舉變量在strongswan vpp中進行了重命名,將這個枚舉中的變量全都加了DNSS前綴,可能是因為這個枚舉里面的變量和VPP里面的內容重名了,我們在替換時,如果編譯失敗了,可能是忘記重命名該名稱導致
重命名后的效果如下

enum dnssec_status_t {
    /**
     * The validating resolver has a trust anchor, has a chain of
     * trust, and is able to verify all the signatures in the response.
     * [RFC4033]
     */
    DNSS_SECURE,
    /**
     * The validating resolver has a trust anchor, a chain of
     * trust, and, at some delegation point, signed proof of the
     * non-existence of a DS record.  This indicates that subsequent
     * branches in the tree are provably insecure.  A validating resolver
     * may have a local policy to mark parts of the domain space as
     * insecure. [RFC4033]
     */
    DNSS_INSECURE,
    /**
     * The validating resolver has a trust anchor and a secure
     * delegation indicating that subsidiary data is signed, but the
     * response fails to validate for some reason: missing signatures,
     * expired signatures, signatures with unsupported algorithms, data
     * missing that the relevant NSEC RR says should be present, and so
     * forth. [RFC4033]
     */
    DNSS_BOGUS,
    /**
     * There is no trust anchor that would indicate that a
     * specific portion of the tree is secure.  This is the default
     * operation mode. [RFC4033]
     */
    DNSS_INDETERMINATE,
};

修改PUNT read socket path

在src/libcharon/plugins/socket_vpp/socket_vpp_socket.c中該項目中vpp的punt read path是/tmp目錄,該地址可以自行設定,例如我將該地址進行了下面的修改,和VPP其他unix socket放置在同一目錄。

#define READ_PATH "/var/run/vpp/ike-punt-read.sock"

3、編譯項目

下載依賴

Centos7,使用下面的命令下載編譯中的依賴項。

yum install gperf
yum install python3
yum install gmp
yum install gmp-devel

編譯vpp

git clone https://github.com/FDio/vpp.git
git checkout v20.01
make install-dep
make build-release

將編譯好的VPP安裝到系統中

cp build-root/install-vpp-native/vpp/include/* /usr/include/ -r
cp build-root/install-vpp-native/vpp/lib/* /lib64/ -r
cp build-root/install-vpp-native/vpp/lib/vpp_plugins /lib/ -r
cp build-root/install-vpp-native/vpp/bin/vpp /usr/bin/
cp build-root/install-vpp-native/vpp/bin/vppctl /usr/bin/

 

編譯strongswan

預處理

最新版本的strongswan在centos下可能編譯不過,pkgconfig版本低,缺少PKG_CHECK_VAR
需要在configure.ac前面添加下面的定義

# backwards compat with older pkg-config
# - pull in AC_DEFUN from pkg.m4
m4_ifndef([PKG_CHECK_VAR], [
# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
# -------------------------------------------
# Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])# PKG_CHECK_VAR
])

執行編譯

./autogen.sh
./configure --enable-socket-vpp --enable-kernel-vpp --enable-libipsec --enable-openssl
make -j 8

將編譯好的strongswan安裝到系統中

make install

安裝好的默認目錄是/usr/local/,主要文件和目錄如下所示

/usr/local/bin/pki
/usr/local/sbin/ipsec
/usr/local/sbin/swanctl
/usr/local/sbin/etc/

4、運行測試

搭建方式

我是通過Vmware虛擬機跑了兩個vpp + strongswan的環境,兩者的接口使用vmnet連接。
strongswan+vpp的啟動順序,需要首先啟動VPP,然后配置好接口之后,啟動strongswan,然后啟動協商隧道。

vpp啟動配置

startup.conf

statseg
{
  default
  per-node-counters on
}
socksvr
{
    socket-name /var/run/vpp/vpp-api.sock
}
unix
{
  cli-listen /run/vpp/cli.sock
  log /tmp/vpe.log
  nodaemon
  coredump-size 1M
}
punt
{
    socket /var/run/vpp/ike-punt-write.sock
}
api-trace { on }
heapsize 1G
buffers
{
    buffers-per-numa 40000
}
plugins
{
  plugin dpdk_plugin.so
  {
    enable
  }
}
cpu
{
  # Dynamic Create Option
  main-core 0
  corelist-workers 1
}
dpdk
{
  log-level debug
  huge-dir /dev/hugepages
  no-tx-checksum-offload
  #vdev crypto_aesni_mb
  dev 0000:02:05.0 { name eth1 }
  dev 0000:02:06.0 { name eth2 }
}

上面的配置CPU部分需要根據自己的環境編寫,綁定工作線程和主線程到某些CPU核。dpdk部分的接口PCI號,也需要根據實際的情況填寫,上面的配置ipsec加解密使用了openssl的能力,沒有使用dpdk的加解密套件,使用dpdk加解密套件請看最后一節。
上面配置中比較重要的一點是punt這一部分,該配置必須填寫。strongswan使用到了兩個punt socket,其中一個是VPP startup.conf中指定,是write socket,strongswan寫報文使用該unix socket。還有一個是punt讀接口,該unix socket在strongswan的socket-vpp插件中啟動時,動態向vpp注冊,接口的路徑在代碼中寫死。上面已經說過這個問題了。
dev 0000:02:05.0 { name G1/1 }中02:05:0通過lspci查詢網卡信息獲得。,G1/1僅是別名,后續vppctl指令設置時使用。dev 0000:02:06.0同理。需要額外注意的是,corelist-workers的值需要視情況而定,可以適當增加虛擬機的核數或減少該值。
heapsize也許關注。

使用vdev crypto_aesni_mb方式,若/var/log/messages下報錯如下:

crypto_create_session_drv_pool: failed to create session drv mempool

則可能是DPDK大頁內存不足引起。擴大DPDK的大頁內存數:

cd /boot/grub2/
grubby --update-kernel=ALL --args="default_hugepagesz=2M hugepagesz=2M hugepages=512"
reboot

vpp運行配置

VPP成功啟動后,需要配置接口的IP信息,這一部分信息就根據測試例的拓撲來配置就可以。
下面是我的環境中site-to-site中配置CLI命令。
moon的配置

vppctl set int state eth1 up
vppctl set int state eth2 up
vppctl set int state local0 up
vppctl set int ip addr eth1 192.168.0.3/24
vppctl set int ip addr eth2 10.1.0.3/16

sun的配置

vppctl set int state eth1 up
vppctl set int state eth2 up
vppctl set int state local0 up
vppctl set int ip addr eth1 192.168.0.2/24
vppctl set int ip addr eth2 10.1.0.2/16

若配置時,無法找到eth1和eth2設備,則可能需要編譯DPDK加載相關驅動。

DPDK編譯后(我選擇的x86_64-native-linux-gcc),需要ifdown掉相關網卡,進行網卡綁定。

cd x86_64-native-linux-gcc/kmod/
modprobe uio_pci_generic
modprobe uio
insmod igb_uio.ko
./dpdk-devbind.py -b igb_uio 02:05.0 ./dpdk-devbind.py -b igb_uio 02:06.0

配置strongswan

開啟vpp插件

在進行配置之前,需要先啟用我們的kernel-vpp和socket-vpp插件。首先我們將

/usr/local/etc/strongswan.d/charon/kernel-netlink.conf 
/usr/local/etc/strongswan.d/charon/socket-default.conf 

兩個默認插件的內容修改一下將默認加載變成不加載
load = no
然后將我們新增的兩個插件加載狀態變為yes

/usr/local/etc/strongswan.d/charon/socket-vpp.conf 
/usr/local/etc/strongswan.d/charon/kernel-vpp.conf

修改為load = yes
strongswan新版本,我們配置的內容主要是/usr/local/etc/swanctl/swanctl.conf文件,具體的場景和配置可以參考上面給出的官方測試例的配置。
注意:按上述編譯strongswan后,未生成/usr/local/etc/strongswan.d/charon/socket-vpp.conf 和/usr/local/etc/strongswan.d/charon/kernel-vpp.conf。
通過對比strongswan和Strongswan-Vpp2001的configue.ac,作出修改后,重新編譯才生成上述兩個文件。

site-to-site配置

| 192.168.0.3 | === | 192.168.0.2 | moon                 sun         
site1 moon配置

swanctl配置:/usr/local/etc/swanctl/swanctl.conf

connections {

   host-host { local_addrs = 192.168.0.3 remote_addrs = 192.168.0.2 local { auth = psk id = moon.strongswan.org } remote { auth = psk id = sun.strongswan.org } children { host-host { local_ts = dynamic[udp/81-65535] remote_ts = 192.168.0.0/24[udp/8887-8888] updown = /usr/local/libexec/ipsec/_updown iptables esp_proposals = aes128-sha256-x25519 } } version = 2 mobike = no proposals = aes128-sha256-x25519 } } secrets { ike-host-host { id = sun.strongswan.org secret = simplepsk } }
site1 sun配置

swanctl配置:/usr/local/etc/swanctl/swanctl.conf

connections {

   host-host { local_addrs = 192.168.0.2 remote_addrs = 192.168.0.3 local { auth = psk id = sun.strongswan.org } remote { auth = psk id = moon.strongswan.org } children { host-host { local_ts = dynamic[udp/8887-8888] remote_ts = 192.168.0.0/24[udp/81-65535] updown = /usr/local/libexec/ipsec/_updown iptables esp_proposals = aes128-sha256-x25519 } } version = 2 mobike = no proposals = aes128-sha256-x25519 } } secrets { ike-host-host { id = moon.strongswan.org secret = simplepsk } }
開始運行

首先啟動VPP,配置好strongswan的配置和VPP的配置,然后兩端都使用systemctl start strongswan-starter.service啟動strongswan
可以使用swanctl --stats命令查看一下vpp的插件加載是否正確,在/var/log/messages文件中查看日志是否有報錯等等。
然后查看一下VPP端,strongswan是否已經建立好了連接,如果建立成功之后,vpp中應該會有如下的顯示:

[root@localhost centos126]# vppctl show api clients
Shared memory clients
                Name      PID   Queue Length           Queue VA Health
          strongswan    10488              0 0x00000001301ce9c0 OK
[root@localhost centos126]# vppctl show udp punt
IPV4 UDP ports punt : 500, 4500
IPV6 UDP ports punt : 500, 4500

注意:我在執行systemctl start strongswan-starter.service后,查看/var/log/messages,發現報錯如下:

Jul 28 23:12:58 localhost charon: 00[LIB] feature CUSTOM:libcharon-receiver in critical plugin 'charon' has unmet dependency: CUSTOM:socket 
Jul 28 23:12:58 localhost charon: 00[LIB] feature CUSTOM:libcharon in critical plugin 'charon' has unmet dependency: CUSTOM:libcharon-receiver

依據博主冰封飛飛的提示,我也懷疑socket-vpp未編譯進來,但是strongswan編譯未報錯,而且已經生成了相應插件的配置文件。於是,我編譯了Strongswan-Vpp2001源碼,但是編譯報錯,socket-vpp插件編譯失敗。通過find指令找到所引頭文件位置,執行如下指令后再次編譯成功。

export C_INCLUDE_PATH=/home/centos119/Ipsec/vpp/src/

其中/home/centos119/Ipsec/vpp/src/是,依賴頭文件所屬父目錄。

在兩端執行swanctl --load-all加載所有的配置和證書。
在協商的發起端執行初始化命令,這個net-net是根據當前的swanctl.conf配置文件中children字段里面的內容填寫的。

swanctl --load-all
swanctl --initiate --child host-host

查看日志/var/log/messages是否成功。

最終,重新編譯strongswan目錄(執行至make install),並單獨編譯socket_vpp和kernel_vpp目錄,執行make; make install。使用5.8.3版本,和上述配置,連接成功。

[root@localhost Tools]# swanctl --list-conns
host-host: IKEv2, no reauthentication, rekeying every 14400s
  local:  192.168.0.2
  remote: 192.168.0.3
  local pre-shared key authentication:
    id: sun.strongswan.org
  remote pre-shared key authentication:
    id: moon.strongswan.org
  host-host: TUNNEL, rekeying every 3600s
    local:  dynamic[udp/8887-8888]
    remote: 192.168.0.0/24[udp/81-65535]
[root@localhost Tools]# vppctl show ipsec all
[0] sa 1 (0x1) spi 2938187978 (0xaf2130ca) protocol:esp flags:[tunnel ]
[1] sa 2 (0x2) spi 955807424 (0x38f876c0) protocol:esp flags:[tunnel ]
spd 1
 ip4-outbound:
   [1] priority 2147483647 action bypass type ip4-outbound protocol IPSEC_AH
     local addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [3] priority 2147483647 action bypass type ip4-outbound protocol IPSEC_ESP
     local addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [5] priority 2147483647 action bypass type ip4-outbound protocol UDP
     local addr range 0.0.0.0 - 255.255.255.255 port range 500 - 500
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [7] priority 2147483647 action bypass type ip4-outbound protocol UDP
     local addr range 0.0.0.0 - 255.255.255.255 port range 4500 - 4500
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [10] priority 2147480829 action protect type ip4-outbound protocol UDP sa 2
     local addr range 192.168.0.2 - 192.168.0.2 port range 8887 - 8888
     remote addr range 192.168.0.3 - 192.168.0.3 port range 81 - 65535
     packets 0 bytes 0
ip6-outbound:
 ip4-inbound-protect:
   [8] priority 2147480829 action protect type ip4-inbound-protect protocol UDP sa 1
     local addr range 192.168.0.2 - 192.168.0.2 port range 81 - 65535
     remote addr range 192.168.0.3 - 192.168.0.3 port range 8887 - 8888
     packets 0 bytes 0
   [9] priority 2147480829 action protect type ip4-inbound-protect protocol UDP sa 1
     local addr range 192.168.0.2 - 192.168.0.2 port range 81 - 65535
     remote addr range 192.168.0.3 - 192.168.0.3 port range 8887 - 8888
     packets 0 bytes 0
 ip6-inbound-protect:
 ip4-inbound-bypass:
   [0] priority 2147483647 action bypass type ip4-inbound-bypass protocol IPSEC_AH
     local addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [2] priority 2147483647 action bypass type ip4-inbound-bypass protocol IPSEC_ESP
     local addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [4] priority 2147483647 action bypass type ip4-inbound-bypass protocol UDP
     local addr range 0.0.0.0 - 255.255.255.255 port range 500 - 500
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
   [6] priority 2147483647 action bypass type ip4-inbound-bypass protocol UDP
     local addr range 0.0.0.0 - 255.255.255.255 port range 4500 - 4500
     remote addr range 0.0.0.0 - 255.255.255.255 port range 0 - 65535
     packets 0 bytes 0
 ip6-inbound-bypass:
SPD Bindings:
  1 -> eth1

參考鏈接:https://blog.csdn.net/a363344923/article/details/105417015


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM