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