Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / IPXrouted / tables.c
1 /*
2  * Copyright (c) 1985, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Copyright (c) 1995 John Hay.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by the University of
18  *      California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $FreeBSD: src/usr.sbin/IPXrouted/tables.c,v 1.7 1999/08/28 01:15:05 peter Exp $
36  */
37
38 #ifndef lint
39 static char sccsid[] = "@(#)tables.c    8.1 (Berkeley) 6/5/93";
40 #endif /* not lint */
41
42 /*
43  * Routing Table Management Daemon
44  */
45 #include "defs.h"
46 #include <sys/ioctl.h>
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50
51 #ifndef DEBUG
52 #define DEBUG   0
53 #endif
54
55 #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
56
57 int     install = !DEBUG;               /* if 1 call kernel */
58 int     delete = 1;
59
60 struct  rthash nethash[ROUTEHASHSIZ];
61
62 /*
63  * Lookup dst in the tables for an exact match.
64  */
65 struct rt_entry *
66 rtlookup(dst)
67         struct sockaddr *dst;
68 {
69         register struct rt_entry *rt;
70         register struct rthash *rh;
71         register u_int hash;
72         struct afhash h;
73
74         if (dst->sa_family >= AF_MAX)
75                 return (0);
76         (*afswitch[dst->sa_family].af_hash)(dst, &h);
77         hash = h.afh_nethash;
78         rh = &nethash[hash & ROUTEHASHMASK];
79         for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
80                 if (rt->rt_hash != hash)
81                         continue;
82                 if (equal(&rt->rt_dst, dst))
83                         return (rt);
84         }
85         return (0);
86 }
87
88 /*
89  * Find a route to dst as the kernel would.
90  */
91 struct rt_entry *
92 rtfind(dst)
93         struct sockaddr *dst;
94 {
95         register struct rt_entry *rt;
96         register struct rthash *rh;
97         register u_int hash;
98         struct afhash h;
99         int af = dst->sa_family;
100         int (*match)() = 0;
101
102         if (af >= AF_MAX)
103                 return (0);
104         (*afswitch[af].af_hash)(dst, &h);
105
106         hash = h.afh_nethash;
107         rh = &nethash[hash & ROUTEHASHMASK];
108         match = afswitch[af].af_netmatch;
109         for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
110                 if (rt->rt_hash != hash)
111                         continue;
112                 if (rt->rt_dst.sa_family == af &&
113                     (*match)(&rt->rt_dst, dst))
114                         return (rt);
115         }
116         return (0);
117 }
118
119 void
120 rtadd(dst, gate, metric, ticks, state)
121         struct sockaddr *dst, *gate;
122         short metric, ticks;
123         int state;
124 {
125         struct afhash h;
126         register struct rt_entry *rt;
127         struct rthash *rh;
128         int af = dst->sa_family, flags;
129         u_int hash;
130
131         FIXLEN(dst);
132         FIXLEN(gate);
133         if (af >= AF_MAX)
134                 return;
135         (*afswitch[af].af_hash)(dst, &h);
136         flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
137         hash = h.afh_nethash;
138         rh = &nethash[hash & ROUTEHASHMASK];
139         rt = (struct rt_entry *)malloc(sizeof (*rt));
140         if (rt == 0)
141                 return;
142         rt->rt_hash = hash;
143         rt->rt_dst = *dst;
144         rt->rt_router = *gate;
145         rt->rt_metric = metric;
146         rt->rt_ticks = ticks;
147         rt->rt_timer = 0;
148         rt->rt_flags = RTF_UP | flags;
149         rt->rt_state = state | RTS_CHANGED;
150         rt->rt_ifp = if_ifwithnet(&rt->rt_router);
151         rt->rt_clone = NULL;
152         if (metric)
153                 rt->rt_flags |= RTF_GATEWAY;
154         insque(rt, rh);
155         TRACE_ACTION("ADD", rt);
156         /*
157          * If the ioctl fails because the gateway is unreachable
158          * from this host, discard the entry.  This should only
159          * occur because of an incorrect entry in /etc/gateways.
160          */
161         if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
162                 if (errno != EEXIST)
163                         perror("SIOCADDRT");
164                 if (errno == ENETUNREACH) {
165                         TRACE_ACTION("DELETE", rt);
166                         remque(rt);
167                         free((char *)rt);
168                 }
169         }
170 }
171
172 void
173 rtadd_clone(ort, dst, gate, metric, ticks, state)
174         struct rt_entry *ort;
175         struct sockaddr *dst, *gate;
176         short metric, ticks;
177         int state;
178 {
179         struct afhash h;
180         register struct rt_entry *rt;
181         struct rthash *rh;
182         int af = dst->sa_family, flags;
183         u_int hash;
184
185         FIXLEN(dst);
186         FIXLEN(gate);
187         if (af >= AF_MAX)
188                 return;
189         (*afswitch[af].af_hash)(dst, &h);
190         flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
191         hash = h.afh_nethash;
192         rh = &nethash[hash & ROUTEHASHMASK];
193         rt = (struct rt_entry *)malloc(sizeof (*rt));
194         if (rt == 0)
195                 return;
196         rt->rt_hash = hash;
197         rt->rt_dst = *dst;
198         rt->rt_router = *gate;
199         rt->rt_metric = metric;
200         rt->rt_ticks = ticks;
201         rt->rt_timer = 0;
202         rt->rt_flags = RTF_UP | flags;
203         rt->rt_state = state | RTS_CHANGED;
204         rt->rt_ifp = if_ifwithnet(&rt->rt_router);
205         rt->rt_clone = NULL;
206         rt->rt_forw = NULL;
207         rt->rt_back = NULL;
208         if (metric)
209                 rt->rt_flags |= RTF_GATEWAY;
210
211         while(ort->rt_clone != NULL)
212                 ort = ort->rt_clone;
213         ort->rt_clone = rt;
214         TRACE_ACTION("ADD_CLONE", rt);
215 }
216
217 void
218 rtchange(rt, gate, metric, ticks)
219         struct rt_entry *rt;
220         struct sockaddr *gate;
221         short metric, ticks;
222 {
223         int doioctl = 0, metricchanged = 0;
224         struct rtuentry oldroute;
225
226         FIXLEN(gate);
227         /*
228          * Handling of clones.
229          * When the route changed and it had clones, handle it special.
230          * 1. If the new route is cheaper than the clone(s), free the clones.
231          * 2. If the new route is the same cost, it may be one of the clones,
232          *    search for it and free it.
233          * 3. If the new route is more expensive than the clone(s), use the
234          *    values of the clone(s).
235          */
236         if (rt->rt_clone) {
237                 if ((ticks < rt->rt_clone->rt_ticks) ||
238                     ((ticks == rt->rt_clone->rt_ticks) &&
239                      (metric < rt->rt_clone->rt_metric))) {
240                         /*
241                          * Free all clones.
242                          */
243                         struct rt_entry *trt, *nrt;
244
245                         trt = rt->rt_clone;
246                         rt->rt_clone = NULL;
247                         while(trt) {
248                                 nrt = trt->rt_clone;
249                                 free((char *)trt);
250                                 trt = nrt;
251                         }
252                 } else if ((ticks == rt->rt_clone->rt_ticks) &&
253                      (metric == rt->rt_clone->rt_metric)) {
254                         struct rt_entry *prt, *trt;
255
256                         prt = rt;
257                         trt = rt->rt_clone;
258
259                         while(trt) {
260                                 if (equal(&trt->rt_router, gate)) {
261                                         prt->rt_clone = trt->rt_clone;
262                                         free(trt);
263                                         trt = prt->rt_clone;
264                                 } else {
265                                         prt = trt;
266                                         trt = trt->rt_clone;
267                                 }
268                         }
269                 } else {
270                         /*
271                          * Use the values of the first clone. 
272                          * Delete the corresponding clone.
273                          */
274                         struct rt_entry *trt;
275
276                         trt = rt->rt_clone;
277                         rt->rt_clone = rt->rt_clone->rt_clone;
278                         metric = trt->rt_metric;
279                         ticks = trt->rt_ticks;
280                         *gate = trt->rt_router;
281                         free((char *)trt);
282                 }
283         }
284
285         if (!equal(&rt->rt_router, gate))
286                 doioctl++;
287         if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
288                 metricchanged++;
289         if (doioctl || metricchanged) {
290                 TRACE_ACTION("CHANGE FROM", rt);
291                 if (doioctl) {
292                         oldroute = rt->rt_rt;
293                         rt->rt_router = *gate;
294                 }
295                 rt->rt_metric = metric;
296                 rt->rt_ticks = ticks;
297                 if ((rt->rt_state & RTS_INTERFACE) && metric) {
298                         rt->rt_state &= ~RTS_INTERFACE;
299                         if(rt->rt_ifp) 
300                                 syslog(LOG_ERR,
301                                 "changing route from interface %s (timed out)",
302                                 rt->rt_ifp->int_name);
303                         else
304                                 syslog(LOG_ERR,
305                                 "changing route from interface ??? (timed out)");
306                 }
307                 if (metric)
308                         rt->rt_flags |= RTF_GATEWAY;
309                 else
310                         rt->rt_flags &= ~RTF_GATEWAY;
311                 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
312                 rt->rt_state |= RTS_CHANGED;
313                 TRACE_ACTION("CHANGE TO", rt);
314         }
315         if (doioctl && install) {
316 #ifndef RTM_ADD
317                 if (rtioctl(ADD, &rt->rt_rt) < 0)
318                   syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
319                    ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
320                    ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
321                 if (delete && rtioctl(DELETE, &oldroute) < 0)
322                         perror("rtioctl DELETE");
323 #else
324                 if (delete == 0) {
325                         if (rtioctl(ADD, &rt->rt_rt) >= 0)
326                                 return;
327                 } else {
328                         if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
329                                 return;
330                 }
331                 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
332                    ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
333                    ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
334 #endif
335         }
336 }
337
338 void
339 rtdelete(rt)
340         struct rt_entry *rt;
341 {
342
343         struct sockaddr *sa = &(rt->rt_router);
344         FIXLEN(sa);
345         sa = &(rt->rt_dst);
346         FIXLEN(sa);
347         if (rt->rt_clone) {
348                 /*
349                  * If there is a clone we just do a rt_change to it.
350                  */
351                 struct rt_entry *trt = rt->rt_clone;
352                 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
353                 return;
354         }
355         if (rt->rt_state & RTS_INTERFACE) {
356                 if (rt->rt_ifp)
357                         syslog(LOG_ERR, 
358                                 "deleting route to interface %s (timed out)",
359                                 rt->rt_ifp->int_name);
360                 else
361                         syslog(LOG_ERR, 
362                                 "deleting route to interface ??? (timed out)");
363         }
364         TRACE_ACTION("DELETE", rt);
365         if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
366                 perror("rtioctl DELETE");
367         remque(rt);
368         free((char *)rt);
369 }
370
371 void
372 rtinit(void)
373 {
374         register struct rthash *rh;
375
376         for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
377                 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
378 }
379 int seqno;
380
381 int
382 rtioctl(action, ort)
383         int action;
384         struct rtuentry *ort;
385 {
386 #ifndef RTM_ADD
387         if (install == 0)
388                 return (errno = 0);
389
390         ort->rtu_rtflags = ort->rtu_flags;
391
392         switch (action) {
393
394         case ADD:
395                 return (ioctl(s, SIOCADDRT, (char *)ort));
396
397         case DELETE:
398                 return (ioctl(s, SIOCDELRT, (char *)ort));
399
400         default:
401                 return (-1);
402         }
403 #else /* RTM_ADD */
404         struct {
405                 struct rt_msghdr w_rtm;
406                 struct sockaddr w_dst;
407                 struct sockaddr w_gate;
408                 struct sockaddr_ipx w_netmask;
409         } w;
410 #define rtm w.w_rtm
411
412         bzero((char *)&w, sizeof(w));
413         rtm.rtm_msglen = sizeof(w);
414         rtm.rtm_version = RTM_VERSION;
415         rtm.rtm_type = (action == ADD ? RTM_ADD :
416                                 (action == DELETE ? RTM_DELETE : RTM_CHANGE));
417         rtm.rtm_flags = ort->rtu_flags;
418         rtm.rtm_seq = ++seqno;
419         rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
420         bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
421         bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
422         w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
423         w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
424         if (rtm.rtm_flags & RTF_HOST) {
425                 rtm.rtm_msglen -= sizeof(w.w_netmask);
426         } else {
427                 rtm.rtm_addrs |= RTA_NETMASK;
428                 w.w_netmask = ipx_netmask;
429                 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
430         }
431         errno = 0;
432         return write(r, (char *)&w, rtm.rtm_msglen);
433 #endif  /* RTM_ADD */
434 }