Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

ルータ⇔PC間とルータ⇔ISP&インターネット間で速度計測

PC⇔ルータ間と、ルータ⇔ISP&インターネット間のどちらにボトルネックがあるのか調べる。

PC⇔ルータ間

PCとルータの両方にiPerf3を入れて計測する。

PC側

PS > .\iperf3.exe -s -i 5 -p 5001 -f m
-----------------------------------------------------------
Server listening on 5001
-----------------------------------------------------------
Accepted connection from 192.168.xxx.1, port 60464
[  5] local 192.168.xxx.yyy port 5001 connected to 192.168.xxx.1 port 60466
[  7] local 192.168.xxx.yyy port 5001 connected to 192.168.xxx.1 port 60468
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-5.00   sec  43.6 MBytes  73.1 Mbits/sec
[  7]   0.00-5.00   sec  43.7 MBytes  73.4 Mbits/sec
[SUM]   0.00-5.00   sec  87.3 MBytes   146 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]   5.00-10.00  sec  47.1 MBytes  79.1 Mbits/sec
[  7]   5.00-10.00  sec  47.0 MBytes  78.8 Mbits/sec
[SUM]   5.00-10.00  sec  94.1 MBytes   158 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]  10.00-15.00  sec  46.2 MBytes  77.5 Mbits/sec
[  7]  10.00-15.00  sec  45.0 MBytes  75.6 Mbits/sec
[SUM]  10.00-15.00  sec  91.3 MBytes   153 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]  15.00-20.00  sec  45.3 MBytes  76.0 Mbits/sec
[  7]  15.00-20.00  sec  45.2 MBytes  75.8 Mbits/sec
[SUM]  15.00-20.00  sec  90.4 MBytes   152 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]  20.00-20.02  sec   153 KBytes  61.9 Mbits/sec
[  7]  20.00-20.02  sec   234 KBytes  94.3 Mbits/sec
[SUM]  20.00-20.02  sec   387 KBytes   156 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  5]   0.00-20.02  sec  0.00 Bytes  0.00 Mbits/sec                  sender
[  5]   0.00-20.02  sec   182 MBytes  76.4 Mbits/sec                  receiver
[  7]   0.00-20.02  sec  0.00 Bytes  0.00 Mbits/sec                  sender
[  7]   0.00-20.02  sec   181 MBytes  75.9 Mbits/sec                  receiver
[SUM]   0.00-20.02  sec  0.00 Bytes  0.00 Mbits/sec                  sender
[SUM]   0.00-20.02  sec   363 MBytes   152 Mbits/sec                  receiver
-----------------------------------------------------------
Server listening on 5001
-----------------------------------------------------------

ルータ側

root# iperf3 -c 192.168.xxx.yyy -P 2 -i 5 -p 5001 -f m -t 20
Connecting to host 192.168.xxx.yyy, port 5001
[  5] local 192.168.xxx.1 port 60466 connected to 192.168.xxx.yyy port 5001
[  7] local 192.168.xxx.1 port 60468 connected to 192.168.xxx.yyy port 5001
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-5.09   sec  44.6 MBytes  73.5 Mbits/sec   82    201 KBytes
[  7]   0.00-5.09   sec  44.7 MBytes  73.8 Mbits/sec    0    212 KBytes
[SUM]   0.00-5.09   sec  89.3 MBytes   147 Mbits/sec   82
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]   5.09-10.02  sec  46.5 MBytes  79.0 Mbits/sec    0    211 KBytes
[  7]   5.09-10.02  sec  46.3 MBytes  78.7 Mbits/sec    0    212 KBytes
[SUM]   5.09-10.02  sec  92.8 MBytes   158 Mbits/sec    0
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]  10.02-15.09  sec  46.8 MBytes  77.5 Mbits/sec    0    211 KBytes
[  7]  10.02-15.09  sec  45.7 MBytes  75.7 Mbits/sec    0    212 KBytes
[SUM]  10.02-15.09  sec  92.4 MBytes   153 Mbits/sec    0
- - - - - - - - - - - - - - - - - - - - - - - - -
[  5]  15.09-20.01  sec  44.5 MBytes  75.9 Mbits/sec    0    211 KBytes
[  7]  15.09-20.01  sec  44.4 MBytes  75.7 Mbits/sec    0    212 KBytes
[SUM]  15.09-20.01  sec  88.9 MBytes   152 Mbits/sec    0
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-20.01  sec   182 MBytes  76.5 Mbits/sec   82             sender
[  5]   0.00-20.01  sec   182 MBytes  76.5 Mbits/sec                  receiver
[  7]   0.00-20.01  sec   181 MBytes  75.9 Mbits/sec    0             sender
[  7]   0.00-20.01  sec   181 MBytes  75.9 Mbits/sec                  receiver
[SUM]   0.00-20.01  sec   363 MBytes   152 Mbits/sec   82             sender
[SUM]   0.00-20.01  sec   363 MBytes   152 Mbits/sec                  receiver

iperf Done.

ルータ⇔ISP&インターネット間

一つは、iPerf3の公開サーバを使用する方法。当然、使用するサーバによって速度が異なる。speedtest.netに参加しているサーバがiPerf3に対応していればいいのだが……

root# iperf3 -c bouygues.iperf.fr -P 1 -i 5 -p 9200 -f m -t 20
Connecting to host bouygues.iperf.fr, port 9200
[  5] local 192.168.1.2 port 47220 connected to 89.84.1.222 port 9200
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-5.00   sec  1.52 MBytes  2.55 Mbits/sec    0    112 KBytes
[  5]   5.00-10.00  sec  4.43 MBytes  7.43 Mbits/sec    1    278 KBytes
[  5]  10.00-15.00  sec  6.89 MBytes  11.6 Mbits/sec    0    396 KBytes
[  5]  15.00-20.00  sec  7.93 MBytes  13.3 Mbits/sec    0    427 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-20.00  sec  20.8 MBytes  8.71 Mbits/sec    1             sender
[  5]   0.00-20.24  sec  20.3 MBytes  8.40 Mbits/sec                  receiver

もう一つは、netperfを使う方法。

root# speedtest-netperf.sh
2020-10-26 20:10:37 Starting speedtest for 60 seconds per transfer session.
Measure speed to netperf.bufferbloat.net (IPv4) while pinging gstatic.com.
Download and upload sessions are sequential, each with 5 simultaneous streams.
..............................................................
 Download:  57.29 Mbps
  Latency: [in msec, 62 pings, 0.00% packet loss]
      Min:   3.820
    10pct:   4.000
   Median:   4.340
      Avg:   4.767# clear
    90pct:   4.700
      Max:  25.681
 CPU Load: [in % busy (avg +/- std dev), 60 samples]
     cpu0:  32.3 +/-  9.3
 Overhead: [in % used of total CPU available]
  netperf:  19.5
.............................................................
   Upload:  74.16 Mbps
  Latency: [in msec, 60 pings, 0.00% packet loss]
      Min:   4.080
    10pct:   4.180
   Median:   4.500
      Avg:   5.555
    90pct:   9.101
      Max:  15.660
 CPU Load: [in % busy (avg +/- std dev), 58 samples]
     cpu0:  53.1 +/- 10.6
 Overhead: [in % used of total CPU available]
  netperf:  14.5

4Kモニタへの60Hz出力についての知見

  • USB3.1 Type-Cコネクタでも、ハードウェアレベルで画面出力には使えないことがある。4K60Hzだと12Gbps必要で、USB 3.1Gen2の10Gbpsでは追いつかない。USB Alt ModeのDisplayPortでも4Kでは30Hzになってしまう。4K 60Hzに追いつくにはThunderbolt 3(40Gbps)が必要。
  • HDMIで4K出力対応と書いてあっても、リフレッシュレートが30Hzかもしれない。30Hzでもブラウザ等の表示に違和感はないが、マウスカーソルの表示がかなりカクついて見える。
  • チップセットがDisplayPortでの4K 60Hz出力に対応していても、HDMI接続では4K 30Hz出力にしか対応していないかもしれない。そのうえ、DisplayPortの出力端子がないかもしれない。
  • USBのディスプレイアダプタにはUSB 3.0で4K 60Hz出力が可能なものもある。ただ、けっこう高価(DisplayLinkのチップを使ったものが多く、だいたい1万円からする)。
  • USB-CポートにDisplayPortのマークが付いていれば画面出力に使用できる。ただ、BIOS画面の出力には使えないことがあるみたい。OSが立ち上がって、画面出力切り替え操作をしないと出力されないケースがあった。
  • USB-CポートにDisplayPortのマークも付いてるし、マシンスペック上は4K 60Hzに対応している場合でも、OSの設定で4K 60Hz出力が使えなくなっている場合がある。例えばシンクライアントの場合。
  • USB Type-Aポートが付いているモニタの場合、USB-Cポートからの入力がないとUSB Type-Aポートへの給電がされない場合があるみたい。
  • PIPが使えるモニタなら4KモニタをスケーリングしてFull HD x 2 60Hzのモニタとして使えるのかな?

TFTPでファイルのリクエストがタイムアウトになる

背景

  • バイスのブート時にイメージをTFTPで流しこみたい。
  • TFTPで通信する際、デバイスIPアドレスは192.168.11.1、TFTPサーバは192.168.11.168で固定になっている。
  • TFTPサーバにはWindows 10上のPumpKINを使っている。
  • PumpKINの設定で、リクエスト・レスポンスともudp/69を使うよう設定している。
  • TFTPサーバとデバイスはLANケーブルで直接接続している。

問題

  • バイスからTFTPのリクエストは来るが、以下のようにPumpKINのコンソールにエラーが出力され、正しくファイルが返送されない。
[10/07/20 22:20:35] 'firmware_XXXX.XXX' of type 'octet' is requested from 192.168.11.1
[10/07/20 22:20:35] UDP packet receive failed
[10/07/20 22:20:40] Transmission of 'firmware_XXXX.XXX' is timed out
[10/07/20 22:20:40] Transfer of 'firmware_XXXX.XXX' was aborted
  • Windows 10上で192.168.11.168に対してtftpでファイルをリクエスト(tftp -i 192.168.11.168 get firmware_XXXX.XXX)すると正常に取得できる。そのため、WindowsファイアウォールでPumpKINがブロックされているとか、TFTPの待ち受け用ポートは開放されているが返送用ポートが閉じているとか、PumpKINがファイルを参照できていない、といった問題ではない。
  • WireSharkでLANケーブルが挿さっているNICのパケットをキャプチャすると、TFTPのリクエストのパケットが192.168.11.1→192.168.11.168へ正常に飛んでいるのが確認できる。たが、その後192.168.11.168→192.168.11.1へ応答のパケットがひとつも出ていっていない。
  • エラーメッセージはタイムアウトした旨の内容になっている。しかし、WireSharkで確認するとリクエストで設定されているタイムアウトはデフォルトの5秒のまま。

原因

IPアドレス(192.168.11.1)の重複が原因。 Windows 10上にVMWareをセットアップしており、その際に作成されたVMware Network Adapter VMnet8(VMWare Virtual Ethernet Adapter)のIPアドレスが192.168.11.1になっていた。返送先のIPアドレスが重複していたため、VMWare Network Adapterの方へレスポンスが飛んでいたものと思われる(WireSharkでキャプチャしていたNICとは別デバイスなので、WireSharkに補足されない)。

対策

VMware Network Adapter VMnet8を無効化して、再度デバイスをTFTPでブートさせたところ、以下のように問題なくPumpKINからファイルが返送された。

[10/08/20 00:01:10] 'firmware_XXXX.XXX' of type 'octet' is requested from 192.168.11.168
[10/08/20 00:01:14] Transfer of 'firmware_XXXX.XXX' has successfully completed

匿名加工情報の作成に使う鍵つきハッシュの鍵をPBKDF2で生成する

個人情報を含むデータをもとにプログラムのテストデータを作成する場合などには、データの匿名化が必要になります。個人情報保護委員会個人情報の保護に関する法律についてのガイドライン(匿名加工情報編)」*1には、管理用IDなど特定の個人の識別に使える情報を加工する際は「乱数等の他の記述等を加えた上でハッシュ関数等を用いるなどの手法を用いる」よう記載されています。

噛みくだくと、たとえば会員データベースから匿名加工情報を作りたかったら

  • 会員IDなどの情報はHMAC-SHA256などの鍵つきハッシュでハッシュ化しろ
  • 鍵は匿名加工情報の提供先には渡すな
  • 複数の提供先に匿名加工情報を渡すならHMAC-SHA256の鍵は別々にしろ

ということだと理解しています。これは、SHA-256などで単純にハッシュ化した場合、匿名化した情報を受け取った人は以下のような攻撃が可能になってしまうからだと理解してます。

  • たとえば、従業員番号をキーに、従業員の過去の行状(これは一般従業員には開示されておらず、管理職のみが参照できる)を記録したDBを匿名化するとします。匿名化処理自体は、DBの参照を許可されている人(この場合は管理職)が行うとします。
    • たとえば個人の従業員番号は社内の検索システムで検索可能だったとします。この場合、誰かの従業員番号をSHA-256でハッシュ化することで、その人の過去の行状を知ることができてしまいます。
    • 逆に、個人の従業員番号はその個人のみが知っているとします。この場合でも、匿名化されたDBを受けとった一般従業員は、自分の従業員番号をSHA-256でハッシュ化することで、開示されていないはずの自分の過去の行状を知ることができてしまいます。

……なんですが、上記のような説明をプロマネにしたら

  • 何それ難しい! ウチのプロジェクトのメンバーはそんな難しい処理は実装できない! 検討してみるけど、上手くいかなかったら単なるSHA-256でもいいよね?
  • 鍵はどこかに保存しとかなきゃいけないんだよね? ISMSの管理対象は増やしたくない! SHA-256でもいいよね?
  • 匿名化された情報を受けとるのは従業員なんだから、問題が起こったらそれは内部犯行でしょ? 内部犯行まで気にし始めたらキリがないから、そこまで気にしなくてもいいよね? *2

みたいな回答が返ってくるのは想像に難くないわけです。

そういう回答に「いやいやマズいですよ」と返すのも大変なので、鍵つきハッシュの鍵をPBKDF2で生成するという方法を考えてみました。

  • initKey()で鍵をセットします。鍵のセットに使うパスワードは、テストデータの作成担当者が覚えていればよいです(DBなどに保存する必要はありません)。名寄せされたらマズいデータが複数セットある場合は、データセットごとにパスワードは変えます。
    • 単にパスワードをSHA-256でハッシュ化して鍵とする方法も考えましたが、匿名化に使われたパスワードを推測されると鍵つきハッシュの意味がありません。ソルトをつけて、推測の容易性を下げます。ソルトは適当に長めの文字列を設定すればよいでしょう。なお通常、鍵の安全性はパスワードの強度で担保すると思うので、あくまでもソルトは補助です。
    • プログラムを逆コンパイルされた場合にはソルトが判明してしまうので、下記に記載の処理ではそれほど強い防御にはなりません(あくまで匿名化された情報を受け取る第三者によるカジュアルな攻撃の容易性を下げるだけ)。一応、パスワードとソルトを別々の人間が入力すれば、匿名化処理をした本人でも特定個人のデータを一本釣りできないデータを作ることはできます。ただ、匿名化処理をしている人は、そもそも生データにアクセスできているわけで、実務上はあまり意味がないですね。
  • 鍵をセットしたら、hash()で平文からハッシュを得ます。
  • initKey()を呼ぶのは最初の1回だけです。データがn件あったら、initKey()を1回呼んだあと、hash()をn回呼ぶことになります。initKey()はキーの生成に時間がかかります。複数回呼んでも結果は変わりませんが、速度が極端に遅くなると思います。
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Scanner;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        try {
            System.out.print("Clear text: ");
            String clearText = sc.next();
            System.out.print("Password for hashing: ");
            String password = sc.next();
            Main main = new Main();
            main.initKey(password, "FIXED_SALT");
            System.out.println(main.hash(clearText));
        } finally {
            sc.close();
        }
    }

    /** Key derivation algorithm */
    private static final String KEY_DERIVATION_ALGORITHM = "PBKDF2WithHmacSHA256";
    /** Key derivation algorithm */
    private static final String HASHING_ALGORITHM = "HmacSHA256";
    /** How many times calculate */
    private static final int ITERATION = 1000;
    /** Key length */
    private static final int KEY_LENGTH = 256;
    /** key */
    private static byte[] key;

    /**
     * Generate key for HMAC from password and salt
     *
     * @param password clear password
     * @param salt salt
     */
    public void initKey(String password, String salt) {
        PBEKeySpec keySpec;
        SecretKeyFactory skf;
        SecretKey secretKey;
        try {
            keySpec = new PBEKeySpec(password.toCharArray(), salt.getBytes("UTF-8"), ITERATION, KEY_LENGTH);
            skf = SecretKeyFactory.getInstance(KEY_DERIVATION_ALGORITHM);
            secretKey = skf.generateSecret(keySpec);
            key = secretKey.getEncoded();
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Get hashed string with HMAC-SHA256
     * @param clearText string to be hashed
     * @return Hash (hex string)
     */
    public String hash(String clearText) {
        try {
            SecretKeySpec sk = new SecretKeySpec(key, HASHING_ALGORITHM);
            Mac mac = Mac.getInstance(HASHING_ALGORITHM);
            mac.init(sk);
            byte[] mac_bytes = mac.doFinal(clearText.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder(2 * mac_bytes.length);
            for(byte b: mac_bytes) {
                sb.append(String.format("%02x", b&0xff) );
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalStateException | UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
}

*1:https://www.ppc.go.jp/files/pdf/guidelines04.pdf

*2:この観点はよく話に挙がります。ただ、セキュリティの実装自体を行っていない場合はデューデリジェンスが実施できていないことになるので、結構大きな違いがあると思います

Microsoft Print to PDFで出力したPDFファイルが0バイトになる問題を回避する

大きなPDFファイルを複数に分割しようと思って、Adobe ReaderからMicrosoft Print to PDFへ印刷してみたんだけど、できあがるPDFファイルが0バイトで、正常に出力されない。 Wordでも同じ現象が起こったので、これはMicrosoft Print to PDFの問題かな?と思ったら、なんとAzure Information Protection Viewer(AIP)からは問題なくPDFを出力できる(なぜだ)。

じゃあWordファイルもAIPから出力すればいいじゃん!と思ったんだけど、今度はAIPでWordファイルを開けない。普通に対応してるって書いてあるんだけどなぁ……

docs.microsoft.com

というわけで、大きなPDFファイルを複数に分割したい場合は、以下の手順を踏むことにした。

  • 元々のファイルがPDFの場合
    1. AIPでPDFを開いて、印刷するページ範囲を指定してMicrosoft Print to PDFへ出力する。
  • 元々のファイルがMicrosoft Officeファイルである場合
    1. Officeの機能でファイルをPDFへエクスポートする。
    2. AIPでPDFを開いて、印刷するページ範囲を指定してMicrosoft Print to PDFへ出力する。