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