Capitolo 4
Implementazione di un modulo per la gestione delle MIB
DiffServ
4.1 RTNetlink Protocol
Il sistema operativo Linux, supporta diverse caratteristiche di rete, compreso policing basato su instradamento e Quality of Service [14].
Queste caratteristiche possono essere configurate e controllate utilizzando un socket netlink.
Il socket netlink, viene utilizzato per trasferire informazioni tra processi kernel e user-space. Esso è costituito da un’interfaccia standard socket-based, per processi di utente e un’interfaccia per programmi applicativi ( API , Application Program Interface ) per il kernel. I messaggi del socket netlink consistono in una sequenza di byte, con una o più nlmsghdr header, ed i rispettivi payload.
Ecco come si presenta la struttura del nlmsghdr: struct nlmsghdr
{
__u32 nlmsg_len; /*Length of message including header */ __u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */ __u32 nlmsg_seq; /* Sequence number */ __u32 nlmsg_pid; /* Sending process PID
};
Il campo nlms_type può indicare, uno dei seguenti messaggi standard : • NLMSG_NOOP : indica che il messaggio deve essere ignorato
• NLMSG_ERROR indica un errore e che il payload contiene una struttura tipo nlmsgeer: struct nlmsgerr
{
int error;/* negative errno or 0 acks*/
struct nlmsghdr msg;/*Message Header that caused the error };
Il formato degli indirizzi è specificato in un ulteriore struttura chiamata sockaddr_nl che descrive un netlink client nell’ user space o nel kernel.
struct sockaddr_nl {
sa_family_t nl_family; /* AF_NETLINK */ unsigned short nl_pad; /* zero */
__u32 nl_pid; /* process pid */ __u32 nl_groups; /* multicast groups mask
};
I messaggi di instradamento, che sono chiamati appunto rtnelink messages, sono particolari messaggi netlink che servono per controllare il comportamento di instradamento di Linux, che include le funzionalità Network Traffic Control [15] e DiffServ.
Network route, indirizzi IP, link parameter, informazioni sulle discipline di coda, sulle classi di traffico e sulla classificazione del pacchetti, sono tutte azioni che possono essere controllate attraverso un socket netlink.
Ciò significa, che un utente può ottenere numerose informazioni dal kernel attraverso una procedura che andremo a descrivere all’interno di questo capitolo. In particolare, la nostra attenzione sarà concentrata, nel recupero di informazioni riguardanti: la classificazione dei pacchetti, da parte dei filtri in ingresso, e i parametri statistici di traffico dei vari PHB configurati sulle interfacce. Il procedimento che permette il recupero di queste informazioni, può essere riassunto nella seguente porzione di pseudo-codice:
(user process)
1. open netlink socket
2. construct request message
3. send message over netlink socket
(kernel)
4. receive and parse message
5. find necessary information in internal data
6. construct response message
7. send message over netlink socket (user process)
8. wait for messages on the open nelink socket
9. receive and parse message
Questo codice, fa parte di un’applicazione che fornisce indicazioni sulle informazioni di controllo, e di traffico. Per consentire una facile interpretazione del metodo corretto per andare a recuperare le informazioni dal kernel, che risultano fondamentali per la configurazione dei nostri oggetti MIB, riportiamo il formato del messaggio netlink, così graficamente rappresentato.
Figura 4.1: Formato del messaggio netlink
Al primo header nlmsghdr appena descritto, seguono le informazioni contenute nel messaggio di Traffic Control (tcmsg). In questa porzione di messaggio possiamo trovare i parametri che individuano le caratteristiche del nostro elemento di Traffic Control e sono i seguenti:
• indice dell’ interfaccia (ifindex)
• class-id, quindi identificativo della classe di servizio, (handle) • parent
In funzione del campo type della porzione di messaggio rta, potremo trovare l’RTA_PAYLOAD, in cui è inserita una stringa che permette di individuare il tipo di oggetto del quale stiamo recuperando gli attributi.
Per esempio, quando viene indicato il valore TCA_STATS nel campo data troveremo una struttura di tipo tc_stats. Nel caso invece del TCA_OPTIONS, il puntatore indicherà la struttura riportata in figura 4.2. Nel nostro caso, seguendo le strutture che possiamo trovare nei file pkt_sched.h e pkt_cls.h del kernel di linux, è stato possibile risalire a tutte le informazioni sia di configurazione del classificatore di pacchetti in ingresso, sia quelle relative ai parametri di traffico delle varie classi di servizio per ogni interfaccia.
Figura 4.2: Formato del messaggio netlink relativo al campo Options
4-2 Recupero delle informazioni configurate nel TC
L’obiettivo di questa implementazione, come detto, è quello di ricercare e memorizzare le informazioni riguardanti la configurazione dei filtri sulle interfacce di ingresso al router Linux, e quelle relative alle statistiche di traffico di ogni PHB configurato, indicate nel capitolo precedente.
Il software realizzato, per prima cosa ricerca il numero di interfacce attive in ingresso al nostro “router”. Per svolgere questa operazione, apre il socket netlink e costruisce un messaggio “ RTM_GETLINK”, che verrà successivamente utilizzato per ottenere le informazioni.
In base al campo ifindex, che è contenuto nel TC message, andremo a creare una lista contenente gli indicatori di ogni interfaccia attiva.
Riportiamo le istruzioni principali del programma rtnl_open (&rth, 0) //apertura del socket netlink
rtnl_wilddump_request (&rth, AF_UNSPEC, RTM_GETLINK) //generazione del messaggio netlink relativo alla richiesta delle informazioni sulle interfacce attive
rtnl_dump_filter (&rth, parse_interfaces, NULL, NULL, NULL) // controllo delle informazioni sulle interfacce , seguito dall’inserimento delle ifindex in una lista.
iflist = (struct iflist_t *) malloc (sizeof (struct iflist_t)); memset (iflist, 0, sizeof (struct iflist_t));
iflist->ifindex = i;
Dopo questa prima operazione dobbiamo creare un nuovo messaggio netlink, che tenendo conto delle interfacce attive sul router, recuperi le informazioni relative ai dati di traffico delle classi di servizio configurate sul router.
Per fare questo il messaggio netlink, che andremo a generare sarà di tipo “RTM_GETTCLASS”, ed infine dopo aver recuperato le informazioni, andremo a memorizzarle in una classlist così definita:
/* CLASS_LIST */ struct classlist_t { int handle; int ifindex; int parent; int info;
unsigned int avpkt;
unsigned int packets; /* numero di pacchetti trasmessi per classe di servizio.*/ unsigned int bytes; /* numero di bytes trasmessi per classe di servizio .*/
unsigned int drops; /*numero di drops avuti, durante la trasmissione di traffico per classe di servizio.*/ unsigned int bps; /*numero medio di byte per secondo*/
unsigned int qlen; /*lunghezza della coda,sull’interfaccia di uscita */ unsigned int overlimits;
unsigned int backlog; unsigned int rate; unsigned int ceil; unsigned int burst; unsigned int cburst; struct classlist_t *next; };
Riportiamo le principali operazione necessarie, per il recupero delle informazioni.
rtnl_dump_request (&rth, RTM_GETTCLASS, &t, sizeof (t)) // generazione del messaggio netlink, relativo alle classi di servizio.
rtnl_dump_filter (&rth, parse_classes, NULL, NULL, NULL, classlist) // controllo delle informazioni ottenute in seguito all’interrogazione del kernel.
A questo punto , sarà necessario puntare il giusto campo del messaggio netlink, che contenga una struttura contenente le informazioni che stiamo cercando.
Scorrendo ciclicamente il messaggio, andiamo quindi a puntare il campo TCA_STATS, ed osserviamo che al suo interno è possibile trovare una struttura denominata tc_statc , riportata qui sotto.
struct tc_stats {
__u64 bytes; /* NUmber of enqueues bytes */ __u32 packets; /* Number of enqueued packets */
__u32 drops; /* Packets dropped because of lack of resources */ __u32 overlimits; /* Number of throttle events when this
* flow goes out of allocated bandwidth */ __u32 bps; /* Current flow byte rate */
__u32 pps; /* Current flow packet rate */ __u32 qlen; __u32 backlog; #ifdef __KERNEL__ spinlock_t *lock; #endif };
Possiamo quindi memorizzare , ogni elemento della tc_stats nella nostra lista, per ogni interfaccia e per classe di servizio.
Infine puntando il campo TCA_OPTIONS e all’interno di questo, puntando il campo TCA_HTB_PARMS, possiamo trovare le informazioni relative al rate, al burst, e al ceil.
Per quanto riguarda, la configurazione dei filtri in ingresso alle interfacce attive del router Linux, abbiamo generato un ulteriore messaggio netlink di tipo “RTM_GETFILTER”, ed andremo in seguito a riempire una lista contenente le informazioni relative. Riportiamo i comandi principale che servono, per trovare e memorizzare i vari parametri:
rtnl_dump_request(&rth, RTM_GETFILTER, &t, sizeof (t)) // permette di generare un messaggio netlink contenente le informazioni sui filtri in ingresso.
rtnl_dump_filter (&rth, parse_filters, NULL, NULL, NULL, filterlist) // permette di controllare le informazioni del messaggio.
Anche in questo caso scorrendo ciclicamente il messaggio TC, andiamo a puntare il campo TCA_OPTIONS, ed in particolare il campo TCA_U32_FILTERS, in cui possiamo trovare una struttura tc_u32_sel:
struct tc_u32_sel {
unsigned char flags; unsigned char offshift; unsigned char nkeys; __u16 offmask; __u16 off; short offoff; short hoff; __u32 hmask; struct tc_u32_key keys[0]; };
e all’interno di questa una struct tc_u32_key, contenente i valori dei parametri configurati nel filtro selezionato. struct tc_u32_key
{
__u32 mask;
__u32 val; // campo corrispondente all’offset sottostante int off; //offset
int offmask; };
In base al valore dell’offset possiamo selezionare il contenuto di un campo corrispondente. Per poter conoscere l’offset, basta osservare la figura 4.3 e la figura 4.4.
byte offset
4-bit
version length 4-bit 8-bit tos 16-bit total packet length in bytes 16-bit identification 3-bit 13-bit fragment offset
flag 8-bit
TTL protocol 8-bit 16-bit header CRC
32-bit IP source address 32-bit IP destination address
options (variable length) data (variable length) 0 4 8 12 16 0 1 2
Figura 4.3 Formato del Pacchetto IP
byte offset
16-bit UDP destination port
16-bit UDP length 16-bit UDP CRC
data (variable length) 0
4 8
0 2
16-bit UDP source port
Figura 4.4: UDP
I valori che possiamo quindi trovare contenuti in val sono: • Ip source address off=12 IP
• Ip destination address off=16 IP • UDP Source Port off=0 UDP • UDP Destination Port off=2 UDP • DSCP off=1 IP
• Protocol off=9 IP
Ed inserirli all’interno della filterlist struct filterlist_t { int handle; int ifindex; int parent; int info; int classid; unsigned int mask; unsigned int protocol;
unsigned int DSCP; //valore del DSCP
unsigned int ipsrc; //valore dell’ indirizzo IP source unsigned int ipdest; //valore dell’indirizzo IP dest unsigned int srcport; //valore della source port unsigned int destport; //valore della dest port struct filterlist_t *next;
};
A questo punto , siamo riusciti a creare 3 diverse liste, una iflist, una classlist e una filterlist, per poter capire quali informazioni sono relative ad una particolare classe di servizio, dobbiamo osservare il campo handle come facilmente comprensibile nella seguente tabella.
CLASS DP DSCP b-DSCP x-DSCP b-DS x-DS handle
AF 1 2 001100 0000-1100 0xc 0011-0000 0x30 131074 3 001110 0000-1110 0xe 0011-1000 0x38 131074 1 010010 0001-0010 0x12 0100-1000 0x48 130175 AF2 2 010100 0001-0100 0x14 0101-0000 0x50 131075 3 010110 0001-0110 0x16 0101-1000 0x58 131075 1 011010 0001-1010 0x1a 0110-1000 0x68 131076 AF3 2 011100 0001-1100 0x1c 0111-0000 0x70 131076 3 011110 0001-1110 0x1e 0111-1000 0x78 131076 1 100010 0010-0010 0x22 1000-1000 0x88 131077 AF4 2 100100 0010-0100 0x24 1001-0000 0x90 131077 3 100110 0010-0110 0x26 1001-1000 0x98 131077 EF 101110 0010-1110 0x2e 1011-1000 0xb8 131073
Figura 4.5 Tabella che relaziona handle e classi e DSCP
4.3 Costruzione del MIB Module
Una volta recuperate le informazioni dal Kernel, è necessario riuscire ad inserirle, in corrispondenza del corretto OID assegnato precedentemente, durante la scrittura delle DiffServMib in ASN.1, in maniera tale, da poter realizzare un modulo MIB completo. Tramite l’utilizzo di un pacchetto software chiamato mib2c [16] presente come applicazione della suite net-snmp 5.0.9 per Linux, è possibile riuscire a prendere una porzione dell’albero delle MIB, identificata da un particolare OID, e generare una porzione di codice in C, necessaria per implementare il MIB Module.
In particolare possiamo generare un codice per realizzare MIB a tabelle (caso dei filtri), ed un altro per realizzare semplicemente delle MIB composte semplicemente da tipi Integer32, nel caso in cui si fissi come nodo di partenza diffServCos (1.3.6.1.2.1.12345) (vedi capitolo precedente),
Nel primo caso, analizziamo il MIB module, che creiamo per poter memorizzare le informazioni relative al traffico delle varie classi di servizio su ogni interfaccia.
mib2c –c mib2c.int_watch.conf diffServCosIf1EF //genera un file diffServCos.c contenente 11 OID statici e 11 interi
Riportiamo una porzione del codice generato tramite mib2c, in modo da mostrare come avviene l’inizializzazione di un determinato OID statico e l’inserimento del corrispettivo valore intero.
static int diffServCosIf1EFTxedPkts=0; //inizializzazione dell’interno statico. static oid diffServCosIf1EFTxedPkts_oid[] =
{ 1, 3, 6, 1, 2, 1, 12345, 1, 9, 1, 1, 2, 0 }; //definizione dell’OID
Per poter inserire i valori in maniera continua, nei rispettivi oid, utilizziamo una funzione definita nelle librerie di net-snmp, e cioè il netsnmp_register
netsnmp_register_int_instance("diffServCosIf1EFTxedPkts", diffServCosIf1EFTxedPkts_oid,
OID_LENGTH(diffServCosIf1EFTxedPkts_oid),
&diffServCosIf1EFTxedPkts, NULL); // inserimento del valore intero relativo ai pacchetti trasmessi della classe EF sull’interfaccia 1.
Per quanto concerne, invece la realizzazione tramite mib2c di una tabella contenente le informazioni scritte in ASN.1, il comando da utilizzare è il seguente:
mib2c –c mib2c.iterate.conf diffServCosIf1FilterTable //genera un file diffServCosIf1FilterTable.c che contiene la definizione di un numero variabile di tabelle, ognuna delle quali è composta da 9 righe.
Il numero di tabelle che andiamo a riempire, corrispondono al numero di filtri che sono stati configurati su una certa interfaccia, ed i valori che inseriamo nelle righe, sono essenzialmente i parametri che definiscono il comportamento di questi filtri (indirizzi IP source e destination, DSCP, etc….). In primo luogo è quindi necessario contare il numero di filtri presenti su una certa interfaccia in maniera tale da definire il numero di tabelle che andremo a creare. Successivamente viene impostato il valore dell’OID in corrispondenza del quale saranno aggiunte le varie tabelle.
static oid diffServFilterIf1Table_oid[] = { 1, 3, 6, 1, 2, 1, 12345, 1, 10, 1, 2 }; Utilizzando quindi le due funzioni:
• diffServFilterIf1Table_get_first_data_point • diffServFilterIf1Table_get_next_data_point
andremo ad inizializzare le tabelle fino a quando non avremo raggiunto lo stesso numero dei filtri configurati sulla data interfaccia. Infine, per inserire i valori dei parametri di configurazione dei vari filtri, in maniera tale da fornire una riposta nel caso di
interrogazione SNMP, utilizzeremo un’altra funzione che, richiamata periodicamente, andrà ad aggiornarli. snmp_set_var_typed_value(var, ASN_UNSIGNED,
(u_char *) &classidarrayifarray[classid],sizeof (long));
In conclusione, siamo riusciti a creare un modulo autoaggiornante, che ad ogni interrogazione SNMP (al corretto OID), restituisce i valori delle statistiche di traffico di ogni BA istante per istante, ed anche lo stato della configurazione di tutti i filtri attivi in ingresso ad ogni interfaccia del router. Riportiamo per completezza alcuni risultati ottenibili, una volta mandato in esecuzione il sub-agent, tramite un’interrogazione SNMP al nodo diffServMib
snmpwalk –v1 -c public postel diffServMib::
Informazioni relative ai parametri di traffico dei vari PHBs configurati sull’interfaccia 1 DIFFSERV-MIB::diffServCosIf1EFbitRate.0 = INTEGER: 2621440 DIFFSERV-MIB::diffServCosIf1EFTxedPkts.0 = INTEGER: 980 DIFFSERV-MIB::diffServCosIf1EFTxedBytes.0 = INTEGER: 49001 DIFFSERV-MIB::diffServCosIf1EFQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1EFQlen.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1EFSchedDropPkts.0 = INTEGER: 10 DIFFSERV-MIB::diffServCosIf1EFOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1EFbps.0 = INTEGER: 30 DIFFSERV-MIB::diffServCosIf1EFceil.0 = INTEGER: 5242880 DIFFSERV-MIB::diffServCosIf1EFcburst.0 = INTEGER: 54028 DIFFSERV-MIB::diffServCosIf1EFBurst.0 = INTEGER: 27814 DIFFSERV-MIB::diffServCosIf1AF1xbitRate.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF1xTxedPkts.0 = INTEGER: 30 DIFFSERV-MIB::diffServCosIf1AF1xTxedBytes.0 = INTEGER: 570
DIFFSERV-MIB::diffServCosIf1AF2xQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF1xQlen.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF1xSchedDropPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF1xOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF1xbps.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF1xceil.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF1xcburst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF1xBurst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF2xbitRate.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF2xTxedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xTxedBytes.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xQlen.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xSchedDropPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xbps.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF2xceil.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF2xcburst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF2xBurst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF3xbitRate.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF3xTxedPkts.0 = INTEGER: 30 DIFFSERV-MIB::diffServCosIf1AF3xTxedBytes.0 = INTEGER: 208 DIFFSERV-MIB::diffServCosIf1AF3xQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF3xQlen.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF3xSchedDropPkts.0 = INTEGER: 5 DIFFSERV-MIB::diffServCosIf1AF3xOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF3xbps.0 = INTEGER: 5 DIFFSERV-MIB::diffServCosIf1AF3xceil.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF3xcburst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF3xBurst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF4xbitRate.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF4xTxedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xTxedBytes.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xQlen.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xSchedDropPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xbps.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1AF4xceil.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1AF4xcburst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1AF4xBurst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1BEbitRate.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1BETxedPkts.0 = INTEGER: 982 DIFFSERV-MIB::diffServCosIf1BETxedBytes.0 = INTEGER: 32898 DIFFSERV-MIB::diffServCosIf1BEQedPkts.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1BEQlen.0 = INTEGER: 10 DIFFSERV-MIB::diffServCosIf1BESchedDropPkts.0 = INTEGER: 10 DIFFSERV-MIB::diffServCosIf1BEOverlimits.0 = INTEGER: 0 DIFFSERV-MIB::diffServCosIf1BEbps.0 = INTEGER: 50 DIFFSERV-MIB::diffServCosIf1BEceil.0 = INTEGER: 1310720 DIFFSERV-MIB::diffServCosIf1BEcburst.0 = INTEGER: 14707 DIFFSERV-MIB::diffServCosIf1BEBurst.0 = INTEGER: 14707
Informazioni relative alla configurazione dei filtri sull’interfaccia 3 DIFFSERV-MIB::diffServFilterId.1 = INTEGER: 1 DIFFSERV-MIB::diffServClassId.1 = Gauge32: 131128 DIFFSERV-MIB::diffServFilterIpsourceaddress.1 = IpAddress: 192.168.12.1 DIFFSERV-MIB::diffServFilterIpdestaddress.1 = IpAddress: 192.168.12.14 DIFFSERV-MIB::diffServFilterProtcol.1 = Gauge32: 0 DIFFSERV-MIB::diffServFilterSrcPort.1 = Gauge32: 0 DIFFSERV-MIB::diffServFilterDstPort.1 = Gauge32: 0 DIFFSERV-MIB::diffServFilterDSCP.1 = Gauge32: 0