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