During communication between sockets a few different problems can arise. Detailed error reports are necessary to be able to analyze them. By setting the flag MSG_ERRQUEUE additional information can be obtained for connectionsless communications in case of an error.
This functionality requires BCAM V25 or newer.
At this time, MSG_ERRQUEUE is only supported for the function recvmsg().
The following program section illustrates the use of recvmsg():
#include <sys.socket.h> #include <sys.uio.h> ... int s; struct msghdr *msg; int flags = MSG_ERRQUEUE; ... recvmsg(s, msg, flags);
For a successful call of recvmsg() the socket, where the message is to be received, an empty msghdr structure with sufficient buffer space and MSG_ERRQUEUE as set flag is needed. Detailed information regarding the function recvmsg() can be found in recvmsg() - receive a message from a socket.
Errors that arise while receiving messages are saved in a cmsghdr structure. The flag MSG_ERRQUEUE is added to msg->msg_flags and a pointer is set to the cmsghdr structure in the field msg->msg_control subsequently.
Errors reports can only be received with MSG_ERRQUEUE if the socket option IP_RECVERR (for AF_INET) or IPV6_RECVERR (for AF_INET6) has been set. These error reports will be emptied if the flag IP_RECVERR or IPV6_RECVERR is unset.
msghdr structure
The msghdr structure is declared in <sys.socket.h> as follows:
struct msghdr { caddr_t msg_name; /* optional address */ int msg_namelen; /* length of the address */ struct iovec *msg_iov; /* scatter/gather fields */ int msg_iovlen; /* number of elements in msg_iov */ caddr_t msg_control; /* auxiliary data */ int msg_controllen; /* length of the buffer for auxiliary data */ int msg_flags; /* flag for received message */ }; struct msghdr *msg;
iovec structure
The iovec structure is declared in <sys.uio.h> as follows:
struct iovec{ caddr_t iov_base; /* buffer for auxiliary data */ int iov_len; /* buffer length */ };
cmsghdr structure
The cmsghdr structure is declared in <sys.socket.h> as follows:
struct cmsghdr { u_int cmsg_len; /* number of data bytes incl. header */ int cmsg_level; /* generating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by u_char cmsg_data[] */ }; struct cmsghdr *cmsg;
The fields of the iovec structure are used to store the received L4-header (TCP, UDP or ICMP).
The fields cmsg->cmsg_level and cmsg->cmsg_type describe, which data is written into the data area of the cmsghdr structure. The error information itself is stored there via a sock_extended_err structure. The data can be accessed with the CMSG_DATA macro.
sock_extended_err structure
The sock_extended_err structure is declared in <linux.errqueue.h> as follows:
struct sock_extended_err { u_int32_t ee_errno; /* copy of errno number */ u_int8_t ee_origin; /* where the error originated */ u_int8_t ee_type; /* type of error */ u_int8_t ee_code; /* error code, specifies the error */ u_int8_t ee_pad; /* padding */ u_int32_t ee_info; /* additional information */ u_int32_t ee_data; /* other data */ }; struct sock_extended_err *ext_err;
More detailed information about the error is contained in the fields ext_err->ee_type and ext_err->ee_code. A list of all possible error codes can be found in the header files <ip.icmp.h> and <icmp6.h>.
Example: Obtaining detailed error information with MSG_ERRQUEUE and recvmsg()
The program code (for AF_INET) below shows the reception and handling of errors with MSG_ERRQUEUE for connectionless (in this case an ICMP protocol) communications.
#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 for the data of the icmp protocol */ 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; ... /* For preparation a socket has to be created and bound (chapter 2.5 and chapter 2.6). */ ... /* Setting IP_RECVERR to be able to receive error reports */ setsockopt (sock, IPPROTO_IPV4, IP_RECVERR, &val, sizeof (val)); ... /* Afterwards a packet is sent via connectionless communication (chapter 2.7.2). Now the reply needs to be received. Before that can happen, the socket must wait with select() or poll() (chapter 2.10)*/ ... /* Filling out the fields of the msghdr structure */ msg.msg_name = &from; /* Contains the destination address */ msg.msg_namelen = sizeof (from); msg.msg_control = control; /* Memory for the control message structure (cmsg) */ msg.msg_controllen = sizeof (control); iov.iov_base = buffer; /* Protocol header and data */ iov.iov_len = sizeof (buffer); msg.msg_iov = &iov; msg.msg_iovlen = 1; recvmsg(sock, &msg, MSG_ERRQUEUE); icmp = (struct icmp *) buffer_ptr; /* Reading from msg.msg_iov (in case of an ICMP packet) */ type = icmp->icmp_type; /* Type of the received ICMP packet */ code = icmp->icmp_code; /* Contains more detailed information about the packet type */ recv_id = ntohs (icmp->icmp_id); /* Only for type Echo Reply: Contains ID of the Echo Request */ recv_seq = ntohs (icmp->icmp_seq); /* Only for type Echo Reply: Contains sequence of the Echo Request */ /* recv_id and recv_seq specify to which (previously sent) package the reply belongs */ 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; /* Reading from the data area */ error_type = ext_err->ee_type; /* Classifies the error of the received error packet */ error_code = ext_err->ee_code; /* Contains more detailed information about the error packet */ memcpy (&recv, SO_EE_OFFENDER (ext_err), sizeof (struct sockaddr_in)); /* Saving the address of the sender of the error package in a sockaddr_in structure */ } } }
This example code does not check the functions return values.
Important: When IP_RECVERR or IPV6_RECVERR is set, the error packages have to be picked up in a timely manner (with the flag MSG_ERRQUEUE)! A maximum of 32 error packages will be stored in the buffer. Packaged not picked up will be deleted as soon as this count is reached.
More information regarding the cmsghdr structure and its macros can be found at CMSG-Macros.