====== Exercice pratique ====== ===== Le problème ===== Soit un switch (toujours le même), configuré [[310lansecure:10_vlans:30_vlan3#choix_vlan_suivant_l_authentification|comme indiqué ici]], c'est-à-dire que nous disposons d'un serveur RADIUS, avec une base de données qui recense toutes les adresses MAC de nos stations. Nous avons enrichi cette base en ajoutant le nom des stations. Par ailleurs, nous avons une table qui relie le numéro de chaque port du switch au numéro de la prise dans l'établissement. Nous voulons utiliser SNMP pour savoir quels sont les clients actuellement en service et sur quelle prise (i.e. sur quel port de switch) ils sont branchés. SNMP va peut-être nous permettre de savoir quelles adresses MAC sont présentes sur chaque port du switch (habituellement une seule), si notre ''BRIDGE-MIB'' définit cette information et indique où la trouver. C'est dans ces cas qu'un browser de MIB montre son utilité. Même ''tkmib'' sera plus agréable à manipuler que ''snmptranslate''. Notons un outil en ligne digne d'intérêt, le « [[http://www.simpleweb.org/ietf/mibs/|SimpleWeb]] » dans la rubrique MIBs en ce qui nous concerne dans le cas de figure. Nous trouvons la ''BRIDGE-MIB'', développons son arborescence, et voyons que : BRIDGE-MIB::dot1dTpFdbAddress dot1dTpFdbAddress OBJECT-TYPE -- FROM BRIDGE-MIB -- TEXTUAL CONVENTION MacAddress SYNTAX OCTET STRING (6) DISPLAY-HINT "1x:" MAX-ACCESS read-only STATUS current DESCRIPTION "A unicast MAC address for which the bridge has forwarding and/or filtering information." ::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) dot1dBridge(17) dot1dTp(4) dot1dTpFdbTable(3) dot1dTpFdbEntry(1) 1 } et que BRIDGE-MIB::dot1dTpFdbPort dot1dTpFdbPort OBJECT-TYPE -- FROM BRIDGE-MIB SYNTAX Integer32 MAX-ACCESS read-only STATUS current DESCRIPTION "Either the value '0', or the port number of the port on which a frame having a source address equal to the value of the corresponding instance of dot1dTpFdbAddress has been seen. A value of '0' indicates that the port number has not been learned, but that the bridge does have some forwarding/filtering information about this address (e.g., in the dot1dStaticTable). Implementors are encouraged to assign the port value to this object whenever it is learned, even for addresses for which the corresponding value of dot1dTpFdbStatus is not learned(3)." ::= { iso(1) org(3) dod(6) internet(1) mgmt(2) mib-2(1) dot1dBridge(17) dot1dTp(4) dot1dTpFdbTable(3) dot1dTpFdbEntry(1) 2 } Ces deux OIDs sont susceptibles de nous intéresser. Comme par ce beau jour d'été (nous sommes le 13 juillet 2010), il n'y a pas grand monde dans l'établissement, nous allons réaliser la manipulation sur le switch de tête, celui qui connecte tous les serveurs et les autres switches, le seul qui voit du monde à cette date. ===== Que dit SNMP ? ===== ==== dot1dTpFdbAddress ====
~$ snmpbulkwalk -Ob -m  +/var/lib/mibs/ietf/BRIDGE-MIB -c public -v 2c 172.16.252.4  BRIDGE-MIB::dot1dTpFdbAddress
L'option ''-Ob'', comme dit le man : //Display table indexes numerically, rather than trying to interpret the instance subidentifiers as string or OID values.//
BRIDGE-MIB::dot1dTpFdbAddress.0.5.93.8.128.1 = STRING: 0:5:5d:8:80:1
BRIDGE-MIB::dot1dTpFdbAddress.0.19.212.88.167.204 = STRING: 0:13:d4:58:a7:cc
BRIDGE-MIB::dot1dTpFdbAddress.0.23.8.228.70.128 = STRING: 0:17:8:e4:46:80
BRIDGE-MIB::dot1dTpFdbAddress.0.23.8.228.70.206 = STRING: 0:17:8:e4:46:ce
BRIDGE-MIB::dot1dTpFdbAddress.0.23.8.228.197.0 = STRING: 0:17:8:e4:c5:0
BRIDGE-MIB::dot1dTpFdbAddress.0.23.8.228.197.79 = STRING: 0:17:8:e4:c5:4f
BRIDGE-MIB::dot1dTpFdbAddress.0.33.145.133.119.77 = STRING: 0:21:91:85:77:4d
BRIDGE-MIB::dot1dTpFdbAddress.0.35.84.55.145.46 = STRING: 0:23:54:37:91:2e
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.84.111 = STRING: 0:30:84:3a:54:6f
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.85.170 = STRING: 0:30:84:3a:55:aa
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.97.178 = STRING: 0:30:84:3a:61:b2
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.99.185 = STRING: 0:30:84:3a:63:b9
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.99.199 = STRING: 0:30:84:3a:63:c7
BRIDGE-MIB::dot1dTpFdbAddress.0.48.132.58.100.151 = STRING: 0:30:84:3a:64:97
BRIDGE-MIB::dot1dTpFdbAddress.144.230.186.157.115.173 = STRING: 90:e6:ba:9d:73:ad
Nous pouvons aussi le faire de façon totalement numérique :
~$ snmpbulkwalk -On -m  +/var/lib/mibs/ietf/BRIDGE-MIB -c public -v 2c 172.16.252.4  BRIDGE-MIB::dot1dTpFdbAddress
.1.3.6.1.2.1.17.4.3.1.1.0.5.93.8.128.1 = STRING: 0:5:5d:8:80:1
.1.3.6.1.2.1.17.4.3.1.1.0.19.212.88.167.204 = STRING: 0:13:d4:58:a7:cc
.1.3.6.1.2.1.17.4.3.1.1.0.23.8.228.70.128 = STRING: 0:17:8:e4:46:80
.1.3.6.1.2.1.17.4.3.1.1.0.23.8.228.70.206 = STRING: 0:17:8:e4:46:ce
.1.3.6.1.2.1.17.4.3.1.1.0.23.8.228.197.0 = STRING: 0:17:8:e4:c5:0
.1.3.6.1.2.1.17.4.3.1.1.0.23.8.228.197.79 = STRING: 0:17:8:e4:c5:4f
.1.3.6.1.2.1.17.4.3.1.1.0.33.145.133.119.77 = STRING: 0:21:91:85:77:4d
.1.3.6.1.2.1.17.4.3.1.1.0.35.84.55.145.46 = STRING: 0:23:54:37:91:2e
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.84.111 = STRING: 0:30:84:3a:54:6f
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.85.170 = STRING: 0:30:84:3a:55:aa
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.97.178 = STRING: 0:30:84:3a:61:b2
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.99.185 = STRING: 0:30:84:3a:63:b9
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.99.199 = STRING: 0:30:84:3a:63:c7
.1.3.6.1.2.1.17.4.3.1.1.0.48.132.58.100.151 = STRING: 0:30:84:3a:64:97
.1.3.6.1.2.1.17.4.3.1.1.144.230.186.157.115.173 = STRING: 90:e6:ba:9d:73:ad
Dans un cas comme dans l'autre, nous avons une table (''.1.3.6.1.2.1.17.4.3.1.1.'') qui contient toutes les adresses MAC que notre switch a vu passer. Ceci ne répond pas à notre question, mais nous apprend tout de même quelque chose, si l'on y regarde de plus près : les clés (surlignées eh jaune), ne sont rien d'autre que l'expression en base 10 de l'adresse MAC correspondante. Cette remarque va nous servir dans la suite immédiate. ==== dot1dTpFdbPort ==== Intéressons nous à la table suivante dont le descriptif dit clairement : \\ //Either the value '0', or the port number of the port on which a frame having a source address equal to the value of the corresponding instance of dot1dTpFdbAddress has been seen.// ~$ snmpbulkwalk -On -m +/var/lib/mibs/ietf/BRIDGE-MIB -c public -v 2c 172.16.252.4 BRIDGE-MIB::dot1dTpFdbPort
.1.3.6.1.2.1.17.4.3.1.2.0.5.93.8.128.1 = INTEGER: 11
.1.3.6.1.2.1.17.4.3.1.2.0.19.212.88.167.204 = INTEGER: 5
.1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.70.128 = INTEGER: 23
.1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.70.206 = INTEGER: 23
.1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.197.0 = INTEGER: 24
.1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.197.79 = INTEGER: 24
.1.3.6.1.2.1.17.4.3.1.2.0.33.145.133.119.77 = INTEGER: 0
.1.3.6.1.2.1.17.4.3.1.2.0.35.84.55.145.46 = INTEGER: 6
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.84.111 = INTEGER: 8
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.85.170 = INTEGER: 9
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.97.178 = INTEGER: 10
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.99.185 = INTEGER: 4
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.99.199 = INTEGER: 3
.1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.100.151 = INTEGER: 7
.1.3.6.1.2.1.17.4.3.1.2.144.230.186.157.115.173 = INTEGER: 13
Cette table contient des numéros de ports, les clés correspondantes n'étant rien d'autre que l'expression décimale de l'adresse MAC vue sur ce port. D'ailleurs :
~$ snmpbulkwalk -OX -m  +/var/lib/mibs/ietf/BRIDGE-MIB -c public -v 2c 172.16.252.4  BRIDGE-MIB::dot1dTpFdbPort
L'option ''-OX'' comme le dit tout à fait clairement le ''man'' : \\ //Display table indexes in a more "program like" output, imitating a traditional array-style index format//. \\ Ceci permet d'afficher le résultat de façon bien lisible :
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:5:5d:8:80:1] = INTEGER: 11
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:13:d4:58:a7:cc] = INTEGER: 5
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:17:8:e4:46:80] = INTEGER: 23
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:17:8:e4:46:ce] = INTEGER: 23
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:17:8:e4:c5:0] = INTEGER: 24
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:17:8:e4:c5:4f] = INTEGER: 24
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:21:91:85:77:4d] = INTEGER: 0
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:23:54:37:91:2e] = INTEGER: 6
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:54:6f] = INTEGER: 8
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:55:aa] = INTEGER: 9
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:61:b2] = INTEGER: 10
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:63:b9] = INTEGER: 4
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:63:c7] = INTEGER: 3
BRIDGE-MIB::dot1dTpFdbPort[STRING: 0:30:84:3a:64:97] = INTEGER: 7
BRIDGE-MIB::dot1dTpFdbPort[STRING: 90:e6:ba:9d:73:ad] = INTEGER: 13
Nous avons ici exactement l'information que nous recherchons, à savoir une table dont les clés sont les adresses MAC et les données le numéro du port qui les a vues. En regardant bien ce tableau, nous constatons deux choses : == Le port 0 == Le port 0 représente une adresse MAC connue du switch, mais ne venant pas d'un port réel. En l'occurrence, il s'agit d'une adresse MAC du switch lui-même : ~# arp -an ... ? (172.16.252.4) at 00:21:91:85:77:4d [ether] on eth0 ... == Les ports 23 et 24 == Ces ports sont référencés deux fois dans la table. C'est possible et même normal qu'un même port puisse voir plusieurs adresses MAC, si un autre switch est connecté dessus, ce qui est le cas ici. ===== Un début de solution ===== Compte tenu du problème énoncé, il est clair qu'il va falloir coder un outil qui réponde à la question. Pas question de le réaliser ici, mais nous allons amorcer la solution en utilisant PHP, juste pour montrer que l'on peut utiliser facilement un langage quelconque, du moment qu'il propose une bibliothèque pour gérer SNMP, ce qui est le cas de PHP. Cette bibliothèque est rudimentaire, mais suffisante pour nos besoins. Nous avons bien dans un coin une Debian qui traine, avec Apache, MySQL et PHP, sur le serveur RADIUS par exemple. Nous allons juste réaliser une fonction qui remplira un tableau avec ce que nous dit SNMP. Il faut bien entendu installer le module ''php5-snmp'' pour réaliser l'opération. ==== Premier essai ==== Grâce par exemple au [[http://www.simpleweb.org/ietf/mibs/modules/html/?category=IETF&module=BRIDGE-MIB|SimpleWeb]], nous savons que l'OID de la table qui nous intéresse est ''.1.3.6.1.2.1.17.4.3.1.2''. Voici un premier code php (>= 5.2) : $value) { $macAddr = $key; $port = $value; echo $port.' == '.$macAddr.'
'; } ?>
La fonction ''snmp_set_oid_output_format''permet d'indiquer comment nous désirons traiter les OIDs. Ici, sous forme numérique. ''SNMP_OID_OUTPUT_NUMERIC'' est une constante pré-définie dans le module ''php5-snmp''. La fonction ''snmprealwalk'' est un genre de ''snmpwalk'' où l'on indique comme paramètres : * la cible (adresse IP ou nom DNS) ; * la communauté ; * l'OID d'où l'on veut partir. Ceci va nous donner le résultat suivant : INTEGER: 11 == .1.3.6.1.2.1.17.4.3.1.2.0.5.93.8.128.1 INTEGER: 5 == .1.3.6.1.2.1.17.4.3.1.2.0.19.212.88.167.204 INTEGER: 23 == .1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.70.128 INTEGER: 23 == .1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.70.206 INTEGER: 24 == .1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.197.0 INTEGER: 24 == .1.3.6.1.2.1.17.4.3.1.2.0.23.8.228.197.79 INTEGER: 0 == .1.3.6.1.2.1.17.4.3.1.2.0.33.145.133.119.77 INTEGER: 6 == .1.3.6.1.2.1.17.4.3.1.2.0.35.84.55.145.46 INTEGER: 8 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.84.111 INTEGER: 9 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.85.170 INTEGER: 10 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.97.178 INTEGER: 4 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.99.185 INTEGER: 3 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.99.199 INTEGER: 7 == .1.3.6.1.2.1.17.4.3.1.2.0.48.132.58.100.151 INTEGER: 13 == .1.3.6.1.2.1.17.4.3.1.2.144.230.186.157.115.173 Pas bien exploitable. Il nous faut nettoyer un peu : * les clés (OIDs) en enlevant l'OID de la table (''.1.3.6.1.2.1.17.4.3.1.2.'') ; * les données en enlevant le type (''INTEGER: '') ; ==== Deuxième essai ==== Comme ceci n'est pas un cours de PHP, voici juste une façon de faire, parmi tant d'autres. [[http://fr.php.net/manual/fr/ref.strings.php|La documentation officielle de PHP]] vous sera éventuellement d'un grand secours. $value) { $macAddr = substr($key,strlen($OID)+1) ; $port = explode(': ',$value,2); echo $port[1].' == '.$macAddr.'
'; } ?>
Ceci nous donne déjà quelque chose de plus exploitable : 11 == 0.5.93.8.128.1 5 == 0.19.212.88.167.204 23 == 0.23.8.228.70.128 23 == 0.23.8.228.70.206 24 == 0.23.8.228.197.0 24 == 0.23.8.228.197.79 0 == 0.33.145.133.119.77 6 == 0.35.84.55.145.46 8 == 0.48.132.58.84.111 9 == 0.48.132.58.85.170 10 == 0.48.132.58.97.178 4 == 0.48.132.58.99.185 3 == 0.48.132.58.99.199 7 == 0.48.132.58.100.151 13 == 144.230.186.157.115.173 Cependant : * les adresses MAC (les clés) ne sont pas dans un format exploitable (représentation hexadécimale) ; * les ports (les données) sont sous forme de texte, si bien qu'un tri se fera suivant le code ASCII, ce qui n'a pas de sens ici. Nous allons donc créer un second tableau, avec des clés (adresses MAC) dans le format utilisé sur FreeRADIUS et des données de type entier. ==== Troisième essai ==== === L'adresse MAC === Créons une fonction dans laquelle nous injecterons l'adresse sous sa forme décimale, et qui nous renverra une chaine sous forme hexadécimale, avec le délimiteur '':'' : function parseMacAddr($T) { $tok = strtok($T,"."); while ($tok !== false) { $oct=dechex($tok); settype($oct,"string"); if (strlen($oct)==1) $oct = "0".$oct; $macAddr = $macAddr.$oct.":"; $tok = strtok("."); } $macAddr = substr($macAddr,0,strlen($macAddr)-1); return $macAddr; } La fonction ''strtok'' n'est pas d'un usage bien intuitif, mais pour ceux qui ne la connaissent pas, la documentation donne des exemples clairs. === La solution définitive(?) === Finalement, le code suivant : $value) { $macAddr = parseMacAddr(substr($key,strlen($OID)+1)); $port = explode(': ',$value,2); settype($port[1],'integer'); $Y[$macAddr]=$port[1]; } unset($X); asort($Y); foreach($Y as $key => $value) { echo $value.' == '.$key.'
'; } ?>
donne quelque chose d'acceptable : 0 == 00:21:91:85:77:4d 3 == 00:30:84:3a:63:c7 4 == 00:30:84:3a:63:b9 5 == 00:13:d4:58:a7:cc 6 == 00:23:54:37:91:2e 7 == 00:30:84:3a:64:97 8 == 00:30:84:3a:54:6f 9 == 00:30:84:3a:55:aa 10 == 00:30:84:3a:61:b2 11 == 00:05:5d:08:80:01 13 == 90:e6:ba:9d:73:ad 23 == 00:17:08:e4:46:80 23 == 00:17:08:e4:46:ce 24 == 00:17:08:e4:c5:4f 24 == 00:17:08:e4:c5:00 Notre tableau ''Y'' contient les informations issues du switch dans un format que l'on pourra aisément utiliser dans des requêtes sur notre base MySQL. ===== Pour finir ===== Beaucoup d'autres informations intéressantes peuvent être obtenues par SNMP. Toujours sur nos switchs, il peut être utile de visualiser le trafic sur certains ports pour surveiller par exemples des goulots d'étranglement. SNMP couplé à des outils comme Cacti ou Zabbix permettra alors d'obtenir de jolis graphes et d'en conserver l'historique.