STUDY · NETWORK GUIDE

5-11 QoS トラブルシュート — 効かない QoS の段を特定する

組んだはずの QoS が効かないときに、分類・マーキング・キューイングのどの段で崩れているかを特定する手順を、Catalyst 9000v (IOS-XE 17.15.03) と csr1000v (17.03.08a) の直列ラボで扱う。正常に動く構成へ誤設定を 1 箇所だけ注入して 3 通りの故障を作り、同じ切り分けチェックリストを 3 回歩く。受信側の症状はどれも「音声が悪い」で同じだが、SW1 の match カウンタ差分・受信側 tcpdump の tos・R1 の b/w exceed drops という決定的指標だけが段を言い当てる。ACL のポート誤記は分類の段で match 差分 +0、set dscp af31 はマーキングの段で tos 0x68、priority percent 25 はキューイングの段で 84.04 バイト/パケットの音声が入ったまま b/w exceed drops +50 として現れる。修正はどれも 1 行で、第 5 章のまとめと第 6 章の予告も収めた章末節。

1. 前節の振り返りと本節の内容

前節 5-10 ハンズオン: 音声優先化 では、端末から出た音声をアクセス端で EF にマーキングし、WAN 出口の LLQ (Low Latency Queuing) で守るところまでを 1 本の経路で通しました。同じトラフィックのまま構成だけを 4 通りに変えた結果、音声が無損失になったのは信頼境界と LLQ の両方が揃った状態だけで、受信側の実測は 0/1003 (0%)、ジッタ 7.425 ms でした。

5-1 から 5-10 までは、QoS を正しく組む側から扱ってきました。本節が出発点にするのは逆の状況です。設計どおりに組んだはずの QoS が、期待どおりに効いていないという申告から始めます。

「QoS が効かない」という申告は、利用者の側からは 1 種類の症状にしか見えません。音声が途切れる、遅れる、相手の声が抜ける。原因は分類・マーキング・キューイングという 3 つの段のどこにでも生じますが、症状の見た目からは段を特定できません。ここで設定を勘で触ると、正常に働いている段を壊して症状を増やすことになります。優先クラスの割当を増やせば他クラスが落ち始め、分類条件を広げれば守る対象でないものが優先枠に入ります。QoS の設定変更は、どれも別のクラスから何かを奪う操作です。

本節では、正常に動く構成へ誤設定を 1 箇所だけ注入して 3 通りの故障を作り、毎回同じ順序で同じ指標を読み、崩れている段を確定させます。**修正はどの故障も 1 行で終わります。**本節の主題は直し方ではなく、その 1 行に辿り着くまでの手順です。

2. 切り分けの型 — パイプラインの段順に読む

QoS は 3 つの段が直列に並んだパイプラインで、前段の結果を後段が使います。分類 (Classification) が対象を掴み、マーキング (Marking) が印を付け、キューイング (Queuing) が印を見て送出順と割当を決めます。分類が掴めていなければ、後段のマーキングもキューイングも働きようがありません。検査もこの依存関係の順に進めます。

この図を大きく開く ↗

切り分けの型。症状「音声が途切れる」は 3 つの故障のどれでも同じに見えるため、パイプラインの段順に決定的指標を読んで止まっている段を絞り込む。段1 分類は SW1 の match カウンタの差分で「掴めているか」を、段2 マーキングは受信側 PC3 の tcpdump が示す tos で「意図した印が付いているか」を、段3 キューイングは R1 のクラス別 pkts と b/w exceed drops の差分で「正しいキューに入って割当が足りているか」を読む。前段の正常を確認してから次段へ進むと、調べる範囲が後段だけに絞れる。カウンタは累積値なので差分で読み、読む前に 30 秒待つ。受信品質の正本は送信側の自己申告ではなく受信側の記録。

段ごとに、読む場所と正常な読み値は決まっています。

確かめる問い読む場所正常な読み値
段1 分類対象を掴めているかSW1 show policy-map interface の match カウンタ差分が +0 ではなく、送出規模なりに増える
段2 マーキング意図した印が付いているか受信側 PC3 の tcpdump の tos音声が tos 0xb8 (EF)
段3 キューイング正しいキューに入り、割当が足りているかR1 show policy-map interface GigabitEthernet3 のクラス別 pkts / bytes と b/w exceed drops音声のサイズが入り、drops の差分が 0

段順に歩く入口とは別に、症状から見当をつける入口もあります。次の表は調べ始める場所を決めるためのもので、確定は §5 以降で使う決定的指標で行います。

受信側で見えている症状まず疑う段見当の根拠
音声も他のクラスも同じように失われている段1 分類 / 段2 マーキング音声が優先キューに入っていない可能性。印が付かなければ他クラスと同じ待ち行列に並ぶ
音声の損失は他より小さいが 0 にならない段3 キューイング優先キューには入っているが、割当が実需要に足りていない可能性
印を持たないはずのアプリが優先されている段1 分類 / 段2 マーキング分類条件が広すぎるか、端末が名乗った印をそのまま採っている可能性 (5-10 の状態③)

症状は入口の見当までで、段の確定には使いません。 これから見る 3 つの故障は受信側の損失率が 15 %・16 %・4.4 % で、どれも同じ「音声が悪い」という申告になります。4.4 % が他より小さいことは上の対応表の見当には使えますが、この差だけを根拠に段を確定することはできないためです。

3. ラボ構成と正常形の基準値

診断に使うトポロジは 5-10 と同一です。端末 2 台がアクセススイッチ SW1 に収容され、SW1 から WAN エッジルータ R1 を経由して対向端末 PC3 へ抜けます。SW1 が信頼境界の適用点、R1 の Gi3 が契約帯域への絞り込みと優先制御の適用点です。

この図を大きく開く ↗

診断ラボの構成と 3 つの観測点。トポロジは 5-10 の通しラボと同一で、信頼境界 + 契約 85 kbps + LLQ 30 % を組んだ状態 (5-10 の状態④) を正常形とし、故障はこの正常形へ 1 箇所だけ注入する。観測点①は SW1 の入口で、show policy-map interface の match カウンタ差分が分類の段を示す。観測点②は受信側 PC3 で、tcpdump の tos が実際に届いた印を、iperf サーバの interval 行が受信品質を示す。観測点③は R1 Gi3 で、クラス別 pkts (bytes ÷ pkts ≈ 84 が音声のパケット長) と b/w exceed drops がキューイングの段を示す。流すものは全ケース共通で、音声 22.4 kbps・EF を詐称するアプリ 32.9 kbps・素の業務データ 48.3 kbps の合計 103.6 kbps を契約 85 kbps へ入れ、常時 122 % の輻輳を保つ。帯域はいずれも QoS カウンタと同じ Ethernet ヘッダ込みの値で、iperf の指定と表示はペイロード基準のため数字が異なる (音声は指定 11200・表示 11.2 Kbits/sec)。

使用した機器と IOS-XE は次のとおりです。

snippet
SW1# show version | include Cisco IOS-XE Software|C9KV|Version
Cisco IOS XE Software, Version 17.15.03
Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.15.3, RELEASE SOFTWARE (fc1)
(GPL ライセンス表記の 3 行を省略)
cisco C9KV-UADP-8P (VXE) processor (revision VXE) with 743842K/3075K bytes of memory.
Switch Ports Model              SW Version        SW Image              Mode   
*    1 24    C9KV-UADP-8P       17.15.03          CAT9K_IOSXE           INSTALL
snippet
R1# show version | include Cisco IOS-XE Software|CSR|Version
Cisco IOS XE Software, Version 17.03.08a
Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.8a, RELEASE SOFTWARE (fc3)
(GPL ライセンス表記の 3 行を省略)
cisco CSR1000V (VXE) processor (revision VXE) with 1104920K/3075K bytes of memory.

診断を始める前に、経路が本当に輻輳しているかを確かめます。シェーパが一度も溢れない条件では、優先制御が働かないので故障の診断そのものが成立しません。 経路の実効天井を 1 本測った結果です。

snippet
## 実効天井の測定 (PC2 → PC3 / 1400 バイトで 1000 kbps を要求 / QoS 未投入)
## 受信側 PC3 iperf サーバの interval 行 (接続前のヘッダ行と接続行、および接続直後の
## 1 行 = 0.00-1.00 sec の 1020/1036 (98%) を省略。損失が最初の 1 秒に集中しており、
## 定常の観察にはその後の行を使う)
[  1] 1.00-2.00 sec  23.2 KBytes   190 Kbits/sec  43.939 ms 0/17 (0%)
[  1] 2.00-3.00 sec  23.2 KBytes   190 Kbits/sec  45.659 ms 0/17 (0%)
[  1] 3.00-4.00 sec  21.9 KBytes   179 Kbits/sec  48.037 ms 0/16 (0%)
(中略)
[  1] 10.00-11.00 sec  21.9 KBytes   179 Kbits/sec  50.955 ms 0/16 (0%)
[  1] 11.00-12.00 sec  19.1 KBytes   157 Kbits/sec  56.951 ms 0/14 (0%)

1400 バイトで 1000 kbps を要求しても、interval 行の範囲で 157 kbps から 190 kbps しか通りません。5-10 で測った 167 kbps から 169 kbps と同水準です。契約帯域 85 kbps はこの天井の半分程度に置いてあるので、送出が契約を超えた分はシェーパで確実に溢れます。ブート直後の 1 回目の疎通確認は 3 パケットとも落ちました。 SW1 を越える経路が温まる前の測定で、再試行すると 3 パケットとも往復 23 ms から 39 ms で到達します。測定を始める前の疎通確認は、1 回の失敗で機器の異常と判断しない扱いが要ります。

正常形は 5-10 の状態④、すなわち信頼境界と LLQ の両方を組んだ構成をそのまま使います。SW1 側は送信元 IP と UDP ポートを見る拡張 ACL で音声を分類し、一致したものに EF を付け、一致しないものを既定へ潰します。

snippet
SW1# show running-config | section class-map|policy-map|ip access-list
(システム既定の system-cpp-* / non-client-nrt-class / policy-map system-cpp-policy を省略)
class-map match-all CM-VOICE
 match access-group name ACL-VOICE
policy-map PM-TRUST-BOUNDARY
 class CM-VOICE
  set dscp ef
 class class-default
  set dscp default
ip access-list extended ACL-VOICE
 10 permit udp host 192.168.10.10 any eq 5001

同じ policy-map を 2 つのアクセスポートに当てています。

snippet
SW1# show running-config interface GigabitEthernet1/0/1
(Building configuration... / 空行 / Current configuration : N bytes / ! の 4 行を省略)
interface GigabitEthernet1/0/1
 description PC1 (IP phone) - trust boundary apply point
 switchport access vlan 10
 switchport mode access
 spanning-tree portfast
 service-policy input PM-TRUST-BOUNDARY
end

R1 側は、親ポリシーが契約帯域へ絞り、子ポリシーが match dscp ef に一致したものを優先キューへ入れる 2 段構成です。

snippet
R1# show running-config | section policy-map
policy-map PM-WAN-CHILD
 class CM-VOICE-WAN
  priority percent 30
 class class-default
policy-map PM-WAN-PARENT
 class class-default
  shape average 85000   
   service-policy PM-WAN-CHILD
snippet
R1# show running-config interface GigabitEthernet3
(Building configuration... / 空行 / Current configuration : N bytes / ! の 4 行を省略)
interface GigabitEthernet3
 description to PC3 (WAN side = sub-rate shaper + LLQ apply point / egress)
 ip address 192.168.20.1 255.255.255.0
 negotiation auto
 no mop enabled
 no mop sysid
 service-policy output PM-WAN-PARENT
end

流すトラフィックは全ケースで共通です。受信側の PC3 で 3 つのポートに iperf サーバを立て、送信側の 2 台から 3 本を同時に起動します。

snippet
PC3 $ iperf -s -u -p 5001 -i 1 &                                          (5002 / 5003 も同様)
PC1 $ iperf -c 192.168.20.20 -u -b 11200 -l 42  -t 30 -p 5001 &            (音声)
PC2 $ iperf -c 192.168.20.20 -u -b 30400 -l 512 -S 0xb8 -t 30 -p 5002 &    (自称優先アプリ)
PC2 $ iperf -c 192.168.20.20 -u -b 44600 -l 512 -t 30 -p 5003 &            (素の業務データ)

(実測ではこれらのコマンドを nohup でバックグラウンド起動し、受信側サーバのログから集計しています)

この構成で 30 秒流したときの各観測点の値が、以降の診断で基準となる「正常な読み値」です。まず SW1 の分類カウンタで、測定前は CM-VOICE が 0 packets でした。

snippet
SW1# show policy-map interface GigabitEthernet1/0/1
 GigabitEthernet1/0/1 

  Service-policy input: PM-TRUST-BOUNDARY

    Class-map: CM-VOICE (match-all)  
      1004 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef

    Class-map: class-default (match-any)  
      1297 packets
      Match: any 
      QoS Set
        dscp default

CM-VOICE の差分は +1004 で、30 秒間に送出した音声の規模 (受信側の集計では 1003 データグラム) とほぼ一致します。次に R1 の優先クラスです。測定前は 0 packets, 0 bytes でした。

snippet
R1# show policy-map interface GigabitEthernet3
(親 class-default と優先クラス全体のキュー統計、子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          1004 packets, 84374 bytes
          5 minute offered rate 1000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0

84374 バイトを 1004 パケットで割ると 84.04 バイト です。RTP 12・G.729 の 30 ms 分 30 バイト・UDP 8・IPv4 20・Ethernet 14 を足した音声のサイズと一致します。このパケット長が、優先クラスに入っているものの正体を示す指紋になります (5-10 §8)。なお 84374 バイトは 84 バイト × 1003 個 + 122 バイト × 1 個と分解でき、先頭の 1 パケットだけ大きいのは iperf が最初のデータグラムに制御情報を載せるためです (IP 長では 70 バイトに対して 108 バイト。5-10 §4 で確認した挙動)。この後の tcpdump の逐語に length 108 が見えるのはこの先頭パケットで、**指紋の根拠は tcpdump の length ではなく、QoS カウンタの bytes ÷ pkts です。**受信側に届いた印と受信品質は次のとおりです。

snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
(取得した 60 パケットの tos を grep -o 'tos 0x[0-9a-f]*' | sort | uniq -c で集計)
     60 tos 0xb8
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5002 -c 60   (自称優先アプリ)
     60 tos 0x0
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5003 -c 60   (素の業務データ)
     60 tos 0x0
snippet
## 音声 (port 5001) 受信側 PC3 iperf サーバレポート
[  1] 0.00-30.03 sec  41.2 KBytes  11.2 Kbits/sec   7.459 ms 0/1003 (0%)
## 自称優先アプリ (port 5002, EF を詐称) 受信側 PC3 iperf サーバレポート
[  1] 0.00-33.46 sec   100 KBytes  24.6 Kbits/sec  34.408 ms 25/226 (11%)
## 素の業務データ (port 5003) 受信側 PC3 iperf サーバレポート
[  1] 0.00-33.34 sec   146 KBytes  35.8 Kbits/sec  37.264 ms 39/330 (12%)

音声は無損失、ジッタ 7.459 ms です。輻輳の代償は class-default 側の 2 本が払っています。以降の 3 ケースは、この状態に誤設定を 1 箇所だけ入れて作ります。

4. カウンタを読む前に知る 3 つの罠

診断は指標を読むところから始まりますが、読み方を誤ると正しい出力から誤った結論が出ます。本検証機には罠が 3 つあります。

1 つ目は、同一の policy-map を複数のインタフェースへ当てると show policy-map interface <if> がポリシー単位の合算を返すことです。 本ラボの PM-TRUST-BOUNDARY は Gi1/0/1 と Gi1/0/2 の両方に当たっているため、どちらのインタフェースを指定しても同じ値が返ります。5-10 §10 では、音声が入らない側のポートを読んでも同じだけ増えることを実測しました。match カウンタが増えたことは「そのポリシーが掴んだ」証拠であって、「そのポートで掴んだ」証拠ではありません。 どのポートで効いたかを確かめる必要があるときは、policy を 1 ポートだけに当てて切り分けます。

2 つ目は、カウンタが累積値であり、更新に遅延があることです。 SW1 (Catalyst 9000v) に clear policy-map counters は存在しません (5-10 §10)。値は投入時からの積み上げなので、測定の前後で 2 回読み、その差分を扱います。加えて、送出直後に読むと計上が間に合っていないことがあります (5-10 §10 では 6 秒分の送出の計上が +203 と +105 の 2 回に分かれました)。本ラボの測定はすべて、30 秒の送出が終わってから 30 秒待って読んでいます。それでも本検証機では、差分が送出数を上回る測定窓が複数あり (1〜2 割の超過から、後で棄却する窓では送出数の 3 倍まで)、その過剰の理由は保存した一次資料からは特定できませんでした。そのため判定は「差分が +0 か、増えるか」という質的な差で行い、端数の一致は求めません。

3 つ目は、送信側 iperf の表示が網を通った量ではないことです。 クライアントが表示するのはソケットに書けた量で、経路のバッファに溜まっている分を含みます。受信品質の正本は受信側サーバの記録です。輻輳下では終了通知のデータグラムが落ちてサーバが最終サマリ行を出さないことがあるため、サーバには -i 1 を付けて interval 行からも読めるようにします。

snippet
## 素の業務データ (port 5003) 受信側 PC3 iperf サーバレポート
[  1] 32.00-33.00 sec  5.50 KBytes  45.1 Kbits/sec  37.249 ms 4/15 (27%)
[  1] 33.00-33.34 sec  2.00 KBytes  48.3 Kbits/sec  33.206 ms 0/4 (0%)
[  1] 0.00-33.34 sec   146 KBytes  35.8 Kbits/sec  37.264 ms 39/330 (12%)
[  3] WARNING: ack of last datagram failed.

末尾の WARNING は、終了のやり取り (サーバが受けた最終データグラムへ確認応答を返して締める流れ) が完了しなかったことを示します。UDP の終了処理はベストエフォートのため、輻輳下では終了通知そのものが落ちて最終サマリ行が出ないことも、この例のようにサマリ行は出ても確認応答が完了しないこともあります。この行が出ているときは、サマリ行が欠けていないかを確認したうえで interval 行の合計と突き合わせます。

5. ケース 1: 分類が掴んでいない

利用者からの申告は「音声が途切れる」です。QoS は組んであり、設定を見た限り異常はありません。切り分けチェックリストの段1 から始めます。3 本を 30 秒流し、30 秒待ってから SW1 の分類カウンタを読みます。

snippet
## 測定前 (基準値)
SW1# show policy-map interface GigabitEthernet1/0/1
(Service-policy 見出しを省略)
    Class-map: CM-VOICE (match-all)  
      1004 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef

    Class-map: class-default (match-any)  
      1345 packets
      Match: any 
      QoS Set
        dscp default
## 測定後 (30 秒送出 + 30 秒待機)
SW1# show policy-map interface GigabitEthernet1/0/1
    Class-map: CM-VOICE (match-all)  
      1004 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef

    Class-map: class-default (match-any)  
      3317 packets
      Match: any 
      QoS Set
        dscp default

CM-VOICE の差分は +0 です。 一方で class-default は 1345 から 3317 へ +1972 増えており、トラフィック自体は確かに SW1 を通っています (この差分は送出した 3 本の合計より大きい。§4 のとおり本検証機の差分は送出数を上回ることがあり、判定には CM-VOICE が +0 という質的な差だけを使います)。音声は届いているのに、分類がそれを掴んでいません。この時点で後段を調べる意味はありません。 掴めていないものに正しい印は付かず、印が無いものは優先キューにも入らないためです。

念のため後段の値も確認しておきます。受信側に届いた印と R1 の優先クラスは、掴めていないという読みと矛盾しません。

snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
(tos の集計行と逐語の先頭 2 行のみを抜粋)
     60 tos 0x0
09:50:38.418998 IP (tos 0x0, ttl 63, id 17861, offset 0, flags [DF], proto UDP (17), length 108)
    192.168.10.10.53452 > 192.168.20.20.5001: UDP, length 80
snippet
## 測定前 (基準値)
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          1004 packets, 84374 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0
## 測定後 (30 秒送出 + 30 秒待機)
R1# show policy-map interface GigabitEthernet3
        Class-map: CM-VOICE-WAN (match-all)  
          1004 packets, 84374 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0

音声の tos は 0x0 のままで、EF が付かずに届いたことを示します (音声はもともと印を付けずに送っているため、この値だけでは class-default 側の set dscp default を通った経路までは特定できませんが、EF マーキングが働いていないことは確定します)。R1 の優先クラスも +0 で、EF が 1 つも到着していません。段1 で崩れると、段2 と段3 の指標も同時に崩れます。 逆に言えば、段2 と段3 の異常だけを見て後段を疑うと、原因から遠ざかります。

段1 で止まったので、分類の実物を読みます。

snippet
SW1# show run | section access-list
ip access-list extended ACL-VOICE
 10 permit udp host 192.168.10.10 any eq 5010
SW1# show access-lists ACL-VOICE
Extended IP access list ACL-VOICE
    10 permit udp host 192.168.10.10 any eq 5010

宛先ポートが eq 5010 になっています。 音声が使うのは 5001 で、1 桁の打鍵ミスです。show access-lists は ACL の中身を行番号付きで返すので、条件と実トラフィックの突き合わせに使えます。修正は 1 行です。

snippet
(configure terminal の投入とその応答行を省略)
SW1(config)#ip access-list extended ACL-VOICE
SW1(config-ext-nacl)# no 10
SW1(config-ext-nacl)# 10 permit udp host 192.168.10.10 any eq 5001
SW1(config-ext-nacl)#exit
SW1(config)#end

修正後に同じ 3 本を流し、決定的指標が戻ったことを確認します。

snippet
SW1# show policy-map interface GigabitEthernet1/0/1
(Service-policy 見出しと class-default の表示を省略)
    Class-map: CM-VOICE (match-all)  
      2010 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          2010 packets, 168916 bytes
          5 minute offered rate 1000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0
snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
     60 tos 0xb8
## 音声 (port 5001) 受信側 PC3 iperf サーバレポート
[  1] 0.00-30.03 sec  41.2 KBytes  11.2 Kbits/sec   7.472 ms 0/1003 (0%)

SW1 の match は 1004 から 2010 へ +1006、R1 の優先クラスは 1004 から 2010 へ +1006 で、増えたバイト数 84542 を 1006 で割ると 84.04 バイトです。届いた印は tos 0xb8 に戻り、受信側は 0/1003 (0%) です。「直った」と述べる根拠は、症状が消えたことではなく、3 段の指標が正常な読み値に戻ったことです。

6. ケース 2: 付いている印が違う

次の申告も同じ「音声が途切れる」です。受信側の音声損失は 162/1004 (16%) で、ケース 1 の 15 % とほとんど変わりません。症状だけを見て前のケースと同じ原因だと決めることはできないため、同じ順序でチェックリストを回します。

snippet
## 測定前 (基準値)
SW1# show policy-map interface GigabitEthernet1/0/1
(Service-policy 見出しと class-default の表示を省略)
    Class-map: CM-VOICE (match-all)  
      2010 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp af31
## 測定後 (30 秒送出 + 30 秒待機)
SW1# show policy-map interface GigabitEthernet1/0/1
    Class-map: CM-VOICE (match-all)  
      3179 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp af31

今回は match の差分が +1169 で、分類は掴んでいます。 差分は送出した音声の約 1004 パケットより大きく、この過剰の理由は特定できていません (§4 のとおり本検証機では差分が送出数を上回る窓が複数あります)。値そのものではなく「+0 ではなく増えている」という質的な差が、段1 の正常を示します。段1 が正常なので、調べる範囲を段2 以降に絞れます。

段2 は受信側の tos で読みます。

snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
(tos の集計行と逐語の先頭 2 行のみを抜粋)
     60 tos 0x68
09:54:11.439931 IP (tos 0x68, ttl 63, id 6575, offset 0, flags [DF], proto UDP (17), length 108)
    192.168.10.10.58411 > 192.168.20.20.5001: UDP, length 80

届いた印は 0xb8 (EF) ではなく 0x68 です。 ToS バイトは上位 6 ビットが DSCP なので、0x68 は DSCP 26、すなわち AF31 を意味します。掴んではいるが、付けている値が違うという状態です。WAN 側の子ポリシーは match dscp ef しか見ていないため、AF31 で届いた音声は優先クラスに入りません。

snippet
## 測定前 (基準値)
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          2010 packets, 168916 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0
## 測定後 (30 秒送出 + 30 秒待機)
R1# show policy-map interface GigabitEthernet3
        Class-map: CM-VOICE-WAN (match-all)  
          2010 packets, 168916 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0

R1 の優先クラスは +0 です。ケース 1 と同じ値ですが、同じ +0 でも意味は違います。 ケース 1 は SW1 が掴んでいないための +0、ケース 2 は掴んだうえで別の印を付けたための +0 です。この 2 つを区別するのは段1 の match 差分だけです。

段2 で止まったので、マーキングの実物を読みます。SW1 の show policy-map interface に出ていた QoS Set の行も同じ値を示していましたが、設定として確認します。

snippet
SW1# show run | section policy-map
policy-map system-cpp-policy
policy-map PM-TRUST-BOUNDARY
 class CM-VOICE
  set dscp af31
 class class-default
  set dscp default

set dscp af31 が入っています。 クラス名は CM-VOICE のままなので、設定を上から眺めるだけでは気づきにくい誤りです。過去の設定を流用したときや、クラス名だけを見て値を確認しなかったときに生じます。修正は 1 行です。

snippet
(configure terminal の投入とその応答行を省略)
SW1(config)#policy-map PM-TRUST-BOUNDARY
SW1(config-pmap)# class CM-VOICE
SW1(config-pmap-c)#  no set dscp af31
SW1(config-pmap-c)#  set dscp ef
SW1(config-pmap-c)# exit
SW1(config-pmap)#exit
SW1(config)#end
snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
     60 tos 0xb8
## 音声 (port 5001) 受信側 PC3 iperf サーバレポート
[  1] 0.00-30.02 sec  41.2 KBytes  11.2 Kbits/sec   7.485 ms 0/1003 (0%)

届いた印は 0xb8 に戻り、受信側は 0/1003 (0%) です。この修正確認では、R1 の優先クラスのカウンタを根拠に使いません。 同じ測定窓で優先クラスが送出数を約 200 パケット上回る +1203 を計上し、b/w exceed drops も +106 と増えていました。音声の受信側が 0/1003 (0%) と無損失であることと明らかに矛盾します。同じ窓では SW1 側の match 差分も +3120 と、送出した音声の 3 倍を超えていました。この窓の過剰計上の原因は、保存した一次資料からは特定できませんでした。カウンタと受信側の記録が矛盾したときは、機器の挙動を疑う前に、まずその測定窓そのものを疑って測り直します。 一連の故障測定がすべて終わった後に、正常形を入れ直して同じ測定をやり直した結果が次のものです。

snippet
## 正常形を入れ直した直後 (この時点の優先クラスは 0 packets)
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          0 packets, 0 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0
## 測定後 (30 秒送出 + 30 秒待機)
R1# show policy-map interface GigabitEthernet3
        Class-map: CM-VOICE-WAN (match-all)  
          1007 packets, 84626 bytes
          5 minute offered rate 1000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 0

再測定では +1007 パケット、1 パケットあたり 84.04 バイト、b/w exceed drops は +0 で、過剰計上は再現しませんでした。原因が特定できない測定値は捨てて、再現する測定だけを根拠にします。

7. ケース 3: 正しいキューで落ちている

3 つ目の申告も「音声が途切れる」です。受信側の音声損失は 44/1003 (4.4%) で、ケース 1・2 の 15 %・16 % より小さい値ですが、正常形の 0 % とは明確に違います。

段1 から順に読みます。SW1 の match は 6299 から 7311 へ +1012 増えており、分類は掴んでいます。

snippet
## 測定前 (基準値)
SW1# show policy-map interface GigabitEthernet1/0/1
(Service-policy 見出しと class-default の表示を省略)
    Class-map: CM-VOICE (match-all)  
      6299 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef
## 測定後 (30 秒送出 + 30 秒待機)
SW1# show policy-map interface GigabitEthernet1/0/1
    Class-map: CM-VOICE (match-all)  
      7311 packets
      Match: access-group name ACL-VOICE
      QoS Set
        dscp ef

段2 の tos も正常です。

snippet
PC3 $ sudo tcpdump -n -v -i eth0 udp port 5001 -c 60   (音声)
(tos の集計行と逐語の先頭 2 行のみを抜粋)
     60 tos 0xb8
09:57:41.551289 IP (tos 0xb8, ttl 63, id 13498, offset 0, flags [DF], proto UDP (17), length 108)
    192.168.10.10.37997 > 192.168.20.20.5001: UDP, length 80

段1 と段2 が正常なので、残るのは段3 です。R1 の優先クラスを読みます。

snippet
## 測定前 (基準値)
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          3213 packets, 270006 bytes
          5 minute offered rate 1000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 25% (21 kbps), burst bytes 1500, b/w exceed drops: 106
## 測定後 (30 秒送出 + 30 秒待機)
R1# show policy-map interface GigabitEthernet3
        Class-map: CM-VOICE-WAN (match-all)  
          4225 packets, 355052 bytes
          5 minute offered rate 2000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 25% (21 kbps), burst bytes 1500, b/w exceed drops: 156

パケットの差分は +1012、バイトの差分は 85046 で、1 パケットあたり 84.04 バイトです。音声は確かに優先キューへ入っています。 それにもかかわらず b/w exceed drops が 106 から 156 へ +50 増えています (基準値の 106 は前の測定窓までの累積で、判定は差分で行います)。これが「正しいキューに入って、割当で落ちている」状態の指紋です。同じ条件でもう一度測ると、差分は +1007 パケット・84.04 バイト、b/w exceed drops は 156 から 203 へ +47 で、受信側も 45/1003 (4.5%) と同水準で再現しました。なお、この表示では drop rate 0000 bps のまま b/w exceed drops の差分だけが増えています。5 分平均のレート表示は 30 秒間に 50 パケットという規模の破棄を示せておらず、ここでも読むのは表示中のレートではなく累積カウンタの差分です。

priority は輻輳時に働く条件付きポリサなので、割当を超えた分はこのカウンタに計上されて破棄されます。割当の設計値を読みます。

snippet
R1# show policy-map PM-WAN-CHILD
  Policy Map PM-WAN-CHILD
    Class CM-VOICE-WAN
      priority 25 (%)
    Class class-default

priority 25 (%) で、契約帯域 85 kbps の 25 % は 21.25 kbps です。 カウンタ表示の Priority: 25% (21 kbps) は整数の kbps に丸めた値なので、正確な割当は計算で出します。守る対象である音声の実レートは、同じカウンタから求められます。優先クラスのバイト差分 85046 を 30 秒で割ると 22.7 kbps、再現測定の 84626 では 22.6 kbps です。割当 21.25 kbps は、守る対象の実レートを下回っています。

ここで踏みやすいのが、設計値を IP 層で計算する誤りです。音声の IP 長は 70 バイト (UDP ペイロード 42 + UDP 8 + IPv4 20) で、30 ms ごとに 1 パケットなら 18.7 kbps になります。この数字で見れば 21.25 kbps には余裕があります。しかし本検証機のクラスカウンタは、Ethernet ヘッダ 14 バイトを足した 84 バイトで数えており、割当に対する超過の出方もこの基準で説明できます。 84374 ÷ 1004 = 84.04 という比が、IP 長 70 バイトとの差がちょうど 14 バイトであることを示しています (この L2 込みの計上基準は前節 5-10 で、詐称アプリ 554.0 バイトと音声 84.04 バイトの 2 つの独立したフローにより確認しました)。割当は、カウンタが数えている基準と同じ基準で計算する必要があります。 修正は 1 行です。

snippet
(configure terminal の投入とその応答行を省略)
R1(config)#policy-map PM-WAN-CHILD
R1(config-pmap)# class CM-VOICE-WAN
R1(config-pmap-c)#  priority percent 30
R1(config-pmap-c)# exit
R1(config-pmap)#exit
R1(config)#end

30 % は 25.5 kbps で、実レート 22.6 kbps に対して 1 割強の余裕があります。上限側は 5-10 §9 で扱った 33 % の原則の内側です。

snippet
R1# show policy-map interface GigabitEthernet3
(親 class-default の shape 表示・queue stats for all priority classes・子 class-default の表示を省略)
        Class-map: CM-VOICE-WAN (match-all)  
          6240 packets, 524388 bytes
          5 minute offered rate 1000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Priority: 30% (25 kbps), burst bytes 1500, b/w exceed drops: 203
snippet
## 音声 (port 5001) 受信側 PC3 iperf サーバレポート
[  1] 0.00-30.02 sec  41.2 KBytes  11.2 Kbits/sec   7.267 ms 0/1003 (0%)

パケットの差分は +1008 で 84.04 バイト、b/w exceed drops は 203 のまま +0 です。累積値なので過去に発生した 203 は消えません。現在の状態を語るときに読むのは差分であり、表示されている絶対値ではありません。 受信側も 0/1003 (0%) に戻りました。

この図を大きく開く ↗

同じチェックリストを 3 回歩いたときに、止まる段が毎回変わる様子。ケース1 の ACL ポート誤記は段1 で止まり、match 差分が +0 になる。ケース2 の set dscp の値誤りは段1 を通過して段2 で止まり、届いた tos が 0x68 (AF31) になる。ケース3 の割当不足は段1・段2 とも通過し、段3 で止まる。優先クラスには 84 バイト/パケットの音声が入ったまま b/w exceed drops だけが増える。受信側から見た症状はどれも同じ「音声が悪い」で、修正はどれも 1 行。違いを言い当てたのは勘ではなく、段順に固定したチェックリストと 3 つの決定的指標。

8. 切り分けチェックリストの総括

3 つのケースで読んだ指標を並べると、同じ手順が毎回違う段で止まったことが 1 枚に収まります。

指標 (読む場所)正常形ケース1 分類ケース2 印ケース3 割当
SW1 CM-VOICE match 差分+1004+0+1169 (増える)+1012 (増える)
PC3 の tos (音声)60 tos 0xb860 tos 0x060 tos 0x6860 tos 0xb8
R1 CM-VOICE-WAN 差分+1004 (84.04 B/pkt)+0+0+1012 (84.04 B/pkt)
R1 b/w exceed drops 差分+0+0+0+50 (再現時 +47)
受信側 音声損失 (参考)0/1003 (0%)149/1004 (15%)162/1004 (16%)44/1003 (4.4%)
崩れている段段1 分類段2 マーキング段3 キューイング
修正ACL 1 行set 1 行割当 1 行

表の「受信側 音声損失」の行が示すとおり、受信側の損失率だけでは段を特定できません。 ケース 1 の 15 % とケース 2 の 16 % は測定のばらつきの範囲で、読み分けられません。ケース 3 の 4.4 % は他より小さく、§2 の対応表のとおり「段3 を疑う」という入口の見当には使えますが、それだけでは確定になりません。段を言い当てたのは、表に並べた決定的指標の読み分けです。

指標は単独では確定に使いません。§6 で見たとおり、カウンタ単独の異常は測定窓の汚染でも起きます。複数の指標を突き合わせ、受信側の記録と矛盾しないことを確認して初めて確定に使えます。

読む順序も指標と同じだけ重要です。段1 が崩れているとき、段2 と段3 の指標も同時に崩れます。ケース 1 では tos が 0x0 のまま (EF が付かず)、優先クラスも +0 でした。この状態から段3 だけを見ると「優先クラスに何も入っていないので割当を増やそう」という誤った処置に進みます。 前段の正常を確認してから次段へ進むという順序が、この誤りを防ぎます。逆に段1・段2 の正常が確認できていれば、ケース 3 のように調べる範囲を割当の計算だけに絞れます。

止まったときに読む「実物」
段1 分類show run | section access-list / show access-lists <名前> / show class-map
段2 マーキングshow run | section policy-map (set 行の値)
段3 キューイングshow policy-map <子ポリシー名> (割当の設計値) と、クラスカウンタから求めた実レート

9. 第 5 章のまとめ

第 5 章は、有限な帯域の中で通信に優先順位をつける仕組みを 11 節で扱いました。

5-1 QoS の必要性と 3 モデル は、QoS の出発点が「帯域は有限で、輻輳時には通信ごとの品質要求の違いが無視される」ことにあると整理しました。QoS が制御する指標は帯域・遅延・ジッタ・損失の 4 つで、全体像は Best Effort (何もしない)・IntServ (経路上の全ルータに予約させるがスケールしない)・DiffServ (印を見てホップごとに扱いスケールする) の 3 モデルに分かれます。実運用の主役は DiffServ です。5-2 マーキング体系 は、その印の中身を扱いました。印は階層ごとに別の場所にあり (L2 は 802.1Q タグ内の CoS、L3 は IP ヘッダの DS フィールド)、現在の標準である DSCP は 6 ビット 64 値で PHB を選ぶコードポイントです。DSCP 値の大小はそのまま優先順位ではありません (AF は同じクラスの中では 2 桁目が大きいほど先に捨てられ、EF = 46 は最大値ではない)。

印を扱う場所は 5-3 Trust Boundary5-4 Classification と Marking が担いました。5-3 では、Catalyst 9000 の QoS が mls qos を持たない MQC モデルであること、既定では外来 DSCP がそのまま宛先まで運ばれること、set dscp default で境界を引くと 0 に付け直されることを実機で確認しました。5-4 では境界の内側の作り込みとして、match dscp による多クラス分類とクラス別 set dscp の効果を、受信側の tcpdump が示す 3 つの分布 (EF 維持・AF41 から AF31 への格下げ・BE) で確かめ、分類基準を match access-group や NBAR2 へ広げる構文も扱いました。

帯域を制御する機構は 4 節に分かれます。5-5 Policing と Shaping は、両者が同じトークンバケツの上に立ち、違いは溢れたときの動作だけであることを実測しました (ポリシングは超過をその場で捨てて loss 58 %・ジッタ 0.070 ms、シェーピングは超過をバッファで平準化するためジッタが 7.058 ms へ約 100 倍に増え、それでもバッファは有限なので loss 59 % が出る = 「シェーピングは捨てないから無損失」は誤解)。5-6 Queuing 基礎 は、キューイングが輻輳時にしか働かないため親シェーパで輻輳点を作り、FIFO・WFQ・CBWFQ・LLQ を対比しました。CBWFQ は帯域を守り、LLQ は遅延を守ります。 5-7 HQoS は 1 本の物理 WAN を拠点ごとのサブレートに分け、拠点 A が過負荷で loss 50 % に沈む裏で拠点 B が loss 0 %・ジッタ 0.071 ms を保つ独立性を示しました。5-8 輻輳回避 は、満杯後に一括破棄する Tail Drop と、平均キュー長が閾値を超えた時点から確率的に早期破棄する WRED を対比し、WRED を入れても Tail drop 列がゼロにならないという実機の姿も記録しました。TCP Global Synchronization の再現は 3 試行とも判定基準に届かず、不成立のまま報告しています。

5-9 LAN QoS / WAN QoS 設計 は、これらを 1 つの設計にまとめる手順を扱いました。設計は「どこまで信じるか・何をどの印にするか・どの速度に絞るか・絞った中をどう分けるか」の 4 つの決定でできています。実測の核は配分のトレードオフで、音声に 10 % しか与えない構成では守るはずの EF が 79 % 失われ、45 % へ上げると EF は無損失に戻る代わりに AF41 が 37 %・AF21 が 31 %・BE が 16 % を失いました。守る対象は選べても、守る総量は増やせません。

5-10 ハンズオン と 5-11 トラブルシュートは、組んだものを通しで確かめる側です。5-10 は信頼境界と LLQ が片方だけでは成立しないことを 4 状態の比較で示し、優先クラスに入ったものの正体がパケット長から読めること (554.0 バイトなら詐称アプリ、84.04 バイトなら音声) を確かめました。5-11 は、その正常形へ誤設定を 1 箇所だけ入れ、分類・マーキング・キューイングのどの段で崩れているかを同じ手順で特定しました。第 5 章を通して繰り返し現れたのは、印は分類の材料にすぎず、それ自体は何も守らないという事実です。 印を読んでキューを分ける側と、割当を実需要に合わせる計算とが揃って初めて、通信の質が変わります。

10. 次章

第 5 章では、1 つの機器あるいは 1 本の経路の中で通信に優先順位をつける仕組みを扱いました。次章の主題は、その経路そのものをどう作るかです。

第 6 章 WAN / モダン編では、拠点間を結ぶ技術を扱います。ラベルで転送する MPLS (Multi-Protocol Label Switching) と、その上に顧客ごとの経路表を載せる MPLS L3VPN、回線をソフトウェアで束ねて経路を選ぶ SD-WAN、データセンタの L2 を L3 網の上に延ばす VXLAN (Virtual Extensible LAN) と EVPN (Ethernet VPN)、キャンパスに同じ考え方を持ち込む SD-Access、動的にトンネルを張る DMVPN (Dynamic Multipoint VPN)、そして経路の品質を測って出口を選ぶ PfR (Performance Routing) が並びます。本章で扱った QoS は、これらの技術の上でも設計項目として現れます。次章では、拠点と拠点をどう結ぶかという視点から見ていきましょう。