それマグで!

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

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

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)

python の netaddr で ip( ipv4) アドレスを扱う

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

ライブラリを読み込む

インストールする。

$ pip install netaddr

使う準備をする。

from netaddr import *

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

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

>>> ip= IPNetwork('10.2.0.1/8')
>>> ip.network
IPAddress('10.0.0.0')
>>> ip.netmask
IPAddress('255.0.0.0')
>>> ip.ip
IPAddress('10.2.0.1')>>>

IPアドレスを扱う。

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

>>> ip = IPAddress('192.168.1.1')
>>> ip
IPAddress('192.168.1.1')

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

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

>>> ip = IPNetwork('192.168.1.1/24')
>>> ip.network
IPAddress('192.168.1.0')

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

>>> ip.ip & ip.netmask
IPAddress('192.168.1.0')

ホスト部を取り出す。

>>> ip.ip
IPAddress('192.168.1.1')
>>> ip.hostmask
IPAddress('0.0.0.255')
>>> ip.hostmask  & ip.ip
IPAddress('0.0.0.1')
>>>

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

>>> ip = IPNetwork('192.168.1.1/24')
>>> ip.ip
IPAddress('192.168.1.1')>>> ( ip.hostmask  & ip.ip )
IPAddress('0.0.0.1')
>>> ( ip.netmask & ip.ip )
IPAddress('192.168.1.0')
>>> ( ip.hostmask  & ip.ip ) + ( ip.netmask & ip.ip )
IPAddress('192.168.1.1')
>>>

ホスト部を変更

>>> ip = IPNetwork('192.168.100.1/24')
>>> ip
IPNetwork('192.168.100.1/24')
>>>
>>> ip.ip & ip.netmask
IPAddress('192.168.100.0')
>>> ip.ip & ip.netmask | IPAddress('0.0.0.24')
IPAddress('192.168.100.24')
>>>

IPアドレスを進める。

>>> ip.ip
IPAddress('192.168.1.1')
>>> ip.ip +  1
IPAddress('192.168.1.2')

/24 アドレスを/28へさらに分割する。

>>> for i in ip.subnet(28):
...     print(i)
...
192.168.1.0/28
192.168.1.16/28
192.168.1.32/28
192.168.1.224/28
192.168.1.240/28

List comprehension でも表現できる。

[ x for x in ip.subnet(30)]

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

演算子(operand) を使ってIPアドレス計算するのが、良かった。

2進数に落としてビットフラグ計算なんてやってられないけど、原理原則としてビットマスク計算がIPアドレスなので、計算はそのままなのがいい。

ipv6については次回。

ipv4 でnetaddr の使い方が把握できたので、それをv6に置き換えて、インターフェースIDを扱うことにする。

https://takuya-1st.hatenablog.jp/entry/2021/01/28/201047

参考資料

openssl と sed でipv6 のULAプレフィックスを作る(IPv6アドレスのランダム生成)

ULAのPrefixを生成するなら

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

ローカルアドレスプレフィックスの場合は、

prefix に16のネットワーク・アドレスを付ける。まとめてランダム生成すると。

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

実行例

root@router01:~# openssl rand -hex 5 | sed -r 's|^(.{2})(.{4})(.{4})|fd\1:\2:\3::/48|'
fd8b:9230:0268::/48
root@router01:~# openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::/64|'
fd71:c054:8514:c035::/64

あとは、これに ::1 とか書いておけば、実験用なサンプルのv6アドレスをランダムに生成することができる。

たとえば、::1と書いておく。

root@router01:~# openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::1/64|'
fd8a:c6e2:e280:275d::1/64

フルランダムで、128ビットのすべてを作ってもいいけど、実験用にv6アドレスを作る分には、MACアドレスから生成したりインタフェース名も冗長だし可視性も低下するので、ランダムで/64くらいで、適当な実験をするには十分なのです。

アドレスを追加する例

ユニークローカルユニキャストアドレスを明示する

ADDR=$(openssl rand -hex 7 | sed -r 's|^(.{2})(.{4})(.{4})(.{4})|fd\1:\2:\3:\4::1/64|')
ip addr add $ADDR dev eth1
ip -6 addr show eth1

ユニークローカルユニキャストアドレス

fd00 ってなに?ユニークローカルユニキャストアドレスってfc00 じゃないの?という疑問も出てくると思うが。

fc00::/7 は 先頭7ビットが1111,110である

F C 0 0
1111 1100 0000 0000

先頭7ビット(1111,110 ) が固定で残りの全ては0/1のすべてを含む

1111,1100,000,....0 から 1111,1101,1111,111...1である。

すなわち FC00:00... から FDFF までである。

2023-04-21

実行例を追記

FC00 と fd12 などのプレフィックスについて追記。

ipv6 のリンクローカルアドレスを使う

ipv6 で通信したい

ipv6 を有効にすると、いっぱいアドレスが割り振られて気持ち悪い。

たくさん割り振られるアドレスのなかでも、fe80 から始まるv6アドレスを リンクローカル・アドレスという。英語では link-local と表記されます。

v6リンクローカルアドレスとは。

v6のリンクローカルアドレスは、IPv4 169.254.*.* と同様のアドレスです。

ホスト間で通信を1対1で行う事ができます。 同一リンク内、つまりスイッチングハブを通して通信できる範囲(これをリンクという)で有効です。

ハブで接続されたホスト間で通信が可能になるのが、リンクローカルの役割です。

言い換えれば、リンクローカルアドレスは、経路を持ちえません。 経路表を使った伝送はできません。

v6 リンクローカルアドレス間で通信を行う。

ホストAとホストBのリンクローカルアドレスを確認します。

リンクローカルアドレスとは fe80::/ なアドレスです。

ホストAとホストBはスイッチング・ハブで接続されています。

ホストA

ip -6 addr show br0
4: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet6 fe80::f86c:d5ff:fe1e:e490/64 scope link
       valid_lft forever preferred_lft forever

ホストB

ip -6 addr show br-lan0
3: br-lan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    inet6 fe80::e080:bcff:fe7a:ddac/64 scope link
       valid_lft forever preferred_lft forever

ホストA/ホストBのv6アドレスの注目ポイント

ここでの注目ポイントは、NICのデバイス名と、リンクローカルのv6アドレスです。

ホストのNICバイス名とアドレスをまとめるたものが、次表になります。

インタフェース リンクローカル・アドレス
ホストA br0 fe80::f86c:d5ff:fe1e:e490
ホストB br-lan0 fe80::e080:bcff:fe7a:ddac

PINGする。ホストAからホストBへ

リンクローカルは経路がないため、どのインタフェースから送信するか明示する必要がある。

ping - I br0 fe80::e080:bcff:fe7a:ddac 

インタフェースをV6アドレスに含める。

ping コマンドはインタフェース指定に対応しているから良いが、非対応のコマンドだと困ることになる。 そのため、ipv6 のリンクローカルでは、デバイスを明示する仕様が盛り込まれている。これを利用すると、ipv6 のアドレスに送信元インタフェースを含めることができる。

リンクローカルのアドレスに、 fe80::1%eth0 のように、デバイス名をつけて送信元となるデバイス名を明示する。指定したデバイス名が出口となりパケットが送出される。

ping  fe80::e080:bcff:fe7a:ddac%br0

これでリンクローカルを使った通信ができる。

まとめ

  • IPv6にはリンクローカルがある。
  • リンクローカルは fe80 から始まる。
  • リンクローカルは経路を持たない
  • 通信するインタフェースを fe80::1%eth0 のように指定する。

php, mysqlで wordpress のテーブルへ pdo したときに文字化けする。

機種依存文字が化けます。

めっちゃ化けます。とくに、外字的な、ハシゴダカのような文字列を扱えません。PDOまじアレ。

PDOのマニュアルとか検索結果を見ても、ほとんど情報がないんですよね。困りました。

Windowsだと外字に登録するような文字列であっても、UTF-8では扱えるのですが、一部の文字では正しく変換されずにクエスチョンマークになって情報が落ちてしまう。

文字化けといえば、文字化けなのですが、豆腐に近い状態の文字化けなのです。

文字コードを次のようにします。

<?php
$pdo = new \PDO( "mysql:host=${host};dbname=${name};charset=utf8mb4", $user, $pass);

または

<?php

$pdo = new \PDO( "mysql:host=${host};dbname=${name}", $user, $pass, [\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"] );

wordpressMySQLの場合、文字コードがutf8mb4になっています。

そのため文字コードutf8mb4 で指定しなくては、ある種の文字が化けます。

wordpress で 直接SQLを発行する場合

<?php

function getPDO (): \PDO {
  [$host, $name, $user, $pass] = [DB_HOST, DB_NAME, DB_USER, DB_PASSWORD];
  $pdo = new \PDO( "mysql:host=${host};dbname=${name};charset=utf8mb4", $user, $pass,
     [\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"] );
  //
  $pdo->setAttribute( \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION );
  //
  return $pdo;
}
function getMySQLi(){
  $mcli = new \mysqli(DB_HOST,DB_USER, DB_PASSWORD, DB_NAME);
  $mcli->set_charset('utf8mb4');
  return $mcli;
}

wsl環境のターミナルで pbcopy/pbpaste でクリップボードにコマンドの結果を入れる。

pbcopy/pbpaste を使いたい

macOSならターミナルとCocoaクリップボードをpbcopy でやり取りできるのですが、WindowsのWSLDebian/Ubuntuでも同じようにコピー・ペーストのクリップボードをターミナルから使いたかった。

調べたら見つかったのでメモ

使い方

openssl -rand -base64 123 | pbcopy
pbpast | cat - 

コマンド pbcopy を作ればいい。

Linuxのときもそうだったけど、ないならなんとかAliasやシェルを組み合わせて実現したらいい。

調べたら、もうすでにあった。

# ~/.bin/pbcopy

#!/bin/bash
# pbcopy for wsl


wsl=/mnt/c/Windows/System32/wsl.exe
clip=$( $wsl wslpath C:/Windows/System32/clip.exe)
ps=$( $wsl wslpath C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe )

##
tee <&0 | $clip
exit 0

# ~/.bin/pbpaste

#!/bin/bash
# pbcopy for wsl


wsl=/mnt/c/Windows/System32/wsl.exe
clip=$( $wsl wslpath C:/Windows/System32/clip.exe)
ps=$( $wsl wslpath C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe )

##
$ps Get-Clipboard | sed 's/\r$//' | sed -z '$ s/\n$//'
exit 0

# PATH に追加。

つくった、コマンドをいつでも使えるようにPATHに追加する。

export PATH=$HOME/.bin:$PATH >> ~/.bashrc
source ~/.bashrc

使っている wsl コマンドについて。

Windows の cmd.exe / ps から wsl を起動するコマンド wsl は、オプションで wslpath を指定すると、windowsのパスを wsl のパスに変換できる。

takuya@DESKTOP-2AO3 C:\Users\takuya > wsl wslpath  C:/Users/takuya/Desktop/
/mnt/c/Users/takuya/Desktop/

つかっている clip コマンドについて

CLIPは「クリップボードへコマンド結果を流し込む」事ができる。昔からあるコマンドです。ただしペーストはできません。

C:\Users\takuya>clip /?

CLIP

Description:
    Redirects output of command line tools to the Windows clipboard.
    This text output can then be pasted into other programs.

Parameter List:
    /?                  Displays this help message.

Examples:
    DIR | CLIP          Places a copy of the current directory
                        listing into the Windows clipboard.

    CLIP < README.TXT   Places a copy of the text from readme.txt
                        on to the Windows clipboard.

ペースト(貼り付け)の実現

ペーストについては、PowerShell(PS)を利用する。

ps Get-Clipboard

これを、WSLのPATH内部からWindowsのコマンドを実行することで実現する。

私の実行環境は

実行環境は、WSL v1 です。WSL2 は暗号化ドライブを一旦解除して、インストールしなおしになるので面倒なので WSL2にバージョンアップしていません・・・

参考資料

https://www.techtronic.us/pbcopy-pbpaste-for-wsl/

VSCode で PlantUML をプレビューする

準備

vscode のインストール

Windows の場合

choco install vscode 

Macの場合

brew install --cask visual-studio-code

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

f:id:takuya_1st:20210117030149p:plain

PlantUML のインストール

choco install plantuml

インストールするとOpenJDKも一緒にインストールされる。

## java と plantuml.jar のパスを確認。

インストールされた場所の確認する。

choco でインストールすると普段と違う場所になったりするので、 実際の場所 を cmd.exeで確認。

# javaの場所

C:\Users\takuya>where java
C:\Program Files\AdoptOpenJDK\jre-15.0.1.9-hotspot\bin\java.exe

# plantuml の場所

plantuml.jar がほしいのだけれど、 plantuml.jar を探すのは手間なので、先に plantuml.exe を探す。

C:\Users\takuya>where plantuml

C:\ProgramData\chocolatey\bin\plantuml.exe

これで、 plantuml がインストールされた場所がわかった。

choco でインストールすると、choco のは以下に作られるのが面白いですね。

java の場所

where java

確認したら次のようになっていました。

C:\Users\takuya>where java
C:\Program Files\AdoptOpenJDK\jre-16.0.1.9-hotspot\bin\java.exe
C:\Program Files\AdoptOpenJDK\jre-15.0.2.7-hotspot\bin\java.exe

java.exe の場所はアップデートで頻繁に変わるので困ります。 バージョン番号なしでアクセスできるようにしてほしい・・・

エクスプローラーで開いて確認

plantuml がインストールされた場所がわかったので、このフォルダを開いて plantuml.jar を探す。

インストールした箇所をエクスプローラーで確認します。見つけました。

f:id:takuya_1st:20210117030153p:plain

plantuml.jarjava.exeのパスを設定へ

先程探した、これらのPATHを、PlantUMLのプラグイン設定に追加。

f:id:takuya_1st:20210117030146p:plain

拡張子 PU のファイルを作る

@startuml test

class PlantUml

@enduml

このファイルをVS Codeで確認

f:id:takuya_1st:20210117030156p:plain

ALT-D を押してプレビュー

f:id:takuya_1st:20210117030159p:plain

もしプレビューがどうしても使えないとき

そして、どうしてもUMLの作成を急ぐときは、オンライン変換を使う。 http://www.plantuml.com/plantuml/uml/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000

マニュアルについて

日本語マニュアルもあるので大丈夫だよ。 https://plantuml.com/ja/download

2021-07-09

PlantUMLでマッチなかったので、記述を見直し

lsof でIPを指定する /v4 or v6 / ポート使ってるプロセスを見る

lsof でIPを指定する

lsof で探すときに、指定したIPでリッスンしているものを見たいときにどうするのか、複数のIPがあると明示的に指定したいですね。

ポートを使ってるプロセスを見る

たとえば、22番を使ってるプロセスを探す。

lsof -i :22 -n

実例

$lsof -i :22 -n
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
dropbear  3272 root    3u  IPv4    618      0t0  TCP 192.168.1.1:ssh (LISTEN)
dropbear 18793 root    0u  IPv4 853114      0t0  TCP 192.168.1.1:ssh->172.16.4.3:54401 (ESTABLISHED)

ip を@マークで指定する。

lsof では @マークで ip アドレスを指定することができる。

アドレス指定

lsof -i @192.168.1.201

アドレス・ポート指定

lsof -i @192.168.1.201:10000

IPv6の場合

いい感じに囲む必要がある。

ls of -i @[2001::::97c]:22

また、v6のIPすべてでリッスンしている場合は、次のようにv4/v6ともに具体的なIPなく表示される。

$sudo lsof -i :22
COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd     946   root    5u  IPv4  23026      0t0  TCP *:ssh (LISTEN)
sshd     946   root    6u  IPv6  23028      0t0  TCP *:ssh (LISTEN)

実際にやってみた例。

アドレス・ポート指定でどのアプリがリッスンしているか見ることができた。

root@jitsi:~# lsof -i @10.185.93.136:3479
COMMAND  PID      USER  FD  TYPE  DEVICE SIZE/OFF NODE NAME
turnserve 569 turnserver  36u  IPv4 17368603      0t0  UDP jitsi.lxd:3479
turnserve 569 turnserver  37u  IPv4 17368604      0t0  UDP jitsi.lxd:3479
turnserve 569 turnserver  38u  IPv4 17368605      0t0  UDP jitsi.lxd:3479
turnserve 569 turnserver  39u  IPv4 17368606      0t0  UDP jitsi.lxd:3479
root@jitsi:~# lsof -i @10.185.93.136:10000
COMMAND  PID USER  FD  TYPE  DEVICE SIZE/OFF NODE NAME
java    1341  jvb  155u  IPv6 17592832      0t0  UDP jitsi.lxd:10000

ss コマンドとの違い

lsof だと「プロセス」を探すので、wireguardのようなカネールモジュールのリッスンは見えない。

該当プロセスが見つからないとき、正確にポートをリッスンしているかどうかをみたいなら ss -nlu(udp) ss -nlt (tcp)を使ってもいいと思う。

2023-04-19 追記

IPv6の場合について追記。

2023-05-16 追記

ポートのみの場合と、ssとの違いについて言及

docker exec で root になる。/ docker exec を指定したユーザーで実行する

docker exec しても root になるとは限らない。

dockerのインスタンスによっては、Dockerfile の記述によっては、docker exec がユーザー root で実行できないことがある。

docker exec を強制的に root にする

uid をオプションに付けたら、強制的にrootユーザ で実行することができる。 -u UIDでUIDを指定することができる。

docker exec -u 0 -it XXXXXX bash 

docker exec で user を指定する。

指定したユーザで起動するには。まずUIDを調べる。UIDがわからないと指定できないし、Dockerのコンテナ内部でどのユーザーがどの番号で存在してるかわからないですね。

たとえば、psqlpostgresql で起動するとする。

# docker exec -it 6a7e5eca6505 getent passwd | grep post
postgres:x:999:999::/var/lib/postgresql:/bin/bash

そのUIDで起動する。

docker exec -it -u 999 6a7e5eca6505 psql

こうすれば、postgresユーザーをdocker exec で直接指定できる。 これで別のユーザーに成り代わってシェルを使えたりする。

apt が使えない docker インスタンスとかで使える

通常通り exec しても apt が使えないときとか。sudo が使えないときとか。

ps がないのでインストールしたいときとかに使える。

docker のイメージファイルはあれこれ省略されているので、ちょっとしたトラブルがあると困るのですが、権限がない程度で Dockerfile から書いて build するのはめんどくさすぎるので。ユーザーを指定されているDockerイメージを扱うためには、ちょっとしたコツがいる。

参考資料

https://stackoverflow.com/questions/59376888/become-root-in-a-docker-container

2021-05-27

表記ゆれ更新

ssh/config で ホストを踏み台にして、直接 lxc のコンテナにログインする。

LXCにssh コマンドでログインしたい。

lxcのコンテナに にいちいちSSHインストールしたりパスワード決めたり、公開鍵を設定するのがめんどくさい。

通常であれば次のようにログインする。

いったん、SSHでログインして、その後にlxc コマンドで shell を起動する。

takuya@local $ ssh lxc-host
takuya@lxc-host$
takuya@lxc-host$ lxc shell lxc-container-01

頻繁に実行しているとめんどくさいです。そこで ssh_config でconfig ファイルを設定してなんとかできないか考えました。

SSH経由でログインを自動化したい。

~/.ssh/configのファイルに記載します。

  Host lxc-container01
    Hostname lxc-host
    RequestTTY force
    RemoteCommand lxc exec --force-interactive  lxc-container-01 bash

ssh でlxc-hostにログインする設定をかき、ログイン後に実行されるRemoteCommandで lxc shell コマンドを実行するわけです。

SSH踏み台経由と組み合わせる。

踏み台を使う場合は次のように書きます。

  Host lxc-host
    Hostname lxc-host
    ProxyCommand ssh -W %h:%p ssh-GW

踏み台を使う場合は、ProxyCommandを書きますよね。 ProxyCommand はローカルで実行され、接続に使用されます。

これを、RemoteCommandと組み合わせてれば、良い訳です。これでGWを経由しつつ、LXCホストを踏み台にし、LXCコンテナのbashに1発でアクセスできるわけです。

組み合わせると次のようになります。

  Host lxc-container01
    Hostname lxc-host
    RequestTTY force
    ProxyCommand ssh -W %h:%p ssh-GW
    RemoteCommand lxc exec --force-interactive  lxc-container-01 bash

この設定では RequestTTY forcelxc exec --force-interactive を使って tty をAllocateしています。こうしないと制御文字(EOF/CTRL-Dなど)がうまく渡せません。

まとめ

  • RemoteCommand でSSHログイン後にリモートで実行するコマンドを指定
  • lxc exec --force-interactive でTTYを確保
  • RequestTTY force でローカルからリモートまでTTYでつなぐ

その他の応用方法

肝になる設定はRemoteCommandなので、踏み台に設置する authorized_keys に書くことで、鍵で識別して接続を振り分けてしまうことができる。

今回は、lxc だが。 docker exec -it でも同じようにすることで、ちょくせつDockerのインスタンスに入れる。

sshの公開鍵authorized_keys ファイルの制限機能について調べてみたら楽しかった. - それマグで!

Snap でインストールしたApp/コマンドの man を見る。

snap でインストールしたコマンドの man が見つからない。

たとえばLDX をインストールしている場合でも、man はない。

takuya@m75q-1:~$ man -k lxc
lxc: nothing appropriate.

LXDのman を見たいんだ。

MANPATH をなんとかすれば見れそうだが

通常であれば、MANPATH をなんとかすれば見れます。

export MANPATH=":/snap/$APP/current/share/man"

LXC/LXDの場合は。manpageそのものがない。

でもLXDの場合は、manpage が存在しないのでインストールされていない。

そもそもmanがないので、MANPATHで解決しない。

調べた結果 manはなかった。

manがないんですよ。man はオワコンなんです?

--help を使えって言われた。

takuya@:~$ lxc shell --help
Description:
  Execute commands in instances

  The command is executed directly using exec, so there is no shell and
  shell patterns (variables, file redirects, ...) won't be understood.
  If you need a shell environment you need to execute the shell
  executable, passing the shell commands as arguments, for example:

    lxc exec <instance> -- sh -c "cd /tmp && pwd"

  Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored).

Usage:
  lxc exec [<remote>:]<instance> [flags] [--] <command line>

Flags:
      --cwd                    Directory to run the command in (default /root)
  -n, --disable-stdin          Disable stdin (reads from /dev/null)
      --env                    Environment variable to set (e.g. HOME=/home/foo)
  -t, --force-interactive      Force pseudo-terminal allocation
  -T, --force-noninteractive   Disable pseudo-terminal allocation
      --group                  Group ID to run the command as (default 0)
      --mode                   Override the terminal mode (auto, interactive or non-interactive) (default "auto")
      --user                   User ID to run the command as (default 0)

Global Flags:
      --debug            Show all debug messages
      --force-local      Force using the local unix socket
  -h, --help             Print help
      --project string   Override the source project
  -q, --quiet            Don't show progress information
  -v, --verbose          Show all information messages
      --version          Print version number

参考資料

https://discuss.linuxcontainers.org/t/how-can-i-make-use-of-man-pages-with-the-snap-package/2572/2

https://stackoverflow.com/questions/34445796/how-do-i-configure-ssh-proxycommand-correctly-to-run-docker-exec