2020년 4월 28일 화요일

ESPRESSObin 보드에 WireGuard VPN 올리기

이번 posting에서는 ESPRESSObin board(Layer 3 Gateway)에 WireGuard VPN을 올리는 과정을 소개하고자 한다. ESPRESSObin board와 WireGuard VPN을 선택한 이유는 이 둘이 암호 통신이 가능한 소형 Security Gateway를 만들기에 아주 적합한 조합이기 때문이다. Security Gateway 개발은 사실 본 blogger가 가장 관심을 가지고 있는 분야 중 하나다.






목차
1. ESPRESSObin 보드 소개
2. ESPRESSObin board 구동시키기
3. WireGuard VPN 소개
4. WireGuard VPN build 및 동작 시험하기
5. TODO
6. References


1. ESPRESSObin 보드 소개
ESPRESSObin은 Marvell Armada 3700LP (88F3720) dual core ARM Cortex A53 processorTopaz Networking Switch가 장착되어 있어, 성능 좋은 Security Gateway를 만들기에 아주 적합한 보드이다. Board revision도 v3 -> v5 -> v7 까지 개발/진행되어 있는 걸 보면, "보드가 우수하다"는 것은 본 blogger만의 생각은 아닌 것 같다. 이번 posting에서는 편의상 (현재 소유하고 있는)revision v5 보드를 기준으로 내용 전개를 해 보도록 하겠다.
[그림 1.1] ESPRESSObin board v5 외관(1)

[그림 1.2] ESPRESSObin board v7 외관(2)

[그림 1.3] ESPRESSObin 주요 스펙


[그림 1.4] Marvell Armada SoC 88F3720

[그림 1.5] ESPRESSObin Topaz Switch(88E6141)

[Tip] ESPRESSObin board는 LAN과 WAN port를 모두 Topaz Switch의 4 port 중에서 선택해서 사용하고 있다. 즉, 위의 그림에서는 WAN port가 CPU(SoC)에서 직접 연결(SoC 내의 ethernet controller)되어 있으나, 사실은 그렇지 않다는 뜻이다. 이는 아래 그림 1.6 ~ 1.7의 block diagram을 살펴보면 그 사실을 확인할 수 있다.

ESPRESSObin 보드의 block diagram을 확인해 보면 다음과 같다.

[그림 1.6] ESPRESSObin board v3/v5 Block Diagram


[그림 1.7] ESPRESSObin board v7 Block Diagram


다음으로, 이번 posting에서는 크게 다루지 않겠지만, ESPRESSObin 보드의 확장 핀 map(v3/v5)을 살펴 보면 다음과 같다.

[그림 1.8] ESPRESSObin board v3/v5 GPIO pinmap

[Tip] 추후 다른 blog posting을 통해서 위의 확장 pin을 사용하는 예(driver 예제)를 다뤄 보도록 하겠다.

지금까지 수박 겉핡기 식으로 ESPRESSObin 보드의 개략적인 모습을 살펴 보았다. 따라서 ESPRESSObin 보드 관련 보다 자세한 사항을 원하시는 분들은 아래 ESPRESSObin 보드 home page를 참조해 주기 바란다. 참으로 정리가 잘 되어 있다.


2. ESPRESSObin board 구동시키기
이 장에서는 ESPRESSObin 보드를 구동시키는 절차를 소개해 보고자 한다. 보드 구동의 전체적인 절차는 다음과 같다.

<보드 구동 절차>
1. AArch64(ARM64)용 toolchain 설치 및 환경 설정하기
2. Kernel download 및 build 하기
3. u-boot bootloader download 및 build 하기
4. Ubuntu rootfs 이미지(prebuilt image 파일) download 및 수정하기
   -> OpenWrt, Yocto project 등도 활용해 볼 수 있겠지만, 여기에서는 가장 간편한 Ubuntu를 사용하도록 하자.
5. 부팅용 MicroSD 만들기
6. u-boot 설정 변경 후 부팅하기
7. 부팅 후, 네트워크 설정하기 : Gateway로 만들기

지금부터 설명할 내용은 아래 espressobin wiki page의 내용을 기초로 하였고, 꼭 필요한 내용만을 중심으로 간략히 정리하였다. 따라서 보다 구체적인 내용 설명이 필요한 분들은 아래 site의 내용을 반드시 참조해 보시기 바란다.

[Tip] bootloader, kernel, device tree, rootfs 등과 관련해서는 이미 본 blog에서 여러 차례 소개한 바 있으므로, 이번 posting에서는 관련 내용을 재차 설명하지는 않을 생각이다.

<1. toolchain 설치하기>
$ mkdir toolchain; cd toolchain

$ wget https://releases.linaro.org/components/toolchain/binaries/5.2-2015.11-2/aarch64-linux-gnu/gcc-linaro-5.2-2015.11-2-x86_64_aarch64-linux-gnu.tar.xz

$ tar -xvf ./gcc-linaro-5.2-2015.11-2-x86_64_aarch64-linux-gnu.tar.xz

$ export PATH=/home/chyi/workspace/ESPRESSObin/toolchain/gcc-linaro-5.2-2015.11-2-x86_64_aarch64-linux-gnu/bin:$PATH


<2. kernel 4.4.52 download & build 하기>
$ mkdir -p kernel/4.4.52; cd mkdir -p kernel/4.4.52
$ git clone https://github.com/MarvellEmbeddedProcessors/linux-marvell .
'.'에 복제합니다...
remote: Enumerating objects: 5827506, done.
remote: Total 5827506 (delta 0), reused 0 (delta 0), pack-reused 5827506
오브젝트를 받는 중: 100% (5827506/5827506), 1.19 GiB | 15.35 MiB/s, 완료.
델타를 알아내는 중: 100% (4864889/4864889), 완료.
파일을 가져옵니다: 100% (43814/43814), 완료.

$ git branch
* linux-3.10.70-15t1

$ git checkout 6adee55d3e07e3cc99ec6248719aac042e58c5e6 -b espressobin-v7
파일을 가져옵니다: 100% (44339/44339), 완료.
새로 만든 'espressobin-v7' 브랜치로 전환합니다

[Tip] 현재 v5 board를 사용하고 있으나, 코드는 v7용으로 받도록 한다. backward compatible하게 porting이 되어 있기 때문이다.

$ git branch
* espressobin-v7
  linux-3.10.70-15t1

=> download ebin_v7_kernel_patches.zip

$ mkdir patch
$ mv download ebin_v7_kernel_patches.zip ./patch
$ cd patch
$ unzip ./download ebin_v7_kernel_patches.zip
$ cd ..

$ git am ./patch/*.patch
적용하는 중: dts: espressobin: add emmc support
적용하는 중: configs: add espressobin network config for hw_v6
적용하는 중: fix: regulator: armada-37xx: overwrite CPU voltage values in 1000MHZ
적용하는 중: dts: add new dts armada-3720-community-v5.dts
적용하는 중: config: enable bluetooth support
적용하는 중: dts: add new espressobin dts for hw rev.7
적용하는 중: btusb: disable realtek bluetooth support
적용하는 중: config: update espressobin defconfig


$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-

$ make mvebu_v8_lsp_defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  SHIPPED scripts/kconfig/zconf.tab.c
  SHIPPED scripts/kconfig/zconf.lex.c
  SHIPPED scripts/kconfig/zconf.hash.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#

$ make menuconfig

$ make -j4
...
...
  UPD     include/generated/compile.h
  CC      init/version.o
  LD      init/built-in.o
  KSYM    .tmp_kallsyms1.o
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm64/boot/Image
  Building modules, stage 2.
  MODPOST 11 modules
  GZIP    arch/arm64/boot/Image.gz
  CC      crypto/drbg.mod.o
  CC      crypto/echainiv.mod.o
  CC      crypto/hmac.mod.o
  CC      crypto/jitterentropy_rng.mod.o
  CC      crypto/sha1_generic.mod.o
  CC      crypto/sha256_generic.mod.o
  CC      crypto/tcrypt.mod.o
  CC      drivers/crypto/inside-secure/crypto_safexcel.mod.o
  CC      drivers/usb/gadget/function/usb_f_mass_storage.mod.o
  CC      drivers/usb/gadget/legacy/g_mass_storage.mod.o
  CC      drivers/usb/gadget/libcomposite.mod.o
  LD [M]  crypto/drbg.ko
  LD [M]  crypto/echainiv.ko
  LD [M]  crypto/hmac.ko
  LD [M]  crypto/jitterentropy_rng.ko
  LD [M]  crypto/sha1_generic.ko
  LD [M]  crypto/sha256_generic.ko
  LD [M]  crypto/tcrypt.ko
  LD [M]  drivers/crypto/inside-secure/crypto_safexcel.ko
  LD [M]  drivers/usb/gadget/function/usb_f_mass_storage.ko
  LD [M]  drivers/usb/gadget/legacy/g_mass_storage.ko
  LD [M]  drivers/usb/gadget/libcomposite.ko

[Tip] kernel build 결과물은 arch/arm64/boot/Image  파일과 arch/arm64/boot/dts/marvell/armada-3720-community*.dtb 파일이다.

<3. bootloader download & build 하기>
$ mkdir bootloader; cd bootloader/
$ git clone https://github.com/MarvellEmbeddedProcessors/u-boot-marvell.git
'u-boot-marvell'에 복제합니다...
remote: Enumerating objects: 594496, done.
remote: Total 594496 (delta 0), reused 0 (delta 0), pack-reused 594496
오브젝트를 받는 중: 100% (594496/594496), 223.46 MiB | 16.81 MiB/s, 완료.
델타를 알아내는 중: 100% (481989/481989), 완료.

$ cd u-boot-marvell/

$ git checkout 6a6581a21ec5d6405f30fd41ee5040d64893651b -b espressobin-v7
새로 만든 'espressobin-v7' 브랜치로 전환합니다

$ git branch
* espressobin-v7
  u-boot-2013.01-15t1

=> download ebin_v7_u-boot_patches.zip

$ mdir patch
$ mv download ebin_v7_u-boot_patches.zip ./patch
$ cd patch
$ unzip ./ebin_v7_u-boot_patches.zip
$ cd ..

$ git am ./patch/*.patch
적용하는 중: git: add some temporary files into git ignore list
적용하는 중: dts: espressobin: add emmc device support
적용하는 중: mtd: add issi is25wp032d spi flash support
적용하는 중: mtd: add macronix mx25u3235f spi flash support
적용하는 중: mtd: add gigadevice gd25lq32d spi flash support

$ make mvebu_espressobin-88f3720_defconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  SHIPPED scripts/kconfig/zconf.tab.c
  SHIPPED scripts/kconfig/zconf.lex.c
  SHIPPED scripts/kconfig/zconf.hash.c
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTLD  scripts/kconfig/conf
#
# configuration written to .config
#

$ sudo apt-get install device-tree-compiler


$ make DEVICE_TREE=armada-3720-espressobin
...
...
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-master/config-space/efuse-1@400F00 simple-bus unit address format error, expected "400008"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-master/config-space/scsi_0 missing or empty reg/ranges property
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space simple-bus unit address format error, expected "f4000000"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space/sar-reg simple-bus unit address format error, expected "400200"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space/efuse-2@400F00 simple-bus unit address format error, expected "400008"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space/efuse-3@400F00 simple-bus unit address format error, expected "400008"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space/scsi_1 missing or empty reg/ranges property
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /cp110-slave/config-space/mci1 simple-bus unit address format error, expected "1c3000"
arch/arm/dts/armada-8040-ocp.dtb: Warning (simple_bus_reg): Node /simple-bus/usb3-vbus0 missing or empty reg/ranges property
  SHIPPED dts/dt.dtb
  CAT     u-boot-dtb.bin
  COPY    u-boot.bin
  SYM     u-boot.sym
  COPY    u-boot.dtb
  CFGCHK  u-boot.cfg

[Tip] bootloader(u-boot)를 build해 보기는 했으나, 이번 blog에서는 실제 u-boot image(u-boot.bin)를 board에  write하지는 않을 생각이다. 즉, 보드에 이미 포함되어 있는 u-boot를 교체하지는 않을 것이다.

<4. ubuntu 16.04 iso 파일 download 및 수정하기>
$ mkdir ubuntu16.04
$ cd ubuntu16.04/

$ wget http://cdimage.ubuntu.com/releases/16.04.5/release/ubuntu-16.04.4-server-arm64.iso

$ mkdir tmp
$ sudo mount -o loop ubuntu-16.04.4-server-arm64.iso ./tmp
mount: /home/chyi/workspace/ESPRESSObin/ubuntu16.04/tmp: WARNING: device write-protected, mounted read-only.

$ cd tmp/
$ ls -la
합계 224
dr-xr-xr-x 1 root root   2048  3월  1  2018 .
drwxr-xr-x 3 chyi chyi   4096  4월 26 16:22 ..
dr-xr-xr-x 1 root root   2048  3월  1  2018 .disk
-r--r--r-- 1 root root    239  3월  1  2018 README.diskdefines
dr-xr-xr-x 1 root root   2048  2월 28  2018 boot
dr-xr-xr-x 1 root root   2048  3월  1  2018 dists
dr-xr-xr-x 1 root root   2048  3월  1  2018 doc
dr-xr-xr-x 1 root root   2048  3월  1  2018 install
-r--r--r-- 1 root root 206106  3월  1  2018 md5sum.txt
dr-xr-xr-x 1 root root   2048  3월  1  2018 pics
dr-xr-xr-x 1 root root   2048  3월  1  2018 pool
dr-xr-xr-x 1 root root   2048  3월  1  2018 preseed
lr-xr-xr-x 1 root root      1  3월  1  2018 ubuntu -> .
lr-xr-xr-x 1 root root      1  3월  1  2018 ubuntu-ports -> .

$ sudo unsquashfs -d rootfs/ tmp/install/filesystem.squashfs
Parallel unsquashfs: Using 8 processors
11378 inodes (12142 blocks) to write

[===================================================================================-] 12142/12142 100%

created 9489 files
created 1130 directories
created 1805 symlinks
created 79 devices
created 0 fifos

$ cd rootfs
$ ls -la
합계 80
drwxr-xr-x 20 root root 4096  3월  1  2018 .
drwxr-xr-x  4 chyi chyi 4096  4월 26 16:26 ..
drwxr-xr-x  2 root root 4096  3월  1  2018 bin
drwxr-xr-x  2 root root 4096  4월 13  2016 boot
drwxr-xr-x  4 root root 4096  3월  1  2018 dev
drwxr-xr-x 63 root root 4096  3월  1  2018 etc
drwxr-xr-x  2 root root 4096  4월 13  2016 home
drwxr-xr-x 11 root root 4096  3월  1  2018 lib
drwxr-xr-x  2 root root 4096  3월  1  2018 media
drwxr-xr-x  2 root root 4096  3월  1  2018 mnt
drwxr-xr-x  2 root root 4096  3월  1  2018 opt
drwxr-xr-x  2 root root 4096  4월 13  2016 proc
drwx------  2 root root 4096  3월  1  2018 root
drwxr-xr-x  7 root root 4096  3월  1  2018 run
drwxr-xr-x  2 root root 4096  3월  1  2018 sbin
drwxr-xr-x  2 root root 4096  3월  1  2018 srv
drwxr-xr-x  2 root root 4096  2월  5  2016 sys
drwxrwxrwt  2 root root 4096  3월  1  2018 tmp
drwxr-xr-x 10 root root 4096  3월  1  2018 usr
drwxr-xr-x 11 root root 4096  3월  1  2018 var

$ sudo vi passwd
   => root password 제거

$ sudo vi securetty
   => console 출력을 위해 파일 맨 마지막 줄에 ttyMV0 추가

<kernel directory로 이동하여 Image, dtb 파일을 boot/ 디렉토리로 복사>
$ sudo cp ./Image /home/chyi/workspace/ESPRESSObin/ubuntu16.04/rootfs/boot
$ sudo cp armada-3720-community.dtb /home/chyi/workspace/ESPRESSObin/ubuntu16.04/rootfs/boot

$ sudo tar -cjvf rootfs.tar.bz2 -C ./rootfs/ .
   => rootfs.tar.bz2 생성


<5. booting용 microSD 만들기>
아래 microSD to USB converter를 이용하여 Linux PC에 연결하도록 한다.
[그림 2.1] TF/SDCard to USB converter

$ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
...
...
loop24   7:24   0  14.8M  1 loop /snap/gnome-characters/399
sda      8:0    0 953.9G  0 disk 
├─sda1   8:1    0   512M  0 part /boot/efi
└─sda2   8:2    0 953.4G  0 part /
sdb      8:16   1  14.9G  0 disk 
└─sdb1   8:17   1  14.9G  0 part /media/chyi/2bf1a8eb-4de9-4869-8844-54056cb6c84f
   => /dev/sdb로 인식되었다.

$ sudo umount /dev/sdb1
$ sudo dd if=/dev/zero of=/dev/sdb bs=1M count=100
   => /dev/sdb를 format 한다. [주의] device 명을 잘못 입력하게 되면 기존 hdd가 format될 수도 있으니, 다시한번 확인하도록 하자.
100+0 레코드 들어옴
100+0 레코드 나감
104857600 bytes (105 MB, 100 MiB) copied, 12.4722 s, 8.4 MB/s

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.

Created a new DOS disklabel with disk identifier 0x06af93cb.

Command (m for help): p

Disk /dev/sdb: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x06af93cb

Command (m for help): n

Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):  

Using default response p.

Partition number (1-4, default 1):  <Enter>
First sector (2048-31116287, default 2048):  <Enter>
Last sector, +sectors or +size{K,M,G,T,P} (2048-31116287, default 31116287): 

Created a new partition 1 of type 'Linux' and of size 14.9 GiB.


Command (m for help): wr

The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

$ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p

Disk /dev/sdb: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x06af93cb

Device     Boot Start      End  Sectors  Size Id Type

/dev/sdb1        2048 31116287 31114240 14.9G 83 Linux

Command (m for help): q


chyi@earth:/$ sudo mkfs.ext4 /dev/sdb1

mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 3889280 4k blocks and 972944 inodes
Filesystem UUID: 033dde1a-4a91-42f0-967b-7cb70406e47c
Superblock backups stored on blocks: 
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

Allocating group tables: done                            

Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

$ cd /mnt/
$ ls -la
합계 8
drwxr-xr-x  2 root root 4096  2월 10  2019 .
drwxr-xr-x 26 root root 4096  4월 12 14:40 ..
chyi@earth:/mnt$ sudo mkdir sdcard
chyi@earth:/mnt$ sudo mount /dev/sdb1 /mnt/sdcard

$ cd /mnt/sdcard/
$ sudo tar -xvf /home/chyi/workspace/ESPRESSObin/ubuntu16.04/rootfs.tar.bz2

$ ls -la
합계 96
drwxr-xr-x 21 root root  4096  3월  1  2018 .
drwxr-xr-x  3 root root  4096  4월 26 17:32 ..
drwxr-xr-x  2 root root  4096  3월  1  2018 bin
drwxr-xr-x  2 root root  4096  4월 26 16:30 boot
drwxr-xr-x  4 root root  4096  3월  1  2018 dev
drwxr-xr-x 63 root root  4096  4월 26 16:28 etc
drwxr-xr-x  2 root root  4096  4월 13  2016 home
drwxr-xr-x 11 root root  4096  3월  1  2018 lib
drwx------  2 root root 16384  4월 26 17:31 lost+found
drwxr-xr-x  2 root root  4096  3월  1  2018 media
drwxr-xr-x  2 root root  4096  3월  1  2018 mnt
drwxr-xr-x  2 root root  4096  3월  1  2018 opt
drwxr-xr-x  2 root root  4096  4월 13  2016 proc
drwx------  2 root root  4096  3월  1  2018 root
drwxr-xr-x  7 root root  4096  3월  1  2018 run
drwxr-xr-x  2 root root  4096  3월  1  2018 sbin
drwxr-xr-x  2 root root  4096  3월  1  2018 srv
drwxr-xr-x  2 root root  4096  2월  5  2016 sys
drwxrwxrwt  2 root root  4096  3월  1  2018 tmp
drwxr-xr-x 10 root root  4096  3월  1  2018 usr
drwxr-xr-x 11 root root  4096  3월  1  2018 var

$ cd /
$ sudo umount /mnt/sdcard


<6. u-boot config 설정 변경 후 부팅하기>
=> 주의: u-boot parameter 설정을 잘 못할 경우, 장비가 부팅되지 않을 수 있으니 주의 요망 !
QS GATING
============= Calibration done: cycle = 0x00 tap =0x5F CH0_PHY_RL_Control_CS1_B0[0xC00011A4]: 0x0000005F CH0_PHY_RL_Control_CS1_B1[0xC00011A8]: 0x0000005F DLL TUNING ============== DLL 0xc0001050[21:16]: [0,2f,17] DLL 0xc0001050[29:24]: [1,2f,18] DLL 0xc0001054[21:16]: [0,2a,15] DLL 0xc0001054[29:24]: [b,33,1f] DLL 0xc0001074[21:16]: [0,3f,1f] DLL 0xc0001074NOTICE: Booting Trusted Firmware NOTICE: BL1: v1.3(release):armada-17.10.3:39a62a1 NOTICE: BL1: Built : 15:39:52, Jun 1 2NOTICE: BL2: v1.3(release):armada-17.10.3:39a621 NOTICE: BL2: Built : 15:39:54, Jun 1 20NOTICE: BL31: v1.3(release):armada-17.10.3:39a1 NOTICE: BL31: U-Boot 2017.03-armada-17.10.2-g14aeedc (Jun 01 2018 - 15:39:10 +0800) Model: Marvell Armada 3720 Community Board ESPRESSOBin CPU @ 1000 [MHz] L2 @ 800 [MHz] TClock @ 200 [MHz] DDR @ 800 [MHz] DRAM: 1 GiB U-Boot DT blob at : 000000003f7161b8 Comphy-0: USB3 5 Gbps Comphy-1: PEX0 2.5 Gbps Comphy-2: SATA0 6 Gbps SATA link 0 timeout. AHCI 0001.0300 32 slots 1 ports 6 Gbps 0x1 impl SATA mode flags: ncq led only pmp fbss pio slum part sxs PCIE-0: Link down MMC: sdhci@d0000: 0, sdhci@d8000: 1 SF: Detected mx25u3235f with page size 256 Bytes, erase size 64 KiB, total 4 MiB Net: eth0: neta@30000 [PRIME] Hit any key to stop autoboot: 0
<= 부팅 중 <Enter>를 입력하면 u-boot 상태로 빠짐. Marvell>> Marvell>>

Marvell>> ?
=> 사용 가능한 명령어 출력됨.


Marvell>> ext4ls mmc 0:1
=> mmc(여기서는 microSD) 파티션 내용 출력(ext4 file system ls)
<DIR>       4096 .
<DIR>       4096 ..
<DIR>      16384 lost+found
<DIR>       4096 sys
<DIR>       4096 lib
<DIR>       4096 opt
<DIR>       4096 bin
<DIR>       4096 root
<DIR>       4096 etc
<DIR>       4096 srv
<DIR>       4096 var
<DIR>       4096 media
<DIR>       4096 usr
<DIR>       4096 run
<DIR>       4096 home
<DIR>       4096 boot
<DIR>       4096 dev
<DIR>       4096 tmp
<DIR>       4096 mnt
<DIR>       4096 proc
<DIR>       4096 sbin
Marvell>>
Marvell>> ext4ls mmc 0:1 boot
<DIR>       4096 .
<DIR>       4096 ..
        12341760 Image
            8175 armada-3720-community.dtb
Marvell>> 


Marvell>> print
baudrate=115200
bootcmd=mmc dev 0; ext4load mmc 0:1 $kernel_addr $image_name;ext4load mmc 0:1 $fdt_addr r
bootdelay=3
bootmmc=mmc dev 0; ext4load mmc 0:1 $kernel_addr $image_name;ext4load mmc 0:1 $fdt_addr r   <= 수정 대상
<= mmc 장치(여기서는 microSD)로 부터 부팅 
console=console=ttyMV0,115200 earlycon=ar3700_uart,0xd0012000
eth1addr=00:00:00:00:51:82
eth2addr=00:00:00:00:51:83
ethact=neta0
ethaddr=F0:AD:4E:03:86:05
ethprime=egiga0
fdt_addr=0x1800000
fdt_high=0xffffffffffffffff
fdt_name=boot/armada-3720-community.dtb          <= device tree blob 수정 대상 
gatewayip=10.4.50.254
get_images=mmc dev 0; fatload mmc 0 $kernel_addr $image_name; fatload mmc 0 $fdt_addr $fs
get_ramfs=if test "${ramfs_name}" != "-"; then setenv ramfs_addr 0x3000000; tftp $ramfs_i
hostname=marvell
image_name=boot/Image          <= kernel image 수정 대상 
initrd_addr=0xa00000
initrd_size=0x2000000
ipaddr=192.168.1.1
kernel_addr=0x2000000
loadaddr=0x2000000
loads_echo=0
netdev=eth0
netmask=255.255.255.0
ramfs_addr=0x3000000
ramfs_name=-
root=root=/dev/mmcblk0p2 rw
rootpath=/srv/nfs/
serverip=192.168.1.100
set_bootargs=setenv bootargs $console $root ip=$ipaddr:$serverip:$gatewayip:$netmask:$hos
stderr=serial
stdin=serial
stdout=serial


Environment size: 1510/65532 bytes
Marvell>>


Marvell>> setenv image_name boot/Image
Marvell>> setenv fdt_name boot/armada-3720-community.dtb

Marvell>> setenv fdt_addr 0x1800000
=> 주의: 이걸 해 주어야 정상 부팅 된다.


Marvell>> setenv bootmmc 'mmc dev 0; ext4load mmc 0:1 $kernel_addr $image_name;ext4load mmc 0:1 $fdt_addr $fdt_name;setenv bootargs $console root=/dev/mmcblk0p1 rw rootwait; booti $kernel_addr - $fdt_addr'

Marvell>> setenv bootcmd 'mmc dev 0; ext4load mmc 0:1 $kernel_addr $image_name;ext4load mmc 0:1 $fdt_addr $fdt_name;setenv bootargs $console root=/dev/mmcblk0p1 rw rootwait; booti $kernel_addr - $fdt_addr'

Marvell>> saveenv
=> 수정 내용 저장


Marvell>> reset
=> bootcmd를 기준으로 부팅함.

이제 모든 것이 준비되었으니 부팅을 시도해 보도록 하자.


[그림 2.2] ESPRESSObin 보드 상에서 Ubuntu 부팅 모습(minicom - 115200, 8N1)


<7. 네트워크 설정하기 - Gateway로 만들기>
아래 page 내용을 참조하여 network 설정을 해 보도록 하자.





[그림 2.3] 동작중인 ESPRESSObin 보드

우선, NAT, packet filter 관련 설정이 kernel config에 포함되어 있지 않으니, 이를 추가해 주도록 하자.

<kernel config 조정 후, 재 build>
CONFIG_NETFILTER=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_IPV4=y
CONFIG_NF_NAT_IPV4=y
CONFIG_NF_NAT_MASQUERADE_IPV4=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
CONFIG_IP_NF_FILTER=7
[Tip] 이 밖에도 추가로 필요한 netfilter 설정이 있다면 설정을 해 주기 바란다.

$ make -j4
...
...
  LD      vmlinux.o
  MODPOST vmlinux.o
  GEN     .version
  CHK     include/generated/compile.h
  UPD     include/generated/compile.h
  CC      init/version.o
  LD      init/built-in.o
  KSYM    .tmp_kallsyms1.o
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm64/boot/Image
  Building modules, stage 2.
  MODPOST 12 modules
  GZIP    arch/arm64/boot/Image.gz
  LD [M]  crypto/drbg.ko
  LD [M]  crypto/echainiv.ko
  LD [M]  crypto/hmac.ko
  LD [M]  crypto/jitterentropy_rng.ko
  LD [M]  crypto/sha1_generic.ko
  LD [M]  crypto/sha256_generic.ko
  LD [M]  drivers/crypto/inside-secure/crypto_safexcel.ko
  CC      net/bridge/br_netfilter.mod.o
  LD [M]  net/bridge/br_netfilter.ko

chyi@earth:~/workspace/ESPRESSObin/kernel/4.4.52/arch/arm64/boot$ sudo cp Image /media/chyi/033dde1a-4a91-42f0-967b-7cb70406e47c/boot
chyi@earth:~/workspace/ESPRESSObin/kernel/4.4.52/arch/arm64/boot/dts/marvell$ sudo cp ./armada-3720-community.dtb /media/chyi/033dde1a-4a91-42f0-967b-7cb70406e47c/boot
  => Image 파일과 armada-3720-community.dtb 파일 교체 후, 재부팅하자.

다음으로 보드를 재 부팅한 후, 아래 네트워크 설정을 진행하도록 한다.


<target board>
root@localhost:~# ifconfig eth0 up
[Tip] CPU 입장에서 볼 때 Topaz switch는 하나의 ethernet interface(eth0)라고 볼 수 있다. 따라서 얘를 먼저 up해 주어야 switch를 통한 network이 동작할 수 있게 된다.



[그림 2.4] ip link show 명령 실행

root@localhost:~# dhclient wan
[Tip] DHCP 서버로 부터 ip를 할당 받는다.

root@localhost:~# ifconfig wan
wan       Link encap:Ethernet  HWaddr f0:ad:4e:06:e0:f2 
          inet addr:192.168.9.177  Bcast:192.168.9.255  Mask:255.255.255.0
          inet6 addr: fe80::f2ad:4eff:fe06:e0f2/64 Scope:Link
          inet6 addr: fde1:402:f1f4:10:f2ad:4eff:fe06:e0f2/64 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:7 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:1010 (1.0 KB)  TX bytes:1270 (1.2 KB)


[그림 2.5] ping test

다음으로 주로 network 설정 관련 ubuntu package를 설치하도록 하자.
root@localhost:~# apt-get update
root@localhost:~# apt-get install bridge-utils
   => bridge utility 설치, 두개의 lan port(lan0, lan1)에 대해 s/w bridge를 구성하기 위해 필요
root@localhost:~# apt-get install dnsmasq-base
   => dnsmasq daemon(DHCP server, DNS server 역할)을 설치
root@localhost:~# apt-get install iptables
   => iptables 설치
root@localhost:~# apt-get install ssh
=> ssh server 설치

ssh server 설치 후에는 ssh root login이 가능하도록 설정을 변경할 차례이다.


root@localhost:/etc/ssh# vi sshd_config
#PermitRootLogin prohibit-password
PermitRootLogin yes <= 위 내용을 막고, 이 내용 추가
...
root@localhost:/etc/ssh# service ssh restart

=> ssh server를 재구동시킨다.

root@localhost:~# passwd

=> ssh root login이 가능하도록 root password를 지정한다.
Enter new UNIX password: root1234
Retype new UNIX password: root1234
passwd: password updated successfully

이렇게 수정 후, ssh root@192.168.5.1을 시도해 보니, 정상적으로 login이 된다.



[그림 2.6] ssh login

그럼, 지금부터는 ESPRESSObon 보드를 gateway로 동작시키기 위한 몇가지 네트워크 설정을 해 보도록 하겠다.



root@localhost:~# ifconfig eth0 0.0.0.0 up
=> eth0를 up한다.
root@localhost:~# brctl addbr br0
root@localhost:~# ifconfig lan0 0.0.0.0 up
root@localhost:~# ifconfig lan1 0.0.0.0 up
root@localhost:~# brctl addif br0 lan0
root@localhost:~# brctl addif br0 lan1
root@localhost:~# ifconfig br0 192.168.5.1 netmask 255.255.255.0 up
   => bridge br0를 생성한다.

root@localhost:~# dnsmasq --interface=br0 --dhcp-range=br0,192.168.5.50,192.168.5.100,12h &
   => dnsmasq daemon을 br0 interface에 대해 동작시킨다.
   => 이때, dhcp ip 범위는 192.168.5.50 ~ 192.168.5.100으로 하고, lease time은 12시간으로 지정한다.


root@localhost:~# echo 1 > /proc/sys/net/ipv4/ip_forward
=> ip forwarding을 enable시킨다.

root@localhost:~# iptables -t nat -A POSTROUTING -o wan -j MASQUERADE
   =>  wan interface에 대해 Source NAT(Masquerading) rule을 추가한다.

여기까지 설정한 후, Desktop PC에서 인터넷으로 ping을 시도해 보자.

PC(Ubuntu 18.04) => ESPRESSObin(Gateway mode) => AP => Internet

[그림 2.7] internet 연결 테스트

OK, 정상적으로 인터넷 연결이 된다.

지금까지 설정한 내용을 하나의 shell script로 정리해 보는 것으로 이장을 마무리하도록 하자.

[그림 2.8] network.sh(Gateway 용) script



3. WireGuard VPN 소개
WireGuard는 구닥다리 IPsec 및 OpenVPN의 대항마로 혜성처럼 등장한 open source VPN project이다. (시작된지는 5년 쯤 되었고) 개인적으로 알게된지는 3년전 쯤 되었는데, 정말 간결한 코드 구조와 빠른 성능(모든 것이 kernel에서 동작하고, 주요 암호 알고리즘에 대해서 ARM NEON 등과 같이 Advanced SIMD 구조를 활용하여 병렬처리하므로써 빠른 속도를 자랑함)이 아주 매력적인 녀석이다. 5년간의 기다림 끝에 최근 linux kernel 5.6에 포함되었다.

[그림 3.1] WireGuard Logo


WireGuard는 보시다시피 경쟁자들인 IPsec, SoftEther VPN, OpenVPN 등과 비교할 때, 코드 size(Line of Codes)가 현격하게 작다. 그말은 코드량이 많지 않으니 그만큼 철저히 검증될 가능성이 높고, 예상치 못한 곳에서의 버그로 인한 취약점이 발생할 가능성이 상대적으로 적다는 얘기가 된다.
[그림 3.2] WireGuard의 코드 규모(Line of Codes)


Kernel code는 아래 내용이 전부이다. (실력있는) 한 사람이 반나절 정도면 충분히 검증 가능한 코드 분량이다(아, 물론 실력 있는 분에게만 해당한다^^).





WireGuard는 Noise Protocol Framework을 기초(참고: Noise Protocol Framework 개발시 WireGuard 개발자인 Jason A. Donenfeld가 직접 참여함)로하여 상호 인증 및 키 교환(ECDH의 변형된 형태로 보면 됨 - Curve25519 ECC 공개키 암호 알고리즘 사용)이 이루어지고 있으며, ChaCha20(Stream Cipher 알고리즘), Poly1305(무결성 검사 알고리즘), BLAKE2s, SipHash24, HKDF 등 가장 빠르고 최신의 암호 및 해쉬 알고리즘으로 무장되어 있다.





[그림 3.3] WireGuard의 암호 알고리즘

OpenVPN, IPsec 등과의 속도 비교 결과를 보면 알 수 있듯이, WireGuard는 성능면에서는 나름의 확고한 위치를 차지하고 있는게 사실이다. IPsec의 경우는 값비싼 H/W 전용 암호 가속기(예: Cavium)를 이용하거나 (최근에는)Intel의 AES-NI 명령 set을 통해 암호화 속도를 향상시키고 있는데, 유사하게 WireGuard의 경우는 NEON(Advanced SIMD) 명령 set을 통해 암호화 속도를 향상시키고 있다.


[그림 3.4] WireGuard의 속도 비교

<여기서 잠깐>
WireGuard는 3가지 암호/해쉬 알고리즘(Curve25519, ChaCha20, Poly1305)의 속도 향상을 위하여 NEON(Advanced SIMD 확장 - 병렬 처리 가능한 몇가지 명령을 하나의 instruction으로 모아서 동시에 처리하는 방식) 구조를 활용한다. 이 3가지 암호 알고리즘은 모두 Kernel에서 Assembly로 구현되어 있고, 모두 NEON 기반의 코드로 구성되어 있다.
ARM NEON programming과 관련해서는 아래 문서 참조하기 바란다.


WireGuard는 IP 패킷(unicast packet only)을 기준으로 암호화 처리 후, (firewall을 통과하기 쉽게)UDP Tunnel 형태로 패킷을 재생성하여 내 보내는 구조를 따른다.


[그림 3.5] WireGuard Packet 구조

지금까지 설명한 WireGuard의 전체 구조를 하나의 그림으로 표현해 보면 다음과 같다.


[그림 3.6] WireGuard 전체 아키텍쳐


WireGuard는 linux kernel 기반으로 출발했지만, (구조적인 이유로 Linux에 비해 속도는 떨어지지만)Windows, MacOS, Android, iOS 등의 버젼도 함께 제공하고 있다.


[그림 3.7] WireGuard Github

WireGuard 설치 및 동작 시험과 관련해서는 아래 공식 Homepage 내용을 참조하기 바란다.



4. WireGuard VPN build 및 동작 시험하기
이 장에서는 WireGuard source code를 내려 받아 build한 후, 실제 어떻게 동작하는지 동작 시험을 해 보기로 하겠다. 이번 장의 내용은 주로 아래 site를 기초로하여 작성하였다.



[그림 4.1] WireGuard kernel 설정 조건

[Tip] CONFIG_NET_UDP_TUNNEL은 kernel version에 따라서는 CONFIG_NET_FOU로 달리 표현되는 것 같다. 이번 posting에서 사용한 kernel 4.4.52에서는 CONFIG_NET_FOU로 표현되어 있다.
[Tip] 위의 kernel config 중, 나머지는 모두 default 설정이므로, CONFIG_NET_UDP_TUNNEL (or CONFIG_NET_FOU)만 별로 enable시켜주면 된다.


그럼, 먼저 wireguard 최신 코드를 내려 받도록 하자. 여기서는 linux full source(5.6 kernel)를 사용하지 않고, wireguard-linux-compat source code(3.10 ~ 5.5 version에 대해 back porting한 코드)를 내려 받아 사용하도록 하겠다.

$ git clone https://git.zx2c4.com/wireguard-linux-compat
'wireguard-linux-compat'에 복제합니다...
remote: Enumerating objects: 8038, done.
remote: Counting objects: 100% (8038/8038), done.
remote: Compressing objects: 100% (1914/1914), done.
remote: Total 8038 (delta 6109), reused 7777 (delta 5928), pack-reused 0
오브젝트를 받는 중: 100% (8038/8038), 1.92 MiB | 1.72 MiB/s, 완료.
델타를 알아내는 중: 100% (6109/6109), 완료.

$ git clone https://git.zx2c4.com/wireguard-tools
'wireguard-tools'에 복제합니다...
remote: Enumerating objects: 290, done.
remote: Counting objects: 100% (290/290), done.
remote: Compressing objects: 100% (275/275), done.
remote: Total 2646 (delta 138), reused 9 (delta 9), pack-reused 2356
오브젝트를 받는 중: 100% (2646/2646), 569.12 KiB | 458.00 KiB/s, 완료.
델타를 알아내는 중: 100% (1761/1761), 완료.
[Tip] 예전에는 코드가 하나로 통합되어 있었는데, 이제는 wireguard kernel module과 tools(wg)를 별도로 관리하고 있다. 

WireGuard를 build하는 방법은 크게 두가지가 있다.
1. Kernel source tree 외부에서 진행하는 방법
  • Makefile을 수정하여 kernel source directory path를 맞추어 주어야 함.
2. Kernel source tree 내부에서 진행하는 방법
  • kernel patch를 한 후, kernel build를 해 주어야 함.

먼저, 위의 두가지 방법을 시도하기 전에 그림 4.1에 따라 kernel config를 새로 조정해 주도록 한다.

$ export PATH=/home/chyi/workspace/ESPRESSObin/toolchain/gcc-linaro-5.2-2015.11-2-x86_64_aarch64-linux-gnu/bin:$PATH
$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ export CC=aarch64-linux-gnu-gcc
$ export LD=aarch64-linux-gnu-ld

$ make menuconfig


[그림 4.2] WireGuard를 위한 kernel config 조정 - CONFIG_NET_FOU 선택

$ make -j4

Kernel compile 결과로 만들어진 새로운 Image 파일을 microSD boot/ 디렉토리에 다시 복사해 준다.
$ sudo cp Image /media/chyi/033dde1a-4a91-42f0-967b-7cb70406e47c/boot

<1. Kernel source tree 외부에서 WireGuard build 하기>
wireguard-linux-compat/src 디렉토리로 이동 후, Makefile을 아래와 같이 수정한 후 build를 시도한다.

$ cd wireguard-linux-compat/src
$ vi Makefile
...
KERNELRELEASE ?= $(shell uname -r)
#KERNELDIR ?= /lib/modules/$(KERNELRELEASE)/build
KERNELDIR = /home/chyi/workspace/ESPRESSObin/kernel/4.4.52
PREFIX ?= /usr
DESTDIR ?=
SRCDIR ?= $(PREFIX)/src
DKMSDIR ?= $(SRCDIR)/wireguard
DEPMOD ?= depmod
...

~


chyi@earth:~/workspace/ESPRESSObin/WG/wireguard-linux-compat/src$ make
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/main.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/noise.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/device.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/peer.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/timers.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/queueing.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/send.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/receive.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/socket.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/peerlookup.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/allowedips.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/ratelimiter.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/cookie.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/netlink.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/chacha20/chacha20.o
  PERLASM /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/chacha20/chacha20-arm64.S
  AS [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/chacha20/chacha20-arm64.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/poly1305/poly1305.o
  PERLASM /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/poly1305/poly1305-arm64.S
  AS [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/poly1305/poly1305-arm64.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/chacha20poly1305.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/blake2s/blake2s.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/crypto/zinc/curve25519/curve25519.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/compat/siphash/siphash.o
  CC [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/compat/dst_cache/dst_cache.o
  LD [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/wireguard.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/wireguard.mod.o
  LD [M]  /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/src/wireguard.ko


chyi@earth:~/workspace/ESPRESSObin/WG/wireguard-linux-compat/src$ file wireguard.ko 
wireguard.ko: ELF 64-bit LSB relocatable, ARM aarch64, version 1 (SYSV), BuildID[sha1]=7b2175d5e675cb61ee2e0025cd5a94399791784b, with debug_info, not stripped

Build 결과 생성된 wireguard.ko를  target board로 복사한다.
chyi@earth:~/workspace/ESPRESSObin/WG/wireguard-linux-compat/src$ scp ./wireguard.ko root@192.168.5.1:~/workspace
root@192.168.5.1's password: 
wireguard.ko                                                         100% 7241KB  11.2MB/s   00:00 

<Target board>
   => Target board로 login 후, 아래 명령을 실행하여 wireguard kernel module을 구동시킨다.

# insmod ./wireguard.ko


[그림 4.3] WireGuard kernel module insmod 후 모습

# dmesg

[그림 4.4] dmesg 출력 모습

OK, 일단 kernel module은 정상적으로 올라간 듯 보인다.



<2. Kernel source tree 내부에서 WireGuard build 하기>
이번에는 kernel tree 내에서 작업하는 방법을 알아 보도록 하자.

chyi@earth:~/workspace/ESPRESSObin/kernel/4.4.52$ /home/chyi/workspace/ESPRESSObin/WG/wireguard-linux-compat/kernel-tree-scripts/create-patch.sh | patch -p1
   => 먼저 kernel directory로 이동하여 wireguard patch script(create-patch.sh)를 실행해 준다.
patching file net/wireguard/allowedips.c
patching file net/wireguard/compat/dst_cache/dst_cache.c
patching file net/wireguard/compat/memneq/memneq.c
patching file net/wireguard/compat/siphash/siphash.c
patching file net/wireguard/compat/udp_tunnel/udp_tunnel.c
patching file net/wireguard/cookie.c
patching file net/wireguard/crypto/zinc/blake2s/blake2s-x86_64-glue.c
patching file net/wireguard/crypto/zinc/blake2s/blake2s.c
patching file net/wireguard/crypto/zinc/chacha20/chacha20-arm-glue.c
patching file net/wireguard/crypto/zinc/chacha20/chacha20-mips-glue.c
patching file net/wireguard/crypto/zinc/chacha20/chacha20-x86_64-glue.c
patching file net/wireguard/crypto/zinc/chacha20/chacha20.c
patching file net/wireguard/crypto/zinc/chacha20poly1305.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519-arm-glue.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519-fiat32.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519-hacl64.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519-x86_64-glue.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519-x86_64.c
patching file net/wireguard/crypto/zinc/curve25519/curve25519.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305-arm-glue.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305-donna32.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305-donna64.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305-mips-glue.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305-x86_64-glue.c
patching file net/wireguard/crypto/zinc/poly1305/poly1305.c
patching file net/wireguard/crypto/zinc/selftest/blake2s.c
patching file net/wireguard/crypto/zinc/selftest/chacha20.c
patching file net/wireguard/crypto/zinc/selftest/chacha20poly1305.c
patching file net/wireguard/crypto/zinc/selftest/curve25519.c
patching file net/wireguard/crypto/zinc/selftest/poly1305.c
patching file net/wireguard/device.c
patching file net/wireguard/main.c
patching file net/wireguard/netlink.c
patching file net/wireguard/noise.c
patching file net/wireguard/peer.c
patching file net/wireguard/peerlookup.c
patching file net/wireguard/queueing.c
patching file net/wireguard/ratelimiter.c
patching file net/wireguard/receive.c
patching file net/wireguard/selftest/allowedips.c
patching file net/wireguard/selftest/counter.c
patching file net/wireguard/selftest/ratelimiter.c
patching file net/wireguard/send.c
patching file net/wireguard/socket.c
patching file net/wireguard/timers.c
patching file net/wireguard/wireguard.mod.c
patching file net/wireguard/allowedips.h
patching file net/wireguard/compat/checksum/checksum_partial_compat.h
patching file net/wireguard/compat/compat-asm.h
patching file net/wireguard/compat/compat.h
patching file net/wireguard/compat/dst_cache/include/net/dst_cache.h
patching file net/wireguard/compat/fpu-x86/include/asm/fpu/api.h
patching file net/wireguard/compat/intel-family-x86/include/asm/intel-family.h
patching file net/wireguard/compat/memneq/include.h
patching file net/wireguard/compat/neon-arm/include/asm/neon.h
patching file net/wireguard/compat/ptr_ring/include/linux/ptr_ring.h
patching file net/wireguard/compat/simd-asm/include/asm/simd.h
patching file net/wireguard/compat/simd/include/linux/simd.h
patching file net/wireguard/compat/siphash/include/linux/siphash.h
patching file net/wireguard/compat/udp_tunnel/include/net/udp_tunnel.h
patching file net/wireguard/compat/udp_tunnel/udp_tunnel_partial_compat.h
patching file net/wireguard/cookie.h
patching file net/wireguard/crypto/include/zinc/blake2s.h
patching file net/wireguard/crypto/include/zinc/chacha20.h
patching file net/wireguard/crypto/include/zinc/chacha20poly1305.h
patching file net/wireguard/crypto/include/zinc/curve25519.h
patching file net/wireguard/crypto/include/zinc/poly1305.h
patching file net/wireguard/crypto/zinc.h
patching file net/wireguard/crypto/zinc/selftest/run.h
patching file net/wireguard/device.h
patching file net/wireguard/messages.h
patching file net/wireguard/netlink.h
patching file net/wireguard/noise.h
patching file net/wireguard/peer.h
patching file net/wireguard/peerlookup.h
patching file net/wireguard/queueing.h
patching file net/wireguard/ratelimiter.h
patching file net/wireguard/socket.h
patching file net/wireguard/timers.h
patching file net/wireguard/uapi/wireguard.h
patching file net/wireguard/version.h
patching file net/wireguard/crypto/zinc/blake2s/blake2s-x86_64.S
patching file net/wireguard/crypto/zinc/chacha20/chacha20-arm64.S
patching file net/wireguard/crypto/zinc/chacha20/chacha20-mips.S
patching file net/wireguard/crypto/zinc/chacha20/chacha20-unrolled-arm.S
patching file net/wireguard/crypto/zinc/curve25519/curve25519-arm.S
patching file net/wireguard/crypto/zinc/poly1305/poly1305-arm64.S
patching file net/wireguard/crypto/zinc/poly1305/poly1305-mips.S
patching file net/wireguard/crypto/zinc/chacha20/chacha20-arm.pl
patching file net/wireguard/crypto/zinc/chacha20/chacha20-arm64.pl
patching file net/wireguard/crypto/zinc/chacha20/chacha20-x86_64.pl
patching file net/wireguard/crypto/zinc/poly1305/poly1305-arm.pl
patching file net/wireguard/crypto/zinc/poly1305/poly1305-arm64.pl
patching file net/wireguard/crypto/zinc/poly1305/poly1305-mips64.pl
patching file net/wireguard/crypto/zinc/poly1305/poly1305-x86_64.pl
patching file net/wireguard/compat/Makefile.include
patching file net/wireguard/crypto/Makefile.include
patching file net/wireguard/Makefile
patching file net/wireguard/Kconfig
patching file net/Kconfig
Hunk #1 succeeded at 82 (offset -3 lines).
patching file net/Makefile

[Tip] 패치 결과, net/Makefile, net/Kconfig 등이 수정되었으며, net/wireguard 디렉토리가 추가되었다.
[Tip] wireguard kernel patch 방법은 kernel-tree-scripts/create-patch.sh와 jury-rig.sh를 이용한 두가지 방법이 있는데, 크게 차이가 없어 여기서는 create-patch.sh 방법만을 소개하였다.

$ make menuconfig
   => IP: WireGuard secure network tunnel (NEW)를 [M]으로 선택한다.


[그림 4.5] WireGuard를 kernel module로 선택
[Tip] 위의 menuconfig에서는 "Debugging checks and verbose messages"를 [*]로 지정하였다. 이는 wireguard 동작 시 각종 debugging message를 출력하게 만들어 주며, 암호/해쉬 알고리즘에 대한 self testing routine을 동작시키는 역할을 하게 된다.
[Tip] [M]이 아니라 [*]를 선택해도 된다. [*]를 선택한 경우는 kernel Image에 wireguard code가 하나로 통합되니, 이후 insmod 과정은 불필요하다.

$ make -j4

chyi@earth:~/workspace/ESPRESSObin/kernel/4.4.52$ make modules
  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  Building modules, stage 2.
  MODPOST 13 modules
chyi@earth:~/workspace/ESPRESSObin/kernel/4.4.52$ find . -name "wireguard.ko" -print
./net/wireguard/wireguard.ko

$ scp ./net/wireguard/wireguard.ko root@192.168.5.1:~/workspace
[Tip] wireguard.ko를  target board로 올려 테스트하기 전에, 새로 build한 kernel Image 파일을 microSD에 먼저 복사해 주어야 한다. 왜냐하면 wireguard를 kernel module로 설정하는 부분이 추가(변경)되었고, 이를 새로운 kernel Image가 인식하고 있어야 하기 때문이다. 이를 무시하고 바로 아래 step을 진행한다면, wireguard.ko가 정상 구동되지 않게 된다.

<Target board>
root@localhost:~/workspace# insmod ./wireguard.ko
root@localhost:~/workspace# dmesg


[그림 4.6] WireGuard를 insmod한 결과
[Tip] 그림 4.4하고는 다르게, 이번에는 self-tests 관련 kernel message가 여러 줄 보인다. 이는 앞서 설명한 대로 "Debugging checks and verbose messages"를 [*]로 선택했기 때문이다.


<wg tool build 하기>
지금까지 wireguard.ko module build 과정을 살펴 보았으니, 이제부터는 wg tool을 build해 볼 차례이다.


[그림 4.7] WireGuard wg와 wireguard.ko와의 관계

[Tip] wg는 generic netlink socket을 이용하여 wireguard.ko를 제어해 주는 userspace tool이다.

$ cd wireguard-tools/src
chyi@earth:~/workspace/ESPRESSObin/WG/wireguard-tools/src$ make
  CC      wg.o
  CC      config.o
  CC      curve25519.o
  CC      encoding.o
  CC      genkey.o
  CC      ipc.o
  CC      pubkey.o
  CC      set.o
  CC      setconf.o
  CC      show.o
  CC      showconf.o
  CC      terminal.o
  LD      wg

chyi@earth:~/workspace/ESPRESSObin/WG/wireguard-tools/src$ file wg
wg: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.7.0, BuildID[sha1]=c30192da556a083c8ed1d992c4a99e3067472f59, with debug_info, not stripped

$ scp ./wg root@192.168.5.1:~/workspace
root@192.168.5.1's password: 
wg                                                                   100%  111KB   8.8MB/s   00:00

이제 wireguard 실행을 위한 모든 것이 준비되었으니, 다음으로는 target board에서 실행해 볼 차례이다.

<Target board>
root@localhost:~/workspace# ip link add dev wg0 type wireguard
root@localhost:~/workspace# ip address add dev wg0 10.1.1.60/8
root@localhost:~/workspace# ip link set up dev wg0
   => 가상의 interface wg0를 만들고, ip 주소 10.1.1.60/8을 할당해 준다.


[그림 4.8] wg0 가상 interface 생성 모습

root@localhost:~/workspace# ./wg genkey | tee ./privatekey | ./wg pubkey > ./publickey

   => ECC(Curve25519) 공개키 암호 알고리즘을 사용하여 한쌍의 private/public key를 생성한다.

root@localhost:~/workspace# cat privatekey
oBQmczphvcdG7jDgVc64J/k2hZHBxgmFIiaGfTQ5PUg=

root@localhost:~/workspace# cat publickey 
08LTYh9LH6lQ6sMRFZCbjieY6fAVJye3tg7qOn3R8hM=

[Tip] ECC Curve25519에 의해 생성된 private, public key의 길이는 32bytes이다. 하지만 위에 보이는 key의 길이는 44bytes이다. 이는 32bytes key 값을 (사람이 보기 쉬운 형태로 만들기 위해) base64로 encoding 했기 때문이다. 즉 32bytes => base64 encoding => 44bytes

root@localhost:~/workspace# ./wg set wg0 listen-port 59760 private-key ./privatekey peer gXlNPhrIpbOSr4rMD9tF614mVFvF/GqyONJo4kmNB3I= allowed-ips 10.100.1.1/8 endpoint 13.125.60.224:59760
Warning: AllowedIP has nonzero host part: 10.100.1.1/8
   => wg tool을 사용하여 자신의 listen port 및 private-key 를 지정한 후, peer의 public key, 허용하고자 하는 peer의 network 정보, endpoint(peer 측 공인 ip 및 허용 port) 정보 등을 설정한다.

[Tip-중요] 만일 wireguard 앞단에 방화벽(or 공유기)이 위치하고 있다면, 두 wireguard 간의 정상 통신(UDP 통신)을 위해 반드시 한쪽 편 방화벽에서는 port forwarding 설정을 해 주어야만 한다.


WireGuard A ===> 방화벽/공유기 A <=== Internet ===> 방화벽/공유기 B <=== WireGuard B
Port Forwarding 필요                         Port Forwarding 필요


이 상태에서 wg 명령을 실행하면, 현재 설정 상태를 한눈에 확인할 수 있다.


[그림 4.9] wg 실행 모습

[Tip] 이 시점에서 반대편 wireguard에서도 비슷한 설정을 진행해 주어야만 하는데, 지면 관계상 여기에서는 따로 설명하지는 않았다.

반대편 WireGuard가 10.100.1.1의 VPN IP를 사용한다고 할 경우, 해당 ip로 ping을 시도해보면 아래와 같이 정상적으로 동작함을 알 수 있다.


ESPRESSObin[10.1.1.60/8] ==> AP <== Internet ==> Firewall <== Server[10.100.1.1/8]
(WireGuard A)                                                                                    (WireGuard B)

# ping 10.100.1.1
# ./wg show


[그림 4.10] peer WG로의 정상 ping 모습

[Tip] 만일 ping이 정상적이지 않거나, wg 실행 결과 transfer, received 결과 중 하나라도 0이 나온다면, 앞서 설정한 내용(방화벽의 port forwarding 설정 포함)을 다시한번 꼼꼼히 체크해 보아야 한다.


그렇다면, 아래 구성 처럼 ESPRESSObin 보드(Gateway로 동작) 내부의 PC에서 Server로 ping을 한다면 어떨까 ?

PC(Ubuntu) => ESPRESSObin[10.1.1.60/8] <==== Internet ===> Server[10.100.1.1/8] 

chyi@earth:~$ ping 10.100.1.1
어라 안된다. 왜일까 ?
정답은 아래와 같이 "wg0 interface에 대해 masquerading rule(source NAT)을 추가해 주어야 한다"는 것이다. 왜 그런지는 곰곰히 생각해 보시기 바란다. :)

<Target board>
root@localhost:~# iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE


[그림 4.11] ESPRESSObin 내부 PC에서 외부 WireGuard 서버로의 ping test

지금까지 WireGuard의 동작 시험을 진행해 보았다. 이쯤되면 WireGuard의 보다 구체적인 동작 원리가 매우 궁금해 졌을 것이다. 이와 관련해서는 WireGuard 개발자인 Jason A. Donenfeld가 직접 작성한 아래 paper(조금 난해할 수 있음)를 읽어 보시길 권한다.




이상으로 (아주 오랫만에)ESPRESSObin 보드를 Gateway로 만들고, 여기에 WireGuard VPN을 올리는 과정을 간략히 정리해 보았다. 궁금한 사항이 있다면 언제든 댓글을 남겨 주시기 바란다(바빠서 댓글에 대한 답을 남겨드리지 못할 수도 있지만^^).


5. TODO

이번 posting에서 정리하지 못한 내용을 열거해 보면 다음과 같다. 시간 나는대로 아래 내용도 정리해 볼 생각이다.

1) ESPRESSObin board의 확장 핀을 사용한 예제 코드를 정리해 보자. 가능하면 아래 책의 예제를 ESPRESSObin board에 적용해 보도록 하겠다.

[그림 5.1] Linux Driver Development for Embedded Processors Book

2) ESPRESSObin board에 SoftEther VPN을 올려 보자.
[Tip] SoftEther VPN은 userspace에서 동작하는 open source VPN project로 성능 측면에서는 WireGuard 보다는 살짝 못할 수도 있지만, 아주 빼어난 기능 및 나름 우수한 성능을 자랑한다. SoftEther VPN의 가장 큰 장점은 ethernet packet을 통째로 암호화해 주므로 인터넷을 경유하여 모든 ethernet 통신이 가능하다는 점이다.

ESPRESSObin 보드 대신 이와 유사한 Gl.iNet MV1000에 SoftEther VPN을 올려 보았다.

3) WireGuard kernel source를 분석해 보자.

4) ESPRESSObin board와 동일한 CPU를 사용하는 Gl.iNet MV1000(Brume)에 WireGuard를 올려 보자.

[그림 5.2] Gl-iNet MV1000 모델(1)


[그림 5.3] Gl-iNet MV1000 모델(2)

[Tip] Gl.iNet은 OpenWrt 기반의 Wi-Fi Router를 제조하는 중국 회사(아주 훌륭한 회사^^)이다. Gl-iNet MV1000은 ESPRESSObin과 동일한 CPU를 사용하는 제품으로 OpenWrt(default OS) or Ubuntu를 탑재할 수 있다.

끝으로 최근에 다니던 회사에서 WireGuard와 SoftEther VPN(물론 그 밖에도 다른 여러가지 내용이 포함되어 있긴 하지만...)을 기반으로 제품을 개발한 바(불행하게도 회사 재정 상태가 악화되어 어렵사리 만들어 놓은 제품이 공중이 떠 버리는 상황이 되었음) 있다. 관련해서는 (개발자에게 도움이 될만한 내용 위주로 올려 두었으니)아래 site의 Technical 문서를 참조해 주기 바란다.



6. References
이번 posting에서 주로 참조한 문서를 열거해 보면 다음과 같다.

[1] http://espressobin.net/
[2] https://www.wireguard.com/
[3] https://www.wireguard.com/papers/wireguard.pdf
[4] https://www.wireguard.com/talks/lpc2018-wireguard-slides.pdf
[5] https://eprint.iacr.org/2019/482.pdf
[6] https://pure.tue.nl/ws/portalfiles/portal/130180306/Peter.Wu_Wireguard_thesis_final.pdf
[7] http://infocenter.arm.com/help/topic/com.arm.doc.dht0002a/DHT0002A_introducing_neon.pdf