2020년 9월 25일 금요일

ESPRESSObin Ultra 보드와 MACCHIATObin 보드의 Switch & Ethernet Device Driver 분석(3)

이번 시간(세번째 시간)에는 지난 시간에 이어, MACCHIATObin 보드 상에서 n2n  - Layer 2 peer to peer VPN을 구동시키는 방법을 소개해 보고자 한다.

          n2n 
  


목차
1. Docker 환경 설정
2. ESPRESSObin Ultra 보드 소개
3. ESPRESSObin Ultra 보드의 Switching Device Driver 분석
4. MACCHIATObin 보드 소개
5. MACCHIATObin 보드의 Ethernet Device Driver 분석
6. 응용편1: MACCHIATObin 보드를 SoftEther VPN Gateway로 만들기
7. 응용편2: MACCHIATObin 보드를 P2P VPN Gateway로 만들기
8. References



7. 응용편2: MACCHIATObin 보드를 P2P VPN Gateway로 만들기
이번 장에서는 Layer 2 Peer to Peer VPN project 중 하나인 n2n을 MACCHIATObin 보드에서 동작시키는 과정을 소개해 보고자 한다.

7.1) n2n project 소개
ntop project 중의 하나인 n2n은 아래와 같은 특징을 갖는 P2P VPN이다.


a) NAT 장비 안쪽에 있는 두 peer(host or user)가 NAT 장비에 영향을 받지 않고 서로 완벽하게 통신하도록 만들어 준다. 즉, NAT 장비에서 port forwarding or DMZ 설정을 전혀 해 줄 필요가 없다. 
   => 또한 port forwarding/DMZ 등으로도 연결 안되는 문제까지 해결해 준다.

[그림 7.1] NAT and home [출처: 참고 문헌 12]

📌 NAT가 사용되는 환경은 가정에 설치된 공유기, 회사에 설치된 Firewall, LTE망과 Internet을 연결해 주는 CGN(Carrier Grade NAT, Firewall), Smart Phone USB Tethering 등 다양하다.
📌 IPv4 환경이 IPv6로 모두 전환되기 전까지는 NAT가 어쩔 수 없이 존재한다. 따라서 n2n과 같은 solution이 필요할 수밖에 없다.

b) edge(peer에 설치)와 supernode(public ip를 갖는 중앙의 서버)라는 2개의 daemon으로 구성되어 있다.
  => edge가 설치되면, 이를 사용하는 application의 코드를 전혀 수정할 필요가 없다. 즉, application transparency를 보장한다.
  => MQTT, HTTP 등의 IoT protocol을 사용하는 application에 대해 코드 수정 없이 사용이 가능하다.

[그림 7.2] n2n의 구조2(edge와 supernode로 구성) [출처: 참고 문헌 11]

c) Full cone NAT에 대해서는 hole punching도 지원하며, symmetric NAT가 있는 경우(hole punching이 불가한 경우)에는 supernode를 통한 relay가 지원된다.
   => Smart Phone <=> Smart Phone 간의 1:1 direct 통신도 가능하다.

[그림 7.3] n2n의 구조1(edge와 supernode로 구성) [출처: 참고 문헌 11]

📌 NAT hole punching & relay 기법과 관련해서는 아래 site를 참조하기 바란다.

d) 하나의 peer는 복수개의 다른 peer와 각각 서로 다른 vpn tunnel을 통해 통신할 수 있다.
   => community 명을 통해 각각의 tunnel을  구분해 줄 수 있다.

[그림 7.4] n2n의 그룹간 통신

📌 supernode는 edge에 할당된 network name(=community name), mac 주소(사설 주소), public NATed ip:port, private unNATed ip:port를 유지/관리한다.

e) 두 peer는 암호화된 tunnel을 통해 ethernet frame을 통째로 암호화한 후 전달할 수 있다.
   => ethernet frame을 전달할 수 있으므로, multicast, broadcast packet도 전달 가능하다.
   => 이는 broadcast packet을 사용하는 dhcp와 multicast DNS 등이 가능하다는 얘기가 된다.
   => 단, multicast의 경우는 -E option을 주어야 수신이 가능하다(default: drop).

f) 두 peer는 tunnel을 만들기 위해 tap(linux 기준) device를 이용하며, tap 기반 network interface에는 사설 ip 주소를 할당해 준다.

[그림 7.5] n2n의 구조3 - tap device를 통한 ethernet frame 전달 [출처: 참고 문헌 11]


[그림 7.6] OpenVPN의 동작 방식 - n2n도 동일한 방식으로 동작함.

📌 위의 그림에서 tun0를 tap0로, OpenVPN을 edge로 변경한 것이 n2n이라고 보면 된다. 물론, 중앙에 별도의 supernode가 있어야 하지만 ...

g) 암호화 알고리즘으로는 twofish, aes, chacha20, speck 등을 지원한다(단, 최신 버젼 2.9 기준임)
  => 이 부분은 key 교환 방식과 더불어 최근에 한창 개발 중에 있다.
  => IKE 등을 지원하려는 것 같은데, 아직까지는 key 교환 관련하여 기능이 신통치 못한 것 같다.


h) 속도 문제를 해결하기 위해 다음의 두가지 압축 알고리즘도 제공한다(2.9 version 기준).
  => lzo1x, zstd 지원 

i) Windows, macOS, Android 버젼을 지원한다.
  => 단, Windows, Android는 (상용 버젼 수준으로 만들려면) 약간의 개조가 필요하다.

j) 설정이 아주 간단하고, 사용하기 매우 쉽다.

k) 단점: tun/tap device를 사용하여 tunnel/암호화(그림 7.5-7.6)를 하다보니, 느릴 수 밖에 없다(OpenVPN이 느린 이유와 동일함). 더더군다나 중앙에 supernode를 통해 relay를 해야 하는 경우에는 더 느려질 수 밖에 없다.
   => 그럼에도 불구하고, 사용할만한  가치가 있어 보인다.
   => (좀 느리지만) NAT 장비에 영향을 받지 않고 game을 하고자 할 경우 적합.
   => 역시 NAT 장비의 설정 변경 없이 client -> server(예: ssh, ftp ..) 연결에 적합.
   => 24시간 운용 서비스 보다는 특정 시점에 연결하여 사용하는 경우에 적합할 듯 보임.

📌 아래 내용은 scp를 이용하여 1Gbit/s 환경에서 n2n의 속도를 측정한 결과이다.

<n2n performance - scp를 이용하여 테스트> [출처: 참고 문헌 12]

n2n 관련 보다 자세한 사항은 아래 n2n 공식 home page의 내용을 참고하기 바란다.

📌 참고로 n2n은 Hamachi와 유사하게 동작하는 program이다.


7.2) ARM64용으로 n2n build하기
그럼, 지금부터는 arm64 환경에서 동작 가능하도록 n2n를 cross-compile해 보도록 하겠다. 6장과 동일하게 docker(ubuntu 16.04) 환경에서 build를 진행해 보기로 하자.

<Docker ubuntu 16.04>
root@23dc5a9270fd:/# su - chyi
$ export PATH=/home/chyi/workspace/toolchain/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin:$PATH
  => arm64용 toolchain path를 지정한다.
$ export CROSS=aarch64-linux-gnu-

$ cd ~/workspace/vpn/n2n-2.8
  => 미리 download 받아 둔, n2n-2.8 directory로 이동하자.
  => 이 글을 쓰는 현재 n2n-2.8이 stable version이다.

$ ./autogen.sh
  => autoreconf가 없다는 에러가 발생한다면 아래 package를 설치해 주자.
# apt-get install autoconf

$ ./configure --build x86_64-pc-linux-gnu --host aarch64-linux-gnu
$ make clean
$ CC="$CROSS"gcc AR="$CROSS"ar RANLIB="$CROSS"ranlib make

chyi@23dc5a9270fd:~/workspace/vpn/n2n-2.8$ file edge
  => 정상적으로 cross-compile이 되었다면 아래 처럼 출력될 것이다.
edge: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=ca3f341915a34f6fdd980a5020a44bfd66371898, not stripped

chyi@23dc5a9270fd:~/workspace/vpn/n2n-2.8$ file supernode
supernode: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=22157b0e54fa825d255f963cd23356f58f5d0e6e, not stripped

chyi@23dc5a9270fd:~/workspace/vpn/n2n-2.8$ ls -l

[그림 7.7] n2n source code 및 build 결과

간단하게 build에 성공하였으니, target board로 올려 동작 테스트를 해 보기로 하자.

7.3) n2n 동작 시험 1
이 절에서 시험할 네트워크 환경은 다음과 같다.

<Testbed 1>
Linux PC A(192.168.1.49/24, n2n edge1: 10.1.2.1) => LTE Router => Internet 
Linux PC B(192.168.2.100/24) => MACCIATObin(192.168.2.1/24, n2n edge2:10.1.2.2) => Repeater => AP => Internet

외부에 서버를 운영할 환경이 안되므로, supernode는 ntop에서 운영하는 supernode.ntop.org를 활용하기로 한다. 만일 이를 직접 운영하고자 한다면 아래와 같이 간단하게 실행해주면 된다.

<Public ip를 가진 서버>
$ sudo ./supdernode -l <port>

📌 AWS EC2 1년 무료 버젼을 사용하면 간단한 서버를 운영할 수 있긴하다.

supernode가 준비되었으니, edge binary를 target board로 복사한 후, 아래 명령을 실행해 주도록 하자.

<Target board: edge2>
root@localhost:/home/chyi/workspace# ./edge -c mynetwork -k mysecretpass -a 10.1.2.2 -r -l supernode.ntop.org:7777 -A2 -z1 -f
  => -c : community 명을 mynetwork으로 지정하였다.
  => -k : shared 암호키를 mysecretpass로 지정하였다.
  => vpn ip를 10.1.2.2로 지정하였다.
  => -r : n2n virtual lan을 통한 ip forwarding을 enable 시켜준다.
  => -l : supernode ip:port를 ntop에서 운영하는 supernode.ntop.org:7777를 입력하였다.
  => -A2: twofish 암호 알고리즘으로 설정한다.
  => -z1: lzo1x 압축 알고리즘을 사용하도록 설정한다.
  => -f: foreground로 동작하도록 한다.
  => -d option을 생략한 관계로 edge0라는 tap interface가 자동으로 생성된다.

[그림 7.8] MACCHIATObin에서 edge(edge2)를 실행한 모습

📌 참고로 암호 알고리즘을 위한 option은 다음과 같다.
 -A2: Twofish(CTS mode), -A3: AES(CBC mode), -A4: ChaCha20(CTR mode), -A5: SPECK(CTR mode)

root@localhost:/home/chyi/workspace# sysctl -w net.ipv4.ip_forward=1
  => ip forwarding을 1로 설정한다.

root@localhost:~# iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE
  => MACCHIATObin 내부의 PC가 internet에 접속할 수 있도록 masquerading 설정을 해 준다.

📌 edge -h 명령을 실행하면 사용 가능한 자세한 option을 확인할 수 있다.

[그림 7.9] edge에서 사용 가능한 option 확인


<Linux  PC A: edge1>
$ ./edge -c mynetwork -k mysecretpass -a 10.1.2.1  -l supernode.ntop.org:7777 -A2 -z1 -f
  => -c : community 명을 mynetwork으로 지정하였다.
  => -k : shared 암호키를 mysecretpass로 지정하였다.
  => vpn ip를 10.1.2.1로 지정하였다.
  => -l : supernode ip:port를 ntop에서 운영하는 supernode.ntop.org:7777를 입력하였다.
  => -A2: twofish 암호 알고리즘으로 설정한다.
  => -z1: lzo1x 압축 알고리즘을 사용하도록 설정한다.
  => -f: foreground로 동작하도록 한다.
  => -d option을 생략한 관계로 edge0라는 tap interface가 자동으로 생성된다.

📌 Linux PC A는 gateway가 아니므로 -r option을 사용할 필요가 없다.

[그림 7.10] Linux PC edge(edge1)를 실행한 모습

$ sudo ip route add 192.168.2.0/24 via 10.1.2.2
  => Linux PC A(edge1)에서 MACCHITObin 내부의 Linux PC B로 routing이 가능하도록 하기 위해 위의 rule을 추가해 준다.
📌 192.168.2.0/24 네트워크로 가기 위해서는 10.1.2.2(반대편 tunnel ip)로 보내야 한다는 뜻임.
_______________________________

n2n의 모든 설정이 끝났으니, VPN이 정상적으로 동작하는지를 ping을 통해 확인해 보도록 하자.

<Linux PC A>
chyi@earth:~$ ping 10.1.2.2
 => Linux PC A에서 edge2 vpn ip로 ping
64 bytes from 10.1.2.1: icmp_seq=869 ttl=63 time=1126 ms
64 bytes from 10.1.2.1: icmp_seq=870 ttl=63 time=129 ms
64 bytes from 10.1.2.1: icmp_seq=871 ttl=63 time=234 ms

chyi@earth:~$ ping 192.168.2.100
  => Linux PC A 에서 Linux PC B로 ping
64 bytes from 10.1.2.1: icmp_seq=863 ttl=63 time=261 ms
64 bytes from 10.1.2.1: icmp_seq=864 ttl=63 time=459 ms
64 bytes from 10.1.2.1: icmp_seq=865 ttl=63 time=452 ms

<Linux PC B>
chyi@mars:~$ ping 10.1.2.1
  => Linux PC B 에서 edge1 vpn ip로 ping
PING 10.1.2.1 (10.1.2.1) 56(84) bytes of data.
64 bytes from 10.1.2.1: icmp_seq=1 ttl=63 time=680 ms
64 bytes from 10.1.2.1: icmp_seq=2 ttl=63 time=709 ms
64 bytes from 10.1.2.1: icmp_seq=3 ttl=63 time=950 ms

<MACCHIATObin target board>
root@localhost:~# ping 10.1.2.1
  => target board에서 edge1 vpn ip로 ping
PING 10.1.2.1 (10.1.2.1) 56(84) bytes of data.
64 bytes from 10.1.2.1: icmp_seq=1 ttl=64 time=691 ms
64 bytes from 10.1.2.1: icmp_seq=2 ttl=64 time=43.8 ms
64 bytes from 10.1.2.1: icmp_seq=3 ttl=64 time=362 ms

OK, 모두 정상 동작한다. 😃

<여기서 잠깐 !>
  => n2n과 openssl version에 관하여 ...
n2n은 AES, ChaCha20 등의 암호 알고리즘을 위해 openssl 1.1을 사용한다. 근데, Ubuntu 16.04에는 1.0.2 version이 latest version으로 설치되어 있다. 따라서 openssl 1.1.0 버젼으로 upgrade해 주어야 한다.

<Docker Ubuntu 16.04>
chyi@23dc5a9270fd:~$ openssl version
OpenSSL 1.0.2g  1 Mar 2016

root@657d7cc2d58e:/home/chyi/workspace/download# wget https://www.openssl.org/source/openssl-1.1.1b.tar.gz
root@657d7cc2d58e:/home/chyi/workspace/download# tar xvzf ./openssl-1.1.1b.tar.gz; cd openssl-1.1.1b

root@657d7cc2d58e:/home/chyi/workspace/download/openssl-1.1.1b# ./config
root@657d7cc2d58e:/home/chyi/workspace/download/openssl-1.1.1b# make
root@657d7cc2d58e:/home/chyi/workspace/download/openssl-1.1.1b# make install

root@657d7cc2d58e:/home/chyi/workspace/download/openssl-1.1.1b# export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
root@localhost:/home/chyi/workspace# openssl version
OpenSSL 1.1.1b  26 Feb 2019

이 상태에서 n2n을 다시 build해 주면 된다.
____________________________________

7.4) n2n 동작 시험 2
이번에는 OpenWrt를 기반으로 하는 Gl.iNet MT300N-V2(이하 MangoBox로 칭하겠음)도 시험 네트워크에 포함시켜 보도록 하겠다.

<Testbed2: a <=> b간의 vpn>
a) Linux PC A(192.168.8.156/24) => MangoBox(n2n edge1: 10.1.2.1)  => LTE Router => Internet 
b) Linux PC B(192.168.2.100/24) => MACCIATObin(n2n edge2: 10.1.2.2) => Repeater => AP => Internet

10.1.2.1 <==> 10.1.2.2
192.168.8.156(Linux PC A) <==> 192.168.2.100(Linux PC B)

[그림 7.11] Gl.iNet MT300N-V2(MangoBox)

그럼, 지금부터는 아래 site의 내용을 참조하여 MangoBox의 개발 환경(openwrt)을 준비해 보도록 하겠다.


<Docker ubuntu 18.04>
  => 앞서와 마찬가지로 Docker 상에 설치된 ubuntu 18.04 위에 openwrt를 설치하도록 하자.
# apt-get update
# apt-get install build-essential subversion libncurses5-dev zlib1g-dev gawk gcc-multilib flex git-core gettext libssl-dev
# apt-get install unzip time
  => openwrt를 위해 필요한 package를 설치해 준다.

chyi@62ca0b4ddbc2:~/gl.inet$ git clone https://github.com/gl-inet/openwrt.git openwrt
chyi@62ca0b4ddbc2:~/gl.inet$ cd openwrt/
chyi@62ca0b4ddbc2:~/gl.inet$ ./scripts/feeds update -a
chyi@62ca0b4ddbc2:~/gl.inet$ ./scripts/feeds install -a

chyi@62ca0b4ddbc2:~/gl.inet$ make menuconfig
  => target board에 맞게 설정 변경을 해준다.

[그림 7.12] MangoBox를 위해 target board 설정 변경

chyi@62ca0b4ddbc2:~/gl.inet/openwrt$ make V=s -j5

OpenWrt가 build되었으니, 이제는 n2n을 build할 차례이다. 먼저 아래 site를 참조하여 openwrt에 설치할 n2n package 파일을 준비한다.


📌 위의 site 내용이 예전 n2n version을 기준으로 한 것이라, 몇가지 문제가 있다. 따라서 아래와 같이 Makefile을 적절히 수정해 주도록 하자.

[그림 7.13] n2n package를 위한 Makefile - 수정 버젼

수정된 n2n package file을 아래와 같이 package/network/services/n2n 디렉토리에 복사한다.

chyi@535261426f3a:~/gl.inet/openwrt/package/network/services/n2n$ ls -la
total 56
drwxrwxr-x 1 chyi chyi  4096 Sep 24 07:52 .
drwxrwxr-x 1 chyi chyi  4096 Sep 23 12:28 ..
-rw-rw-r-- 1 chyi chyi 32472 Sep 24 02:46 LICENSE
-rw-rw-r-- 1 chyi chyi  1995 Sep 24 07:49 Makefile
drwxrwxr-x 2 chyi chyi  4096 Sep 23 12:24 files

chyi@535261426f3a:~/gl.inet/openwrt$ make menuconfig
  => 이후 menuconfig 하여, n2n를 선택해 준다.

[그림 7.14] n2n-edge, supernode 선택 화면

chyi@535261426f3a:~/gl.inet/openwrt$ make package/network/services/n2n/configure
  => n2n 최신 버젼에서는 autogen.sh 후 configure를 해 주어야 한다.
chyi@535261426f3a:~/gl.inet/openwrt$ make package/network/services/n2n/compile -j1 V=s
  => build를 진행한다.

성공적으로 build가 진행되었으니, edge, supernode binary 및 ipk  파일이 어디에 생성되었는지 찾아보도록 하자.

chyi@535261426f3a:~/gl.inet/openwrt$ find . -name "edge" -print
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/.pkgdir/n2n-edge/usr/sbin/edge
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/edge
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/ipkg-mipsel_24kc/n2n-edge/usr/sbin/edge
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/packages/openwrt/etc/init.d/edge
./staging_dir/target-mipsel_24kc_musl/root-ramips/usr/sbin/edge

chyi@535261426f3a:~/gl.inet/openwrt$ find . -name "supernode" -print
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/.pkgdir/n2n-supernode/usr/sbin/supernode
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/supernode
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/ipkg-mipsel_24kc/n2n-supernode/usr/sbin/supernode
./build_dir/target-mipsel_24kc_musl/n2n-/n2n-2.8/packages/openwrt/etc/init.d/supernode
./staging_dir/target-mipsel_24kc_musl/root-ramips/usr/sbin/supernode

chyi@535261426f3a:~/gl.inet/openwrt$ find . -name "n2n*ipk" -print
./bin/packages/mipsel_24kc/base/n2n-edge_2.8-1_mipsel_24kc.ipk
./bin/packages/mipsel_24kc/base/n2n-supernode_2.8-1_mipsel_24kc.ipk

다음으로 할 일은 앞서 생성한 두개의 ipk 파일을 target board에 설치하는 것이다.

<MangoBox>
root@mango:~# opkg update
root@mango:~# opkg install /tmp/n2n-edge_2.8-1_misel_24kc.ipk
root@mango:~# opkg install /tmp/n2n-supernode_2.8-1_misel_24kc.ipk

📌 설치 후, edge를 실행하면 openssl 관련하여 error가 발생할 수 있다. 이는 openssl version이 불일치해서 생기는 문제로 openssl version을 맞추는 작업을 해주어야 한다.

edge가 정상 동작하는 것을 확인한 후에는, 아래와 같이 uci 명령을 사용하여 추가 firewall 설정을 해 주어야 한다. 즉, n2n을 위한 zone을 하나 만들고, n2n <=> lan, n2n <=> wan 간에 packet forwarding이 되도록 하는 firewall rule을 추가해 주어야 한다.

[그림 7.15] uci 명령을 사용하여 firewall 규칙 설정 변경하기

📌 위의 과정을 생략하면 firewall에 의해 막혀 n2n(edge)이 제대로 동작하지 못하게 되므로 반드시 설정해 주도록  한다.

자, 그럼 모든 준비가 끝났으니, 본격적으로 edge 명령 설정 단계로 들어가 보자.

<MangoBox>
root@mango:~/workspace#  ./edge -c mynetwork -k mysecretpass -a 10.1.2.1  -r -l supernode.ntop.org:7777 -A2 -z1 -f -n 192.168.2.0/24:10.1.2.2

[그림 7.16] MangoBox에서 edge를 실행한 모습

📌 위의 -n option이 의미하는 것은 192.168.2.0/24 network으로 패킷을 내보내기 위해서는 10.1.2.2 gateway로 전달해야 한다는 뜻이다. 위 명령을 실행하고 나면 아래와 같은 routing table 항목(4번째 line)이 자동으로 생성되게 된다.

[그림 7.17] MangoBox의 routing table(4번째 line 주목)

다음으로 MACCHIATObin에서도 edge 설정을 해 주도록 하자.

<MACCHIATObin board>
root@localhost:/home/chyi/workspace# ./edge -c mynetwork -k mysecretpass -a 10.1.2.2 -r -l supernode.ntop.org:7777 -A2 -z1 -f -n 192.168.8.0/24:10.1.2.1

모든 설정이 끝났으니 이 상태에서 Linux PC B(MACCHIATObin 내부망 PC)로 부터 Linux PC A(MangoBox 내부망 PC)로 ping을 시도해 보자. 

192.168.2.100(Linux PC B) => 192.168.8.156(Linux PC A)

[그림 7.18] 192.168.2.100(Linux PC B) => 192.168.8.156(Linux PC A) ping 모습

이번에는 반대 방향으로도 해 보자.

192.168.8.156(Linux PC A) => 192.168.2.100(Linux PC B)

[그림 7.19] 192.168.8.156(Linux PC A) => 192.168.2.100(Linux PC B) ping 모습

OK, 둘다 모두 정상이다. 😏
_______________________

이상으로 ntop n2n을 가지고 MACCHIATObin board & MangoBox를 P2P VPN Gateway로 만드는 과정을 간략히 살펴 보았다.
아직 안전한 암호 key 교환 방법(예: IKE, ECDH) 등 넘어야 할 산이 좀 남아 있긴 하지만, p2p VPN 영역에서 n2n이 나름의 역할을 할 것으로 기대하며, 이번 글을 마치고자 한다. 🎈


8. References
[2] ESPRESSObin ULTRA- Quick Start Guide -Rev 03
[3] Ethernet switch support in the Linux kernel, Alexandre Belloni, Bootlin
[4] From the Ethernet MAC to the link partner, Maxime Chevallier, Antoine Ténart, Bootlin
[6] marvell-link-street-88E6341-product-brief.pdf
[7] http://wiki.macchiatobin.net/tiki-index.php?page=BSP+HowTo
[8] https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04
[9] http://pyrasis.com/Docker/Docker-HOWTO#ps
[10] https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/
[11] N2N: A Layer Two Peer-to-Peer VPN, Luca Deri and Richard Andrews
[12] Creating Network Overlays with IoT Devices using N2N, Emanuele Faranda


SlowBoot

2020년 9월 20일 일요일

ESPRESSObin Ultra 보드와 MACCHIATObin 보드의 Switch & Ethernet Device Driver 분석(2)

이번 시간에는 지난 시간에 이어, Marvell chip을 사용하는 보드 중 두번째인 MACCHIATObin 보드에 관한 이야기(network 중심)를 해 보고자 한다.


 


목차
1. Docker 환경 설정
2. ESPRESSObin Ultra 보드 소개
3. ESPRESSObin Ultra 보드의 Switching Device Driver 분석
4. MACCHIATObin 보드 소개
5. MACCHIATObin 보드의 Ethernet Device Driver 분석
6. 응용편: MACCHIATObin 보드를 SoftEther VPN Gateway로 만들기
7. References



4. MACCHIATObin 보드 소개
MACCHIATObin은 Marvell ARMADA 8040(88F8040, Quad core Cortex-A72, 2GHz) processor(ARMv8 고성능 CPU + PPv2라는 network processor로 구성)를 사용하고, 10Gb Ethernet(Copper or SFP) 2개/2.5Gb Ethernet(SFP) 1개, 1Gb Ethernet(Copper)를 장착한 (고성능 network 장비로 사용 가능한) community board이다. 2년 전 쯤에 한 대를 구매해 두었었는데, 이제와서 꺼내 보게 되었다 😓



📌 MACCIATObin은 network interface가 가히 환상적이라고 말할 수 있다.

4.1) MACCHIATObin 보드 개요
MACCHIATObin은 아래 그림과 같이 Single Shot과 Double Shot 두가지 모델이 있는데, 이 중 보다 스펙이 좋은 Double Shot을 가지고 내용 전개를 하도록 하겠다.


[그림 4.1] MACCHIATOBin 보드(1) 

[그림 4.2] MACCHIATOBin 보드(2) - 스펙

[그림 4.3] MACCHIATOBin 보드(2) - Double Shot h/w 블록도

4.2) Docker 환경에서 MACCHIATObin 코드 build하기
지금부터는 MACCHIATObin code를 build하기 위해 docker 상에 Ubuntu 16.04를 설치하고, 다시 그 위에 OpenWrt 17.10 설치해 보도록 하겠다.


$ vi Dockerfile_ubuntu1604

[그림 4.4] Ubuntu 16.04 설치용 Dockerfile

$ docker build -f Dockerfile_ubuntu1604 -t u1604 .
  => docker image를 만들어 보자.

[그림 4.5] Ubuntu 16.04 설치용 docker image 생성 모습(1)

[그림 4.6] Ubuntu 16.04 설치용 docker image 생성 모습(2)

$ docker run -i -t --name mcbin1604 u1604 /bin/bash
root@9c114a442454:/# uname -a
Linux 9c114a442454 5.4.0-45-generic #49~18.04.2-Ubuntu SMP Wed Aug 26 16:29:02 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
root@9c114a442454:/# cat /etc/issue
Ubuntu 16.04.7 LTS \n \l

root@9c114a442454:/# su - chyi
chyi@9c114a442454:~/$ mkdir workspace; cd workspace
📌 1 ~ 2장에서 설명한 내용과 동일한 내용은 반복해서 언급하지 않도록 하겠다.

chyi@9c114a442454:~/workspace$ git clone https://github.com/MarvellEmbeddedProcessors/openwrt-kernel.git -b openwrt_17.10_release
  => kernel source를 먼저 내려 받도록 한다. openwrt build 시 이 kernel 코드를 활용하기 위해서 이다.

chyi@9c114a442454:~/workspace$ git clone https://github.com/MarvellEmbeddedProcessors/openwrt-dd.git -b openwrt_17.10_release
  => openwrt 17.10 version을 내려 받는다.

chyi@9c114a442454:~/workspace$ cd openwrt-dd/
chyi@9c114a442454:~/workspace/openwrt-dd$ ls -la
total 104
drwxrwxr-x 11 chyi chyi  4096 Sep  9 11:35 .
drwxrwxr-x  3 chyi chyi  4096 Sep  9 11:34 ..
drwxrwxr-x  8 chyi chyi  4096 Sep  9 11:35 .git
-rw-rw-r--  1 chyi chyi     8 Sep  9 11:35 .gitattributes
-rw-rw-r--  1 chyi chyi   224 Sep  9 11:35 .gitignore
-rw-rw-r--  1 chyi chyi   179 Sep  9 11:35 BSDmakefile
-rw-rw-r--  1 chyi chyi   576 Sep  9 11:35 Config.in
-rw-rw-r--  1 chyi chyi 17992 Sep  9 11:35 LICENSE
-rw-rw-r--  1 chyi chyi  2670 Sep  9 11:35 Makefile
-rw-rw-r--  1 chyi chyi  1272 Sep  9 11:35 README
drwxrwxr-x  2 chyi chyi  4096 Sep  9 11:35 config
drwxrwxr-x  2 chyi chyi  4096 Sep  9 11:35 docs
-rw-rw-r--  1 chyi chyi   457 Sep  9 11:35 feeds.conf.default
drwxrwxr-x  3 chyi chyi  4096 Sep  9 11:35 include
drwxrwxr-x 11 chyi chyi  4096 Sep  9 11:35 package
-rw-rw-r--  1 chyi chyi 12237 Sep  9 11:35 rules.mk
drwxrwxr-x  4 chyi chyi  4096 Sep  9 11:35 scripts
drwxrwxr-x  6 chyi chyi  4096 Sep  9 11:35 target
drwxrwxr-x 12 chyi chyi  4096 Sep  9 11:35 toolchain
drwxrwxr-x 58 chyi chyi  4096 Sep  9 11:35 tools

chyi@9c114a442454:~/workspace/openwrt-dd$ ./scripts/feeds update -a
chyi@9c114a442454:~/workspace/openwrt-dd$ ./scripts/feeds install -a
chyi@9c114a442454:~/workspace/openwrt-dd$ make menuconfig
  => 아래 내용을 참조하여 적절히 config를 조정하도록 한다.


[그림 4.7] openwrt menuconfig 모습

chyi@88e6a5c10d5f:~/workspace/openwrt-dd$ make -j4
  => build를 시작한다.

[그림 4.8] openwrt build 결과물

chyi@88e6a5c10d5f:~/workspace/openwrt-dd$ exit
root@9c114a442454:/# exit

$ docker commit -a "chunghan <chunghan.yi@gmail.com>" -m "add openwrt codes" mcbin1604 u1604
  => 지금까지 작업한 내용을 commit 하자.


4.3) Target board에서 돌려 보기 
이 절에서는 아래 site의 내용을 참조하여 MACCHIATObin 보드를 부팅해 보도록 하겠다.


부팅 가능한 microSD를 만드는 방법은 생략하였다. 관련해서는 위의 site 내용을 참조하기 바란다.

<Target board>
Marvell>> setenv image_name boot/openwrt-armada-a8k-MACHIATOBin-Image
Marvell>> setenv fdt_name boot/armada-8040-mcbin.dtb

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

Marvell>> saveenv
Marvell>> reset

[그림 4.9] MACCHIATObin 보드 부팅 모습(1) - minicom 115200 8N1

[그림 4.10] MACCHIATObin 보드 부팅 모습(2)

아래 그림은 MACCHIATObin의 port가 u-boot 및 linux에서 인식되는 내용을 보여준다.

[그림 4.11] MACCHIATObin 보드 Ethernet Port 구성
(egiga0 => eth0, egiga1 => eth1, egiga2 => eth2)

[그림 4.12] /etc/config/network 설정 내용 - LAN(bridge): eth0, eth1, eth3, WAN: eth2


[그림 4.13] 동작 중인 MACCHIATObin 보드(LAN: 검정색 선, WAN: 파란색 선)

📌 팬이 하나 달려 있어, 오래 틀어 놓으니 좀 시끄럽다.
📌 10G Ethernet을 위해서는 Cat6A 이상의 UTP cable을 사용해야 한다.

[그림 4.14] 동작 중인 MACCHIATObin 보드 - routing table

OK, 이 상태에서 인터넷을 연결해 보니, 정상 동작한다.

[그림 4.15] 동작 중인 MACCHIATObin 보드 - ps 명령 실행 모습

이상으로 MACCHIATObin 보드에 OpenWrt 17.10을 올리고, 간단히 network 동작 테스트를 진행해 보았다. 


5. MACCHIATObin 보드의 Ethernet Device Driver 분석
이 장의 내용은 [참고문헌 4]에서 영감을 받아 작성하였다.




[그림 5.1] Ethernet Link Layer 개념도 [출처: 참고문헌 4]


5.1) MACCHIATObin의 네트워크 인터페이스 
아래 그림은 MACCHIATObin 보드의 Ethernet 구성 요소를 전체적으로 보여주고 있다.

[그림 5.2] MACCHIATObin 보드 MAC/PHY 구성도 [출처: 참고문헌 4]

📌 SFP는 small form-factor pluggable transceiver를 뜻한다. SFP는 주로 광 케이블을 연결할 때 많이 사용되지만, copper cable을 연결하는 용도로도 사용될 수 있다.

먼저 eth2(그림 4.11 기준 가장 왼쪽, 위의 그림 5.2 기준 가장 아래쪽) 1G interface는 아래와 같이 가장 일반적인 MAC(PPv2.2)과 PHY(88E1512) 연결 형태로 구성되어 있다.

[그림 5.3] MACCHIATObin 보드 eth2 1G Interface [출처: 참고문헌 4]

다음으로 eth0, eth1 10G interface(그림 4.11 기준 가운데 두쌍 - SFP+ and Copper -)는 다음과 같이 MAC(PPv2.2)와 PHY(88X3310) 연결 형태로 구성되어 있다. SFP+의 경우는 내부에 i2c device가 포함되어 있어 i2c controller(SoC 내부)와 연결되어 있다(SFP+에 대한 레지스터 설정은 i2c를 통해 이루어짐).

[그림 5.4] MACCHIATObin 보드 eth0/1 10G Interface [출처: 참고문헌 4]

마지막으로 eth3(그림 4.11 기준 맨 우측 포트)은 2.5G interface를 지원하며, 중간에 별도의 PHY를 거치지 않고 곧바로 MAC(PPv2.2)과 연결되어 있다(즉, MAC to MAC 통신을 한다는 뜻). 경우에 따라서는 SFP+ 내에 PHY가 있는 경우도 있는데, 이 경우는 일반적인 MAC to PHY 통신으로 이어지게 된다.

[그림 5.5] MACCHIATObin 보드 eth3 2.5G MAC to MAC Interface [출처: 참고문헌 4]

5.2)  MACCHIATObin의 Ethernet Device Driver 분석
이런, 4.2~4.3 절에서 설치한 linux kernel(4.4.52)에는 [참고문헌 4]에서 말하는 kernel code가 보이질 않는다. 이 내용은 오히려 1장에서 설치한 linux kernel(4.19.62)에 보인다.

<device tree>
arch/arm64/boot/dts/marvell/armada-8040-mcbin.dts
<TBD>

[그림 5.6] armada-8040-mcbin.dts 내용 중 일부 발췌

<device drivers>
<Ethernet MAC controller>
drivers/net/ethernet/marvell/mvpp2/*
drivers/net/ethernet/marvell/mvmdio.c

<PHY driver>
drivers/net/phy/marvell.c
drivers/net/phy/marvell10g.c
<TBD>

Device Tree 및 device driver(ethernet MAC & PHY 관련)에 대한 분석은 추후 다시 하기로 하자.

<여기서 잠깐 !>
    => MUSDK와 ODP를 이용한 고속 패킷 처리 방법에 관하여 ...

Marvell User-Space SDK(a.k.a MUSDK)와 ODP(Open Data Plane)를 사용하면 고속 패킷 처리(linux kernel을 경유한 slow path가 아니라 PPv2.2를 이용한 fastpath)가 가능하다고 한다. 이 내용 자체도 하나의 커다른 주제가 될 듯한데, 자세한 사항은 아래 site를 참고하도록 하자.



[그림 5.7] MUSDK를 이용하여 PPv2 활용하기


[그림 5.8] ODP를 이용하여 PPv2 활용하기

이 장의 내용은 (아쉬움이 많이 남는 관계로) 추후 시간을 내어 좀 더 상세히 분석해 보아야 할 것 같다. 😂


6. 응용편: MACCHIATObin 보드를 SoftEther VPN Gateway로 만들기
이 장에서는 MACCHIATObin 보드를 VPN Gateway(SoftEther VPN 기반)로 만드는 과정을 소개하고자 한다. SoftEther VPN과 관련해서는 아래 blog post를 통해 이미 한 차례 소개한 바 있다.


위의 내용을 보면 알겠지만, SoftEther VPN을 cross-compile하는 과정은 연관 library가 많은 관계로 생각처럼 간단하지가 않다. x86_64 환경 처럼 arm64 개발 환경이 준비되어 있다면, cross-compile 없이 native compile 과정만으로도 원하는 결과를 얻을 수 있을텐데, 주변에서 arm64를 기반으로한 PC를 구하는 것은 매우 어렵다. 그렇다면 어떻게 해야 할까 ? 
📌 (비용은 좀 들겠지만) arm64를 지원하는 AWS EC2(or Google Cloud, Azure)를 활용하는 것도 하나의 방법이 될 수는 있다.

지금부터는 이 물음에 대한 대답으로, docker와 QEMU의 도움을 받아 x86 환경에서 arm64 개발 환경을 꾸미고, 그 위에서 SoftEther VPN을 build하는 방법을 소개하고자 한다. 물론 build가 성공한 후에는 target board 상에 올려 정상 동작하는지를 직접 확인해 볼 생각이다.


[그림 6.1] x86 위에 ARM emulation 환경 구축 [출처 - 참고문헌 10]

📌 물론 PINE64, ESPRESSObin 등 arm64 보드를 준비하고 그 위에서 직접 compile하는 방법도 하나의 대안이 될 수 있다. 하지만, 필요할 때마나 개발 보드를 하나씩 준비할 수는 없는 노릇이다.

6.1)  arm64v8(ubuntu 16.04)에서 SoftEther VPN build하기
지금부터는 arm64 emulation 환경을 만들고, 그 위에서 SoftEther VPN를 build해 보도록 하자.

<x86_64 Desktop PC>
$ sudo apt-get install qemu binfmt-support qemu-user-static
  => qemu, binfmt 등을 설치한다.
$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
  => multi architecture를 지원하는 qemu container를 실행하자.
Unable to find image 'multiarch/qemu-user-static:latest' locally
latest: Pulling from multiarch/qemu-user-static
61c5ed1cbdf8: Pull complete 
6957fb4d1698: Pull complete 
ed8a5179ae11: Pull complete 
1ec39da9c97d: Pull complete 
2901bd4ab058: Pull complete 
Digest: sha256:28ebe2e48220ae8fd5d04bb2c847293b24d7fbfad84f0b970246e0a4efd48ad6
Status: Downloaded newer image for multiarch/qemu-user-static:latest
Setting /usr/bin/qemu-alpha-static as binfmt interpreter for alpha
Setting /usr/bin/qemu-arm-static as binfmt interpreter for arm
Setting /usr/bin/qemu-armeb-static as binfmt interpreter for armeb
Setting /usr/bin/qemu-sparc-static as binfmt interpreter for sparc
Setting /usr/bin/qemu-sparc32plus-static as binfmt interpreter for sparc32plus
Setting /usr/bin/qemu-sparc64-static as binfmt interpreter for sparc64
Setting /usr/bin/qemu-ppc-static as binfmt interpreter for ppc
Setting /usr/bin/qemu-ppc64-static as binfmt interpreter for ppc64
Setting /usr/bin/qemu-ppc64le-static as binfmt interpreter for ppc64le
Setting /usr/bin/qemu-m68k-static as binfmt interpreter for m68k
Setting /usr/bin/qemu-mips-static as binfmt interpreter for mips
Setting /usr/bin/qemu-mipsel-static as binfmt interpreter for mipsel
Setting /usr/bin/qemu-mipsn32-static as binfmt interpreter for mipsn32
Setting /usr/bin/qemu-mipsn32el-static as binfmt interpreter for mipsn32el
Setting /usr/bin/qemu-mips64-static as binfmt interpreter for mips64
Setting /usr/bin/qemu-mips64el-static as binfmt interpreter for mips64el
Setting /usr/bin/qemu-sh4-static as binfmt interpreter for sh4
Setting /usr/bin/qemu-sh4eb-static as binfmt interpreter for sh4eb
Setting /usr/bin/qemu-s390x-static as binfmt interpreter for s390x
Setting /usr/bin/qemu-aarch64-static as binfmt interpreter for aarch64    <=== 이걸 지원한다 !
Setting /usr/bin/qemu-aarch64_be-static as binfmt interpreter for aarch64_be
Setting /usr/bin/qemu-hppa-static as binfmt interpreter for hppa
Setting /usr/bin/qemu-riscv32-static as binfmt interpreter for riscv32
Setting /usr/bin/qemu-riscv64-static as binfmt interpreter for riscv64
Setting /usr/bin/qemu-xtensa-static as binfmt interpreter for xtensa
Setting /usr/bin/qemu-xtensaeb-static as binfmt interpreter for xtensaeb
Setting /usr/bin/qemu-microblaze-static as binfmt interpreter for microblaze
Setting /usr/bin/qemu-microblazeel-static as binfmt interpreter for microblazeel
Setting /usr/bin/qemu-or1k-static as binfmt interpreter for or1k

$ docker run -i -t --name arm64 arm64v8/ubuntu:16.04 /bin/bash
  => arm64v8 용 ubuntu 16.04를 실행(container)하자.
root@b2940a44a2ca:/# uname -a
Linux 88ab315a52ad 5.4.0-47-generic #51~18.04.1-Ubuntu SMP Sat Sep 5 14:35:50 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux
root@88ab315a52ad:/# cat /etc/issue
Ubuntu 16.04.7 LTS \n \l

root@b2940a44a2ca:/# apt-get update
root@b2940a44a2ca:/# apt -y install cmake gcc g++ libncurses5-dev libreadline-dev libssl-dev make zlib1g-dev
  => SoftEther VPN build에 필요한 몇가지 package를 설치한다. 추가로 필요한 패키지가 있다면, 적절히 설치해 주면 된다.

📌 예상대로 docker + QEMU 구조이다 보니, 전체적으로 느린 느낌이다.

<SoftEther VPN build>
  => SoftEther VPN s/w와 관련해서는 아래 site 내용을 참조하자.


root@b2940a44a2ca:/# mkdir ~/workspace; cd workspace
root@b2940a44a2ca:/# git clone https://github.com/SoftEtherVPN/SoftEtherVPN.git
root@b2940a44a2ca:/# cd SoftEtherVPN
root@b2940a44a2ca:/# git submodule init && git submodule update
root@b2940a44a2ca:/# ./configure
📌 SoftEther VPN build를 위해서는 cmake 3.7 이상이 필요한데, Ubuntu 16.04는 3.6.1을 사용하고 있어 update해 주었다(자세한 과정은 생략).

root@b2940a44a2ca:/# make -C build
root@b2940a44a2ca:/# make -C build install

[그림 6.2] softether vpn build 결과물

root@b2940a44a2ca:/# tar cvzf sebin.tar.gz /usr/local/lib/lib*.so /usr/local/libexec/softether/
 => build 결과물을 tar.gz 파일로 묶어 보자.
/usr/bin/tar: Removing leading `/' from member names
/usr/local/lib/libcedar.so
/usr/bin/tar: Removing leading `/' from hard link targets
/usr/local/lib/libmayaqua.so
/usr/local/libexec/softether/
/usr/local/libexec/softether/vpnclient/
/usr/local/libexec/softether/vpnclient/vpnclient
/usr/local/libexec/softether/vpnclient/hamcore.se2
/usr/local/libexec/softether/vpncmd/
/usr/local/libexec/softether/vpncmd/hamcore.se2
/usr/local/libexec/softether/vpncmd/vpncmd
/usr/local/libexec/softether/vpnserver/
/usr/local/libexec/softether/vpnserver/hamcore.se2
/usr/local/libexec/softether/vpnserver/vpnserver
/usr/local/libexec/softether/vpnbridge/
/usr/local/libexec/softether/vpnbridge/hamcore.se2
/usr/local/libexec/softether/vpnbridge/vpnbridge

SoftEther VPN에 대한 build가 이루어졌으니, 이제부터는 build 결과 파일을 target board에 올린 후, vpnserver를 구동시켜 볼 차례이다. 편의상 지금부터는 (4장에서 설치한) OpenWrt 대신 Ubuntu 16.04를 사용하도록 하겠다.
📌 물론 OpenWrt에서도 SoftEther VPN을 구동시킬 수 있다. 이와 관련해서는 아래 site 내용이 도움이 될 것 같다.

6.2)  MACCHIATObin 보드에 Ubuntu 16.04 설치 후, SoftEther VPN vpncmd 실행하기
Ubuntu 16.04 설치와 관련해서는 MACCHIATObin wiki page에 상세히 소개되어 있다. 따라서 아래 site의 내용을 반드시 읽어보기 바란다. 여기에서는 꼭 필요한 내용을 중심으로 간략히 정리하고 넘어가도록 하겠다.


<Desktop PC>
$ gunzip -c ./ubuntu-xenial-mcbin_u-boot_sd.img_.gz | sudo dd of=/dev/sdb
7577600+0 레코드 들어옴
7577600+0 레코드 나감
3879731200 bytes (3.9 GB, 3.6 GiB) copied, 1773.49 s, 2.2 MB/s
📌 시간이 매우 오래 걸릴 수 있으니 주의하기 바란다.

<Target board>
Marvell>> setenv bootmmc 'mmc dev 0; ext4load mmc 1:2 $kernel_addr $image_name;ext4load mmc 1:2 $fdt_addr $fdt_name;setenv bootargs $console root=/dev/mmcblk1p2 rw rootwait; booti $kernel_addr - $fdt_addr'
📌 부트로더의 몇가지 설정을 변경해 준다. 자세한 사항은 위의 wiki page를 참조하도록 하자.

[그림 6.3] Ubuntu 16.04 부팅 모습

Ubuntu 16.04.3 LTS localhost.localdomain ttyS0

localhost login: root

root@localhost:~# apt-get update
root@localhost:~# apt-get install libncurses5-dev
root@localhost:~# apt-get install libreadline-dev
root@localhost:~# apt-get install libssl-dev
root@localhost:~# apt-get install zlib1g-dev
📌 SoftEther VPN 실행을 위해 필요한 몇가지 library를 설치해 주자. 설치가 불가한 경우에는 위의 library 파일을 docker 환경(6.1 절)에서 가져올 수도 있겠다.

root@localhost:~# su - chyi
  => user chyi를 사전에 추가하였다.
chyi@localhost:~$ mkdir workspace; cd workspace

<Desktop PC>
chyi@mars:/var/lib/tftpboot$ scp sebin.tar.gz chyi@192.168.2.1:~/workspace
chyi@192.168.1.1's password: 
sebin.tar.gz                                                               100%   14MB  63.6MB/s   00:00

<Target board>
chyi@localhost:~/workspace$ tar xvzf sebin.tar.gz 
usr/local/lib/libcedar.so
usr/local/lib/libmayaqua.so
usr/local/libexec/softether/
usr/local/libexec/softether/vpnclient/
usr/local/libexec/softether/vpnclient/vpnclient
usr/local/libexec/softether/vpnclient/hamcore.se2
usr/local/libexec/softether/vpncmd/
usr/local/libexec/softether/vpncmd/hamcore.se2
usr/local/libexec/softether/vpncmd/vpncmd
usr/local/libexec/softether/vpnserver/
usr/local/libexec/softether/vpnserver/hamcore.se2
usr/local/libexec/softether/vpnserver/vpnserver
usr/local/libexec/softether/vpnbridge/
usr/local/libexec/softether/vpnbridge/hamcore.se2
usr/local/libexec/softether/vpnbridge/vpnbridge

chyi@localhost:~/workspace$ export LD_LIBRARY_PATH=/home/chyi/workspace/usr/local/lib:$LD_LIBRARY_PATH
chyi@localhost:~/workspace$ cd usr/local/libexec/softether/vpncmd
  => OK, vpncmd가 동작한다.

[그림 6.4] softether vpncmd 실행 모습

6.3)  SoftEther VPN 연결 시험하기
이 절에서는 MACCHATObin 보드에 VPN server를 설치하고, (인터넷 반대편에 있는) Windows10 PC에 VPN Client를 설치한 후, 이를 서로 연결하는 시험을 진행해 보도록 하겠다. 시험용 Testbed는 다음과 같다.

<Testbed>
PC(VPN Client) => LTE Router <=== Internet ===> KT AP <= Small AP(repeater mode) <= MACCHATObin(VPN Server) <= Linux PC

VPN server  구동에 앞서, MACCHIATObin을 gateway로 만들기 위해 kernel의 Netfilter 기능을 enable하는 작업을 먼저 진행해야 한다. 

Kernel code download & build 방법은 역시 아래 page에 잘 정리되어 있으니 이를 참조하도록 한다.


$ export PATH=/home/chyi/workspace/mcbin/toolchain/gcc-linaro-5.3.1-2016.05-x86_64_aarch64-linux-gnu/bin:$PATH

$ git clone https://github.com/MarvellEmbeddedProcessors/linux-marvell .
$ git checkout linux-4.4.52-armada-17.10
$ git branch
  linux-3.10.70-15t1
* linux-4.4.52-armada-17.10

$ export ARCH=arm64
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make mvebu_v8_lsp_defconfig
$ make menuconfig
  => Netfilter 관련 설정은 꽤나 다양하니, 내용을 일일히 살펴본 후 적절히 enable시켜 주도록 한다(상세 내용 생략).

[그림 6.5] Netfilter enable하기

$ make -j4

Kernel compile이 완료되었으니, arch/arm64/boot/Image 파일을 target board에 올린 후, 부팅을 시도해 보자.

<Target board>
root@localhost:~# echo 1 > /proc/sys/net/ipv4/ip_forward
root@localhost:~# iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE
  => 시스템 부팅 후에는 ip_forwarding을 1로 설정하고, masquerading NAT rule을 하나 추가해 준다.

[그림 6.6] iptables masquerading rule 설정

OK, 이 상태에서 정상적으로 인터넷 접속이 되는 것을 확인했다면, 이제 부터는 vpnserver & vpncmd 설정을 시작할 준비할 차례다.

<Target board>
# export LD_LIBRARY_PATH=/home/chyi/workspace/usr/local/lib:$LD_LIBRARY_PATH
# cd usr/local/libexec/softether/vpnserver/
#  ./vpnserver start

[그림 6.7] vpnserver 실행 모습

# cd ../vpncmd
# ./vpncmd
  => 여기서는 VPN Server admin password만을 설정하도록 한다. 나머지 복잡한 설정은 VPN Server manager를 통해 진행할 것이다.

[그림 6.8] vpncmd 실행 후 VPN Server admin password 지정 모습

다음으로 VPN Server Manager를 이용하여 VPN Server에 대한 상세 설정을 진행한다(자세한 설정 과정은 생략).
Windows 10 PC(VPN Server Manager) ==> MACCHIATObin board(LAN port)
📌  VPN Server Manager  & VPN Client Manager 상세 설정 방법은 아래 site에 자세히 정리해 두었으니, 이를 참조하도록 하자(같은 내용을 반복해서 설명하지는 않겠음^^).

VPN Server 설정이 끝난 후에는 아래와 같이 bridge 및 dhcp server 설정을 진행하도록 하자.

<Target board>
  => 먼저 bridge 설정을 해 준다. bridge 설정 대상은 eth0와 tap_vpn(VPN server 설정시 생성되는 tap 기반 interface)이다.
root@localhost:~# ifconfig eth0 0.0.0.0
root@localhost:~# brctl addbr br0
root@localhost:~# brctl addif br0 eth0
root@localhost:~# brctl addif br0 tap_vpn
   => 얘는 VPN Server Manager에서 설정해 주어야만 생성이 된다.
root@localhost:~# ifconfig br0 192.168.2.1 netmask 255.255.255.0 up
root@localhost:~# brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.005182112200       no              eth0
                                                                             tap_vpn

다음으로 br0 interface(LAN interface)를 대상으로 dhcp server를 구동시켜 주어야 한다. 
📌  dhcp server가 없으면, vpn client가 vpn server에 연결한 후, ip 주소 & default gw 설정을 못하게 되어 vpn 연결이 완성되지 못하는 문제가 발생한다.

root@localhost:~# apt-get install isc-dhcp-server
root@localhost:~# vi /etc/dhcp/dhcpd.conf 
  => 기존 내용을 모두 지우고, 아래 정보를 입력하도록 한다.

[그림 6.9] /etc/dhcp/dhcpd.conf 수정 내용
root@localhost:~# vi /etc/default/isc-dhcp-server
  => 아래와 같이 br0 interface 정보를 추가한다.
...
INTERFACES="br0"
~
root@localhost:~# systemctl restart isc-dhcp-server
  => dhcp server를 재구동시킨다.

지금까지 설정한 내용(vi 편집 내용 제외)을 하나의 shell script로 정리해 보면 다음과 같다. 부팅시 이 파일이 실행되도록 만들어 주면 편리할 것이다.

[그림 6.10] MACCHIATObin network 설정 내용 정리

자, 모든 것(VPN Server 설정)이 준비되었으니, VPN Client(Windows 10)에서 VPN Client Manager를 통해 vpn 연결을 시도해 보기로 하자(VPN client manager 설정 화면에 대한 설명은 생략).

               Windows 10 PC(VPN Client Manager) => 인터넷 => MACCHIATObin board(WAN port)

VPN 연결이 성공한 후 ip  정보를 확인해 보니, 마치 VPN Gateway 내부에 위치한 것 처럼 ip: 192.168.2.101이고, default gateway: 192.168.2.1 임을 알 수 있다.

[그림 6.11] VPN 연결 후, VPN Client ip 주소 할당 내용 확인

C:\> ping www.google.co.kr
  => OK, ping도 정상이다.

C:\> ssh chyi@192.168.2.1
  => Target board로 ssh login도 가능하다.

[그림 6.12] VPN Client PC에서 VPN Server에 ssh login 모습 

📌  SoftEther VPN(a.k.a SSL VPN)은 Ethernet Frame을 통째로 tunnel에 넣어 전달하기 때문에, (VPN Client가 설치된 PC 입장에서 볼 때) 마치 긴 LAN cable을 VPN Gateway의 LAN port에 연결한 듯한 착각을 일으키게 하는 특징(장점)이 있다. 쉽게 말해 집에 있지만 사무실 망에 연결되어 있는 것과 동일한 효과를 내게 해준다.

끝으로 아래 그림은 VPN client PC에서 (VPN server를 경유하여) internet 접속(web browser)을 할 경우의 최대 CPU 사용량(max: 50% 육박)을 보여주고 있다. 50%는 최악의 상황인 듯 보이며, (편차가 있기는 하지만) 대략 20 ~ 30% 정도의 CPU 점유율을 보여준다(생각보다 좀 많이 먹는 느낌이다).

[그림 6.13] VPN Server의 CPU 점유율 

이상으로 MACCHIATObin 보드에 SoftEther VPN server를 올리고, VPN Client로 접속(Remote access)하는 시험을 해 보았다. 실험 결과, 성능 좋은 Intel CPU 보다는 못하지만, MACCHIATObin을 SoftEther VPN Gateway로 사용하지 못할 이유는 없어  보인다^^.

______________________________

지금까지 MACCHIATObin 보드를 가지고, 이런 저런 시험을 진행해 보았다. 제목에서 보듯이 이번 blog post의 원래 의도는 5장(Ethernet device driver 분석)을 상세히 소개하는 것이었으나, 내용 정리를 하다보니 docker와 SoftEther VPN(6장)에 관한 내용으로 설명이 편중된 느낌이다.

언제나 그렇듯 미흡한 부분은 추후 좀 더 보충할 것을 기약하며 이번 글을 마치도록 하겠다. 이 글이 어느 누군가에게 조금이라도 도움이 되었으면 하는 바램을 가져본다. 😋



7. References
[2] ESPRESSObin ULTRA- Quick Start Guide -Rev 03
[3] Ethernet switch support in the Linux kernel, Alexandre Belloni, Bootlin
[4] From the Ethernet MAC to the link partner, Maxime Chevallier, Antoine Ténart, Bootlin
[6] marvell-link-street-88E6341-product-brief.pdf
[7] http://wiki.macchiatobin.net/tiki-index.php?page=BSP+HowTo
[8] https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04
[9] http://pyrasis.com/Docker/Docker-HOWTO#ps
[10] https://www.stereolabs.com/docs/docker/building-arm-container-on-x86/


SlowBoot