tables.c revision 15248
111820Sjulian/*
211820Sjulian * Copyright (c) 1985, 1993
311820Sjulian *	The Regents of the University of California.  All rights reserved.
411820Sjulian *
511820Sjulian * Copyright (c) 1995 John Hay.  All rights reserved.
611820Sjulian *
711820Sjulian * Redistribution and use in source and binary forms, with or without
811820Sjulian * modification, are permitted provided that the following conditions
911820Sjulian * are met:
1011820Sjulian * 1. Redistributions of source code must retain the above copyright
1111820Sjulian *    notice, this list of conditions and the following disclaimer.
1211820Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1311820Sjulian *    notice, this list of conditions and the following disclaimer in the
1411820Sjulian *    documentation and/or other materials provided with the distribution.
1511820Sjulian * 3. All advertising materials mentioning features or use of this software
1611820Sjulian *    must display the following acknowledgement:
1711820Sjulian *	This product includes software developed by the University of
1811820Sjulian *	California, Berkeley and its contributors.
1911820Sjulian * 4. Neither the name of the University nor the names of its contributors
2011820Sjulian *    may be used to endorse or promote products derived from this software
2111820Sjulian *    without specific prior written permission.
2211820Sjulian *
2311820Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2411820Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2511820Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2611820Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2711820Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2811820Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2911820Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3011820Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3111820Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3211820Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3311820Sjulian * SUCH DAMAGE.
3411820Sjulian *
3515248Sjhay *	$Id: tables.c,v 1.1 1995/10/26 21:28:27 julian Exp $
3611820Sjulian */
3711820Sjulian
3811820Sjulian#ifndef lint
3911820Sjulianstatic char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 6/5/93";
4011820Sjulian#endif /* not lint */
4111820Sjulian
4211820Sjulian/*
4311820Sjulian * Routing Table Management Daemon
4411820Sjulian */
4511820Sjulian#include "defs.h"
4611820Sjulian#include <sys/ioctl.h>
4711820Sjulian#include <errno.h>
4811820Sjulian#include <stdlib.h>
4911820Sjulian#include <unistd.h>
5011820Sjulian/* XXX I thought that this should work! #include <sys/systm.h> */
5111820Sjulian#include <machine/cpufunc.h>
5211820Sjulian
5311820Sjulian#ifndef DEBUG
5411820Sjulian#define	DEBUG	0
5511820Sjulian#endif
5611820Sjulian
5711820Sjulian#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
5811820Sjulian
5911820Sjulianint	install = !DEBUG;		/* if 1 call kernel */
6011820Sjulianint	delete = 1;
6115248Sjhay
6215248Sjhaystruct  rthash nethash[ROUTEHASHSIZ];
6315248Sjhaystruct  rthash hosthash[ROUTEHASHSIZ];
6415248Sjhay
6511820Sjulian/*
6611820Sjulian * Lookup dst in the tables for an exact match.
6711820Sjulian */
6811820Sjulianstruct rt_entry *
6911820Sjulianrtlookup(dst)
7011820Sjulian	struct sockaddr *dst;
7111820Sjulian{
7211820Sjulian	register struct rt_entry *rt;
7311820Sjulian	register struct rthash *rh;
7411820Sjulian	register u_int hash;
7511820Sjulian	struct afhash h;
7611820Sjulian	int doinghost = 1;
7711820Sjulian
7811820Sjulian	if (dst->sa_family >= AF_MAX)
7911820Sjulian		return (0);
8011820Sjulian	(*afswitch[dst->sa_family].af_hash)(dst, &h);
8111820Sjulian	hash = h.afh_hosthash;
8211820Sjulian	rh = &hosthash[hash & ROUTEHASHMASK];
8311820Sjulianagain:
8411820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
8511820Sjulian		if (rt->rt_hash != hash)
8611820Sjulian			continue;
8711820Sjulian		if (equal(&rt->rt_dst, dst))
8811820Sjulian			return (rt);
8911820Sjulian	}
9011820Sjulian	if (doinghost) {
9111820Sjulian		doinghost = 0;
9211820Sjulian		hash = h.afh_nethash;
9311820Sjulian		rh = &nethash[hash & ROUTEHASHMASK];
9411820Sjulian		goto again;
9511820Sjulian	}
9611820Sjulian	return (0);
9711820Sjulian}
9811820Sjulian
9911820Sjulian/*
10011820Sjulian * Find a route to dst as the kernel would.
10111820Sjulian */
10211820Sjulianstruct rt_entry *
10311820Sjulianrtfind(dst)
10411820Sjulian	struct sockaddr *dst;
10511820Sjulian{
10611820Sjulian	register struct rt_entry *rt;
10711820Sjulian	register struct rthash *rh;
10811820Sjulian	register u_int hash;
10911820Sjulian	struct afhash h;
11011820Sjulian	int af = dst->sa_family;
11111820Sjulian	int doinghost = 1, (*match)() = 0;
11211820Sjulian
11311820Sjulian	if (af >= AF_MAX)
11411820Sjulian		return (0);
11511820Sjulian	(*afswitch[af].af_hash)(dst, &h);
11611820Sjulian	hash = h.afh_hosthash;
11711820Sjulian	rh = &hosthash[hash & ROUTEHASHMASK];
11811820Sjulian
11911820Sjulianagain:
12011820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
12111820Sjulian		if (rt->rt_hash != hash)
12211820Sjulian			continue;
12311820Sjulian		if (doinghost) {
12411820Sjulian			if (equal(&rt->rt_dst, dst))
12511820Sjulian				return (rt);
12611820Sjulian		} else {
12711820Sjulian			if (rt->rt_dst.sa_family == af &&
12811820Sjulian			    (match != 0) &&
12911820Sjulian			    (*match)(&rt->rt_dst, dst))
13011820Sjulian				return (rt);
13111820Sjulian		}
13211820Sjulian	}
13311820Sjulian	if (doinghost) {
13411820Sjulian		doinghost = 0;
13511820Sjulian		hash = h.afh_nethash;
13611820Sjulian		rh = &nethash[hash & ROUTEHASHMASK];
13711820Sjulian		match = afswitch[af].af_netmatch;
13811820Sjulian		goto again;
13911820Sjulian	}
14011820Sjulian	return (0);
14111820Sjulian}
14211820Sjulian
14311820Sjulianvoid
14411820Sjulianrtadd(dst, gate, metric, ticks, state)
14511820Sjulian	struct sockaddr *dst, *gate;
14611820Sjulian	short metric, ticks;
14711820Sjulian	int state;
14811820Sjulian{
14911820Sjulian	struct afhash h;
15011820Sjulian	register struct rt_entry *rt;
15111820Sjulian	struct rthash *rh;
15211820Sjulian	int af = dst->sa_family, flags;
15311820Sjulian	u_int hash;
15411820Sjulian
15511820Sjulian	FIXLEN(dst);
15611820Sjulian	FIXLEN(gate);
15711820Sjulian	if (af >= AF_MAX)
15811820Sjulian		return;
15911820Sjulian	(*afswitch[af].af_hash)(dst, &h);
16011820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
16111820Sjulian	if (flags & RTF_HOST) {
16211820Sjulian		hash = h.afh_hosthash;
16311820Sjulian		rh = &hosthash[hash & ROUTEHASHMASK];
16411820Sjulian	} else {
16511820Sjulian		hash = h.afh_nethash;
16611820Sjulian		rh = &nethash[hash & ROUTEHASHMASK];
16711820Sjulian	}
16811820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
16911820Sjulian	if (rt == 0)
17011820Sjulian		return;
17111820Sjulian	rt->rt_hash = hash;
17211820Sjulian	rt->rt_dst = *dst;
17311820Sjulian	rt->rt_router = *gate;
17411820Sjulian	rt->rt_metric = metric;
17511820Sjulian	rt->rt_ticks = ticks;
17611820Sjulian	rt->rt_timer = 0;
17711820Sjulian	rt->rt_flags = RTF_UP | flags;
17811820Sjulian	rt->rt_state = state | RTS_CHANGED;
17911820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
18011820Sjulian	rt->rt_clone = NULL;
18111820Sjulian	if (metric)
18211820Sjulian		rt->rt_flags |= RTF_GATEWAY;
18311820Sjulian	insque(rt, rh);
18411820Sjulian	TRACE_ACTION(ADD, rt);
18511820Sjulian	/*
18611820Sjulian	 * If the ioctl fails because the gateway is unreachable
18711820Sjulian	 * from this host, discard the entry.  This should only
18811820Sjulian	 * occur because of an incorrect entry in /etc/gateways.
18911820Sjulian	 */
19011820Sjulian	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
19111820Sjulian		if (errno != EEXIST)
19211820Sjulian			perror("SIOCADDRT");
19311820Sjulian		if (errno == ENETUNREACH) {
19411820Sjulian			TRACE_ACTION(DELETE, rt);
19511820Sjulian			remque(rt);
19611820Sjulian			free((char *)rt);
19711820Sjulian		}
19811820Sjulian	}
19911820Sjulian}
20011820Sjulian
20111820Sjulianvoid
20211820Sjulianrtadd_clone(ort, dst, gate, metric, ticks, state)
20311820Sjulian	struct rt_entry *ort;
20411820Sjulian	struct sockaddr *dst, *gate;
20511820Sjulian	short metric, ticks;
20611820Sjulian	int state;
20711820Sjulian{
20811820Sjulian	struct afhash h;
20911820Sjulian	register struct rt_entry *rt;
21011820Sjulian	struct rthash *rh;
21111820Sjulian	int af = dst->sa_family, flags;
21211820Sjulian	u_int hash;
21311820Sjulian
21411820Sjulian	FIXLEN(dst);
21511820Sjulian	FIXLEN(gate);
21611820Sjulian	if (af >= AF_MAX)
21711820Sjulian		return;
21811820Sjulian	(*afswitch[af].af_hash)(dst, &h);
21911820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
22011820Sjulian	if (flags & RTF_HOST) {
22111820Sjulian		hash = h.afh_hosthash;
22211820Sjulian		rh = &hosthash[hash & ROUTEHASHMASK];
22311820Sjulian	} else {
22411820Sjulian		hash = h.afh_nethash;
22511820Sjulian		rh = &nethash[hash & ROUTEHASHMASK];
22611820Sjulian	}
22711820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
22811820Sjulian	if (rt == 0)
22911820Sjulian		return;
23011820Sjulian	rt->rt_hash = hash;
23111820Sjulian	rt->rt_dst = *dst;
23211820Sjulian	rt->rt_router = *gate;
23311820Sjulian	rt->rt_metric = metric;
23411820Sjulian	rt->rt_ticks = ticks;
23511820Sjulian	rt->rt_timer = 0;
23611820Sjulian	rt->rt_flags = RTF_UP | flags;
23711820Sjulian	rt->rt_state = state | RTS_CHANGED;
23811820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
23911820Sjulian	rt->rt_clone = NULL;
24011820Sjulian	rt->rt_forw = NULL;
24111820Sjulian	rt->rt_back = NULL;
24211820Sjulian	if (metric)
24311820Sjulian		rt->rt_flags |= RTF_GATEWAY;
24411820Sjulian
24511820Sjulian	while(ort->rt_clone != NULL)
24611820Sjulian		ort = ort->rt_clone;
24711820Sjulian	ort->rt_clone = rt;
24811820Sjulian	TRACE_ACTION(ADD_CLONE, rt);
24911820Sjulian}
25011820Sjulian
25111820Sjulianvoid
25211820Sjulianrtchange(rt, gate, metric, ticks)
25311820Sjulian	struct rt_entry *rt;
25411820Sjulian	struct sockaddr *gate;
25511820Sjulian	short metric, ticks;
25611820Sjulian{
25711820Sjulian	int doioctl = 0, metricchanged = 0;
25811820Sjulian	struct rtuentry oldroute;
25911820Sjulian
26011820Sjulian	FIXLEN(gate);
26111820Sjulian	/*
26211820Sjulian 	 * Handling of clones.
26311820Sjulian 	 * When the route changed and it had clones, handle it special.
26411820Sjulian 	 * 1. If the new route is cheaper than the clone(s), free the clones.
26511820Sjulian	 * 2. If the new route is the same cost, it may be one of the clones,
26611820Sjulian	 *    search for it and free it.
26711820Sjulian 	 * 3. If the new route is more expensive than the clone(s), use the
26811820Sjulian 	 *    values of the clone(s).
26911820Sjulian 	 */
27011820Sjulian	if (rt->rt_clone) {
27111820Sjulian		if ((ticks < rt->rt_clone->rt_ticks) ||
27211820Sjulian		    ((ticks == rt->rt_clone->rt_ticks) &&
27311820Sjulian		     (metric < rt->rt_clone->rt_metric))) {
27411820Sjulian			/*
27511820Sjulian			 * Free all clones.
27611820Sjulian			 */
27711820Sjulian			struct rt_entry *trt, *nrt;
27811820Sjulian
27911820Sjulian			trt = rt->rt_clone;
28011820Sjulian			rt->rt_clone = NULL;
28111820Sjulian			while(trt) {
28211820Sjulian				nrt = trt->rt_clone;
28311820Sjulian				free((char *)trt);
28411820Sjulian				trt = nrt;
28511820Sjulian			}
28611820Sjulian		} else if ((ticks == rt->rt_clone->rt_ticks) &&
28711820Sjulian		     (metric == rt->rt_clone->rt_metric)) {
28811820Sjulian			struct rt_entry *prt, *trt;
28911820Sjulian
29011820Sjulian			prt = rt;
29111820Sjulian			trt = rt->rt_clone;
29211820Sjulian
29311820Sjulian			while(trt) {
29411820Sjulian				if (equal(&trt->rt_router, gate)) {
29511820Sjulian					prt->rt_clone = trt->rt_clone;
29611820Sjulian					free(trt);
29711820Sjulian					trt = prt->rt_clone;
29811820Sjulian				} else {
29911820Sjulian					prt = trt;
30011820Sjulian					trt = trt->rt_clone;
30111820Sjulian				}
30211820Sjulian			}
30311820Sjulian		} else {
30411820Sjulian			/*
30511820Sjulian			 * Use the values of the first clone.
30611820Sjulian			 * Delete the corresponding clone.
30711820Sjulian			 */
30811820Sjulian			struct rt_entry *trt;
30911820Sjulian
31011820Sjulian			trt = rt->rt_clone;
31111820Sjulian			rt->rt_clone = rt->rt_clone->rt_clone;
31211820Sjulian			metric = trt->rt_metric;
31311820Sjulian			ticks = trt->rt_ticks;
31411820Sjulian			*gate = trt->rt_router;
31511820Sjulian			free((char *)trt);
31611820Sjulian		}
31711820Sjulian	}
31811820Sjulian
31911820Sjulian	if (!equal(&rt->rt_router, gate))
32011820Sjulian		doioctl++;
32111820Sjulian	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
32211820Sjulian		metricchanged++;
32311820Sjulian	if (doioctl || metricchanged) {
32411820Sjulian		TRACE_ACTION(CHANGE FROM, rt);
32511820Sjulian		if (doioctl) {
32611820Sjulian			oldroute = rt->rt_rt;
32711820Sjulian			rt->rt_router = *gate;
32811820Sjulian		}
32911820Sjulian		rt->rt_metric = metric;
33011820Sjulian		rt->rt_ticks = ticks;
33111820Sjulian		if ((rt->rt_state & RTS_INTERFACE) && metric) {
33211820Sjulian			rt->rt_state &= ~RTS_INTERFACE;
33311820Sjulian			if(rt->rt_ifp)
33411820Sjulian				syslog(LOG_ERR,
33511820Sjulian				"changing route from interface %s (timed out)",
33611820Sjulian				rt->rt_ifp->int_name);
33711820Sjulian			else
33811820Sjulian				syslog(LOG_ERR,
33911820Sjulian				"changing route from interface ??? (timed out)");
34011820Sjulian		}
34111820Sjulian		if (metric)
34211820Sjulian			rt->rt_flags |= RTF_GATEWAY;
34311820Sjulian		else
34411820Sjulian			rt->rt_flags &= ~RTF_GATEWAY;
34511820Sjulian		rt->rt_state |= RTS_CHANGED;
34611820Sjulian		TRACE_ACTION(CHANGE TO, rt);
34711820Sjulian	}
34811820Sjulian	if (doioctl && install) {
34911820Sjulian#ifndef RTM_ADD
35011820Sjulian		if (rtioctl(ADD, &rt->rt_rt) < 0)
35111820Sjulian		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
35211820Sjulian		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
35311820Sjulian		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
35411820Sjulian		if (delete && rtioctl(DELETE, &oldroute) < 0)
35511820Sjulian			perror("rtioctl DELETE");
35611820Sjulian#else
35711820Sjulian		if (delete == 0) {
35811820Sjulian			if (rtioctl(ADD, &rt->rt_rt) >= 0)
35911820Sjulian				return;
36011820Sjulian		} else {
36111820Sjulian			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
36211820Sjulian				return;
36311820Sjulian		}
36411820Sjulian	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
36511820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
36611820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
36711820Sjulian#endif
36811820Sjulian	}
36911820Sjulian}
37011820Sjulian
37111820Sjulianvoid
37211820Sjulianrtdelete(rt)
37311820Sjulian	struct rt_entry *rt;
37411820Sjulian{
37511820Sjulian
37611820Sjulian	struct sockaddr *sa = &(rt->rt_router);
37711820Sjulian	FIXLEN(sa);
37811820Sjulian	sa = &(rt->rt_dst);
37911820Sjulian	FIXLEN(sa);
38011820Sjulian	if (rt->rt_clone) {
38111820Sjulian		/*
38211820Sjulian		 * If there is a clone we just do a rt_change to it.
38311820Sjulian		 */
38411820Sjulian		struct rt_entry *trt = rt->rt_clone;
38511820Sjulian		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
38611820Sjulian		return;
38711820Sjulian	}
38811820Sjulian	if (rt->rt_state & RTS_INTERFACE) {
38911820Sjulian		if (rt->rt_ifp)
39011820Sjulian			syslog(LOG_ERR,
39111820Sjulian				"deleting route to interface %s (timed out)",
39211820Sjulian				rt->rt_ifp->int_name);
39311820Sjulian		else
39411820Sjulian			syslog(LOG_ERR,
39511820Sjulian				"deleting route to interface ??? (timed out)");
39611820Sjulian	}
39711820Sjulian	TRACE_ACTION(DELETE, rt);
39811820Sjulian	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
39911820Sjulian		perror("rtioctl DELETE");
40011820Sjulian	remque(rt);
40111820Sjulian	free((char *)rt);
40211820Sjulian}
40311820Sjulian
40411820Sjulianvoid
40511820Sjulianrtinit(void)
40611820Sjulian{
40711820Sjulian	register struct rthash *rh;
40811820Sjulian
40911820Sjulian	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
41011820Sjulian		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
41111820Sjulian	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
41211820Sjulian		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
41311820Sjulian}
41411820Sjulianint seqno;
41511820Sjulian
41611820Sjulianint
41711820Sjulianrtioctl(action, ort)
41811820Sjulian	int action;
41911820Sjulian	struct rtuentry *ort;
42011820Sjulian{
42111820Sjulian#ifndef RTM_ADD
42211820Sjulian	if (install == 0)
42311820Sjulian		return (errno = 0);
42411820Sjulian
42511820Sjulian	ort->rtu_rtflags = ort->rtu_flags;
42611820Sjulian
42711820Sjulian	switch (action) {
42811820Sjulian
42911820Sjulian	case ADD:
43011820Sjulian		return (ioctl(s, SIOCADDRT, (char *)ort));
43111820Sjulian
43211820Sjulian	case DELETE:
43311820Sjulian		return (ioctl(s, SIOCDELRT, (char *)ort));
43411820Sjulian
43511820Sjulian	default:
43611820Sjulian		return (-1);
43711820Sjulian	}
43811820Sjulian#else /* RTM_ADD */
43911820Sjulian	struct {
44011820Sjulian		struct rt_msghdr w_rtm;
44111820Sjulian		struct sockaddr w_dst;
44211820Sjulian		struct sockaddr w_gate;
44311820Sjulian		struct sockaddr_ipx w_netmask;
44411820Sjulian	} w;
44511820Sjulian#define rtm w.w_rtm
44611820Sjulian
44711820Sjulian	bzero((char *)&w, sizeof(w));
44811820Sjulian	rtm.rtm_msglen = sizeof(w);
44911820Sjulian	rtm.rtm_version = RTM_VERSION;
45011820Sjulian	rtm.rtm_type = (action == ADD ? RTM_ADD :
45111820Sjulian				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
45211820Sjulian	rtm.rtm_flags = ort->rtu_flags;
45311820Sjulian	rtm.rtm_seq = ++seqno;
45411820Sjulian	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
45511820Sjulian	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
45611820Sjulian	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
45711820Sjulian	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
45811820Sjulian	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
45911820Sjulian	if (rtm.rtm_flags & RTF_HOST) {
46011820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask);
46111820Sjulian	} else {
46211820Sjulian		rtm.rtm_addrs |= RTA_NETMASK;
46311820Sjulian		w.w_netmask = ipx_netmask;
46411820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
46511820Sjulian	}
46611820Sjulian	errno = 0;
46711820Sjulian	return write(r, (char *)&w, rtm.rtm_msglen);
46811820Sjulian#endif  /* RTM_ADD */
46911820Sjulian}
47011820Sjulian
471