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++;