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