Complete Communications Engineering

This can be done by using the PKTINFO socket option and the function recvmsg.  The PKTINFO socket option sets if extra packet information will be returned by recvmsg or not.  By default, the extra information is not returned.  The recvmsg function is similar to recvfrom.  It can read data from a socket and write it to a provided buffer, but it can also return extra information from the header of each packet that is received.  It uses the msghdr structure for this.  The following code example demonstrates using the recvmsg function:

int enable_pktinfo (int s)

{

    int enable = 1;

    if (setsockopt(s, IPPROTO_IP, IP_PKTINFO,

                  (char *)&enable, sizeof(enable)) != 0) {

        return 1;

    }

    return 0;

}

 

int recv_localaddr (int s, uint8_t *buf, size_t buf_sz,

                    struct sockaddr_in *remote_addr,

                    struct sockaddr_in *local_addr)

{

    int len;

    struct msghdr msg = {0};

    struct iovec iov = {0};

    uint8_t cmdbuf[512];

    struct cmsghdr *cmsg;

    struct in_pktinfo *pi;

 

    /* Fill out the msghdr structure */

    iov.iov_base = buf;

    iov.iov_len = buf_sz;

    msg.msg_name = remote_addr;

    msg.msg_namelen = sizeof(*remote_addr);

    msg.msg_iov = &iov;

    msg.msg_iovlen = 1;

    msg.msg_control = cmdbuf;

    msg.msg_controllen = sizeof(cmdbuf);

 

    /* Receive a packet */

    len = recvmsg(s, &msg, 0);

    if (len < 0) {

        return 1;

    }

 

    /* Parse the header info, look for the local address */

    cmsg = CMSG_FIRSTHDR(&msg);

    for (; cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {

        if ((cmsg->cmsg_level == IPPROTO_IP) &&

            (cmsg->cmsg_type == IP_PKTINFO)) {

            pi = (struct in_pktinfo *)CMSG_DATA(cmsg);

            local_addr->sin_family = AF_INET;

            local_addr->sin_addr = pi->ipi_addr;

            break;

        }

    }

 

    return len;

}

The function enable_pktinfo shows how to enable the PKTINFO socket option.  When this option is on, the recvmsg function will generate the PKTINFO message in the ancillary data buffer.  The function recv_localaddr shows how to use recvmsg to get the local address of a packet.  It starts by filling out the msghdr structure which contains fields that tell recvmsg where to put all of the information that it gathers.  The packet data will be written to buf.  The remote address will be written to remote_addr.  The variable cmdbuf will receive the ancillary data.  This buffer must be big enough for all of the data that recvmsg is expected to generate.  After calling recvmsg, the ancillary data is parsed using macros.  The code looks for a specific message type and copies its data to local_addr.