Bei der Kommunikation zwischen Sockets können verschiedene Probleme auftreten. Um diese analysieren zu können, werden detaillierte Fehlermeldungen benötigt. Mit dem Setzen des Flags MSG_ERRQUEUE können im verbindungslosen Betrieb im Falle eines Fehlers zusätzliche Informationen zu diesem erhalten werden.
Diese Funktionalität erfordert BCAM V25 oder neuer.
MSG_ERRQUEUE wird derzeit nur für die Funktion recvmsg() unterstützt.
Der folgende Programmausschnitt skizziert die Verwendung von recvmsg():
#include <sys.socket.h> #include <sys.uio.h> ... int s; struct msghdr *msg; int flags = MSG_ERRQUEUE; ... recvmsg(s, msg, flags);
Der Aufruf von recvmsg() benötigt als Parameter den Socket, an dem die Nachricht empfangen werden soll, eine leere Struktur msghdr mit ausreichender Puffergröße und einen gesetzten Flag MSG_ERRQUEUE. Genauere Informationen zu dem Aufruf von recvmsg() sind in recvmsg() - Nachricht von einem Socket empfangen erläutert.
Falls es einen Fehler bei dem Empfang der Nachricht gibt, wird dieser in einer Struktur cmsghdr abgespeichert. Außerdem wird das Flag MSG_ERRQUEUE im Feld msg->msg_flags eingetragen. Im Feld msg->msg_control wird anschließend ein Zeiger auf die Struktur cmsghdr gesetzt.
Fehlermeldungen können nur dann empfangen werden, wenn die Socketoption IP_RECVERR (für AF_INET) oder IPV6_RECVERR (für AF_INET6) gesetzt worden ist. Wenn das Flag IP_RECVERR oder IPV6_RECVERR entfernt wird, dann wird auch die ERRQUEUE geleert.
Struktur msghdr
Die Struktur msghdr ist in <sys.socket.h> wie folgt deklariert:
struct msghdr { caddr_t msg_name; /* optionale Adresse */ int msg_namelen; /* Länge der Adresse */ struct iovec *msg_iov; /* scatter/gather-Felder */ int msg_iovlen; /* Anzahl der Elemente in msg_iov */ caddr_t msg_control; /* Hilfsdaten */ int msg_controllen; /* Länge des Puffers für Hilfsdaten */ int msg_flags; /* Flag für empfangene Nachricht */ }; struct msghdr *msg;
Struktur iovec
Die Struktur iovec ist in <sys.uio.h> wie folgt deklariert:
struct iovec{ caddr_t iov_base; /* Puffer für Hilfsdaten */ int iov_len; /* Pufferlänge */ };
Struktur cmsghdr
Die Struktur cmsghdr ist in <sys.socket.h> wie folgt deklariert:
struct cmsghdr { u_int cmsg_len; /* Anzahl der Datenbytes inkl. Header */ int cmsg_level; /* erzeugendes Protokoll */ int cmsg_type; /* protokollspezifischer Typ */ /* gefolgt von u_char cmsg_data[] */ }; struct cmsghdr *cmsg;
In die Felder der Struktur iovec wird der empfangene L4-Header (TCP, UDP oder ICMP) geschrieben.
Die Felder cmsg->cmsg_level und cmsg->cmsg_type geben an, was in den Datenbereich der Struktur cmsghdr geschrieben wurden. Die Fehlerinformationen selbst befinden sich dort im Format einer Struktur sock_extended_err. Auf die Daten kann mit dem Makro CMSG_DATA zugegriffen werden.
Struktur sock_extended_err
Die Struktur sock_extended_err ist in <linux.errqueue.h> wie folgt deklariert:
struct sock_extended_err { u_int32_t ee_errno; /* Kopie von der errno */ u_int8_t ee_origin; /* Ursprung des Fehlers */ u_int8_t ee_type; /* Art des Fehlers */ u_int8_t ee_code; /* Fehlercode, spezifiziert den Fehler */ u_int8_t ee_pad; /* Padding */ u_int32_t ee_info; /* Zusätzliche Informationen (optional) */ u_int32_t ee_data; /* Zusatzdaten (optional) */ }; struct sock_extended_err *ext_err;
In den Feldern ext_err->ee_type und ext_err->ee_code stehen genauere Informationen zu dem Fehler. Eine Liste aller möglichen Fehlercodes befindet sich in den Include-Dateien <ip.icmp.h> und <icmp6.h>.
Beispiel: Auslesen eines Fehlers mithilfe von MSG_ERRQUEUE und recvmsg()
Mit folgendem Programmcode wird (für AF_INET) und verbindungsloser Übertragung (für ein ICMP-Protokoll) mithilfe von MSG_ERRQUEUE ein Fehler empfangen und ausgelesen.
#include <stdlib.h> #include <sys.socket.h> #include <sys.uio.h> #include <sys.types.h> #include <netinet.in.h> #include <netdb.h> #include <stdio.h> #include <ip.icmp.h> #include <linux.errqueue.h> main(){ int sock; int type, code, recv_id, recv_seq; /* Integer für die ICMP-Protokoll-Daten */ int val = 1; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsg; struct sock_extended_err *ext_err = NULL; struct sockaddr_in from; struct sockaddr_in recv; struct icmp *icmp; char buffer[1280]; char *buffer_ptr = buffer; char control[1024]; int error_type; int error_code; ... /* Als Vorbereitung muss ein Socket erstellt und gebunden werden (Kapitel 2.5 und Kapitel 2.6). */ ... /* Setzen von IP_RECVERR, damit die Fehlermeldungen empfangen werden können */ setsockopt (sock, IPPROTO_IPV4, IP_RECVERR, &val, sizeof (val)); ... /* Anschließend wird im verbindungslosen Betrieb ein Paket gesendet (Kapitel 2.7.2). Nun soll die Antwort empfangen werden. Dafür muss zuerst mit select oder poll gewartet werden (Kapitel 2.10)*/ ... /* Setzen der Felder der Struktur msghdr */ msg.msg_name = &from; /* Beinhaltet die Ziel-Adresse */ msg.msg_namelen = sizeof (from); msg.msg_control = control; /* Speicher für die cmsg-Daten */ msg.msg_controllen = sizeof (control); iov.iov_base = buffer; /* Protokoll-Header und Daten */ iov.iov_len = sizeof (buffer); msg.msg_iov = &iov; msg.msg_iovlen = 1; recvmsg(sock, &msg, MSG_ERRQUEUE); icmp = (struct icmp *) buffer_ptr; /* Auslesen von msg.msg_iov (im Falle eines ICMP-Pakets) */ type = icmp->icmp_type; /* Typ des empfangenen ICMP-Pakets */ code = icmp->icmp_code; /* Gibt genauere Information zum Typ */ recv_id = ntohs (icmp->icmp_id); /* Nur für Typ Echo Reply: Beinhaltet ID des Echo Requests */ recv_seq = ntohs (icmp->icmp_seq); /* Nur für Typ Echo Reply: Beinhaltet Sequenz des Echo Requests */ /* Mit recv_id und recv_seq wird angegeben, auf welches (vorher gesendete) Paket geantwortet wurde */ for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NEXTHDR (&msg, cmsg)) { void *ptr = CMSG_DATA (cmsg); if (cmsg->cmsg_type == IP_RECVERR) { ext_err = (struct sock_extended_err *) ptr; /* Auslesen des Datenbereichs */ error_type = ext_err->ee_type; /* Klassifiziert den Fehler des empfangenen Fehlerpakets */ error_code = ext_err->ee_code; /* Gibt weitere Informationen zu dem Fehlerpaket */ memcpy (&recv, SO_EE_OFFENDER (ext_err), sizeof (struct sockaddr_in)); /* Speichern der Adresse des Absenders des Fehlerpakets in einer Struktur sockaddr_in */ } } }
Im Beispielcode wurde auf Fehlerüberprüfungen bei den Rückgabewerten verzichtet.
Wichtig: Wenn IP_RECVERR oder IPV6_RECVERR gesetzt ist, müssen die Fehlerpakete zeitnah (mit gesetztem Flag MSG_ERRQUEUE) abgeholt werden! Es werden maximal 32 Fehlerpakete zwischengespeichert. Nicht abgeholte Pakete werden überschrieben, sobald diese Anzahl überschritten wird.
Weitere Informationen bezüglich der Struktur cmsghdr und den dazugehörigen Makros können bei CMSG-Makros gefunden werden.