Nessuna parte di questa pubblicazione puo' essere riprodotta o trasmessa, in qualsiasi forma o con qualsiasi mezzo, elettronico, meccanico, fotocopie, registrazione, senza il consenso dell'autore. This material is not sponsored by, endorsed by, or affiliated with Cisco Systems, Inc., Cisco, Cisco Systems, and the Cisco Systems logo are trademarks or registered trade marks of Cisco Systems, Inc. or its affiliates. All other trademarks are trademarks of their respective owners.
169
Abbiamo pertanto a disposizione una nuova famiglia di comandi pensata principalmente per consentire misure sulle performance della rete in
170
tempo reale ed intervenire automaticamente con delle azioni in conseguenza al verificarsi di determinati eventi. Quindi si ha la possibilita' di monitorare un servizio Internet, come la raggiungibilita' di un indirizzo IP, oppure la raggiungibilita' di un servizio web, o lo stato di una interfaccia, o la latenza e anche di eseguire delle azioni in conseguenza all'attivita' monitorata. Il numero di attivita' monitorabili dipende dalla versione di IOS. E' interessante notare che le performance vengono monitorate attivamente, ovvero con la generazione di traffico. E' anche possibile configurare dei threshold per cui al superare di determinate soglie il router invia una trap SNMP. In alternativa da CLI si possono verificare gli SLA.
La quantita' di servizi e funzionalita' monitorabili e' davvero ampia. Con qualche esempio si potra' capirne il principio di funzionamento.
Per alcune funzionalita' di monitoraggio e' necessario configurare un router mittente e uno ricevente, quest'ultimo in modalita' "IP SLA responder" che replica ai pacchetti di monitoraggio inviati dal primo, in modo da verificare alcuni parametri di circuito. Con il comando "ip sla responder" (oppure "ip sla monitor responder") e' possibile attivare in un secondo router Cisco un risponditore automatico per alcuni tipi di richieste inviate da una configurazione "ip sla" di un primo router. Ad esempio se si utilizza il monitoraggio dell'UDP Jitter (udp-jitter), che poi monitorizza anche il packet-loss e altri parametri, e' necessario un risponditore all'altro capo della tratta da
171
monitorare. Questo e' estremamente utile per la VoIP perche' la qualita' del link e' fondamentale per la qualita' della voce.
Configurazione IP SLA
In questi esempi ci limiteremo a delle configurazioni di base. Una volta capito il principio di funzionamento sara' possibile preparare delle configurazioni piu' specifiche. Supponiamo di voler monitorare la raggiungibilita' di un indirizzo IP. La configurazione di uno SLA viene effettuata con il comando "ip sla {NUM}". Dopo aver preparato il proprio SLA lo si attiva utilizzando il comando "ip sla schedule". Una volta attivo non e' piu' modificabile. Se necessario dovremo cancellarlo dalla configurazione e ricrearlo.
giarout(config)#ip sla 12 giarout(config-ip-sla)#? IP SLAs entry configuration commands: dhcp DHCP Operation dns DNS Query Operation ethernet Ethernet Operations exit Exit Operation Configuration frame-relay Frame-relay Operation ftp FTP Operation http HTTP Operation icmp-echo ICMP Echo Operation icmp-jitter ICMP Jitter Operation mpls MPLS Operation path-echo Path Discovered ICMP Echo Operation path-jitter Path Discovered ICMP Jitter Operation tcp-connect TCP Connect Operation udp-echo UDP Echo Operation udp-jitter UDP Jitter Operation voip Voice Over IP Operation
172
Per verificare le possibilita' di monitoraggio offerte dall'IOS del nostro router possiamo aiutarci con l'help di linea del CLI, mostrato in figura 105.
Supponiamo di voler creare uno SLA che monitorizzi un circuito utilizzando il protocollo ICMP in modo da accertarsi che non si superi un tempo di latenza pari a 20ms. Il test viene effettuato ogni 70 secondi. Nel caso in cui per 5 volte consecutive il test fallisce si genera una trap e si genera un trigger. Quest'ultimo ha la funzione di fare partire un secondo sla, il numero "11" che non e' riportato nell'esempio e che effettuera' dei test aggiuntivi.
ip sla 10 icmp-echo 192.168.30.1 source-interface GigabitEthernet0/1 timeout 20 frequency 70 ip sla schedule 10 life forever start-time now ip sla reaction-configuration 10 react connectionLoss thresholdtype consecutive 5 action-type trapAndTrigger ip sla reaction-trigger 10 11
Con il comando "show ip sla configuration {NUM}" possiamo verificare che la configurazione dello sla 10 sia stata recepita correttamente. In figura 107 e mostrata la corretta sintassi e loutput.
Verifiche SLA
Relativamente all'esempio del paragrafo preceden173
te vediamo come verificare che lo SLA sia correttamente funzionante e come vederne le statistiche. Come si vede dalla figura 108 dal momento dell'avvio ci sono stati 4 successi e 1 fallimento, non sufficiente per linvio di una trap e all'avvio dello sla "11".
giatisc#show ip sla configuration 10 IP SLAs, Infrastructure Engine-II. Entry number: 10 Owner: Tag: Type of operation to perform: icmp-echo Target address/Source interface: 192.168.30.1/GigabitEthernet0/1 Type Of Service parameter: 0x0 Request size (ARR data portion): 28 Operation timeout (milliseconds): 20 Verify data: No Vrf Name: Schedule: Operation frequency (seconds): 70 (not considered if randomly scheduled) Next Scheduled Start Time: Start Time already passed Group Scheduled : FALSE Randomly Scheduled : FALSE Life (seconds): Forever Entry Ageout (seconds): never Recurring (Starting Everyday): FALSE Status of entry (SNMP RowStatus): Active Threshold (milliseconds): 5000 (not considered if react RTT is configured) Distribution Statistics: Number of statistic hours kept: 2 Number of statistic distribution buckets kept: 1 Statistic distribution interval (milliseconds): 20 History Statistics: Number of history Lives kept: 0 Number of history Buckets kept: 15 History Filter Type: None Enhanced History:
174
giarout#show ip sla statistics 10 IPSLAs Latest Operation Statistics IPSLA operation id: 10 Type of operation: icmp-echo Latest RTT: 1 milliseconds Latest operation start time: *14:18:43.961 UTC Fri Sep 11 2009 Latest operation return code: OK Number of successes: 4 Number of failures: 1 Operation time to live: Forever
Figura 108 - Comando 'show ip sla statistics' giarout#show ip sla reaction-configuration 10 Entry number: 10 Index: 1 Reaction: connectionLoss Threshold Type: Consecutive Threshold CountX: 5 Threshold CountY: 5 Action Type: Trap and trigger Figura 109 - Comando 'show ip sla reactionconfiguration' giarout#show ip sla reactiontrigger 10 Entry number: 10 Target Entry Number: 11 Status of Entry (SNMP RowStatus): active Operational State: pending Figura 110 - Comando 'show ip sla reactiontrigger'
la
raggiungibilita'
di
un
ip sla 10 ftp get ftp://anonymous:test@@ftp.gianrico.com/ mode active ip sla schedule 10 life forever start-time now
Il comando "track"
Il comando "track" ha lo scopo di monitorare un evento e di permettere delle azioni in conseguenza al suo verificarsi. Il comando track viene utilizzato per molti scopi, ad esempio per monitorare lo stato delle interfacce di un router. Questo comando viene utilizzato anche con il comando "ip sla". Questo permette delle azioni diverse rispetto la generazione di trap oppure trigger in conseguenza al superamento di un threshold. Il comando e' "track ip sla". In alcune versioni di IOS potrebbe essere assente e in tal caso va utilizzata la variante "track rtr". Questo comando e' stato introdotto con IOS 12.3(4)T.
possiamo monitorare lo stato di una interfaccia ATM e cancellare una riga di routing nel caso in cui la linea vada nello stato di protocollo down.
track 10 interface ATM0/3/0 line-protocol delay down 5 up 30 ip route 18.181.0.0 255.255.255.0 dialer0 track 10 (1)
Allora se il protocollo dell'interfaccia ATM0/3/0 permane nello stato di down per oltre 5 secondi la riga di routing (1) viene tolta dalla tabella di routing. Con i comandi "show track" e "show track brief" si puo' verificare lo stato del monitoring:
gia-gw#show track 10 Track 10 Interface ATM0/3/0 line-protocol Line protocol is Up 1 change, last change 00:06:27 Delay up 30 secs, down 5 secs Tracked by: STATIC-IP-ROUTING 0 gia-gw#show track brief Track Object 10 interface ATM0/3/0
Parameter line-protocol
Con il comando "debug track" e' possibile fare il debugging degli eventi. Ecco cosa succede quando il protocollo dell'interfaccia Atm0/3/0 passa nello stato di down:
177
000917: Jan 20 16:09:43.646: Track: 10 Down change delayed for 5 secs 000923: Jan 20 16:09:48.646: Track: 10 Down change delay expired 000924: Jan 20 16:09:48.646: Track: 10 Change #4 interface ATM0/3/0, line-protocol Up->Down
Con il comando "show ip route track-table" si possono vedere quali sono le righe di routing monitorate:
gia-gw#show ip route track-table ip route 18.181.0.0 255.255.255.0 Dialer1 track 10 state is [up] Figura 116 - Comando 'show ip route track-table'
178
Anche se dal punto di vista pratico la configurazione puo' non avere molta utilita', dal punto di vista didattico e' molto interessante. Se il sito web http://www.itesys.it diventa irraggiungibile la riga di routing (2) viene tolta dalla tabella di routing. Con il comando "debug ip sla trace" si puo' vedere lo stato del tracking come si vede in figura 118.
Nel caso in cui la risoluzione del nome fallisca, oppure l'indirizzo IP del sito web diventa irraggiungibile, oppure il server web non risponde alla porta 80 per il sito web indicato, la riga di routing (2) viene tolta dalla tabella di routing.
179
*Jan 20 16:26:16.532: IP SLAs(10) saaSchedulerEventWakeup *Jan 20 16:26:16.532: IP SLAs(10) operation *Jan 20 16:26:16.532: IP SLAs(10) http operation *Jan 20 16:26:16.532: IP SLAs(10) dns operation *Jan 20 16:26:16.532: IP SLAs(10) - www.itesys.it *Jan 20 16:26:16.532: IP SLAs(10) server - 151.99.125.2 *Jan 20 16:26:16.532: IP SLAs(10) target queried = www.itesys.it *Jan 20 16:26:16.616: IP SLAs(10) return code - no error *Jan 20 16:26:16.616: IP SLAs(10) Address 82.85.14.71 *Jan 20 16:26:16.616: IP SLAs(10) *Jan 20 16:26:16.760: IP SLAs(10) 18 bytes *Jan 20 16:26:16.760: IP SLAs(10) connection - connected *Jan 20 16:26:16.948: IP SLAs(10) first byte: 328 ms *Jan 20 16:26:16.948: IP SLAs(10) *Jan 20 16:26:16.948: IP SLAs(10)
Scheduler: Scheduler: Starting an http operation: Starting dns operation: Starting dns operation: Query name dns operation: Query name dns operation: actual dns operation: Query dns operation: received IP dns operation: RTT=87 http operation: Sent 18 of http operation: Wait http operation: Time to http operation: RTT=417 Scheduler: Updating result
E' possibile creare delle condizioni complesse con l'uso di 'and' oppure 'or'. Vediamone un esempio.
track 111 list boolean and object 10 object 20
Nota 1: spesso i server web rispondono comunque nel caso in cui un singolo sito web non sia funzionante, magari con messaggi del tipo "service
180
unavailable". In questo caso per il router il sito web e' funzionante. Quindi il comando "http get" dell'esempio e' valido per verificare la raggiungibilita' TCP/IP di un server ma non per monitorare lo stato di un singolo sito web che va oltre lo scopo dei servizi di IOS; Nota 2: nelle prime versioni di IOS si puo' trovare il comando "rtr" invece di "ip sla". Nelle versioni dalla 12.4(20)T si ha "track ... sla..." invece di "track ... rtr..." Nota 3: Nel caso si utilizzi il 'track' con 'rtr' osservate che non si puo' utilizzare la parola chiave "consecutive" in "ip sla react configuration". Per fare in modo tale da scatenare un evento dopo un certo numero di ping falliti bisogna utilizzare un "delay" sufficientemente lungo in 'track' e dei valori di 'frequency' e 'timeout' sufficientemente corti in 'ip sla' in modo da farci cadere il numero di pacchetti icmp voluti.
dinamico oppure, appunto, righe statiche con AD maggiore. Il limite nell'utilizzo di questo sistema sta nel fatto che in molte tipologie di guasto le interfacce rimangono nello stato di UP senza pero' consentire il transito di dati, ma vedremo nei paragrafi successivi come si puo' trovare rimedio a casi del genere. In tali casi le righe di route rimangono in RIB. In tutti questi casi questa forma di backup non funziona. Segue in figura un esempio di floating static route.
ip route 0.0.0.0 0.0.0.0 192.168.10.1 ip route 0.0.0.0 0.0.0.0 192.168.20.2 200
In circostanze normali la route di default utilizzata e' la prima e il gateway della rete e' l'indirizzo IP 192.168.10.1. Se questa riga di routing viene cancellata dalla RIB, cosa che avviene solo nei casi sopra indicati, il gateway della rete diventera' l'indirizzo IP 192.168.10.2. Se affianchiamo le floating route con il track e l'ip sla possiamo realizzare interessanti configurazioni di backup dove piuttosto che monitorare lo stato di una interfaccia monitoriamo l'effettiva sua capacita' di trasmettere dati. E' possibile utilizzare il comando track insieme alle floating static route per potenziarne il funzionamento, come in figura.
ip route 0.0.0.0 0.0.0.0 192.168.10.1 track 223 ip route 0.0.0.0 0.0.0.0 192.168.20.2 200
Anche in tale caso vale quanto detto in precedenza tuttavia le interfacce di tipo dialer hanno una particolarita' che e' bene conoscere. Puo' accadere che entrambe le righe di routing restino in tabella di routing anche se una delle due interfacce non e' funzionante. Questo perche' le interfacce dialer sono sempre nello stato di "up". Semmai l'interfaccia "virtual-access" associata puo' essere presente o meno a seconda dello stato del circuito associato.
Se una riga di routing relativa ad un collegamento non funzionante rimane nella tabella di routing questo determina una perdita di pacchetti. Nel caso di un bilanciamento di carico per destinazione circa il 50% delle connessioni fallira'.
183
La soluzione e' abbastanza articolata e implica l'utilizzo del comando "track" che crea un processo di monitoraggio automatico in grado di determinare se una interfaccia dialer e' funzionante o meno. Se e' vero che una interfaccia dialer puo' restare sempre nello stato di UP e' anche vero che, se un collamento ADSL non funziona l'interfaccia sara' sprovvista di indirizzo IP in quando il provider ISP non e' stato in grado di assegnarlo. Questo e' vero quando l'IP viene assegnato dal provider tramite negoziazione del PPP.
track 10 interface Dialer0 ip routing delay down 5 up 30 track 11 interface Dialer1 ip routing delay down 5 up 30 ip route 0.0.0.0 0.0.0.0 Dialer0 track 10 ip route 0.0.0.0 0.0.0.0 Dialer1 track 11 ! 1841-gia#sh track 10 Track 10 Interface Dialer0 IP routing IP routing is Down (no IP addr) 1 change, last change 00:43:32 Delay up 30 secs, down 5 secs Tracked by: STATIC-IP-ROUTING 0 1841-gia# 1841-gia#sh track 11 Track 11 Interface Dialer1 IP routing IP routing is Up 1 change, last change 00:43:33 Delay up 30 secs, down 5 secs Tracked by: STATIC-IP-ROUTING 0
184
Il comando "track", come abbiamo gia visto, e' in grado di monitorare alcuni stati di un router. Interessa nel nostro caso perche' e' in grado di verificare la capacita' di una interfaccia di fare "routing", il che e' possibile solo se questo ha un indirizzo IP. Questo tracciamento si aggancia alle righe di routing statico che verranno eliminate temporaneamente in caso di mancanza dei prerequisiti imposti. Nell'esempio in figura 123, nel caso in cui l'interfaccia dialer non sia in grado di fare routing per piu' di 5 secondi, viene eliminata la statica in corrispondenza del track. Se l'interfaccia torna attiva dopo un disservizio si attenderanno 30 secondi per essere sicuri che non si tratti di un flap. Con il comando "show track" si puo' verificare il processo di monitoraggio.
In questo esempio monitoriamo non lo stato del protocollo in una interfaccia (up o down), ma il fatto che questa abbia o meno un indirizzo IP. Nel caso delle interfacce di tipo dialer, con negoziazione PPP dell'indirizzo IP, l'assenza di indirizzo IP implica assenza di collegamento.
185
Nell'esempio si verifica la raggiungibilita' ICMP dell'indirizzo IP 18.87.179.129, attraverso l'interfaccia outside. Il monitoraggio e' continuo "life forever" ed e' attivo sempre. Nel caso di mancanza di raggiungibilita' il comando track elimina la prima riga di default che attiva, secondo le regole del floating static route, la seconda. Con i comandi "show ip sla statistics", "show ip sla configuration", "show track" e' possibile monitorare lo stato.
Cisco EEM - Embedded Event Manager Cisco IOS Embedded Event Manager (EEM) e' un potente modulo software di Cisco IOS che permette di automatizzare azioni in un router IOS scrivendo delle policy. Per policy si intendono degli applet
186
oppure degli script realizzati utilizzando il linguaggio di programmazione TCL. Puo' essere immaginato come un'evoluzione di "IP SLA" ma in realta' lo affianca e le due funzionalita' si possono integrare. Infatti gli eventi generati dagli IP SLA possono integrarsi con EEM. EEM e' disponibile nella versione 1.0 a partire dalla versione di IOS 12.0(26)S ma e' consigliabile utilizzarlo a partire da IOS 12.3(4)T.
Event Manager permette di generare script di monitoraggio utilizzando gli stessi comandi di IOS oppure utilizzando il linguaggio TCL. L'introduzione di un linguaggio di programmazione come il TCL consente una grande flessibilita'.
EEM evolve di continuo e nel momento della stesura di questo libro e' alla versione 3.2 presente a partire da IOS 12.4(22)T. EEM 3.1 e' stato introdotto con IOS 15.0 con TCL 8.3.4. A seconda della release presente in IOS, che partono dalla 1.0, le funzionalita' a disposizione possono variare. Con il "Cisco Feature Navigator", strumento disponibile nel sito web Cisco, e' possibile consultare le funzionalita' a disposizione in ogni release di IOS.
Per conoscere quale versione di EEM e' attiva in IOS potete utilizzare il comando "show event manager version" come in figura 125. Osservate la presenza di una lista di processi chiamati event detectors. La lista, riportata parzialmente in figura, rappresenta l'elenco di processi che monitorizzano lo stato di IOS.
187
giahouse#show event manager version Embedded Event Manager Version 3.20 Component Versions: eem: (v320_rel1)1.0.1 eem-gold: (v320_rel1)1.0.0 eem-call-home: (v320_rel1)1.0.0 Event Detectors: Name Version Node Type application 01.00 node0/0 RP neighbor-discovery 01.00 node0/0 RP ... routing 02.00 node0/0 RP syslog 01.00 node0/0 RP cli 01.00 node0/0 RP track 01.00 node0/0 RP ...
Con le nuove release di EEM vengono introdotti spesso nuovi event detectors che permettono un monitoraggio sempre piu' completo. Per fare un esempio il 'syslog' event detector, mostrato in figura, intercetta tutti i messaggi syslog di sistema. Per interderci quelli che vengono mostrati in console. E' allora possibile da uno script EEM agire in corrispondenza di determinati messaggi di syslog. Lo stesso discorso si puo' ripetere per gli altri event detector. Ad esempio l'event detector "cli" permette di pubblicare un evento che esegue dei comandi CLI, permettendo di modificare la configurazione del router.
Notiamo anche la presenza dell'event detector "track". Questo e' molto interessante perche' e' legato al monitoraggio del processo associato ai comandi "track". Poter intercettare gli eventi
188
generati dal comando track ci permette di estenderne le potenzialita' come vedremo in un esempio nei paragrafi successivi.
189
Nel sorprendente script in figura il sistema monitorizza i messaggi di log della console di un router. Se compare esattamente il messaggio indicato tra doppi apici si attiva un trigger da cui conseguono delle azioni (action). Questo determina l'esecuzione in sequenza di tre comandi IOS con lo scopo di attivare un collegamento di backup e quindi di inviare una email per segnalare all'amministratore di sistema l'evento (EEM V.2.1 per l'invio di email da applet). L'esempio e' veramente illuminante. Infatti potete osservare come il router si riconfiguri in autonomia in conseguenza ad un evento. E' una possibilita' considerevole. Le righe sono numerate. Questo ci consente in un secondo tempo di aggiungere righe, ma fate molta attenzione che le righe sono interpretate alfabeticamente ovvero 10.0 viene prima di 2.0!!!
Con il comando "event manager run" si possono testare gli applet. Tuttavia prima bisogna accertarsi che vi sia la riga "event none" nell'applet altrimenti si ottiene il messaggio di errore in figura.
giahouse#event manager run mioapplet EEM policy mioapplet not registered with event none Event Detector
Nel caso il cui l'applet utilizzi l'event syslog si puo' inviare manualmente un messaggio al syslog in modo da forzare l'avvio dello script in modo da testarlo. Per fare questo da console utilizzare il
190
comando "send log {messaggio}". In questo modo possiamo testare rapidamente gli applet. Per stabilire se un applet e' stato eseguito si puo' utilizzare il comando "show event manager history events" che mostra lo storico dell' esecuzione degli applet. Affinche' una policy sia utilizzabile questa dev'essere registrata. Per gli applet la registrazione e' automatica a differenza di quanto avviene per gli script TCL. Per vedere quali policy sono registrate si utilizza il comando "show event manager policy registered". Nel caso dell'applet dell'esempio precedente si avra' l'output mostrato in figura.
giahouse#show event manager policy registered No. Class Type Event Trap Time Registered Secu Name 1 applet user syslog Off Thu Dec 30 15:01:09 none mioapplet pattern {Interface Ethernet 0/0, changed status to down} maxrun 20.000 action 1.0 cli command "enable" action 1.1 cli command "conf t" action 1.2 cli command "ip route 10.10.10.0 255.255.255.0 1.1.1.1" action 1.3 mail server "192.85.14.73" to "gianrico@gianrico.com" from "cisco" subject "interfaccia down" body "attivato routing secondario"
Presentiamo un nuovo esempio in cui mostriamo come gestire un evento relativo alla irraggiungibilita' di un indirizzo IP tramite ICMP. Questo esempio e' molto utile perche' ci mostra come integrare le funzionalita' di "IP SLA" con "EEM". Nell'evento rileviamo l'irraggiungibilita' di un host utilizzando le funzionalita' offerta da "IP SLA". In caso di irraggiungibilita' EEM intercetta l'evento e con un applet cambia la route di default. Questo puo' essere estremamente utile per rilevare
191
correttamente un disservizio in un collegamento Internet quindi questo applet e' di larga applicabilita'.
catalyst_backup#sh run | section sla ip sla 10 icmp-echo 10.11.12.13 source-ip 10.11.12.14 timeout 100 ip sla schedule 10 life forever start-time now track 1 ip sla 10 reachability catalyst_backup#sh run | section event event manager applet pingfailed event track 1 state down <-- 12.4(2)T in IOS almeno action 1.0 syslog msg " ping failed " action 1.1 cli command "enable" action 1.2 cli command "conf term" action 2.0 cli command "ip route 0.0.0.0 0.0.0.0 192.168.30.145" action 3.0 cli command "exit"
Combinando le funzionalita' di EEM, SLA e TRACK possiamo anche creare eventi combinati tramite funzioni booleane. Nell'esempio in figura 130 monitoriamo due collegamenti e solo nel caso in cui entrambi siano DOWN si genera un evento.
Si noti che "AND" e "OR" del comando track sono booleani. Ovvero ci vuole un OR per generare un evento nel caso di contemporaneita' di due FAIL. Una conseguenza di quanto spiegato e' che possiamo anche riavviare il router in caso di eventi straordinari. La sintassi e' nell'esempio che segue.
192
catalyst_backup#show run | section sla ip sla 10 icmp-echo 10.11.12.13 source-ip 10.11.12.14 timeout 100 frequency 20 ip sla schedule 10 life forever start-time now ip sla 11 icmp-echo 10.11.12.17 source-ip 10.11.12.18 timeout 100 frequency 20 ip sla schedule 11 life forever start-time now track 111 ip sla 10 reachability track 112 ip sla 11 reachability catalyst_backup#show run | section track track 1 list boolean or object 111 object 112 track 111 ip sla 10 reachability track 112 ip sla 11 reachability catalyst_backup#show run | section event event manager applet pingfailed event track 1 state down <-- 12.4(2)T IOS almeno action 1.0 syslog msg " ping failed " action 1.1 cli command "enable" action 1.2 cli command "conf term" action 2.0 cli command "ip route 0.0.0.0 0.0.0.0 19.16.4.1" action 4.0 cli command "exit" action 5.0 mail server "112.112.12.12" to "gianrico@prova.it" from "catalyst@gia.it" subject "corpo del messaggio" body "messaggio di test" catalyst_backup#show track 1 Track 1 List boolean and Boolean OR is Up 6 changes, last change 00:10:14 object 111 Up object 112 Up Tracked by: EEM applet pingfailed catalyst_backup#
193
event manager applet ErroreGrave event track 100 state down action 1.0 syslog msg "Errore grave, riavvio il router" action 2.0 reload
Nell'esempio che segue consideriamo una configurazione piu' complessa e quindi piu' istruttiva con la presenza di due circuiti HDSL che
194
vogliamo in load-balancing con NAT e un terzo circuito ADSL. L'ADSL viene utilizzato per traffico di tipo VoIP, riconosciuta in base all'ip mittente. Se l'ADSL non funziona vogliamo che la VoIP passi automaticamente sull'HDSL. Viceversa se le HDSL vanno entrambe giu' i dati devono transitare sull'ADSL. Possiamo supporre che i collegamenti siano anche con operatori differenti.
... ip sla 3 icmp-echo AA.AA.AA.AA source-interface Serial0/0/0.1 timeout 1500 frequency 8 ip sla schedule 3 life forever start-time now ! ip sla 4 icmp-echo BB.BB.BB.BB source-interface Serial0/0/1.1 timeout 1500 frequency 8 ip sla schedule 4 life forever start-time now ! track 10 rtr 3 reachability delay down 40 up 40 ! track 11 rtr 4 reachability delay down 40 up 40 ! track 12 interface Dialer1 IP routing delay down 5 up 30 ! track 13 list boolean or object 10 object 11 ! ! ! interface FastEthernet0/0 description --- interfaccia clienti con load-sharing --ip address FF.FF.FF.FF 255.255.255.0 ip policy route-map private ip nat inside ! interface FastEthernet0/1 description --- interfaccia server e clienti con nat statico --IP address EE.EE.EE.EE 255.255.255.0 IP policy route-map private
195
! interface Serial0/0/0 no IP address encapsulation frame-relay IETF load-interval 30 frame-relay lmi-type cisco ! interface Serial0/0/0.1 point-to-point bandwidth 2048 IP address CC.CC.CC.CC 255.255.255.252 IP nat outside frame-relay interface-dlci 50 IETF ! interface Serial0/0/1 no IP address encapsulation frame-relay IETF load-interval 30 frame-relay lmi-type cisco ! interface Serial0/0/1.1 point-to-point bandwidth 2048 IP address DD.DD.DD.DD 255.255.255.252 IP nat outside frame-relay interface-dlci 50 IETF ! interface ATM0/1/0 no IP address IP nat outside no atm ilmi-keepalive dsl operating-mode auto pvc 8/35 encapsulation aal5mux ppp dialer dialer pool-member 1 ! ! interface Dialer1 ip address negotiated ip nat outside ... configurazione ADSL... ! ip route 0.0.0.0 0.0.0.0 Serial0/0/0.1 track 10 ip route 0.0.0.0 0.0.0.0 Serial0/0/1.1 track 11 ip route 0.0.0.0 0.0.0.0 Dialer1 200 ! ip nat inside source route-map adslvoip interface Dialer1 overload ip nat inside source route-map uscitaload1 interface Serial0/0/0.1 overload ip nat inside source route-map uscitaload2 interface Serial0/0/1.1 overload ! access-list 10 remark -- classi di IP sorgenti per HDSL --
196
access-list 10 permit ... access-list 20 remark -- classi di IP sorgenti per ADSL -access-list 20 permit ... access-list 30 remark -- classi di IP sorgenti per secondo HDSL-access-list 30 permit ... !!! Con queste route map il NAT utilizza i tre collegamenti in base al mittente !route-map adslvoip permit 15 match IP address 20 set interface Dialer1 ! route-map uscitaload2 permit 20 match IP address 102 match interface Serial0/0/1.1 ! route-map uscitaload1 permit 10 match IP address 102 match interface Serial0/0/0.1 !!! Con queste route map i mittenti con IP pubblico escono dalla connettivita' associata route-map private permit 10 description --- HDSL --match IP address 10 set IP next-hop ... ! route-map private permit 20 description --- ADSL --match IP address 20 set interface Dialer1 ! route-map private permit 30 description --- HDSL --match IP address 30 set IP next-hop ... ! ! ... ! ! EEM riconfigura il router in base al disservizio sul circuito segnalato dagli eventi ! generati dal comando track ! event manager applet ADSLDOWN event track 12 state down action 1.0 cli command "enable" action 1.1 cli command "conf t" action 1.5 cli command "no access-list 40" action 1.6 cli command "access-list 102 permit IP ..." action 1.8 cli command "exit" event manager applet ADSLUP event track 12 state up
197
action 1.0 cli command "enable" action 1.1 cli command "conf t" action 1.5 cli command "access-list 40 permit ..." action 1.6 cli command "no access-list 102" action 1.7 cli command "access-list 102 permit IP ..." action 1.8 cli command "access-list 102 permit IP ..." action 1.9 cli command "exit" event manager applet DUEHDSLDOWN event track 13 state down action 1.0 cli command "enable" action 1.1 cli command "conf t" action 1.2 cli command "access-list 40 permit ..." action 1.3 cli command "exit" event manager applet DUEHDSLUP event track 13 state up action 1.0 cli command "enable" action 1.1 cli command "conf t" action 1.5 cli command "no access-list 40" action 1.6 cli command "access-list 40 permit ..." action 1.8 cli command "exit" ! end gianricotest# sh track Track 10 Response Time Reporter 3 reachability Reachability is Up 1 change, last change 04:15:47 Delay up 40 secs, down 40 secs Latest operation return code: OK Latest RTT (millisecs) 19 Tracked by: STATIC-IP-ROUTING 0 Track 11 Response Time Reporter 4 reachability Reachability is Up 7 changes, last change 03:37:58 Delay up 40 secs, down 40 secs Latest operation return code: OK Latest RTT (millisecs) 76 Tracked by: STATIC-IP-ROUTING 0 Track 12 Interface Dialer1 IP routing IP routing is Up 10 changes, last change 06:31:19 Delay up 30 secs, down 5 secs Tracked by: EEM applet ADSLUP EEM applet ADSLDOWN Track 13 List boolean and
198
Boolean OR is Up 8 changes, last change 03:37:58 object 10 Up object 11 Up Tracked by: EEM applet DUEHDSLUP EEM applet DUEHDSLDOWN gianricotest#show IP sla statistics Round Trip Time (RTT) for Index 3 Latest RTT: 15 milliseconds Latest operation start time: *20:25:48.344 UTC Fri Jan 8 2010 Latest operation return code: OK Number of successes: 288 Number of failures: 0 Operation time to live: Forever Round Trip Time (RTT) for Index 4 Latest RTT: 648 milliseconds Latest operation start time: *20:25:45.400 UTC Fri Jan 8 2010 Latest operation return code: OK Number of successes: 290 Number of failures: 0 Operation time to live: Forever gianricotest#show event manager history events No. Time of Event Event Type Name 1 Fri Jan 8 08:45:57 2010 track applet: ADSLDOWN 2 Fri Jan 8 08:47:25 2010 track applet: ADSLUP 3 Fri Jan 8 13:53:37 2010 track applet: ADSLDOWN 4 Fri Jan 8 13:54:28 2010 track applet: ADSLUP 5 Fri Jan 8 16:18:33 2010 track applet: DUEHDSLDOWN 6 Fri Jan 8 16:22:43 2010 track applet: DUEHDSLUP 7 Fri Jan 8 16:24:08 2010 track applet: DUEHDSLDOWN 8 Fri Jan 8 16:28:43 2010 track applet: DUEHDSLUP 9 Fri Jan 8 16:40:03 2010 track applet: DUEHDSLDOWN 10 Fri Jan 8 16:47:48 2010 track applet: DUEHDSLUP gianricotest#show ip route track-table ip route 0.0.0.0 0.0.0.0 Serial0/0/0.1 track 10 state is [up] ip route 0.0.0.0 0.0.0.0 Serial0/0/1.1 track 11 state is [up]
199
Gli script TCL vengono chiamati da Cisco policy. Router diversi con diverse versioni di IOS hanno file di esempio differenti. Quindi cio' che vedrete nelle figure a seguire potrebbe non combaciare con quanto presente nel vostro router. Con il comando "show event manager policy available" possiamo vedere quali policy sono installate nel nostro router.
giahouse#show event manager No. Type Time Created Name 1 system Mon Feb 7 07:28:15 2 system Mon Feb 7 07:28:15 3 system Mon Feb 7 07:28:15 4 system Mon Feb 7 07:28:15 5 system Mon Feb 7 07:28:15 6 system Mon Feb 7 07:28:15 7 system Mon Feb 7 07:28:15 policy available 2036 2036 2036 2036 2036 2036 2036 ap_perf_test_base_cpu.tcl cl_show_eem_tech.tcl no_perf_test_init.tcl sl_intf_down.tcl tm_cli_cmd.tcl tm_crash_reporter.tcl tm_fsys_usage.tcl
200
Senza scendere nel dettaglio, che uscirebbe dagli scopi di questo libro, osserviamo le policy di nome "sl_intf_down.tcl" e "tm_cli_cmd.tcl".
Con il comando "show event manager policy available detailed {nomepolicy}" e' possibile vedere il sorgente TCL di una policy. Cio' che ci puo' interessare sono le righe iniziali che spiegano le funzionalita' dello script e le variabili che utilizza. Come esempio vediamo una parte dello script "tm_cli_cmd.tcl" fornito da Cisco in ogni IOS con EEM. Questo script lo trovate in ogni router con EEM ed e copyright Cisco System.
ite-7204#show event manager policy available detailed tm_cli_cmd.tcl ::cisco::eem::event_register_timer cron name crontimer2 cron_entry $_cron_entry maxrun 240 #---------------------------------# EEM policy that will periodically execute a cli command # and email the results to a user. # # July 2005, Cisco EEM team # # Copyright (c) 2005-2006 by cisco Systems, Inc. # All rights reserved. #---------------------------------### The following EEM environment variables are used: ### ### _cron_entry (mandatory) ### -A CRON specification that determines ### when the policy will run. See the ### IOS Embedded Event Manager ### documentation for more information ### on how to specify a cron entry. ### Example: _cron_entry 0-59/1 0-23/1 * * 0-7 ### ### _log_file (mandatory without _email_....) ### - A filename to append the output to. ### If this variable is defined, the ### output is appended to the specified ### file with a timestamp added. ### Example: _log_file disk0:/my_file.log ### ### _email_server (mandatory without _log_file)
201
### - A Simple Mail Transfer Protocol (SMTP) ### mail server used to send e-mail. ### Example: _email_server mailserver.customer.com ### ### _email_from (mandatory without _log_file) ### - The address from which e-mail is sent. ### Example: _email_from devtest@customer.com ### ### _email_to (mandatory without _log_file) ### - The address to which e-mail is sent. ### Example: _email_to engineering@customer.com ### ### _email_cc (optional)- The address to which the e-mail ### must be copied. ### Example: _email_cc manager@customer.com ### ### _show_cmd (mandatory) - The CLI command to be executed ### when the policy is run. ### Example: _show_cmd show version ### # check if all the env variables we need exist # If any of them doesn't exist, print out an error msg and quit if {![info exists _log_file]} { if {![info exists _email_server]} { set result \ "Policy cannot be run: variable _log_file or _email_server has not been set" error $result $errorInfo } if {![info exists _email_from]} { set result \ "Policy cannot be run: variable _log_file or _email_from has not been set" error $result $errorInfo } if {![info exists _email_to]} { set result \ "Policy cannot be run: variabl _log_file ore _email_to has not been set" error $result $errorInfo } if {![info exists _email_cc]} { #_email_cc is an option, must set to empty string if not set. set _email_cc "" } }if {![info exists _show_cmd]} { set result \ "Policy cannot be run: variable _show_cmd has not been set" error $result $errorInfo
202
} namespace import ::cisco::eem::* namespace import ::cisco::lib::* snip # 1. execute the command if [catch {cli_open} result] { error $result $errorInfo } else { array set cli1 $result } if [catch {cli_exec $cli1(fd) "en"} result] { error $result $errorInfo } # save exact execution time for command set time_now [clock seconds] # execute command if [catch {cli_exec $cli1(fd) $_show_cmd} result] { error $result $errorInfo } else { set cmd_output $result # format output: remove trailing router prompt set prompt [format "(.*\n)(%s)(\\(config\[^\n\]*\\))?(#|>)" $routername] if [regexp "[set prompt]" $result dummy cmd_output] { # do nothing, match will be in $cmd_output } else { # did not match router prompt so use original output set cmd_output $result } } if [catch {cli_close $cli1(fd) $cli1(tty_id)} result] { error $result $errorInfo } # 2. log the success of the CLI command set msg [format "Command \"%s\" executed successfully" $_show_cmd] action_syslog priority info msg $msg if {$_cerrno != 0} { set result [format "component=%s; subsys err=%s; posix err=%s;\n%s" \ $_cerr_sub_num $_cerr_sub_err $_cerr_posix_err $_cerr_str] error $result } # 3. if _log_file is defined, then attach it to the file if {[info exists _log_file]} { # attach output to file if [catch {open $_log_file a+} result] { error $result } set fileD $result # save timestamp of command execution
203
# (Format = 00:53:44 PDT Mon May 02 2005) set time_now [clock format $time_now -format "%T %Z %a %b %d %Y"] puts $fileD "%%% Timestamp = $time_now" puts $fileD $cmd_output close $fileD } # 4. if _email_server is defined send the email out if {[info exists _email_server]} { if {[string match "" $routername]} { error "Host name is not configured" } if [catch {smtp_subst [file join $tcl_library email_template_cmd.tm]} \ result] { error $result $errorInfo } if [catch {smtp_send_email $result} result] { error $result $errorInfo } }
Come si evince dai commenti questa policy esegue periodicamente un comando CLI e ne invia i risultati via email oppure li memorizza in un file. Vediamo come utilizzarla nel caso piu' semplice. Dobbiamo innanzitutto inizializzare le variabili "_cron_entry" e "_log_file" e "_show_cmd" con dei valori.
Per inizializzare le variabili bisogna utilizzare il comando "event manager environment {variabile} {valore}". Con il comando "show event manager environment all" e' possibile verificare i valori di tutte le variabili inserite. Il cron si aspetta nell'ordine: minuti, ore, giorno del mese, mese, giorno della settimana. Per la ripetizione si usa lo "/" e per gli intervalli il "-". Per eseguire lo script ogni 10 minuti inseriamo "*/10 * * * *".
204
Con il comando "event manager policy {nomepolicy} type system" registriamo la policy che cosi' viene resa utilizzabile dal sistema. Se si vuole attivare il debug per EEM utilizzare il comando "debug event manager all".
Nella figura che segue vediamo un esempio di applicazione. Inizializziamo le variabili per far eseguire il comando "show run" e per inserirne l'output nel file di nome "test.log".
GIA-7204(config)#event manager environment _cron_entry */10 * * * * GIA-7204(config)#event manager policy tm_cli_cmd.tcl type system GIA-7204(config)#event manager environment _log_file flash:/test.log giahouse#show event manager environment all No. Name Value 1 _log_file flash:/test.log 2 _ show_cmd show run 3 _cron_entry 0-59/5 * * * 4 ... GIA-7204#show event manager policy registered No. Class Type Event Type Trap Time Registered Secu Name 1 script system timer cron Off Sat Dec 26 13:20:42 2009 none tm_cli_cmd.tcl name {crontimer2} cron entry {*/10 * * * *} nice 0 queue-priority normal maxrun 240.000 scheduler rp_primary
Riprenderemo la spiegazione di questo codice Cisco piu avanti. Presentiamo adesso l'altra policy fornita da Cisco che abbiamo attenzionato. Il suo nome e' sl_intf_down.tcl e monitorizza il syslog di sistema inviando una email nel caso in cui appaia
205
un messaggio che fa match con una espressione regolare indicata dall'utente. E' molto utile per monitorare lo stato delle interfacce.
Le policy che abbiamo visto sinora sono di sistema ovvero fornite da Cisco. Non e' possibile modificare queste policy. Quello che si puo' fare e' duplicarne il contenuto e quindi editarlo a piacimento.
E' ora necessario impostare questa directory per l'uso con TCL con i comandi in figura.
206
giahouse(config)#event manager directory user library flash:/mieitcl giahouse(config)#event manager directory user policy flash:/mieitcl giahouse(config)#exit giahouse#show event manager directory user library flash:/mieitcl giahouse# giahouse#show event manager directory user policy flash:/mieitcl giahouse#
Adesso siamo pronti per la realizzazione dei nostri script TCL. I nomi devono seguire le convenzioni Cisco. Ci limitiamo per adesso alla piu' banale, ovvero tutti i file devono terminare con ".tcl".
Minicorso di TCL
Ovviamente non si puo' apprendere un linguaggio di programmazione in poche pagine. Per imparare il TCL si rimanda ad un testo specifico. Questo paragrafo e' una buona traccia per comprenderne la filosofia e i comandi principali.
Per poter inserire comandi TCL si deve entrare nella shell TCL di IOS. Si entra nella shell comandi TCL con il comando "tclsh". Con il comando "tclsh {pathfile}" si esegue uno script TCL che puo'
207
trovarsi nel router oppure in una location esterna (piu' avanti scenderemo nel dettaglio).
Nella shell tclsh potete inserire direttamente comandi TCL e vederne subito i risultati in quanto si tratta di un linguaggio interpretato. Per eseguire dei programmi completi TCL bisogna scriverli nel proprio PC in quanto Cisco non ha un editor di testo embedded nel sistema operativo. Questo non complica le cose in quanto e' possibile eseguire in tempo reale dei programmi TCL che sono esterni al router e solo successivamente, dopo aver completato tutti i test, e' possibile trasferire all'interno del router. E' possibile selezionare come "pathfile" del comando tclsh una destinazione comprendente una delle seguenti parole chiave: TFTP, FTP, TCP, SCP, HTTP, HTTPS. Per eseguire uno script gia' presente nel router bastera' digitare "tclsh {script.tcl}".
Anche se TCL e' un linguaggio interpretato e' possibile caricare nel router degli script precompilati oppure compilati. Questo e' utile ai fini della sicurezza in quanto un codice precompilato non e' facilmente consultabile. E' disponibile gratuitamente un compilatore TCL presso l'URL http://tclpro. sourceforge.net. Per conoscere quale versione di TCL e' installata e i comandi disponibili si eseguono i comandi nella figura che segue.
208
giahouse(tcl)#info patchlevel 8.3.4 giahouse(tcl)#info commands tell socket subst open eof pwd glob list exec pid snmp_getone time eval lrange tcl_trace fblocked lsearch gets case lappend proc break variable llength return linsert snmp_getid error catch clock info split array if log_user fconfigure concat join lreplace snmp_setany source fcopy global switch snmp_getbulk update close cd for file append format udp_open read package set binary namespace scan verify_signature seek while flush after vwait snmp_getnext typeahead uplevel continue hostname ios_config foreach rename fileevent regexp upvar unset encoding expr load regsub interp history puts incr lindex lsort udp_peek string
una sottostringa oppure determinare la lunghezza. La figura mostra unapplicazione di quanto detto.
giahouse(tcl)#set miavar1 5 5 giahouse(tcl)#set miavar2 6 6 giahouse(tcl)#puts $miavar1 5 giahouse(tcl)#info exists miavar2 1 giahouse(tcl)#puts $miavar1+$miavar2 5+6 giahouse(tcl)#expr {$miavar1+$miavar2} 11 giahouse(tcl)#puts [expr {$miavar1+$miavar2}] 11 giahouse(tcl)#puts "ciao [expr {$miavar1+$miavar2}]" ciao 11 giahouse(tcl)#puts "Prima riga \nSeconda riga" Prima riga Seconda riga giahouse(tcl)#puts "\t Colonna 1 \t Colonna2" Colonna 1 Colonna2 giahouse(tcl)#set b(1) 1 1 giahouse(tcl)#set b(2) 2 2 giahouse(tcl)#set b(3) 3 3 giahouse(tcl)#set miafrase "casa cisco router internet" casa cisco router internet giahouse(tcl)#string match cisco $miafrase 0 giahouse(tcl)#string match *cisco* $miafrase 1 giahouse(tcl)#string match *cisbo* $miafrase 0 giahouse(tcl)#string length $miafrase 26
210
File
La gestione dei file ci permette di creare dei file di testo inserendone o leggendone dei valori. Se non indicato altrimenti questi file vengono inseriti nel device di default che in genere e' "flash:". Un file si crea o si apre con il comando "open" indicando per la lettura la lettera "w" oppure per la scrittura la lettera "r". Con il comando "puts" e' possibile inserire dati e con "close" chiudere il file. Segue un esempio.
giahouse(tcl)#set miofile [ open "filetesto1" w] file0 giahouse(tcl)#puts $miofile "prova" giahouse(tcl)#close $miofile giahouse(tcl)#more flash:/filetesto1 prova giahouse(tcl)#set miofile [ open "filetesto1" r] file0 giahouse(tcl)#gets $miofile prova giahouse(tcl)#close $miofile
Espressioni regolari
Dovete imparare bene l'utilizzo delle espressioni regolari se volete poter scrivere degli script utili con IOS. Infatti molto spesso uno script legge l'output di comandi di IOS alla ricerca di dati o valori ben precisi. Per fare questo si utilizzano le espressioni regolari. Nella figura che segue, assegnata una stringa di testo estraiamo alcuni valori utilizzando delle espressioni regolari. Si utilizza il comando
211
"regexp" che si aspetta in input la stringa su cui effettuare la ricerca e poi una seconda variabile in cui memorizzare il risultato. Il comando restituisce anche l'esito dell'operazione ovvero "1" nel caso in cui abbia trovato un match, "0" in caso contrario. Con ".*" si intende una qualsiasi sequenza di testo o numeri, con "[0-9]" si intende una singola cifra, con "[0-9]+" si intende una sequenza di cifre. Il valore "\" indica di non interpretare il carattere successivo, nel caso in cui si crei un'ambiguita' perche' quel carattere e' anche un comando di espressione regolare.
giahouse(tcl)#set stringa "L'indirizzo IP e' 192.168.30.10" L'indirizzo IP e' 192.168.30.10 giahouse(tcl)#regexp {IP.*} $stringa risultato 1 giahouse(tcl)#puts $risultato IP e' 192.168.30.10 giahouse(tcl)#set ris [regexp {[0-9]} $stringa risultato] 1 giahouse(tcl)#puts $risultato 1 giahouse(tcl)#set ris [regexp {[0-9]+} $stringa risultato] 1 giahouse(tcl)#puts $risultato 192 giahouse(tcl)#regexp {[0-9]+\.[0-9]+} $stringa risultato 1 giahouse(tcl)#puts $risultato 192.168
Gestione errori
Con il comando "catch" possiamo intercettare le condizioni di errore. Nell'esempio che segue
212
inseriamo nella variabile "risultato" la configurazione del router. In una ipotesi di errore questo viene catturato e inserito nella variabile "errore" e quindi puo' essere gestito all'interno del codice.
giahouse(tcl)#if {[ catch {set risultato [exec {show run}]} errore]} { puts "Errore: $e" } Figura 142 - Cisco TCL - Comando 'catch' -
Se invece di "show run" avessimo utilizzato un comando non presente nella versione di IOS in uso oppure un comando improprio avremmo potuto gestire lerrore. Se riprendiamo il file TCL di esempio tm_cli_cmd.tcl presentato nei paragrafi precedenti osserviamo l'utilizzo del comando "catch" ad ogni esecuzione di un comando per intercettare una condizione di errore.
Procedure
Come in tutti i linguaggi di programmazione e' possibile scrivere subroutine da chiamare nel codice. Nella figura 143 si mostra una subroutine che effettua la moltiplicazione di due valori numerici e ne restituisce il risultato.
213
gia(tcl)#proc moltiplica {var1 var2} { +>set x [expr {$var1 * $var2}] +>return $x +>} gia(tcl)#puts [moltiplica 2 3] 6
Cicli
Anche in questo caso si tratta dei soliti comandi presenti praticamente in ogni linguaggio di programmazione, con una sintassi differente, ovvero gli onnipresenti "for" e "while".
gian(tcl)#for {set i 0 } { $i <= 20} {incr i} {puts $i} 0 ... 20 gian(tcl)#set x 10 gian(tcl)#while {$x < 12} { +>puts $x +>set x [expr {$x +1}] +>} 10 11
Espressioni condizionali
Si utilizzano i comandi "if...then...else" e "switch" come in figura.
214
gia(tcl)#if {$x == 2} {puts "vale 2"} else {puts "vale 3"} gia(tcl)#if {$x != 3} {puts "ciao"} gia(tcl)#set x 20 gia(tcl)#switch $x { +>"10" { puts "dieci"} +>"20" { puts "venti"} +>}
215
Socket
Sappiamo che i socket sono degli endpoint per la comunicazione IP. Un socket e' caratterizzato da un indirizzo IP e da una porta e permette la comunicazione tra dispositivi presenti in una rete TCP/IP. Qualunque programma del nostro PC che si interfaccia con la rete IP utilizza i socket: browser http, client di posta, client FTP, programmi di comunicazione come quelli di chat, voip, peer-topeer.
Ogni linguaggio di programmazione che si rispetti permette di creare socket per permettere la comunicazione tra processi presenti nello stesso o in PC differenti tramite il protocollo IP.
Molti virus, trojan e quant'altro utilizzano i socket. Un socket infatti apre il nostro PC a comunicazioni verso l'esterno, e questo e' sconosciuto ai comuni utenti Internet. E' possibile utilizzare i socket in TCL? La risposta e' affermativa. Lo strumento e' potente e pericoloso. Roba tosta, ragazzi.
Per capire come funzionano i socket, e divertirci allo stesso tempo, creiamo una backdoor nel nostro router, utile come accesso di emergenza.
216
set port 1212 set miosock [socket -server miaprocedura $port] vwait attendi
Il comando "socket" ci permette di creare un endpoint TCP per la comunicazione. La sintassi "socket -server {funzione} {porta}" apre il socket in ascolto nella porta indicata con 'porta', ovvero crea il lato server di una comunicazione con uno o piu' client. Nel caso dell'esempio non appena arrivera' una richiesta di connessione alla porta 1212 verra' automaticamente chiamata la funzione 'miaprocedura'. Dopo l'apertura di un socket viene rilasciato dal sistema un identificativo, inserito nell'esempio nella variabile "miosock" con il quale e' possibile fare riferimento al socket durante tutto lo script TCL.
Con il comando "vwait {var}" lo script entra in attesa finche' la variabile 'var' non viene modificata. Senza quest'ultima condizione lo script non e' in grado di restare in ascolto. Entrare in attesa e' fondamentale per poter accettare in qualsiasi momento richieste esterne. Sara' nostra responsabilita' utilizzare la variabile al momento giusto, cosi' da arrivare alla successiva istruzione 'close' dell'esempio, altrimenti il server restera' in attesa indefinitivamente, cosa che pero' e' normalmente voluta.
217
Andiamo dunque alla seconda parte dell'esempio ovvero la scrittura della procedura da avviare al momento in cui arriva una richiesta di connessione dall'esterno.
# Occhio ai commenti, che siano a inizio riga # Occhio alle parentesi graffe, devono essere aperte nella # stessa riga dei comandi proc miaprocedura {sock indirizzoip porta} { puts "Debug: Connessione $sock accettata dall'indirizzo $indirizzoip con porta $porta" fconfigure $sock -buffering line -translation lf # # Invia un messaggio al client # puts $sock "----------------------------" puts $sock "|- GF backdoor - welcome |" puts $sock "----------------------------" puts $sock "gf>" # # Legge un messaggio dal client # fileevent $sock readable [list leggi $sock] }
Non appena arriva una richiesta esterna il sistema chiama automaticamente la procedura 'miaprocedura' e questo perche' l'abbiamo istruito il tal modo con il comando 'socket'. Per facilitare la fase di test dello script, visualizziamo sullo schermo un messaggio indicante l'avvenuta connessione da parte di un client. A questo punto introduciamo il comando "fconfigure {socket}{parametri}" che ci permette di impostare delle opzioni per un socket. Con '-buffering line' richiediamo una trasmissione separata per ogni linea che termina con un newline. Detto in altri termini ogni comando 'puts' del nostro esempio inviera' un suo pachetto
218
IP. Con '-translation lf' definiamo il tipo di 'newline'. In questo caso con 'lf' intendiamo lo stile UNIX di utilizzare un singolo carattere di newline.
Con il comando "fileevent {sock} readable | writable {script}" il sistema aggancia uno script ad un canale (tecnicamente file event handler). Quando e' possibile leggere o scrivere dal canale il sistema utilizza lo script indicato. Nel nostro esempio non appena il client invia qualche dato lo script chiama automaticamente la procedura 'leggi' passando come parametro l'identificativo del socket. L'uso del comando "list" che crea una lista, ovvero una sequenza di dati, serve per poter inviare a fileevent il nome della procedura con tutti i suoi parametri correttamente. A questo punto ci manca semplicemente la procedura per gestire le richieste del client.
proc leggi {sock} { if {[eof $sock] || [catch {gets $sock richiesta}]} { # situazione di anormalita', ad esempio caduta connessione puts "Debug: condizione di errore nella lettura dal client rilevata" } else { set risposta[exec "$richiesta"] puts $sock "gf>" puts $sock $response # Termina la comunicazione con un client # L'applicazione TCL rimane in ascolto # Togliere questa riga per fare piu' interrogazioni al router in una unica # connessione close $sock } }
219
E' necessario attenzionare la posizione delle parentesi graffe negli script. Come avrete forse notato dai commenti le parentesi graffe vanno aperte o chiuse nella stessa riga in cui si trova il comando associato. Intendo dire che "} else {" non e' la stessa cosa di "else" con le parentesi in righe differenti. Per lo stesso motivo si scrive "if {condizione} {" e non "if {condizione}" e la parentesi nella riga successiva. A questo punto vorremmo lanciare questo script e testarlo. Lo script e' in grado di leggere messaggi da una porta TCP. Cosa possiamo utilizzare come client? Utilizzeremo il comando "nc" presente nei sistemi Linux e che consente di inviare facilmente dei dati tramite connessioni TCP/IP.
Da una postazione client Linux colleghiamoci al router tramite la backdoor creata. Quindi lanciamo un comando.
220
[root@radius1 ~]# nc 192.168.50.12 1213 ---------------------------|- GF backdoor - welcome | ---------------------------gf> show ver | include Cisco gf> Cisco IOS Software, C870 Software (C870-ADVIPSERVICESK9-M), Version 15.1(3)T, RELEASE SOFTWARE (fc1) Copyright (c) 1986-2010 by Cisco Systems, Inc. use. Delivery of Cisco cryptographic products does not imply A summary of U.S. laws governing Cisco cryptographic products may be found at: Cisco 877 (MPC8272) processor (revision 2.0) with 236544K/25600K bytes of memory.
Figura 151 - Cisco TCL: Utilizzo comando linux 'nc' per connessione a server
Come si vede e' possibile inviare comandi al router e vedere le risposte senza utilizzare il telnet o l'ssh. Vediamo cosa succede se proviamo a cambiare la configurazione del router tramite la backdoor.
[root@radius1 CISCO_TCL]# nc XX.XX.XX.XX 1213 ---------------------------|- GF backdoor - welcome | ---------------------------gf> conf t gf> % Configuration not allowed from TCL shell.Use 'ios_config' instead
L'esempio e' molto istruttivo anche perche' ci introduce a nuovi tipi di minacce alla sicurezza dei nostri router che prima non esistevano. E' abbastanza ovvio che per inserire lo script TCL nel router bisogna averne prima l'accesso quindi non e' il caso di allarmarsi eccessivamente.
Questo esempio ci suggerisce la possibilita per uno script TCL in esecuzione in un router di collegarsi in telnet ad un secondo router e di interagire con esso con una sequenza di comandi. Allora in caso di condizioni di guasto possiamo non solo modificare la configurazione di un router A ma anche router esterni B e C che NON hanno supporto TCL.
giahouse#tclsh ftp://itesys:it7jhd@192.168.14.70/CISCO_TCL/giaserver.tcl Loading CISCO_TCL/giaserver.tcl ! [OK - 1060/4096 bytes] sock0_ipv4 couldn't open socket: address already in use while executing "socket -server miaprocedura $port" invoked from within "set miosock [socket -server miaprocedura $port]" (file "ftp://itesys:itesys75@82.85.14.70/CISCO_TCL/giaserver.tcl " line 33) giahouse#show processes cpu | include Tcl 48 44 21 2095 0.00% 0.00% 0.00% 2 Tcl Serv tty2 giahouse#show users Line User Host(s) Idle Location 2 vty 0 gianrico idle 00:16:23 69it.itesys.it * 3 vty 1 gianrico idle 00:00:00 69it.itesys.it Interface User Mode Idle Peer Address
Tornando al nostro esempio si puo' osservare che lo script lanciato da TCL sembra instoppabile. Di fatto rimane in esecuzione. Se si apre una nuova sessione con il router ecco mostrato in figura 153 come individuare gli script che sono in esecuzione in background.
Secondo la documentazione con il comando "clear line" si dovrebbe poter stoppare l'esecuzione di uno script. Infatti non esiste un comando "kill" per fermare i processi come in Linux. Nel mio caso ho dovuto riavviare il router tuttavia e' probabile che dipenda dalla versione di IOS utilizzata nel lab. Approfitto per segnalare che gli esempi riportati in questo libro sono legati a specifiche versioni di IOS la cui evoluzione e' continua. Inoltre vi sono un numero notevole di versioni differenti ognuna con dei bug noti (basta cercare nel sito cisco le keyword 'caveats release' per rendersene conto). Quindi per far funzionare tutto bene nel vostro sistema potrebbero essere necessari degli aggiustamenti.
Namespace
I namespace sono collezioni di procedure e variabili. Sono un po' come le classi in C. Permettono di creare librerie di funzioni che non interferiscono con il codice esterno al namespace. Per esempio le variabili in un namespace possono avere lo stesso nome di altre variabili in altri namespace. Con il simbolo "::" ci si muove all'interno di una gerarchia di namespace. La root
223
della gerarchia e' il simbolo "::" posto inizialmente. Sotto il namespace globale c'e' definito il namespace "cisco" che ha a sua volta il namespace "eem" al suo interno.
Lo script tm_cli_cmd.tcl
Riprendiamo lo script di esempio mostrato nei paragrafi precedenti per capire altri elementi di TCL specifici ai router Cisco. - La prima riga di uno script TCL e' di fondamentale importanza in quanto definisce quando lo script verra' eseguito. E' possibile schedulare l'esecuzione di uno script un po' come si fa in ambiente linux utilizzando 'crontab'.
Questa indica che lo script deve essere eseguito periodicamente in base a quanto indicato nella variabile _cront_entry che e' prescelta dall'utente. Con "maxrun" si indica al sistema il tempo massimo che puo' impiegare lo script per la sua esecuzione.
224
- Le righe successive normalmente verificano che l'utente abbia inserito un valore per le variabili richieste.
if![info exists _email_server]} { set result \ "Policy cannot be run: variable _email_server has not been set" error $result $errorInfo }
Figura 156 - TCL: verifica esistenza variabili set par1 [lindex $argv 0] # primo parametro set par2 [lindex $argv 1] # secondo parametro
- A questo punto incontriamo l'import dei namespace relativi a eem e lib. Questo permette allo script di utilizzare le procedure presenti in questi namespace. Si veda nel sito Cisco l'elenco completo. Nel prossimo script di esempio ne e' presentata qualcuna.
225
namespace import ::cisco::eem::* namespace import ::cisco::lib::* Figura 158 - TCL: 'namespace'
Ecco in figura un'applicazione che utilizza gli ultimi concetti descritti. E derivata direttamente da uno degli esempi messi a disposizione da Cisco con TCL in IOS.
:cisco::eem::event_register_none #'event_register_none': # questa policy viene eseguita da cli quando si esegue il comando 'event manager run' # - Si puo' anche usare 'event_register_timer' # per indicare ogni quanto la policy dev'essere eseguita # - Si puo' anche usare 'event_register_syslog' # per fare eseguire la policy se nel syslog appare un # certo messaggio per un certo numero di volte # - Si puo' anche usare 'event_register_ipsla' # per fare eseguire la policy in conseguenza ad un # trigger 'ip sla' # - Si puo' anche usare 'event_register_inteface' # per far eseguire la policy se un contatore # in una interfaccia # supera una certa soglia # - Si puo' anche usare 'event_register_rpc' # per far eseguire la query da un sistema remoto tramite # SSH. # Vedi anche perl 'CISCO::EEM::RPC' # Vedi anche nel sito Cisco cerca "Writing Embedded Event Manager Policies Using Tcl". # # Questo script esegue una sequenza di comandi e ne # memorizza l'output in un file # namespace import ::cisco::eem::* namespace import ::cisco::lib::* # # Nella procedura che segue vedete delle funzioni presenti # nel namespace. In particolare'cli_open' apre una vty, # cioe' una shell che e' poi importante chiudere # 'cli_close' alla fine del codice. # Fallisce se non ci sono almeno 3 vty libere proc CLICmdProc {cmds} {
226
if [catch {cli_open} result] { error $result $errorInfo } else { array set cli1 $result } # Si esegue il comando 'enable' if [catch {cli_exec $cli1(fd) "enable"} result] { error $result $errorInfo } # 'cli_exec' e' una procedura Cisco che esegue un comando # di shell .Notate il comando "term lenght 0" che # disabilita la space bar per far # avanzare l'output dei comandi su schermo if [catch {cli_exec $cli1(fd) "term len 0"} result] { error $result $errorInfo } # In questo ciclo si eseguono i comandi richiesti foreach comando $cmds { if [catch {cli_exec $cli1(fd) $comando} result] { error $result $errorInfo } else { lappend risultato $result } } if [catch {cli_close $cli1(fd) $cli1(tty_id)} result] { error $result $errorInfo } # Restituisce una lista contenente l'output return $risultato } # MAIN - INIZIO ESECUZIONE # # Creiamo una lista di comandi: 'lappend' aggiunge un # elemento ad una lista lappend clicmd "show ip int brief" lappend clicmd "show tech" lappend clicmd "show clock" # Si eseguono i comandi. 'cliout' contiene il valore # restituito dalla procedura set cliout [CLICmdProc $clicmd] # Il valore di 'cliout' viene memorizzato in un file set idfile [open "fileuscita" w+] foreach outs $cliout { puts $idfile $outs } close $idfile