This is the second part of a two-part post on IPv6 socket
programming.
Part 1 was entitled, "Creating neighbor solicitation packet with c
language and ancillary data structures"
This post is the second part, with my solution to reading the neighbor
advertisement we get back from our solicitation and extracting various
pieces of data like the all-important link-layer (MAC) address of the
node we sent the solicitation to.
To be practical, it should probably have a timer on the recvmsg loop.
Also, it would normally be in the same program as that which sent the
solicitation.
Dave
// Receives a neighbor advertisement and extracts hop limit,
// destination address and interface index from ancillary
// data, and advertising link-layer address (i.e., MAC)
// from options data.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // close()
#include <netinet/icmp6.h> // struct nd_neighbor_solicit/advert,
which contains icmp6_hdr and ND_NEIGHBOR_ADVERT
#include <netinet/ip.h> // IP_MAXPACKET (65535)
#include <arpa/inet.h> // inet_pton() and inet_ntop()
#include <netdb.h> // struct addrinfo
#include <sys/ioctl.h> // macro ioctl is defined
#include <bits/ioctls.h> // defines values for argument "request"
of ioctl. Here, we need SIOCGIFHWADDR
#include <bits/socket.h> // structs msghdr and cmsghdr
#include <net/if.h> // struct ifreq
// Taken from <linux/ipv6.h>, also in <netinet/in.h>
struct in6_pktinfo {
struct in6_addr ipi6_addr;
int ipi6_ifindex;
};
// Function prototypes
static void *find_ancillary (struct msghdr *, int);
int
main (int argc, char **argv)
{
int i, status, sd, on, ifindex, hoplimit;
struct nd_neighbor_advert *na;
unsigned char *inpack;
int len;
struct msghdr msghdr;
struct iovec iov[2];
unsigned char *opt, *pkt;
char *interface, *target, *destination;
struct in6_addr dst;
int rcv_ifindex;
struct ifreq ifr;
void *tmp;
// Allocate memory for various arrays.
tmp = (unsigned char *) malloc (IP_MAXPACKET * sizeof (unsigned
char));
if (tmp != NULL) {
inpack = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'inpack'.
\n");
exit (EXIT_FAILURE);
}
memset (inpack, 0, IP_MAXPACKET * sizeof (unsigned char));
tmp = (char *) malloc (40 * sizeof (char));
if (tmp != NULL) {
target = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array 'target'.
\n");
exit (EXIT_FAILURE);
}
memset (target, 0, 40 * sizeof (char));
tmp = (char *) malloc (20 * sizeof (char));
if (tmp != NULL) {
interface = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array
'interface'.\n");
exit (EXIT_FAILURE);
}
memset (interface, 0, sizeof (interface));
strcpy (interface, "wlan0");
tmp = (char *) malloc (40 * sizeof (char));
if (tmp != NULL) {
destination = tmp;}
else {
fprintf (stderr, "ERROR: Cannot allocate memory for array
'destination'.\n");
exit (EXIT_FAILURE);
}
memset (destination, 0, sizeof (destination));
// Prepare msghdr for recvmsg().
memset (&msghdr, 0, sizeof (msghdr));
msghdr.msg_name = NULL;
msghdr.msg_namelen = 0;
memset (&iov, 0, sizeof (iov));
iov[0].iov_base = (unsigned char *) inpack;
iov[0].iov_len = IP_MAXPACKET;
msghdr.msg_iov = iov;
msghdr.msg_iovlen = 1;
msghdr.msg_control = (unsigned char *) malloc (IP_MAXPACKET * sizeof
(unsigned char));
msghdr.msg_controllen = IP_MAXPACKET * sizeof (unsigned char);
// Request a socket descriptor sd.
if ((sd = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
perror ("Failed to get socket descriptor ");
exit (EXIT_FAILURE);
}
// Set flag so we receive hop limit from recvmsg.
on = 1;
if ((status = setsockopt (sd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
sizeof (on))) < 0) {
perror ("setsockopt to IPV6_RECVHOPLIMIT failed ");
exit (EXIT_FAILURE);
}
// Set flag so we receive destination address from recvmsg.
on = 1;
if ((status = setsockopt (sd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
sizeof (on))) < 0) {
perror ("setsockopt to IPV6_RECVPKTINFO failed ");
exit (EXIT_FAILURE);
}
// Obtain MAC address of this node.
memset (&ifr, 0, sizeof (ifr));
snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface);
if (ioctl (sd, SIOCGIFHWADDR, &ifr) < 0) {
perror ("ioctl() failed to get source MAC address ");
return (EXIT_FAILURE);
}
// Retrieve interface index of this node.
if ((ifindex = if_nametoindex (interface)) == 0) {
perror ("if_nametoindex() failed to obtain interface index ");
exit (EXIT_FAILURE);
}
printf ("\nOn this node, index for interface %s is %i\n", interface,
ifindex);
// Bind socket to interface of this node.
if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &ifr,
sizeof (ifr)) < 0) {
perror ("SO_BINDTODEVICE failed");
exit (EXIT_FAILURE);
}
// Grab incoming message from socket sd.
// Keep at it until we get a neighbor advertisement.
na = (struct nd_neighbor_advert *) inpack;
while (na->nd_na_hdr.icmp6_type != ND_NEIGHBOR_ADVERT) {
if ((len = recvmsg (sd, &msghdr, 0)) < 0) {
perror ("recvmsg failed ");
return (EXIT_FAILURE);
}
}
// Ancillary data
printf ("\nIPv6 header data:\n");
opt = find_ancillary (&msghdr, IPV6_HOPLIMIT);
if (opt == NULL) {
fprintf (stderr, "Unknown hop limit\n");
exit (EXIT_FAILURE);
}
hoplimit = *(unsigned int *) opt;
printf ("Hop limit: %u\n", hoplimit);
opt = find_ancillary (&msghdr, IPV6_PKTINFO);
if (opt == NULL) {
fprintf (stderr, "Unkown destination address\n");
exit (EXIT_FAILURE);
}
dst = ((struct in6_pktinfo *) opt)->ipi6_addr;
inet_ntop (AF_INET6, &dst, destination, 40);
printf ("Destination address: %s\n", destination);
rcv_ifindex = ((struct in6_pktinfo *) opt)->ipi6_ifindex;
printf ("Destination interface index: %i\n", rcv_ifindex);
// ICMPv6 header and options data
printf ("\nICMPv6 header data:\n");
printf ("Type: %u\n", na->nd_na_hdr.icmp6_type);
printf ("Code: %u\n", na->nd_na_hdr.icmp6_code);
printf ("Checksum: %x\n", ntohs (na->nd_na_hdr.icmp6_cksum));
printf ("Router flag: %u\n", ntohl (na->nd_na_hdr.icmp6_data32[0])
>> 31);
printf ("Solicited flag: %u\n", ((ntohl (na-
>nd_na_hdr.icmp6_data32[0])) >> 30) & 1);
printf ("Override flag: %u\n", ((ntohl (na-
>nd_na_hdr.icmp6_data32[0])) >> 29) & 1);
printf ("Reserved: %i\n", (ntohl (na->nd_na_hdr.icmp6_data32[0])) &
536870911u);
inet_ntop (AF_INET6, &(na->nd_na_target), target, 40);
printf ("Target address of neighbor solicitation: %s\n", target);
printf ("\nOptions:\n");
pkt = (unsigned char *) inpack;
printf ("Type: %u\n", pkt[sizeof (struct nd_neighbor_advert)]);
printf ("Length: %u (units of 8 octets)\n", pkt[sizeof (struct
nd_neighbor_advert) + 1]);
printf ("MAC address: ");
for (i=2; i<7; i++) {
printf ("%02x:", pkt[sizeof (struct nd_neighbor_advert) + i]);
}
printf ("%02x\n", pkt[sizeof (struct nd_neighbor_advert) + 7]);
close (sd);
free (inpack);
free (target);
free (interface);
free (destination);
free (msghdr.msg_control);
return (EXIT_SUCCESS);
}
static void *
find_ancillary (struct msghdr *msg, int cmsg_type)
{
struct cmsghdr *cmsg = NULL;
for (cmsg = CMSG_FIRSTHDR (msg); cmsg != NULL; cmsg = CMSG_NXTHDR
(msg, cmsg)) {
if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type ==
cmsg_type)) {
return (CMSG_DATA (cmsg));
}
}
return (NULL);
}