Networking Forums

Networking Forums > Computer Networking > Linux Networking > Stumped on IPv6 ping in c language with PF_PACKET/SOCK_DGRAM

Reply
Thread Tools Display Modes

Stumped on IPv6 ping in c language with PF_PACKET/SOCK_DGRAM

 
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-02-2011, 01:47 PM
Hi guys,

This is a question I posted on linuxquestions.org, but no takers so
far, so I thought I'd widen the audience to newsgroups.

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 ping6
ipv6.google.com and on Wireshark see the IPv6 echo request and reply
on sit0. Basde on this test, I believe I should be able to see the
traffic from my program by monitoring sit0 with Wireshark.

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

#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);
}
 
Reply With Quote
 
 
 
 
J G Miller
Guest
Posts: n/a

 
      11-02-2011, 02:30 PM
On Wednesday, November 2nd, 2011 07:47:24 -0700, P D Buchan wrote:

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


Dumb question -- is a name allowed there?

How does it get the IPv6 numeric address?

If you change that to a local IPv6 numeric address
do you see anything when monitoring with wireshark?
 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a

 
      11-02-2011, 02:43 PM
On Wed, 2011-11-02, (E-Mail Removed) wrote:
> Hi guys,
>
> This is a question I posted on linuxquestions.org, but no takers so
> far, so I thought I'd widen the audience to newsgroups.
>
> 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 ping6
> ipv6.google.com and on Wireshark see the IPv6 echo request and reply
> on sit0. Basde on this test, I believe I should be able to see the
> traffic from my program by monitoring sit0 with Wireshark.
>
> 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.


When you don't see any actual I/O, it's a good idea to run strace(1) on
your binary to see what system calls it makes. Possibly ltrace(1) too.

Do you need this to work, or is it just for fun? Generating ICMP
traffic is a bit hard -- and not particularly useful.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-02-2011, 02:46 PM
Hi!

There are no dumb questions. I appreciate all questions and comments.
Obviously I'm missing something and need a fresh perspective.

I convert URL ipv6.google.com to IPv6 address via the call to
getaddrinfo().

The result is located at res->ai_addr in sockaddr_in6 form.

I then make ptr a pointer to that address:

ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;

I then wipe the contents of target and plop the IPv6 address into
target:

inet_ntop (AF_INET6, ptr, target, 40);

Finally, I put that into the IP header with:

inet_pton (AF_INET6, target, &iphdr.ip6_dst);

Perhaps not the most efficient or beautiful way to to it though.

I haven't tried a local IPv6 address. I'll do that tonight when I get
home. Good idea.

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

 
      11-02-2011, 02:55 PM
On Nov 2, 11:43*am, Jorgen Grahn <grahn+n...@snipabacken.se> wrote:
> On Wed, 2011-11-02, pdbuc...@yahoo.com wrote:
> > Hi guys,

>
> > This is a question I posted on linuxquestions.org, but no takers so
> > far, so I thought I'd widen the audience to newsgroups.

>
> > 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 ping6
> > ipv6.google.com and on Wireshark see the IPv6 echo request and reply
> > on sit0. Basde on this test, I believe I should be able to see the
> > traffic from my program by monitoring sit0 with Wireshark.

>
> > 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.

>
> When you don't see any actual I/O, it's a good idea to run strace(1) on
> your binary to see what system calls it makes. Possibly ltrace(1) too.
>
> Do you need this to work, or is it just for fun? Generating ICMP
> traffic is a bit hard -- and not particularly useful.
>
> /Jorgen
>
> --
> * // Jorgen Grahn <grahn@ *Oo *o. * . * * .
> \X/ * * snipabacken.se> * O *o * .


Thanks. I will check out strace/ltrace.

I'm trying to understand how to send IPv6 packets (TCP, UDP, and ICMP)
where I'm able to modify all values in the IP header. As such, I
cannot use ancillary data methods which don't allow all values to be
modified (I miss good old HDRINCL). I don't care about the MAC
addresses however, so I'm not interested in SOCK_RAW. I'm starting
with a "simple" ping as a starting point. I figure if I can get that
to work, I can then work on other protocols. I don't need to receive
these packets (yet). That's for future investigations.

Dave
 
Reply With Quote
 
Richard Kettlewell
Guest
Posts: n/a

 
      11-02-2011, 04:37 PM
J G Miller <(E-Mail Removed)> writes:
> P D Buchan wrote:


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

>
> Dumb question -- is a name allowed there?


Yes, you pass a name to getaddrinfo(). Copying it into a buffer first
is pointless though.

--
http://www.greenend.org.uk/rjk/
 
Reply With Quote
 
Richard Kettlewell
Guest
Posts: n/a

 
      11-02-2011, 07:05 PM
(E-Mail Removed) writes:
> This is a question I posted on linuxquestions.org, but no takers so
> far, so I thought I'd widen the audience to newsgroups.
>
> 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 ping6
> ipv6.google.com and on Wireshark see the IPv6 echo request and reply
> on sit0. Basde on this test, I believe I should be able to see the
> traffic from my program by monitoring sit0 with Wireshark.
>
> 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,


I don't believe that. Or rather, I believe it doesn't report any
errors, but that's because your code does not check for them, rather
than because none occur.

> but nothing shows up on Wireshark on sit0. In fact, if I ask Wireshark
> to monitor all interfaces, I still see no IPv6 traffic.


You have at least two bugs:
(i) The destination address for AF_PACKET needs to be a sockaddr_ll.
(ii) In various places you pass the size of a pointer rather than the
size of the object pointed to.

A few other remarks:

> interface = (char *) malloc (10 * sizeof (char));


char (and unsigned char) always have size 1, by definition. There
is no point whatsoever in multiplying by sizeof(char).

malloc() returns "void *" which can be implicitly converted to a pointer
to any object type. Including the cast adds nothing and can actually
hide problems.

Moreover there is actually no point in most of the memory allocation
you're doing. For the strings, just use the string literal. For packet
assembly, the use of malloc() does get you alignment, though a union
could be used to achieve this too. It's really the pointless use of
malloc that has led you into (ii) above.

> memset (payload, 0, sizeof (payload));
> memset (interface, 0, 10);
> memset (src, 0, 39);
> memset (target, 0, 80);


These are pointless.

> free (interface);
> free (src);
> free (target);
> free (payload);
> free (packet);
>
> return (EXIT_SUCCESS);


There is no point calling free() just before exiting. The OS will clean
up for you.

--
http://www.greenend.org.uk/rjk/
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-03-2011, 12:00 AM
Richard, that's fan-bloody-tastic!
You've given me a lot to look at.
I really appreciate you taking the time.

I'll report back when I clean it all up.

Thanks again,
Dave
 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a

 
      11-03-2011, 10:36 AM
On Wed, 2011-11-02, Richard Kettlewell wrote:

[snip things I agree with]

>> free (interface);
>> free (src);
>> free (target);
>> free (payload);
>> free (packet);
>>
>> return (EXIT_SUCCESS);

>
> There is no point calling free() just before exiting. The OS will clean
> up for you.


There are no leaks, but there are two points:
- as a reminder that things were malloced and will need
to be freed if this code is reused elsewhere
- the valgrind(1) leak checker will be happier and
complain less about "possible leaks" or whatever it calls it

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
pdbuchan@yahoo.com
Guest
Posts: n/a

 
      11-03-2011, 02:28 PM
I think I need clarification on sockaddr_ll:

According to man 7 packet:

[quote]
When you send packets it is enough to specify:

sll_family,
sll_addr,
sll_halen, and
sll_ifindex.

The other fields should be 0.
[end quote]

But ssl_addr is the "physical layer address", which I interpret to be
the destination MAC address. Is that correct?

The thing is, I don't want to have to specify the destination MAC
address, which is why I used SOCK_DGRAM instead of SOCK_RAW.

Perhaps I misunderstood this portion of man 7 packet:

[quote]
SOCK_DGRAM operates on a slightly higher level. ... 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.
[end quote]

Reading that now makes me think I DO still have to specify the
destination MAC address. Which if I understand it, means I need to do
neighbor solicitation and a whole bunch of stuff, most likely using
pcap.

Comments?

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
Some C language examples of linux raw sockets for IPv4 and IPv6 pdbuchan@yahoo.com Linux Networking 0 12-17-2011 03:30 AM
Ping IPv6? Cork Soaker Linux Networking 5 06-03-2008 10:14 PM
Reliability of SOCK_DGRAM Unix-Domain sockets Kai Hergenroether Linux Networking 0 09-25-2006 09:52 AM
Linux host PC(ipv6) can't ping with embedded board(ipv6) with ping6 sangsu Linux Networking 2 10-11-2005 09:06 AM
Can't ping IPv6 domains Corwin Burgess Linux Networking 3 07-07-2004 06:26 PM



1 2 3 4 5 6 7 8 9 10 11