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