今日のCCO

読んだ
Anycast RP - Cisco
そっか、デフォルトで最大のIPアドレスを利用されるから、AnycastRP用のLoopbackインタフェースが選択されないようにIGP/EGPでのrouter-idは手動設定するべきなのね
Configuration example to migrate Spanning Tree from PVST+ to MST - Cisco
Spanning Tree Protocol Problems and Related Design Considerations - Cisco
BPDUの最大HOP数は覚えてなかった
Troubleshooting STP on Catalyst Switches Running Cisco IOS System Software - Cisco
Spanning-Tree Protocol Enhancements using Loop Guard and BPDU Skew Detection Features - Cisco

  • Loop guard must be enabled on the non-designated ports (more precisely, on root and alternate ports) for all possible combinations of active topologies.
  • The root guard cannot be enabled on the same port as the loop guard.
  • Loop guard cannot be enabled for ports on which portfast is enabled.

Spanning Tree PortFast BPDU Guard Enhancement - Cisco

When STP BPDU guard disables the port, the port remains in the disabled state unless the port is enabled manually. You can configure a port to reenable itself automatically from the errdisable state. Issue these commands, which set the errdisable-timeout interval and enable the timeout feature
Note: The default timeout interval is 300 seconds and, by default, the timeout feature is disabled.

Understanding and Tuning Spanning Tree Protocol Timers - Cisco

diameter of the STP domain (dia)—This value is the maximum number of bridges between any two points of attachment of end stations. The IEEE recommendation is to consider a maximum diameter of seven bridges for the default STP timers.

max age, forward delayがその他のパラメータによって決まってたとは知らんかった

max age
= End-to-end_BPDU_propa_delay + Message_age_overestimate
= 14 + 6

2 x forward delay
= end-to-end_BPDU_propagation_delay + Message_age_overestimate +
Maximum_frame_lifetime + Maximum_transmission_halt_delay
= 14 + 6 + 7.5 + 1 = 28.5 )

These calculations leave you with these two final formulas (if you round the 0.5 value):

max_age = (4 x hello) + (2 x dia) – 2
forward_delay = *1 / 2

hello, diaによってmax_age, forward_delayの導出ができるので、helloをいじればその他のタイマーも変わるけど

A decrease of the hello time to 1 sec is the easiest and surest way to decrease the STP parameters. However, remember that if you drop the hello time from 2 sec to 1 sec, you double the number of BPDUs that are sent/received by each bridge.

each BPDU includes the hello, forward delay, and max age STP timers. An IEEE bridge is not concerned about the local configuration of the timers value. The IEEE bridge considers the value of the timers in the BPDU that the bridge receives. Effectively, only a timer that is configured on the root bridge of the STP is important.

タイマーの定義はrootのものが使われるので

you must at least configure any timer changes on the root bridge and on the backup root bridge.

Understanding EtherChannel Inconsistency Detection - Cisco
EtherChannelの対向がチャネル解除されてる状態か…

*1:4 x hello) + (3 x dia

再配送についての整理(メモ)

再配送時のトラブルには3パターン
■High AD -> Low AD -> High AD(Fed Back)
Summary)
GatewayがLow ADから入手した情報をHigh ADへ渡すことでHigh AD側が誤ってLow ADからの再配送を信頼してしまい、High AD側でLow ADからの再配送経路を広告してしまうため自側の経路情報が消えてしまう
Solution)
High AD -> Low ADの再配送経路にタグ情報を付加し、Low AD -> High ADの再配送時に上記タグ付き経路を再配送対象から除外する。(route-map)
GatewayルータはLow AD側の情報を信頼するので、そこも修正したい場合は同一のroute-mapによってdistribute-listでLow AD側からRIBへ取り込む経路情報をフィルタする。

■複数プロトコルでの経路情報取得ループ(AD起因)
Prerequisite)
・複数のGateway(AおよびB)が複数のプロトコルでネイバリングし
それぞれ再配送を行っている
Summary)
1.GatewayAにてHigh AD -> Low ADの再配送を行う
2.同様に、GatewayBにてLow AD -> High AD(逆向き)の再配送を行う
3.GatewayAがHigh AD -> Low ADの再配送経路をRIBに乗せるためGatewayBにはLow ADにて再配送経路が広告される
4.GatewayBはHigh ADではなくLow ADで受け取った再配送経路情報をRIBに乗せる
5.GatewayAはGatewayBのLow ADから広告された経路情報(再配送)が信頼性が高いため自身がHigh ADから再配送した経路のネクストホップをGatewayBとする(##Control Planeでの選択ミス##)
6.GatewayBはGatewayAから広告された再配送経路のネクストホップが自分になっているため、送信先が解決できず経路情報が有効にならない(=無効経路)
7.GatewayBにて、再配送経路がLow ADで無効経路となったため、High ADでの経路情報がGatewayAに広告される。
8.GatewayAはLow ADでの広告がなくなるため、High AD -> Low ADの再配送が動作する。
【以降3〜8をループ】
Reason)
Low AD -> High ADの再配送によるFed Backではなく、複数プロトコルでの経路情報交換によってLow ADに再配送された経路情報がHigh ADではなくLow AD側で交換されることにより、ネクストホップ選択不正が発生するため
Solution)
Low AD側でHigh ADからの再配送経路(外部経路)のADをHigh ADよりも高くする。それによって再配送経路の生成元をHigh AD側であると判断させることができる。

■複数プロトコルでの経路情報取得ループ(Metric)
Prerequisite)
・複数のGateway(AおよびB)が複数のプロトコルでネイバリングしそれぞれ再配送を行っている
・再配送がディスタンスベクタとリンクステートなど、Metric情報が伝搬しないプロトコル間である
・ディスタンスベクタ側がHigh ADである
Summary)
1.GatewayAがディスタンスベクタ -> リンクステートへ再配送する
(Metric情報の喪失)
2.GatewayBはリンクステート側がLow ADであるため再配送経路をRIBに乗せる
3.GatewayBがリンクステート -> ディスタンスベクタへ、再配送元より低いシードメトリックで再配送する(誤ったメトリック設定)
4.ディスタンスベクタ ドメイン内で、もともとの生成元のメトリックより低いメトリックで経路情報が広告されることにより、再配送元経路のネクストホップがGatewayBになる。それにより隣接するノードはGatewayBに近い経路を最適経路と判断する。そのため正しい生成元経路(再配送シードメトリックより高いメトリック)情報がドメイン内で喪失する。(##経路選択不正発生##)
6.GatewayBはLow ADであるリンクステートドメイン側経路情報を利用しGatewayA(リンクステートへの再配送元)への経路を選択する。またGatewayAはディスタンスベクタ ドメイン内の経路情報に基づきGatewayBに到達するための最適経路がネクストホップとなる
7.GatewayA,Bはリンクステートからの再配送経路情報がなくなるためディスタンスベクタ ドメインの正しい生成元経路情報が復活する
【以降1-7をループ】
Reason)
再配送時に喪失したメトリック情報より低いシードメトリックで経路情報が戻ってくることにより、本来の生成元経路情報が喪失するため
Solution)
・シードメトリックが再配送前のメトリックより低くならないようにする
・再配送対象とならないようタグでフィルタする

重要知識)
・再配送は「RIB上の情報」をベースに行われる。
・RIP/IGRP/EIGRPはIGPの動作するインタフェースのネットワーク情報が再配送対象となる。
・再配送を行うノード上ではConnectedしかRIBに乗らないが、再配送先ではIGPの動作するインタフェースのネットワーク情報が広告される。

参考URL)
http://www.cisco.com/c/en/us/support/docs/ip/enhanced-interior-gateway-routing-protocol-eigrp/8606-redist.html

BGP Dumpning計算

Cisco公式のコマンドリファレンス見ても公式しか書いてないし、ワークブックで展開してくれてる式も「なぜそうなるの?」っていうところにいきなり飛んでてさっぱり分かんなかった。対数とかわかんないよ…
と嘆きながら調べてみるといい記事があったのでそこからの考え方を自分なりに整理。

例:ワークブックの問題そのままでいくと、公式に当てはめたら

2000 = 750 * 2^{(5/x)}

となる。ここからWindows標準の計算機で計算できるようにしようとすると

  1. 2000 / 750 = 2^(5/x)
  2. func_logtwo(2000 / 750) = func_logtwo(2^(5/x))
  3. func_logtwo(2000 / 750) = 5/x
  4. ln(2000 / 750) / ln(2) = 5/x
  5. x = 5 / ln(2000 / 750) * ln(2) = 3.5334... ≒ 4

で、正解のhalf_timeである4が出てくる。max_suppress_timeはhalf_timeの4倍なので16
よって、デフォルトも含めて記載すると

bgp dampening 4 750 2000 16

が出てくる。ということ。ワークブックに書いてあった式にいきなり飛んだのはこういう流れねーと納得

※func_logtwo(x) = log2(x)を求める関数と仮定義。計算式は ln(x) / ln(2)

#!/bin/bash
pushd ~/test/ine.ccie.rsv5.workbook.initial.configs > /dev/null

CONFIG_DIR=""
CONFIG_DIR_PRE=""
LIST=($(dir | egrep "^" | awk -F. '{print $1}' | sort | uniq))

GNS3HOST=127.0.0.1
BASEPORT=2100
TFTPHOST=192.168.200.200

function sendconfig () {
# enter selected config directory
pushd $CONFIG_DIR > /dev/null

# do expect scripting for R1 to R20, SW1 to SW4.
ROUTER_NUM=`expr $(ls -l R*.cfg | wc -l) + 1`
expect -c "
match_max 20
set timeout 5
log_user 0
log_file -noappend -a /tmp/sendconfig.log
set i 1
while {\$i < $ROUTER_NUM} {
    puts \"------------------------------------------------------------\"
    puts [exec \"date\"]
    send_user \"Send config to R\$i...\"
    puts \"\r\n------------------------------------------------------------\"
    set PORT [expr $BASEPORT + \$i]

    spawn telnet $GNS3HOST \$PORT
    set timeout 1
    expect \"Connection refused\" {
        send_user \"\\033\\[1;31m\"
        send_user \"R\$i connect NG!\r\n\"
        send_user \"\\033\\[0;39m\"
        incr i 1
        continue
    }
    #if {\$i==1} { exp_internal 1} else { exp_internal 0 }
    send \"\r\n\"
    expect \"R\$i\\u0007\"
    send \"\r\n\r\n\"
    expect \"\r\n\r\nR\$i#\"
    set timeout 60
    send \"config replace tftp://$TFTPHOST/$CONFIG_DIR.\$i.cfg force\r\n\"
    expect {
        \"\r\nError: \" {
            send \"config replace tftp://$TFTPHOST/default.R\$i.cfg force\r\n\"
            expect \"Rollback Done\"
            set timeout 1
            send \"\r\n\"
            expect \"R\$i#\"
            set fp [open \"R\$i.cfg\"]
            while {! [eof \$fp]} {
                gets \$fp line
                send \"\$line\r\n\"
                expect \"R\$i\(\"
            }
            close \$fp
            expect \"R\$i#\"
            set timeout 60
            send \"\copy run tftp://$TFTPHOST/$CONFIG_DIR.\$i.cfg\r\n\"
            expect \"]\"
            send \"\r\n\"
            expect \"]\"
            send \"\r\n\"
            expect \"R\$i#\"
            close
        }
        \"\r\nRollback Done\" {
            expect \"R\$i#\"
            close
        }
    }
    incr i 1
}
if {[file exists \"SW1.cfg\"]} {
    set i 1
    while {\$i < 5} {
       puts \"------------------------------------------------------------\"
       puts [exec \"date\"]
       send_user \"Send config to SW\$i...\"
       puts \"\r\n--------------------------------------------------------\"
       set PORT [expr $BASEPORT + 10 + \$i]

       spawn telnet $GNS3HOST \$PORT
       send \"\r\n\r\n\"
       expect \"SW\$i#\"
       set fp [open \"SW\$i.cfg\"]
       while {! [eof \$fp]} {
           gets \$fp line
           send \"\$line\r\n\"
           expect \"SW\$i\(\"
       }
       close \$fp
       expect \"SW\$i#\"
       incr i 1
    }
}
send_user \"send Configuration done.\r\n\"
exit
"
popd > /dev/null

}

if [ ! -z $1 ]; then
    CONFIG_DIR=$1
    echo ------------------------------------------------------------
    echo SELECTED: [ $CONFIG_DIR ]
    echo ------------------------------------------------------------
    sendconfig
    exit
fi

# create menu
PS3="complete=0 > "
while :
do
    clear
    echo ------------------------------------------------------------
    echo SELECTED: [ $CONFIG_DIR ]
    echo ------------------------------------------------------------

    if [ ${#LIST[*]} -eq 0 ]; then
        break
    fi

    select SEL in ${LIST[*]}
    do
        if [ ${REPLY} = "q" ]; then # || [ ${REPLY} = "0" ]; then
            echo "Exit Menu."
            exit 0;
        else
        case "${REPLY}" in
            # Top
            "t" )
            echo "Goto Top."
            CONFIG_DIR=""
            NUMBER_OF_FIELD=0
            break
            ;;
            # Upper
            "u" )
            # Not last 1 hop to top menu.
            if [ ${NUMBER_OF_FIELD} -gt 1 ]; then
                let NUMBER_OF_FIELD=${NUMBER_OF_FIELD}-1
                CONFIG_DIR=${CONFIG_DIR_PRE}
            else
                NUMBER_OF_FIELD=0
                CONFIG_DIR=""
                CONFIG_DIR_PRE=""
            fi
            break
            ;;
            "0" )
            # complete menu check
            if [ -d $CONFIG_DIR ]; then
                break 2
            fi
            echo "### Directory NOT Exist! ###"
            continue
            ;;
            * )
            # Check mishit-key
            if [ -z $SEL ]; then
                # complete directory
                if [ ! -z $CONFIG_DIR ] && [ -d $CONFIG_DIR ]; then
                    break 2
                fi
                echo "### Directory NOT Exist! ###"
                continue 2
            fi
            if [ -z $CONFIG_DIR ]; then
                NUMBER_OF_FIELD=1
                CONFIG_DIR=${SEL}
            else
                CONFIG_DIR_PRE=${CONFIG_DIR}
                CONFIG_DIR=${CONFIG_DIR}.${SEL}
                NUMBER_OF_FIELD=`echo "$CONFIG_DIR"  | awk -F. '{print NF}'`
            fi
        esac
        fi
        break
    done
    LIST=($(dir | egrep "^${CONFIG_DIR}" | awk -v NUMFIELD=$NUMBER_OF_FIELD -F. 'BEGIN{NUMFIELD = NUMFIELD + 1} {print $NUMFIELD}' | sort | uniq))
done

sendconfig

popd > /dev/null

コンフィグ自動投入シェル改造

config replaceに使えるようにTFTPへコンフィグ保管するように変更

#!/bin/bash
pushd ~/test/ine.ccie.rsv5.workbook.initial.configs > /dev/null

CONFIG_DIR=""
CONFIG_DIR_PRE=""
LIST=($(dir | egrep "^" | awk -F. '{print $1}' | sort | uniq))

GNS3HOST=127.0.0.1
BASEPORT=2100
TFTPHOST=127.0.0.1

# create menu
PS3="complete=0 > "
while :
do
    clear
    echo ------------------------------------------------------------
    echo SELECTED: [ $CONFIG_DIR ]
    echo ------------------------------------------------------------

    if [ ${#LIST[*]} -eq 0 ]; then
        break
    fi

    select SEL in ${LIST[*]}
    do
        if [ ${REPLY} = "q" ]; then # || [ ${REPLY} = "0" ]; then
            echo "Exit Menu."
            exit 0;
        else
        case "${REPLY}" in
            # Top
            "t" )
            echo "Goto Top."
            CONFIG_DIR=""
            NUMBER_OF_FIELD=0
            break
            ;;
            # Upper
            "u" )
            # Not last 1 hop to top menu.
            if [ ${NUMBER_OF_FIELD} -gt 1 ]; then
                let NUMBER_OF_FIELD=${NUMBER_OF_FIELD}-1
                CONFIG_DIR=${CONFIG_DIR_PRE}
            else
                NUMBER_OF_FIELD=0
                CONFIG_DIR=""
                CONFIG_DIR_PRE=""
            fi
            break
            ;;
            "0" )
            # complete menu check
            if [ -d $CONFIG_DIR ]; then
                break 2
            fi
            echo "### Directory NOT Exist! ###"
            continue
            ;;
            * )
            # Check mishit-key
            if [ -z $SEL ]; then
                # complete directory
                if [ ! -z $CONFIG_DIR ] && [ -d $CONFIG_DIR ]; then
                    break 2
                fi
                echo "### Directory NOT Exist! ###"
                continue 2
            fi
            if [ -z $CONFIG_DIR ]; then
                NUMBER_OF_FIELD=1
                CONFIG_DIR=${SEL}
            else
                CONFIG_DIR_PRE=${CONFIG_DIR}
                CONFIG_DIR=${CONFIG_DIR}.${SEL}
                NUMBER_OF_FIELD=`echo "$CONFIG_DIR"  | awk -F. '{print NF}'`
            fi
        esac
        fi
        break
    done
    LIST=($(dir | egrep "^${CONFIG_DIR}" | awk -v NUMFIELD=$NUMBER_OF_FIELD -F. 'BEGIN{NUMFIELD = NUMFIELD + 1} {print $NUMFIELD}' | sort | uniq))
done

# enter selected config directory
pushd $CONFIG_DIR > /dev/null

# do expect scripting for R1 to R20, SW1 to SW4.
ROUTER_NUM=`expr $(ls -l R*.cfg | wc -l) + 1`
expect -c "
set timeout 5
log_user 0
log_file -noappend -a /tmp/sendconfig.log
set i 1
while {\$i < $ROUTER_NUM} {
    puts \"------------------------------------------------------------\"
    puts [exec \"date\"]
    send_user \"Send config to R\$i...\"
    puts \"\r\n------------------------------------------------------------\"
    set PORT [expr $BASEPORT + \$i]

    spawn telnet $GNS3HOST \$PORT
    set timeout 1
    expect \"Connection refused\" {
        send_user \"\\033\\[1;31m\"
        send_user \"R\$i connect NG!\r\n\"
        send_user \"\\033\\[0;39m\"
        incr i 1
        continue
    }
    send \"\r\n\r\n\"
    expect \"\r\nR\$i#\"
    set timeout 10
    send \"config replace tftp://$TFTPHOST/$CONFIG_DIR.\$i.cfg force\r\n\"
    expect \"force\r\nError: \" {
            send \"config replace tftp://$TFTPHOST/default.R\$i.cfg force\r\n\"
            expect \"Rollback Done\"
            set timeout 1
            set fp [open \"R\$i.cfg\"]
            while {! [eof \$fp]} {
                gets \$fp line
                send \"\$line\r\n\"
                expect \"R\$i\(\"
            }
            close \$fp
            expect \"R\$i#\"
            send \"\copy run tftp://$TFTPHOST/$CONFIG_DIR.\$i.cfg\r\n\"
            expect \"]\"
            send \"\r\n\"
            expect \"]\"
            send \"\r\n\"
            expect \"R\$i#\#\"
            close
    } \"R\$i#\" {
        close
    }
    incr i 1
}
if {[file exists \"SW1.cfg\"]} {
    set i 1
    while {\$i < 5} {
       puts \"------------------------------------------------------------\"
       puts [exec \"date\"]
       send_user \"Send config to SW\$i...\"
       puts \"\r\n--------------------------------------------------------\"
       set PORT [expr $BASEPORT + 10 + \$i]

       spawn telnet $GNS3HOST \$PORT
       send \"\r\n\r\n\"
       expect \"SW\$i#\"
       set fp [open \"SW\$i.cfg\"]
       while {! [eof \$fp]} {
           gets \$fp line
           send \"\$line\r\n\"
           expect \"SW\$i\(\"
       }
       close \$fp
       expect \"SW\$i#\"
       incr i 1
    }
}
send_user \"send Configuration done.\r\n\"
exit
"
popd > /dev/null
popd > /dev/null

自動コンフィグ投入シェル

自動的に投げるためのシェル。

#!/bin/bash
pushd ~/test/ine.ccie.rsv5.workbook.initial.configs > /dev/null

CONFIG_DIR=""
CONFIG_DIR_PRE=""
LIST=($(dir | egrep "^" | awk -F. '{print $1}' | sort | uniq))

GNS3HOST=127.0.0.1
BASEPORT=2100

# create menu
PS3="complete=0 > "
while :
do
    clear
    echo ------------------------------------------------------------
    echo SELECTED: [ $CONFIG_DIR ]
    echo ------------------------------------------------------------

    if [ ${#LIST[*]} -eq 0 ]; then
        break
    fi

    select SEL in ${LIST[*]}
    do
        if [ ${REPLY} = "q" ]; then
            echo "Exit Menu."
            exit 0;
        else
        case "${REPLY}" in
            # Top
            "t" )
            echo "Goto Top."
            CONFIG_DIR=""
            NUMBER_OF_FIELD=0
            break
            ;;
            # Upper
            "u" )
            # Not last 1 hop to top menu.
            if [ ${NUMBER_OF_FIELD} -gt 1 ]; then
                let NUMBER_OF_FIELD=${NUMBER_OF_FIELD}-1
                CONFIG_DIR=${CONFIG_DIR_PRE}
            else
                NUMBER_OF_FIELD=0
                CONFIG_DIR=""
                CONFIG_DIR_PRE=""
            fi
            break
            ;;
            "0" )
            # complete menu check
            if [ -d $CONFIG_DIR ]; then
                break 2
            fi
            echo "### Directory NOT Exist! ###"
            continue
            ;;
            * )
            # Check mishit-key
            if [ -z $SEL ]; then
                # complete directory
                if [ ! -z $CONFIG_DIR ] && [ -d $CONFIG_DIR ]; then
                    break 2
                fi
                echo "### Directory NOT Exist! ###"
                continue 2
            fi
            if [ -z $CONFIG_DIR ]; then
                NUMBER_OF_FIELD=1
                CONFIG_DIR=${SEL}
            else
                CONFIG_DIR_PRE=${CONFIG_DIR}
                CONFIG_DIR=${CONFIG_DIR}.${SEL}
                NUMBER_OF_FIELD=`echo "$CONFIG_DIR"  | awk -F. '{print NF}'`
            fi
        esac
        fi
        break
    done
    LIST=($(dir | egrep "^${CONFIG_DIR}" | awk -v NUMFIELD=$NUMBER_OF_FIELD -F. 'BEGIN{NUMFIELD = NUMFIELD + 1} {print $NUMFIELD}' | sort | uniq))
done

# enter selected config directory
pushd $CONFIG_DIR > /dev/null

# do expect scripting for R1 to R20, SW1 to SW4.
ROUTER_NUM=`expr $(ls -l R*.cfg | wc -l) + 1`
expect -c "
set timeout 1
log_user 0
log_file -noappend -a /tmp/sendconfig.log
set i 1
while {\$i < $ROUTER_NUM} {
    puts \"------------------------------------------------------------\"
    puts [exec \"date\"]
    send_user \"Send config to R\$i...\"
    puts \"\r\n------------------------------------------------------------\"
    set PORT [expr $BASEPORT + \$i]

    spawn telnet $GNS3HOST \$PORT
    expect \"Connection refused\" {
        send_user \"\\033\\[1;31m\"
        send_user \"R\$i connect NG!\r\n\"
        send_user \"\\033\\[0;39m\"
        incr i 1
        continue
    }
    send \"\r\n\r\n\"
    expect \"R\$i#\"
    set fp [open \"R\$i.cfg\"]
    while {! [eof \$fp]} {
        gets \$fp line
        send \"\$line\r\n\"
        expect \"R\$i\(\"
    }
    close \$fp
    expect \"R\$i#\"
    incr i 1
}
if {[file exists \"SW1.cfg\"]} {
    set i 1
    while {\$i < 5} {
       puts \"------------------------------------------------------------\"
       puts [exec \"date\"]
       send_user \"Send config to SW\$i...\"
       puts \"\r\n--------------------------------------------------------\"
       set PORT [expr $BASEPORT + 10 + \$i]

       spawn telnet $GNS3HOST \$PORT
       send \"\r\n\r\n\"
       expect \"SW\$i#\"
       set fp [open \"SW\$i.cfg\"]
       while {! [eof \$fp]} {
           gets \$fp line
           send \"\$line\r\n\"
           expect \"SW\$i\(\"
       }
       close \$fp
       expect \"SW\$i#\"
       incr i 1
    }
}
send_user \"send Configuration done.\r\n\"
exit
"
popd > /dev/null
popd > /dev/null