OrangePi R2S セットアップの記録

OrangePi R2S は $30ドル+送料 で入手できる 2.5GBE x 2, 1GBE x 2 が特徴のRISC-V シングルボードコンピューター。 今回はこれをファイアーウォールに仕立ててみる。

公式サイト: http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-R2S.html

スペック

"OrangePi_R2S_X1_User Manual_v1.0.pdf" p7

ボード外観

"OrangePi_R2S_X1_User Manual_v1.0.pdf" p9

購入から起動まで

USB Type-Cポートに5V 3Aを供給すると電源が入り、Power Indicatorが赤色に点灯する。 eMMCにはデフォルトでOpenWRTが書き込まれていて、これが自動的にブートする。 Ubuntuに変更したければガイドに従いイメージの書き込みを行う。

管理コンソールへの接続

管理コンソールへの接続を行うため、WANではなく、LAN側のいずれかのポートにPCを接続する。 IPv4/IPv6共にDHCPサーバーが有効であるため、PCにIPが自動的に割り当てられる。 ブラウザで http://192.168.2.1 (非TLS)に接続すれば管理コンソールが開く。

"OrangePi_R2S_X1_User Manual_v1.0.pdf" p89

デフォルトのユーザーアカウントは root で、パスワードが設定されていない。

rootのパスワード設定(System -> Administration -> Router Password)

Web管理コンソールのHTTPS化

luci-ssl のインストール

opkg update
opkg install luci-ssl
uci set uhttpd.main.redirect_https='1'
uci commit uhttpd
service uhttpd reload

注記: opkg updateで次のエラーが出るが、 luci-sslのようなコアではないものをインストールするのには問題がない。

Downloading https://downloads.openwrt.org/releases/24.10.0/targets/ky/riscv64/packages/Packages.gz

*** Failed to download the package list from https://downloads.openwrt.org/releases/24.10.0/targets/ky/riscv64/packages/Packages.gz

上記URL中の ky/riscv64 は この環境の DISTRIB_TARGET だが、公式OpenWrt の標準ターゲットではない。 つまり、RISC-V用の勝手ビルドなので存在しない適当なアドレスになっていて解決策はないはずだが、正しいURLなど、もしエラーを解消する方法があれば教えてください…

root@OpenWrt:~# cat /etc/openwrt_release
DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='24.10.0'
DISTRIB_REVISION='r28427-6df0e3d02a'
DISTRIB_TARGET='ky/riscv64'
DISTRIB_ARCH='riscv64_riscv64'
DISTRIB_DESCRIPTION='OpenWrt 24.10.0 r28427-6df0e3d02a'
DISTRIB_TAINTS='no-all'

~~ファイアーウォール機器にするための設定~~

ここからはファイアーウォール機器にするための設定になります。

現在の設定をバックアップ

sysupgrade -b /tmp/backup.tar.gz

WAN側から管理できるようにする

WAN側からのSSH接続の許可 (Network -> Firewall -> Traffic Rules)

WAN側からWeb管理コンソールへの接続許可 (Network -> Firewall -> Traffic Rules)

WAN側からSSH/Web管理コンソールへ接続する

上記設定後、WAN側から接続して作業を続行します。

ネットワークの再作成

lan, br-lanを破棄してbr-25gを作る

uci delete network.lan

sec="$(uci -q show network | sed -n "s/^\(network\.@device\[[0-9]\+\]\)\.name='br-lan'$/\1/p")"
echo "br-lan device section = $sec"
[ -n "$sec" ] && uci delete "$sec"

uci add network device
uci set network.@device[-1].name='br-25g'
uci set network.@device[-1].type='bridge'
uci add_list network.@device[-1].ports='eth2'
uci add_list network.@device[-1].ports='eth3'
uci set network.@device[-1].stp='0'

uci set network.br25g='interface'
uci set network.br25g.device='br-25g'
uci set network.br25g.proto='none'
uci set network.br25g.auto='1'

uci commit network
/etc/init.d/network reload

ファイアーウォール ルールを再作成

  • 基本的なWAN側からのパケットのフィルタ
  • WAN側からSSH, Web管理コンソールへアクセスできるようにする
  • br-25gの2つのポートについて、eth2が上流、eth3を下流とする
: > /etc/config/firewall
uci show firewall
uci -q batch <<'EOF'
set firewall.defaults=defaults
set firewall.defaults.input='DROP'
set firewall.defaults.output='ACCEPT'
set firewall.defaults.forward='DROP'
set firewall.defaults.synflood_protect='1'
set firewall.defaults.flow_offloading='0'
set firewall.defaults.flow_offloading_hw='0'

set firewall.wan=zone
set firewall.wan.name='wan'
add_list firewall.wan.network='wan'
set firewall.wan.input='DROP'
set firewall.wan.output='ACCEPT'
set firewall.wan.forward='DROP'
set firewall.wan.masq='0'
set firewall.wan.mtu_fix='0'

set firewall.wan_dhcp_renew=rule
set firewall.wan_dhcp_renew.name='Allow-DHCP-Renew'
set firewall.wan_dhcp_renew.src='wan'
set firewall.wan_dhcp_renew.proto='udp'
set firewall.wan_dhcp_renew.dest_port='68'
set firewall.wan_dhcp_renew.family='ipv4'
set firewall.wan_dhcp_renew.target='ACCEPT'

set firewall.wan_ping=rule
set firewall.wan_ping.name='Allow-Ping'
set firewall.wan_ping.src='wan'
set firewall.wan_ping.proto='icmp'
set firewall.wan_ping.icmp_type='echo-request'
set firewall.wan_ping.family='ipv4'
set firewall.wan_ping.target='ACCEPT'

set firewall.wan_dhcpv6=rule
set firewall.wan_dhcpv6.name='Allow-DHCPv6'
set firewall.wan_dhcpv6.src='wan'
set firewall.wan_dhcpv6.proto='udp'
set firewall.wan_dhcpv6.dest_port='546'
set firewall.wan_dhcpv6.family='ipv6'
set firewall.wan_dhcpv6.target='ACCEPT'

set firewall.wan_icmp6=rule
set firewall.wan_icmp6.name='Allow-ICMPv6'
set firewall.wan_icmp6.src='wan'
set firewall.wan_icmp6.proto='icmp'
set firewall.wan_icmp6.family='ipv6'
set firewall.wan_icmp6.target='ACCEPT'

set firewall.wan_ssh=rule
set firewall.wan_ssh.name='Allow-SSH-from-WAN'
set firewall.wan_ssh.src='wan'
set firewall.wan_ssh.proto='tcp'
set firewall.wan_ssh.dest_port='22'
set firewall.wan_ssh.target='ACCEPT'

set firewall.wan_https=rule
set firewall.wan_https.name='Allow-HTTPS-from-WAN'
set firewall.wan_https.src='wan'
set firewall.wan_https.proto='tcp'
set firewall.wan_https.dest_port='443'
set firewall.wan_https.target='ACCEPT'
EOF

uci commit firewall
fw4 check
/etc/init.d/firewall restart

ブリッジ用ルールは nftables の table bridge なのでfw4 のライフサイクル(restart/reload)に合わせて 自分で nft をロードする必要がある。

mkdir -p /etc/nft-bridge.d
cat > /etc/nft-bridge.d/br25g-bridge.nft <<'EOF'
table bridge br25g_filter {
  chain forward {
    type filter hook forward priority 0; policy accept;

    #
    # 0) 下流 -> 上流 の必須L2ブロードキャスト/マルチキャスト
    #

    # IPv4 DHCP 等: L2ブロードキャスト
    iifname "eth3" oifname "eth2" ether daddr ff:ff:ff:ff:ff:ff counter accept comment "ALLOW downstream broadcast (DHCPv4 etc)"

    # IPv6: all-nodes / all-routers / DHCPv6 multicast
    iifname "eth3" oifname "eth2" ether daddr 33:33:00:00:00:01 counter accept comment "ALLOW IPv6 multicast all-nodes"
    iifname "eth3" oifname "eth2" ether daddr 33:33:00:00:00:02 counter accept comment "ALLOW IPv6 multicast all-routers"
    iifname "eth3" oifname "eth2" ether daddr 33:33:00:01:00:02 counter accept comment "ALLOW DHCPv6 multicast (ff02::1:2)"

    # IPv6: solicited-node multicast (33:33:ff:xx:xx:xx) をマスクで許可
    iifname "eth3" oifname "eth2" ether daddr & ff:ff:ff:00:00:00 == 33:33:ff:00:00:00 counter accept comment "ALLOW IPv6 solicited-node multicast (ND)"

    #
    # 1) 同一L2維持に必須(従来どおり)
    #
    ether type arp counter accept comment "ALLOW ARP"
    ether type ip6 ip6 nexthdr icmpv6 icmpv6 type {133,134,135,136} counter accept comment "ALLOW ICMPv6 ND/RA (parsed)"

    # DHCPv4/DHCPv6(パースできる場合)
    iifname "eth3" oifname "eth2" ether type ip  ip protocol udp udp sport 68  udp dport 67  counter accept comment "ALLOW DHCPv4 c->s (parsed)"
    iifname "eth2" oifname "eth3" ether type ip  ip protocol udp udp sport 67  udp dport 68  counter accept comment "ALLOW DHCPv4 s->c (parsed)"
    iifname "eth3" oifname "eth2" ether type ip6 ip6 nexthdr udp udp sport 546 udp dport 547 counter accept comment "ALLOW DHCPv6 c->s (parsed)"
    iifname "eth2" oifname "eth3" ether type ip6 ip6 nexthdr udp udp sport 547 udp dport 546 counter accept comment "ALLOW DHCPv6 s->c (parsed)"

    #
    # 2) 上流 -> 下流 は許可
    #
    iifname "eth2" oifname "eth3" counter accept comment "ALLOW upstream->downstream"

    #
    # 3) 下流 -> 上流 は「戻り」+「ルータMAC宛のみ」許可
    #
    iifname "eth3" oifname "eth2" ct state established,related counter accept comment "ALLOW return traffic (ct est/rel)"
    iifname "eth3" oifname "eth2" ether daddr ac:8b:a9:10:db:8f counter accept comment "ALLOW to router MAC only"

    #
    # 4) それ以外の下流 -> 上流 は遮断
    #
    iifname "eth3" oifname "eth2" counter drop comment "DROP downstream->upstream (non-router MAC, new)"
  }
}
EOF

/etc/firewall.user を “ロード用スクリプト” として用意し、UCIfirewall include に登録して fw4 に管理させる。

cat > /etc/firewall.user <<'EOF'
#!/bin/sh
nft delete table bridge br25g_filter 2>/dev/null
nft -f /etc/nft-bridge.d/br25g-bridge.nft
EOF
chmod +x /etc/firewall.user

uci add firewall include
uci set firewall.@include[-1].path='/etc/firewall.user'
uci set firewall.@include[-1].type='script'
uci set firewall.@include[-1].enabled='1'
uci set firewall.@include[-1].fw4_compatible='1'

uci commit firewall
fw4 check
/etc/init.d/firewall restart

nft list table bridge br25g_filter

Linux のブリッジはパケットをL2で転送するが、br-netfilter が有効だと ブリッジを通過する IPv4/IPv6iptables でも検査する挙動になる。 今回の構成は「ブリッジ通過フレームを table bridge で制御する」方針なのでbr-netfilter を無効化して bridge 側に一本化する。

cat > /etc/sysctl.d/99-bridge-nf.conf <<'EOF'
net.bridge.bridge-nf-call-iptables=0
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-arptables=0
EOF

/etc/init.d/sysctl restart

sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.bridge.bridge-nf-call-arptables 2>/dev/null

不要なサービスを停止

今回の構成ではDHCP/RAサーバー機能は不要なので無効化する。

uci -q delete dhcp.lan
uci -q delete dhcp.wan
uci commit dhcp
/etc/init.d/dnsmasq restart

/etc/init.d/odhcpd disable

今回の構成ではSamba機能は不要なので無効化する。

/etc/init.d/samba4 disable

ファイアーウォールの動作確認

# カウンターをリセット
nft reset counters table bridge br25g_filter
# カウンターを確認
nft -a list chain bridge br25g_filter forward
  • DHCPv4 が動いているか

    • DHCP Discover/Request は L2 ブロードキャストで出るため、次が増えるのが自然: ALLOW downstream broadcast (DHCPv4 etc)(ff:ff:ff:ff:ff:ff) 一方で、L3/L4 でパースしている ALLOW DHCPv4 c->s (parsed) が 0 のままでも、L2 側で許可できていれば DHCP は成立する場合がある。
  • IPv6 が動いているか(ND/RA)

    • IPv6 は下記が増えるのが自然: ALLOW IPv6 multicast all-nodes(33:33:00:00:00:01) ALLOW IPv6 multicast all-routers(33:33:00:00:00:02) ALLOW IPv6 solicited-node multicast (ND)(33:33:ff:xx:xx:xx) ALLOW ICMPv6 ND/RA (parsed)(RS/RA/NS/NA)
  • 分離が効いているか(下流→上流の他端末遮断)

    • 下流端末は上流側へ色々投げがちなので、次が増えるのは想定内: DROP downstream->upstream (non-router MAC, new) ここが 0 で、代わりに他の許可が増えている場合は “想定より穴がある” 可能性がある。

実際の正常動作時のカウンターの例

root@OpenWrt:~# nft -a list chain bridge br25g_filter forward
table bridge br25g_filter {
    chain forward { # handle 1
        type filter hook forward priority 0; policy accept;
        iifname "eth3" oifname "eth2" ether daddr ff:ff:ff:ff:ff:ff counter packets 11 bytes 2335 accept comment "ALLOW downstream broadcast (DHCPv4 etc)" # handle 2
        iifname "eth3" oifname "eth2" ether daddr 33:33:00:00:00:01 counter packets 0 bytes 0 accept comment "ALLOW IPv6 multicast all-nodes" # handle 3
        iifname "eth3" oifname "eth2" ether daddr 33:33:00:00:00:02 counter packets 0 bytes 0 accept comment "ALLOW IPv6 multicast all-routers" # handle 4
        iifname "eth3" oifname "eth2" ether daddr 33:33:00:01:00:02 counter packets 0 bytes 0 accept comment "ALLOW DHCPv6 multicast (ff02::1:2)" # handle 5
        iifname "eth3" oifname "eth2" ether daddr & ff:ff:ff:00:00:00 == 33:33:ff:00:00:00 counter packets 174 bytes 12528 accept comment "ALLOW IPv6 solicited-node multicast (ND)" # handle 6
        ether type arp counter packets 7036 bytes 323656 accept comment "ALLOW ARP" # handle 7
        ip6 nexthdr ipv6-icmp icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } counter packets 4833 bytes 347768 accept comment "ALLOW ICMPv6 ND/RA (parsed)" # handle 9
        iifname "eth3" oifname "eth2" ip protocol udp udp sport 68 udp dport 67 counter packets 0 bytes 0 accept comment "ALLOW DHCPv4 c->s (parsed)" # handle 10
        iifname "eth2" oifname "eth3" ip protocol udp udp sport 67 udp dport 68 counter packets 2 bytes 681 accept comment "ALLOW DHCPv4 s->c (parsed)" # handle 11
        iifname "eth3" oifname "eth2" ip6 nexthdr udp udp sport 546 udp dport 547 counter packets 0 bytes 0 accept comment "ALLOW DHCPv6 c->s (parsed)" # handle 12
        iifname "eth2" oifname "eth3" ip6 nexthdr udp udp sport 547 udp dport 546 counter packets 0 bytes 0 accept comment "ALLOW DHCPv6 s->c (parsed)" # handle 13
        iifname "eth2" oifname "eth3" counter packets 37848 bytes 9109894 accept comment "ALLOW upstream->downstream" # handle 14
        iifname "eth3" oifname "eth2" ct state established,related counter packets 773 bytes 128154 accept comment "ALLOW return traffic (ct est/rel)" # handle 15
        iifname "eth3" oifname "eth2" ether daddr ac:8b:a9:10:db:8f counter packets 86 bytes 5102 accept comment "ALLOW to router MAC only" # handle 16
        iifname "eth3" oifname "eth2" counter packets 875 bytes 142058 drop comment "DROP downstream->upstream (non-router MAC, new)" # handle 17
    }
}

ベンチマーク

Thread  Time(s) Throughput(KB/s) Avg B / Compl
======  ======= ================ =============
     0    0.000            0.000     65536.000
     1    0.000            0.000     65536.000
     2    0.000            0.000     65536.000
     3    0.000            0.000     65536.000


#####  Totals:  #####


   Bytes(MEG)    realtime(s) Avg Frame Size Throughput(MB/s)
================ =========== ============== ================
    15101.437500     100.001       1454.055          151.013


Throughput(Buffers/s) Cycles/Byte       Buffers
===================== =========== =============
             2416.206     136.706    241623.000


DPCs(count/s) Pkts(num/DPC)   Intr(count/s) Pkts(num/intr)
============= ============= =============== ==============
    55574.601         0.934      129467.780          0.401


Packets Sent Packets Received Retransmits Errors Avg. CPU %
============ ================ =========== ====== ==========
    10890240          5191793         361     69     24.424

パフォーマンスが出ない…と思ったら、ドライバがRSSなどに対応してない。嘘でしょ…

[   18.279304] r8125 Ethernet controller driver 9.014.01-NAPI loaded

9.014.01-NAPI-RSS のようなRSS対応版が存在するようだ。 同じ事象で困っている人がいた。 https://www.reddit.com/r/OrangePI/comments/1okp321/orange_pi_r2s_subpar_switch_performance/