Radxa Cubie A5Eゲートウェイをとして仕立てる

Linux
組み込み
SBC
Published

May 9, 2025

はじめに

Ethernetポートを2つ備えた低価格なSBCとして、Radxa Cubie A5E があります。 今回はこのSBCを用いて、以下のネットワークサービスを提供するシンプルなゲートウェイとして構築しました。

  • DHCP
  • TFTP
  • NFS

技術基準適合証明(技適)に関しては、対象の無線機能を無効化することにより問題ないと判断しました。 具体的には、ドライバを削除することで該当の無線機能を有効化しない構成としています。この方針は文献 [2] を参考にしています。

やったこと

  • 利用目的に合わせたカーネルの再構築を行いました
  • カスタムカーネルを反映したインストールメディアを作成しました
  • dnsmasq による DHCP・TFTPサーバー、および NFS サーバーのセットアップを行いました

ユースケースに合わせたカーネルの再構築

カーネルの再構築にあたっては、[1] を参考に、無線機能を無効化した上でシステムを構成しました。

まず、Radxaが提供しているSDK(t527_tina5.0_aiot_sdk.repo.tar.gz)を取得し、展開します。このSDKは repo によって構成されているため、展開後に各リポジトリの同期を行います。

$ tar -zxvf t527_tina5.0_aiot_sdk.repo.tar.gz
$ repo sync -l

続いて、置き換えが必要なリポジトリを指定のブランチでクローンします。

$ cd target
$ rm -rf a527
$ git clone --depth 1 --branch target-a527 git@github.com:radxa/allwinner-target.git a527
$ cd ../device/config/chips
$ rm -rf a527
$ git clone --depth 1 --branch device-a527 git@github.com:radxa/allwinner-device.git a527
$ cd ../../../
$ rm -rf bsp
$ git clone --depth 1 --branch product-t527-linux git@github.com:radxa/allwinner-bsp.git bsp
$ cd kernel
$ rm -rf linux-5.15
$ git clone --depth 1 --branch allwinner-aiot-linux-5.15 git@github.com:radxa/kernel.git linux-5.15
$ cd ../brandy/brandy-2.0
$ rm -rf u-boot-2018
$ git clone --depth 1 --branch allwinner-aiot-v2018.07 git@github.com:radxa/u-boot.git u-boot-2018

提供されている quick.sh スクリプトには条件式の誤りがあるため、以下のように修正を行います。

$ cat quick.sh.diff
735c735
< if [ ! -f $1 ] && [ $1 = "build_help" ] ; then
---
> if [ ! -f $1 ] && [ $1 == "build_help" ] ; then

これで、ビルドに必要なファイルが揃いました。次に、ビルド環境の初期化と必要なパッケージの導入を行います。Radxaのドキュメントに記載された source build/envsetup.sh に加えて、32bitアーキテクチャ向けのライブラリ群を導入します。

$ source build/envsetup.sh
NOTE: The SDK(/home/argon/workspace/radxa) was successfully loaded

...

$ sudo dpkg --add-architecture i386
$ sudo apt install libc6-i386 libz1:i386
$ sudo apt-get install libc6:i386 libstdc++6:i386 zlib1g:i386
$ export PATH=$PATH:$(pwd)/brandy/arisc/ar100s/tools/or32-elf-gcc/toolchain/bin

次に、システムの全体構成を設定します。 ./build.sh config を実行し、ルートファイルシステムとして linaro-bullseye-lite-arm64.tar.gz を選択します。

$ ./build.sh config
...

You read time left 8 seconds....
I have already read, understood and accepted the above terms? [Y/N]Y
You select Yes, Build continue....
========ACTION List: mk_config ;========
options :
All available platform:
   0. android
   1. linux
Choice [android]: 1
All available linux_dev:
   0. bsp
   1. dragonboard
   2. buildroot
   3. debian
Choice [bsp]: 3
All available kern_name:
   0. linux-5.10
   1. linux-5.15
Choice [linux-5.10]: 1
All available ic:
   0. a523
   1. a527
   2. t527
Choice [a523]: 1
All available board:
   0. cubie_a5e
   1. d10_linux_aiot
   2. demo_linux_aiot
   3. pro2_linux_aiot
Choice [cubie_a5e]: 0
All available flash:
   0. default
   1. nor
Choice [default]: 0
cat: /home/argon/workspace/radxa_blog/debian/.config: No such file or directory
All available rootfs files:
   0. linaro-bullseye-gnome-arm64.tar.gz
   1. linaro-bullseye-lite-arm64.tar.gz
   2. linaro-bullseye-lxde-arm64.tar.gz
   3. linaro-bullseye-xfce-arm64.tar.gz
Choice [linaro-bullseye-gnome-arm64.tar.gz]: 1

...

カーネル設定は ./build.sh menuconfig から行います。ローカルバージョンの指定、Wi-Fi/Bluetoothの無効化、NFSの有効化、loopデバイスの有効化などを行います。具体的な変更項目は以下の通りです。

  • ローカルバージョンの指定
General setup  --->
    (-cubie-A5E-giteki) Local version - append to kernel release
  • Wifiを無効化
Device Drivers  --->
  Network device support  --->
    [ ] Wireless LAN  --->
  • BlueToothを無効化
Networking support  --->
   < > Bluetooth subsystem support  ---
  • nfsを有効化
File systems  --->
  Network File Systems  --->
    <*> NFS client support
    <*>   NFS client support for NFS version 2
    <*>   NFS client support for NFS version 3
    <*> NFS server support
    -*-   NFS server support for NFS version 3
    [*]   NFS server support for NFS version 4
  • loopデバイスのマウントを有効化
Device Drivers  --->
  Block devices  --->
    <*> Loopback device support

システム初期化用のファイルは、target/a527/debian/cubie_a5e/overlay に配置し、ルートファイルシステム内の設定を上書きします。以下の例のように、APTソース、ホスト名、DNS、ユーザー環境、ネットワーク設定などを行います。

$ cat >> /etc/apt/sources.list
deb http://deb.debian.org/debian/ stable main contrib non-free
deb http://deb.debian.org/debian/ stable-updates main contrib non-free
deb http://security.debian.org/debian-security stable-security main contrib non-free

今回はホスト名をmochiという名前にします。

$ echo mochi > /etc/hostname
$ cat > /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4

これらの環境変数は後述するconfig.shで使用します

$ cat > environment
USERNAME=argon
GROUPNAME=nebarinet
USER_ID=501
GROUP_ID=6809
GITHUB_ID=ar90n
$ cat > network/interfaces.d/eth0.conf
auto eth0
iface eth0 inet static
    address 10.0.1.1
    netmask 255.255.255.0
$ cat > network/interfaces.d/eth1.conf
auto eth1
iface eth1 inet dhcp
$ cat > sysctl.conf
net.ipv4.ip_forward=1

初回起動時にのみ実行される初期化スクリプト config.sh も同様に配置します。このスクリプトは3段階で構成され、パーティショニング・ユーザー作成・Docker導入などを自動化します。各ステージの詳細を以下に述べます。

ステージ1: ディスクの初期化とマウント設定

NVMeディスク /dev/nvme0n1 を初期化し、以下の構成でパーティションを作成します。

  • p1: swap
  • p2: /home
  • p3: /usr
  • p4: /var

各パーティションには対応ディレクトリの内容を rsync でコピーし、fstab に登録します。また、無線関連の systemd サービス(wpa_supplicanthostapdbluetooth など)を無効化します。

処理完了後、stage2 を記録し再起動します。

ステージ2: パッケージ導入とユーザー作成

zshchronyavahi-daemon などをインストールし、環境変数で定義されたユーザー・グループを作成します。 GITHUB_ID が指定されていれば、SSH公開鍵を GitHub から取得して登録します。また、タイムゾーンを Asia/Tokyo に設定し、guest ユーザーを削除します。

完了後、stage3 に移行して再起動します。

ステージ3: Dockerのインストールと後処理

APTを通じて Docker CE をインストールし、対象ユーザーを docker グループに追加します。処理完了後、スクリプト自身と /etc/rc.local からの呼び出しを削除し、初期化を終了します。

以下に実際のconfig.shを示します。

#!/bin/bash
# /etc/config.sh - 3-stage init script for direct partitioning and user setup

export DEBIAN_FRONTEND=noninteractive

STAGE_FILE="/etc/config-stage"
ENV="/etc/environment"
[ -f "$ENV" ] && source "$ENV"

DISK="/dev/nvme0n1"
MOUNT_BASE="/mnt/ssd"

# === Stage 1: Partition, format, and copy from SD ===
if [ ! -f "$STAGE_FILE" ]; then
  echo "[Stage 1] Partitioning and copying rootfs..."

  # Disable services
  systemctl mask wpa_supplicant.service
  systemctl stop hostapd.service
  systemctl disable hostapd.service
  systemctl stop bluetooth.target
  systemctl disable bluetooth.target
  systemctl stop wpa_supplicant-nl80211@.service
  systemctl disable wpa_supplicant-nl80211@.service
  systemctl stop wpa_supplicant-wired@.service
  systemctl disable wpa_supplicant-wired@.service
  systemctl stop wpa_supplicant@.service
  systemctl disable wpa_supplicant@.service
  systemctl stop dbus-fi.w1.wpa_supplicant1.service
  systemctl disable dbus-fi.w1.wpa_supplicant1.service
  systemctl stop NetworkManager
  systemctl disable NetworkManager

  # Install packages
  ntpdate pool.ntp.org
  dpkg --configure -a || true
  apt --fix-broken install -y || true
  apt-get update
  apt-get install -y rsync

  # Uninstall packages
  apt-get purge -y x11-common hostapd wpasupplicant bluez pulseaudio

  # Partition the disk (swap, home, usr, var)
  wipefs -a $DISK
  parted -s $DISK mklabel gpt
  parted -s $DISK mkpart primary linux-swap 1MiB 4097MiB
  parted -s $DISK mkpart primary ext4 4097MiB 20481MiB     # home
  parted -s $DISK mkpart primary ext4 20481MiB 36865MiB    # usr
  parted -s $DISK mkpart primary ext4 36865MiB 100%

  # Wait for partitions to settle
  sleep 2
  partprobe
  sleep 1

  mkswap ${DISK}p1
  mkfs.ext4 -F ${DISK}p2  # home
  mkfs.ext4 -F ${DISK}p3  # usr
  mkfs.ext4 -F ${DISK}p4  # var

  mkdir -p $MOUNT_BASE/home $MOUNT_BASE/usr $MOUNT_BASE/var
  mount ${DISK}p2 $MOUNT_BASE/home
  rsync -aAX /home/ $MOUNT_BASE/home/ || exit 0
  umount $MOUNT_BASE/home

  mount ${DISK}p3 $MOUNT_BASE/usr
  rsync -aAX /usr/ $MOUNT_BASE/usr/ || exit 0
  umount $MOUNT_BASE/usr

  mount ${DISK}p4 $MOUNT_BASE/var
  rsync -aAX /var/ $MOUNT_BASE/var/ || exit 0
  umount $MOUNT_BASE/var

  echo "UUID=$(blkid -s UUID -o value ${DISK}p1) none swap sw 0 0" >> /etc/fstab
  echo "UUID=$(blkid -s UUID -o value ${DISK}p2) /home ext4 defaults 0 2" >> /etc/fstab
  echo "UUID=$(blkid -s UUID -o value ${DISK}p3) /usr  ext4 defaults 0 2" >> /etc/fstab
  echo "UUID=$(blkid -s UUID -o value ${DISK}p4) /var  ext4 defaults 0 2" >> /etc/fstab

  echo "stage2" > "$STAGE_FILE"
  sync
  reboot
  exit 0
fi

# === Stage 2: Install base packages and create user ===
if grep -q "stage2" "$STAGE_FILE"; then
  echo "[Stage 2] Installing base packages and creating user..."

  apt install -y avahi-daemon avahi-utils zsh chrony curl gnupg ca-certificates lsb-release
  systemctl enable avahi-daemon
  systemctl disable avahi-daemon.socket
  cp /lib/systemd/system/avahi-daemon.service /etc/systemd/system/avahi-daemon.service

  if [ -n "$USERNAME" ] && [ -n "$GROUPNAME" ] && [ -n "$USER_ID" ] && [ -n "$GROUP_ID" ]; then
    groupadd -g "$GROUP_ID" "$GROUPNAME"
    useradd -m -u "$USER_ID" -g "$GROUP_ID" -s /usr/bin/zsh "$USERNAME"
    echo "$USERNAME:$USERNAME" | chpasswd
    usermod -aG sudo "$USERNAME"

    mkdir -p "/home/$USERNAME/.ssh"
    if [ -n "$GITHUB_ID" ]; then
      curl -sL "https://github.com/$GITHUB_ID.keys" > "/home/$USERNAME/.ssh/authorized_keys"
      chown "$USERNAME:$GROUPNAME" "/home/$USERNAME/.ssh/authorized_keys"
      chmod 600 "/home/$USERNAME/.ssh/authorized_keys"
    fi
    chown -R "$USERNAME:$GROUPNAME" "/home/$USERNAME/.ssh"
  fi

  userdel -r guest

  timedatectl set-timezone Asia/Tokyo

  echo "stage3" > "$STAGE_FILE"
  reboot
  exit 0
fi

# === Stage 3: Install Docker CE and finalize user setup ===
if grep -q "stage3" "$STAGE_FILE"; then
  echo "[Stage 3] Installing Docker CE..."

  install -m 0755 -d /etc/apt/keyrings
  curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  chmod a+r /etc/apt/keyrings/docker.gpg

  echo \
    "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
    $(lsb_release -cs) stable" | \
    tee /etc/apt/sources.list.d/docker.list > /dev/null

  apt update
  apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  systemctl enable docker

  if [ -n "$USERNAME" ]; then
    usermod -aG docker "$USERNAME"
  fi

  echo "cleanup" > "$STAGE_FILE"
  reboot
  exit 0
fi

# === Cleanup state: remove self and rc.local entry ===
if grep -q "cleanup" "$STAGE_FILE"; then
  echo "[Stage Cleanup] Removing config.sh and rc.local entry"
  rm -f /etc/config.sh
  sed -i '/\/etc\/config.sh/d' /etc/rc.local
  rm -f "$STAGE_FILE"
  exit 0
fi

最後に、全体のビルドとパッケージングを行い、SDカードイメージを作成します。

$ ./buils.sh
$ ./buils.sh kernel
$ ./buils.sh pack

DHCP・TFTP・NFSサーバーの構築

このゲートウェイは、他のSBC(シングルボードコンピュータ)に対してネットブート環境を提供することも目的としています。 そのため、DHCP、TFTP、NFS といった基本的なネットワークブート用サービスを構成します。

今回構築したサービス群の設定は、GitHubリポジトリ にまとめてあります。 この構成では、以下の各サービスを docker-compose.yml によってコンテナとしてホスティングします。

dnsmasqによるDHCP・TFTPサーバーの構築

DHCPとTFTPは、dnsmasq を利用して構築します。 dnsmasq は、以下の Dockerfile に基づいた軽量コンテナで提供します。

FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    dnsmasq \
    && rm -rf /var/lib/apt/lists/*

CMD ["dnsmasq", "--no-daemon", "--conf-file=/etc/dnsmasq.conf"]

設定ファイル(dnsmasq.conf)の内容は以下の通りです。

# TFTP
enable-tftp
tftp-root=/var/tftpboot
tftp-no-blocksize
tftp-max=65535

# DHCP
interface=eth0
bind-interfaces
dhcp-range=10.0.1.100,10.0.1.200,12h

# DHCP option
dhcp-option=66,"10.0.1.1"   # TFTPサーバIP
dhcp-option=67,"kernel8.img" # ブートファイル名

NFSサーバーの構築

NFSサーバーも同様に、以下の Dockerfile によって構築します。 公開ディレクトリは /var/nfsroot とし、/etc/exports によってエクスポート設定を行います。

FROM debian:stable-slim

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    nfs-kernel-server && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

RUN mkdir -p /var/nfsroot
VOLUME ["/var/nfsroot"]

COPY exports /etc/exports

EXPOSE 2049 111/udp 111/tcp

CMD ["/bin/sh", "-c", "\
  mkdir -p /run/rpcbind && \
  rpcbind && \
  exportfs -rv && \
  /usr/sbin/rpc.nfsd && \
  /usr/sbin/rpc.mountd -F -N 2 && \
  tail -f /dev/null"]

エクスポート設定ファイルは以下の通りです。

/var/nfsroot *(rw,sync,no_subtree_check,no_root_squash,fsid=0)

このように、DHCP・TFTP・NFS の各サービスはコンテナ化されており、docker-compose を使って一括で管理できる構成としています。 設定の移植性・再現性を高める目的で、ホスト上ではなくコンテナベースで提供しています。

参考

[1]
Radxa cubie A5E low-level build guide: 2024. https://docs.radxa.com/en/cubie/a5e/low-level-dev/build.
[2]
ハードはそのまま技適を通ってないWi-fi、BLEボードを合法的に使う: 2022. https://qiita.com/maclineto/items/8939e703f844e95b820e.