Routing IPv6 packets towards oneself with routing sockets?
Hiroki Sato
hrs at FreeBSD.org
Thu Aug 7 20:43:43 UTC 2014
Fernando Gont <fernando at gont.com.ar> wrote
in <53E35DA7.4020800 at gont.com.ar>:
fe> Yes: <https://github.com/fgont/snippets/raw/master/bsd-lookup-simple.c>
fe>
fe> Run it as:
fe> bsd-lookup-simple -v IPV6_DEST_ADDR
Hmm, I tried and it seems it worked as expected.
"./bsd-lookup-simple -v fc00:1::1" returns RTA_DST with fc00:1::1,
and "-v fc00:1::2" returns RTA_DST with fc00:1::/64 like the following:
% netstat -nrf inet6 | grep ^fc00
fc00:1::/64 link#1 U em0
fc00:1::1 link#1 UHS lo0
% ./bsd-lookup-simple -v fc00:1::1
DEBUG: 1 SOCKET_RAW query
DEBUG: Received message
DEBUG: rtm_type: 4 (4), rtm_pid: 15079 (15079), rtm_seq: 1804289383 (1804289383)
DEBUG: RTA_DST was set
RTA_DST: fc00:1::1
DEBUG: RTA_GATEWAY was set
DEBUG: Family: 18, size 54, realsize: 56
DEBUG: sizeof(AF_LINK): 54, sizeof(AF_INET6): 28
DEBUG: RTA_GATEWAY: Name: em0, Index: 1
DEBUG: Quitted loop. onlink_f: 1, queries: 1
Outgoing interface: em0 (Index: 1)
% ./bsd-lookup-simple -v fc00:1::2
DEBUG: 1 SOCKET_RAW query
DEBUG: Received message
DEBUG: rtm_type: 4 (4), rtm_pid: 15085 (15085), rtm_seq: 1804289383 (1804289383)
DEBUG: RTA_DST was set
RTA_DST: fc00:1::
DEBUG: RTA_GATEWAY was set
DEBUG: Family: 18, size 54, realsize: 56
DEBUG: sizeof(AF_LINK): 54, sizeof(AF_INET6): 28
DEBUG: RTA_GATEWAY: Name: em0, Index: 1
DEBUG: Quitted loop. onlink_f: 1, queries: 1
Outgoing interface: em0 (Index: 1)
fe> However, whenever I lookup an entry for fc00:1::1 with routing sockets,
fe> the only entry I obtain is fc00:1::/64 (a network route) rather than
fe> fc00:1::1/128 (a host route).
Does this mean you got RTA_DST with fc00:1::/64 when
"bsd-lookup-simple -v fc00:1::1"? If so, it is very strange. What
was returned when you entered "route -n get -inet6 fc00:1::1" and "route
-n get -inet6 fc00:1::2" on your box?
Although your code assumes RTA_GATEWAY eventually returns the
outgoing interface, it is not always true. RTA_IFP should be used if
you want to look up it instead of looking up gateways until AF_LINK
is obtained. Certainly RTA_GATEWAY returns AF_LINK and you can check
sdl_index in it, but the index number is not always the same as the
actual outgoing interface (one of the examples is a host route).
A revised source file is attached. Some nits are also fixed: 1)
SA_SIZE() on MacOSX is not aligned with sizeof(long) and 2)
IFACE_LENGTH should be IFNAMSIZ.
-- Hiroki
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bsd-lookup-simple.c.diff
Type: text/x-patch
Size: 3721 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20140808/623a6b85/attachment.bin>
-------------- next part --------------
/*
* Program: bsd-routing-sockets.c
*
* Test IPv6 Routing sockets
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <pwd.h>
#define TRUE 1
#define FALSE 0
#ifdef __linux__
/* Consulting the routing table */
#define MAX_NLPAYLOAD 1024
#else
#define MAX_RTPAYLOAD 1024
#endif
#ifndef SA_SIZE
#if defined(__APPLE__)
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
((struct sockaddr *)(sa))->sa_len )
#elif defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#else
#define SA_SIZE(sa) sizeof(struct sockaddr)
#endif
#endif
#ifndef SA_NEXT
#define SA_NEXT(sa) (sa= (struct sockaddr *) ( (char *) sa + SA_SIZE(sa)))
#endif
#if defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined(__APPLE__)
#ifndef s6_addr16
#define s6_addr16 __u6_addr.__u6_addr16
#endif
#ifndef s6_addr
#define s6_addr __u6_addr.__u6_addr8
#endif
#ifndef s6_addr8
#define s6_addr8 __u6_addr.__u6_addr8
#endif
#ifndef s6_addr32
#define s6_addr32 __u6_addr.__u6_addr32
#endif
#elif defined __linux__ || ( !defined(__FreeBSD__) && defined(__FreeBSD_kernel__))
#ifndef s6_addr16
#define s6_addr16 __in6_u.__u6_addr16
#endif
#ifndef s6_addr32
#define s6_addr32 __in6_u.__u6_addr32
#endif
#endif
#ifdef IFNAMSIZ
#define IFACE_LENGTH IFNAMSIZ
#else
#define IFACE_LENGTH 255
#endif
unsigned int print_ipv6_address(char *s, struct in6_addr *);
int main(int argc, char *argv[]){
int sockfd;
pid_t pid;
int seq;
ssize_t r;
size_t ssize;
unsigned int queries=0;
char reply[MAX_RTPAYLOAD];
unsigned char nhifindex_f=0;
unsigned int nhifindex;
char nhiface[IFACE_LENGTH], pv6addr[INET6_ADDRSTRLEN];
#if defined(__APPLE__)
char aflink_f= FALSE;
#endif
struct rt_msghdr *rtm;
struct sockaddr_in6 *sin6;
struct sockaddr_dl *sockpptr;
struct sockaddr *sa;
struct sockaddr *so[RTAX_MAX];
char *cp;
int i;
void *end;
unsigned char onlink_f=FALSE, nhaddr_f=FALSE, verbose_f=TRUE, debug_f=FALSE;
struct in6_addr dstaddr, nhaddr;
if(argc < 2){
puts("usage: lookup [-v] IPV6_ADDRESS");
exit(1);
}
else if(argc > 2){
debug_f= TRUE;
}
if( (sockfd=socket(AF_ROUTE, SOCK_RAW, 0)) == -1){
if(verbose_f)
puts("Error in socket() call from sel_next_hop()");
return(EXIT_FAILURE);
}
if ( inet_pton(AF_INET6, (strlen(argv[1]) <= 2 && debug_f)?argv[2]:argv[1], &dstaddr) <= 0){
puts("inet_pton(): Target Address not valid");
exit(EXIT_FAILURE);
}
nhaddr= dstaddr;
do{
if(debug_f)
printf("DEBUG: %u SOCKET_RAW query\n", queries+1);
rtm= (struct rt_msghdr *) reply;
memset(rtm, 0, sizeof(struct rt_msghdr));
rtm->rtm_msglen= sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
rtm->rtm_version= RTM_VERSION;
rtm->rtm_type= RTM_GET;
rtm->rtm_addrs= RTA_DST | RTA_IFP;
rtm->rtm_pid= pid= getpid();
rtm->rtm_seq= seq= random();
sin6= (struct sockaddr_in6 *) (rtm + 1);
memset(sin6, 0, sizeof(struct sockaddr_in6));
sin6->sin6_len= sizeof(struct sockaddr_in6);
sin6->sin6_family= AF_INET6;
sin6->sin6_addr= nhaddr;
#if defined(__APPLE__)
if(IN6_IS_ADDR_LINKLOCAL(&nhaddr)){
aflink_f= TRUE;
}
#endif
if(write(sockfd, rtm, rtm->rtm_msglen) == -1){
if(verbose_f)
puts("write() failed. No route to the intenteded destination in the local routing table");
exit(EXIT_FAILURE);
}
do{
if( (r=read(sockfd, rtm, MAX_RTPAYLOAD)) < 0){
if(verbose_f)
puts("Error in read() call from sel_next_hop()");
exit(EXIT_FAILURE);
}
/* The size of the structure should be at least sizof(long) */
end= (char *) rtm + r - (sizeof(long) -1);
if(debug_f){
puts("DEBUG: Received message");
printf("DEBUG: rtm_type: %d (%d), rtm_pid: %d (%d), rtm_seq: %d (%d)\n", rtm->rtm_type, RTM_GET, rtm->rtm_pid, pid, \
rtm->rtm_seq, seq);
}
}while( rtm->rtm_type != RTM_GET || rtm->rtm_pid != pid || rtm->rtm_seq != seq);
/* The rt_msghdr{} structure is followed by sockaddr structures */
cp = (char *)(rtm + 1);
for (i = 0; i < RTAX_MAX; i++) {
if (rtm->rtm_addrs & (1 << i)) {
so[i] = (struct sockaddr *)cp;
cp += SA_SIZE((struct sockaddr *)cp);
} else
so[i] = NULL;
}
if(so[RTAX_DST] != NULL) {
sa = (struct sockaddr *)so[RTAX_DST];
if(debug_f){
puts("DEBUG: RTA_DST was set");
print_ipv6_address("RTA_DST: ", &( ((struct sockaddr_in6 *)sa)->sin6_addr));
}
}
if(so[RTAX_GATEWAY] != NULL){
sa = (struct sockaddr *)so[RTAX_GATEWAY];
if(debug_f){
puts("DEBUG: RTA_GATEWAY was set");
printf("DEBUG: Family: %d, size %d, realsize: %lu\n", sa->sa_family, sa->sa_len, SA_SIZE(sa));
printf("DEBUG: sizeof(AF_LINK): %lu, sizeof(AF_INET6): %lu\n", sizeof(struct sockaddr_dl), sizeof(struct sockaddr_in6));
}
if(sa->sa_family == AF_INET6){
nhaddr= ((struct sockaddr_in6 *) sa)->sin6_addr;
nhaddr_f=TRUE;
if(debug_f){
print_ipv6_address("DEBUG: RTA_GATEWAY: ", &nhaddr);
}
}
}
if (so[RTAX_IFP] != NULL) {
sa = (struct sockaddr *)so[RTAX_IFP];
sockpptr = (struct sockaddr_dl *) (sa);
if(debug_f){
puts("DEBUG: RTA_IFP was set");
printf("DEBUG: Family: %d, size %d, realsize: %lu\n", sa->sa_family, sa->sa_len, SA_SIZE(sa));
}
if (sockpptr->sdl_family == AF_LINK) {
nhifindex= sockpptr->sdl_index;
nhifindex_f=TRUE;
if (sockpptr->sdl_nlen >= sizeof(nhiface)) {
puts("ifname is too long.");
return(EXIT_FAILURE);
}
strncpy(nhiface, sockpptr->sdl_data,
sockpptr->sdl_nlen);
nhiface[sizeof(nhiface) - 1] = '\0';
if(debug_f)
printf("DEBUG: RTA_IFP: Name: %s, Index: %d\n", nhiface, nhifindex);
onlink_f=TRUE;
}
}
queries++;
}while(!onlink_f && queries < 10);
if(debug_f)
printf("DEBUG: Quitted loop. onlink_f: %d, queries: %d\n", onlink_f, queries);
close(sockfd);
if(nhifindex_f){
if(IN6_IS_ADDR_LINKLOCAL(&nhaddr)){
/* BSDs store the interface index in s6_addr16[1], so we must clear it */
nhaddr.s6_addr16[1] =0;
nhaddr.s6_addr16[2] =0;
nhaddr.s6_addr16[3] =0;
}
if(nhaddr_f){
if(inet_ntop(AF_INET6, &nhaddr, pv6addr, sizeof(pv6addr)) == NULL){
puts("inet_ntop(): Error converting IPv6 Address to presentation format");
exit(EXIT_FAILURE);
}
printf("Next-Hop address: %s\n", pv6addr);
}
printf("Outgoing interface: %s (Index: %d)\n", nhiface, nhifindex);
return(EXIT_SUCCESS);
}
else{
return(EXIT_FAILURE);
}
}
/*
* Function: print_ipv6_addresss()
*
* Prints an IPv6 address with a legend
*/
unsigned int print_ipv6_address(char *s, struct in6_addr *v6addr){
char pv6addr[INET6_ADDRSTRLEN];
if(inet_ntop(AF_INET6, v6addr, pv6addr, sizeof(pv6addr)) == NULL){
puts("inet_ntop(): Error converting IPv6 Source Address to presentation format");
return(EXIT_FAILURE);
}
printf("%s%s\n", s, pv6addr);
return(EXIT_SUCCESS);
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20140808/623a6b85/attachment.sig>
More information about the freebsd-net
mailing list