Skip site navigation (1)Skip section navigation (2)
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>