Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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  * $DragonFly: src/usr.sbin/IPXrouted/tables.c,v 1.2 2003/06/17 04:29:52 dillon Exp $
37  *
38  * @(#)tables.c 8.1 (Berkeley) 6/5/93
39  */
40
41 /*
42  * Routing Table Management Daemon
43  */
44 #include "defs.h"
45 #include <sys/ioctl.h>
46 #include <errno.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49
50 #ifndef DEBUG
51 #define DEBUG   0
52 #endif
53
54 #define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
55
56 int     install = !DEBUG;               /* if 1 call kernel */
57 int     delete = 1;
58
59 struct  rthash nethash[ROUTEHASHSIZ];
60
61 /*
62  * Lookup dst in the tables for an exact match.
63  */
64 struct rt_entry *
65 rtlookup(dst)
66         struct sockaddr *dst;
67 {
68         register struct rt_entry *rt;
69         register struct rthash *rh;
70         register u_int hash;
71         struct afhash h;
72
73         if (dst->sa_family >= AF_MAX)
74                 return (0);
75         (*afswitch[dst->sa_family].af_hash)(dst, &h);
76         hash = h.afh_nethash;
77         rh = &nethash[hash & ROUTEHASHMASK];
78         for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
79                 if (rt->rt_hash != hash)
80                         continue;
81                 if (equal(&rt->rt_dst, dst))
82                         return (rt);
83         }
84         return (0);
85 }
86
87 /*
88  * Find a route to dst as the kernel would.
89  */
90 struct rt_entry *
91 rtfind(dst)
92         struct sockaddr *dst;
93 {
94         register struct rt_entry *rt;
95         register struct rthash *rh;
96         register u_int hash;
97         struct afhash h;
98         int af = dst->sa_family;
99         int (*match)() = 0;
100
101         if (af >= AF_MAX)
102                 return (0);
103         (*afswitch[af].af_hash)(dst, &h);
104
105         hash = h.afh_nethash;
106         rh = &nethash[hash & ROUTEHASHMASK];
107         match = afswitch[af].af_netmatch;
108         for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
109                 if (rt->rt_hash != hash)
110                         continue;
111                 if (rt->rt_dst.sa_family == af &&
112                     (*match)(&rt->rt_dst, dst))
113                         return (rt);
114         }
115         return (0);
116 }
117
118 void
119 rtadd(dst, gate, metric, ticks, state)
120         struct sockaddr *dst, *gate;
121         short metric, ticks;
122         int state;
123 {
124         struct afhash h;
125         register struct rt_entry *rt;
126         struct rthash *rh;
127         int af = dst->sa_family, flags;
128         u_int hash;
129
130         FIXLEN(dst);
131         FIXLEN(gate);
132         if (af >= AF_MAX)
133                 return;
134         (*afswitch[af].af_hash)(dst, &h);
135         flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
136         hash = h.afh_nethash;
137         rh = &nethash[hash & ROUTEHASHMASK];
138         rt = (struct rt_entry *)malloc(sizeof (*rt));
139         if (rt == 0)
140                 return;
141         rt->rt_hash = hash;
142         rt->rt_dst = *dst;
143         rt->rt_router = *gate;
144         rt->rt_metric = metric;
145         rt->rt_ticks = ticks;
146         rt->rt_timer = 0;
147         rt->rt_flags = RTF_UP | flags;
148         rt->rt_state = state | RTS_CHANGED;
149         rt->rt_ifp = if_ifwithnet(&rt->rt_router);
150         rt->rt_clone = NULL;
151         if (metric)
152                 rt->rt_flags |= RTF_GATEWAY;
153         insque(rt, rh);
154         TRACE_ACTION("ADD", rt);
155         /*
156          * If the ioctl fails because the gateway is unreachable
157          * from this host, discard the entry.  This should only
158          * occur because of an incorrect entry in /etc/gateways.
159          */
160         if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
161                 if (errno != EEXIST)
162                         perror("SIOCADDRT");
163                 if (errno == ENETUNREACH) {
164                         TRACE_ACTION("DELETE", rt);
165                         remque(rt);
166                         free((char *)rt);
167                 }
168         }
169 }
170
171 void
172 rtadd_clone(ort, dst, gate, metric, ticks, state)
173         struct rt_entry *ort;
174         struct sockaddr *dst, *gate;
175         short metric, ticks;
176         int state;
177 {
178         struct afhash h;
179         register struct rt_entry *rt;
180         struct rthash *rh;
181         int af = dst->sa_family, flags;
182         u_int hash;
183
184         FIXLEN(dst);
185         FIXLEN(gate);
186         if (af >= AF_MAX)
187                 return;
188         (*afswitch[af].af_hash)(dst, &h);
189         flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
190         hash = h.afh_nethash;
191         rh = &nethash[hash & ROUTEHASHMASK];
192         rt = (struct rt_entry *)malloc(sizeof (*rt));
193         if (rt == 0)
194                 return;
195         rt->rt_hash = hash;
196         rt->rt_dst = *dst;
197         rt->rt_router = *gate;
198         rt->rt_metric = metric;
199         rt->rt_ticks = ticks;
200         rt->rt_timer = 0;
201         rt->rt_flags = RTF_UP | flags;
202         rt->rt_state = state | RTS_CHANGED;
203         rt->rt_ifp = if_ifwithnet(&rt->rt_router);
204         rt->rt_clone = NULL;
205         rt->rt_forw = NULL;
206         rt->rt_back = NULL;
207         if (metric)
208                 rt->rt_flags |= RTF_GATEWAY;
209
210         while(ort->rt_clone != NULL)
211                 ort = ort->rt_clone;
212         ort->rt_clone = rt;
213         TRACE_ACTION("ADD_CLONE", rt);
214 }
215
216 void
217 rtchange(rt, gate, metric, ticks)
218         struct rt_entry *rt;
219         struct sockaddr *gate;
220         short metric, ticks;
221 {
222         int doioctl = 0, metricchanged = 0;
223         struct rtuentry oldroute;
224
225         FIXLEN(gate);
226         /*
227          * Handling of clones.
228          * When the route changed and it had clones, handle it special.
229          * 1. If the new route is cheaper than the clone(s), free the clones.
230          * 2. If the new route is the same cost, it may be one of the clones,
231          *    search for it and free it.
232          * 3. If the new route is more expensive than the clone(s), use the
233          *    values of the clone(s).
234          */
235         if (rt->rt_clone) {
236                 if ((ticks < rt->rt_clone->rt_ticks) ||
237                     ((ticks == rt->rt_clone->rt_ticks) &&
238                      (metric < rt->rt_clone->rt_metric))) {
239                         /*
240                          * Free all clones.
241                          */
242                         struct rt_entry *trt, *nrt;
243
244                         trt = rt->rt_clone;
245                         rt->rt_clone = NULL;
246                         while(trt) {
247                                 nrt = trt->rt_clone;
248                                 free((char *)trt);
249                                 trt = nrt;
250                         }
251                 } else if ((ticks == rt->rt_clone->rt_ticks) &&
252                      (metric == rt->rt_clone->rt_metric)) {
253                         struct rt_entry *prt, *trt;
254
255                         prt = rt;
256                         trt = rt->rt_clone;
257
258                         while(trt) {
259                                 if (equal(&trt->rt_router, gate)) {
260                                         prt->rt_clone = trt->rt_clone;
261                                         free(trt);
262                                         trt = prt->rt_clone;
263                                 } else {
264                                         prt = trt;
265                                         trt = trt->rt_clone;
266                                 }
267                         }
268                 } else {
269                         /*
270                          * Use the values of the first clone. 
271                          * Delete the corresponding clone.
272                          */
273                         struct rt_entry *trt;
274
275                         trt = rt->rt_clone;
276                         rt->rt_clone = rt->rt_clone->rt_clone;
277                         metric = trt->rt_metric;
278                         ticks = trt->rt_ticks;
279                         *gate = trt->rt_router;
280                         free((char *)trt);
281                 }
282         }
283
284         if (!equal(&rt->rt_router, gate))
285                 doioctl++;
286         if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
287                 metricchanged++;
288         if (doioctl || metricchanged) {
289                 TRACE_ACTION("CHANGE FROM", rt);
290                 if (doioctl) {
291                         oldroute = rt->rt_rt;
292                         rt->rt_router = *gate;
293                 }
294                 rt->rt_metric = metric;
295                 rt->rt_ticks = ticks;
296                 if ((rt->rt_state & RTS_INTERFACE) && metric) {
297                         rt->rt_state &= ~RTS_INTERFACE;
298                         if(rt->rt_ifp) 
299                                 syslog(LOG_ERR,
300                                 "changing route from interface %s (timed out)",
301                                 rt->rt_ifp->int_name);
302                         else
303                                 syslog(LOG_ERR,
304                                 "changing route from interface ??? (timed out)");
305                 }
306                 if (metric)
307                         rt->rt_flags |= RTF_GATEWAY;
308                 else
309                         rt->rt_flags &= ~RTF_GATEWAY;
310                 rt->rt_ifp = if_ifwithnet(&rt->rt_router);
311                 rt->rt_state |= RTS_CHANGED;
312                 TRACE_ACTION("CHANGE TO", rt);
313         }
314         if (doioctl && install) {
315 #ifndef RTM_ADD
316                 if (rtioctl(ADD, &rt->rt_rt) < 0)
317                   syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
318                    ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
319                    ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
320                 if (delete && rtioctl(DELETE, &oldroute) < 0)
321                         perror("rtioctl DELETE");
322 #else
323                 if (delete == 0) {
324                         if (rtioctl(ADD, &rt->rt_rt) >= 0)
325                                 return;
326                 } else {
327                         if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
328                                 return;
329                 }
330                 syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
331                    ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
332                    ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
333 #endif
334         }
335 }
336
337 void
338 rtdelete(rt)
339         struct rt_entry *rt;
340 {
341
342         struct sockaddr *sa = &(rt->rt_router);
343         FIXLEN(sa);
344         sa = &(rt->rt_dst);
345         FIXLEN(sa);
346         if (rt->rt_clone) {
347                 /*
348                  * If there is a clone we just do a rt_change to it.
349                  */
350                 struct rt_entry *trt = rt->rt_clone;
351                 rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
352                 return;
353         }
354         if (rt->rt_state & RTS_INTERFACE) {
355                 if (rt->rt_ifp)
356                         syslog(LOG_ERR, 
357                                 "deleting route to interface %s (timed out)",
358                                 rt->rt_ifp->int_name);
359                 else
360                         syslog(LOG_ERR, 
361                                 "deleting route to interface ??? (timed out)");
362         }
363         TRACE_ACTION("DELETE", rt);
364         if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
365                 perror("rtioctl DELETE");
366         remque(rt);
367         free((char *)rt);
368 }
369
370 void
371 rtinit(void)
372 {
373         register struct rthash *rh;
374
375         for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
376                 rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
377 }
378 int seqno;
379
380 int
381 rtioctl(action, ort)
382         int action;
383         struct rtuentry *ort;
384 {
385 #ifndef RTM_ADD
386         if (install == 0)
387                 return (errno = 0);
388
389         ort->rtu_rtflags = ort->rtu_flags;
390
391         switch (action) {
392
393         case ADD:
394                 return (ioctl(s, SIOCADDRT, (char *)ort));
395
396         case DELETE:
397                 return (ioctl(s, SIOCDELRT, (char *)ort));
398
399         default:
400                 return (-1);
401         }
402 #else /* RTM_ADD */
403         struct {
404                 struct rt_msghdr w_rtm;
405                 struct sockaddr w_dst;
406                 struct sockaddr w_gate;
407                 struct sockaddr_ipx w_netmask;
408         } w;
409 #define rtm w.w_rtm
410
411         bzero((char *)&w, sizeof(w));
412         rtm.rtm_msglen = sizeof(w);
413         rtm.rtm_version = RTM_VERSION;
414         rtm.rtm_type = (action == ADD ? RTM_ADD :
415                                 (action == DELETE ? RTM_DELETE : RTM_CHANGE));
416         rtm.rtm_flags = ort->rtu_flags;
417         rtm.rtm_seq = ++seqno;
418         rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
419         bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
420         bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
421         w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
422         w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
423         if (rtm.rtm_flags & RTF_HOST) {
424                 rtm.rtm_msglen -= sizeof(w.w_netmask);
425         } else {
426                 rtm.rtm_addrs |= RTA_NETMASK;
427                 w.w_netmask = ipx_netmask;
428                 rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
429         }
430         errno = 0;
431         return write(r, (char *)&w, rtm.rtm_msglen);
432 #endif  /* RTM_ADD */
433 }