2024년 5월 30일 목요일

NanoPi와 Wireguard-Go로 Quantum Resistant VPN Router 만들기

이번 posting에서는 NanoPi R2S Plus board를 가지고 Quantum Safe WireGuard-Go 기반 초소형 VPN router를 만드는 과정을 소개해 보고자 한다.  😎


목차
1. Quantum Resistant Wireguard-Go
2. NanoPi R2S Plus Overview
3. NanoPi R2S Plus 용 f/w image build 및 write 하기
4. Quantum Resistant WireGuard-Go 돌려 보기
5. 그 밖의 흥미로운 주제
6. References
keyword: wireguard-go, nanopi r2s plus, pqc, 10GbE throughput, wireguard-ui


日新又日新 (일신우일신)
"진실로 새로워지기 위해서는, 날마다 새로워야 하고 또 새로워야 한다"

안녕, 이크테크 ~ 이제 부터 다시 시작이다. 🌈

1. Quantum Resistant WireGuard-Go

Linux용 wireguard는 처음부터 kernel 기반으로 개발되었기 때문에, 사용자 영역에서 동작하는 방식인 wireguard-go 코드가 상대적으로 덜 중요한 것으로 인식되어 온 것이 사실이다. 그도 그럴 것이  Wireguard 핵심 개발자(창시자)인 Jason A. Donenfeld는 wireguard-go code 안에 (linux의 경우는) kernel version을 사용할 것을 권고하는 경고 문구를 넣어 두기까지 하였으니, 두말하면 잔소리인 셈이다. 필자의 경우도 이제껏 wireguard kernel code만을 주요 대상으로 삼았을 뿐, wireguard-go code에는 그다지 관심을 두지 않았던 것도 사실이다. 😓

하지만, 최근들어 wireguard-go가 kernel version에 비해 우수한 성능(10GbE 환경에서의 throughput)을 낼 수 있다는 사실이 알려지면서, wireguard-go 버젼의 가능성(필요성)이 다시 재조명되기 시작하고 있다(최소한 필자에게는 😎). 따라서 이번 post에서는 오로지 wireguard-go 코드만을 대상으로 내용 전개를 이어가 보고자 한다.

<여기서 잠깐 ! - wireguard-go를 이용하여 10GbE 환경에서의 성능 개선에 관하여>
------------------------------------------------------------------------------------------------------------
Kernel version의 wireguard 사용 시, 1GbE 환경에서는 거의 wire speed에 육박하는 성능치를 보여주지만, 10GbE 환경에서 1-2Gbps 정도의 throughput 밖에 나오지 않는 심각한 문제가 있다.

# iperf3 -c 10.10.1.2 -b 10G -w 4096K

[그림 1.1] 10GbE 환경에서의 kernel wireguard  udp 성능 측정 결과

아래 link는 tailscale사 엔지니어가 wireguard-go를 이용해서 10GbE 환경에서 성능을 끌어올린 아주 흥미로운 내용을 소개하고 있다. 내용이 조금은 어려울 수도 있으니, 읽기전 마음의 준비를 하기 바란다. 😕


[그림 1.2] 10GbE 환경에서의 kernel wireguard vs wireguard-go의 tcp 성능 비교[출처: 참고문헌 8]

[그림 1.3] 25GbE 지원하는 NIC 사용 시, kernel wireguard vs wireguard-go의 tcp 성능 비교[출처: 참고문헌 8]
------------------------------------------------------
------------------------------------------------------


a) WireGuard-Go의 구조(패킷 흐름)
우선 아래 site에서 wireguard-go code를 download 받아 build해 보도록 하자.


<How to download and build the wireguard-go>
$ git clone https://git.zx2c4.com/wireguard-go
$ cd wireguard-go
$ make

외관상으로 볼 때, wireguard-go는 tun device를 사용하는 여타의 vpn s/w의 구조와 크게 다를 바가 없다. 물론 깊숙히 들여다 보면, 코드의 난이도가 매우 높으며, 상당히 수준 높은 코드로 구성되어 있음을 금방 알게 된다. 😍 


[그림 1.4] WireGuard-Go 패킷 처리 구조(1) - tun0/eth0 간의 packet proxy 역할


[그림 1.5] WireGuard-Go 패킷 처리 구조(2) [출처: 참고문헌 7]

[그림 1.6] WireGuard-Go 패킷 처리 구조(3) - 2개의 NanoPi 간 통신

자, 그럼 지금부터 간략하게 나마 wireguard-go code를 파헤쳐(?) 보도록 하자.

<wireguard-go main routine에서 하는 일>
 ----> wireguard-go/main.go 
 a) tun device 생성 
    => tun device open, tun device read 함수 정의, tun 관련 event 처리 routine 정의 
 b) wireguard device 생성
    => 3개의 queue(handshaking, encryption, decryption) 생성, 3개의 go routine(handshaking, encryption, decryption worker) 호출
 c) UAPI 처리 코드
    => wg tool을 통해 내려주는 설정을 받아주는 코드(unix domain socket 코드)
 d) 기타 daemon화 및 로그 초기화 등 진행

wireguard-go는 위의 b) 단계에서 wireguard device(예: wg0)를 생성하면서 아래와 같이 3개의 queue(go 채널)를 만드는데, 이 3개의 queue는 이어서 설명하는 패킷 처리 routine과 자연스럽게 연결된다.

  • device.queue.handshake = newHandshakeQueue() //handshaking message용 queue
  • device.queue.encryption = newOutboundQueue() //encryption을 하기 전에 대기하는 queue
  • device.queue.decryption = newInboundQueue() //decryption을 하기 전에 대기하는 queue

<wireguard-go code의 패킷 처리>
 a) device.RoutineHandshake( ) : peer와의 tunnel을 형성하기 위한 noise handshaking 과정
 b) device.RoutineEncryption( ) : tun device로 부터 패킷 수신 후, tunneling 처리. 이어서 tunnel 처리된 패킷을 eth0 device로 전송하는 과정 (주의: 실제로 이 함수는 암호화 및 tunnel 처리만 담당)
 c) device.RoutineDecryption( ) : eth0 device로 부터 tunnel packet 수신 후, tunnel 제거. 이어서 tunnel 제거된 패킷을 tun device로 전송하는 과정 (주의: 실제로 이 함수는 터널 제거 및 복호화 처리만 담당)


wireguard-go code에는 중요한 파일들이 많이 있으나, 위에서 설명한 내용으로 국한시켜 정리해 보면 다음과 같은 파일들이 눈에 들어온다.

<wireguard-go 주요 패킷 흐름과 연관된 파일>
  • main.go
  • tun/tun_linux.go
  • device/device.go, send.go, receive.go, noise-protocol.go, peer.go
  • ipc/uapi_linux.go

wireguard-go의 대략적인 코드 흐름(혹은 패킷 flow)을 살펴 보았으니, 이제 부터는 wireguard-go code에 pqc code를 통합시켜 보도록 하자.
📌 당초 계획은 wireguard-go code를 아주 면밀히 분석/정리하는 것이었으나, 생각처럼 쉽지가 않다. 😔

b) PQ-WireGuard 프로토콜 review

PQ-WireGuard 프로토콜과 관련해서는 이미 이전 blog(5절)를 통해서 상세히 소개한 바 있다. 따라서 PQ-WireGuard 관련 자세한 사항은 아래 link를 참조하기 바란다.




[그림 1.7] WireGuard vs PQ-WireGuard




[그림 1.8] PQ-WireGuard 프로토콜 요약 - Noise IK + Kyber KEM


c) wireguard-go에 PQC 적용하기
이전 blog에서는 wireguard kernel code에 PQC(Kyber768) 알고리즘을 적용해 보았다면, 이번 시간에는 wireguard-go code에 kyber1024를 적용하는 방법(kyber768 적용과 동일)을 소개해 보고자 한다.


Go언어로 구현된 Kyber code는 여러가지가 있지만, 본 blog에서는 아래 code를 사용하는 것으로 하였다.


먼저, Kyber-k2so 코드의 간단한 사용 방법을 정리해 보면 다음과 같다.
==================================================================
go get -u github.com/symbolicsoft/kyber-k2so
package main

import (
	kyberk2so "github.com/symbolicsoft/kyber-k2so"
)

func main() {
	privateKey, publicKey, _ := kyberk2so.KemKeypair768()
	ciphertext, ssA, _ := kyberk2so.KemEncrypt768(publicKey)
	ssB, _ := kyberk2so.KemDecrypt768(ciphertext, privateKey)
}


==================================================================

자, 그럼 본격적으로 kyber1024 code를 noise protocol에 추가해 보도록 하자. 작업할 내용이 많을 것 같지만, 수정이 필요한 파일은 오직 하나 즉, wireguard-go/device/noise-protocol.go 파일 뿐이다.

<noise-protocol.go 파일 수정 절차>
단계#1> kyberk2so package import 하기
import kyberk2so "github.com/symbolicsoft/kyber-k2so"

단계#2> Packet size 설정 값 변경하기
const (
    MessageInitiationSize      = (148 + kyberk2so.Kyber1024PKBytes)             // size of handshake initiation message
    MessageResponseSize        = (92 + kyberk2so.Kyber1024CTBytes)              // size of response message
    MessageCookieReplySize     = 64                                            // size of cookie reply message
    MessageTransportHeaderSize = 16                                            // size of data preceding content in transport message
    MessageTransportSize       = MessageTransportHeaderSize + poly1305.TagSize // size of empty transport
    MessageKeepaliveSize       = MessageTransportSize                          // size of keepalive
    MessageHandshakeSize       = MessageInitiationSize                         // size of largest handshake related message
)

단계#3> Initiation Message 수정하기
type MessageInitiation struct {
    Type      uint32
    Sender    uint32
    Ephemeral NoisePublicKey
    Static    [NoisePublicKeySize + poly1305.TagSize]byte
    Timestamp [tai64n.TimestampSize + poly1305.TagSize]byte
    PqcPublickey [kyberk2so.Kyber1024PKBytes]byte
    MAC1      [blake2s.Size128]byte
    MAC2      [blake2s.Size128]byte
}
📌 PqcPublickey의 첫글자는 반드시 대문자이어야 한다. 그 이유는 다른 package code에서 이 필드를 참조하기 때문이다. 소문자로 할 경우, panic error가 발생하므로 주의를 요한다.

단계#4> Response Message 수정하기
type MessageResponse struct { Type uint32 Sender uint32 Receiver uint32 Ephemeral NoisePublicKey Empty [poly1305.TagSize]byte PqcCiphertext [kyberk2so.Kyber1024CTBytes]byte MAC1 [blake2s.Size128]byte MAC2 [blake2s.Size128]byte }

단계#5> Noise Protocol 코드 수정하기
Noise protocol에 kyber1024 code를 추가하는 것은 어렵지 않다. 먼저 위의 그림 1.8을 자세히 살펴보고, 그에 맞게 적절히 작업해 주기만 하면 된다.

<kyber1024 keypair 생성 단계>
func (device *Device) CreateMessageInitiation(peer *Peer) (*MessageInitiation, error) {
    ...
    var pqcerr error     handshake.pqcKem.pqcSK, handshake.pqcKem.pqcPK, pqcerr = kyberk2so.KemKeypair1024()     if pqcerr == nil {
        msg.PqcPublickey = handshake.pqcKem.pqcPK
    } else {
        fmt.Printf("kyberk2so.KemKeypair1024() failed.\n") }
    ...
}

<kyber1024 encapsulation 단계>
func (device *Device) ConsumeMessageInitiation(msg *MessageInitiation) *Peer {
    ...
    var pqcerr error
    handshake.pqcCiphertext, handshake.presharedKey, pqcerr = kyberk2so.KemEncrypt1024(msg.PqcPublickey)
    if pqcerr != nil {
        fmt.Printf("kyberk2so.KemEncrypt1024() failed.\n")
    }
    ...
}

<kyber1024 ciphertext 복사 단계>
func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error) {
    ...
    msg.PqcCiphertext = handshake.pqcCiphertext
    ...
}

<kyber1024 decapsulation 단계>
func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
    ...
    var pqcerr error
handshake.presharedKey, pqcerr = kyberk2so.KemDecrypt1024(msg.PqcCiphertext, handshake.pqcKem.pqcSK)
    if pqcerr != nil {
        fmt.Printf("kyberk2so.KemDecrypt1024() failed.\n")
    }
    ...
}

위의 내용이 적용된 코드는 아래 위치에서 확인 가능하다. 😋



<여기서 잠깐 ! - Rosenpass 프로토콜에 관하여>
==============================================================
WireGuard와 PQC 알고리즘을 접목하는 방법은 여러가지가 있을 수 있다. 아래에 소개하는 Rosenpass project도 그 중의 하나이다.


[그림 1.9] Rosenpass site


"Rosenpass implements a post-quantum-secure key exchange in the spirit of the Noise Protocol Framework. "


[그림 1.10] Rosenpass Key Exchange Protocol


[그림 1.11] Rosenpass Message Types

Rosenpass는 userspace daemon형태로 구현되어 있으며, 아래 그림과 같은 방식으로 wireguard(kernel)와 통신한다.


[그림 1.12] Rosenpass + WireGuard


Rosenpass는 Rust로 구현되어 있다. 먼저 Build만 간단히 진행해 보면 다음과 같다.

<Ubuntu 22.04>

git clone https://github.com/rosenpass/rosenpass
cd rosenpass
$ ls -aF
./              .github/         LICENSE-APACHE  config-examples/      fuzz/       rp/
../             .gitignore       LICENSE-MIT     constant-time/        marzipan/   secret-memory/
.ci/            .gitlab-ci.yml   analysis/       doc/                  oqs/        to/
.devcontainer/  .prettierignore  analyze.sh*     flake.lock            papers/     util/
.envrc          Cargo.lock       cipher-traits/  flake.nix             readme.md
.git/           Cargo.toml       ciphers/        format_rust_code.sh*  rosenpass/
chyi@earth:/mnt/hdd/workspace/download/rosenpass$ vi readme.md 

cargo build
    Updating crates.io index
  Downloaded adler v1.0.2
  Downloaded anstyle-parse v0.2.2
  Downloaded anstyle-query v1.0.0
  Downloaded anstyle v1.0.4
  Downloaded addr2line v0.21.0
  Downloaded allocator-api2 v0.2.18
  Downloaded anyhow v1.0.82
  Downloaded aead v0.5.2
  Downloaded block-buffer v0.10.4
  Downloaded anstream v0.6.13
  Downloaded bitflags v2.4.1
  Downloaded aho-corasick v1.1.2
  Downloaded byteorder v1.5.0
  Downloaded blake2 v0.10.6
  Downloaded autocfg v1.1.0
  Downloaded pin-utils v0.1.0
  Downloaded clang-sys v1.6.1
  Downloaded cexpr v0.6.0
  Downloaded clap_derive v4.5.4
  Downloaded bitflags v1.3.2
  Downloaded clap v4.5.4
  Downloaded chacha20 v0.9.1
  Downloaded bytes v1.6.0
  Downloaded base64 v0.21.7
  Downloaded equivalent v1.0.1
  Downloaded colorchoice v1.0.0
  Downloaded memsec v0.6.3
  Downloaded cfg-if v1.0.0
  Downloaded cmake v0.1.50
  Downloaded futures-macro v0.3.30
  Downloaded fnv v1.0.7
  Downloaded cpufeatures v0.2.12
  Downloaded clap_lex v0.7.0
  Downloaded digest v0.10.7
  Downloaded cipher v0.4.4
  Downloaded num_cpus v1.16.0
...

cd target/debug
$ ls -la
합계 129948
drwxrwxr-x   7 chyi chyi     4096  4월 26 15:09 .
drwxrwxr-x   3 chyi chyi     4096  4월 26 15:09 ..
-rw-rw-r--   1 chyi chyi        0  4월 26 15:09 .cargo-lock
drwxrwxr-x 228 chyi chyi    16384  4월 26 15:09 .fingerprint
drwxrwxr-x  50 chyi chyi     4096  4월 26 15:09 build
drwxrwxr-x   2 chyi chyi    65536  4월 26 15:09 deps
drwxrwxr-x   2 chyi chyi     4096  4월 26 15:09 examples
drwxrwxr-x  13 chyi chyi     4096  4월 26 15:09 incremental
-rw-rw-r--   1 chyi chyi     3404  4월 26 15:09 librosenpass.d
-rw-rw-r--   2 chyi chyi 17500318  4월 26 15:09 librosenpass.rlib
-rwxrwxr-x   2 chyi chyi 52229312  4월 26 15:09 rosenpass
-rw-rw-r--   1 chyi chyi     3456  4월 26 15:09 rosenpass.d
-rwxrwxr-x   2 chyi chyi 63215256  4월 26 15:09 rp
-rw-rw-r--   1 chyi chyi     3603  4월 26 15:09 rp.d

실제로 rosenpass daemon과 wireguard kernel을 연동하는 시험은 독자 여러분의 몫으로 남긴다. 😓

그런데, rosenpass를 GoLang으로 porting한 것은 없을까 ? 아래 site에 그 해답이 있다.


==============================================================


2. NanoPi R2S Plus Overview

FriendlyElec사의 NanoPi R2S Plus를 하나 구입했다. 이유는 PQC WireGuard VPN Router를 만들어 보기 위해서이다. 이쯤되면 FriendlyElec사로 부터 상(?)이라도 하나 받아야 하지 않을까 ? 😛


https://www.friendlyelec.com/index.php?route=product/product&product_id=296


<NanoPi R2S Plus 선택 이유>

  • 가격이 아주 저렴하다. Case 포함 $36 (단, Power adapter는 제외) => 다해도 $40면 충분!

  • 2개의 Ethernet port(WAN port는 USB 3.0 기반임)가 나와 있어 Security Gateway로 만들 수 있다.

  • eMMC(32GB)가 장착되어 있다. microSD로만 부팅하는 구조의 경우 보안상 문제가 될 수 있다.

  • Wi-Fi chipset이 장착되어 있다(이건 option이고, $9.99 추가 비용 발생).

  • Case외부에 debug interface(USB 3.0)가 나와 있다.

  • NanoPi는 OrangePi 등 타 Pi 류에 비해 상당히 안정적이다(상품화하는데 무리가 없음).


[그림 2.1] NanoPi R2S Plus(1) - 주요 특징



[그림 2.2] NanoPi R2S Plus(2) - 보드 top/bottom



[그림 2.3] NanoPi R4S Plus(3) - Metal Case



[그림 2.4] NanoPi R4S Plus(4) - Cortex-A53 CPU Core(Quad-core)




[그림 2.5] NanoPi R4S Plus(5) - Network Speed




[그림 2.6] NanoPi R2S Plus - Specifications


[그림 2.7] NanoPi R2S Plus 부팅 모습(Serial Console: 1500000, 8N1)


보다 자세한 사항은 아래 link를 참조하기 바란다.

https://wiki.friendlyelec.com/wiki/index.php/NanoPi_R2S_Plus


3. NanoPi 용 f/w image build 및 write 하기

이번 장에서는 friendlywrt-rk3328(OpenWrt) source code를 내려 받아 build하는 과정을 소개하고자 한다. 이번 장에서 소개하는 내용은 아래 wiki page의 9절 내용(Build Openwrt/Friendlywrt)을 참조하였다.


https://wiki.friendlyelec.com/wiki/index.php/NanoPi_R2S_Plus


📌 NanoPi R2S Plus에는 다양한 linux 배포판이 탑재될 수 있다. 하지만, 이번 posting에서는 OpenWrt만 관심이 있는 바, 이번에 소개하는 내용은 friendlywrt(NanoPi용 openwrt)로 국한하기로 한다.


a) source code download

$ mkdir friendlywrt23-rk3328

$ cd friendlywrt23-rk3328

$ git clone https://github.com/friendlyarm/repo --depth 1 tools

$ tools/repo init -u https://github.com/friendlyarm/friendlywrt_manifests -b master-v23.05 -m rk3328.xml --repo-url=https://github.com/friendlyarm/repo  --no-clone-bundle

$ tools/repo sync -c  --no-clone-bundle


b) source code build하기

<Build 전 준비 사항>

  • Python2가 설치되어 있어야 함. Python3로 할 경우, 에러 발생함.

  • Ubuntu 22.04에서 build 함. 하위 버젼에서 할 경우, 에러 발생함.

  • Intel i7 processor, RAM이 최소 16GB 정도는 되어야 build를 빨리 끝낼 수 있다.


$ ./build.sh rk3328.mk

  • 얘는 u-boot, kernel, friendlywrt(OpenWrt)를 차례로 build함.
  • 에러#1 : u-boot 관련 error 발생
  • 에러#2 : friendlywrt 관련 packages 디렉토리 내용이 없다는 식의 에러 발생


<build 시 아래 에러#1 발생>

using config device/friendlyelec/rk3328/rk3328.mk

============Start building uboot============

SRC = /mnt/hdd/workspace/mini_devices/04062024/friendlywrt23-rk3328/u-boot

TARGET_ARCH = arm64

TARGET_PLAT = rk3328

TARGET_UBOOT_CONFIG=nanopi_r2_defconfig

TARGET_OSNAME = friendlywrt

=========================================

uboot src: /mnt/hdd/workspace/mini_devices/04062024/friendlywrt23-rk3328/u-boot

make for nanopi_r2_defconfig by -j48

  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

#

scripts/kconfig/conf  --silentoldconfig Kconfig

  CHK     include/config.h

  UPD     include/config.h

… 

DTC     arch/arm/dts/rk3288-popmetal.dtb

  DTC     arch/arm/dts/rk3288-rock2-square.dtb

  DTC     arch/arm/dts/rk3288-tinker.dtb

  DTC     arch/arm/dts/rk3288-veyron-jerry.dtb

Error: arch/arm/dts/.rk3036-sdk.dtb.pre.tmp:77.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3036-sdk.dtb] Error 1

make[2]: *** Waiting for unfinished jobs....

  DTC     arch/arm/dts/rk3288-veyron-mickey.dtb

Error: arch/arm/dts/.rk3188-radxarock.dtb.pre.tmp:383.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3188-radxarock.dtb] Error 1

make[1]: *** [dts/Makefile:101: arch-dtbs] Error 2

make: *** [Makefile:883: dts/dt.dtb] Error 2

make: *** Waiting for unfinished jobs....



아무래도 u-boot build 시 에러가 발생하는 듯 싶다. 그렇다면 U-boot만 단독으로 build해 보자.


$ ./build.sh uboot

  • dtb 관련 build error 발생 
  • u-boot/Makefile 파일 수정
  • u-boot/arch/arm/dts/Makefile 파일 수정


<u-boot build 시 아래 발생>

DTC     arch/arm/dts/rk3368-sheep.dtb

  DTC     arch/arm/dts/rk3368-geekbox.dtb

Error: arch/arm/dts/.rk3036-sdk.dtb.pre.tmp:77.1-10 syntax error

FATAL ERROR: Unable to parse input tree

Error: arch/arm/dts/.rk3188-radxarock.dtb.pre.tmp:383.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3036-sdk.dtb] 오류 1

make[2]: *** 끝나지 않은 작업을 기다리고 있습니다....

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3188-radxarock.dtb] 오류 1

  DTC     arch/arm/dts/rk3368-px5-evb.dtb

Error: arch/arm/dts/.rk3368-geekbox.dtb.pre.tmp:320.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3368-geekbox.dtb] 오류 1

Error: arch/arm/dts/.rk3368-sheep.dtb.pre.tmp:288.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3368-sheep.dtb] 오류 1

Error: arch/arm/dts/.rk3368-px5-evb.dtb.pre.tmp:335.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3368-px5-evb.dtb] 오류 1

Error: arch/arm/dts/.rk3368-lion.dtb.pre.tmp:196.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3368-lion.dtb] 오류 1

Error: arch/arm/dts/.rk3328-evb.dtb.pre.tmp:291.1-10 syntax error

FATAL ERROR: Unable to parse input tree

make[2]: *** [scripts/Makefile.lib:319: arch/arm/dts/rk3328-evb.dtb] 오류 1

make[1]: *** [dts/Makefile:101: arch-dtbs] 오류 2

make: *** [Makefile:883: dts/dt.dtb] 오류 2

make: *** 끝나지 않은 작업을 기다리고 있습니다....

----------------------------------------------


<u-boot에러 이므로, 아래와 같이 수정>

u-boot/Makefile

================


KBUILD_CFLAGS   += $(call cc-disable-warning, address-of-packed-member)

# -- -- --

KBUILD_CFLAGS   += -fshort-wchar -Werror

KBUILD_AFLAGS   := -D__ASSEMBLY__



u-boot/arch/arm/dts/Makefile

=============================


dtb-$(CONFIG_ARCH_ROCKCHIP) += \           # 이 아래의 dtb 중 build 에러 발생시키는 것들은 과감히 제거 (아래에 표시된 내용만 남기고 나머지를 제거하면 됨)

    rk3066a-mk808.dtb \

    rk3126-evb.dtb \

    rk3128-evb.dtb \

    rk3288-evb.dtb \

    rk3288-fennec.dtb \

    rk3288-firefly.dtb \

    rk3288-miqi.dtb \

    rk3288-phycore-rdk.dtb \

    rk3288-popmetal.dtb \

    rk3288-rock2-square.dtb \

    rk3288-tinker.dtb \

    rk3288-veyron-jerry.dtb \

    rk3288-veyron-mickey.dtb \

    rk3288-veyron-minnie.dtb \

    rk3288-vyasa.dtb \

    rk3308-evb.dtb \

    rv1108-evb.dtb

dtb-$(CONFIG_ARCH_MESON) += \

    meson-gxbb-odroidc2.dtb



📌 u-boot은 왠만해서는 nano pi에서 기본적으로 제공하는 것을 교체 없이 그대로 사용할 것이다. 실제로 eMMC flash writing 시에도 u-boot은 교체되지 않는다(kernel과 rootfs 부분만 교체될 것임).


$ ./build.sh rk3328.mk

-> u-boot 수정 후, 다시 build를 시도하면 이번에는 아래와 같이 friendlywrt 부분에서 에러가 나면서 종료된다.

📌 사실 다른 PC(Ubuntu 22.04 LTS, Intel i7, RAM 32GB)에서는 이 단계에서 곧 바로 build에 성공하였다.


<build 시 아래 에러#2 발생>


Checking 'fts.h'... ok.

Checking 'obstack.h'... ok.

Checking 'libintl.h'... ok.

Checking 'ldconfig-stub'... ok.

find: 'feeds/packages': No such file or directory

Collecting package info: done

find: 'feeds/packages': No such file or directory

Collecting target info: done

Create index file './feeds/luci.index' 

Collecting package info: done

Collecting target info: done

Create index file './feeds/routing.index' 

Collecting package info: done

Collecting target info: done

Create index file './feeds/telephony.index' 

ERROR: please fix feeds/telephony/net/freeswitch/Makefile - see logs/feeds/telephony/net/freeswitch/dump.txt for details

ERROR: please fix feeds/telephony/net/kamailio/Makefile - see logs/feeds/telephony/net/kamailio/dump.txt for details

Collecting package info: done

Collecting target info: done

====Building friendlywrt failed!====



$ ./build.sh friendlywrt

-> friendlywrt만 단독 build해 본다.

-> 위와 동일한 에러가 발생한다.


그렇다면, friendlywrt를 손으로(?) build해 보자.

$ cd friendlywrt/

$ make -j1 V=s

  • 위 명령을 실행하면, 곧 이어 menuconfig 화면 나옴. menuconfig 화면에서 아래와 같이 상단 설정 변경 후, Exit
  • 이후 한참동안 build 진행 => 어라, 얘는 정상 build 된다.


[그림 3.1] friendlywrt menuconfig


이 상태에서 ./build.sh rk3328.mk를 하면 희한하게 friendlywrt에서 다시 error가 난다. 따라서 friendlywrt를 다시 build해 보자.

$ ./build.sh friendlywrt

  • 희한하게, build OK


다시, 전체 build를 다시 시도하자.

$ ./build.sh rk3328.mk

  • 어라, 이제는 된다. 참으로 이상하다.


0+0 레코드 들어옴

0+0 레코드 나감

0 bytes copied, 0.000259573 s, 0.0 kB/s

----------------------------------------------------------------

[out/friendlywrt_23.05_20240330_rk3328_arm64_sd.img] capacity = 1430MB, 1499999232 bytes

current out/friendlywrt_23.05_20240330_rk3328_arm64_sd.img partition:

----------------------------------------------------------------

parsing ./friendlywrt23/parameter.txt:

create new GPT 9:

----------------------------------------------------------------

copy from: ./friendlywrt23 to out/friendlywrt_23.05_20240330_rk3328_arm64_sd.img

 [RAW. 0]:      100 KB | ./friendlywrt23/idbloader.img  > 100% : done.

 [RAW. 1]:     4096 KB | ./friendlywrt23/uboot.img      > 100% : done.

 [RAW. 2]:     4096 KB | ./friendlywrt23/trust.img      > 100% : done.

 [RAW. 3]:       48 KB | ./friendlywrt23/misc.img       > 100% : done.

 [RAW. 4]:        1 KB | ./friendlywrt23/dtbo.img       > 100% : done.

 [RAW. 5]:     2895 KB | ./friendlywrt23/resource.img   > 100% : done.

 [RAW. 6]:    29240 KB | ./friendlywrt23/kernel.img     > 100% : done.

 [RAW. 7]:     7882 KB | ./friendlywrt23/boot.img       > 100% : done.

 [RAW. 8]:   356696 KB | ./friendlywrt23/rootfs.img     > 100% : done.

 [RAW. 9]:      156 KB | ./friendlywrt23/userdata.img   > 100% : done.

----------------------------------------------------------------

---------------------------------

RAW image successfully created (17:36:13).

-rw-rw-r-- 1 chyi chyi 1499999232  3월 30 17:36 out/friendlywrt_23.05_20240330_rk3328_arm64_sd.img

Tip: You can compress it to save disk space.

-----------------------------------------

Run the following command for sdcard install:

sudo dd if=out/friendlywrt_23.05_20240330_rk3328_arm64_sd.img bs=1M of=/dev/sdX


$ cd out

chyi@earth:/mnt/hdd/workspace/mini_devices/friendlywrt23-rk3328/out$ ls -la

합계 563048

drwxrwxr-x 13 chyi chyi       4096  3월 30 17:36 .

drwxrwxr-x  9 chyi chyi       4096  3월 30 17:35 ..

-rw-rw-r--  1 chyi chyi        167  3월 30 16:11 .gitignore

drwx------  2 chyi chyi       4096  3월 30 17:36 boot.r3u3AOxQu

drwxrwxr-x  7 chyi chyi       4096  3월 30 17:29 cryptodev-linux

-rw-rw-r--  1 chyi chyi 1499999232  3월 30 17:36 friendlywrt_23.05_20240330_rk3328_arm64_sd.img

-rw-rw-r--  1 chyi chyi  160552062  3월 30 17:36 friendlywrt_23.05_20240330_rk3328_arm64_sd.img.gz

drwxrwxr-x  4 chyi chyi       4096  3월 30 17:18 nft-fullcone

drwxrwxr-x  3 chyi chyi       4096  3월 30 17:29 output_rk3328_kmodules

drwxr-xr-x 19 chyi chyi       4096  3월 30 17:36 rootfs.VIYlrA6yB

drwxrwxr-x  8 chyi chyi       4096  3월 30 17:30 rtl8812au

drwxrwxr-x  9 chyi chyi       4096  3월 30 17:30 rtl8821CU

drwxrwxr-x  8 chyi chyi       4096  3월 30 17:30 rtl8822bu

drwxrwxr-x  8 chyi chyi       4096  3월 30 17:30 rtl8822ce

drwxrwxr-x  8 chyi chyi       4096  3월 30 17:30 rtl8822cs

drwxrwxr-x  4 chyi chyi      16384  3월 30 17:30 rtw88


여기까지 build 과정에 문제가 없었으니, 이제는 emmc용 image를 생성해 보자.

$ ./build.sh emmc-img

Bootloader image is fused successfully.


----------------------------------------------------------------

[/dev/loop12] capacity = 7438MB, 7799999488 bytes

current /dev/loop12 partition:

----------------------------------------------------------------

parsing ./eflasher/partmap.txt:

part.0 flash=mmc,1:loader:idb:0x8000,0x280000:idbloader.img:[RAW] ./eflasher/idbloader.img

part.1 flash=mmc,1:env:env:0x3f8000,0x8000::[RAW] 

part.2 flash=mmc,1:parm:parm:0x400000,0x400000:param4sd.txt:[RAW] ./eflasher/param4sd.txt

part.3 flash=mmc,1:uboot:raw:0x800000,0x400000:uboot.img:[RAW] ./eflasher/uboot.img

part.4 flash=mmc,1:trust:raw:0xc00000,0x400000:trust.img:[RAW] ./eflasher/trust.img

part.5 flash=mmc,1:misc:raw:0x1000000,0x400000::[RAW] 

part.6 flash=mmc,1:resc:raw:0x1400000,0xc00000:resource.img:[RAW] ./eflasher/resource.img

part.7 flash=mmc,1:kern:raw:0x2000000,0x2000000:kernel.img:[RAW] ./eflasher/kernel.img

part.8 flash=mmc,1:boot:raw:0x4000000,0x2000000:boot.img:[RAW] ./eflasher/boot.img

part.9 flash=mmc,1:data:fat:0x66000000,0x0::[MBR] 

part.10 flash=mmc,1:rootfs:ext4:0x6000000,0x60000000:rootfs.img:[MBR] ./eflasher/rootfs.img

----------------------------------------------------------------

create new MBR 2:

[MBR.0] start : 0x0066000000 size 0x0000000000 

[MBR.1] start : 0x0006000000 size 0x0060000000 

----------------------------------------------------------------

copy from: ./eflasher to /dev/loop12

 [RAW. 0]:      100 KB | ./eflasher/idbloader.img > 100% : done.

 [RAW. 2]:        1 KB | ./eflasher/param4sd.txt  > 100% : done.

 [RAW. 3]:     4096 KB | ./eflasher/uboot.img     > 100% : done.

 [RAW. 4]:     4096 KB | ./eflasher/trust.img     > 100% : done.

 [RAW. 6]:     3935 KB | ./eflasher/resource.img  > 100% : done.

 [RAW. 7]:    29112 KB | ./eflasher/kernel.img    > 100% : done.

 [RAW. 8]:     7882 KB | ./eflasher/boot.img      > 100% : done.

 [MBR. 1]:   745460 KB | ./eflasher/rootfs.img    > 100% : done.

----------------------------------------------------------------

/dev/loop12: msdos partitions 2 1

---------------------------------

All done.

exfatprogs version : 1.1.3

Creating exFAT filesystem(/dev/loop12p1, cluster size=32768)


Writing volume boot record: done

Writing backup volume boot record: done

Fat table creation: done

Allocation bitmap creation: done

Upcase table creation: done

Writing root directory entry: done

Synchronizing...


exFAT format complete!

---------------------------------

RAW image successfully created (17:59:08).

-rw-r--r-- 1 root root 7799999488  3월 30 17:59 out/rk3328-eflasher-20240330.img

Tip: You can compress it to save disk space.


copying /mnt/hdd/workspace/mini_devices/friendlywrt23-rk3328/scripts/sd-fuse/friendlywrt23/* to /tmp/media_exfat/friendlywrt23 ...

-rw-r--r-- 1 root root 7799999488  3월 30 17:59 out/friendlywrt_23.05_20240330_rk3328_arm64_eflasher.img

all done.

-----------------------------------------

Run the following command for sdcard install:

sudo dd if=out/friendlywrt_23.05_20240330_rk3328_arm64_eflasher.img bs=1M of=/dev/sdX

-----------------------------------------


<Build 절차 요약>

$ ./build.sh rk3328.mk

  • 정상이라면, 이걸로 build가 끝나야 함.


$ ./build.sh uboot

  • U-boot Makefile 2개 수정 후, build

$ cd friendlywrt/

$ make -j1 V=s

$ ./build.sh friendlywrt

  • 위의 3개를 차례로 시도하여, friendlywrt issue pass


$ ./build.sh rk3328.mk

  • 2개의 문제가 해결되었으니, 다시 build하면 OK

$ ./build.sh emmc-img

$ ./build.sh sd-img



c) friendlywrt image를 eMMC에 writing하기

<eMMC에 booting image 설치하기>

  1. 아래와 같이 eMMC용 image를 microSD에 write 한다(Ubuntu 환경).

sudo dd if=out/friendlywrt_23.05_20240330_rk3328_arm64_eflasher.img bs=1M of=/dev/sdb

📌 주의: /dev/sdb 자리에는 자신의 PC에서 microSD가 인식된 내용으로 대체해 주어야 한다.

📌 주의: 7GB size의 파일을 dd로 writing 하는데 생각보다 오랜 시간이 걸린다(10분 이상 걸림).

📌 참고: eMMC writing 방법은 microSD를 이용한 방법 외에도 (아직 해 보지는 않았지만) USB stick을 이용한 방법이 있다.


[그림 3.2] microSD와 microSD to USB converter


  1. microSD를 target board에 장착하고, 전원을 켜면, (부팅하면서) 바로 eMMC writing을 진행한다.

  2. eMMC fusing 진행 상태 확인은 Console에서 대략 확인 가능하다. 정확하게는 HDMI를 연결하면 볼 수 있다고 하는데, (번거로워) 해 보지 않았다. 또한 LED blinking으로도 확인 가능하단다.


4.4.3.1 Option 1: Install OS via TF Card

This method firstly boots a mini Linux from a TF card and then automatically runs an EFlasher utility to install the OS to eMMC. You can connect your system to an HDMI monitor and watch its progress.
This is optional. You can watch its progress by observing its LEDs as well:

By default, flashing starts automatically upon power-up, so be sure to back up the data in eMMC. If you don't want it to start automatically, you can use image file with a filename containing the words 'multiple-os' and manually select the OS you want to flash on the interface.


아래 로그는 microSD를 target board에 장착하고, 전원을 켠 후, Console 출력 내용을 확인한 것이다.


[그림 3.3] microSD로 부팅하면서 eMMC writing 하기(1)



[그림 3.4] microSD로 부팅하면서 eMMC writing 하기(2)


어라, 근데 위의 console log를 자세히 들여다 보니, 뜬금없이 Ubuntu 22.04.3 LTS가 보인다. 그렇다면, microSD에 올라간 image는 Ubuntu이며, Ubuntu가 부팅되면서 eMMC에 openwrt image를 설치하는 것으로 이해하면 될 것 같다.



d) eMMC fusing 실패시 복구 방법

만일 위의 과정이 실패할 경우, prebuilt image를 download 받아 설치하면 된다.


[그림 3.5] Prebuilt image download link(1)



[그림 3.6] Prebuilt image download link(2) - Google Drive 선택



[그림 3.7] Prebuilt image download link(3) - 아래 파일 download



Rk3328-eflasher-friendlywrt-23.05-20231223.img



설치 방법은 앞서와 동일하므로, 여기서는 생략하기로 한다.



e) WebUI를 통한 eMMC fusing 방법

WebUI로 로긴이 가능할 경우, eMMC tools 메뉴(Luci: 시스템 메뉴 -> eMMC tools 메뉴)를 통해서도 eMMC writing이 가능하다. 주의할 점은 반드시 아래 파일을 사용해야 한다는 것이다(400MB 정도의 gz 압축 파일).


friendlywrt_23.05_20240330_rk3328_arm64_sd.img.gz


[그림 3.8] webUI eMMC tools 메뉴를 통한 eMMC flash writing 하기(1)



[그림 3.9] webUI eMMC tools 메뉴를 통한 eMMC flash writing 하기(2)


이후 NanoPi R2S Plus를 부팅해 보면 다음과 같다.


[그림 3.10] eMMC writing 후, 부팅하는 모습


부팅 후, ssh login을 시도해 본다.

$ ssh root@192.168.2.1

[그림 3.11] NanoPi R2S Plus ssh login 모습


$ ps w

root@FriendlyWrt:~/workspace# ps w

  PID USER       VSZ STAT COMMAND

    1 root      1960 S    /sbin/procd

    2 root         0 SW   [kthreadd]

    3 root         0 IW<  [rcu_gp]

    4 root         0 IW<  [rcu_par_gp]

    5 root         0 IW<  [slub_flushwq]

    6 root         0 IW<  [netns]

   10 root         0 IW<  [mm_percpu_wq]

   11 root         0 IW   [rcu_tasks_rude_]

   12 root         0 IW   [rcu_tasks_trace]

   13 root         0 SW   [ksoftirqd/0]

   14 root         0 IW   [rcu_sched]

   15 root         0 SW   [migration/0]

   17 root         0 SW   [cpuhp/0]

   18 root         0 SW   [cpuhp/1]

   19 root         0 SW   [migration/1]

   20 root         0 SW   [ksoftirqd/1]

   22 root         0 IW<  [kworker/1:0H-ev]

   23 root         0 SW   [cpuhp/2]

   24 root         0 SW   [migration/2]

   25 root         0 SW   [ksoftirqd/2]

   26 root         0 IW   [kworker/2:0-eve]

   27 root         0 IW<  [kworker/2:0H-ev]

   28 root         0 SW   [cpuhp/3]

   29 root         0 SW   [migration/3]

   30 root         0 SW   [ksoftirqd/3]

   33 root         0 SW   [kdevtmpfs]

   34 root         0 IW<  [inet_frag_wq]

   35 root         0 SW   [kauditd]

   36 root         0 SW   [khungtaskd]

   37 root         0 SW   [oom_reaper]

   39 root         0 IW<  [writeback]

   40 root         0 SW   [kcompactd0]

   41 root         0 SWN  [khugepaged]

   42 root         0 IW<  [cryptd]

   43 root         0 IW<  [kintegrityd]

   44 root         0 IW<  [kblockd]

   45 root         0 IW<  [blkcg_punt_bio]

   46 root         0 IW<  [ata_sff]

   47 root         0 IW<  [devfreq_wq]

   49 root         0 SW   [watchdogd]

   51 root         0 IW   [kworker/1:1-eve]

   54 root         0 IW<  [rpciod]

   55 root         0 IW<  [xprtiod]

   56 root         0 IW   [kworker/3:1-eve]

   58 root         0 SW   [kswapd0]

   59 root         0 IW<  [nfsiod]

   60 root         0 IW<  [kthrotld]

   66 root         0 SW   [irq/22-rockchip]

   67 root         0 SW   [irq/23-rockchip]

   68 root         0 SW   [irq/24-rockchip]

   69 root         0 SW   [irq/25-rockchip]

   70 root         0 SW   [hwrng]

   71 root         0 IW<  [nvme-wq]

   72 root         0 IW<  [nvme-reset-wq]

   73 root         0 IW<  [nvme-delete-wq]

   74 root         0 IW<  [stmmac_wq]

   76 root         0 SW   [irq/38-rk805]

   77 root         0 IW   [kworker/1:2-eve]

   78 root         0 SW   [irq/47-rockchip]

   79 root         0 IW<  [dm_bufio_cache]

   80 root         0 IW<  [mld]

   81 root         0 IW<  [ipv6_addrconf]

   82 root       824 S    bpfilter_umh

   83 root         0 SW<  [krfcommd]

   86 root         0 IW<  [zswap-shrink]

   87 root         0 IW<  [kworker/u9:0]

  195 root         0 IW<  [kworker/3:1H-kb]

  199 root         0 IW<  [mmc_complete]

  201 root         0 IW<  [kworker/3:2H]

  211 root         0 IW<  [kworker/2:2H-kb]

  215 root         0 IW<  [kworker/1:2H-kb]

  228 root         0 SW   [jbd2/mmcblk2p8-]

  229 root         0 IW<  [ext4-rsv-conver]

  239 root         0 SW   [jbd2/mmcblk2p9-]

  240 root         0 IW<  [ext4-rsv-conver]

  256 root         0 IW<  [tpm_dev_wq]

  257 root         0 IW   [kworker/0:3-eve]

  571 ubus      1380 S    /sbin/ubusd

  594 root       912 S    /sbin/askfirst /bin/login

  664 root      1068 S    /sbin/urngd

  872 root         0 IW<  [cfg80211]

  945 root         0 IW<  [cryptodev_queue]

  970 root         0 IW<  [fullcone nft_fu]

  972 root         0 IW<  [uas]

 1211 logd      1336 S    /sbin/logd -S 64

 1266 root      4064 S    /sbin/rpcd -s /var/run/ubus/ubus.sock -t 30

 1521 root      1152 S    /usr/sbin/dropbear -F -P /var/run/dropbear.1.pid -p 22 -K 300 -T 3 -W 262144

 1786 root      2768 S    {hostapd} /sbin/ujail -t 5 -n hostapd -U network -G network -C /etc/capabilitiesi

 1787 root      2768 S    {wpa_supplicant} /sbin/ujail -t 5 -n wpa_supplicant -U network -G network -C /et 

 1790 network   3748 S    /usr/sbin/hostapd -s -g /var/run/hostapd/global

 1791 network   3712 S    /usr/sbin/wpa_supplicant -n -s -g /var/run/wpa_supplicant/global

 1851 root      2072 S    /sbin/netifd

 2106 root      1736 S    /usr/sbin/odhcpd

 2268 root      1376 S    /usr/sbin/crond -f -c /etc/crontabs -l 5

 2601 root      7856 S    /usr/sbin/uhttpd -f -h /www -r FriendlyWrt -x /cgi-bin -u /ubus -t 180 -T 30 -k p

 2638 root      1048 S    /usr/sbin/vsftpd

 2694 root      1968 S    /usr/bin/dbus-daemon --system --nofork

 2881 nobody    2396 S    avahi-daemon: running [FriendlyWrt.local]

 3051 root      5172 SN   /usr/sbin/collectd -C /tmp/collectd.conf -f

 3173 root      1656 S    /sbin/blockd

 3807 root       944 S    /usr/sbin/thd --socket /tmp/triggerhappy.socket --triggers /etc/triggerhappy/trin

 4195 root     41108 S    /usr/sbin/smbd -F

 4196 root     27780 S    /usr/sbin/nmbd -F

 4257 root      2768 S    {ntpd} /sbin/ujail -t 5 -n ntpd -U ntp -G ntp -C /etc/capabilities/ntpd.json -c b

 4261 ntp       1372 S    /usr/sbin/ntpd -n -N -S /usr/sbin/ntpd-hotplug -p time.apple.com -p ntp.tencent.o

 4338 root     39400 S    {smbd-notifyd} /usr/sbin/smbd -F

 4341 root     39392 S    {smbd-cleanupd} /usr/sbin/smbd -F

 4370 root     35772 SN   /usr/sbin/netdata -D -c /etc/netdata/netdata.conf

 4393 root     12204 SN   /usr/sbin/netdata --special-spawn-server

 4707 root      8380 S    /usr/bin/ttyd /bin/login

 5454 root       960 S    /usr/bin/wsdd2 -i br-lan -N FriendlyWrt -G WORKGROUP -b vendor:friendlyelec,modeS

 5916 root      2768 S    {dnsmasq} /sbin/ujail -t 5 -n dnsmasq -u -l -r /bin/ubus -r /etc/TZ -r /etc/dnsm-

 5918 dnsmasq   5128 S    /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmd

 5933 root      5116 S    /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmd

 6292 root      1080 S    odhcp6c -s /lib/netifd/dhcpv6.script -P0 -t120 eth0

 6295 root      1372 S    udhcpc -p /var/run/udhcpc-eth0.pid -s /lib/netifd/dhcp.script -f -t 0 -i eth0 -xC

 6487 root      1444 SN   /usr/sbin/nlbwmon -o /var/lib/nlbwmon -b 524288 -i 24h -r 30s -p /usr/share/nlbw1

 7732 root      2996 S    -bash

10759 root         0 IW   [kworker/0:0-eve]

12538 root         0 IW   [kworker/2:1-eve]

17283 root         0 IW<  [kworker/0:1H-kb]

18169 root         0 IW<  [kworker/0:0H-mm]

19323 root         0 IW   [kworker/u8:0-ev]

19550 root      2932 SN   bash /usr/lib/netdata/plugins.d/tc-qos-helper.sh 2

19603 root         0 IW   [kworker/u8:2-ev]

19878 root         0 IW   [kworker/u8:3-ev]

20037 root         0 IW   [kworker/3:0-eve]

20151 root         0 IW   [kworker/u8:1-ev]

20301 root         0 IW   [kworker/1:0-mm_]

20451 root         0 IW<  [kworker/0:2H-kb]

20458 root      1380 R    ps w

root@FriendlyWrt:~/workspace# 




[그림 3.12] NanoPi R2S Plus 구동 모습


여기까지 NanoPi R2S Plus source code를 build한 후, eMMC에 writing하기 까지의 절차를 살펴 보았다.


4. Quantum Resistant WireGuard-Go 돌려보기

이번 장에서는 PQC WireGuard-Go code를 NanoPi R2S Plus에서 돌려보는 과정을 소개하고자 한다. 자 그럼, 지금 부터 앞서 1장에서 적용한 코드가 제대로 동작하는지 확인해 보기로 하자.

<NanoPi R2S Plus#1> <=== Internet ===> <NanoPi R2S Plus#2>

<Desktop PC - Ubuntu 22.04>
먼저 arm64용으로 pq-wireguard-go code를 cross-compile하도록 한다.

$ cd wireguard-go
$ export GOOS=linux; export GOARCH=arm64
$ make clean; make
$ file wireguard-go  
wireguard-go: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, Go Bu
ildID=-me12zm3sUDhLoT-rWGv/PkbaL6oovQY1MI6G00wL/eg2d4nXCfWV7pK1I5mmQ/-A_xOyn7qr5CGM5grG-x, with
debug_info, not stripped

다음으로 pq-wireguard-go 구동을 위해 간단한 shell script를 하나 만들자.
# cat myrun.sh
#!/bin/sh
export LOG_LEVEL=debug
./wireguard-go wg0

sleep 1
ip address add dev wg0  172.16.1.200/24
ip link set up dev wg0
wg set wg0 listen-port 51820 private-key ../keys/privatekey peer dZopqlLFIFCSSxIQbI1+f6sCUWlrjj4X19VC7iA34Bs= allowed-ips 172.16.1.0/24 endpoint x.x.x.x:51820
~

📌 주의: wg tool을 이용하여 사전에 curve25519 keypair를 만드는 과정은 생략하였다. 관련 내용이 필요한 분들은 아래 link(2절)를 참조하기 바란다.

앞서 build한 wireguard binary를 target board로 올려, 구동시키도록 하자.

$ scp ./wireguard-go root@192.168.2.1:~/wg
root@192.168.2.1's password:  
wireguard                                                 100% 4190KB  20.6MB/s   00:00

<NanoPi R2S Plus#1>
# ./myrun.sh

...
DEBUG: (wg0) 2024/05/09 10:38:45 Starting wireguard-go version 0.0.20230223-46-g12269c2-dirty DEBUG: (wg0) 2024/05/09 10:38:45 Starting wireguard-go version 0.0.20230223-46-g12269c2-dirty DEBUG: (wg0) 2024/05/09 10:38:45 Device started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 3 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 1 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 1 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 1 - started DEBUG: (wg0) 2024/05/09 10:38:45 UAPI listener started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 2 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 2 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 2 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 3 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 10 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 3 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 7 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 7 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 8 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 9 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 8 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 8 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 9 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 7 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 9 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 10 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 4 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 16 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 4 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 14 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 4 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 15 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 5 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 6 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 6 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 5 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 6 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 5 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 15 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: event worker - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 16 - started DEBUG: (wg0) 2024/05/09 10:38:45 Interface up requested DEBUG: (wg0) 2024/05/09 10:38:45 Routine: TUN reader - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 16 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 15 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 12 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 10 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 11 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 12 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 14 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 12 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 13 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 13 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 13 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: encryption worker 14 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: decryption worker 11 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: handshake worker 11 - started DEBUG: (wg0) 2024/05/09 10:38:45 UDP bind has been updated DEBUG: (wg0) 2024/05/09 10:38:45 Interface state was Down, requested Up, now Up DEBUG: (wg0) 2024/05/09 10:38:45 Routine: receive incoming v4 - started DEBUG: (wg0) 2024/05/09 10:38:45 Routine: receive incoming v6 - started DEBUG: (wg0) 2024/05/09 10:38:46 Interface up requested DEBUG: (wg0) 2024/05/09 10:38:46 UAPI: Updating private key DEBUG: (wg0) 2024/05/09 10:38:46 UAPI: Updating listen port DEBUG: (wg0) 2024/05/09 10:38:46 Routine: receive incoming v4 - stopped DEBUG: (wg0) 2024/05/09 10:38:46 Routine: receive incoming v6 - stopped DEBUG: (wg0) 2024/05/09 10:38:46 UDP bind has been updated DEBUG: (wg0) 2024/05/09 10:38:46 Routine: receive incoming v4 - started DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - UAPI: Created DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - UAPI: Updating endpoint DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - UAPI: Removing all allowedips DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - UAPI: Adding allowedip DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - Starting DEBUG: (wg0) 2024/05/09 10:38:46 Routine: receive incoming v6 - started DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - Routine: sequential receiver - started DEBUG: (wg0) 2024/05/09 10:38:46 peer(dZop…34Bs) - Routine: sequential sender - started


# wg show interface: wg0 public key: 90ayFIF+/9RuXvUroTOtEa2qwInVY7dNM3s/N6C02kg= private key: (hidden) listening port: 51820 peer: dZopqlLFIFCSSxIQbI1+f6sCUWlrjj4X19VC7iA34Bs= endpoint: x.x.x.x:51820 allowed ips: 172.16.1.0/24

# ifconfig wg0 wg0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1420 inet 172.16.1.200 netmask 255.255.255.0 destination 172.16.1.200 inet6 fe80::d7a6:fa61:ace:f167 prefixlen 64 scopeid 0x20<link> unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 27 bytes 3236 (3.2 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

# ping 172.16.1.100 PING 172.16.1.100 (172.16.1.100) 56(84) bytes of data. 64 bytes from 172.16.1.100: icmp_seq=1 ttl=64 time=12.0 ms 64 bytes from 172.16.1.100: icmp_seq=2 ttl=64 time=3.38 ms 64 bytes from 172.16.1.100: icmp_seq=3 ttl=64 time=3.39 ms ^C --- 172.16.1.100 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 3.378/6.266/12.034/4.078 ms
📌 주의: 당연히 아래 NanoPi R2S Plus#2의 설정이 준비된 상태에서 ping이 가능하다. 여기서는 내용 전개 상 ping 결과를 먼저 기술한 것 뿐이다.

<NanoPi R2S Plus#2>
앞서와 유사한 설정 과정을 거친다(자세한 사항은 생략)

# wg show interface: wg0 public key: dZopqlLFIFCSSxIQbI1+f6sCUWlrjj4X19VC7iA34Bs= private key: (hidden) listening port: 51820 peer: 90ayFIF+/9RuXvUroTOtEa2qwInVY7dNM3s/N6C02kg= endpoint: y.y.y.y:51820 allowed ips: 172.16.1.0/24

# ifconfig wg0 wg0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1420 inet 172.16.1.100 netmask 255.255.255.0 destination 172.16.1.100 inet6 fe80::af1:339a:185d:1751 prefixlen 64 scopeid 0x20<link> unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 6 bytes 288 (288.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

ping 172.16.1.200
PING 172.16.1.200 (172.16.1.200) 56(84) bytes of data.
64 bytes from 172.16.1.200: icmp_seq=1 ttl=64 time=3.26 ms
64 bytes from 172.16.1.200: icmp_seq=2 ttl=64 time=3.29 ms
64 bytes from 172.16.1.200: icmp_seq=3 ttl=64 time=3.21 ms
^C
--- 172.16.1.200 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 3.216/3.259/3.294/0.056 ms

좀 싱겁기는 하지만, pqc가 적용된 wireguard-go code가 정상적으로 동작함을 확인할 수 있다.

📌 주의: OpenWrt의 firewall 설정과 관련해서는 이전 blog post(5절)를 참조하기 바란다.


5. 그 밖의 흥미로운 주제

이번 장에서는 WireGuard 관련 몇가지 흥미로운 프로젝트가 있어서 간략히 소개해 보고자 한다. 자세한 내용 보다는 이런 것이 있다 정도만 언급하고 넘어갈 생각이니, 관심있는 독자 여러분들의 많은 삽질(?)을 바란다. 😁


a) AmneziaWG

앰니지어 WireGuard는 wireguard-go에서 fork된 project로, firewall에서 vpn packet을 감지 후 차단하는 상황이 빈번하므로, 이를 타개하기 위해 시작된 project이다.



앰니지어 WireGuard는 크기를 변경하기 위해 모든 인증 패킷에 랜덤 바이트를 추가한다. 따라서 Initiation, Response handshake packet은 시작 부분에 "junk"가 추가되며, 그 크기는 S1과 S2 값에 의해 결정된다. 익히 알고 있는 것 처럼, 기본적으로 Initiation handshake 패킷의 크기는 148 byte로 고정되어 있는데, 정크를 추가하면 크기는 148바이트 + S1이 된다.

앰니지어 WireGuard는 더 안정적인 마스킹을 위해 또 다른 트릭을 사용한다. 즉, 세션을 시작하기 전에 DPI(Deep Packet Inspection) 시스템을 완전히 혼란스럽게 하기 위해 특정한 수의 "정크" 패킷을 보낸다. 이러한 패킷의 수와 최소 바이트 및 최대 바이트 크기는 매개 변수 Jc, Jmin 및 Jmax를 사용하여 설정에서 조정할 수도 있다.
마지막으로, 앰니지어 WireGuard는 하위 호환성을 보장한다. 즉, 매개 변수 Jc, Jmin 및 Jmax를 기본값(0과 동일)으로 설정하면 표준 WireGuard 처럼 동작하게 된다.

b) wiretap 프로젝트
일종의 vpn을 이용한 proxy 프로젝트이다. wireguard를 이용해 vpn service를 하면서도 gateway 형태로 배치할 필요 없이, standalone server 형태의 구성이 가능하다. 일전에 잠시 운용해 보았는데, 나름 쓸만하다.


c) wireguard-ui 프로젝트
Golang을 이용하여 wireguard 전용 webui를 만드는 project이다. 화면이 생각보다 아주 수려하다.


이 project는 Golang echo framework을 기반으로 하고 있다.

sudo ./wireguard-ui  
[sudo] chyi 암호:  
Wireguard UI
App Version     : development
Git Commit      : N/A
Git Ref         : N/A
Build Time      : 05-22-2024 07:44:54
Git Repo        : https://github.com/ngoduykhanh/wireguard-ui
Authentication  : true
Bind address    : 0.0.0.0:5000
Email from      :  
Email from name : WireGuard UI
Custom wg.conf  :  
Base path       : /
Subnet ranges   :  
Valid subnet ranges:  
⇨ http server started on [::]:5000


[그림 5.1] wireguard-ui 화면


d) netbird 프로젝트
"NetBird는 WireGuard® 위에 구축된 오픈 소스 VPN 관리 플랫폼으로 조직이나 가정에 안전한 사설 네트워크를 쉽게 만들 수 있도록 해 준다. 기술적으로는 ICE 프로토콜(WebRTC)을 사용하여 오버레이 네트워크를 생성하여 P2P 연결을 만들고, wireguard를 사용하여 machine 간에 빠르고 암호화된 터널을 생성하여 P2P 연결이 불가능한 경우 다시 릴레이(TURN)로 돌아간다. 클라이언트 앱만 있으면 되고 나머지는 소프트웨어가 수행한다. OpenZiti와 같은 제로 트러스트 보안 모델을 도입하므로써, 기존 VPN을 넘어서는 것을 목표로 하고 있다(일종의 ZTNA 제품으로 보아도 되려나 😎)."



[그림 5.2] Netbird의 주요 특징(1) [출처 - 참고 문헌 10]


[그림 5.3] Netbird의 주요 특징(2) [출처 - 참고 문헌 11]


[그림 5.4] Netbird Architecture [출처 - 참고 문헌 12]


[그림 5.5] Netbird WebUI 화면


NetBird를 한마디로 쉽게 말하면 wireguard를 기반으로 하는 mesh VPN(or overlay network VPN)이라고 말할 수 있겠다. 여기에 요즘 대세인 Zero Trust 모델을 도입했다는 점과 PQC(양자내성 암호 - Rosenpass를 사용하고 있음)가 고려되었다는 점 등이 주목할만한 점이다.


To be continued...

6. References

[2] https://github.com/WireGuard/wireguard-go
[3] https://rosenpass.eu/docs/whitepaper/
[4] https://github.com/symbolicsoft/kyber-k2so
[5] https://docs.amnezia.org/documentation/amnezia-wg/
[6] https://github.com/ngoduykhanh/wireguard-ui
[7] https://tailscale.com/blog/throughput-improvements
[8] https://tailscale.com/blog/more-throughput
[9] https://github.com/sandialabs/wiretap
[10] https://github.com/netbirdio/netbird
[11] https://netbird.io/knowledge-hub/the-first-quantum-resistant-mesh-vpn
[12] https://docs.netbird.io/about-netbird/how-netbird-works#architecture
[13] https://github.com/cedrickchee/awesome-wireguard
[14] https://github.com/cunicu/go-rosenpass
[15] https://github.com/ChunghanYi/wireguard-go
[16] And Google~



Slowboot