# 20220529 低レベル勉強会 Go言語でTCP/IP をみんなでチャレンジ! ## 用意するもの PC、ないし何らかのLinux環境 - RaspberryPi や WSL でもOK - Docker でも OKかな? - Goをインストールする必要があります Mac OSX はうまく行かない?  →TCPIPスタックが異なるので下記にあるエラーが出てしまう MacはDockerでするのが吉? ## go-tcpip レポジトリ 今回のワークショップはこのレポジトリのサンプルプログラムを用います https://github.com/sat0ken/go-tcpip ## pingを試す ```shell= git clone https://github.com/sat0ken/go-tcpip.git cd go-tcpip go get cd example vim ping.go # destのIPアドレスとインターフェイスを自分の環境に書き換える go build ping.go sudo ./ping # root権限が必要なんで注意 ``` 実行例 ``` ffffffffffffb827eb4e681308060001080006040001b827eb4e6813c0a82a08000000000000c0a82a01 ARP Reply : d4f756b49066 send icmp packet ICMP Reply is 0, OK! ``` ### 注意点 - IPアドレスは今のネットワークアドレス内のものを指定しよう - 127.0.0.1 を使う場合はインターフェースを lo 用に変更 ## Mac OSX 環境でうまくいかないぞ! ``` deth@~/Documents/Study/go-tcpip-main/example$ go get # tcpip ../arp.go:51:10: undefined: syscall.SockaddrLinklayer ../arp.go:56:32: undefined: syscall.AF_PACKET ../arp.go:56:79: undefined: syscall.ETH_P_ALL ../connection.go:36:10: undefined: syscall.SockaddrLinklayer ../dns.go:49:10: undefined: syscall.SockaddrLinklayer ../dns.go:54:32: undefined: syscall.AF_PACKET ../dns.go:54:79: undefined: syscall.ETH_P_ALL ../icmp.go:37:10: undefined: syscall.SockaddrLinklayer ../icmp.go:42:32: undefined: syscall.AF_PACKET ../socket.go:66:42: undefined: syscall.SockaddrLinklayer ../icmp.go:42:32: too many errors note: module requires Go 1.17 ``` Docker 使って回避? ## Dockerでテスト Dockerfile: ```dockerfile= FROM golang:1.18.2-bullseye RUN apt-get update && apt-get install -y \ iproute2 \ vim \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN mkdir /go/src/app WORKDIR /go/src/app COPY go.mod /go/src/app COPY go.sum /go/src/app RUN go mod download ADD . /go/src/app ``` docker build と実行(`--net=host`オプションが無いと動かない) ```shell= #Dockerfileがあるディレクトリで実行 docker build -t go-tcpip . docker run --net=host -it go-tcpip ``` :::info go.modの以下の箇所をコメントアウトしないとエラーが出るかもしれない ``` replace ( github.com/lucas-clemente/quic-go => ./debug/quic-go github.com/marten-seemann/qtls-go1-17 => ./debug/qtls-go1-17 ) ``` ::: ```shell= cd example vim ping.go # network if eth0 go build ping.go ./ping ``` ## Go 環境をインストールしよう! ### 公式からインストール goのインストール https://go.dev/doc/install ### Raspberry Pi に公式パッケージをインストール https://qiita.com/nanbuwks/items/9de251ec171c6757eebe 同様にして、 WSL2でもインストールできました ### Ubuntu にレポジトリを追加してインストール 普通にインストールした場合、goのバージョンが古い可能性があります。 ↓ ubuntu@primary:~/go-tcpip/example$ go build ping.go  #tcpip ../utils.go:81:12: undefined: tls.CipherSuites note: module requires Go 1.17 20.04のリポジトリ追加 ``` $ sudo add-apt-repository ppa:longsleep/golang-backports $ sudo apt update $ sudo apt install golang ``` ## Tips ### WSL Windows で WSL を使う場合の注意 - WSL2だと仮想マシンとなり別NWとなる - WSL2 で 127.0.0.1/lo をターゲットにしても返事が返ってこないぞ 原因は不明 - 今回のプログラムはTCPヘッダサイズを 20Bytes としている。Widnowsはヘッダに付加情報を付けるので何か影響が出るかも? 今回のプログラムで Ping を送る対象は同一ネットワーク上のものとなります。127.0.0.1 が使えないぽいので送る対象はコンテナを起動させてそれにARPやPingを送ってみるとか、ホストWindowsに送るなどの工夫が必要です。 以下はホストWindows にPingを送る例です WSL2でホストWindowsのアドレスを取得 ``` $ ip route | grep 'default via' ``` 取得例 ``` default via 172.18.160.1 dev eth0 ``` /etc/resolv.conf を参照したり Windows側で IPCONFIG コマンドを使っても取得できます。 取得したアドレスに WSL2上のUbuntuコマンドで ping が通る状態にしておきます。 通常は ICMP 応答が Windows 側で遮断されているので、以下の資料などを基に遮断を解除します。 「【Windows 11対応】Windowsのファイアウォールで「ping」コマンドへの応答を許可する」 https://atmarkit.itmedia.co.jp/ait/articles/1712/21/news018.html WSL2上のネットワークアダプターなどの情報を取得する ``` $ ip address 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: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether de:89:07:12:6b:5d brd ff:ff:ff:ff:ff:ff 3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 32:5c:6f:c4:7f:83 brd ff:ff:ff:ff:ff:ff 4: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:cf:c5:ce brd ff:ff:ff:ff:ff:ff inet 172.18.160.195/20 brd 172.18.175.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::215:5dff:fecf:c5ce/64 scope link valid_lft forever preferred_lft forever 5: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 6: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000 link/sit 0.0.0.0 brd 0.0.0.0 ``` 上記の例を基にした場合、以下のように ping.go を設定することになります。 ``` // 各自の環境に変えてください destip := "172.18.160.1" // wlp3s0は各自の環境に変えてください localif, err := tcpip.GetLocalInterface("eth0") ``` 上記のように設定したものの実行例です ``` $ sudo ./ping ffffffffffff00155dcfc5ce0806000108000604000100155dcfc5ceac12a0c3000000000000ac12a001 ARP Reply : 00155d7b5138 send icmp packet ICMP Reply is 0, OK! ``` ### RaspberryPi でエラー RaspberryPi OS として 32bit のものを使っているのが原因かな。 ``` $ go build ping.go # tcpip ../tcpip.go:33:49: cannot use 4294967295 (untyped int constant) as int value in argument to rand.Intn (overflows) ``` 該当箇所がこうなっているので ``` binary.BigEndian.PutUint32(b, uint32(rand.Intn(4294967295))) ``` 以下のように変更して動きました ``` binary.BigEndian.PutUint32(b, uint32(rand.Uint32())) ``` ### docker build で RUN go mod downloadでエラー go.modファイルの以下をコメントアウトしてください。 ``` replace ( github.com/lucas-clemente/quic-go => ./debug/quic-go github.com/marten-seemann/qtls-go1-17 => ./debug/qtls-go1-17 ) ``` ### multipath multipassを使ってM1 Mac上でUbuntu VM (ARM版)を動かしてみた - Qiita https://qiita.com/notakaos/items/928987623fc61e815363 #### Windowsの場合 1. インストーラをダウンロード。 https://multipass.run/download/windows 3. インストーラ実行 基本的に「次へ」で進めます。 参考:multipass を Windowsで使う https://qiita.com/ynott/items/7b462172890140aad738 5. PC再起動 6. コマンドプロンプトかパワーシェルを起動 7. ubuntuを起動するため以下のコマンドを実行 multipass shell 8. ubuntuが起動 9. 「Go 環境をインストールしよう!」「Ubuntu にレポジトリを追加してインストール」を参考にgoインストール。 10. あとは「pingを試す」の項目と流れは同じ 11. 終了したい時は exit でシェルを抜ける。 12. ubuntuを終了するには下記コマンドを実行。 multipass stop 以上 #### Macの場合 1. Homebrewにてインストール brew install --cask multipass 2. Ubuntu VMを作成する multipass launch --cpus 2 --disk 20G --mem 4G --name ubuntu 3. ubuntuを起動するため以下のコマンドを実行 multipass shell