Reputation: 181
I am currently trying to create raw ICMPv6 packet in C. I only found IPv4 examples that works well and I don't see what I do wrong with IPv6.
What I know so far :
I looked over an old mailing-list post to discover I needed to set some variables in in6_addr (→ error 22), but besides they are using :
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
On the BCP38 project, they use LIBNET apparently. I prefer avoid librairies if I can do with sockets. In that case, how the calls are made to the "networking API" if not through socket.h.
I read here that IP_HDRINCL has no equivalent in IPv6. (but why ?)
The following code, is sending an ICMPv6 packet probably due to IPPROTO_ICMPV6 but with kernel-added headers and quite bad packet content... (with a wrong destination address, I didn't fix yet some endianness issue). It worked in IPv6. When I use IPPROTO_RAW, the packet is simply not sent...
Any idea ? Thanks in advance
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct ipv6header {
unsigned char priority:4, version:4;
unsigned char flow[3];
unsigned short int length;
unsigned char nexthdr;
unsigned char hoplimit;
unsigned int saddr[4];
unsigned int daddr[4];
};
struct icmpv6header {
unsigned char type;
unsigned char code;
unsigned short int chk_sum;
unsigned int body;
};
int main()
{
char* packet = (char*) malloc(sizeof(struct ipv6header)+sizeof(struct icmpv6header));
struct ipv6header* ip = (struct ipv6header*) packet;
struct icmpv6header* icmp = (struct icmpv6header*) (packet+sizeof(struct ipv6header));
icmp->type = 128;
icmp->code = 0;
icmp->chk_sum = (0x6a13);
icmp->body = htonl(1234);
ip->version = 6;
ip->priority = 0;
(ip->flow)[0] = 0;
(ip->flow)[1] = 0;
(ip->flow)[2] = 0;
ip->length = ((unsigned short int) sizeof(struct icmpv6header));
ip->nexthdr = 58;
ip->hoplimit = 255;
struct sockaddr_in6 remote;
remote.sin6_family = AF_INET6;
remote.sin6_port = 0;
remote.sin6_flowinfo = 0;
remote.sin6_scope_id = 0;
inet_pton(AF_INET6, "2001:470:x:x:y:y:y:dd7b", &(remote.sin6_addr));
inet_pton(AF_INET6, "2001:470:x:x:bee:bee:bee:bee", &(ip->saddr));
inet_pton(AF_INET6, "2001:470:x:x:y:y:y:dd7b", &(ip->daddr));
int sock, optval;
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if(sock == -1)
{
printf("Error setting socket\n");
return -1;
}
int ret = setsockopt(sock, IPPROTO_IPV6, IP_HDRINCL, &optval, sizeof(int));
if(ret != 0) {
printf("Error setting options %d\n", ret);
return -1;
}
printf("Socket options done\n");
int ret = sendto(sock, packet, ip->length, 0, (struct sockaddr *) &remote, sizeof(remote));
if(ret != ip->length) {
printf("Packet not sent : %d (%d)\n",ret,errno);
return -1;
}
printf("Packet sent\n");
return 0;
}
Upvotes: 1
Views: 3638
Reputation: 81
I came across a similar issue and solved it by using IPV6_HDRINCL
instead of IP_HDRINCL
. when setting the socket options.
Upvotes: 2
Reputation: 181
Okay after few code rewrites I succeeded :
the main problem was, the packet was truncated due to a bad length when I used IPPROTO_RAW
. As a result, the system discarded it before it could reach the network.
I also changed
uint16_t daddr[8];
and
uint16_t ipdst[8] = {htons(0x2001),htons(0x470),htons(x),htons(x),htons(y),htons(y),htons(y),htons(0xdd7b)};
uint16_t ipsrc[8] = {htons(0x2001),htons(0x470),htons(x),htons(x),htons(0xbee),htons(0xbee),htons(0xbee),htons(0xbee)};
int j = 0;
for(j=0;j<=7;j++) {
remote.sin6_addr.s6_addr16[j]=ipdst[j];
ip->saddr[j]=ipsrc[j];
ip->daddr[j]=ipdst[j];
}
Thanks !
Upvotes: 0