tables.c revision 11820
11556Srgrimes/*
21556Srgrimes * Copyright (c) 1985, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Copyright (c) 1995 John Hay.  All rights reserved.
61556Srgrimes *
71556Srgrimes * Redistribution and use in source and binary forms, with or without
81556Srgrimes * modification, are permitted provided that the following conditions
91556Srgrimes * are met:
101556Srgrimes * 1. Redistributions of source code must retain the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer.
121556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
131556Srgrimes *    notice, this list of conditions and the following disclaimer in the
141556Srgrimes *    documentation and/or other materials provided with the distribution.
151556Srgrimes * 3. All advertising materials mentioning features or use of this software
161556Srgrimes *    must display the following acknowledgement:
171556Srgrimes *	This product includes software developed by the University of
181556Srgrimes *	California, Berkeley and its contributors.
191556Srgrimes * 4. Neither the name of the University nor the names of its contributors
201556Srgrimes *    may be used to endorse or promote products derived from this software
211556Srgrimes *    without specific prior written permission.
221556Srgrimes *
231556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
241556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
251556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
261556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
271556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
281556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
291556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
311556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
321556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
331556Srgrimes * SUCH DAMAGE.
341556Srgrimes *
351556Srgrimes *	$Id: tables.c,v 1.6 1995/10/11 18:57:31 jhay Exp $
361556Srgrimes */
371556Srgrimes
3836150Scharnier#ifndef lint
3936150Scharnierstatic char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 6/5/93";
4036150Scharnier#endif /* not lint */
4136150Scharnier
4250471Speter/*
431556Srgrimes * Routing Table Management Daemon
441556Srgrimes */
4517987Speter#include "defs.h"
4617987Speter#include <sys/ioctl.h>
4717987Speter#include <errno.h>
481556Srgrimes#include <stdlib.h>
491556Srgrimes#include <unistd.h>
501556Srgrimes/* XXX I thought that this should work! #include <sys/systm.h> */
511556Srgrimes#include <machine/cpufunc.h>
5217525Sache
5317525Sache#ifndef DEBUG
541556Srgrimes#define	DEBUG	0
551556Srgrimes#endif
561556Srgrimes
571556Srgrimes#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
581556Srgrimes
591556Srgrimesint	install = !DEBUG;		/* if 1 call kernel */
601556Srgrimesint	delete = 1;
611556Srgrimes/*
621556Srgrimes * Lookup dst in the tables for an exact match.
631556Srgrimes */
641556Srgrimesstruct rt_entry *
651556Srgrimesrtlookup(dst)
661556Srgrimes	struct sockaddr *dst;
6720425Ssteve{
6817987Speter	register struct rt_entry *rt;
6917987Speter	register struct rthash *rh;
7017987Speter	register u_int hash;
711556Srgrimes	struct afhash h;
721556Srgrimes	int doinghost = 1;
731556Srgrimes
741556Srgrimes	if (dst->sa_family >= AF_MAX)
751556Srgrimes		return (0);
761556Srgrimes	(*afswitch[dst->sa_family].af_hash)(dst, &h);
771556Srgrimes	hash = h.afh_hosthash;
781556Srgrimes	rh = &hosthash[hash & ROUTEHASHMASK];
791556Srgrimesagain:
8090111Simp	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
811556Srgrimes		if (rt->rt_hash != hash)
821556Srgrimes			continue;
831556Srgrimes		if (equal(&rt->rt_dst, dst))
841556Srgrimes			return (rt);
851556Srgrimes	}
861556Srgrimes	if (doinghost) {
8717987Speter		doinghost = 0;
881556Srgrimes		hash = h.afh_nethash;
8917987Speter		rh = &nethash[hash & ROUTEHASHMASK];
901556Srgrimes		goto again;
911556Srgrimes	}
921556Srgrimes	return (0);
931556Srgrimes}
941556Srgrimes
951556Srgrimes/*
961556Srgrimes * Find a route to dst as the kernel would.
971556Srgrimes */
981556Srgrimesstruct rt_entry *
991556Srgrimesrtfind(dst)
10020425Ssteve	struct sockaddr *dst;
1011556Srgrimes{
1021556Srgrimes	register struct rt_entry *rt;
1031556Srgrimes	register struct rthash *rh;
10420425Ssteve	register u_int hash;
10520425Ssteve	struct afhash h;
1061556Srgrimes	int af = dst->sa_family;
10717987Speter	int doinghost = 1, (*match)() = 0;
10820425Ssteve
10920425Ssteve	if (af >= AF_MAX)
11017987Speter		return (0);
11120425Ssteve	(*afswitch[af].af_hash)(dst, &h);
11220425Ssteve	hash = h.afh_hosthash;
11320425Ssteve	rh = &hosthash[hash & ROUTEHASHMASK];
11420425Ssteve
11520425Ssteveagain:
11620425Ssteve	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
11720425Ssteve		if (rt->rt_hash != hash)
11820425Ssteve			continue;
1198855Srgrimes		if (doinghost) {
1201556Srgrimes			if (equal(&rt->rt_dst, dst))
1211556Srgrimes				return (rt);
12220425Ssteve		} else {
12320425Ssteve			if (rt->rt_dst.sa_family == af &&
1241556Srgrimes			    (match != 0) &&
12520425Ssteve			    (*match)(&rt->rt_dst, dst))
12620425Ssteve				return (rt);
1271556Srgrimes		}
12820425Ssteve	}
12920425Ssteve	if (doinghost) {
13020425Ssteve		doinghost = 0;
13120425Ssteve		hash = h.afh_nethash;
1321556Srgrimes		rh = &nethash[hash & ROUTEHASHMASK];
1331556Srgrimes		match = afswitch[af].af_netmatch;
1341556Srgrimes		goto again;
1351556Srgrimes	}
13690111Simp	return (0);
13790111Simp}
13890111Simp
1391556Srgrimesvoid
1401556Srgrimesrtadd(dst, gate, metric, ticks, state)
1411556Srgrimes	struct sockaddr *dst, *gate;
1421556Srgrimes	short metric, ticks;
1431556Srgrimes	int state;
1441556Srgrimes{
1451556Srgrimes	struct afhash h;
1461556Srgrimes	register struct rt_entry *rt;
1471556Srgrimes	struct rthash *rh;
1481556Srgrimes	int af = dst->sa_family, flags;
1491556Srgrimes	u_int hash;
1501556Srgrimes
1511556Srgrimes	FIXLEN(dst);
1521556Srgrimes	FIXLEN(gate);
1531556Srgrimes	if (af >= AF_MAX)
1541556Srgrimes		return;
1551556Srgrimes	(*afswitch[af].af_hash)(dst, &h);
1561556Srgrimes	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
1571556Srgrimes	if (flags & RTF_HOST) {
1581556Srgrimes		hash = h.afh_hosthash;
1591556Srgrimes		rh = &hosthash[hash & ROUTEHASHMASK];
1601556Srgrimes	} else {
1611556Srgrimes		hash = h.afh_nethash;
1621556Srgrimes		rh = &nethash[hash & ROUTEHASHMASK];
1631556Srgrimes	}
1641556Srgrimes	rt = (struct rt_entry *)malloc(sizeof (*rt));
1651556Srgrimes	if (rt == 0)
16690111Simp		return;
16790111Simp	rt->rt_hash = hash;
1681556Srgrimes	rt->rt_dst = *dst;
1691556Srgrimes	rt->rt_router = *gate;
1701556Srgrimes	rt->rt_metric = metric;
1711556Srgrimes	rt->rt_ticks = ticks;
1721556Srgrimes	rt->rt_timer = 0;
1731556Srgrimes	rt->rt_flags = RTF_UP | flags;
1741556Srgrimes	rt->rt_state = state | RTS_CHANGED;
1751556Srgrimes	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
1761556Srgrimes	rt->rt_clone = NULL;
1771556Srgrimes	if (metric)
1781556Srgrimes		rt->rt_flags |= RTF_GATEWAY;
17920425Ssteve	insque(rt, rh);
1801556Srgrimes	TRACE_ACTION(ADD, rt);
1811556Srgrimes	/*
1821556Srgrimes	 * If the ioctl fails because the gateway is unreachable
1831556Srgrimes	 * from this host, discard the entry.  This should only
1841556Srgrimes	 * occur because of an incorrect entry in /etc/gateways.
1851556Srgrimes	 */
1861556Srgrimes	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
1871556Srgrimes		if (errno != EEXIST)
1881556Srgrimes			perror("SIOCADDRT");
1891556Srgrimes		if (errno == ENETUNREACH) {
1901556Srgrimes			TRACE_ACTION(DELETE, rt);
1911556Srgrimes			remque(rt);
1921556Srgrimes			free((char *)rt);
1931556Srgrimes		}
1941556Srgrimes	}
19520425Ssteve}
19620425Ssteve
19720425Sstevevoid
19820425Sstevertadd_clone(ort, dst, gate, metric, ticks, state)
19990111Simp	struct rt_entry *ort;
20020425Ssteve	struct sockaddr *dst, *gate;
20120425Ssteve	short metric, ticks;
20220425Ssteve	int state;
20320425Ssteve{
20420425Ssteve	struct afhash h;
20520425Ssteve	register struct rt_entry *rt;
20620425Ssteve	struct rthash *rh;
20720425Ssteve	int af = dst->sa_family, flags;
20820425Ssteve	u_int hash;
20920425Ssteve
21020425Ssteve	FIXLEN(dst);
21120425Ssteve	FIXLEN(gate);
21220425Ssteve	if (af >= AF_MAX)
21320425Ssteve		return;
21420425Ssteve	(*afswitch[af].af_hash)(dst, &h);
21520425Ssteve	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
21620425Ssteve	if (flags & RTF_HOST) {
21720425Ssteve		hash = h.afh_hosthash;
21820425Ssteve		rh = &hosthash[hash & ROUTEHASHMASK];
21920425Ssteve	} else {
22045621Scracauer		hash = h.afh_nethash;
2211556Srgrimes		rh = &nethash[hash & ROUTEHASHMASK];
2221556Srgrimes	}
2231556Srgrimes	rt = (struct rt_entry *)malloc(sizeof (*rt));
2241556Srgrimes	if (rt == 0)
22590111Simp		return;
22617987Speter	rt->rt_hash = hash;
2271556Srgrimes	rt->rt_dst = *dst;
2281556Srgrimes	rt->rt_router = *gate;
2291556Srgrimes	rt->rt_metric = metric;
2301556Srgrimes	rt->rt_ticks = ticks;
2311556Srgrimes	rt->rt_timer = 0;
2321556Srgrimes	rt->rt_flags = RTF_UP | flags;
2331556Srgrimes	rt->rt_state = state | RTS_CHANGED;
2341556Srgrimes	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
23517525Sache	rt->rt_clone = NULL;
2361556Srgrimes	rt->rt_forw = NULL;
23717525Sache	rt->rt_back = NULL;
2381556Srgrimes	if (metric)
2391556Srgrimes		rt->rt_flags |= RTF_GATEWAY;
2401556Srgrimes
2411556Srgrimes	while(ort->rt_clone != NULL)
2421556Srgrimes		ort = ort->rt_clone;
2431556Srgrimes	ort->rt_clone = rt;
2441556Srgrimes	TRACE_ACTION(ADD_CLONE, rt);
2451556Srgrimes}
2461556Srgrimes
2471556Srgrimesvoid
2481556Srgrimesrtchange(rt, gate, metric, ticks)
2491556Srgrimes	struct rt_entry *rt;
2501556Srgrimes	struct sockaddr *gate;
2511556Srgrimes	short metric, ticks;
2521556Srgrimes{
2531556Srgrimes	int doioctl = 0, metricchanged = 0;
2541556Srgrimes	struct rtuentry oldroute;
2551556Srgrimes
2561556Srgrimes	FIXLEN(gate);
2571556Srgrimes	/*
2581556Srgrimes 	 * Handling of clones.
2591556Srgrimes 	 * When the route changed and it had clones, handle it special.
2601556Srgrimes 	 * 1. If the new route is cheaper than the clone(s), free the clones.
2611556Srgrimes	 * 2. If the new route is the same cost, it may be one of the clones,
2621556Srgrimes	 *    search for it and free it.
2631556Srgrimes 	 * 3. If the new route is more expensive than the clone(s), use the
2641556Srgrimes 	 *    values of the clone(s).
2651556Srgrimes 	 */
26617525Sache	if (rt->rt_clone) {
26790111Simp		if ((ticks < rt->rt_clone->rt_ticks) ||
26890111Simp		    ((ticks == rt->rt_clone->rt_ticks) &&
26917525Sache		     (metric < rt->rt_clone->rt_metric))) {
27017525Sache			/*
27117525Sache			 * Free all clones.
27217525Sache			 */
27317525Sache			struct rt_entry *trt, *nrt;
2741556Srgrimes
27517525Sache			trt = rt->rt_clone;
27617525Sache			rt->rt_clone = NULL;
27717525Sache			while(trt) {
27817525Sache				nrt = trt->rt_clone;
27917525Sache				free((char *)trt);
28017525Sache				trt = nrt;
28117525Sache			}
28217525Sache		} else if ((ticks == rt->rt_clone->rt_ticks) &&
28317525Sache		     (metric == rt->rt_clone->rt_metric)) {
28417525Sache			struct rt_entry *prt, *trt;
28517525Sache
2861556Srgrimes			prt = rt;
2871556Srgrimes			trt = rt->rt_clone;
2881556Srgrimes
2891556Srgrimes			while(trt) {
2901556Srgrimes				if (equal(&trt->rt_router, gate)) {
2911556Srgrimes					prt->rt_clone = trt->rt_clone;
2921556Srgrimes					free(trt);
2931556Srgrimes					trt = prt->rt_clone;
2941556Srgrimes				} else {
29590111Simp					prt = trt;
29617987Speter					trt = trt->rt_clone;
2971556Srgrimes				}
2981556Srgrimes			}
29945263Scracauer		} else {
30045263Scracauer			/*
3011556Srgrimes			 * Use the values of the first clone.
3021556Srgrimes			 * Delete the corresponding clone.
3031556Srgrimes			 */
3041556Srgrimes			struct rt_entry *trt;
30520425Ssteve
3061556Srgrimes			trt = rt->rt_clone;
3071556Srgrimes			rt->rt_clone = rt->rt_clone->rt_clone;
3081556Srgrimes			metric = trt->rt_metric;
30920425Ssteve			ticks = trt->rt_ticks;
31020425Ssteve			*gate = trt->rt_router;
31120425Ssteve			free((char *)trt);
31220425Ssteve		}
3131556Srgrimes	}
3141556Srgrimes
31520425Ssteve	if (!equal(&rt->rt_router, gate))
31620425Ssteve		doioctl++;
3171556Srgrimes	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
3181556Srgrimes		metricchanged++;
31920425Ssteve	if (doioctl || metricchanged) {
32020425Ssteve		TRACE_ACTION(CHANGE FROM, rt);
32120425Ssteve		if (doioctl) {
32220425Ssteve			oldroute = rt->rt_rt;
32320425Ssteve			rt->rt_router = *gate;
3241556Srgrimes		}
3251556Srgrimes		rt->rt_metric = metric;
32617525Sache		rt->rt_ticks = ticks;
32717525Sache		if ((rt->rt_state & RTS_INTERFACE) && metric) {
32817525Sache			rt->rt_state &= ~RTS_INTERFACE;
32917525Sache			if(rt->rt_ifp)
3301556Srgrimes				syslog(LOG_ERR,
3311556Srgrimes				"changing route from interface %s (timed out)",
3321556Srgrimes				rt->rt_ifp->int_name);
3331556Srgrimes			else
3341556Srgrimes				syslog(LOG_ERR,
3351556Srgrimes				"changing route from interface ??? (timed out)");
3361556Srgrimes		}
3371556Srgrimes		if (metric)
3381556Srgrimes			rt->rt_flags |= RTF_GATEWAY;
33920425Ssteve		else
34017525Sache			rt->rt_flags &= ~RTF_GATEWAY;
3411556Srgrimes		rt->rt_state |= RTS_CHANGED;
34217525Sache		TRACE_ACTION(CHANGE TO, rt);
34317525Sache	}
34417525Sache	if (doioctl && install) {
34517525Sache#ifndef RTM_ADD
34617525Sache		if (rtioctl(ADD, &rt->rt_rt) < 0)
3471556Srgrimes		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
3481556Srgrimes		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
3491556Srgrimes		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
3501556Srgrimes		if (delete && rtioctl(DELETE, &oldroute) < 0)
3511556Srgrimes			perror("rtioctl DELETE");
3521556Srgrimes#else
3531556Srgrimes		if (delete == 0) {
3541556Srgrimes			if (rtioctl(ADD, &rt->rt_rt) >= 0)
3551556Srgrimes				return;
35690111Simp		} else {
35790111Simp			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
3581556Srgrimes				return;
3591556Srgrimes		}
3601556Srgrimes	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
3611556Srgrimes		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
3621556Srgrimes		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
3631556Srgrimes#endif
3641556Srgrimes	}
3651556Srgrimes}
3661556Srgrimes
3671556Srgrimesvoid
3681556Srgrimesrtdelete(rt)
3691556Srgrimes	struct rt_entry *rt;
3701556Srgrimes{
3711556Srgrimes
3721556Srgrimes	struct sockaddr *sa = &(rt->rt_router);
3731556Srgrimes	FIXLEN(sa);
37490111Simp	sa = &(rt->rt_dst);
37590111Simp	FIXLEN(sa);
3761556Srgrimes	if (rt->rt_clone) {
3771556Srgrimes		/*
3781556Srgrimes		 * If there is a clone we just do a rt_change to it.
3791556Srgrimes		 */
3801556Srgrimes		struct rt_entry *trt = rt->rt_clone;
3811556Srgrimes		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
3821556Srgrimes		return;
3831556Srgrimes	}
3841556Srgrimes	if (rt->rt_state & RTS_INTERFACE) {
3851556Srgrimes		if (rt->rt_ifp)
3861556Srgrimes			syslog(LOG_ERR,
3871556Srgrimes				"deleting route to interface %s (timed out)",
3881556Srgrimes				rt->rt_ifp->int_name);
3891556Srgrimes		else
3901556Srgrimes			syslog(LOG_ERR,
3911556Srgrimes				"deleting route to interface ??? (timed out)");
3921556Srgrimes	}
3931556Srgrimes	TRACE_ACTION(DELETE, rt);
3941556Srgrimes	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
3951556Srgrimes		perror("rtioctl DELETE");
3961556Srgrimes	remque(rt);
39790111Simp	free((char *)rt);
39817987Speter}
3991556Srgrimes
4001556Srgrimesvoid
4011556Srgrimesrtinit(void)
4021556Srgrimes{
4031556Srgrimes	register struct rthash *rh;
4041556Srgrimes
4051556Srgrimes	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
4061556Srgrimes		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
4071556Srgrimes	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
40817987Speter		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
40917987Speter}
4101556Srgrimesint seqno;
4111556Srgrimes
4121556Srgrimesint
4131556Srgrimesrtioctl(action, ort)
4141556Srgrimes	int action;
4151556Srgrimes	struct rtuentry *ort;
4161556Srgrimes{
4171556Srgrimes#ifndef RTM_ADD
4181556Srgrimes	if (install == 0)
4191556Srgrimes		return (errno = 0);
4201556Srgrimes
4211556Srgrimes	ort->rtu_rtflags = ort->rtu_flags;
4221556Srgrimes
4231556Srgrimes	switch (action) {
4241556Srgrimes
42590111Simp	case ADD:
42690111Simp		return (ioctl(s, SIOCADDRT, (char *)ort));
4271556Srgrimes
4281556Srgrimes	case DELETE:
4291556Srgrimes		return (ioctl(s, SIOCDELRT, (char *)ort));
4301556Srgrimes
4311556Srgrimes	default:
4321556Srgrimes		return (-1);
4331556Srgrimes	}
4341556Srgrimes#else /* RTM_ADD */
4351556Srgrimes	struct {
4361556Srgrimes		struct rt_msghdr w_rtm;
4371556Srgrimes		struct sockaddr w_dst;
4381556Srgrimes		struct sockaddr w_gate;
4391556Srgrimes		struct sockaddr_ipx w_netmask;
4401556Srgrimes	} w;
4411556Srgrimes#define rtm w.w_rtm
4421556Srgrimes
4431556Srgrimes	bzero((char *)&w, sizeof(w));
4441556Srgrimes	rtm.rtm_msglen = sizeof(w);
4451556Srgrimes	rtm.rtm_version = RTM_VERSION;
4461556Srgrimes	rtm.rtm_type = (action == ADD ? RTM_ADD :
4471556Srgrimes				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
4481556Srgrimes	rtm.rtm_flags = ort->rtu_flags;
4491556Srgrimes	rtm.rtm_seq = ++seqno;
4501556Srgrimes	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
4511556Srgrimes	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
4521556Srgrimes	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
4531556Srgrimes	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
4541556Srgrimes	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
4551556Srgrimes	if (rtm.rtm_flags & RTF_HOST) {
4561556Srgrimes		rtm.rtm_msglen -= sizeof(w.w_netmask);
4571556Srgrimes	} else {
4581556Srgrimes		rtm.rtm_addrs |= RTA_NETMASK;
4591556Srgrimes		w.w_netmask = ipx_netmask;
4601556Srgrimes		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
4611556Srgrimes	}
4621556Srgrimes	errno = 0;
4631556Srgrimes	return write(r, (char *)&w, rtm.rtm_msglen);
46490111Simp#endif  /* RTM_ADD */
46590111Simp}
4661556Srgrimes
4671556Srgrimes