Under Construction

Liste der NDD Geräte (KINFO_NDD)

Über den Operations-Code KINFO_NDD liefert der System-Call getkerninfo() Informationen über alle NDD (Network Device Driver) Geräte. Das sind neben Netzwerk-Geräten z.B. auch FC-Adapter. Für jedes gefundene Gerät wird eine kinfo_ndd Struktur (definiert in /usr/include/sys/ndd.h) befüllt. Die Struktur kinfo_ndd hat die folgenden Felder:

/*
* Structure returned by getkerninfo KINFO_NDD
*/
struct kinfo_ndd {
    char ndd_name[NDD_MAXNAMELEN];  /* name, e.g. ``en0'' or ``tr0'' */
    char ndd_alias[NDD_MAXNAMELEN]; /* alternate name */
    __ulong32_t ndd_flags;          /* up/down, broadcast, etc. */
    __ulong32_t ndd_mtu;            /* maximum transmission unit */
    __ulong32_t ndd_mintu;          /* minimum transmission unit */
    __ulong32_t ndd_type;           /* ethernet, etc (see interface types */
    __ulong32_t ndd_addrlen;        /* physical address length */
    u_char ndd_addr[64];            /* physical address */
};

Neben dem Namen (ndd_name) und dem Typ des Geräts (ndd_type) enthält die Struktur z.B. auch die Hardware-Adresse des unterliegenden Gerätes (ndd_addr).

Da im Falle von KINFO_NDD nicht klar ist, wie groß der benötigte Puffer sein muss, muss dies durch einen Aufruf von getkerninfo() mit Argument NULL für Puffer und Puffer-Größe zunächst ermittelt werden. Im Folgenden ist ein kleines Beispiel-Programm gezeigt, welches die notwendigen Schritte durchführt:

    1. Bestimmen der notwendigen Puffer-Größe durch Aufruf von getkerninfo() mit NULL-Pointer für Puffer und Größe.
    2. Allozieren von Speicher mit malloc(3).
    3. Abfragen der Informationen durch Aufruf von getkerninfo() mit dem allozierten Speicher.
    4. Benutzen der gewonnenen Daten.
    5. Zurückgeben des allozierten Speichers.
$ cat kinfo_ndd.c
#include <sys/kinfo.h>
#include <sys/ndd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

int getkerninfo(int operation, char *buffer, int *buffer_size, int32long64_t argument);

int main()
{
        struct kinfo_ndd* info;
        int size;
        int ret;

        // 1. determine needed size of buffer
        if ( ( size = getkerninfo(KINFO_NDD,NULL,NULL,0) ) == -1 )
        {
                perror("kinfo_ndd");
                exit(errno);
        }
        printf("needed buffer size: %d\n",size);

      // 2. allocate buffer of appropriate size
        info = (struct kinfo_ndd*)malloc(size);
        if ( info == NULL )
        {
                perror("kinfo_ndd");
                exit(errno);
        }
        printf("memory allocated\n");

        // 3. get infourmation about ndd devices from kernel
        if ( ( ret = getkerninfo(KINFO_NDD,(char*)info,&size,0) ) == -1 )
        {
                perror("kinfo_ndd");
                exit(errno);
        }
        printf("got %d records from getkerninfo()\n",ret/sizeof(struct kinfo_ndd));

        // 4. use the data
        for ( int i = 0 ; i < ret/sizeof(struct kinfo_ndd) ; ++i )
                printf("%d: %s\n",i,info[i].ndd_name);

        // 5. free the memory allocated
        free(info);
        printf("memory freed\n");

        return 0;
}

$

Das Programm kann z.B. mit dem GNU C-Compiler übersetzt und anschließend gestartet werden:

$ gcc -o kinfo_ndd kinfo_ndd.c
$ kinfo_ndd
needed buffer size: 348
memory allocated
got 3 records from getkerninfo()
0: fcs1
1: fcs0
2: ent0
memory freed
$

Die Ausgabe zeigt, dass in diesem Fall 348 Bytes für den Puffer notwendig sind. Diese werden mittels malloc(3) alloziert und anschließend werden die Datensätze für die vorhandenen NDD Geräte mit Hilfe des Puffers angefordert. Es werden die Daten für drei NDD Geräte zurückgeliefert. Über eine kleine Schleife werden die Namen der Geräte ausgegeben und letztlich der Speicher wieder freigegeben.

Die Struktur kinfo_ndd ist 116 Bytes groß. Da in unserem Fall eine Puffer-Größe von mindestens 348 Bytes notwendig ist, bedeutet dies das 348 Bytes / 116 Bytes = 3 Datensätze existieren. Wird getkerninfo() mit einem Puffer kleiner 348 Bytes aufgerufen, so führt dies nicht zu einem Überlauf! Es werden dann einfach entsprechend weniger Daten in den Puffer kopiert. Wieviele Bytes tatsächlich kopiert wurden, wird über das dritte Argument zurückgeliefert.

Als konkretes Beispiel wurde der Wert von size auf 250 gesetzt, etwas mehr als 2 mal 116 Bytes. Der Rückgabe-Wert von getkerninfo() und der Wert von size werden ausgegeben:

        size = 250;
        ret = getkerninfo(KINFO_NDD,buffer,&size,0);
        printf("ret=%d size=%d\n",ret,size);

Dies führt zu der folgenden Ausgabe:

ret=348 size=232

Der Rückgabe-Wert zeigt weiterhin an wieviele Bytes an Daten verfügbar sind (348 Bytes) und damit wie groß der Puffer mindestens sein sollte. Der Wert von size nach dem Aufruf von getkerninfo() gibt an wieviele Daten (in Bytes) tatsächlich kopiert wurden. In unserem Falle 232 Bytes, was 2 kompletten Datensätzen entspricht (2 * 116 Bytes = 232 Bytes). D.h. die Informationen für ein NDD Gerät wurden nicht in den Puffer kopiert um einen Überlauf zu vermeiden!

Wurden alle verfügbaren Daten kopiert, sollte der Rückgabe-Wert gleich dem Wert von size sein!