Xiaomi Redmi Note 9Sのカメラで撮った写真は綺麗?汚い?

Xiaomi Redmi Note 9Sのカメラ写真は綺麗?汚い?

7月にXiaomiのミドルロー帯のハイコストパフォーマンス機のRedmi Note 9Sを購入した。本当はこの姉妹モデルでもう少し安いRedmi Note 9か、以前から出ると噂になっていたOPPO Reno4の5G非対応版が欲しいと思っていたのだが、Xiaomiの方は日本向けに出してきたのがRedmi Note 9Sで、OPPOの方は目当てのモデルがリリースされるのが思ったより遥かに遅かった(7月末になってしかも今のところはタイだけ)ということなので、まぁRedmi Note 9Sを選ぶことになったのは仕方がないかなと。それに、XiaomiがRedmi Note 9Sを日本でもぼったくらずに安く出してくれたのと、バンド対応しっかりしていたので、これが決め手。Xiaomiはコストパフォーマンスが良くても日本以外の地域向けモデルは日本で使うにはバンド対応が良くないのよね。正式な日本向けモデルなら技適警察さん達に怒られなくて済むし。

Redmi Note 9Sは正式に日本で販売されるモデルの中ではこれまでになくコストパフォーマンスが高いという評価は確かではあるけど、もちろん魔法じゃないから超凄い機種がウソみたいに安く販売されているというわけではない。欠点はスペックからは読み取れない部分に隠されてるので、無駄に期待し過ぎていた人は買ってから「アレ?」と思うか、その辺りを覚悟してた人なら「やっぱりか・・」と思う部分がそれぞれある筈。Redmi Note 9Sの場合は大きく3つ、MIUI含むOS周りの挙動、音質、カメラ、このあたりが目立つところかと。MIUIはもともと好きじゃなかった(カスタムロムでしか触れたことがないけど)ので半分諦めてたし。音質はまぁ仕方が無いところ。ただし、イヤホンをしても良くないというのは予想してなかったけど。カメラもまぁこの価格だとスペック上はそれなりでも画質は期待できないよね。もともとスマホのカメラは解像度高いとロクなことないし、Redmi Note 9SのカメラセンサーがSamsungとOmnivisionなので期待してなかった。それと中国人・中国メーカーはハデな色の写真が好きなのでカメラのセンサーが得た画像を塗りつぶして油絵とかゲームのCG背景みたいにしちゃう傾向があるしね。

今回はRedmi Note 9Sの写真について。標準カメラアプリによるJpeg出しとRaw出しで画質を見てみたい。

標準カメラアプリのJPEG出し 900万画素モード

Xiaomi Redmi Note 9Sのカメラ画像 標準カメラ+JPEG出し 赤い花
デジタルカメラが大の苦手とする赤い花。Redmi Note 9Sの標準カメラは無駄に明るく現像してJpegにした印象。それで赤が潰れるのを防ごうとしている?その代わり緑の葉っぱが単一色みたいなノッペリしたヘンなの。赤い花の色そのものは眼で見た感じに近くなるように雰囲気だけは頑張ってはいる。

Xiaomi Redmi Note 9Sのカメラ画像 標準カメラ+JPEG出し オレンジの花
オレンジの花。これがヘタクソで立体感のない「塗りました」みたいな色になっている。緑の葉っぱはやっぱり全部同じ色に見える。

Xiaomi Redmi Note 9Sのカメラ画像 標準カメラ+JPEG出し ピンクの花
ピンク色の花。これは白い花にピンクのスプレーを吹きかけたかという感じで全く花が台無しに。葉っぱはハデ過ぎるほど鮮やかに描かれているが葉の表面のテカりは眼で見た雰囲気を壊してはいない。

標準カメラアプリの標準設定(何も変更をしない状態)で、出力も標準のJpegのまま。おそらくメインカメラのSumsungのS5KGM2センサーが暗くて色乗りが悪くノイジーなのでこの本来の1/2 x 1/2の解像度で上下を捨てた16:9が標準モードになってる。(S5KGM2がテトラセルなので仕方ない・・・)
画像は出力された画像を加工はせずに、そのまま1920 x 1080pxに切り抜いただけ。つまり等倍。ただし、カメラが出力したJpegを切り抜いてもう一度Jpegにして、閲覧環境によってはさらにWebPに変換することになるのでブラウザで閲覧したときにはディテールが壊れているかも。

標準カメラアプリのRAW出しそのまま 1190万画素モード

Xiaomi Redmi Note 9Sのカメラ画像  標準カメラ+RAW出し 赤い花
赤い花は、真っ赤な服を漂白剤と蛍光増白剤を入れて洗濯して色落ちしたみたいななんともいえない変な色。でも完全に潰れてはいないのでソフトウエア次第ではある程度修正が効く。ただし彩度の高い赤い色はとても難しい。背景の緑の葉っぱはボケが効いているのもあるが、元々1色の緑しか無いみたいな状態で撮れていたというのが判った。これではソフトウエアで修正してもフクザツな色は出そうにない。

Xiaomi Redmi Note 9Sのカメラ画像  標準カメラ+RAW出し オレンジの花
左側のオレンジの花にピントを合わせた筈だが、実際にはこの画像の中央付近で花につながる茎にピントが合っているみたい。花はピンボケで脱色したみたいなオレンジ色だけどそれでもオレンジのグラデーションは残っている。背景の葉っぱは全体的に白っぽいけど潰れてはいないので悪くはないかも。現像しだいというところかな。

Xiaomi Redmi Note 9Sのカメラ画像  標準カメラ+RAW出し ピンクの花
このピンクの花は立体感が消えやすくて難しそうだけど意外と綺麗に写っている。全体的に脱色した感があるが、葉の表面のテカりや葉の表面の凸凹感は失われていない。これはコントラストを少し上げる程度で良さそう。

Redmi Note 9S の標準カメラアプリはモード設定をシャッターボタン近くのモード切り替えスライダーで「プロ」を選択すると右上のハンバーガーアイコンからRaw出力を選択できるようになる。Redmi Note 9SのRaw出力はDigital Negative(DNG)形式なので専用ドライバや専用アプリケーションは不要で有名処の画像加工アプリや現像アプリは対応している筈。スマートフォンのカメラアプリはセンサーが撮った画像をJpeg出力する際に派手めに加工するが、Raw出力なら(完全ではないにしても)加工なしのカメラセンサーが撮ったままの画像を得ることができる。Redmi Note 9SのリアカメラのメインセンサーはSumsungのS5KGM2という名目4800万画素でセンサーサイズが8mm、ドットあたり0.8umといういかにも暗そうなスペックで、実際昼間に屋外で撮影して上の3枚の写真のように暗くて色が無くなる酷い代物。まぁこの機種・S5KGM2だけじゃなくちっちゃいセンサー+ちっちゃいレンズのくせに妙に高解像度なスマホカメラ全般の傾向ではあるけど。これをアプリケーションで色を強調してド派手にしているんだけど。正しい色にならないのも微妙な色の変化が撮れないのも無理はない。

標準カメラアプリのRAW出し現像調整 1190万画素モード

Xiaomi Redmi Note 9Sのカメラ画像  GCam+JPEG出し 赤い花
先の3枚をDarktableで現像した。
花びらが赤で潰れないようにしながら背景の葉っぱの色が変にならないよう、のっぺりしないように気をつけたが、元のRaw画像で失われていた本来の鮮やかで明るい赤い花の色を再現することは無理だった。「赤」にはなったけどちょっと違う赤なのよね。服を洗濯して色落ちしたら元には戻せないのと同じ。

Xiaomi Redmi Note 9Sのカメラ画像  GCam+JPEG出し オレンジの花
背景の葉っぱは肉眼で見たときの色にできた。オレンジの花は意外と綺麗に色が乗らなかった。特に右側の花はオレンジというより黄色っぽく見えてしまっている。左側の花はオレンジの濃淡がキツめ。

Xiaomi Redmi Note 9Sのカメラ画像  GCam+JPEG出し ピンクの花
全体に白っぽい気がしないではないけど、この写真はちょこっと触っただけで比較的簡単にマシな結果となった。

コンデジのRawだとせいぜいノイズを取るとか少し暗めにするとかホワイトバランスを変える程度だけど、暗くて色が失われたスマホのRawを現像するのは凄い手間。不自然さの無い色味にするのが難しい。写真撮りまくるのは良いけど後で現像しなきゃって考えるとゲンナリしそう。

3つの花とも撮影時には風があって花びらがプルプルパタパタしてたのが殆どブレずに影響無しな画像になったのはちょっと褒めるべきかも。でも、それなら少しでも色が載るように「オート」の選択肢にシャッター少し速度遅めるというのがあっても良いんじゃないかと思う。今回は4800万画素のメインカメラセンサーを使いながらも4000x3000px以下程度の画素になるモード(初期値)で撮影した。選択すれば4800万画素モードも使えるようだけど、近くの物はボッケボケで色がなくて恐ろしくノイズだらけという酷い画像しか撮れないみたい。4800万画素を活かすには数メートル以上離れた屋外の明るい被写体だけなのかな?これは難しいかも。
Redmi Note 9sのメインカメラはSamsung S5KGM2というテトラセルのセンサー。このテトラセルはクセモノで、カタログスペック的には高解像度だけど実は4画素で1画素として使うモードでしかまともに撮れない。つまりテトラセルで4800万画素センサーということなら綺麗に撮るには1200万画素でということになる。はっきり書けば、カタログスペックは4800万画素だけど実は1200万画素、そういうこと。4800万画素モードで糞みたいなキッタナイ写真しか撮れないのはそういうことなのね。

なお、Redmi Note 9Sの標準カメラアプリでRaw出力は「プロ」モードだけで、そのモードは絞りやシャッター速度ISOなど撮影者が都度適切に合わせるのが本来の撮影方法だと思う。今回は「プロ」モードは使ったけど全てAUTOで撮影したので、それで暗いだの色が乗らないだの文句を言うのは筋違いかもしれない。関係者の方で気を悪くしたらスミマセン。

Redmi Note 9Sで簡単に綺麗に写真を撮るならおそらく今のところはGCam。もし次回があればGCamで撮った写真を見てみたい。

NanoPi NEO3をv6プラスのルーターにする systemd-networkd + nftables

NanoPi NEO3

これまでNanoPi NEO2をv6プラスのIPv4用(MAP-E)のルーターにしていたが、NanoPi NEO2はGbEのポートが1つとUSB2.0ポートが1つあるだけなので、USBネットワークアダプタを使って2ポートのルーターにしても片側は900Mbpsほど出る可能性があるもののUSB側は頑張っても350Mbps程度しか出ない。つまり、NanoPi NEO2をルーターにするとフレッツ回線の名目1Gbpsにはほど遠い。そこで2020年7月に販売開始されたUSB3.0ポート搭載のNanoPi NEO3を購入してみた。(ルーターなら中身が似たもの姉妹モデルのNanoPi R2Sの方が良いかも)
これで、名目上はネットワーク2ポート共に1Gbps対応になる。

以前に書いた「NanoPi NEO2をv6プラスのルーターにする 後編」ではスクリプト1つを挙げたけど、シンプルでわかり易いがやり方として古さが否めない。それと、NetworkManagerは簡単で良い部分はあるけど勝手な動作のせいで最近のArmbian(のDebian Buster)では挙動がおかしいことがあるのよね。SLAACで自動的にVNEとIPv6で繋がる筈が全く繋がらないとか・・

正直なところ、ずっとFreeBSDしか触ってなかったのでLinuxのCLIを触りまくるようになったのはNanoPi NEOで遊び始めてから。NanoPi NEO2をMAP-Eのルーターにしたのも時期的には大して変わらない。だからド素人なのでその頃だと「systmed、ナニそれ美味しいの?」状態。最近ようやくLinuxにも慣れてきてやりたいことがあったらどういう方向で設定すれば良いかちょっぴり解り始めてきた。だから、以前とは違うやり方でやりたいと思った。

そこでネットワークの接続には NetworkManager ではなく systemd-networkd を使い、フィルタリングには iptables の代わりに nftables を使う。これで用意するファイルはバラけるもののだいぶモダン??な感じに。

これまで使ってきたNanoPi NEO2のルーターを置き換えるものだが、ネットワークの構成は以前の記事とはちょっと違う。(というか、以前の記事のときには図を簡単にするためにNAT66のLANを書かなかった)
2ポートのルーターで、片側はLANに、他方がVNEに、要するにONUの直下(のL2SW)に。このVNE側のNIC上にIPv4 over IPv6のトンネルを作る。LAN側はIPv4だけで通信する。
この記事のルーター設定には関係ないが、ONU直下のL2SWには別のルーターがぶら下がっていてそいつはIPv6専用でNAT66のLANを作っている。そのIPv6のLANに今回作るルーターのIPv4でデュアルスタックする。1台のルーターでIPv6とIPv4の両方のルーターにするわけではないので念の為。

$ sudo apt update    #パッケージデータベースを更新
$ sudo install iptables

以前と同じmap.shでやるなら、Armbianのミニマルにはiptablesは入っていないのでインストールする。他は特にはなくても動作そのものには困らない。このときに上の例だと素のiptablesが入るかもなのでiptablesではなくnftablesを指定してインストールする方が良いかも。(記事の後半で)

LAN側・VNE側の接続設定

USBネットワークアダプタをNanoPi NEO3に接続する。

$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: enxa0cec81818aa <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether yy:yy:yy:yy:yy:yy brd ff:ff:ff:ff:ff:ff

NICの名称は eth0 (これはNanoPi NEO3標準のネットワークポートの方)、USBネットワークアダプタが enxa0cec81818aa 。これをメモる。

今回はNetworkManagerを使わずに systemd-networkd を使う。そこで NetworkManager のサービス2つを無効にして systemd-networkd と systemd-resolvedを有効にする。

$ sudo systemctl disable NetworkManager-dispatcher.service
$ sudo systemctl disable NetworkManager.service
$ sudo systemctl enable systemd-networkd.service
$ sudo systemctl enable systemd-resolved.service

ここでシステムを停止すると、次回システム起動時にネットワークに繋がらなくなる可能性が高いので、少なくとも次の /etc/systemd/network/10-eth0.network だけは設定を書いておく。

/etc/systemd/network/10-eth0.network (新規)
1
2
3
4
5
[Match]
Name=eth0

[Network]
Address=192.168.0.10/24

eth0がLAN側のNICとする。この例ではIPアドレスを 192.168.0.10 とした。今回はルーターにするホストなのでこのNICの設定にはデフォルトゲートウェイなどは要らない。

/etc/systemd/network/11-enxa0cec81818aa.network (新規)
1
2
[Match]
Name=enxa0cec81818aa

enxa0cec81818aaがWAN側というかVNE側。フレッツの俗にいうIPoEならSLAACで自動設定されるが、MatchセクションにNameでNICの名前(システムに認識されている名前)を書かないとリンクアップしてもネットワーク的には存在しない同然になるので必ず設定。逆に、SLAACなので上の例で書いた2行以外はこの時点では不要。(書きたければご自由に)

$ ip addr
中略
3: enxa0cec81818aa:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether a0:ce:c8:18:50:cc brd ff:ff:ff:ff:ff:ff
    inet6 24xx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591982sec preferred_lft 604782sec
    inet6 fe80::yyyy:yyyy:yyyy:yyyy/64 scope link 
       valid_lft forever preferred_lft forever

システムを起動してみて、WAN側(フレッツ側)のNICに自動的にIPv6アドレスが割り振られることを確認する。
ここで、inet6の行が表示されないならネットワークの物理接続が正しくないか設定を間違っていて、VNEとIPv6で繋がっていない状態。
確認するのは、上の例だと 24xx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/64 というアドレスがそれ。fe80の方は存在することだけ確認。これでVNEと繋がった(筈)。

/etc/resolv.conf (編集)
これはテキトーにネームサーバだけ書いておけば良い。(この記事では省略)

/etc/sysctl.conf (2行追加)
1
2
net.ipv4.ip_forward=1      #ルーターにするのでとりあえず追加(後で消す)
net.ipv6.conf.eth0.disable_ipv6=1    #これはLAN側でIPv6の通信したくない場合に追加。普通は不要。

ここで、以前と同じmap.shを起動すれば機能する。

ここまで、LAN側とVNE側をsystemd-networkd用の設定にしたので、ついでにトンネルもsystemd-networkd用の設定にしたい。

先に作ったVNE側のネットワーク設定ファイルを触る。
/etc/systemd/network/11-enxa0cec81818aa.network (空行+Networkセクションの3行追加)

1
2
3
4
5
6
[Match]
Name=enxa0cec81818aa

[Network]
Address=CEのIPv6アドレス/64
Tunnel=v6ptnl0

AddressはVNE側に設定したいIPv6アドレスで、MAP-EではIPv6のプリフィックス等から決められた計算方法で求めたもの。以前のスクリプトではip -6 addr add $CE dev $WANDEVに相当するもの。最後にスラッシュとプリフィックスのビット数も書く。
Tunnelのv6ptnl0はv6プラス用トンネルとして勝手に決めた名前。以前のスクリプトだとip6tnl1。これは好きな名前でどうぞ。

トンネル用の設定

/etc/systemd/network/12-v6ptnl.network (新規)ファイル名のv6ptnlの部分は任意
1
2
3
4
5
6
7
8
[Match]
Name=v6ptnl0

[Network]
IPForward=ipv4

[Route]
Destination=0.0.0.0/0

MatchセクションのNameは先のVNE側.networkで決めたTunnelと同じ名前を指定。
NetworkセクションのIPForward=ipv4は以前だと/etc/sysctl.confにnet.ipv4.ip_forward=1とか書いてたやつ。このトンネルデバイスでフォワーディングするのでここで書いてしまう。/etc/sysctl.confのnet.ipv4.ip_forward=1は要らなくなったので消す。
Routeセクションでデフォルトルートを指定。以前のスクリプトでは route add default dev $TUNDEV に相当。

/etc/systemd/network/v6ptnl.netdev (新規)ファイル名のv6ptnlの部分は任意
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[NetDev]
Name=v6ptnl0
Kind=ip6tnl
#MTUBytes=1460

[Tunnel]
Mode=ipip6
Local=CEのIPv6アドレス
Remote=BRのIPv6アドレス
DiscoverPathMTU=yes
EncapsulationLimit=none

NetDevセクションのNameは先のVNE側.networkで決めたTunnelと同じ名前を指定。
Kindは名前と役割が決まっている30種類以上から選択して指定。今回はIPv4 over IPv6のトンネルにするのでip6tnlを指定。 MTUBytesは1460を指定。これは以前のスクリプトのip link set dev $TUNDEV mtu 1460に相当。ただし、この行が無くても種類がip6tnlだと勝手に1460になるっぽい?上の例ではコメントにしている。

TunnelセクションのModeはipip6。これはiproute2あたりのマニュアルを見た。
LocalとRemoteはトンネルの入り口と出口。Localは以前のスクリプトだと$CEに入れたIPv6アドレスで、VNE側のNICに自動的に割り振られたIPv6アドレスではなく、MAP-E用に決められた計算方法で求めたもの。Remoteは以前のスクリプトだと$BRに入れたIPv6アドレスで、これは各VNEのNTTの東西エリアそれぞれ決まったBorder RelayホストのIPアドレス。BRのアドレス情報は「がとらぼ」では書かないが、ググれば見つかる筈。
DiscoverPathMTU=trueは、Path MTU Discoveryの自動検出を「あり」にしたつもり。これの動作は確認できていない。
最後のEncapsulationLimit=noneは以前のスクリプトでは ip -6 tunnel・・・ encaplimit none の最後の部分に相当。

これで、トンネルが掘れる。

$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether ??:??:??:??:??:?? brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.10/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::xxxx:xxxx:xxxx:xxxx/64 scope link 
       valid_lft forever preferred_lft forever
3: enxa0cec81818aa: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether ??:??:??:??:??:?? brd ff:ff:ff:ff:ff:ff
    inet6 24xx:自動で割り当てられたIPv6アドレス/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591609sec preferred_lft 604409sec
    inet6 CEのIPv6アドレス/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::yyyy:yyyy:yyyy:yyyy/64 scope link 
       valid_lft forever preferred_lft forever
4: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1000
    link/tunnel6 :: brd ::
5: v6ptnl0@enxa0cec81818aa: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1460 qdisc noqueue state UNKNOWN group default qlen 1000
    link/tunnel6 CEのIPv6デバイス peer BRのIPv6アドレス
    inet6 fe80::zzzz:zzzz:zzzz:zzzz/64 scope link 
       valid_lft forever preferred_lft forever

システムを再起動してから ip addr を実行。いつの間にか存在するip6tnl0は以前の記事でも書いたけどIPv4 over IPv6トンネルを作ると勝手に出来るもので、これは無視。設定ファイルで指定したトンネルデバイス(この記事の例ではv6ptnl0)が出来ていることを確認。ip addrでなくifconfig -aを実行した時ロケールが日本語だと「不明なネット」とか表示されるのは無視してよい。
v6ptnl0(名前は設定に書いたもの)が求めているもの、これがVNE側のNICに出来ていること(@enxa0cec81818aaの部分)。CEとBRのアドレスが入っていてトンネルとして正常に作成されていることを確認する。(実際にはこれを見ただけで正常か判らないかもだけど)

DS-Liteと異なりMAP-Eはトンネルが出来ただけだとルーターとして期待される通信はできないのでiptablesを動かす。
以前のmap.shの TUNDEV='ip6tnl1' を 今回の名称に変更する。TUNDEV='v6ptnl0'。
ip -6 hogeの2行、ip link hogeの2行、route hogeの2行、計6行を削除またはコメントにしてシステム起動後に実行。要するにiptables関連の部分だけ残したスクリプトにして実行。

map.shを実行するとルーターとして普通に動いている筈。

  1. NetworkManager(OS初期状態)を使ってLAN側と通信可能で、VNEのネットワーク側とIPv6でつながることを確認。
  2. ネットワークの情報をメモる。
  3. systemd-networkdでLAN側、VNE側の両方と接続できる設定を作る。
  4. NetworkManagerを無効化しsystemd-networkdを有効化する。
  5. LAN側と通信可能で、VNEのネットワーク側とIPv6でつながることを確認。
  6. メモったネットワーク情報からMAP-Eのためのアドレス等を作成する。
  7. systemd-networkdでトンネル用の設定を作る。
  8. トンネルが正常に作成されることを確認する。
  9. map.shのiptables部分だけを実行。(要iptables)
  10. IPv4のルーターとしての動作を確認。

段階を設定して各段階で動作確認することが大事。何もかも1度にしようとするとIPv4のルーターとして動作しないとなったときにどこが間違っているのか探しにくい。

NanoPi NEO3のプラスチックケース入りは放熱が下手くそなのでアイドル状態でも80℃以上(室温25℃)これはArmbianでSoCクロックが1.5GHzのとき。LinuxではガバナーがondemandならSoCがアイドルならクロックが下がるものだと思っていたけど、ステージがWIPのArmbianではアイドルでも基本1.5GHzでSoCが忙しいときに時々1.008GHzくらいまで落ちる。クロックの変化の仕方としては逆だと思うし温度が急上昇したわけでもないのになぜクロックが下がるのかは判らない。ただ、このクロックの動きのせいなのかは判らないが、ルーターとしての動作がスムースだったりガックガク(停まった?)になることが多々ある。1.008GHzでクロック固定したところ、温度は70℃(風を当てると60℃)に下がり、遅くなったり速くなったりもなくなったような・・・

ポートマッピングにパケットフィルタnftablesを使う

$ sudo iptables --version
iptables v1.8.2 (nf_tables)

iptablesのバージョンを確認して上の例のようにカッコの中にnf_tablesと表示されたらiptablesを使っているつもりで既にnftablesを使っている。カッコが無いなら本来のiptablesを使っている。

$ sudo apt install nftables

nftablesのパッケージをインストール。nftablesの設定ファイル(自動で読み込まれるルールファイル)は /etc/nftables.conf 。この /etc/nftables.conf が実行可のパーミッションになっていることを確認しておく。

/etc/nftables.conf
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
        }
}
パッケージインストール直後の未変更状態ならこんな感じ。
iptables部分だけに書き換えたmap.shの最後の行がnftablesでは互換性がない部分があるので修正。(問題なく動くけど)
iptables -t mangle -o $TUNDEV --insert FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

map.shを実行。
ルーターとして機能することを確認。

$ sudo nft list ruleset

map.shで適用したiptablesのルールをnftablesのルール書式で表示。

この内容を/etc/nftables.confの後に追記する。もしくは既存のtable inet filterは要らないのでflush rule set の次の行から最後までを消してから追加。
chain PREROUTINGの中にある counter packets xx bytes xxxx meta の部分を15行全てで削除。
chain POSTROUTINGの中にある counter packets xx bytes xxxx の部分を45行全てで削除。
chain OUTPUTの中にあるcounter packets xx bytes xxxx meta の部分を15行全てで削除。
つまり、
%s/counter.*meta//g
%s/counter.*snat/snat/g
エディタで置換2回で終わり。綺麗じゃないけど置換無しでも動くのは動く。
置換対象のテーブルのtable ip natのルールだけなら nft list table ip nat でも。こちらは置換は要らないが、当然table ip natのルールしか見えないので table ip mangle のルールも足す必要がある。テーブルリストを見るなら nft list tables

/etc/nftables.conf (変更後の例)
#!/usr/sbin/nft -f

flush ruleset

table ip nat {
        chain PREROUTING {
                type nat hook prerouting priority -100; policy accept;
                numgen inc mod 15 0  mark set 0x11 
                numgen inc mod 15 1  mark set 0x12 
                numgen inc mod 15 2  mark set 0x13 
                numgen inc mod 15 3  mark set 0x14 
                numgen inc mod 15 4  mark set 0x15 
                numgen inc mod 15 5  mark set 0x16 
                numgen inc mod 15 6  mark set 0x17 
                numgen inc mod 15 7  mark set 0x18 
                numgen inc mod 15 8  mark set 0x19 
                numgen inc mod 15 9  mark set 0x1a 
                numgen inc mod 15 10  mark set 0x1b 
                numgen inc mod 15 11  mark set 0x1c 
                numgen inc mod 15 12  mark set 0x1d 
                numgen inc mod 15 13  mark set 0x1e 
                numgen inc mod 15 14  mark set 0x1f 
        }

        chain INPUT {
                type nat hook input priority 100; policy accept;
        }

        chain POSTROUTING {
                type nat hook postrouting priority 100; policy accept;
                oifname "v6ptnl0" meta l4proto icmp mark 0x11 snat to IPv4アドレス:6xxx-6xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x11 snat to IPv4アドレス:6xxx-6xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x11 snat to IPv4アドレス:6xxx-6xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x12 snat to IPv4アドレス:10xxx-10xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x12 snat to IPv4アドレス:10xxx-10xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x12 snat to IPv4アドレス:10xxx-10xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x13 snat to IPv4アドレス:15xxx-15xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x13 snat to IPv4アドレス:15xxx-15xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x13 snat to IPv4アドレス:15xxx-15xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x14 snat to IPv4アドレス:19xxx-19xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x14 snat to IPv4アドレス:19xxx-19xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x14 snat to IPv4アドレス:19xxx-19xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x15 snat to IPv4アドレス:23xxx-23xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x15 snat to IPv4アドレス:23xxx-23xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x15 snat to IPv4アドレス:23xxx-23xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x16 snat to IPv4アドレス:27xxx-27xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x16 snat to IPv4アドレス:27xxx-27xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x16 snat to IPv4アドレス:27xxx-27xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x17 snat to IPv4アドレス:31xxx-31xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x17 snat to IPv4アドレス:31xxx-31xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x17 snat to IPv4アドレス:31xxx-31xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x18 snat to IPv4アドレス:35xxx-35xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x18 snat to IPv4アドレス:35xxx-35xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x18 snat to IPv4アドレス:35xxx-35xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x19 snat to IPv4アドレス:39xxx-39xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x19 snat to IPv4アドレス:39xxx-39xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x19 snat to IPv4アドレス:39xxx-39xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1a snat to IPv4アドレス:43xxx-43xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1a snat to IPv4アドレス:43xxx-43xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1a snat to IPv4アドレス:43xxx-43xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1b snat to IPv4アドレス:47xxx-47xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1b snat to IPv4アドレス:47xxx-47xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1b snat to IPv4アドレス:47xxx-47xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1c snat to IPv4アドレス:51xxx-51xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1c snat to IPv4アドレス:51xxx-51xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1c snat to IPv4アドレス:51xxx-51xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1d snat to IPv4アドレス:56xxx-56xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1d snat to IPv4アドレス:56xxx-56xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1d snat to IPv4アドレス:56xxx-56xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1e snat to IPv4アドレス:60xxx-60xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1e snat to IPv4アドレス:60xxx-60xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1e snat to IPv4アドレス:60xxx-60xxx
                oifname "v6ptnl0" meta l4proto icmp mark 0x1f snat to IPv4アドレス:64xxx-64xxx
                oifname "v6ptnl0" meta l4proto tcp mark 0x1f snat to IPv4アドレス:64xxx-64xxx
                oifname "v6ptnl0" meta l4proto udp mark 0x1f snat to IPv4アドレス:64xxx-64xxx
        }

        chain OUTPUT {
                type nat hook output priority -100; policy accept;
                numgen inc mod 15 0  mark set 0x11 
                numgen inc mod 15 1  mark set 0x12 
                numgen inc mod 15 2  mark set 0x13 
                numgen inc mod 15 3  mark set 0x14 
                numgen inc mod 15 4  mark set 0x15 
                numgen inc mod 15 5  mark set 0x16 
                numgen inc mod 15 6  mark set 0x17 
                numgen inc mod 15 7  mark set 0x18 
                numgen inc mod 15 8  mark set 0x19 
                numgen inc mod 15 9  mark set 0x1a 
                numgen inc mod 15 10  mark set 0x1b 
                numgen inc mod 15 11  mark set 0x1c 
                numgen inc mod 15 12  mark set 0x1d 
                numgen inc mod 15 13  mark set 0x1e 
                numgen inc mod 15 14  mark set 0x1f 
        }
}
table ip mangle {
        chain PREROUTING {
                type filter hook prerouting priority -150; policy accept;
        }

        chain INPUT {
                type filter hook input priority -150; policy accept;
        }

        chain FORWARD {
                type filter hook forward priority -150; policy accept;
                oifname "v6ptnl0" meta l4proto tcp tcp flags & (syn|rst) == syn  tcp option maxseg size set rt mtu
        }

        chain OUTPUT {
                type route hook output priority -150; policy accept;
        }

        chain POSTROUTING {
                type filter hook postrouting priority -150; policy accept;
        }
}
ポート番号など異なる部分はあるかもだけどこんな感じになる筈。
$ sudo systemctl enable nftables.service

nftablesパッケージをインストールした場合は有効化されていない可能性があるので実行しておく。既に有効化されていても影響無し。

次回からはシステム起動時にnftablesが自動的にこのルールを読み込むのでmap.shを自動実行する設定にしていたならそれを消す。
動作確認としてシステムを再起動してルーターとして機能することを確認。

なお、この記事では「ルーターとして機能することを確認」とか書いてるけど、LANの中の端末がIPv4でインターネット側と通信したいときにこいつがそのルーターであることを勝手に知ってはくれない。それぞれの端末にデフォルトゲートウェイの設定を固定で書くか、DHCPサーバのルーターオプションに指定するか。これは別途行ってください。

これで、気分は一新。ただし、設定の書き方が違うだけで実際は何も変わってないんじゃないかという気はしないでもない。もしかしたらnftablesの設定の書き方によってはフィルタリング処理が高速化したり違いを出せるのかもしれないけど、nftables本来の設定の書き方を見るのも触るのも今回が始めてなので良く解ってない。

関連記事:

Up