Networking Forums

Networking Forums > Computer Networking > Linux Networking > Creating neighbor solicitation packet with c language and ancillarydata structures

Reply
Thread Tools Display Modes

Creating neighbor solicitation packet with c language and ancillarydata structures

 
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-05-2011, 01:18 AM
Hi guys,

Sorry, it's me again. I'm probably being greedy asking for another
favor, but here goes.

Based on previous discussions, it looks like I need to do neighbor
solicitation to get the MAC address of the target, and then I can
proceed with PF_PACKET/SOCK_RAW as desired. I'm using Lucid Lynx
Ubuntu (not a fan of Unity).

The plan is to create a neighbor solicitation packet using the
ancillary data structures (I find these very confusing). What I have
so far is below. Output is as follows:

MAC address for interface wlan0 is 00:XX:XX:XX:XX:XX (redacted for
this post)
Index for interface wlan0 is 3
sendmsg() failed : No such device

Now I've also tried with sit0 as the interface as I'm using 6to4, but
I believe it is ok to use wlan0 since...

ping6 -I wlan0 ipv6.google.com

....generates all the right traffic on sit0 as seen by Wireshark.

I'm not clear on why it says 'No such device.'

Ideas?

Dave

[code]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <bits/ioctls.h>
#include <linux/socket.h>
#include <net/if.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ifaddrs.h>

/* Taken from <linux/ipv6.h> */
struct in6_pktinfo {
struct in6_addr ipi6_addr;
int ipi6_ifindex;
};

#define MAXPACKETLEN 131072
#define IP6HDRLEN 40 /* Length of ip6_hdr */
#define NSHDRLEN 24 /* Length of neighbor solicitation
message header */
#define MACLEN 6 /* Length of a MAC address */

/* Function prototypes */
unsigned short int checksum (unsigned short int *, int);

int
main (int argc, char **argv)
{
int i, sd, status, payloadlen, ifindex, cmsglen, hoplimit;
struct addrinfo hints;
struct addrinfo *res;
struct sockaddr_in6 dst;
struct sockaddr_in6 src;
struct nd_neighbor_solicit *ns;
socklen_t srclen;
unsigned char outpack[MAXPACKETLEN], *payload;
struct msghdr msghdr;
struct ifreq ifr;
struct cmsghdr *cmsghdr1, *cmsghdr2;
struct iovec iov[2];
char *target, *source, *interface;
void *tmp;

payloadlen = 6;
hoplimit = 255;

interface = malloc (20);
strcpy (interface, "wlan0");
target = malloc (40);
source = malloc (40);
payload = malloc (payloadlen);

strcpy (target, "ipv6.google.com");
strcpy (source, "2002:XXXX:XXXX::1"); /* redacted for this post */

memset (&msghdr, 0, sizeof (msghdr));

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);

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;

if ((sd = socket (res->ai_family, res->ai_socktype, res-
>ai_protocol)) < 0) {

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

/* Obtain my 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 in my_mac() ");
return (EXIT_FAILURE);
}
for (i=0; i<payloadlen; i++) {
payload[i] = (unsigned char) ifr.ifr_addr.sa_data[i];
}
printf ("MAC address for interface %s is ", interface);
for (i=0; i<5; i++) {
printf ("%02x:", payload[i]);
}
printf ("%02x\n", payload[5]);

if (bind (sd, (struct sockaddr *) &src, srclen) < 0) {
perror ("Failed to bind the socket descriptor to the source
address ");
exit (EXIT_FAILURE);
}

ns = (struct nd_neighbor_solicit *) outpack;
memset (ns, 0, sizeof (*ns));

ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_SOLICIT;
ns->nd_ns_hdr.icmp6_code = 0;
ns->nd_ns_hdr.icmp6_cksum = 0;
ns->nd_ns_hdr.icmp6_data32[0] = 0;
ns->nd_ns_target = dst.sin6_addr;

memcpy (outpack + IP6HDRLEN + NSHDRLEN, payload, MACLEN);

msghdr.msg_name = &dst;
msghdr.msg_namelen = sizeof (dst);
memset (&iov, 0, sizeof (iov));
iov[0].iov_base = (unsigned char *) outpack;
iov[0].iov_len = IP6HDRLEN + NSHDRLEN + MACLEN;
msghdr.msg_iov = iov;
msghdr.msg_iovlen = 1;
cmsglen = CMSG_SPACE (sizeof (struct in6_pktinfo)) + CMSG_SPACE
(sizeof (int));
msghdr.msg_control = malloc (cmsglen);
msghdr.msg_controllen = cmsglen;

hoplimit = 255;
cmsghdr1 = (struct cmsghdr *) (msghdr.msg_control);
cmsghdr1->cmsg_level = IPPROTO_IPV6;
cmsghdr1->cmsg_type = IPV6_HOPLIMIT;
cmsghdr1->cmsg_len = CMSG_LEN (sizeof (int));
memcpy (CMSG_DATA (cmsghdr1), &hoplimit, sizeof (int));

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);

cmsghdr2 = (struct cmsghdr *) ((msghdr.msg_control) + CMSG_SPACE
(sizeof (int)));
cmsghdr2->cmsg_level = IPPROTO_IPV6;
cmsghdr2->cmsg_type = IPV6_PKTINFO;
cmsghdr2->cmsg_len = CMSG_LEN (sizeof (struct in6_pktinfo));
memcpy (CMSG_DATA (cmsghdr2), &ifindex, sizeof (int));

ns->nd_ns_hdr.icmp6_cksum = checksum ((unsigned short int *)
outpack, IP6HDRLEN + NSHDRLEN + MACLEN);

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

free (interface);
free (target);
free (source);
free (payload);

return (EXIT_SUCCESS);
}

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);
}
[end of code]
 
Reply With Quote
 
 
 
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-06-2011, 08:54 PM
ok, I made some progress. I can do everything except pin the socket to
the interface.

Changing hop limit with ancillary data structures is finally working.

In case anybody else is curious, I will be posting my final solution
when I've got it.

Dave
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-11-2011, 02:18 AM
Hi guys,

Here's an update.

I seem to send a valid Neighbor Solicitation packet, but wireshark
sees no response.
I'm a bit confused about joining or not joining multicast.
From the wireshark view of my packet, I can't see any problems.

Anyway, maybe someone can tell me why I get no response.

Dave

[code]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <bits/ioctls.h>
#include <linux/socket.h>
#include <net/if.h>

struct in6_pktinfo {
struct in6_addr ipi6_addr;
int ipi6_ifindex;
};

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

int
main (int argc, char **argv)
{
int MAXPACKETLEN = 131072;
int NSHDRLEN = sizeof (struct nd_neighbor_solicit);
int OPTLEN = 8;
int PSDHDRLEN = 16 + 16 + 4 + 3 + 1 + NSHDRLEN + OPTLEN;

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

interface = malloc (20);
memset (interface, 0, sizeof (interface));
strcpy (interface, "wlan0");

target = malloc (40);
memset (target, 0, 40 * sizeof (char));

source = malloc (40);
memset (source, 0, 40 * sizeof (char));

outpack = malloc (MAXPACKETLEN);
memset (outpack, 0, MAXPACKETLEN * sizeof (unsigned char));

options = malloc (OPTLEN);
memset (options, 0, OPTLEN * sizeof (unsigned char));

psdhdr = malloc (PSDHDRLEN);
memset (psdhdr, 0, PSDHDRLEN * sizeof (unsigned char));

strcpy (target, "ipv6.google.com");

strcpy (source, "2002:XXXX:XXXX::1"); /* redacted for this post */

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 (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);

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);
tmp = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;

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 (&dst, res->ai_addr, res->ai_addrlen);
memcpy (psdhdr + 16, temp, 16);

if ((sd = socket (res->ai_family, res->ai_socktype, res-
>ai_protocol)) < 0) {

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

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);
}

options[0] = 1;
options[1] = OPTLEN / 8;
for (i=0; i<6; i++) {
options[i+2] = (unsigned char) ifr.ifr_addr.sa_data[i];
}

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]);

if (bind (sd, (struct sockaddr *) &src, srclen) < 0) {
perror ("Failed to bind the socket descriptor to the source
address ");
exit (EXIT_FAILURE);
}

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);

if (setsockopt (sd, SOL_SOCKET, SO_BINDTODEVICE, (void *) &ifr,
sizeof (ifr)) < 0) {
perror ("SO_BINDTODEVICE failed");
exit (EXIT_FAILURE);
}

ns = (struct nd_neighbor_solicit *) outpack;
memset (ns, 0, sizeof (*ns));

ns->nd_ns_hdr.icmp6_type = ND_NEIGHBOR_SOLICIT;
ns->nd_ns_hdr.icmp6_code = 0;
ns->nd_ns_hdr.icmp6_cksum = htons(0);
ns->nd_ns_hdr.icmp6_data32[0] = htonl(0);
ns->nd_ns_target = dst.sin6_addr;

memcpy (outpack + NSHDRLEN, options, OPTLEN);

memset (&msghdr, 0, sizeof (msghdr));
msghdr.msg_name = &dst;
msghdr.msg_namelen = sizeof (dst);
memset (&iov, 0, sizeof (iov));
iov[0].iov_base = (unsigned char *) outpack;
iov[0].iov_len = NSHDRLEN + OPTLEN;
msghdr.msg_iov = iov;
msghdr.msg_iovlen = 1;

cmsglen = CMSG_SPACE (sizeof (int));
msghdr.msg_control = malloc (cmsglen);
memset (target, 0, 40);
msghdr.msg_controllen = cmsglen;

hoplimit = 255;

cmsghdr1 = (struct cmsghdr *) (msghdr.msg_control);
cmsghdr1->cmsg_level = IPPROTO_IPV6;
cmsghdr1->cmsg_type = IPV6_HOPLIMIT;
cmsghdr1->cmsg_len = CMSG_LEN (sizeof (int));
memcpy (CMSG_DATA (cmsghdr1), &hoplimit, sizeof (int));

psdhdr[32] = 0;
psdhdr[33] = 0;
psdhdr[34] = (NSHDRLEN + OPTLEN) / 256;
psdhdr[35] = (NSHDRLEN + OPTLEN) % 256;
psdhdr[36] = 0;
psdhdr[37] = 0;
psdhdr[38] = 0;
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));

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

free (interface);
free (target);
free (source);
free (outpack);
free (options);
free (psdhdr);
free (msghdr.msg_control);

return (EXIT_SUCCESS);
}

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);
}
[end of code]
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-11-2011, 04:05 PM
Incidentally, the reason I don't join my interface with any multicast
address is that I'm not doing stateless autoconfiguration (which
requires that I join).

I've already got a unicast address, and I expect the neighbor
advertisement to respond to my unicast address. I think that is the
way it works for link level address resolution.

Dave
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-19-2011, 02:53 AM
ok. I think it's all working now.
I will post the neighbor solicitation code below, but start a new
thread to show my solution for receiving the neighbor advertisement
which comes back and extracting the neighbor's link-layer (MAC)
address from the advertisement.

This code uses two ancillary data structures, mostly to learn how to
do it. It modifies the hop limit and interface index using ancillary
data.
I've peppered comments throughout to explain what's going on.

I've only successfully tested it with link-local addresses, as I have
to use 6to4 to tunnel to the outside.
I have still to research how to wrap an ipv6 packet in ipv4.

In the mean time, if anyone has ipv6 service, please do try it out on
say, ipv6.google.com.

Dave

// 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);
}
 
Reply With Quote
 
clemens fischer
Guest
Posts: n/a

 
      11-19-2011, 05:51 PM
pdbuchan@... wrote:

> ok. I think it's all working now.
> I will post the neighbor solicitation code below, but start a new
> thread to show my solution for receiving the neighbor advertisement
> which comes back and extracting the neighbor's link-layer (MAC)
> address from the advertisement.


Hi,

why are you doing all this? AFAIK neighbor discovery/solicitation is
done by the IP stack all by itself.

Look at:

$ egrep . /proc/sys/net/ipv6/neigh/wlan0/* /proc/sys/net/ipv6/conf/default/*
/proc/sys/net/ipv6/neigh/wlan0/anycast_delay:99
/proc/sys/net/ipv6/neigh/wlan0/app_solicit:0
/proc/sys/net/ipv6/neigh/wlan0/base_reachable_time:30
/proc/sys/net/ipv6/neigh/wlan0/base_reachable_time_ms:30000
/proc/sys/net/ipv6/neigh/wlan0/delay_first_probe_time:5
/proc/sys/net/ipv6/neigh/wlan0/gc_stale_time:60
/proc/sys/net/ipv6/neigh/wlan0/locktime:0
/proc/sys/net/ipv6/neigh/wlan0/mcast_solicit:3
/proc/sys/net/ipv6/neigh/wlan0/proxy_delay:79
/proc/sys/net/ipv6/neigh/wlan0/proxy_qlen:64
/proc/sys/net/ipv6/neigh/wlan0/retrans_time:300
/proc/sys/net/ipv6/neigh/wlan0/retrans_time_ms:1000
/proc/sys/net/ipv6/neigh/wlan0/ucast_solicit:3
/proc/sys/net/ipv6/neigh/wlan0/unres_qlen:3
/proc/sys/net/ipv6/conf/default/accept_dad:1
/proc/sys/net/ipv6/conf/default/accept_ra:0
/proc/sys/net/ipv6/conf/default/accept_ra_defrtr:0
/proc/sys/net/ipv6/conf/default/accept_ra_pinfo:0
/proc/sys/net/ipv6/conf/default/accept_ra_rt_info_max_plen:0
/proc/sys/net/ipv6/conf/default/accept_ra_rtr_pref:0
/proc/sys/net/ipv6/conf/default/accept_redirects:0
/proc/sys/net/ipv6/conf/default/accept_source_route:0
/proc/sys/net/ipv6/conf/default/autoconf:0
/proc/sys/net/ipv6/conf/default/dad_transmits:1
/proc/sys/net/ipv6/conf/default/disable_ipv6:0
/proc/sys/net/ipv6/conf/default/force_mld_version:0
/proc/sys/net/ipv6/conf/default/force_tllao:0
/proc/sys/net/ipv6/conf/default/forwarding:1
/proc/sys/net/ipv6/conf/default/hop_limit:64
/proc/sys/net/ipv6/conf/default/max_addresses:16
/proc/sys/net/ipv6/conf/default/max_desync_factor:600
/proc/sys/net/ipv6/conf/default/mc_forwarding:0
/proc/sys/net/ipv6/conf/default/mtu:1280
/proc/sys/net/ipv6/conf/default/optimistic_dad:0
/proc/sys/net/ipv6/conf/default/proxy_ndp:0
/proc/sys/net/ipv6/conf/default/regen_max_retry:5
/proc/sys/net/ipv6/conf/default/router_probe_interval:60
/proc/sys/net/ipv6/conf/default/router_solicitation_delay:1
/proc/sys/net/ipv6/conf/default/router_solicitation_interval:4
/proc/sys/net/ipv6/conf/default/router_solicitations:3
/proc/sys/net/ipv6/conf/default/temp_prefered_lft:86400
/proc/sys/net/ipv6/conf/default/temp_valid_lft:604800
/proc/sys/net/ipv6/conf/default/use_tempaddr:2

You might consider the relevant RFC's [1]

[1] http://www.ietf.org/rfc/rfc3756.txt

A tool that might help you is ip(8) from package iproute2.

Youz might also use:

http://www.google.com/search?q=linux...20solicitation


clemens
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-19-2011, 10:20 PM
Hi Clemens,

I wanted to do raw sockets in IPv6 where I could edit all aspects of
the IP header. Ancillary data methods do not allow all values to be
edited. As IPv6 does not have the IP_HDRINCL option (although I hear
Windows has something like it), I was trying to use PF_PACKET/
SOCK_DGRAM.

man 7 packet (on Ubuntu) reads, in part:

"The socket_type is either SOCK_RAW for raw packets including the link
level header or SOCK_DGRAM for cooked packets with the link level
header removed. The link level header information is available in a
common format in a sockaddr_ll.

SOCK_RAW packets are passed to and from the device driver without any
changes in the packet data. .... When transmitting a packet, the user
supplied buffer should contain the physical layer header. That packet
is then queued unmodified to the network driver of the interface
defined by the destination address.

SOCK_DGRAM operates on a slightly higher level. The physical header
is removed before the packet is passed to the user. Packets sent
through a SOCK_DGRAM packet socket get a suitable physical layer
header based on the information in the sockaddr_ll destination
address before they are queued."

I had mis-read this to mean that I didn't need the physical layer
header data (MAC address) and that linux would do the neighbor
discovery for me and fill it in. But on second reading, I now believe
that I must still provide it.

Regardless, the purpose is merely to learn and have fun bouncing
packets between my laptops.

Dave
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-26-2011, 03:14 PM
One final follow-up:

I looked at how to encapsulate IPv6 in IPv4 (6to4 tunneling) and RFC
4213 gives the details.
It's very simple. You prepend an IPv4 header on the front of your IPv6
header (20 bytes). You set values according to the RFC,
and send to the 6to4 IPv4 address (192.88.99.1).

I tried to send an IPv6 neighbor solicitation to ipv6.google.com over
6to4 and got no response.
But if I sent a hand-crafted ICMPv6 echo request to ipv6.google.com
over 6to4, I DO receive an echo reply.
This got me thinking that maybe the neighbor solicitation was being
dropped. I went back to my neighbor solicitation
program that successfully sends solicits within link-local scope (from
laptop to laptop on my lan) and I tunnelled it through 6to4.
Nothing showed up on the wire, so I can only conclude that either the
sending or receiving machine's stack is silently dropping the packet.
Perhaps there's a rule about dropping 6to4 traffic if the IPv6 header
is going to a multicast address (for neighbor solicitation, you need
to send
to the solicited-node multicast address). Regardless, I now see why I
never received a reply.

I will assume that since my IPv6 (not 6to4 tunnelled) neighbor
solicitation works for link-local addresses, it will work on a native
IPv6 connection.

The original purpose of the neighbor solicitation was to obtain the
link-layer (MAC) address of the target so I could prepare PF_PACKET/
SOCK_RAW packets in IPv6 which require the link-layer address of the
target. I seem to be able to do that. For targets external to my
network, I need to use 6to4 since I don't have native IPv6 service,
but in that case I'm using raw IPv4 sockets and can use IP_HDRINCL.
Also, the IPv6 header is fully accessible to me in that case anyway
since it follows the IPv4 header as just data. So manual neighbor
solicitation is unnecessary for 6to4 traffic.

Dave
 
Reply With Quote
 
 
 
Reply

Thread Tools
Display Modes

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 On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Packet sniffing wi-Fi-hacking neighbor ( arkland Wireless Internet 58 07-30-2011 06:45 AM
Using vs. Avoiding Steel Structures jlaham Wireless Internet 1 10-15-2007 10:15 AM
sendto() and structures ericunfuk Linux Networking 1 03-24-2007 12:22 PM
max iovec structures in msghdr? Oliver Kowalke Linux Networking 0 01-15-2007 05:16 PM
How to send a tcp packet to same machine from a kernel module by creating a sk_buff struct will_u_tellmemore Linux Networking 0 01-11-2007 12:45 PM



1 2 3 4 5 6 7 8 9 10 11