Date: Fri, 08 Aug 2014 05:37:57 +0900 (JST) From: Hiroki Sato <hrs@FreeBSD.org> To: fernando@gont.com.ar Cc: freebsd-net@freebsd.org Subject: Re: Routing IPv6 packets towards oneself with routing sockets? Message-ID: <20140808.053757.1725805140861121363.hrs@allbsd.org> In-Reply-To: <53E35DA7.4020800@gont.com.ar> <53E2B586.3080700@gont.com.ar> References: <53E2B586.3080700@gont.com.ar> <20140807.192403.845244220459089560.hrs@allbsd.org> <53E35DA7.4020800@gont.com.ar>
next in thread | previous in thread | raw e-mail | index | archive | help
----Security_Multipart0(Fri_Aug__8_05_37_57_2014_299)-- Content-Type: Multipart/Mixed; boundary="--Next_Part(Fri_Aug__8_05_37_57_2014_990)--" Content-Transfer-Encoding: 7bit ----Next_Part(Fri_Aug__8_05_37_57_2014_990)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Fernando Gont <fernando@gont.com.ar> wrote in <53E35DA7.4020800@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(Fri_Aug__8_05_37_57_2014_990)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="bsd-lookup-simple.c.diff" --- bsd-lookup-simple.c.orig 2014-08-08 04:47:55.000000000 +0900 +++ bsd-lookup-simple.c 2014-08-08 04:47:55.000000000 +0900 @@ -38,7 +38,12 @@ #endif #ifndef SA_SIZE -#if defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined(__APPLE__) +#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) : \ @@ -78,7 +83,11 @@ #endif #endif +#ifdef IFNAMSIZ +#define IFACE_LENGTH IFNAMSIZ +#else #define IFACE_LENGTH 255 +#endif unsigned int print_ipv6_address(char *s, struct in6_addr *); @@ -104,6 +113,9 @@ 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; @@ -139,7 +151,7 @@ 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; + rtm->rtm_addrs= RTA_DST | RTA_IFP; rtm->rtm_pid= pid= getpid(); rtm->rtm_seq= seq= random(); @@ -181,18 +193,27 @@ }while( rtm->rtm_type != RTM_GET || rtm->rtm_pid != pid || rtm->rtm_seq != seq); /* The rt_msghdr{} structure is followed by sockaddr structures */ - sa= (struct sockaddr *) (rtm+1); + 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(rtm->rtm_addrs & RTA_DST){ if(debug_f){ puts("DEBUG: RTA_DST was set"); print_ipv6_address("RTA_DST: ", &( ((struct sockaddr_in6 *)sa)->sin6_addr)); } - - SA_NEXT(sa); } - if(rtm->rtm_addrs & RTA_GATEWAY){ + 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)); @@ -207,20 +228,29 @@ print_ipv6_address("DEBUG: RTA_GATEWAY: ", &nhaddr); } } - else if(sa->sa_family == AF_LINK){ - sockpptr = (struct sockaddr_dl *) (sa); + } + + 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(if_indextoname(nhifindex, nhiface) == NULL){ - puts("Error calling if_indextoname() from sel_next_hop()"); + 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_GATEWAY: Name: %s, Index: %d\n", nhiface, nhifindex); - } - + if(debug_f) + printf("DEBUG: RTA_IFP: Name: %s, Index: %d\n", nhiface, nhifindex); onlink_f=TRUE; } } ----Next_Part(Fri_Aug__8_05_37_57_2014_990)-- Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="bsd-lookup-simple.c" /* * 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(Fri_Aug__8_05_37_57_2014_990)---- ----Security_Multipart0(Fri_Aug__8_05_37_57_2014_299)-- Content-Type: application/pgp-signature Content-Transfer-Encoding: 7bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iEYEABECAAYFAlPj46UACgkQTyzT2CeTzy0+dgCfSM+VFavDSY1XB9jAICfmoK0o tn0AoMoDbvE5v/Fy460jYm5XUkHzzIk6 =fVr9 -----END PGP SIGNATURE----- ----Security_Multipart0(Fri_Aug__8_05_37_57_2014_299)----
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20140808.053757.1725805140861121363.hrs>