Subject: Re: independant setting of ifp/ifa on routes
To: Christian E. Hopps <chopps@merit.edu>
From: Bill Sommerfeld <sommerfeld@orchard.arlington.ma.us>
List: tech-net
Date: 06/25/1999 12:45:07
> I'd like to use this though.  GateD currently uses the gateway router
> (next hop) internally to determine which interface the route is attached
> to.  I believe this echos whats done in the kernel.

Yep, at least until I started meddling with things..

> If I understand you change I can now specifically state which interface
> the route should be bound to regardless of the gateway.  Clearly
> for RTA_IFP sockaddr_dl is the right way to go.  If you can make
> this change it would please me greatly. :)

Well, that was easy to do...

Given the limits of link_addr(), you need to put a colon after the
interface name in the current code:

i.e.:

	route add -if foo0: ....

but that will be trivial to clean up in userland code.

> This would be very nice.  I'm guessing that the work involves removing
> the need for a gateway to be specified, as long as the interface is
> given.

You still need a next-hop-gateway address for non-point-to-point
subnets.


					- Bill

changes to /sbin/route:

Index: route.c
===================================================================
RCS file: /cvsroot/basesrc/sbin/route/route.c,v
retrieving revision 1.30
diff -u -r1.30 route.c
--- route.c	1998/10/23 05:36:42	1.30
+++ route.c	1999/06/25 16:40:23
@@ -662,6 +662,13 @@
 			case K_STATIC:
 				flags |= RTF_STATIC;
 				break;
+			case K_IF:
+				if (!--argc)
+					usage(1+*argv);
+				rtm_addrs |= RTA_IFP;
+				so_ifp.sdl.sdl_len = sizeof(so_ifp.sdl);
+				link_addr(*++argv, &so_ifp.sdl);
+				break;
 			case K_IFA:
 				if (!--argc)
 					usage(1+*argv);
@@ -1303,7 +1310,7 @@
 	struct rt_msghdr *rtm;
 	int msglen;
 {
-	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL;
+	struct sockaddr *dst = NULL, *gate = NULL, *mask = NULL, *ifa = NULL;
 	struct sockaddr_dl *ifp = NULL;
 	struct sockaddr *sa;
 	char *cp;
@@ -1344,6 +1351,9 @@
 					   ((struct sockaddr_dl *)sa)->sdl_nlen)
 						ifp = (struct sockaddr_dl *)sa;
 					break;
+				case RTA_IFA:
+					ifa = sa;
+					break;
 				}
 				ADVANCE(cp, sa);
 			}
@@ -1360,6 +1370,8 @@
 	}
 	if (gate && rtm->rtm_flags & RTF_GATEWAY)
 		(void)printf("    gateway: %s\n", routename(gate));
+	if (ifa)
+		(void)printf("     ifaddr: %s\n", routename(ifa));
 	if (ifp)
 		(void)printf("  interface: %.*s\n",
 		    ifp->sdl_nlen, ifp->sdl_data);
Index: keywords.sh
===================================================================
RCS file: /cvsroot/basesrc/sbin/route/keywords.sh,v
retrieving revision 1.4
diff -u -r1.4 keywords.sh
--- keywords.sh	1997/04/03 02:35:49	1.4
+++ keywords.sh	1999/06/25 16:40:23
@@ -22,6 +22,7 @@
 get
 host
 hopcount
+if
 iface
 interface
 ifa


Kernel changes:

Index: route.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/route.c,v
retrieving revision 1.24
diff -u -r1.24 route.c
--- route.c	1998/12/27 18:27:48	1.24
+++ route.c	1999/06/25 16:42:26
@@ -155,8 +155,8 @@
 	    ((rn->rn_flags & RNF_ROOT) == 0)) {
 		newrt = rt = (struct rtentry *)rn;
 		if (report && (rt->rt_flags & RTF_CLONING)) {
-			err = rtrequest(RTM_RESOLVE, dst, SA(0),
-					      SA(0), 0, &newrt);
+			err = rtrequest(RTM_RESOLVE, dst, SA(0), SA(0),
+					      SA(0), SA(0), 0, &newrt);
 			if (err) {
 				newrt = rt;
 				rt->rt_refcnt++;
@@ -278,7 +278,7 @@
 		create:
 			flags |=  RTF_GATEWAY | RTF_DYNAMIC;
 			error = rtrequest((int)RTM_ADD, dst, gateway,
-				    netmask, flags,
+				    netmask, SA(0), SA(0), flags,
 				    (struct rtentry **)0);
 			stat = &rtstat.rts_dynamic;
 		} else {
@@ -358,8 +358,9 @@
 		struct rtentry *rt = rtalloc1(dst, 0);
 		if (rt == 0)
 			return (0);
+		ifa = rt->rt_ifa;
 		rt->rt_refcnt--;
-		if ((ifa = rt->rt_ifa) == 0)
+		if (ifa == 0)
 			return (0);
 	}
 	if (ifa->ifa_addr->sa_family != dst->sa_family) {
@@ -371,12 +372,38 @@
 	return (ifa);
 }
 
+/*
+ * find the interface we'd use to talk to "gateway"
+ */
+struct ifnet *
+ifp_ifwithroute (flags, dst, gateway)
+	int flags;
+	struct sockaddr *dst, *gateway;
+{
+	struct rtentry *rt;
+	struct ifnet *ifp;
+	
+	rt = rtalloc1(gateway, 0);
+	if (rt == 0)
+		rt = rtalloc1(dst, 0);
+
+	if (rt == 0)
+		return 0;
+
+	ifp = rt->rt_ifp;
+	rt->rt_refcnt--;
+
+	return ifp;
+}
+
+	
+
 #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
 
 int
-rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
+rtrequest(req, dst, gateway, netmask, ifaaddr, ifpaddr, flags, ret_nrt)
 	int req, flags;
-	struct sockaddr *dst, *gateway, *netmask;
+	struct sockaddr *dst, *gateway, *netmask, *ifaaddr, *ifpaddr;
 	struct rtentry **ret_nrt;
 {
 	int s = splsoftnet(); int error = 0;
@@ -384,6 +411,7 @@
 	register struct radix_node *rn;
 	register struct radix_node_head *rnh;
 	struct ifaddr *ifa;
+	struct ifnet *ifp;
 	struct sockaddr *ndst;
 #define senderr(x) { error = x ; goto bad; }
 
@@ -418,6 +446,7 @@
 		if (ret_nrt == 0 || (rt = *ret_nrt) == 0)
 			senderr(EINVAL);
 		ifa = rt->rt_ifa;
+		ifp = rt->rt_ifp;
 		flags = rt->rt_flags & ~RTF_CLONING;
 		gateway = rt->rt_gateway;
 		if ((netmask = rt->rt_genmask) == 0)
@@ -425,8 +454,35 @@
 		goto makeroute;
 
 	case RTM_ADD:
-		if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
+		/*
+		 * ifa/ifp selection:
+		 *
+		 * if ifp specified, use that to identify the interface.
+		 * otherwise use ifa_ifwithroute to pick it.
+		 *
+		 * if ifa specified, use that as ifa, otherwise use
+		 * ifa_withroute to pick one.
+		 *
+		 * Note that ifa doesn't have to be on ifp's interface.
+		 */
+		if (ifaaddr)
+			ifa = ifa_ifwithaddr(ifaaddr);
+		else
+			ifa = ifa_ifwithroute(flags, dst, gateway);
+
+		if (!ifa)
 			senderr(ENETUNREACH);
+
+		if (ifpaddr)
+			ifp = ifp_ifwithaddr(ifpaddr);
+		else if (!ifaaddr)
+			ifp = ifa->ifa_ifp;
+		else 
+			ifp = ifp_ifwithroute(flags, dst, gateway);
+		 
+		if (!ifp)
+			senderr(ENETUNREACH);
+
 	makeroute:
 		rt = pool_get(&rtentry_pool, PR_NOWAIT);
 		if (rt == 0)
@@ -454,7 +510,7 @@
 		}
 		ifa->ifa_refcnt++;
 		rt->rt_ifa = ifa;
-		rt->rt_ifp = ifa->ifa_ifp;
+		rt->rt_ifp = ifp;
 		if (req == RTM_RESOLVE)
 			rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
 		if (ifa->ifa_rtrequest)
@@ -559,7 +615,7 @@
 		}
 	}
 	error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
-			flags | ifa->ifa_flags, &nrt);
+	    SA(0), SA(0), flags | ifa->ifa_flags, &nrt);
 	if (m)
 		(void) m_free(m);
 	if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) {
@@ -606,7 +662,7 @@
 	} else {						\
 		rtrequest((int) RTM_DELETE,			\
 			  (struct sockaddr *)rt_key(r->rtt_rt),	\
-			  0, 0, 0, 0);				\
+			  0, 0, 0, 0, 0, 0);			\
 	}							\
 }
 
Index: route.h
===================================================================
RCS file: /cvsroot/syssrc/sys/net/route.h,v
retrieving revision 1.17
diff -u -r1.17 route.h
--- route.h	1998/12/27 18:27:48	1.17
+++ route.h	1999/06/25 16:42:27
@@ -303,7 +303,8 @@
 int	 rtioctl __P((u_long, caddr_t, struct proc *));
 void	 rtredirect __P((struct sockaddr *, struct sockaddr *,
 	    struct sockaddr *, int, struct sockaddr *, struct rtentry **));
-int	 rtrequest __P((int, struct sockaddr *,
-	    struct sockaddr *, struct sockaddr *, int, struct rtentry **));
+int	 rtrequest __P((int, struct sockaddr *, struct sockaddr *,
+	    struct sockaddr *, struct sockaddr *, struct sockaddr *,
+            int, struct rtentry **));
 #endif /* _KERNEL */
 #endif /* _NET_ROUTE_H_ */
Index: if.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/if.c,v
retrieving revision 1.48
diff -u -r1.48 if.c
--- if.c	1998/12/10 15:10:48	1.48
+++ if.c	1999/06/25 16:42:29
@@ -162,6 +162,43 @@
 	}
 	return ((struct ifaddr *)0);
 }
+
+/*
+ * Locate an interface based on a complete interface address.
+ */
+/*ARGSUSED*/
+struct ifnet *
+ifp_ifwithaddr(addr)
+	register struct sockaddr *addr;
+{
+	register struct ifnet *ifp;
+	register struct ifaddr *ifa;
+
+	if (addr->sa_family == AF_LINK) {
+		char tmp[256];	/* XXX assumes sdl->nlen is u_char */
+		struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
+		if (sdl->sdl_nlen == 0)
+			return ((struct ifnet *)0);
+		memcpy(tmp, sdl->sdl_data, sdl->sdl_nlen);
+		tmp[sdl->sdl_nlen] = 0;
+		return ifunit(tmp);
+	}
+	
+#define	equal(a1, a2) \
+  (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
+	for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
+	    for (ifa = ifp->if_addrlist.tqh_first; ifa != 0; ifa = ifa->ifa_list.tqe_next) {
+		if (ifa->ifa_addr->sa_family != addr->sa_family)
+			continue;
+		if (equal(addr, ifa->ifa_addr))
+			return (ifp);
+		if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
+		    equal(ifa->ifa_broadaddr, addr))
+			return (ifp);
+	}
+	return ((struct ifnet *)0);
+}
+
 /*
  * Locate the point to point interface with a given destination address.
  */
Index: rtsock.c
===================================================================
RCS file: /cvsroot/syssrc/sys/net/rtsock.c,v
retrieving revision 1.29
diff -u -r1.29 rtsock.c
--- rtsock.c	1999/04/02 17:22:21	1.29
+++ rtsock.c	1999/06/25 16:42:29
@@ -234,6 +234,8 @@
 		if (gate == 0)
 			senderr(EINVAL);
 		error = rtrequest(RTM_ADD, dst, gate, netmask,
+		    (rtm->rtm_addrs & RTA_IFA) ? ifaaddr : 0,
+		    (rtm->rtm_addrs & RTA_IFP) ? ifpaddr : 0,		    
 		    rtm->rtm_flags, &saved_nrt);
 		if (error == 0 && saved_nrt) {
 			rt_setmetrics(rtm->rtm_inits,
@@ -244,7 +246,7 @@
 		break;
 
 	case RTM_DELETE:
-		error = rtrequest(RTM_DELETE, dst, gate, netmask,
+		error = rtrequest(RTM_DELETE, dst, gate, netmask, 0, 0,
 		    rtm->rtm_flags, &saved_nrt);
 		if (error == 0) {
 			(rt = saved_nrt)->rt_refcnt++;