Rebol et le protocole SNMP

Ce papier concerne l’implémentation du protocole SNMPv1 en REBOL, vous y trouverez une explication succincte de ce protocole, et quelques exemples de son utilisation.

Télécharger les programmes décrits dans cet article.


 

1 – Le protocole SNMP

SNMP est l’acronyme de Simple Network Management Protocol, il a été défini (1988) pour permettre d’administrer des appareils connectés à un réseau. Avec son aide vous pouvez identifier les différents composants d’un appareil ( interfaces réseau, logiciels installés, tables de routage, … ), obtenir des mesures des différents flux passant par l’appareil ( nombre de trames, nombre de paquets IP entrant, sortant, fragmentés, …), changer les paramètres de fonctionnement, …

Les RFC définissent actuellement 3 versions du protocole SNMP :

SNMPV1

Se distingue des autres par son manque de sécurité.

Comprends les messages GET, GETNEXT, RESPONSE, TRAP

SNMPv2

Implémente 3 messages supplémentaires : GETBULKREQUEST, INFORMREQUEST, SNMPv2TRAP. Non pris en comptes dans cette version REBOL.

Apporte un peu plus de sécurité, en proposant des authentifications.

(non supporté dans cette version).

SNMPv3

Nouvelle norme qui n’est pas encore utilisée à ma connaissance.

Le protocole SNMP est situé dans le modèle OSI dans la couche application, et sur la dernière couche du modèle TCP/IP. Il existe des développement pour les principaux types de réseaux ( TCP/IP, APPLETALK, NETWARE, …), l’implémentation REBOL ne concerne que le réseau TCP/IP, et utilise le protocole UDP.

On distingue plusieurs types de composants dans ce protocole :

Chaque appareil appartient à une communauté (community) identifiée par son nom (par défaut : public), ce nom fournit un niveau de sécurité très basique puisque vous avez besoin de le connaître pour communiquer avec l’appareil mais qu’il circule en clair sur le réseau. D’expérience, beaucoup d’administrateurs laissent le nom par défaut…

2 – MIBs

Les agents fournissent l’accès à leurs variables ( nommés aussi objets, bien que cela n’est pas trop de rapports avec la programmation objet.)., ces variables sont décrites dans une structure de données nommée MIB (pour Management Information Base).

Une MIB s’organise en un structure arborescente, et obéit à un sous-ensemble de la syntaxe ASN.1.

Chaque variable est identifiée par son chemin d’accès dans l’arborescence.

Par exemple, la variable SysDescr dans system(1) qui fournit une description de l’appareil est identifiée par :

Iso.Identified-organization.dod.internet.mgmt.mib2.system.sysdescr

Soit: 1.3.6.1.2.1.1.1.0.

Chacune de ces variables a un type, simple (ASN.1) ou composé ( cf. RFC1155) :

 

Type SNMP

Correspondance REBOL

OBJECT IDENTIFIER : par exemple 1.3.6.1.2.1.1.1.0

String ! (impossible de le traiter comme un tuple ! parce qu’ils dépassent la longueur maximale.)

INTEGER

Integer !

OCTET STRING: chaîne de caractères

String !

NETWORK ADRESS : suite d’octets

String! à décoder

IP ADDRESS: 127.0.0.1

Tuple !

COUNTER: entier positif qui s’incrémente jusqu’à la valeur maximale de 2^32-1 (4294967295) avant de repasser à zéro.

Integer!

GAUGE : entier positif qui peut croître ou décroître avec une valeur maximum de 4294967295.

Integer !

TIMETICKS : entier positif qui représente une durée en centièmes de secondes depuis une une certaine époque.

String ! de la forme "  X day(s) hh :mm :ss ", il n’est pas impossible pour que je me décide finalement pour le type Time!

OPAQUE : permet de passer un type arbitraire en ASN.1

String ! le décodage de cette chaîne.

 

3 – Messages SNMP

Ne seront aborder ici que les messages actuellement implémentés en REBOL, cela concerne notamment les messages TRAP qui n’utilisent pas une procédure envoi – réception mais juste une tâche d’écoute en boucle.

Les 3 différents messages actuellement pris en compte par cette version en REBOL sont : GET, SET, GETNEXT. Les urls correspondantes sont formées de la façon suivante :

Snmp://<communauté>@<adresse IP de l’agent>/<GET | SET | GETNEXT>/<liste d’object identifier>

3-1 message GET

Le message GET permet de retrouver la valeur d’une variable, si vous voulez retrouver la valeur de la variable Iso.Identified-organization.dod.internet.mgmt.mib2.system.sysdescr

Vous pouvez utiliser les syntaxes suivantes :

p: open snmp://public@127.0.0.1/GET/

insert p "1.3.6.1.2.1.1.1.0"

rep1: copy p

close p

Ou plus simplement:

rep1: read snmp://public@127.0.0.1/GET/1.3.6.1.2.1.1.1.0

L’objet retourné a la forme suivante :

>> source rep1

make object! [

version: 0

community: "public"

request-id: 80742

error-status: 0

error-message: "NoError"

error-index: 0

values: [["1.3.6.1.2.1.1.1.0" {Hardware: x86 Family 5 Model 8 Stepping 12 AT/AT COMPATIBLE - Software:

Windows 2000 Version 5.0 (Build 2195 Uniprocessor Free)}]]

]

 

A partir de là, il est assez simple de construire un scanner SNMP qui détectera tous les matériels SNMP de votre réseau :

do %snmp-protocol.r

;fixe le time out à une petite valeur pour ne pas attendre trop longtemps le résultat

system/schemes/default/timeout: 0.1

;adresse de depart du scan.

adr: 172.16.1.1

while [ adr <> 172.16.1.254 ] [

error? try [

rep: read to-url rejoin [ "snmp://" adr "/GET/1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.5.0" ]

print [ "A " adr " trouvé: " second first rep/values " : " second second rep/values ]

]

adr: adr + 0.0.0.1

]

3-2 message SET

<non encore testé>

Ajoutez dans l’url, derrière chaque object-identifier la valeur de la variable que vous voulez positionner.

3-3 message GETNEXT

Le message GETNEXT permet de parcourir une table. Nous allons par exemple lire une table de routage identifiée par Iso.Identified-organization.dod.internet.mgmt.mib2.ip.ipRoutingTable

Soit 1.3.6.1.2.1.4.21, qui identifie une table (SEQUENCE OF en syntaxe ASN.1) de ipRouteEntry(1).

IpRouteEntry étant elle-même une variable composée de plusieurs champs :

 

;lire ipRouteTable

rep5: read snmp://public@127.0.0.1/GETnext/1.3.6.1.2.1.4.21.1.1

;variable qui permet d’identifier la fin de la table

tmp1: copy "1.3.6.1.2.1.4.21.1"

until [

rep5: read join snmp://public@127.0.0.1/getnext/ first first rep5/values

print [ first first rep5/values " --> " second first rep5/values ]

tmp: copy/part first first rep5/values length? tmp1

;Tant que l’object identifier change continuer

res: (tmp <> tmp1)

tmp1: copy tmp

res

]

résultat :

Les différents destination de routes :

1.3.6.1.2.1.4.21.1.1.172.16.10.0 --> 172.16.10.0

1.3.6.1.2.1.4.21.1.1.172.16.10.1 --> 172.16.10.1

1.3.6.1.2.1.4.21.1.1.172.16.255.255 --> 172.16.255.255

1.3.6.1.2.1.4.21.1.1.224.0.0.0 --> 224.0.0.0

1.3.6.1.2.1.4.21.1.1.255.255.255.255 --> 255.255.255.255

Les différentes métriques :

1.3.6.1.2.1.4.21.1.3.127.0.0.0 --> 1

1.3.6.1.2.1.4.21.1.3.172.16.10.0 --> 1

1.3.6.1.2.1.4.21.1.3.172.16.10.1 --> 1

1.3.6.1.2.1.4.21.1.3.172.16.255.255 --> 1

1.3.6.1.2.1.4.21.1.3.224.0.0.0 --> 1

1.3.6.1.2.1.4.21.1.3.255.255.255.255 --> 1

Les différentes adresses de passerelles :

1.3.6.1.2.1.4.21.1.7.127.0.0.0 --> 127.0.0.1

1.3.6.1.2.1.4.21.1.7.172.16.10.0 --> 172.16.10.1

1.3.6.1.2.1.4.21.1.7.172.16.10.1 --> 127.0.0.1

1.3.6.1.2.1.4.21.1.7.172.16.255.255 --> 172.16.10.1

1.3.6.1.2.1.4.21.1.7.224.0.0.0 --> 172.16.10.1

1.3.6.1.2.1.4.21.1.7.255.255.255.255 --> 172.16.10.1

4 – Un exemple complet 

Voici un petit exemple permettant d’afficher sous forme graphique n’importe quelle variable SNMP du type GAUGE ou COUNTER.

1.3.3.1.2.1.4.10.0 correspond au compteur de paquets IP en sortie de l’appareil

Le principe est simple, nous allons lire de manière répétée la valeur d’un compteur, garder les 15 dernières valeurs et les afficher de manière circulaire pour donner l’impression d’un mouvement à l’écran.

REBOL []

do %snmp-protocol.r

;Une première fonction qui va construire la liste circulaire

;sous la forme d’un objet

make-snmp-data: function [

"Return a circular list"

community [string!]

ip-adr [tuple!]

counter-id [string!]

size [integer!] "how many data we will memorize"

/counter "counter type is snmp counter (snmp gauge is default value)"

][

][

;Ne pas attendre trop longtemps une réponse du réseau

system/schemes/default/timeout: 0.1

;Une classe utilitaire qui va être sous-classée un peu plus bas

;la liste circulaire est implémentée sous la forme d’un tableau

;muni d’un pointeur qui marque l’entrée courante de la liste

; il y a certainement un manière plus élégante de faire ça en REBOL

circular-list: make object! [

data: copy []

size: 0

current: 1

;fixe la taille du tableau

set-size: function [ new-size ] [temp-data] [

either new-size = 0 [

data: none

size: 0

current: 0

][

size: new-size

temp-data: array/initial new-size 0

change temp-data copy/part data new-size

current: 1

data: copy temp-data

]

]

;déplace le pointeur

inc: does [

current: current + 1

if current > size [ current: 1 ]

]

;ajoute un valeur à l’emplacement du pointeur

addv: func [ element ] [

if size > 0 [

poke data current element

]

]

;retourne la valeur à la position du pointeur

getv: function [][element][

element: none

if size > 0 [ element: pick data current ]

return element

]

;retourne la valeur à la position précédente du pointeur

get-prec: function [] [element pos] [

element: 0

if size > 0 [

pos: current - 1

if pos < 1 [

pos: length? data

element: pick data pos

]

]

return element

]

]

;l’objet proprement dit que nous allons retourner

; sous classe circular-list

snmp-counter-list: make circular-list [

;construit l’url d’accés au compteur

url: to-url rejoin [ "snmp://" community "@" ip-adr "/GET/" counter-id ]

;pour le type snmp COUNTER qui s’incrémente tout le temps

;si nous voulons afficher un nombre par unité de temps

;il faut prendre non pas la valeur du compteur mais la

,différence entre la dernière valeur et la valeur courante

last-value: -1

;Met à jour le compteur

;la valeur 4294967295 qui apparaît un peu partout

;est le limite maximum que peut atteindre un compteur SNMP

update-counter: does [

use [ snmp-data current-val ] [

snmp-data: read url

if snmp-data/error-status = 0 [

current-val: second first snmp-data/values

either counter [

;skip first value

if last-value = -1 [ last-value: current-val return ]

if current-val < last-value [ current-val: current-val + (4294967295 - last-value) ]

addv (current-val - last-value)

last-value: current-val

inc

][ ; snmp gauge type

addv current-val

inc

]

]

]

]

;renvoie un bloque avec les 15 dernières valeurs du compteur

;dont la première est la position courante.

;on coupe le bloc en deux au niveau de la position courante

;et on inverse les 2 parties. Ce qui donneras l’illusion d’un déplacement.

draw-block: does [

return join (skip data current) (copy/part data current)

]

]

;initialisation de l’objet compteur snmp

snmp-counter-list/set-size size

snmp-counter-list/update-counter

return snmp-counter-list

]

 

;Construction de l’interface

view layout [

do [

;initialisation du compteur

snmp-counter: make-snmp-data/counter "public" 127.0.0.1 "1.3.6.1.2.1.4.10.0" 15

;preparation d’un bloc effect pour le dessin du graphique

fx: copy/deep [ grid 20x10 draw [] ]

;obtention d’une reference sur le bloc draw pour ce faciliter la tâche

counter-line: fourth fx

state: off

]

;rien de bien special, on construit simplement l’interface

across

vh2 "SNMP monitor" gold return

text 80 "ip:" ip: field "127.0.0.1" return

text 80 "community:" community: field "public" return

text 80 "counter id:" counter-id: field "1.3.6.1.2.1.4.10.0" return

name: text 315 255.255.255 0.0.0 "not identified" center return

descr: text 315 255.255.255 0.0.0 "not identified" center return

text "rate:" rate-txt: field "0:0:1" return

;affiche la valeur maximale du compteur pour les 15 dernières valeurs

max-text: text 30 "0" right

;box pour l’affichage du graphique

counter-box: box 285x110 ivory rate 0:0:5 effect fx return

;ce bouton va nous permettre d’afficher une description du système

;sur le que réside l’agent SNMP interrogé

button "get name" [

use [snmp-name][

if error? try [

snmp-name: read to-url rejoin ["snmp://" community/text "@" ip/text "/GET/1.3.6.1.2.1.1.5.0 1.3.6.1.2.1.1.1.0"]

either snmp-name/error-status = 0 [

name/text: second first snmp-name/values

descr/text: second second snmp-name/values

][

name/text: join "error:" snmp-name/error-status

descr/text: snmp-name/error-message

]

][

name/text: "network error"

descr/text: "verify ip address ..."

]

show name

show descr

]

]

;ce bouton va arrêter ou lancer l’affichage du graphique

rotary "Go" "stop" [

either state [

;arrêt du compteur

counter-box/rate: 0

state: off

;ne rien faire quand un événement arrive

counter-box/feel/engage: none

;rafraîchir le box de façon à ce que rien ne se passe

show counter-box

][

;démarrage du compteur

state: on

;preparation de l’objet

snmp-counter: make-snmp-data/counter community/text to-tuple ip/text counter-id/text 15

;fixe l’intervalle de temps entre 2 interrogations du compteur

counter-box/rate: to-time rate-txt/text

;met en place la fonction qui mettra à jour l’affichage du graphique

counter-box/feel/engage: function [

face act evt

][

maxi

][

if act = 'time [

snmp-counter/update-counter

db: snmp-counter/draw-block

maxi: first maximum-of db

;calcul l’échelle verticale du graphique

y-scale: 100 / maxi

max-text/text: mold maxi

;mise à jour du bloc draw

clear counter-line

insert counter-line [pen 255.0.0 line]

for i 1 length? db 1 [

;insère chaque valeur de 20 en 20

; le 100 – correspond à une inversion de l’axe y

; et db/:i * y-scale à la mise à l’échelle.

append counter-line to-pair reduce [ (i - 1) * 20 to-integer (100 - (db/:i * y-scale)) ]

]

show max-text

show counter-box

]

]

show counter-box

]

]

]

5 – A FAIRE…

5 – LECTURES, RESSOURCES

Vincent Demongodin – Janvier 2002

vdemong@club-internet.fr

 

Retour