Subject: Re: default route and private networks
To: Manuel Bouyer <bouyer@antioche.lip6.fr>
From: David Young <dyoung@pobox.com>
List: tech-net
Date: 04/13/2005 12:29:10
--rJwd6BRFiFCcLxzm
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Wed, Apr 13, 2005 at 07:14:05PM +0200, Manuel Bouyer wrote:
> Hi,
> I have the following setup:
>                 _______________                     _________
> --- public IP | NetBSD 3.0 box |10.1.1.1----10.1.1.2| Router|Internet-------
>                 ---------------                     ---------
> 
> That is, the NetBSD box has a public IP, on a public IP network, but it's
> not the network connecting it to the default router (its default route is
> 10.1.1.2).
> This works mostly fine, exept that for outgoing connections from the NetBSD
> box, the source address is 10.1.1.1.
> Is there a way to force it to use the public IP for outgoing connections ?
> I tried playing with the -ifa modifier to route, without results.

Manuel,

This patch (previously sent with subject 'rfc: link-local ipv4 addrs
and source selection') can be adapted to do what you like.  Actually,
I think that the the IPv4 address selection should resemble IPv6 address
selection, where the "scope" of the destination address is considered
(global, link- or site-local), and a source address with the same scope
is preferred.  IPv4 should likewise prefer a private sources (192.168/16,
10/8, ...) when the destination is private, a link-local (169.254/16)
for link-local destinations, and global source for a global destination.
Make sense?

Dave

-- 
David Young             OJC Technologies
dyoung@ojctech.com      Urbana, IL * (217) 278-3933

--rJwd6BRFiFCcLxzm
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=in_getifa-patch

Index: sys/net/if.h
===================================================================
RCS file: /cvsroot/src/sys/net/if.h,v
retrieving revision 1.100
diff -u -r1.100 if.h
--- sys/net/if.h	24 Jan 2005 21:25:09 -0000	1.100
+++ sys/net/if.h	19 Feb 2005 07:02:49 -0000
@@ -129,6 +129,7 @@
 struct rtentry;
 struct socket;
 struct ether_header;
+struct ifaddr;
 struct ifnet;
 struct rt_addrinfo;
 
@@ -429,6 +430,8 @@
 	u_int	ifa_flags;		/* mostly rt_flags for cloning */
 	int	ifa_refcnt;		/* count of references */
 	int	ifa_metric;		/* cost of going out this interface */
+	struct ifaddr	*(*ifa_getifa)(struct ifaddr *,
+			               const struct sockaddr *);
 };
 #define	IFA_ROUTE	RTF_UP /* 0x01 *//* route installed */
 
Index: sys/net/route.c
===================================================================
RCS file: /cvsroot/src/sys/net/route.c,v
retrieving revision 1.64
diff -u -r1.64 route.c
--- sys/net/route.c	23 Jan 2005 18:41:56 -0000	1.64
+++ sys/net/route.c	19 Feb 2005 07:02:49 -0000
@@ -509,7 +509,6 @@
 rt_getifa(struct rt_addrinfo *info)
 {
 	struct ifaddr *ifa;
-	int error = 0;
 
 	/*
 	 * ifp may be specified by sockaddr_dl when protocol address
@@ -533,12 +532,13 @@
 		else if (sa != NULL)
 			info->rti_ifa = ifa_ifwithroute(flags, sa, sa);
 	}
-	if ((ifa = info->rti_ifa) != NULL) {
-		if (info->rti_ifp == NULL)
-			info->rti_ifp = ifa->ifa_ifp;
-	} else
-		error = ENETUNREACH;
-	return (error);
+	if ((ifa = info->rti_ifa) == NULL)
+		return ENETUNREACH;
+	if (ifa->ifa_getifa != NULL)
+		info->rti_ifa = ifa = (*ifa->ifa_getifa)(ifa, dst);
+	if (info->rti_ifp == NULL)
+		info->rti_ifp = ifa->ifa_ifp;
+	return 0;
 }
 
 int
Index: sys/netinet/in.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.c,v
retrieving revision 1.101
diff -u -r1.101 in.c
--- sys/netinet/in.c	24 Jan 2005 21:25:09 -0000	1.101
+++ sys/netinet/in.c	19 Feb 2005 07:02:51 -0000
@@ -139,7 +139,7 @@
 static void in_len2mask __P((struct in_addr *, u_int));
 static int in_lifaddr_ioctl __P((struct socket *, u_long, caddr_t,
 	struct ifnet *, struct proc *));
-
+static struct ifaddr *in_getifa(struct ifaddr *, const struct sockaddr *);
 static int in_addprefix __P((struct in_ifaddr *, int));
 static int in_scrubprefix __P((struct in_ifaddr *));
 
@@ -402,6 +402,7 @@
 			ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
 			ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
 			ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
+			ia->ia_ifa.ifa_getifa = in_getifa;
 			ia->ia_sockmask.sin_len = 8;
 			if (ifp->if_flags & IFF_BROADCAST) {
 				ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
@@ -947,6 +948,8 @@
 		/*
 		 * if we got a matching prefix route inserted by other
 		 * interface address, we don't need to bother
+		 *
+		 * XXX RADIX_MPATH implications here? -dcy
 		 */
 		if (ia->ia_flags & IFA_ROUTE)
 			return 0;
@@ -1157,4 +1160,37 @@
 	}
 	splx(s);
 }
+
+static struct ifaddr *
+in_getifa(struct ifaddr *ifa, const struct sockaddr *dst)
+{
+	int linklocal;
+	struct ifaddr *alt_ifa;
+
+#define	LOGIC_EQ(__a, __b)	(!(__a) == !(__b))
+#define	SIN_ANY_LOCAL(__sin)	IN_ANY_LOCAL((__sin)->sin_addr.s_addr)
+
+	if (ifa->ifa_addr->sa_family != AF_INET)	/* XXX impossible? */
+		return ifa;
+	if (dst != NULL && dst->sa_family != AF_INET)	/* does occur */
+		return ifa;
+
+	linklocal = (dst != NULL && SIN_ANY_LOCAL(satosin(dst)));
+
+	if (LOGIC_EQ(linklocal, SIN_ANY_LOCAL(satosin(ifa->ifa_addr))))
+		return ifa;
+
+	TAILQ_FOREACH(alt_ifa, &ifa->ifa_ifp->if_addrlist, ifa_list) {
+		const struct sockaddr *alt_sa = alt_ifa->ifa_addr;
+		if (alt_ifa == ifa)
+			continue;
+		if (alt_sa->sa_family != AF_INET)
+			continue;
+		if (LOGIC_EQ(linklocal, SIN_ANY_LOCAL(satosin(alt_sa))))
+			return alt_ifa;
+	}
+#undef	SIN_ANY_LOCAL
+#undef	LOGIC_EQ
+	return ifa;
+}
 #endif
Index: sys/netinet/in.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/in.h,v
retrieving revision 1.69
diff -u -r1.69 in.h
--- sys/netinet/in.h	15 Dec 2004 04:25:19 -0000	1.69
+++ sys/netinet/in.h	19 Feb 2005 07:02:51 -0000
@@ -168,6 +168,9 @@
 #define	__IPADDR(x)	((uint32_t)(x))
 #endif
 
+#define IN_LINKLOCAL(i)	(((uint32_t)(i) & __IPADDR(0xffff0000)) == \
+			 __IPADDR(0xa9fe0000))
+
 #define	IN_CLASSA(i)		(((uint32_t)(i) & __IPADDR(0x80000000)) == \
 				 __IPADDR(0x00000000))
 #define	IN_CLASSA_NET		__IPADDR(0xff000000)
@@ -204,6 +207,8 @@
 #define	IN_LOCAL_GROUP(i)	(((uint32_t)(i) & __IPADDR(0xffffff00)) == \
 				 __IPADDR(0xe0000000))
 
+#define	IN_ANY_LOCAL(i)		(IN_LINKLOCAL(i) || IN_LOCAL_GROUP(i))
+
 #define	INADDR_ANY		__IPADDR(0x00000000)
 #define	INADDR_LOOPBACK		__IPADDR(0x7f000001)
 #define	INADDR_BROADCAST	__IPADDR(0xffffffff)	/* must be masked */
Index: sys/netinet/in_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
retrieving revision 1.96
diff -u -r1.96 in_pcb.c
--- sys/netinet/in_pcb.c	29 Sep 2004 21:30:00 -0000	1.96
+++ sys/netinet/in_pcb.c	19 Feb 2005 07:02:52 -0000
@@ -1013,5 +1013,6 @@
 			}
 		}
 	}
+	ia = ifatoia((*ia->ia_ifa.ifa_getifa)(&ia->ia_ifa, sintosa(sin)));
 	return satosin(&ia->ia_addr);
 }

--rJwd6BRFiFCcLxzm--