それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

wireguardのvpn をwg-quickを抜きで作ってみる。(ip6tablesなどの細かい設定を制御してv6NATしたい)

wireguard vpn を手作業で作る

openwrt がv6のアドレスに対して wireguard を作成すると、インタフェースの設定ではなぜかエラーになったので、手作業でコマンドを打ち込んでwireguard を起動する必要があってめんどくさかった。wg-quick もそのままインストールできないし。

マニュアルで接続する

wg-quick がないときに、手作業(マニュアル)でwg0のインターフェイスを定義して、そこをWireguardを使ってVPNを接続する。

最初に受け付ける側(サーバー動作)

wireguardはサーバとクライアントの区別が曖昧なので、明確にサーバーと呼べないと思われる。

最初に、接続受けつける側で受け付けられる用に設定を作っていく。

既存の削除

ip link set wg0 down
ip link delete dev wg0

インターフェースの作成

インターフェイスを作成して、IPアドレスを割り当てる。

ip link add dev wg0 type wireguard
ip addr add 172.16.3.2/32 dev wg0

ポートとプライベートキーの設定

どのポートでリッスンするかを決める。リッスンポートは決められるがリッスンするインターフェイスは決められない。

ポートは自由に決められる。iOSのソフトウェアやwireguardのマニュアルは 51821 を使っているのでそれに倣う。

wg set wg0 listen-port 51821 \
private-key /etc/config/custom/wireguard/wg0.server.key 

peer 接続

さらにサーバー側に、peer 間接続で使う設定を入れる。

wg set wg0 \
 peer UF52AMi4xLFYHN21XVcZUnDmuYY6xriM3TyAOrmNTHE=\
 preshared-key   /etc/config/custom/wireguard/wg_psk.psk\
 endpoint 2xxx:xxx:8383:a300:5054:ff:fxxx:xxxx:51821\
 allowed-ips 172.16.3.1/32

次のクライアントのを接続を受け付けますよ。という設定をする。

接続可能なクライアントの設定という体になる。 peer 同士が接続するのでサーバーとクライアントの明確な区別はないのだが。便宜上。

クライアント側も同様にしておく。

同様の設定を peerの両端に行う。

開始

ip link set wg0 up

必要に応じて routing を設定する。

172.16.3.1/32 側(今回のサーバー側で、クライアント側への経路)

ip route add 172.16.3.2/32 dev wg0

172.16.3.2/32 側(今回のクライアント側で。サーバー側への経路)

ip route add 172.16.3.1/32 dev wg0

覚えにくいのでスクリプトにまとめる。

シンプルとはいえ、手順は多いので、スクリプトにwg0 の作成と接続とルーティングをまとめて行うスクリプトをを作っておいた。

wireguard の 手順がわかりやすくなった。

出来上がった実行スクリプト

/etc/config/custom/wireguard/wg0-start.sh

#!/bin/bash

# ## LOCAL
# WG_IF=wg0
# WG_PORT=51821
# WG_LOCAL_LISTEN_PORT=$WG_PORT
# WG_LOCAL_IPv4=172.16.3.2/24
# WG_LOCAL_IPv6=fd00:baba:afac:4610::2/64
# WG_LOCAL_v4_NETADDR="${WG_LOCAL_IPv4%.*}.0/24"
# WG_LOCAL_v6_NETADDR="${WG_LOCAL_IPv6%:*}::/64"
#
# CONFIG_PATH=/etc/config/custom/wireguard/$WG_IF
#
# ## 秘密鍵
# WG_LOCAL_SECRETKEY_PATH=$CONFIG_PATH/secret.key
#
# ## PEER 設定
# WG_PEER_PUBKEY=$(cat $CONFIG_PATH/peer.pub)
# WG_PEER_PSK_PATH=$CONFIG_PATH/peer.psk
# WG_PEER_KEEPALIVE=60
# WG_PEER_ENDPOINT_IP=
# WG_PEER_ENDPOINT_PORT=$WG_PORT
# WG_PEER_ENDPOINT=$WG_PEER_ENDPOINT_IP:$WG_PEER_ENDPOINT_PORT
# ## 対向IPアドレス
# WG_PEER_IPv4="${WG_LOCAL_IPv4%.*}.1/24"
# WG_PEER_IPv6="${WG_LOCAL_IPv6%:*}:1/64"
# ## wg0ネットワークアドレス
# WG_PEER_IPv4_NET_ADDR=$WG_LOCAL_v4_NETADDR
# WG_PEER_IPv6_NET_ADDR=$WG_LOCAL_v6_NETADDR
# ## 転送をするリモートネットワーク
# REMOTE_NETWORK=( 192.168.2.0/24 192.168.100.0/24 )
# ENABLE_ROUTING=1
# ENABLE_DEFAULT_GW=1


WG_NATv6_ENABLED=1


function restart(){
  stop;start;
}
function stop(){
  ## 既存の削除
  if ip link show  $WG_IF 1>/dev/null  2>&1  ; then
    ip link set $WG_IF down
    ip link delete dev $WG_IF
  fi
}
function start_wg(){
  ## 既存の削除
  if ip link show  $WG_IF 1>/dev/null  2>&1  ; then
    echo already up.
    return 0;
  fi
  ## v6 経由の経路を追加しとく
  set_NTTflets_NGN_route;

  # インターフェースの作成
  ip link add dev $WG_IF type wireguard
  ip addr add $WG_LOCAL_IPv4 dev $WG_IF
  ip addr add $WG_LOCAL_IPv6 dev $WG_IF
  ip link set mtu 1420 up dev $WG_IF


  # ポートとプライベートキーの設定
  cmd="wg set $WG_IF listen-port $WG_LOCAL_LISTEN_PORT \
   private-key $WG_LOCAL_SECRETKEY_PATH"
  #echo $cmd
  $cmd;
  # 開始
  ip link set $WG_IF up

}
function add_peer(){
  ## peer 接続
  cmd="wg set wg0
   peer $WG_PEER_PUBKEY
   preshared-key   $WG_PEER_PSK_PATH
   persistent-keepalive $WG_PEER_KEEPALIVE
   allowed-ips $WG_PEER_IPv4_NET_ADDR,$WG_PEER_IPv6_NET_ADDR,$(IFS=,; printf '%s' "${REMOTE_NETWORK[*]}")
   "
  if [[ ! -z $WG_PEER_ENDPOINT ]]; then
    cmd="$cmd endpoint $WG_PEER_ENDPOINT ";
  fi
  # 実行
  # echo $cmd
  $cmd;

  ## 経路作成
  allow_forwardings_v4
  #allow_forwardings_v6
  add_route

}
function start(){
  ## wg接続を開始
  start_wg;

  ## peer を作成
  add_peer


}

function allow_forwardings_v6(){
  enable_v6_forwarding
  allow_v6_forwarding
}
function allow_v6_forwarding(){

  echo WG_NATv6_ENABLED=$WG_NATv6_ENABLED
  if [[ -z $WG_NATv6_ENABLED  ]] ; then
    ip6tables -I INPUT   -i $WG_IF -j ACCEPT
    ip6tables -I FORWARD -i $WG_IF -j ACCEPT
    ip6tables -I OUTPUT  -o $WG_IF -j ACCEPT
    ip6tables -I FORWARD -o $WG_IF -j ACCEPT
  else
    allow_v6_masquerade
  fi
}
function allow_v6_masquerade(){

  WG_IF=wg0
  NGN_IF=eth1
  LAN_IF=eth0

  DEFAULT_v6_GW=$( [[ $(ip neigh show dev $NGN_IF ) =~ (fe80[0-9a-f:]+) ]] ; echo ${BASH_REMATCH} )
  NGN_Adr=$([[ $(ip -6 addr show dev $NGN_IF ) =~ (2001|2400)[a-f0-9:/]+ ]]; echo $BASH_REMATCH)
  NGN_NAdr=${NGN_v6%:*:*:*:*}::/64

  WG_Adr=$([[ $(ip -6 addr show dev $WG_IF ) =~ fd([a-f0-9:]{1,4})+/[0-9]+ ]]; echo $BASH_REMATCH)
  WG_NAdr=$(owipcalc  $WG_Adr network)

  ## WG_IF → NGN へのNAT
  #echo ip6tables -A FORWARD -i $WG_IF  -o $NGN_IF -j ACCEPT
  #echo ip6tables -A FORWARD -i $NGN_IF -o $WG_IF  -j ACCEPT -m conntrack --ctstate RELATED,ESTABLISHED
  #echo ip6tables -t nat -A POSTROUTING -o $NGN_IF -d ::/0 -j MASQUERADE
  ip6tables -A FORWARD -i $WG_IF  -o $NGN_IF -j ACCEPT
  ip6tables -A FORWARD -i $NGN_IF -o $WG_IF  -j ACCEPT -m conntrack --ctstate RELATED,ESTABLISHED
  ip6tables -t nat -A POSTROUTING -o $NGN_IF -d ::/0 -j MASQUERADE
  ## LAN→ WG_IF へのNAT
  ip6tables -I FORWARD -i $WG_IF  -o $LAN_IF -j ACCEPT
  ip6tables -t nat -A POSTROUTING -o $WG_IF -d ::/0 -j MASQUERADE

}

function allow_forwardings_v4(){
  enable_v4_forwarding
  allow_v4_forwarding
}
function allow_v4_forwarding(){
  iptables -I INPUT   -i $WG_IF -j ACCEPT
  iptables -I FORWARD -i $WG_IF -j ACCEPT
  iptables -I OUTPUT  -o $WG_IF -j ACCEPT
  iptables -I FORWARD -o $WG_IF -j ACCEPT
}
function enable_v4_forwarding(){
  sysctl net.ipv4.conf.all.forwarding=1 > /dev/null
}

function enable_v6_forwarding(){
  sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
}
function add_route(){


  ip route add ${WG_PEER_IPv6%/*} from ${WG_LOCAL_IPv6%/*} dev $WG_IF metric 499 onlink

  if [[ $ENABLE_ROUTING != 1 ]]; then
    return;
  fi
  ## 例
  # 特定のIPだけ転送する場合。
  #   ip route add 192.168.100.5/32  dev $WG_IF via ${WG_PEER_IPv4%/*} onlink
  # ネットワーク転送する場合。
  #   ip route add 192.168.100.0/24  dev $WG_IF via ${WG_PEER_IPv4%/*} onlink
  for e in ${REMOTE_NETWORK[@]} ; do
    GATEWAY=''
    if [[ $e =~ : ]] ; then
      v6='-6'
      GATEWAY=${WG_PEER_IPv6%/*}
    else
      v6=''
      GATEWAY=${WG_PEER_IPv4%/*}
    fi
    if [[ $e == 0.0.0.0/0 && ! $ENABLE_DEFAULT_GW == 1 ]]; then
      return
    fi
    if [[ $e == ::/0 && ! $ENABLE_DEFAULT_GW == 1 ]]; then
      return
    fi

    echo ip route add $e  dev $WG_IF via $GATEWAY  metric 499 onlink
    ip route add $e  dev $WG_IF via $GATEWAY metric 499 onlink
  done
}
function set_NTTflets_NGN_route(){
  ## v6 の経路を明示的に指定したいとき
  ## わたしはNGNを壊れたまま運用。v6デフォルトGWを設定せずv6を壊してある。
  ## 特定のv6と通信するとき経路情報を都度入れている。
  /etc/config/custom/ipip6/v6_default_route.sh add $WG_PEER_ENDPOINT_IP
}





function main(){

  case $1 in
    start*)
      echo "start interface $WG_IF";
      start;
      ;;
    stop*)
      echo "stop interface $WG_IF";
      stop;
      ;;
     restart*)
      echo "restart interface $WG_IF";
      restart;
      ;;
    *)
      echo $0 'start|stop|restart';
      ;;

   esac

}

接続する側(クライアント)

#!/bin/bash


scripts=$(echo $(dirname $0)/../wg-start.sh)
source $scripts

## LOCAL
WG_IF=wg0
WG_PORT=51821
WG_LOCAL_LISTEN_PORT=$WG_PORT
WG_LOCAL_IPv4=172.16.3.1/24
WG_LOCAL_IPv6=fd00:baba:afac:4610::1/64
WG_LOCAL_v4_NETADDR="${WG_LOCAL_IPv4%.*}.0/24"
WG_LOCAL_v6_NETADDR="${WG_LOCAL_IPv6%:*}:/64"

CONFIG_PATH=/etc/config/custom/wireguard/$WG_IF

## 秘密鍵
WG_LOCAL_SECRETKEY_PATH=$CONFIG_PATH/secret.key

## PEER 設定
WG_PEER_PUBKEY=$(cat $CONFIG_PATH/peer.pub)
WG_PEER_PSK_PATH=$CONFIG_PATH/peer.psk
WG_PEER_KEEPALIVE=60
#### サーバー側ではendpoint は使わない
#### WG_PEER_ENDPOINT_IP=
#### WG_PEER_ENDPOINT_PORT=$WG_PORT
#### WG_PEER_ENDPOINT=$WG_PEER_ENDPOINT_IP:$WG_PEER_ENDPOINT_PORT
## 対向IPアドレス
WG_PEER_IPv4="${WG_LOCAL_IPv4%.*}.2/24"
WG_PEER_IPv6="${WG_LOCAL_IPv6%:*}:2/64"
## wg0ネットワークアドレス
WG_PEER_IPv4_NET_ADDR=$WG_LOCAL_v4_NETADDR
WG_PEER_IPv6_NET_ADDR=$WG_LOCAL_v6_NETADDR
## 転送をするリモートネットワーク
REMOTE_NETWORK=( 192.168.1.0/24 ::/0 )
##
ENABLE_ROUTING=1


##
main $@

接続を貰う側(サーバー)

#!/bin/bash


scripts=$(echo $(dirname $0)/../wg-start.sh)
source $scripts

## LOCAL
WG_IF=wg0
WG_PORT=51821
WG_LOCAL_LISTEN_PORT=$WG_PORT
WG_LOCAL_IPv4=172.16.3.2/24
WG_LOCAL_IPv6=fd00:baba:afac:4610::2/64
WG_LOCAL_v4_NETADDR="${WG_LOCAL_IPv4%.*}.0/24"
WG_LOCAL_v6_NETADDR="${WG_LOCAL_IPv6%:*}:/64"

CONFIG_PATH=/etc/config/custom/wireguard/$WG_IF

## 秘密鍵
WG_LOCAL_SECRETKEY_PATH=$CONFIG_PATH/secret.key

## PEER 設定(接続先)
WG_PEER_PUBKEY=$(cat $CONFIG_PATH/peer.pub)
WG_PEER_PSK_PATH=$CONFIG_PATH/peer.psk
WG_PEER_KEEPALIVE=60
WG_PEER_ENDPOINT_IP=2xxx:xxxx:a300:5054:ff:fea9:xxxxx
WG_PEER_ENDPOINT_PORT=$WG_PORT
WG_PEER_ENDPOINT=$WG_PEER_ENDPOINT_IP:$WG_PEER_ENDPOINT_PORT
## 対向IPアドレス
WG_PEER_IPv4="${WG_LOCAL_IPv4%.*}.1/24"
WG_PEER_IPv6="${WG_LOCAL_IPv6%:*}:1/64"
## wg0ネットワークアドレス
WG_PEER_IPv4_NET_ADDR=$WG_LOCAL_v4_NETADDR
WG_PEER_IPv6_NET_ADDR=$WG_LOCAL_v6_NETADDR
## 転送をするリモートネットワーク
REMOTE_NETWORK=( 192.168.2.0/24 192.168.100.0/24 ::/0 )
##
ENABLE_ROUTING=1


##
main $@

参考資料

https://qiita.com/fluo10/items/78e91884042645b08fb9

OpenWrt の Luci (web)で wireguard を作る

OpenWrt の Luci (web)で wireguard を作る

WireGuardは、サービスではなくインタフェースから作る。

インタフェースを追加したら、Peerを追加する。

秘密鍵と共有鍵の一覧は、ステータスにでてくる。

注意点

wireguard の設定を save & apply して適用しあとに、

ネットワークからインターフェース再起動が必要です。

2023-11-02 追記

OpenWrt 22.03.5 r20134で Wireguard を設定してもクライアントとして動作しない

Endpointを設定しても、通信が開始されない。

Persistent Keep Alive を設定する通信が開始された。Persistent Keep AliveがないときはEndpointへ接続へ行かないのはなぜだろう。

2023-11-02 追記

wireguardを入れたインターフェースをファイアウォール・ゾーン(グループ)に入れた場合、wireguardで割り当てるIPだけが対象かと思ってたが、wireguardの接続リクエストのパケットもゾーン対象に入ってしまう。そのためゾーン設定するには、wireguardの「接続確立」ができるようなAccept設定が必要だった。これは不具合だと思うので修正してほしい。。。

windows のIPアドレスの確認 ・変更( netshと ip の比較 )

windowsIPアドレスの確認 ( netsh )

Windowsには netsh コマンドがあって、IPアドレス関連の各種設定ができる。

netsh の使い方をまとめておく

コマンド linux windows
IPアドレス確認 ip addr show eth0 netsh interface ipv4 show addresses eth0
v6アドレス確認 ip -6 addr show eth0 netsh interface ipv6 show addresses eth0
IP追加 ip addr add 192.168.10.2/24 dev eth1 netsh interface ip add address eth01 192.168.10.2/24
ip 削除 ip addr del 192.168.10.2 dev eth1 netsh interface delete address 'eth0' 192.168.10.2
ルーティング表 ip  ip route netsh interface ipv4 show route
v6ルーティング表 ip -6 ip route netsh interface ipv6 show route
route 追加 ip route add 192.168.1.0/24 via 192.168.1.1 netsh interface add route 192.168.1.0/24 "eth0" 192.168.1.1
route 削除 ip route delete 192.168.1.0/24 via 192.168.1.1 netsh interface delete route 192.168.1.0/24 "eth0" 192.168.1.1

windows 固有

ネットワークデバイス(インタフェース)の名前を変える。

netsh interface set interface name = "ローカルエリア接続 2" newname = "eth2"

デフォルトの空白の入った長い名前は、コマンドから扱いづらいので、私は分かりやすいシンプルな名前にしている。

ネットワーク・デバイス(インタフェース)の一覧を取得

netsh interface ipv4 show interface 

ipv4 / ipv6 を使い分ける。

netsh interface show interface 
netsh interface ipv4 show interface 
netsh interface ipv6 show interface 

省略 / ipv4 / ipv6 でそれぞれに、動作がが違うので注意。

サンプル v6

IPv6アドレスを指定する。

netsh interface ipv6 add address fd03:ac7f:3852:67bb::3

v6デフォルトGWを設定する。

netsh interface ipv6 add route  ::/0  eth01  fd03:ac7f:3852:67bb::1

v6 でルーティングテーブルに追加する。

netsh interface ipv6 add route  2001:a253:8383:a300::/64 eth01  fd03:ac7f:3852:67bb::1 

省略形・省略しない場合。

ここまで書いた書式は省略形になっている。

次の2つは同じコマンドになる。引数の書き方の流儀が違うだけ。

これは

netsh interface ipv6 add address eth01 fd03:ac7f:3852:67bb::3/64

これと同じ。

netsh interface ipv6 add address interface=eth01 address=fd03:ac7f:3852:67bb::2/64

サンプルv4

v4 の場合は次のようになっている。

netsh interface ipv4 add address eth01 192.168.5.2/24

v6ルーティングテーブルに追加する。(デフォルトGWを設定する。)

netsh interface ipv4 add route 0.0.0.0/0  eth01 192.168.5.1
netsh interface ipv4 add route 10.100.100.0/24 vpn01 10.100.100.1

Joplin でプラグインのインストール

joplin のプラグインをインストールするには、

設定からプラグインのインストールができる。次のような画面からインストールできる。

f:id:takuya_1st:20210224031719p:plain

プラグインの一覧を出すには

検索ボックスに「スペース」を入れてエンターすると全部出てくる。これは気づかない。

f:id:takuya_1st:20210224031737p:plain

Joplin を Nextcloud と連携する

joplin とは?

Evernoteのように、メモを同期するやつ。Markdownで書けるアプリ。

似たようなものとして、Boostnote や Quiver が挙げられる。

特徴として、Self-hosted可能であり、オープンソース(MITライセンス)である。

同期先に、ローカルファイルや WebDAV や docker Joplin Server や S3 Bucket などが使えて、ストレージをSelf-hosted可能なところである。ローカルファイルの保存先をGoogleDriveへ同期フォルダに指定するのが一般的だろうか。

Joplin を Nextcloud と連携する

Joplin は NextCloud のWebDAVと連携して、NextCloudの指定フォルダにデータを書き込むことができる。

わたしは脱Google依存したいので、NextCloud を使うことにした。

joplin-nextcloud のアプリの有効化

Nextcloud の管理画面から joolin のアプリを有効にする。

f:id:takuya_1st:20210224030750p:plain 2021-01-16 現在の最新版では、NextCloudのAppStoreでエラーが出ているのでマニュアルでインストールが必要ですね。Issue には上がってるので、そのうち解決されると思います。

手動インストール

手動インストールは、NextCloudのアプリの手動インストールに従う。

  • nextcloud/apps フォルダに移動
  • app を wget で取得
  • app を unarchive する
  • app を有効にする
cd PATH/TO/NextCloud
cd apps
wget https://github.com/laurent22/joplin-nextcloud/releases/download/v0.0.25/joplin-0.0.25.tar.gz
unar joplin-0.0.25.tar.gz
rm joplin-0.0.25.tar.gz
cd ..
sudo -u www-data php occ app:enable joplin

joplin のアドレスを取得する。

NextCloudの「ファイル」から、Joplinを保存したいディレクトリを作る。

私は、 /Documents/joplin のフォルダを作成した。

f:id:takuya_1st:20210224030901p:plain

このフォルダを含めた アドレスを webdav のアドレスとして生成する

https://nextcloud.EXAMPLE.COM/remote.php/webdav/Documents/joplin

末尾に、NextCloud上でのルートからのディレクトリを入れるのがポイント

joplin クライアントに設定する。

joplin の 設定画面を開いて NextCloud を指定する。

f:id:takuya_1st:20210224030941p:plain 先程のWebDAVのURLと、ログインユーザー名・パスワードを入力して接続テストする。

f:id:takuya_1st:20210224031056p:plain

接続テストができればOK

iOSAndroid も同様に

スマートフォンのJoplinも同様にすることで同期ができるようになる。

快適マークダウンメモ生活。

スクショも含めた、マークダウンを手軽に書けるようになり、とても快適である。

惜しいところ

ゴミ箱機能がないところがとても惜しい。

Evernoteのような暗号化テキストを扱えないのがつらい。

markdown の表を扱いづらいのがちょっとめんどくさい。

Electron なアプリにありがちな日本語入力がおかしくなる現象は仕方ないとしても残念。

はてなブログにインポート機能がないので画像をうまくいインポートできない。

添付ファイルは、resource/ にまとめて保存されるので、ノート数が巨大になると多分動かない。enex はその点やっぱり良く出来てるフォーマットだったと思う。Evernoteはenexを捨てようとしてるっぽいけど。

課題は多いけど無料で、概ね良好に使える。

Windows版のEvernoteが酷くて、まともにメモを取れないので、本当に助かる。

WindowsでAlt-F4が指がつりそうになるのでWin-wに割り当ててmacOSっぽくしたら革命が起きた。

ALT-F4を Win-w に割り当てた。

PowerToysのキーリマッピングで、Win-WをAlt-F4に割り当てたら、すごく便利だった。

f:id:takuya_1st:20210223193728p:plain

macOSの command-W の便利さ

macOSのCMD-Wが便利すぎるので、Windowsでもやろうかとおもってやったら便利だった。

Alt-F4って押しにくいので、このキーリマッピングはすごく気に入った。

ほかにもmacから移植した

macOSのキー操作ってすごく便利で、なれたら必須と言えるレベルなので、Windowsにも移植しよう

Home/Endが押しにくい

Home/Endは押しにくいし、INSERTキーを間違えて押すトラブルがあってつらい。

かといて、Ctrl-A/EはWindowでは重要なショートカットなので、リマッピングはつらい。

そこで、よく考えた。一番良く使うのは行頭まで選択、行末まで選択じゃないか。そこでShift-Home/Shift-EndをShift-Ctrl-E/Aに移植した。便利。

DELETEが押しにくい

DELETEも押し間違える定番で苦しい。ノートPCのDELETEなど勇者スキルでも持ってないと正確に押せないじゃないか。

そこで Win-Backspaceにした。Deleteキーは退場してもらった。明示的にWinキーを押さないとDELETEできないの。macOSの誤発動を防止の良さを取り込むことができた。幸せだ。

いままで、Alt-F4が押しにくいので Win-spaceでメニューをだし、↑おしてEnterってした。

いままでは、Windowメニューを使ってた。 f:id:takuya_1st:20210223194120p:plain

LXC のストレージサイズ変更(拡張・縮小)する。

LXC のストレージサイズが足りない。

LXC のストレージのりサイズをなんとかしたいです。容量不足になって困らないようにしたい。調べました。

lxdのリサイズはストレージのフォーマットに従う。

バックエンドのストレージにbtrfs を使っていたら btrfs の手法。LVMならLVMを使う。

今回は、btrfs をリサイズして、縮小(shrink)と拡張(expand)を試す。

lxd のbtrfsを拡大する場合

全体の流れはこんな感じになります。

lxc storage list
lxc storage info bt01
truncate -s +10G /var/snap/lxd/common/lxd/disks/bt01.img
losetup -c /dev/loop6
mount /dev/loop6 /mnt
btrfs filesystem resize max /mnt
btrfs filesystem show
lxc storage list
lxc storage info bt01

lxd のストレージ一覧

ストレージ一覧から、拡大したいストレージを調べます。

takuya@lxd-host:~$ lxc storage list
+---------+-------------+--------+--------------------------------------------+---------+
|  NAME   | DESCRIPTION | DRIVER |                   SOURCE                   | USED BY |
+---------+-------------+--------+--------------------------------------------+---------+
| bt01    |             | btrfs  | /var/snap/lxd/common/lxd/disks/bt01.img    | 3       |
+---------+-------------+--------+--------------------------------------------+---------+
| bt02    |             | btrfs  | /var/snap/lxd/common/lxd/disks/bt02.img    | 0       |
+---------+-------------+--------+--------------------------------------------+---------+
| default |             | zfs    | /var/snap/lxd/common/lxd/disks/default.img | 13      |
+---------+-------------+--------+--------------------------------------------+---------+

このコマンドで、ストレージのファイル名とフォーマットが確認できる。

今回は、btrfs のbt01 が容量が足りないので、容量を割り当てる。

念の為に、増加前の容量を確認しておく。

lxc storage info bt01

bt01 / btrfs 増加させる。

bt01 は btrfs で作成しているので、btrfs のやり方に従う。

ファイルを増量させる

割当ファイルのサイズをtruncate で増加させる。

takuya@lxd-host:~$ truncate -s +70G /var/snap/lxd/common/lxd/disks/bt01.img

loop デバイスを確認

takuya@lxd-host:~$ losetup | grep bt
/dev/loop6          0      0         1  0 /var/snap/lxd/common/lxd/disks/bt01.img         0     512
/dev/loop7          0      0         1  0 /var/snap/lxd/common/lxd/disks/bt02.img         0     512

lxd のストレージはループデバイスにブロックデバイス接続されているので、ループデバイスのファイル名(番号)を確認。

loopデバイスにファイルの増量を検知させる。

losetup -c で loop デバイスを更新し、ファイルの容量を反映する。

takuya@lxd-host:~$ losetup -c /dev/loop6

(2022-05-14) 追記

追加容量反映はlxc stop でコンテナ停止が必要。ストレージを利用しているコンテナが起動していると容量反映がうまくいかないことがある。停止なし容量追加でうまくいくこともあるのだが・・・

マウント

ループデバイスを、ファイルシステムにマウント

takuya@lxd-host:~$ mount /dev/loop6 /mnt

btrfs の resize が、マウントポイントへ行われるらしいのでマウントする。

btrfs を容量変更(最大に)

btrfs filesystem コマンドを使ってデバイス空き容量の最大値までファイルシステムを拡張する。

takuya@lxd-host:~$ btrfs filesystem resize max /mnt
Resize 'mnt' of 'max'
もしマウントしてなかったら

マウントをせずに、直接ブロックデバイスへ操作しようとしてもエラーになった。ちょっと不思議。マウントが必要な理由はよくわからない。

losetup で デバイスファイルをloop デバイスから確認して、そのデバイスを mount して、いったん /mnt に持ってくる。 その上でないと、 btrfs の filesystem resize が動かない。

takuya@lxd-host:~$ btrfs filesystem resize max /dev/loop6
ERROR: resize works on mounted filesystems and accepts only
directories as argument. Passing file containing a btrfs image
would resize the underlying filesystem instead of the image.

リサイズ(拡張)結果を確認する。

btrfsのファイルシステムが拡張されたことを確認する。

takuya@lxd-host:~$ btrfs filesystem show
Label: 'bt01'  uuid: 6d9a8707-3766-4561-99c5
        Total devices 1 FS bytes used 59.97GiB
        devid    1 size 170.00GiB used 95.13GiB path /dev/loop6

Label: 'bt02'  uuid: 7c4aca0a-e942-4fa5-abef
        Total devices 1 FS bytes used 384.00KiB
        devid    1 size 50.00GiB used 2.02GiB path /dev/loop7

LXD/lxc で認識されたことを確認する。

lxc storgate info を使って、btrfsのファイルシステム拡張がlxd側で確認できる。

takuya@lxd-host:~$ lxc storage info bt01
info:
  description: ""
  driver: btrfs
  name: bt01
  space used: 65.24GB
  total space: 171.80GB
used by:
  images:
  - afcfbe9e40d27eb91cfbabf240823186d
  instances:
  - lxd-guest
  - vps

lxd の仮想マシン内部から確認する。

lxdのコンテナから、容量が増えていることを確認する。

root@lxd-guest:/var/www/virtualhosts# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop6      170G   66G  103G  40% /

縮小するときは

縮小はほぼ同じ。truncate で負数指定が変わるくらい。

truncate -10G /path/to/storgage

(2022-05-14) 拡張は、truncate -> btrfs resize の順、縮小はbtrfs resize -> truncate の順。

Snap版とapt版の違い

snap 版は /var/snap/lxd/common/lxd/disks

apt 版は /var/lib/lxd/disks/

とファイルの格納場所が異なる。

まとめ

lxd でのストレージはbtrfs では、手軽にサイズ変更できる。

ゲスト(コンテナ)の終了しなくても良かった。強い。

truncate コマンド便利。

参考資料

https://linuxcontainers.org/lxd/docs/master/storage#btrfs
https://discuss.linuxcontainers.org/t/snap-lxd-resize-default-btrfs-storage-pool/2587

ipv6NAT転送をして、NTTフレッツ倶楽部にログインできたらいいな。

ipv6のNAT転送で、NTTフレッツ倶楽部にログインできたらいいな。

LAN内部にグローバルのv6 アドレスを割り当てたくない。

IPv6グローバルを割り当てるとフォールバックでイラッとする。

WindowsにULAを割当て、ルーターからNATしてv6通信をすることにした。

フォールバックさせたくない、でもフレッツ網内と通信はしたい(フレッツClubログイン回線認証はしたい)というレアケースに対応した。

準備した環境

最初はこのような構成。

これにv6をユニークローカルアドレスを追加していく

windows --- ルーター

windowsルーターの間は fd03:ac7f:3852:67bb::/64 でネットワークしている。

windows は、 fd03:ac7f:3852:67bb::3 ルーターは、 fd03:ac7f:3852:67bb::1

IPv6をそれぞれに、割り当てて、接続することにする。

window にIPアドレスを割り当てる。

netsh interface ipv6 add address eth01 fd03:ac7f:3852:67bb::3/64

ちなみに、削除する場合は

netsh interface ipv6 del address eth01 fd03:ac7f:3852:67bb::3

追加時は、プレフィックスがついていても問題ないが、削除時はプレフィックスがあると動かない。

設定からやる場合。

確認方法

netsh interface ipv6 show addresses

Windows側から、ルーターをデフォルトGWにする。

netsh interface ipv6 add route  ::/0  eth01 fd03:ac7f:3852:67bb::1

ルーター内部動作

ルーターでは、WAN(eth1) / LAN ( eth0 )を定義している

ルータにIPを追加

ルーターに v6 アドレスを割り当てる

今回使ってるルータはLinuxなので、ipコマンドを使って追加する。

nic=eth0
ip addr add  fd03:ac7f:3852:67bb::1/64 dev $nic 

Linuxではnic は br-lan とか eth0 とかenp3s0とか、アレコレと名前が変わるはず。

疎通を確認する。

Windows→ルータ

PS C:\WINDOWS\system32> ping  fd03:ac7f:3852:67bb::1

WSL → ルータ

takuya@WSL:~$ ping6 fd03:ac7f:3852:67bb::1

ルータ → Windows

root@OpenWrt:~# ping fd03:ac7f:3852:67bb::3

疎通を確認ができたら、サブネットとv6アドレスは大丈夫。v6アドレスは長いので一瞥してわからないのでたまに間違えていたりする。v6では別の視点から疎通確認が大事。

ルーター側で、LAN→WANのv6 をNATする。

サンプルは、default allow のネットワークなので-A にしている。Default Deny のネットワークであれば、-I にしてルール先頭に加えること。

LAN_IF=eth0
WAN_IF=eth1

ip6tables -A FORWARD -i $LAN_IF -o $WAN_IF -j ACCEPT
ip6tables -A FORWARD -i $WAN_IF -o $LAN_IF  -j ACCEPT -m conntrack --ctstate RELATED,ESTABLISHED
ip6tables -t nat -A POSTROUTING -o $WAN_IF -d ::/0 -j MASQUERADE

IPv6のNATは最近の ip6tables でサポートされている。 「IPv6にはNATがない」なんて昔話なんですね。

ルーター側で、フレッツへの経路を追加する。

たとえば、フレッツ倶楽部の経路を作成し、Windowsからフレッツログインができるようにする。

ルーターの経路を作る。

ゲートウェイのアドレスは fe80 のやつでいい。

WAN_IF=eth1
GW=$([[ $(ip neigh show dev $WAN_IF ) =~ (fe80[0-9a-f:]+) ]] ; echo ${BASH_REMATCH})
ip route add default via $GW dev eth1 metric 1024 pref medium

v6ではゲートウェイをneigh で示されたfe08アドレス宛に投げつけておけばいいので便利だ。

WindowsがNATでv6グローバルと通信できる

NAT通信できるので、Windowsがグローバルv6 アドレスをもたなくても、NATでv6グローバルと通信ができる。

メリット

  • 冒頭にも書いたが、NATすることで、V6のGUAを割当しなくていい。
  • グローバルなv6アドレスがなくULAだけだと、フレッツ閉域網フォールバックが発生しなくなる。
  • v6の外部との通信を、ルーターの経路表でも制御できるようになる。
  • v6のあれこれ、特に近隣探索を覚えなくていい。

v6アドレスでNATができると、「フレッツ固有」のグローバルIPに縛られずに済む。 VPN経由で通信しても接続先の拠点のグローバルなv6アドレスで通信ができるようになる。 たとえば、フレッツ回線認証のようなv6アドレス認証は、接続先のv6アドレスで認証される。

そのため、IPアドレス範囲でのファイアウォールが記述しやすくなる。

NTT のフォールバック問題

https://www.geekpage.jp/blog/?id=2012/3/28/1

NGNのv6フレッツは、グローバルなアドレスをRA・DHCPv6してくるので、WindowsiOSはv6通信を試みる。 v6 接続が失敗してはじめて4v接続が開始される。つまり、初回接続が極めて遅い問題がある。

ちょっと面倒な問題なので、v6をストップして運用するかIPoEを申し込むのが手っ取り早い解決策である。

v6網内通信をしたい

v6をオフにして運用していたとしても、v6網内折返しは使いたい。

あれこれ考えてみて、NATゲートウェイにすることにした。

v6でNATなどと思うんだけど、壊れたv6へ出ていくことを考えればGUAを割り振って無ければいいんじゃないかと。

フレッツclub 西日本と通信するには

西日本の場合、つぎのアドレスと通信できれば回線認証が通る。

2001:a7ff:ff63:1::a
2001:a7ff:ff47:101::1

次の経路を、以下のようにNATルーターの経路表へ追記しておくと、どこからでもNTT西日本フレッツ・クラブへログインができる。

WAN_IF=eth1
GW=$([[ $(ip neigh show dev $WAN_IF ) =~ (fe80[0-9a-f:]+) ]] ; echo ${BASH_REMATCH})
##
ip -6 route add from ::/0 to 2001:a7ff:ff63:1::a via $GW dev eth1 
ip -6 route add from ::/0 to 2001:a7ff:ff47:101::1 via $GW dev eth1 

ULAでNATする例として、網内折返しのサンプルを使いづらいのでフレッツの回線認証を例とした。NATができるといういいサンプルですね。

DNSホストの解決を上書きする。などAAAAの解決を図る必要がある。

www.flets-west.jp 2001:a7ff:ff47:101::1 aaaa

Adguard でv6フォールバックを防いでるときなど、このあたりを注意しておく

2021-08-17

修正

2023-04-21 追加

openwrt が iptables から nft に変わったので、そのままでは動かなくなってる。

以下のように一旦 translate してから追加する。

ip6tables-translate -A FORWARD -i $LAN_IF -o $WAN_IF -j ACCEPT
ip6tables-translate -A FORWARD -i $WAN_IF -o $LAN_IF  -j ACCEPT -m conntrack --ctstate RELATED,ESTABLISHED
ip6tables-translate -t nat -A POSTROUTING -o $WAN_IF -d ::/0 -j MASQUERADE

または、ii6tables で追加してから再起動する

ip6tables -I FORWARD -i $LAN_IF -o $WAN_IF -j ACCEPT
ip6tables -I FORWARD -i $WAN_IF -o $LAN_IF  -j ACCEPT -m conntrack --ctstate RELATED,ESTABLISHED
ip6tables -t nat -A POSTROUTING -o $WAN_IF -d ::/0 -j MASQUERADE

/etc/init.d/firewall restart

なぜか、理由はまだ調べてないので分からないが、再起動すると初期化されず、iptablesで追加したものがnftablesに変換されるようだ。

2023-07-15 追記

NATして接続するようにした。

(fe80 が届かないので調べたら、 use default gatway が動いていて fe80 が何処か別のネットワークに流れていたぜ)

Windowsで日本語キーボードを英語キーボードとして認識させつつ、全角半角を使う。

英語キーボードがかっこいい

英語キーボードはかっこいいし、記号の入力が楽でプログラムをするのにとても向いている。

とくに、記号の入力が楽ちんで素晴らしい。[] = ;` などの記号をとクォートの入力が快適なのが嬉しい。

しかし、変換とIMEのオンオフがとても不便なのですが。そのあたりをなんとかできないかと考える。

そうすると、日本語キーボードをUS配列で使うという変態仕様に行き着くのである。

Windows で日本語キーボードを英語キーボードとして認識させる。

設定の言語オプションから、キーボードのレイアウトを 102 キーボードにむりやり変更することができる。

設定(setting) → 言語と時刻( time and language) → Language → Prefered Language → Japanese → opions → change layout

f:id:takuya_1st:20210218220325p:plain

102 キーにする。

f:id:takuya_1st:20210715013112p:plain

全角半角キーが死ぬ

キーボードのレイアウトを変えただけでは、ハードウェアの同位置のキーにマッピングされるので、単純に全角半角が死ぬ。

power toys でなんとかする

幸いにWindows 10にはPowerToysがあり、それでキーをマッピング変更ができるようになった。

以前はレジストリだとか色々めんどくさかったが、現在はこれでできるので快適だ。

f:id:takuya_1st:20210218220722p:plain

undefined は見えてないだけで、実際には「かな・カナ・ローマ字」が指定されている。

VK193 は102に存在しないキーで、実際には「ろ・\」が指定されている。

VK235 は102 に存在しないキーで、実際には「無変換」が指定されている。

「 ` ( バックォート) 」は、「全角半角(IME Kanji) 」に割り当て、VK193とスワップしている。

無変換とか、漢字の変換を適当に割り振って

GoogleIME側でも無変換とか適当に

f:id:takuya_1st:20210218221217p:plain

これで日本語キーボードだけど、102英字配列が出来上がる、変態キーボードを自作するより安上がりでいい。

追記

そもそも、全角半角いらなくね?

全角半角の位置はESCキーの位置と同等で押し間違えが頻発するので、いっそのこと消しちゃおう。

無変換をIMEKanjiに割り当てててれば。全角半角キーなど不要じゃないだろうか。

ついでに、右側の「かな・カナ」も不要じゃないだろうか。右ALTキーにしてしまえ

ついでにAppキーも最近は使わないので、Windowsキーにしてしまえ。

sudo / visudo で、一時的にvim を使い nano を使わない。

ubuntu の設定を変えずに vim を使いたい

sudo をしたときに、nanoが自動的に開いていしまう系のコマンドでvim を使いたい。

update-alternatives をすればいいだろうが、影響が大きいときにどうするか。

環境変数を使う。

環境変数EDITORを sudo 以降で実行する。

 sudo EDITOR=vim visudo

他の解決方法

sudo したときだけ vim を使いたい場合

sudoers の記述で環境変数を渡すということもできる。

Defaults editor=/usr/bin/vim 

参考資料

https://askubuntu.com/questions/539243/how-to-change-visudo-editor-from-nano-to-vim

wget が ipv6 でエラーになる→ wgetrc でv4に限定して解決

wget がエラーになる。

wget が名前解決エラーになる場合、ipv6 アドレスへ接続に行き、接続失敗することがある。

wgetrc を設定ファイルに書いてもユーザーごとになってしまうので、システムグローバルに適用する wgetrcファイル に設定を掛けば、自動実行のプロセスやフォークした先でも大丈夫。 openwrt で wget を使っているので opkg update が失敗する。v6アドレス怖い。

wgetrc で v4 へ固定する。

解決策は2つ

wget 引数

wget -4 https://example.com
## または
alias wget='wget -4 '

ただし、スクリプトから/bin/wgetが呼ばれる場合は対応できない。

/etc/wgetrc

inet4_only = on

これで解決する。ipv6 の名前解決をとめてもいいし、alias でもいいし、wgetrcでもいい。wgetrc が個人的には影響範囲が少なくてファイルを指定するのが楽な手法だと思う。

ほんと、フレッツのv6ってやつは面倒を引き起こす。

ただし wget パッケージ依存

wgetbusybox 提供のミニマム版だとwegetrc を読み込んでくれないので フルパッケージをインストールする必要があった。

wgetgnu wget に切り替える必要があった

opkg install wget 
echo inet4_only = on > /etc/wgetrc

それでも解決しない場合。

外部スクリプトから直接呼ばれる場合など、wget を移動させて強引に解決する。

/usr/bin/wget/usr/bin/wget.back -4 を呼ぶように改造

mv /usr/bin/wget /usr/bin/wget.bak
touch /usr/bin/wget
chmod +x /usr/bin/wget
cat <<EOF > /usr/bin/wget
#!/bin/sh
## 2021-12-02 ntt v6
/usr/bin/wget.bak -4  "\$@"
EOF

もとに戻す。

mv /usr/bin/wget.bak /usr/bin/wget

2021-12-02

強引に解決する方法を追記。

2023-10-16

追記

参考資料

https://www.gnu.org/software/wget/manual/html_node/Wgetrc-Location.html

https://www.gnu.org/software/wget/manual/wget.html

wget: Installing package breaks opkg due to default IPv6 preference · Issue #11814 · openwrt/packages · GitHub

dig がエラーを出した。

dig がエラーになってた

名前解決ができない。

dig: isc_nm_udpconnect: network unreachable

なんのエラーかと思って調べてたら、v6 で経路が見つからない。というv6ルーティングやv6アドレスのエラーだった。

無理やりv6を指定して、v4 ネームサーバーを指定すればいつでも再現できる。

root@Wrt:~# dig -6 t.co  @1.1.1.1
dig: isc_nm_udpconnect: network unreachable

つまり、壊れたフレッツv6網で出るわけですね。まいったね。

同じことを drill でやると・・・

$ drill -6 t.co @1.1.1.1
Error: error sending query: No (valid) nameservers defined in the resolver

drill のほうがメッセージが親切ですね。drill 使おうかな

wireguard で ip route default のデフォルトルートがおかしくなる。

wireguard で ip route がおかしくなる。

wiregaurd でwg0 を設定すると、 デフォルトルートが完全にwg0 に持っていかれる。

ip route でルーティングテーブルを確認し、 eth0 へデフォルト・ルートが向いているのにも関わらず、 ip route 1.1.1.1 してもwg0 へ向いてしまう。

とくに、クライアント側が AllowIPs で 0.0.0.0/0 を設定しているときに起きてしまう。

default を設定しているのに。

takuya@ubuntu01:~$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         Aw_Wrt.lan      0.0.0.0         UG    0      0        0 br0
172.16.3.0      0.0.0.0         255.255.255.0   U     0      0        0 wg0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 br0
192.168.2.223   0.0.0.0         255.255.255.255 UH    0      0        0 wg0
takuya@ubuntu01:~$ ip route
default via 192.168.1.1 dev br0
172.16.3.0/24 dev wg0 scope link
192.168.1.0/24 dev br0 proto kernel scope link src 192.168.1.240
192.168.2.223 dev wg0 scope link

経路は wg0 経由になっている。

takuya@ubuntu01:~$ ip route get 1.1.1.1
1.1.1.1 dev wg0 table 51820 src 172.16.3.2 uid 1000
    cache

tableを使っている。

wireguard の起動をよく見ていると route add 時に fwmarktableを使っている。

takuya@ubuntu01:~$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 172.16.3.2 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 192.168.2.223/32 dev wg0
[#] ip -4 route add 172.16.3.0/24 dev wg0
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
[#] iptables-restore -n

table とは?

LinuxのPBR( policy base routeing ) 関連ですね。私達が ip routeで見ているのは、 main テーブルです。

入口が複数で、疎通可能な出口が複数あって、戻りパケットや転送を、いcずれから出すんだよ。みたいなときに使う設定だったと思う。

Table 設定=Off?

wg-quick でデフォルトは、Table=autoです。 rule テーブルは自動的に作成され、AllowIpSに記載された経路がmainより、高い優先度で作成されます。

Table — Controls the routing table to which routes are added.
              There are two special values: `off' disables the creation
              of routes altogether, and `auto' (the default) adds 
              routes to the default table and enables special handling 
              of default routes.

Table=auto のとき、

takuya@ubuntu01:~$ sudo wg-quick down wg0
takuya@ubuntu01:~$ sudo ip rule list
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

起動後にrule table が追加さたのがわかる。

takuya@ubuntu01:~$ sudo wg-quick up wg0
takuya@ubuntu01:~$ sudo ip rule list
0:      from all lookup local
32764:  from all lookup main suppress_prefixlength 0
32765:  not from all fwmark 0xca6c lookup 51820
32766:  from all lookup main
32767:  from all lookup default

wg-quick により、netfilterによるタグ付け(Fwmark) が有効になっている。

ここで main より先に、fwmark 0xca6c lookup 51820 にマッチするので wg0 が作成した 51820テーブルへパケットが流される。

なので、ip route で表示されないがパケットは wg0 へ流れるわけです。

一般ユーザーが、wg-quick 0.0.0.0/0許可設定するとき、default ルートを触らずにしておくなら Table=off でも構わないと思われる。

また、経路設定時に余計なことが起きないように tableへのデフォルトルートをいったん削除するということもあり。

sudo ip route del table 51820 default

テーブルIDは、ENDPointのポート番号になっているらしい。

https://unix.stackexchange.com/questions/607313/wireguard-routing

choco でインストールしたものを最新版にアップグレード

アップグレードするには

インストール済みのアプリケーションをUPGRADEをしたいときに、まとめてすべてアップグレードしたいなとおもったら。

choco upgrade all

何も考えず、パッケージ名にAllを指定すればよかった。

https://docs.chocolatey.org/en-us/choco/commands/upgrade

細かい定義は次のようになっている。

choco upgrade <pkg|all> [<pkg2> <pkgN>] [<options/switches>]

all をパッケージ名と同等に扱うのはちょっと盲点でした。オプションじゃないんですね。。。。

all で分かった注意点。

完全に自動化として自動実行するのは、ちょっと厳しい時がある。インストーラーが起動しクリック待ちで停止するアプリケーションがまれにあるので、寝ている間に実行などは控えたほうが良さそうでした。

python の netaddr で ip6( ipv6) アドレスを扱う

前の、ipv6 版です、

ip アドレスを計算を簡便にするライブラリがあったので使ってみることにした。

ライブラリを読み込む

インストールする。

$ pip install netaddr

使う準備をする。

from netaddr import *

v4/ v6 ともに同じライブラリで扱うことが出来ます。

ip6アドレス(ネットマスク含む)を扱う。

サブネットマスク表記を含む場合は IPNetwork を使う。

>>> ip = IPNetwork('fdc2:be69:2aeb:bd6c::1/64')
>>> ip.ip
IPAddress('fdc2:be69:2aeb:bd6c::1')
>>> ip.network
IPAddress('fdc2:be69:2aeb:bd6c::')
>>> ip.netmask
IPAddress('ffff:ffff:ffff:ffff::')

IPアドレスを扱う。

IPアドレスだけを扱うときは、IPAddress を使う。

>>> ip = IPAddress('fdc2:be69:2aeb:bd6c::1')
>>> ip
IPAddress('fdc2:be69:2aeb:bd6c::1')

主に、IPNetworkの戻り値として得られる。

IPアドレスのネットワーク部を扱う。

>>> ip = IPNetwork('fd8f:4f9a:417f:b584::/64')
>>> ip.network
IPAddress('fd8f:4f9a:417f:b584::')

または、ちゃんとネットマスクを適用する。AND演算でネットマスクを掛けてやる。

>>> ip.ip & ip.network
IPAddress('fd8f:4f9a:417f:b584::')

ホスト部を取り出す。

>>> ip
IPNetwork('fd8f:4f9a:417f:b584::1/64')
>>> ip.hostmask
IPAddress('::ffff:ffff:ffff:ffff')
>>> ip.hostmask & ip.ip
IPAddress('::1')

ホスト部とネットワーク部をあわせてIPアドレスを作る。

>>> ip = IPNetwork('fd8f:4f9a:417f:b584::1/64')
>>> ip.hostmask & ip.ip
IPAddress('::1')
>>> ip.netmask & ip.ip
IPAddress('fd8f:4f9a:417f:b584::')
>>> ( ip.hostmask & ip.ip ) + ( ip.netmask & ip.ip )
IPAddress('fd8f:4f9a:417f:b584::1')
>>>

ホスト部を変更

ホスト部 と ネットワーク部を足し合わせると完成。

>>> ip = IPNetwork('fd8f:4f9a:417f:b584::1/64')
>>> ip.ip & ip.netmask
IPAddress('fd8f:4f9a:417f:b584::')
>>> ip.ip & ip.netmask | IPAddress('::2')
IPAddress('fd8f:4f9a:417f:b584::2')
>>>

ネットワーク部を変更する。

>>> ip = IPNetwork('fd8f:4f9a:417f:b584::1/64')
>>> ip2 = IPNetwork('fdab:ab1d:1ad8:75d8::/64')
>>> ip.network
IPAddress('fd8f:4f9a:417f:b584::')
>>>
>>> ip.hostmask & ip.ip
IPAddress('::1')
>>> ip.hostmask & ip.ip | ip2.network
IPAddress('fdab:ab1d:1ad8:75d8::1')
>>>

インターフェースIDからホスト部を生成

v6アドレスのホスト部は、インターフェースIDから生成される。 インターフェースIDは、MACアドレスから生成される。

これを覚えておけば、プレフィックスを変更するだけでIPv6アドレスが手軽に得られるようになるはずだ。

インターフェースID生成しホスト部と、ULAやリンクローカル、グローバルと組み合わせるだけでよいのだから。

EUI-64でホスト・アドレスを生成する。

MACアドレスからインターフェースIDを生成し、インターフェースIDとネットワーク部を併せULAを作る。

MACアドレスはEUI48であり、EUI-48→ EUI-64 に変換してインターフェースIDに使う。 インターフェースIDは、EUI-64へ変換したあと、上位7 ビット目をフラグを反転してあげる。

ビットの反転はXORを使う。XORは0を入れるとそのまま出力され、1を入れると反転する。 なので、該当ビットだけを反転させた値をint として使う。

EUI-48 ( MACアドレス)からEUI-64へ変換。

>>> mac = EUI('94:c6:91:3b:09:7c')
>>> mac.eui64()
EUI('94-C6-91-FF-FE-3B-09-7C')
>>> int(mac.eui64())
10720416491670014332
>>> int(mac.eui64()) ^ 144115188075855872
10864531679745870204
>>> EUI(int(mac.eui64()) ^ 144115188075855872)
EUI('96-C6-91-FF-FE-3B-09-7C')

EUI-64に変換が終われば、7ビット目を反転させる。

出来上がったEUIを v6 のネットワーク部と合わせれば 「v6アドレス」をインターフェースIDから生成が完成である。

>>> ip = IPNetwork('fe80::')
>>> ip.network | EUI('96-C6-91-FF-FE-3B-09-7C')
IPAddress('fe80::96c6:91ff:fe3b:97c')

ここで、ビット反転に使ったINTは次のとおりである。

>>> format(144115188075855872, '064b')
'0000001000000000000000000000000000000000000000000000000000000000'

上記の例でで生成したのはリンクローカルアドレスである。 Ubuntuが自動生成したリンクローカルアドレスと比較しよう。

3: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 94:c6:91:3b:09:7c brd ff:ff:ff:ff:ff:ff
    inet6 fe80::96c6:91ff:fe3b:97c/64 scope link

やったね。一致します。

Uubntu genrated                fe80::96c6:91ff:fe3b:97c/64
python  genrated IPAddress('fe80::96c6:91ff:fe3b:97c')

ULAを生成する

インターフェースIDが生成出来たので、ユニキャスト・リンクローカルアドレスを作りたい。

ランダムなプレフィックスを作る

$ openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::/64|'
fd8b:eb64:cbf6:b721::/64

このランダムなプレフィックスと、MACアドレスを使って、ネットワーク部と合体させる。

>>> ip  = IPNetwork('fd8b:eb64:cbf6:b721::/64')
>>> ip.network | EUI(int(EUI('94:c6:91:3b:09:7c').eui64()) ^ 144115188075855872)
IPAddress('fd8b:eb64:cbf6:b721:96c6:91ff:fe3b:97c')
>>>IPNetwork(str(ip.network | EUI(int(EUI('94:c6:91:3b:09:7c').eui64()) ^ 144115188075855872))+'/64')
IPNetwork('fd8b:eb64:cbf6:b721:96c6:91ff:fe3b:97c/64')

やった、これでインターフェースIDを用いたULAが生成できるぞ。

グローバルなプレフィックスが来ても大丈夫。自動生成に頼ってるのでいいんだけど、RA・DHCPv6ーPDとか疎通関連 で考えることが多く、設定しても疎通確認が自動生成に頼るのでテストがめんどくさい。

ULAをインターフェースIDでMACアドレスから作成する、コピペ用

コピペ用にまとめておく。

from netaddr import *
ip =  IPNetwork('fd34:5f2d:d3cb:8161::/64')
mac = EUI('00:16:3e:fc:57:d5')
a = IPNetwork(str(ip.network | EUI(int(mac.eui64()) ^ 144115188075855872))+'/64')
print(a)

IPアドレスを進める。

>>> ip.ip
IPAddress('fd8f:4f9a:417f:b584::1')
>>> ip.ip + 1
IPAddress('fd8f:4f9a:417f:b584::2')
>>>

/64 アドレスを/66へさらに分割する。

実際に使うことが出来ないが、計算だけはできる。

>>> for i in ip.subnet(66):
...   print(i)
...
fd8f:4f9a:417f:b584::/66
fd8f:4f9a:417f:b584:4000::/66
fd8f:4f9a:417f:b584:8000::/66
fd8f:4f9a:417f:b584:c000::/66

List comprehension でも表現できる。

演算子で計算がカッコいい

IPv6 でもv4 と同様に演算子(operand) を使ってIPアドレス計算するのがよくわかった。

おまけ

MACアドレス取得

ip link show eth0 | grep -Po '(?<=link/ether ).+'

v6 プレフィックス作成

openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::/64|'

MACアドレスからv6アドレスを生成

from netaddr import *
ip =  
mac = EUI('00:16:3e:fc:57:d5')
a = IPNetwork(str(IPNetwork('fd34:5f2d:d3cb:8161::/64').network | EUI(int(mac.eui64()) ^ 144115188075855872))+'/64' )
print(a)

これらを併せたワンライナー

python3 -c   "from netaddr import *;\
print(IPNetwork(str(IPNetwork( \
'$(openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::/64|')').network \
 | EUI(int(EUI('$(ip link show eth0 | grep -Po '(?<=link/ether ).+')').eui64()) \
 ^ 144115188075855872))+'/64' ))"

コマンドとして

ipcalc コマンドとして、自動計算する MACアドレスと、IPv6アドレスを与えて、EUIからユニークなv6アドレスを計算するコマンドをこれからサクッと作れるわけです。

#!/usr/bin/env python3


from netaddr import *
import sys

ip = None

if len(sys.argv)>2 :
  ip = IPNetwork(sys.argv[2])
else:
  ip = IPNetwork('fe80::/64')

if len(sys.argv)>1 :
  mac = EUI(sys.argv[1])
else:
  raise Exception(f'{sys.argv[0]} MAC_ADDR')



a = IPNetwork(str(ip.network | EUI(int(mac.eui64()) ^ 144115188075855872))+'/64' )
print(a)