LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-30-2011, 07:29 PM   #1
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Rep: Reputation: Disabled
Stumped on IPv6 ping in c language with PF_PACKET/SOCK_DGRAM


Hi guys,

I'm trying to write a c program on Ubuntu to send an ICMP echo request over IPv6. I'm using 6to4/sit0 to do IPv6 as my ISP doesn't support it yet. I'm monitoring sit0 with Wireshark.

I can use the ping6 which comes with Ubuntu and successfully ping ipv6.google.com and on Wireshark see the IPv6 echo request and reply on sit0.

I've used PF_PACKET and SOCK_DGRAM because I don't want to have to specify the MAC addresses. I do want to specify source and destination IP addresses. I understand that SOCK_PACKET is "strongly" deprecated, so I'm trying not to use it.

My program compiles and runs without errors, but nothing shows up on Wireshark on sit0. In fact, if I ask Wireshark to monitor all interfaces, I still see no IPv6 traffic.

Any help would be appreciated.

Dave

Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>

unsigned short int checksum (unsigned short int *, int);

int
main (int argc, char **argv)
{
  int i, temp, status;
  int version, class, label, length, ttl;
  int type, code, id, seq, payloadlen;
  int sd;
  unsigned int old;
  char *interface;
  unsigned char *src, *target;
  unsigned char *payload, *packet;
  struct ip6_hdr iphdr;
  struct icmp icmphdr;
  struct addrinfo *res;
  struct addrinfo hints;
  struct sockaddr_in6 dst, icmp_sin;

  payload = (unsigned char *) malloc (48 * sizeof (unsigned char));
  memset (payload, 0, sizeof (payload));
  for (i=8;i<56;i++) {
    payload[i-8] = i;
  }
  payloadlen = 48;

  packet = (unsigned char *) malloc ((sizeof (iphdr) + sizeof (icmphdr) + payloadlen) * sizeof (unsigned char));
  memset (packet, 0, sizeof (packet));

/* Set the interface to send packet to sit0 */
  interface = (char *) malloc (10 * sizeof (char));
  memset (interface, 0, 10);
  strcpy (interface, "sit0");

  src = (unsigned char *) malloc (39 * sizeof (unsigned char));
  memset (src, 0, 39);
  strcpy (src, "2002:XXXXXXXX::1");  /* redacted for this post */

  target = (unsigned char *) malloc (80 * sizeof (unsigned char));
  memset (target, 0, 80);
  strcpy (target, "ipv6.google.com");  // 2001:4860:800f::93

  memset (&hints, 0, sizeof (struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = AF_INET6;
  hints.ai_socktype = SOCK_RAW;
  hints.ai_protocol = IPPROTO_ICMPV6;

  if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
    fprintf (stderr, "getaddrinfo in target: %s\n", gai_strerror (status));
    return (EXIT_FAILURE);
  }
  memcpy (&dst, res->ai_addr, res->ai_addrlen);

/* ip header */
  version = 6;
  iphdr.ip6_flow = htonl (version << 28);
  class = 0;
  temp = ntohl (iphdr.ip6_flow);
  iphdr.ip6_flow = htonl ((class << 20) + (old & 4027580415u));
  label = 0;
  iphdr.ip6_flow = htonl (label + (old & 4293918720u));
  iphdr.ip6_plen = htons (sizeof (icmphdr) + payloadlen);
  iphdr.ip6_nxt = IPPROTO_ICMP;
  iphdr.ip6_hops = 255;
  inet_pton (AF_INET6, src, &iphdr.ip6_src);
  inet_pton (AF_INET6, target, &iphdr.ip6_dst);

/* icmp header */
  type = ICMP_ECHO;
  icmphdr.icmp_type = type;
  code = 0;
  icmphdr.icmp_code = code;
  id = 1000;
  icmphdr.icmp_id = htons (id);
  seq = 0;
  icmphdr.icmp_seq = htons (seq);
  icmphdr.icmp_cksum = 0;

/* build packet */
  memcpy (packet, &iphdr, sizeof (iphdr));
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));
  memcpy (packet + sizeof (iphdr) + sizeof (icmphdr), payload, payloadlen);
  icmphdr.icmp_cksum = checksum ((unsigned short *) &icmphdr, sizeof (icmphdr) + payloadlen);
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));

/* request a socket descriptor */
  sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));

/* bind socket to interface */
  setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, interface, (socklen_t) sizeof (interface));

  memset (&icmp_sin, 0, sizeof (icmp_sin));
  icmp_sin.sin6_family = PF_PACKET;
  icmp_sin.sin6_addr = iphdr.ip6_dst;

/* send packet */
  sendto (sd, packet, sizeof (packet), 0, (struct sockaddr *) &icmp_sin, sizeof (struct sockaddr));

  free (interface);
  free (src);
  free (target);
  free (payload);
  free (packet);

  return (EXIT_SUCCESS);
}

/* checksum function */
unsigned short int
checksum (unsigned short int *addr, int len)
{
  int nleft = len;
  int sum = 0;
  unsigned short int *w = addr;
  unsigned short int answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= sizeof (unsigned short int);
  }

  if (nleft == 1) {
    *(unsigned char *) (&answer) = *(unsigned char *) w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}
 
Old 10-31-2011, 09:27 PM   #2
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Original Poster
Rep: Reputation: Disabled
Update

Apologies. I found some not-so-minor bugs. I had a bunch of IPv4 things to change.

The behavior is the same though. Nothing shows up on Wireshark

I'm still stumped.

Code with bugs fixed:

Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>

unsigned short int checksum (unsigned short int *, int);

int
main (int argc, char **argv)
{
  int i, temp, status;
  int version, class, label, length, ttl;
  int type, code, id, seq, payloadlen;
  int sd;
  unsigned int old;
  char *interface;
  unsigned char *src, *target;
  unsigned char *payload, *packet;
  struct ip6_hdr iphdr;
  struct icmp6_hdr icmphdr;
  struct addrinfo *res;
  struct addrinfo hints;
  struct sockaddr_in6 dst, icmp_sin;

  payload = (unsigned char *) malloc (48 * sizeof (unsigned char));
  memset (payload, 0, sizeof (payload));
  for (i=8;i<56;i++) {
    payload[i-8] = i;
  }
  payloadlen = 48;

  packet = (unsigned char *) malloc ((sizeof (iphdr) + sizeof (icmphdr) + payloadlen) * sizeof (unsigned char));
  memset (packet, 0, sizeof (packet));

  interface = (char *) malloc (10 * sizeof (char));
  memset (interface, 0, 10);
  strcpy (interface, "sit0");

  src = (unsigned char *) malloc (39 * sizeof (unsigned char));
  memset (src, 0, 39);
  strcpy (src, "2002:XXXXXXXX::1");  //redacted for this post

  target = (unsigned char *) malloc (80 * sizeof (unsigned char));
  memset (target, 0, 80);
  strcpy (target, "ipv6.google.com");  // 2001:4860:800f::93

  memset (&hints, 0, sizeof (struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = AF_INET6;
  hints.ai_socktype = SOCK_RAW;
  hints.ai_protocol = IPPROTO_ICMPV6;

  if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
    fprintf (stderr, "getaddrinfo in target: %s\n", gai_strerror (status));
    return (EXIT_FAILURE);
  }
  memcpy (&dst, res->ai_addr, res->ai_addrlen);

/* ip header */
  version = 6;
  iphdr.ip6_flow = htonl (version << 28);
  class = 0;
  temp = ntohl (iphdr.ip6_flow);
  iphdr.ip6_flow = htonl ((class << 20) + (old & 4027580415u));
  label = 0;
  iphdr.ip6_flow = htonl (label + (old & 4293918720u));
  iphdr.ip6_plen = htons (sizeof (icmphdr) + payloadlen);
  iphdr.ip6_nxt = IPPROTO_ICMPV6;
  iphdr.ip6_hops = 255;
  inet_pton (AF_INET6, src, &iphdr.ip6_src);
  inet_pton (AF_INET6, target, &iphdr.ip6_dst);

/* icmp header */
  type = ICMP6_ECHO_REQUEST;
  icmphdr.icmp6_type = type;
  code = 0;
  icmphdr.icmp6_code = code;
  id = 1000;
  icmphdr.icmp6_id = htons (id);
  seq = 0;
  icmphdr.icmp6_seq = htons (seq);
  icmphdr.icmp6_cksum = 0;

  memcpy (packet, &iphdr, sizeof (iphdr));
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));
  memcpy (packet + sizeof (iphdr) + sizeof (icmphdr), payload, payloadlen);
  icmphdr.icmp6_cksum = checksum ((unsigned short *) (packet + sizeof (iphdr)), sizeof (icmphdr) + payloadlen);
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));

  sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));

  setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, interface, (socklen_t) sizeof (interface));

  memset (&icmp_sin, 0, sizeof (icmp_sin));
  icmp_sin.sin6_family = PF_PACKET;
  icmp_sin.sin6_addr = iphdr.ip6_dst;

  sendto (sd, packet, sizeof (packet), 0, (struct sockaddr *) &icmp_sin, sizeof (struct sockaddr));

  free (interface);
  free (src);
  free (target);
  free (payload);
  free (packet);

  return (EXIT_SUCCESS);
}

/* checksum function */
unsigned short int
checksum (unsigned short int *addr, int len)
{
  int nleft = len;
  int sum = 0;
  unsigned short int *w = addr;
  unsigned short int answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= sizeof (unsigned short int);
  }

  if (nleft == 1) {
    *(unsigned char *) (&answer) = *(unsigned char *) w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}

Last edited by pdbuchan; 10-31-2011 at 10:06 PM.
 
Old 11-01-2011, 04:13 PM   #3
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Original Poster
Rep: Reputation: Disabled
...and more cleanups, still no luck

I fixed some more errors. Very embarrassing.

But anyway, still no luck.

Dave

Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>

unsigned short int checksum (unsigned short int *, int);

int
main (int argc, char **argv)
{
  int i, status;
  int version, class, label, length, ttl;
  int type, code, id, seq, payloadlen, packetlen;
  int sd;
  unsigned int temp;
  char *interface, *src, *target;
  unsigned char *payload, *packet;
  struct ip6_hdr iphdr;
  struct icmp6_hdr icmphdr;
  struct addrinfo *res;
  struct addrinfo hints;
  struct sockaddr_in6 icmp_sin;
  void *ptr;

  payload = (unsigned char *) malloc (48 * sizeof (unsigned char));
  memset (payload, 0, sizeof (payload));
  for (i=8;i<56;i++) {
    payload[i-8] = i;
  }
  payloadlen = 48;

  packetlen = sizeof (iphdr) + sizeof (icmphdr) + payloadlen;
  packet = (unsigned char *) malloc (packetlen * sizeof (unsigned char));
  memset (packet, 0, packetlen);

  interface = (char *) malloc (10 * sizeof (char));
  memset (interface, 0, 10);
  strcpy (interface, "sit0");

  src = (char *) malloc (39 * sizeof (char));
  memset (src, 0, 39);
  strcpy (src, "2002:XXXX:XXXX::1");  // redacted for this post

  target = (char *) malloc (80 * sizeof (char));
  memset (target, 0, 80);
  strcpy (target, "ipv6.google.com");  // 2001:4860:800f::93

  memset (&hints, 0, sizeof (struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = AF_INET6;
  hints.ai_socktype = SOCK_RAW;  // Doesn't matter
  hints.ai_protocol = IPPROTO_ICMPV6;  // Doesn't matter

  if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
    fprintf (stderr, "getaddrinfo in target: %s\n", gai_strerror (status));
    return (EXIT_FAILURE);
  }
  memset (target, 0, 80);
  ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
  inet_ntop (AF_INET6, ptr, target, 40);

/* ip header */
  version = 6;
  iphdr.ip6_flow = htonl (version << 28);
  class = 0;
  temp = ntohl (iphdr.ip6_flow);
  iphdr.ip6_flow = htonl ((class << 20) + (temp & 4027580415u));
  label = 0;
  temp = ntohl (iphdr.ip6_flow);
  iphdr.ip6_flow = htonl (label + (temp & 4293918720u));
  length = sizeof (icmphdr) + payloadlen;
  iphdr.ip6_plen = htons (length);
  iphdr.ip6_nxt = IPPROTO_ICMPV6;
  ttl = 255;
  iphdr.ip6_hops = ttl;
  inet_pton (AF_INET6, src, &iphdr.ip6_src);
  inet_pton (AF_INET6, target, &iphdr.ip6_dst);

/* icmp6 header */
  type = ICMP6_ECHO_REQUEST;
  icmphdr.icmp6_type = type;
  code = 0;
  icmphdr.icmp6_code = code;
  icmphdr.icmp6_cksum = 0;
  id = 1000;
  icmphdr.icmp6_id = htons (id);
  seq = 1;
  icmphdr.icmp6_seq = htons (seq);

  memcpy (packet, &iphdr, sizeof (iphdr));
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));
  memcpy (packet + sizeof (iphdr) + sizeof (icmphdr), payload, payloadlen);
  icmphdr.icmp6_cksum = checksum ((unsigned short *) (packet + sizeof (iphdr)), sizeof (icmphdr) + payloadlen);
  memcpy (packet + sizeof (iphdr), &icmphdr, sizeof (icmphdr));

  sd = socket (PF_PACKET, SOCK_DGRAM, htons (ETH_P_ALL));
  setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, interface, (socklen_t) sizeof (interface));

  memset (&icmp_sin, 0, sizeof (icmp_sin));
  icmp_sin.sin6_family = PF_PACKET;
  icmp_sin.sin6_addr = iphdr.ip6_dst;

  sendto (sd, packet, sizeof (packet), 0, (struct sockaddr *) &icmp_sin, sizeof (struct sockaddr));

  free (interface);
  free (src);
  free (target);
  free (payload);
  free (packet);

  return (EXIT_SUCCESS);
}

/* checksum function */
unsigned short int
checksum (unsigned short int *addr, int len)
{
  int nleft = len;
  int sum = 0;
  unsigned short int *w = addr;
  unsigned short int answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= sizeof (unsigned short int);
  }

  if (nleft == 1) {
    *(unsigned char *) (&answer) = *(unsigned char *) w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  answer = ~sum;
  return (answer);
}
 
Old 11-19-2011, 09:11 AM   #4
pdbuchan
LQ Newbie
 
Registered: Oct 2011
Posts: 8

Original Poster
Rep: Reputation: Disabled
SOLVED

From my investigations, I believe you cannot send PF_PACKET/SOCK_DGRAM packets without knowledge of the target node's link-layer (MAC) address.

If that is correct, you need to do neighbor solicitation, receive the elicited neighbor advertisement, and extract the MAC address. Then you can compose the raw sockets using SOCK_RAW.

The neighbor solicitation is this:

Code:
// Sends a valid IPv6 ICMP neighbor solicitation packet,
// changes hoplimit and specifies interface using ancillary
// data method.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>           // close()
#include <netinet/icmp6.h>    // struct nd_neighbor_solicit, which
contains icmp6_hdr
#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
unsigned short int checksum (unsigned short int *, int);

int
main (int argc, char **argv)
{
  int MAXPACKETLEN = 131072;
  int NSHDRLEN = sizeof (struct nd_neighbor_solicit);  // Length of NS
message header
  int OPTLEN = 8; // Option Type (1 byte) + Length (1 byte) + Length
of MAC address (6 bytes)
  int PSDHDRLEN = 16 + 16 + 4 + 3 + 1 + NSHDRLEN + OPTLEN;  // Section
8.1 of RFC 2460

  int i, sd, status, ifindex, cmsglen, hoplimit;
  struct addrinfo hints;
  struct addrinfo *res;
  struct sockaddr_in6 dst;
  struct sockaddr_in6 src;
  struct sockaddr_in6 dstsnmc;
  struct nd_neighbor_solicit *ns;
  socklen_t srclen;
  unsigned char *outpack, *options, *psdhdr;
  unsigned char temp[16];
  struct msghdr msghdr;
  struct ifreq ifr;
  struct cmsghdr *cmsghdr1, *cmsghdr2;
  struct in6_pktinfo *pktinfo;
  struct iovec iov[2];
  char *target, *source, *interface;
  void *tmp;

// Allocate memory for various arrays.
  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) {
    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 (40 * sizeof (char));
  if (tmp != NULL) {
    source = tmp;}
  else {
    fprintf (stderr, "ERROR: Cannot allocate memory for array 'source'.
\n");
    exit (EXIT_FAILURE);
  }
  memset (source, 0, 40 * sizeof (char));

  tmp = (unsigned char *) malloc (MAXPACKETLEN * sizeof (unsigned
char));
  if (tmp != NULL) {
    outpack = tmp;}
  else {
    fprintf (stderr, "ERROR: Cannot allocate memory for array
'outpack'.\n");
    exit (EXIT_FAILURE);
  }
  memset (outpack, 0, MAXPACKETLEN * sizeof (unsigned char));

  tmp = (unsigned char *) malloc (OPTLEN * sizeof (unsigned char));
  if (tmp != NULL) {
    options = tmp;}
  else {
    fprintf (stderr, "ERROR: Cannot allocate memory for array
'options'.\n");
    exit (EXIT_FAILURE);
  }
  memset (options, 0, OPTLEN * sizeof (unsigned char));

  tmp = (unsigned char *) malloc (PSDHDRLEN * sizeof (unsigned char));
  if (tmp != NULL) {
    psdhdr = tmp;}
  else {
    fprintf (stderr, "ERROR: Cannot allocate memory for array 'psdhdr'.
\n");
    exit (EXIT_FAILURE);
  }
  memset (psdhdr, 0, PSDHDRLEN * sizeof (unsigned char));

// Hard-code target address
  strcpy (target, "ipv6.google.com");

// Hard-code source address
  strcpy (source, "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx");
// Note that you can't bind to link-local address unless you add
scope)

// Fill out hints, which is a struct addrinfo to be used by
getaddrinfo.
  memset (&hints, 0, sizeof (struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = AF_INET6;
  hints.ai_socktype = SOCK_RAW;
  hints.ai_protocol = IPPROTO_ICMPV6;

// Turn source URL or IPv6 address into sockaddr_in6 form and copy to
src, and
// copy source address to checksum pseudo-header (RFC 2460).
  if ((status = getaddrinfo (source, NULL, &hints, &res)) != 0) {
    fprintf (stderr, "getaddrinfo in target: %s\n", gai_strerror
(status));
    return (EXIT_FAILURE);
  }
  memcpy (&src, res->ai_addr, res->ai_addrlen);
  srclen = res->ai_addrlen;
  inet_pton (AF_INET6, source, psdhdr);

// Turn target URL or IPv6 address into sockaddr_in6 form and copy to
dst, and
// copy destination address to checksum pseudo-header (RFC 2460).
  if ((status = getaddrinfo (target, NULL, &hints, &res)) != 0) {
    fprintf (stderr, "getaddrinfo in target: %s\n", gai_strerror
(status));
    return (EXIT_FAILURE);
  }
  memcpy (&dstsnmc, res->ai_addr, res->ai_addrlen);
  memcpy (&dst, res->ai_addr, res->ai_addrlen);
  tmp = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;

// Convert target's IPv6 unicast address to solicited-node multicast
address.
// Section 2.7.1 of RFC 4291.
  memset (temp, 0, 16);
  memcpy (temp, tmp, 16);
  printf ("Target unicast IPv6 address: ");
  for (i=0; i<16; i++) {
    printf ("%02x ", temp[i]);
  }
  printf("\n");
  temp[0]= 255;
  temp[1]=2;
  for (i=2; i<11; i++) {
    temp[i] = 0;
  }
  temp[11]=1;
  temp[12]=255;
  printf ("Target solicited-node multicast address: ");
  for (i=0; i<16; i++) {
    printf ("%02x ", temp[i]);
  }
  printf("\n");
  memcpy (tmp, temp, 16);
  memcpy (&dstsnmc, res->ai_addr, res->ai_addrlen);
  memcpy (psdhdr + 16, temp, 16);

// Request a socket descriptor sd.
  if ((sd = socket (res->ai_family, res->ai_socktype, res-
>ai_protocol)) < 0) {

    perror ("Failed to get socket descriptor ");
    exit (EXIT_FAILURE);
  }

// Obtain source MAC address.
  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);
  }

// Copy source MAC address into options buffer.
  options[0] = 1;           // Option Type - "source link layer
address" (Section 4.6 of RFC 4861)
  options[1] = OPTLEN / 8;  // Option Length - units of 8 octets (RFC
4861)
  for (i=0; i<6; i++) {
    options[i+2] = (unsigned char) ifr.ifr_addr.sa_data[i];
  }

// Report source MAC address to stdout.
  printf ("MAC address for interface %s is ", interface);
  for (i=0; i<5; i++) {
    printf ("%02x:", options[i+2]);
  }
  printf ("%02x\n", options[5+2]);

// Bind the socket descriptor to the source address if not site-local
or link-local.
  if (!(psdhdr[0] == 0xfe)) {
    if (bind (sd, (struct sockaddr *) &src, srclen) < 0) {
      perror ("Failed to bind the socket descriptor to the source
address ");
      exit (EXIT_FAILURE);
    }
  }

// Retrieve source interface index.
  if ((ifindex = if_nametoindex (interface)) == 0) {
    perror ("if_nametoindex() failed to obtain interface index ");
    exit (EXIT_FAILURE);
  }
  printf ("Index for interface %s is %i\n", interface, ifindex);

// Define first part of buffer outpack to be a neighbor solicit
struct.
  ns = (struct nd_neighbor_solicit *) outpack;
  memset (ns, 0, sizeof (*ns));

// Populate icmp6_hdr portion of neighbor solicit struct.
  ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_SOLICIT;  // 135 (RFC 4861)
  ns->nd_ns_hdr.icmp6_code = 0;              // zero for neighbor
solicitation (RFC 4861)
  ns->nd_ns_hdr.icmp6_cksum = htons(0);      // zero when calculating
checksum
  ns->nd_ns_hdr.icmp6_data32[0] = htonl(0);  // Reserved - must be set
to zero (RFC 4861)
  ns->nd_ns_target = dst.sin6_addr;          // Target address (NOT
MULTICAST) (as type in6_addr)

// Append options to end of neighbor solicit struct.
  memcpy (outpack + NSHDRLEN, options, OPTLEN);

// Prepare msghdr for sendmsg().
  memset (&msghdr, 0, sizeof (msghdr));
  msghdr.msg_name = &dstsnmc;  // Destination IPv6 address (solicited
node multicast) (as struct sockaddr_in6)
  msghdr.msg_namelen = sizeof (dstsnmc);
  memset (&iov, 0, sizeof (iov));
  iov[0].iov_base = (unsigned char *) outpack;  // Point msghdr to
buffer outpack
  iov[0].iov_len = NSHDRLEN + OPTLEN;
  msghdr.msg_iov = iov;                 // scatter/gather array
  msghdr.msg_iovlen = 1;                // number of elements in
scatter/gather array

// Tell msghdr we're adding cmsghdr data to change hop limit and
specify interface.
// Allocate some memory for our cmsghdr data.
  cmsglen = CMSG_SPACE (sizeof (int)) + CMSG_SPACE (sizeof (struct
in6_pktinfo));
  tmp = (unsigned char *) malloc (cmsglen * sizeof (unsigned char));
  if (tmp != NULL) {
    msghdr.msg_control = tmp;}
  else {
    fprintf (stderr, "ERROR: Cannot allocate memory for array
'msghdr.msg_control'.\n");
    exit (EXIT_FAILURE);
  }
  memset (msghdr.msg_control, 0, cmsglen);
  msghdr.msg_controllen = cmsglen;

// Change hop limit to 255 as required for neighbor solicitation (RFC
4861).
  hoplimit = 255;
  cmsghdr1 = CMSG_FIRSTHDR (&msghdr);
  cmsghdr1->cmsg_level = IPPROTO_IPV6;
  cmsghdr1->cmsg_type = IPV6_HOPLIMIT;  // We want to change hop limit
  cmsghdr1->cmsg_len = CMSG_LEN (sizeof (int));
  *((int *) CMSG_DATA (cmsghdr1)) = hoplimit;

// Specify source interface index for this packet via cmsghdr data.
  cmsghdr2 = CMSG_NXTHDR (&msghdr, cmsghdr1);
  cmsghdr2->cmsg_level = IPPROTO_IPV6;
  cmsghdr2->cmsg_type = IPV6_PKTINFO;  // We want to specify interface
here
  cmsghdr2->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo));
  pktinfo = (struct in6_pktinfo *) CMSG_DATA (cmsghdr2);
  pktinfo->ipi6_ifindex = ifindex;

// Compute ICMPv6 checksum (RFC 2460).
// psdhdr[0 to 15] = source IPv6 address, set earlier.
// psdhdr[16 to 31] = destination IPv6 address, set earlier.
  psdhdr[32] = 0;  // Length should not be greater than 65535 (i.e., 2
bytes)
  psdhdr[33] = 0;  // Length should not be greater than 65535 (i.e., 2
bytes)
  psdhdr[34] = (NSHDRLEN + OPTLEN)  / 256;  // Upper layer packet
length
  psdhdr[35] = (NSHDRLEN + OPTLEN)  % 256;  // Upper layer packet
length
  psdhdr[36] = 0;  // Must be zero
  psdhdr[37] = 0;  // Must be zero
  psdhdr[38] = 0;  // Must be zero
  psdhdr[39] = IPPROTO_ICMPV6;
  memcpy (psdhdr + 40, outpack, NSHDRLEN + OPTLEN);
  ns->nd_ns_hdr.icmp6_cksum = checksum ((unsigned short int *) psdhdr,
PSDHDRLEN);

  printf ("Checksum: %x\n", ntohs (ns->nd_ns_hdr.icmp6_cksum));

// Send packet.
  if (sendmsg (sd, &msghdr, 0) < 0) {
    perror ("sendmsg() failed ");
    exit (EXIT_FAILURE);
  }
  close (sd);

// Free allocated memory.
  free (interface);
  free (target);
  free (source);
  free (outpack);
  free (options);
  free (psdhdr);
  free (msghdr.msg_control);

  return (EXIT_SUCCESS);

}

// Checksum function
unsigned short int
checksum (unsigned short int *addr, int len)
{
  int nleft = len;
  int sum = 0;
  unsigned short int *w = addr;
  unsigned short int answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= sizeof (unsigned short int);
  }

  if (nleft == 1) {
    *(unsigned char *) (&answer) = *(unsigned char *) w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xFFFF);
  sum += (sum >> 16);
  answer = ~sum;

  return (answer);
}
... and the code to receive the neighbor advertisement and extract the link-layer address is this:

Code:
// 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);
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.

I also note that since I'm using 6to4 because my ISP does not support IPv6, the code above won't work for addresses outside my network. I need to create a IPv4 tunnel and send through interface sit0. I still have to investigate how to do that.

Dave
 
Old 11-19-2011, 10:22 AM   #5
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,541

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Quote:
Originally Posted by pdbuchan View Post
SOLVED
From my investigations, I believe you cannot send PF_PACKET/SOCK_DGRAM packets without knowledge of the target node's link-layer (MAC) address.
I know that the statement above is not applicable to IPv4, thus I presume this is something you discovered when dealing with IPv6.
 
Old 11-19-2011, 10:26 AM   #6
TimothyEBaldwin
Member
 
Registered: Mar 2009
Posts: 249

Rep: Reputation: 27
You should be using PF_INET6/AF_INET6 with SOCK_RAW. PF_PACKET is for Ethernet, and whilst you could construct an IPv4 header to put before your IPv6 packet, it would be silly to do so; as your program would not work on other types of connection.
 
Old 11-19-2011, 10:29 AM   #7
TimothyEBaldwin
Member
 
Registered: Mar 2009
Posts: 249

Rep: Reputation: 27
Quote:
Originally Posted by dwhitney67 View Post
I know that the statement above is not applicable to IPv4, thus I presume this is something you discovered when dealing with IPv6.
I think you are confusing it with PF_INET/SOCK_RAW, PF_PACKET sockets work with link-layer addresses.
 
Old 11-19-2011, 10:31 AM   #8
dwhitney67
Senior Member
 
Registered: Jun 2006
Location: Maryland
Distribution: Kubuntu, Fedora, RHEL
Posts: 1,541

Rep: Reputation: 335Reputation: 335Reputation: 335Reputation: 335
Quote:
Originally Posted by TimothyEBaldwin View Post
I think you are confusing it with PF_INET/SOCK_RAW, PF_PACKET sockets work with link-layer addresses.
Yep! I should read more carefully.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Question about create a socket(PF_PACKET, SOCK_DGRAM, htons(xx)) arrow0815 Linux - Networking 1 11-13-2010 04:44 PM
Question about create a socket(PF_PACKET, SOCK_DGRAM, htons(xx)) arrow0815 Programming 0 11-11-2010 07:58 AM
Please Help me : Sendto: Invalid argument PF_PACKET SOCK_DGRAM htons(ETH_P_IP) tuxtuxtux Linux - Networking 3 03-24-2010 06:08 AM
Ping: unknown (local) host, FC-12, w/ router & DSL modem, not newbie, but stumped. Al Schapira Linux - Networking 4 03-18-2010 11:49 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 06:00 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration