WZR-HP-AG300Hのファームウェアを書き換える (macOS)
macOSでWZR-HP-AG300Hのファームウェアを書き換える方法を説明します。DD-WRTの書き込みや純正ファームへの書き戻しに使えます。
まず、WZR-HP-AG300HのLAN側ポートを有線LANでMacBookに接続します。IPアドレスは以下を設定します。
WZR-HP-AG300Hは電源を入れておきます。
NICのインタフェース名を確認します。ここでは en2
とします。
$ ifconfig en2: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
APRテーブルを手動で設定します。
sudo arp -s 192.168.11.1 02:AA:BB:CC:DD:20 en2
$ arp -a ? (192.168.11.1) at 2:aa:bb:cc:dd:20 on en2 permanent [ethernet]
ここで、WZR-HP-AG300Hの電源を入れ直します。すると、数秒後にTFTPのアップロードを実行できるタイミングが来ます。
- USBコネクタのLEDが光る(2〜3秒)
- USBコネクタのLEDが消える
- EthernetポートすべてのLEDが光る(2〜3秒)
- EthernetポートすべてのLEDが消える
- MacBookと接続しているポートのLEDが光る
- TFTPのアップロードを実行できる(4秒間)
下記の記事が分かりやすいので参照してみてください。
TFTPのアップロードを実行できるタイミングがきたら以下を実行します。
echo -e "verbose\nbinary\nput wzr_hp_ag300h_jp_175" | tftp 192.168.11.1
MacBookと接続しているポートのLEDが光ったら何回か実行してみてください。以下のように、最初の数回はエラーが出ると思います。
% echo -e "verbose\nbinary\nput wzr_hp_ag300h_jp_175" | tftp 192.168.11.1 Verbose mode on. mode set to octet putting wzr_hp_ag300h_jp_175 to 192.168.11.1:wzr_hp_ag300h_jp_175 [octet] tftp: sendto: Can't assign requested address % echo -e "verbose\nbinary\nput wzr_hp_ag300h_jp_175" | tftp 192.168.11.1 Verbose mode on. mode set to octet putting wzr_hp_ag300h_jp_175 to 192.168.11.1:wzr_hp_ag300h_jp_175 [octet] Sent 20472060 bytes in 9.4 seconds [17423030 bits/sec]
TFTPのアップロードが完了したら、正面のLEDが赤色になってファームウェアの焼き込みが始まります。数分後に正面のLEDが緑色になれば成功です。
先ほど設定したARPテーブルのエントリを削除します。
sudo arp -d 192.168.11.1
ブラウザで http://192.168.11.1 を開くと見慣れた設定画面が表示されるはずです。
TerraformでプライベートサブネットとNATゲートウェイを管理する
AWSのNATゲートウェイ構成をTerraformで管理する方法を調べたのでまとめます。
NATゲートウェイ構成とは下記のような構成を指します。
次の図は、NAT ゲートウェイを使用した VPC のアーキテクチャを示しています。メインルートテーブルは、プライベートサブネットのインスタンスから NAT ゲートウェイにインターネットトラフィックを送信します。NAT ゲートウェイは、NAT ゲートウェイの Elastic IP アドレスをソース IP アドレスとして使用し、インターネットゲートウェイにトラフィックを送信します。
https://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html
マネジメントコンソールでNATゲートウェイを構成する場合は以下の流れで行います。パブリックサブネットはすでに存在する前提です。
- プライベートサブネットを作成する。
- EIPを確保する。
- パブリックサブネットにNATゲートウェイを作成する。
- ルートテーブルを作成し、デフォルトゲートウェイをNATゲートウェイに向ける。
- プライベートサブネットにルートテーブルをアタッチする。
Terraformの場合は以下のリソースを定義します。
- プライベートサブネット (
aws_subnet
) - EIP (
aws_eip
) - NATゲートウェイ (
aws_nat_gateway
) - ルートテーブル (
aws_route_table
) - ルートテーブルの関連付け (
aws_route_table_association
)
実装例
具体的なコードを見ていきましょう。
説明を簡単にするため、ここではVPCとパブリックサブネットがすでに存在しており、そこにNATゲートウェイを付け加える場面を考えます。
# すでに存在するVPCを参照 data "aws_vpc" "example" { tags { Name = "example_vpc" } } # すでに存在するパブリックサブネットを参照 data "aws_subnet" "public" { vpc_id = "${data.aws_vpc.example.id}" tags { Name = "example_public_subnet" } } resource "aws_subnet" "private" { vpc_id = "${data.aws_vpc.example.id}" cidr_block = "${cidrsubnet(data.aws_vpc.example.cidr_block, 3, 4)}" tags { Name = "example_private_subnet" } } resource "aws_eip" "nat_gateway" { vpc = true tags { Name = "example_nat_gateway_eip" } } resource "aws_nat_gateway" "private" { allocation_id = "${aws_eip.nat_gateway.id}" subnet_id = "${data.aws_subnet.public.id}" tags { Name = "example_nat_gateway" } } resource "aws_route_table" "private" { vpc_id = "${aws_subnet.private.vpc_id}" route { cidr_block = "0.0.0.0/0" nat_gateway_id = "${aws_nat_gateway.private.id}" } tags { Name = "example_private_subnet_route_table" } } resource "aws_route_table_association" "private" { route_table_id = "${aws_route_table.private.id}" subnet_id = "${aws_subnet.private.id}" }
サブネットのCIDRはべた書きしてもよいですが、ここでは cidrsubnet() を使ってVPC CIDRから自動計算しています。以下のように記述すると、VPC CIDRの上位3ビットをサブネットに確保します。
cidr_block = "${cidrsubnet(data.aws_vpc.example.cidr_block, 3, 0)}"
例えば、VPC CIDRが 172.20.0.0/16
の場合、サブネットのCIDRは 172.20.0.0/19
が割り当てられます。サブネットマスク長が16 + 3 = 19になるということです。
cidr_block = "${cidrsubnet(data.aws_vpc.example.cidr_block, 3, 4)}"
と記述すると、サブネットのCIDRは 172.20.128.0/19
が割り当てられます。/16
の上位3ビットに4(100
)を割り当てるので128(1000 0000
)になるということです。説明が難しい。
まとめ
TerraformでプライベートサブネットとNATゲートウェイを定義するのは意外と簡単でした。
See Also:
Googleフォトに写真をアップロードするツールを作った
Googleフォトに写真をアップロードするコマンドラインツールを作りました。
このツールは先月に公開されたGoogle Photos Library APIを利用しています。
Getting Started
APIにアクセスできるように初期設定が必要です。
- https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com/ を開く。
- 「Photos Library API」を有効にする。
- https://console.cloud.google.com/apis/credentials を開く。
- 新しい「OAuth client ID」を作成する。application typeはotherを選ぶ。
- Client IDとClient Secretが発行されるので、以下の環境変数を設定する。
export GOOGLE_CLIENT_ID= export GOOGLE_CLIENT_SECRET=
releasesから gpup
をダウンロードします。
試しに my-photos
フォルダをアップロードしてみましょう。
$ gpup my-photos/ 2018/06/14 10:28:40 The following 2 files will be uploaded: 1: travel.jpg 2: lunch.jpg 2018/06/14 10:28:40 Open http://localhost:8000 for authorization 2018/06/14 10:28:43 GET / 2018/06/14 10:28:49 GET /?state=...&code=... 2018/06/14 10:28:49 Storing token cache to /home/user/.gpup_token 2018/06/14 10:28:49 Queued 2 file(s) 2018/06/14 10:28:49 Uploading travel.jpg 2018/06/14 10:28:49 Uploading lunch.jpg 2018/06/14 10:28:52 Adding 2 file(s) to the library
初回はAPIへのアクセスを許可するためにブラウザで http://localhost:8000 を開く必要があります。Googleアカウントで認証すればOKです。2回目からは ~/.gpup_token
にキャッシュされた情報が使われます。
以下のように -n
オプションを付けると、新しいアルバムにファイルをアップロードできます。
gpup -n "My Album" my-photos/
以下のオプションが使えます。
Usage: gpup [OPTIONS] FILE or DIRECTORY... Setup: 1. Open https://console.cloud.google.com/apis/library/photoslibrary.googleapis.com/ 2. Enable Photos Library API. 3. Open https://console.cloud.google.com/apis/credentials 4. Create an OAuth client ID where the application type is other. 5. Export GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET variables or set the options. Application Options: -n, --new-album=TITLE Create an album and add files into it --oauth-method=[browser|cli] OAuth authorization method (default: browser) --google-client-id= Google API client ID [$GOOGLE_CLIENT_ID] --google-client-secret= Google API client secret [$GOOGLE_CLIENT_SECRET] Help Options: -h, --help Show this help message
今のところ以下の制約事項があります。
- アップロードしたファイルの並び順を指定できない。連番のファイルをアップロードしても、アルバム上の並び順はぐちゃぐちゃになってしまう。
- 写真にEXIFヘッダがない場合はタイムスタンプが現在日時になってしまう。
How it works
このツールはGoで書いています。
APIクライアント
Photos Library APIのクライアントは Google APIs Client Library for Go を利用しています。これはAPI仕様から自動生成されたHTTPクライアントで、だいたいのAPIが網羅されています。今のところPhotos Library APIに特化したクライアントが公開されていないので、自動生成されたクライアントを使っています。
ただし、写真や動画などのバイナリをアップロードするAPIはGoogle APIs Client Library for Goで用意されていないため、以下のように独自に実装しています。
const apiVersion = "v1" const basePath = "https://photoslibrary.googleapis.com/" func (p *Photos) UploadFile(ctx context.Context, filepath string) (*photoslibrary.NewMediaItem, error) { //...snip... r, err := os.Open(filepath) if err != nil { return nil, fmt.Errorf("Could not open file %s: %s", filepath, err) } defer r.Close() req, err := http.NewRequest("POST", fmt.Sprintf("%s%s/uploads", basePath, apiVersion), r) if err != nil { return nil, fmt.Errorf("Could not create a request for uploading file %s: %s", filepath, err) } req.Header.Add("X-Goog-Upload-File-Name", filename) p.log.Printf("Uploading %s", filepath) res, err := p.client.Do(req) if err != nil { p.log.Printf("Error while uploading %s: %s", filepath, err) continue } //...snip... }
アップロードを逐次実行すると時間がかかるため、並列実行するようにしています。
Photos Library APIのガイドラインではExponential backoffアルゴリズムでリトライを行うことと記載されています。さらに、HTTPレスポンスのステータスコードが500番台の場合とネットワークエラーの場合はリトライが可能と明記されています。本ツールでは、 lestrrat-go/backoff を利用し、以下のようにリトライ処理を実装しています。
func (p *Photos) UploadFile(ctx context.Context, filepath string) (*photoslibrary.NewMediaItem, error) { b, cancel := uploadRetryPolicy.Start(ctx) defer cancel() for backoff.Continue(b) { //...snip... switch { case res.StatusCode == 200: return // 正常系の戻り値 case IsRetryableStatusCode(res.StatusCode): p.log.Printf("Error while uploading %s: status %d: %s", filepath, res.StatusCode, body) default: return nil, fmt.Errorf("Could not upload %s: status %d: %s", filepath, res.StatusCode, body) } } } // IsRetryableStatusCode returns true if the status code is retryable, // such as status code is 5xx or network error occurs. // Otherwise returns false. // See https://developers.google.com/photos/library/guides/best-practices#retrying-failed-requests func IsRetryableStatusCode(code int) bool { return code >= 500 && code <= 599 }
OAuth
OAuthの認可コードフローはブラウザで完結するように設計しています。コマンドラインツールで認可コードフローを採用する場合、一般的な実装ではユーザに認可コードを貼り付けてもらう必要があります。具体的には、以下のような流れが必要です。
- ターミナルでコマンドを実行する。
- ターミナルに表示されたURLを開く。
- ブラウザに表示されたGoogleの認可画面でアクセスを許可する。
- ブラウザに表示された認可コードをターミナルに貼り付ける。
- コマンドがAPIにアクセスする。
本ツールでは4の手間を省略し、3が完了したらすぐに5に進むように実装しています。
これは localhost:8000
でHTTPサーバを立ち上げて、Googleの認可画面から localhost:8000
にリダイレクトバックすることで実現しています。
まとめ
Photos Library APIを利用してGoogleフォトに写真をアップロードするコマンドラインツールを作りました。