]> arthur.barton.de Git - netatalk.git/blob - etc/atalkd/rtmp.c
replace remaining %m printf glibc extension with %s strerror(errno)
[netatalk.git] / etc / atalkd / rtmp.c
1 /*
2  * $Id: rtmp.c,v 1.12.8.1.2.1 2008-11-14 10:04:52 didg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved. See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <atalk/logger.h>
16 #include <sys/types.h>
17 #include <sys/param.h>
18 #include <sys/socket.h>
19 #include <sys/ioctl.h>
20 #ifdef TRU64
21 #include <sys/mbuf.h>
22 #include <net/route.h>
23 #endif /* TRU64 */
24 #include <net/if.h>
25 #include <net/route.h>
26 #include <netatalk/endian.h>
27 #include <netatalk/at.h>
28
29 #ifdef __svr4__
30 #include <sys/sockio.h>
31 #endif /* __svr4__ */
32
33 #include <atalk/ddp.h>
34 #include <atalk/atp.h>
35 #include <atalk/rtmp.h>
36
37 #include "interface.h"
38 #include "gate.h"
39 #include "rtmp.h"
40 #include "zip.h"
41 #include "list.h"
42 #include "atserv.h"
43 #include "route.h"
44 #include "main.h"
45
46 void rtmp_delzonemap(rtmp)
47     struct rtmptab *rtmp;
48 {
49     struct list         *lz, *flz, *lr, *flr;
50     struct ziptab       *zt;
51
52     lz = rtmp->rt_zt;
53     while ( lz ) {                                      /* for each zone */
54         zt = (struct ziptab *)lz->l_data;
55         lr = zt->zt_rt;
56         while ( lr ) {                                  /* for each route */
57             if ( (struct rtmptab *)lr->l_data == rtmp ) {
58                 if ( lr->l_prev == NULL ) {             /* head */
59                     if ( lr->l_next == NULL ) {         /* last route in zone */
60                         if ( zt->zt_prev == NULL ) {
61                             ziptab = zt->zt_next;
62                         } else {
63                             zt->zt_prev->zt_next = zt->zt_next;
64                         }
65                         if ( zt->zt_next == NULL ) {
66                             ziplast = zt->zt_prev;
67                         } else {
68                             zt->zt_next->zt_prev = zt->zt_prev;
69                         }
70                         free( zt->zt_bcast );
71                         free( zt->zt_name );
72                         free( zt );
73                     } else {
74                         zt->zt_rt = lr->l_next;
75                     }
76                 } else {
77                     lr->l_prev->l_next = lr->l_next;
78                 }
79                 if ( lr->l_next != NULL ) {
80                     lr->l_next->l_prev = lr->l_prev;
81                 }
82                 flr = lr;
83                 lr = lr->l_next;
84                 free( flr );
85             } else {
86                 lr = lr->l_next;
87             }
88         }
89         flz = lz;
90         lz = lz->l_next;
91         free( flz );
92     }
93     rtmp->rt_zt = NULL;
94 }
95
96
97 /*
98  * Complete configuration for phase 1 interface using RTMP information.
99  */
100 static int rtmp_config( rh, iface )
101     struct rtmp_head    *rh;
102     struct interface    *iface;
103 {
104     extern int          stabletimer;
105     int cc;
106
107     /*
108      * If we're configuring a phase 2 interface, don't complete
109      * configuration with RTMP.
110      */
111     if ( iface->i_flags & IFACE_PHASE2 ) {
112         LOG(log_info, logtype_atalkd, "rtmp_config ignoring data" );
113         return 0;
114     }
115
116     /*
117      * Check our seed information, and reconfigure.
118      */
119     if ( rh->rh_net != iface->i_addr.sat_addr.s_net ) {
120         if (( iface->i_flags & IFACE_SEED ) &&
121                 rh->rh_net != iface->i_caddr.sat_addr.s_net) {
122             LOG(log_error, logtype_atalkd, "rtmp_config net mismatch %u != %u",
123                     ntohs( rh->rh_net ),
124                     ntohs( iface->i_addr.sat_addr.s_net ));
125             return 1;
126         }
127         iface->i_addr.sat_addr.s_net = rh->rh_net;
128
129         /*
130          * It is possible that we will corrupt our route database
131          * by just forcing this change.  XXX
132          */
133         iface->i_rt->rt_firstnet = iface->i_rt->rt_lastnet = rh->rh_net;
134
135         setaddr( iface, IFACE_PHASE1, iface->i_addr.sat_addr.s_net,
136                 iface->i_addr.sat_addr.s_node, rh->rh_net, rh->rh_net );
137         stabletimer = UNSTABLE;
138     }
139
140     /* add addr to loopback route */
141     if ((cc = looproute( iface, RTMP_ADD )) < 0 )
142       return -1;
143
144     if (cc) {
145         LOG(log_error, logtype_atalkd, "rtmp_config: can't route %u.%u to loopback: %s",
146                 ntohs( iface->i_addr.sat_addr.s_net ),
147                 iface->i_addr.sat_addr.s_node,
148                 strerror(errno) );
149     }
150
151     LOG(log_info, logtype_atalkd, "rtmp_config configured %s", iface->i_name );
152     iface->i_flags |= IFACE_CONFIG;
153     if ( iface == ciface ) {
154         ciface = ciface->i_next;
155         bootaddr( ciface );
156     }
157
158     return 0;
159 }
160
161 /*
162  * Delete rtmp from the per-interface in-use table, remove all
163  * zone references, and remove the route from the kernel.
164  */
165 static void rtmp_delinuse( rtmp )
166     struct rtmptab      *rtmp;
167 {
168     struct rtmptab      *irt;
169
170     irt = rtmp->rt_gate->g_iface->i_rt;
171     if ( irt->rt_inext == rtmp ) {                      /* first */
172         if ( rtmp->rt_iprev == rtmp ) {                 /* only */
173             irt->rt_inext = NULL;
174         } else {
175             irt->rt_inext = rtmp->rt_inext;
176             rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
177         }
178     } else {
179         if ( rtmp->rt_inext == NULL ) {                 /* last */
180             rtmp->rt_iprev->rt_inext = NULL;
181             irt->rt_inext->rt_iprev = rtmp->rt_iprev;
182         } else {
183             rtmp->rt_iprev->rt_inext = rtmp->rt_inext;
184             rtmp->rt_inext->rt_iprev = rtmp->rt_iprev;
185         }
186     }
187     rtmp->rt_iprev = NULL;
188     rtmp->rt_inext = NULL;
189
190     /* remove zone map */
191     rtmp_delzonemap(rtmp);
192
193     /* remove old route */
194     gateroute( RTMP_DEL, rtmp );
195 }
196
197 /*
198  * Add rtmp to the per-interface in-use table.  No verification is done...
199  */
200 static void rtmp_addinuse( rtmp )
201     struct rtmptab      *rtmp;
202 {
203     struct rtmptab      *irt;
204
205     gateroute( RTMP_ADD, rtmp );
206
207     irt = rtmp->rt_gate->g_iface->i_rt;
208     if ( irt->rt_inext == NULL ) {      /* empty list */
209         rtmp->rt_inext = NULL;
210         rtmp->rt_iprev = rtmp;
211         irt->rt_inext = rtmp;
212     } else {
213         rtmp->rt_inext = irt->rt_inext;
214         rtmp->rt_iprev = irt->rt_inext->rt_iprev;
215         irt->rt_inext->rt_iprev = rtmp;
216         irt->rt_inext = rtmp;
217     }
218 }
219
220
221 /*
222  * Change the zone mapping to replace "from" with "to".  This code assumes
223  * the consistency of both the route -> zone map and the zone -> route map.
224  * This is probably a bad idea.  How can we insure that the data is good
225  * at this point?  What do we do if we get several copies of a route in
226  * an RTMP packet?
227  */
228 static int rtmp_copyzones( to, from )
229     struct rtmptab      *to, *from;
230 {
231     struct list         *lz, *lr;
232
233     to->rt_zt = from->rt_zt;
234     from->rt_zt = NULL;
235     if ( from->rt_flags & RTMPTAB_HASZONES ) {
236         to->rt_flags |= RTMPTAB_HASZONES;
237     }
238     for ( lz = to->rt_zt; lz; lz = lz->l_next ) {
239         for ( lr = ((struct ziptab *)lz->l_data)->zt_rt; lr; lr = lr->l_next ) {
240             if ( (struct rtmptab *)lr->l_data == from ) {
241                 lr->l_data = (void *)to;        /* cast BS */
242                 break;
243             }
244         }
245         if ( lr == NULL ) {
246             LOG(log_error, logtype_atalkd, "rtmp_copyzones z -> r without r -> z, abort" );
247             return -1;
248         }
249     }
250
251     return 0;
252 }
253
254
255 /*
256  * Remove rtmp from the in-use table and the per-gate table.
257  * Free any associated space.
258  */
259 void rtmp_free( rtmp )
260     struct rtmptab      *rtmp;
261 {
262     struct gate         *gate;
263
264     LOG(log_info, logtype_atalkd, "rtmp_free: %u-%u", ntohs(rtmp->rt_firstnet),
265            ntohs(rtmp->rt_lastnet));
266     if ( rtmp->rt_iprev ) {
267         rtmp_delinuse( rtmp );
268     }
269
270     /* remove from per-gate */
271     gate = rtmp->rt_gate;
272     if ( gate->g_rt == rtmp ) {                         /* first */
273         if ( rtmp->rt_prev == rtmp ) {                  /* only */
274             gate->g_rt = NULL;
275         } else {
276             gate->g_rt = rtmp->rt_next;
277             rtmp->rt_next->rt_prev = rtmp->rt_prev;
278         }
279     } else {
280         if ( rtmp->rt_next == NULL ) {                  /* last */
281             rtmp->rt_prev->rt_next = NULL;
282             gate->g_rt->rt_prev = rtmp->rt_prev;
283         } else {
284             rtmp->rt_prev->rt_next = rtmp->rt_next;
285             rtmp->rt_next->rt_prev = rtmp->rt_prev;
286         }
287     }
288
289     free( rtmp );
290 }
291
292
293 /*
294  * Find a replacement for "replace".  If we can't find a replacement,
295  * return 1.  If we do find a replacement, return 0. -1 on error.
296  */
297 int rtmp_replace( replace )
298     struct rtmptab      *replace;
299 {
300     struct interface    *iface;
301     struct gate         *gate;
302     struct rtmptab      *rtmp, *found = NULL;
303
304     LOG(log_info, logtype_atalkd, "rtmp_replace %u-%u", ntohs(replace->rt_firstnet),
305            ntohs(replace->rt_lastnet));
306     for ( iface = interfaces; iface; iface = iface->i_next ) {
307         if ((replace->rt_iface != iface) && 
308             ((iface->i_flags & IFACE_ISROUTER) == 0))
309           continue;
310
311         for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
312             for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
313                 if ( rtmp->rt_firstnet == replace->rt_firstnet &&
314                         rtmp->rt_lastnet == replace->rt_lastnet ) {
315                     if ( found == NULL || rtmp->rt_hops < found->rt_hops ) {
316                         found = rtmp;
317                     }
318                     break;
319                 }
320             }
321         }
322     }
323
324     if ( found != replace ) {
325         if (rtmp_copyzones( found, replace ) < 0)
326           return -1;
327         rtmp_delinuse( replace );
328         rtmp_addinuse( found );
329         if ( replace->rt_state == RTMPTAB_BAD ) {
330             rtmp_free( replace );
331         }
332         return( 0 );
333     } else {
334         if ( replace->rt_hops == RTMPHOPS_POISON ) {
335             gateroute( RTMP_DEL, replace );
336         }
337         return( 1 );
338     }
339 }
340
341
342 static int rtmp_new( rtmp )
343     struct rtmptab      *rtmp;
344 {
345     struct interface    *i;
346     struct rtmptab      *r;
347     extern int          newrtmpdata;
348
349     newrtmpdata = 1;
350
351     /*
352      * Do we already have a gateway for this route?
353      */
354     for ( i = interfaces; i; i = i->i_next ) {
355         if ((rtmp->rt_iface != i) && 
356             ((i->i_flags & IFACE_ISROUTER) == 0))
357           continue;
358
359         for ( r = i->i_rt; r; r = r->rt_inext ) {
360             /* Should check RTMPTAB_EXTENDED here. XXX */
361             if (( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_firstnet ) &&
362                     ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_firstnet )) ||
363                     ( ntohs( r->rt_firstnet ) <= ntohs( rtmp->rt_lastnet ) &&
364                     ntohs( r->rt_lastnet ) >= ntohs( rtmp->rt_lastnet ))) {
365                 break;
366             }
367         }
368         if ( r ) {
369             break;
370         }
371     }
372
373     /*
374      * This part of this routine is almost never run.
375      */
376     if ( i ) {  /* can we get here without r being set? */
377         if ( r->rt_firstnet != rtmp->rt_firstnet ||
378                 r->rt_lastnet != rtmp->rt_lastnet ) {
379             LOG(log_info, logtype_atalkd, "rtmp_new netrange mismatch %u-%u != %u-%u",
380                     ntohs( r->rt_firstnet ), ntohs( r->rt_lastnet ),
381                     ntohs( rtmp->rt_firstnet ), ntohs( rtmp->rt_lastnet ));
382             return 1;
383         }
384
385         /*
386          * Note that our whole methodology is wrong, if we want to do
387          * route "load balancing."  This entails changing our route
388          * each time we receive a tuple of equal value.  In fact, we can't
389          * do this, using our method, since we only check against in-use
390          * routes when a tuple is new from a router.
391          */
392         if ( r->rt_hops < rtmp->rt_hops ) {
393             return 1;
394         }
395
396         if (rtmp_copyzones( rtmp, r ) < 0)
397           return -1;
398         rtmp_delinuse( r );
399     }
400
401     rtmp_addinuse( rtmp );
402     return 0;
403 }
404
405
406 int rtmp_packet( ap, from, data, len )
407     struct atport       *ap;
408     struct sockaddr_at  *from;
409     char                *data;
410     int                 len;
411 {
412     struct rtmp_head    rh;
413     struct rtmp_tuple   rt, xrt;
414     struct gate         *gate;
415     struct interface    *iface;
416     struct rtmptab      *rtmp;
417     char                *end, packet[ ATP_BUFSIZ ];
418     int                 cc;
419
420     end = data + len;
421
422     if ( data >= end ) {
423         LOG(log_info, logtype_atalkd, "rtmp_packet no data" );
424         return 1;
425     }
426
427     iface = ap->ap_iface;
428
429     /* ignore our own packets */
430     if ( from->sat_addr.s_net == iface->i_addr.sat_addr.s_net &&
431             from->sat_addr.s_node == iface->i_addr.sat_addr.s_node  ) {
432         return 0;
433     }
434
435     switch( *data++ ) {
436     case DDPTYPE_RTMPRD :
437         /*
438          * Response and Data.
439          */
440         if ( data + sizeof( struct rtmprdhdr ) > end ) {
441             LOG(log_info, logtype_atalkd, "rtmp_packet no data header" );
442             return 1;
443         }
444         memcpy( &rh, data, sizeof( struct rtmprdhdr ));
445         data += sizeof( struct rtmprdhdr );
446
447         /* check rh address against from address */
448         if ( rh.rh_nodelen != 8 ) {
449             LOG(log_info, logtype_atalkd, "rtmp_packet bad node len (%d)", rh.rh_nodelen );
450             return 1;
451         }
452         if (( from->sat_addr.s_net != 0 && 
453               from->sat_addr.s_net != rh.rh_net ) ||
454               from->sat_addr.s_node != rh.rh_node ) {
455             LOG(log_info, logtype_atalkd, "rtmp_packet address mismatch" );
456             return 1;
457         }
458
459         if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG )) == IFACE_ADDR ) {
460             if ( iface->i_flags & IFACE_NOROUTER ) {
461                 /* remove addr to loopback route */
462                 if ((cc = looproute( iface, RTMP_DEL )) < 0) {
463                   LOG(log_error, logtype_atalkd, "rtmp_packet: looproute");
464                   return -1;
465                 } 
466
467                 if (cc)
468                   LOG(log_error, logtype_atalkd, "rtmp_packet: can't remove loopback: %s",
469                           strerror(errno) );
470
471                 iface->i_flags &= ~IFACE_NOROUTER;
472                 iface->i_time = 0;
473                 LOG(log_info, logtype_atalkd, "rtmp_packet router has become available" );
474             }
475             if ( iface->i_flags & IFACE_PHASE1 ) {
476               if (rtmp_config( &rh, iface ) < 0) {
477                   LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_config");
478                   return -1;
479               }
480             } else if (zip_getnetinfo( iface ) < 0) {
481               LOG(log_error, logtype_atalkd, "rtmp_packet: zip_getnetinfo");
482               return -1;
483             }
484             return 0;
485         }
486
487         if (( iface->i_flags & IFACE_CONFIG ) == 0 ) {
488             return 0;
489         }
490
491         /*
492          * Parse first tuple.  For phase 2, verify that net is correct.
493          */
494         if ( data + SZ_RTMPTUPLE > end ) {
495             LOG(log_info, logtype_atalkd, "rtmp_packet missing first tuple" );
496             return 1;
497         }
498         memcpy( &rt, data, SZ_RTMPTUPLE );
499         data += SZ_RTMPTUPLE;
500
501         if ( rt.rt_net == 0 ) {
502             if ( rt.rt_dist != 0x82 ) {
503                 LOG(log_info, logtype_atalkd, "rtmp_packet bad phase 1 version" );
504                 return 1;
505             }
506
507             /*
508              * Grab the next tuple, since we don't want to pass the version
509              * number to the parsing code.  We're assuming that there are
510              * no extended tuples in this packet.
511              */
512             if ( data + SZ_RTMPTUPLE > end ) {
513                 LOG(log_info, logtype_atalkd, "rtmp_packet missing second tuple" );
514                 return 1;
515             }
516             memcpy( &rt, data, SZ_RTMPTUPLE );
517             data += SZ_RTMPTUPLE;
518         } else if ( rt.rt_dist & 0x80 ) {
519             if ( data + SZ_RTMPTUPLE > end ) {
520                 LOG(log_info, logtype_atalkd, "rtmp_packet missing first range-end" );
521                 return 1;
522             }
523             memcpy( &xrt, data, SZ_RTMPTUPLE );
524             data += SZ_RTMPTUPLE;
525
526             if ( xrt.rt_dist != 0x82 ) {
527                 LOG(log_info, logtype_atalkd, "rtmp_packet bad phase 2 version" );
528                 return 1;
529             }
530
531             /*
532              * Check for net range conflict.
533              */
534             if ( rt.rt_net != iface->i_rt->rt_firstnet ||
535                     xrt.rt_net != iface->i_rt->rt_lastnet ) {
536                 LOG(log_info, logtype_atalkd, "rtmp_packet interface mismatch" );
537                 return 1;
538             }
539         } else {
540 #ifdef PHASE1NET
541             /*
542              * Gatorboxes put a net number in the first tuple, even on
543              * phase 1 nets.  This is wrong, but since we've got it, we
544              * might just as well check it.
545             if ( rt.rt_net != iface->i_rt->rt_firstnet ||
546                     rt.rt_net != iface->i_rt->rt_lastnet ) {
547                 LOG(log_info, logtype_atalkd, "rtmp_packet phase 1 interface mismatch" );
548                 return 1;
549             }
550              */
551 #else /* PHASE1NET */
552             LOG(log_info, logtype_atalkd, "rtmp_packet bad first tuple" );
553             return 1;
554 #endif /* PHASE1NET */
555         }
556
557         /*
558          * Find gateway.
559          */
560         for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
561             if ( gate->g_sat.sat_addr.s_net == from->sat_addr.s_net &&
562                     gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
563                 break;
564             }
565         }
566         if ( !gate ) {  /* new gateway */
567             if (( gate = (struct gate *)malloc( sizeof( struct gate ))) == 0 ) {
568                 LOG(log_error, logtype_atalkd, "rtmp_packet: malloc: %s", strerror(errno) );
569                 return -1;
570             }
571             gate->g_next = iface->i_gate;
572             gate->g_prev = 0;
573             gate->g_rt = 0;
574             gate->g_iface = iface;      /* need this? */
575             gate->g_sat = *from;
576             if ( iface->i_gate ) {
577                 iface->i_gate->g_prev = gate;
578             }
579             iface->i_gate = gate;
580             LOG(log_info, logtype_atalkd, "rtmp_packet gateway %u.%u up",
581                     ntohs( gate->g_sat.sat_addr.s_net ),
582                     gate->g_sat.sat_addr.s_node );
583         }
584
585         /*
586          * Reset the timeout on this gateway.  We'll remove the gateway
587          * entry, if the timeout gets to RTMPTAB_BAD.
588          */
589         gate->g_state = RTMPTAB_GOOD;
590
591         /*
592          * Parse remaining tuples.
593          */
594         for (;;) {
595             /*
596              * Is route on this gateway?
597              */
598             for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
599                 if ( ntohs( rtmp->rt_firstnet ) <= ntohs( rt.rt_net ) &&
600                         ntohs( rtmp->rt_lastnet ) >= ntohs( rt.rt_net )) {
601                     break;
602                 }
603                 if (( rt.rt_dist & 0x80 ) &&
604                         ntohs( rtmp->rt_firstnet ) <= ntohs( xrt.rt_net ) &&
605                         ntohs( rtmp->rt_lastnet ) >= ntohs( xrt.rt_net )) {
606                     break;
607                 }
608             }
609
610             if ( rtmp ) {       /* found it */
611                 /*
612                  * Check for range conflicts.  (This is getting a little
613                  * ugly.)
614                  */
615                 if ( rtmp->rt_firstnet != rt.rt_net ) {
616                     LOG(log_info, logtype_atalkd, "rtmp_packet firstnet mismatch %u!=%u",
617                             ntohs( rtmp->rt_firstnet ), ntohs( rt.rt_net ));
618                     return 1;
619                 }
620                 if ( rt.rt_dist & 0x80 ) {
621                     if (( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
622                         LOG(log_info, logtype_atalkd, "rtmp_packet extended mismatch %u",
623                                 ntohs( rtmp->rt_firstnet ));
624                         return 1;
625                     }
626                     if ( rtmp->rt_lastnet != xrt.rt_net ) {
627                         LOG(log_info, logtype_atalkd, "rtmp_packet lastnet mismatch %u!=%u",
628                             ntohs( rtmp->rt_lastnet ), ntohs( xrt.rt_net ));
629                         return 1;
630                     }
631                 } else {
632                     if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
633                         LOG(log_info, logtype_atalkd, "rtmp_packet !extended mismatch %u",
634                                 ntohs( rtmp->rt_firstnet ));
635                         return 1;
636                     }
637                     if ( rtmp->rt_lastnet != rt.rt_net ) {
638                         LOG(log_info, logtype_atalkd, "rtmp_packet lastnet mismatch %u!=%u",
639                             ntohs( rtmp->rt_lastnet ), ntohs( rt.rt_net ));
640                         return 1;
641                     }
642                 }
643
644                 rtmp->rt_state = RTMPTAB_GOOD;
645
646                 /*
647                  * Check hop count.  If the count has changed, update
648                  * the routing database.
649                  */
650                 if (( rtmp->rt_hops != ( rt.rt_dist & 0x7f ) + 1 ) &&
651                         ( rtmp->rt_hops != RTMPHOPS_POISON ||
652                         ( rt.rt_dist & 0x7f ) + 1 <= RTMPHOPS_MAX )) {
653                     if ( rtmp->rt_iprev ) {     /* route is in use */
654                         if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
655                             /*
656                              * If this was POISON, we've deleted it from
657                              * the kernel.  Add it back in.
658                              */
659                             if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
660                                 gateroute( RTMP_ADD, rtmp );
661                             }
662                             rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
663                         } else {
664                             /*
665                              * Hop count has gone up for this route.
666                              * Search for a new best route.  If we can't
667                              * find one, just keep this route.  "poison"
668                              * route are deleted in as_timer().
669                              */
670                             if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
671                                 rtmp->rt_hops = RTMPHOPS_POISON;
672                             } else {
673                                 rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
674                             }
675                             if (rtmp_replace( rtmp ) < 0) {
676                               LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_replace");
677                               return -1;
678                             }
679                         }
680                     } else {                    /* route not in use */
681                         rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
682                         if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
683                           if (rtmp_new( rtmp ) < 0) {
684                               LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_new");
685                               return -1;
686                           }
687                         }
688                     }
689                 }
690
691                 /*
692                  * Make the *next* node the head, since
693                  * we're not likely to be asked for the same tuple twice
694                  * in a row.
695                  */
696                 if ( rtmp->rt_next != 0 ) {
697                     gate->g_rt->rt_prev->rt_next = gate->g_rt;
698                     gate->g_rt = rtmp->rt_next;
699                     rtmp->rt_next = 0;
700                 }
701             } else if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
702                 LOG(log_info, logtype_atalkd, "rtmp_packet bad hop count from %u.%u for %u",
703                         ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
704                         ntohs( rt.rt_net ));
705             } else {            /* new for router */
706                 if (( rtmp = newrt(iface)) == NULL ) {
707                     LOG(log_error, logtype_atalkd, "rtmp_packet: newrt: %s", strerror(errno) );
708                     return -1;
709                 }
710                 rtmp->rt_firstnet = rt.rt_net;
711                 if ( rt.rt_dist & 0x80 ) {
712                     rtmp->rt_lastnet = xrt.rt_net;
713                     rtmp->rt_flags = RTMPTAB_EXTENDED;
714                 } else {
715                     rtmp->rt_lastnet = rt.rt_net;
716                 }
717                 rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
718                 rtmp->rt_state = RTMPTAB_GOOD;
719                 rtmp->rt_gate = gate;
720
721                 /*
722                  * Add rtmptab entry to end of list (leave head alone).
723                  */
724                 if ( gate->g_rt == 0 ) {
725                     rtmp->rt_prev = rtmp;
726                     gate->g_rt = rtmp;
727                 } else {
728                     rtmp->rt_prev = gate->g_rt->rt_prev;
729                     gate->g_rt->rt_prev->rt_next = rtmp;
730                     gate->g_rt->rt_prev = rtmp;
731                 }
732                 
733                 if (rtmp_new( rtmp ) < 0) {
734                     LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_new");
735                     return -1;
736                 }
737             }
738
739             if ( data + SZ_RTMPTUPLE > end ) {
740                 break;
741             }
742             memcpy( &rt, data, SZ_RTMPTUPLE );
743             data += SZ_RTMPTUPLE;
744             if ( rt.rt_dist & 0x80 ) {
745                 if ( data + SZ_RTMPTUPLE > end ) {
746                     LOG(log_info, logtype_atalkd, "rtmp_packet missing range-end" );
747                     return 1;
748                 }
749                 memcpy( &xrt, data, SZ_RTMPTUPLE );
750                 data += SZ_RTMPTUPLE;
751             }
752         }
753
754         /*
755          * Make sure we've processed the whole packet.
756          */
757         if ( data != end ) {
758             LOG(log_info, logtype_atalkd, "rtmp_packet length and count mismatch" );
759         }
760         break;
761
762     case DDPTYPE_RTMPR :
763         /*
764          * Request and RDR.
765          */
766         if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
767             iface->i_rt->rt_zt == 0 ||
768             ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
769             return 0;
770         }
771         if ( *data == 1 ) {
772             data = packet;
773             *data++ = DDPTYPE_RTMPRD;
774             rh.rh_net = iface->i_addr.sat_addr.s_net;
775             rh.rh_nodelen = 8;
776             rh.rh_node = iface->i_addr.sat_addr.s_node;
777             memcpy( data, &rh, sizeof( struct rtmp_head ));
778             data += sizeof( struct rtmp_head );
779
780             if ( iface->i_flags & IFACE_PHASE2 ) {
781                 rt.rt_net = iface->i_rt->rt_firstnet;
782                 rt.rt_dist = 0x80;
783                 memcpy( data, &rt, SZ_RTMPTUPLE );
784                 data += SZ_RTMPTUPLE;
785
786                 rt.rt_net = iface->i_rt->rt_lastnet;
787                 rt.rt_dist = 0x82;
788                 memcpy( data, &rt, SZ_RTMPTUPLE );
789                 data += SZ_RTMPTUPLE;
790             }
791             if ( sendto( ap->ap_fd, packet, data - packet, 0,
792                     (struct sockaddr *)from,
793                     sizeof( struct sockaddr_at )) < 0 ) {
794                 LOG(log_error, logtype_atalkd, "as_timer sendto: %s", strerror(errno) );
795             }
796         } else if ( *data == 2 || *data == 3 ) {
797 #ifdef DEBUG
798             printf( "rtmp_packet rdr (%d) from %u.%u\n",
799                     *data, ntohs( from->sat_addr.s_net ),
800                     from->sat_addr.s_node );
801 #endif /* DEBUG */
802         } else {
803             LOG(log_info, logtype_atalkd, "rtmp_packet unknown request from %u.%u",
804                     ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
805         }
806         break;
807
808     default :
809         LOG(log_info, logtype_atalkd, "rtmp_packet bad ddp type from %u.%u",
810                     ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
811         return 0;
812     }
813
814     return 0;
815 }
816
817 int rtmp_request( iface )
818     struct interface    *iface;
819 {
820     struct sockaddr_at  sat;
821     struct atport       *ap;
822     char                *data, packet[ 2 ];
823
824     LOG(log_info, logtype_atalkd, "rtmp_request for %s", iface->i_name );
825
826     for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
827         if ( ap->ap_packet == rtmp_packet ) {
828             break;
829         }
830     }
831     if ( ap == 0 ) {
832         LOG(log_error, logtype_atalkd, "rtmp_request can't find rtmp socket!" );
833         return -1;
834     }
835
836     data = packet;
837     *data++ = DDPTYPE_RTMPR;
838     *data++ = RTMPROP_REQUEST;
839
840     /*
841      * There is a problem with the net zero "hint" hack.
842      */
843     memset( &sat, 0, sizeof( struct sockaddr_at ));
844 #ifdef BSD4_4
845     sat.sat_len = sizeof( struct sockaddr_at );
846 #endif /* BSD4_4 */
847     sat.sat_family = AF_APPLETALK;
848     sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
849     sat.sat_addr.s_node = ATADDR_BCAST;
850     sat.sat_port = ap->ap_port;
851     if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
852             sizeof( struct sockaddr_at )) < 0 ) {
853         LOG(log_error, logtype_atalkd, "rtmp_request sendto: %s", strerror(errno) );
854         return -1;
855     }
856     return 0;
857 }
858
859
860 int looproute( iface, cmd )
861     struct interface    *iface;
862     unsigned int        cmd;
863 {
864     struct sockaddr_at  dst, loop;
865
866     if ( cmd == RTMP_DEL && ( iface->i_flags & IFACE_LOOP ) == 0 ) {
867         LOG(log_error, logtype_atalkd, "looproute panic no route" );
868         return -1;
869     }
870
871     if ( cmd == RTMP_ADD && ( iface->i_flags & IFACE_LOOP )) {
872         LOG(log_error, logtype_atalkd, "looproute panic two routes" );
873         return -1;
874     }
875
876     memset( &dst, 0, sizeof( struct sockaddr_at ));
877 #ifdef BSD4_4
878     dst.sat_len = sizeof( struct sockaddr_at );
879 #endif /* BSD4_4 */
880     dst.sat_family = AF_APPLETALK;
881     dst.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
882     dst.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
883     memset( &loop, 0, sizeof( struct sockaddr_at ));
884 #ifdef BSD4_4
885     loop.sat_len = sizeof( struct sockaddr_at );
886 #endif /* BSD4_4 */
887     loop.sat_family = AF_APPLETALK;
888     loop.sat_addr.s_net = htons( ATADDR_ANYNET );
889     loop.sat_addr.s_node = ATADDR_ANYNODE;
890
891 #ifndef BSD4_4
892     if ( route( cmd,
893                 (struct sockaddr *) &dst,
894                 (struct sockaddr *) &loop,
895                 RTF_UP | RTF_HOST ) ) {
896         return( 1 );
897     }
898 #else /* ! BSD4_4 */
899     if ( route( cmd,
900                 (struct sockaddr_at *) &dst,
901                 (struct sockaddr_at *) &loop,
902                 RTF_UP | RTF_HOST ) ) {
903         return ( 1);
904     }
905 #endif /* BSD4_4 */
906     if ( cmd == RTMP_ADD ) {
907         iface->i_flags |= IFACE_LOOP;
908     }
909     if ( cmd == RTMP_DEL ) {
910         iface->i_flags &= ~IFACE_LOOP;
911     }
912     return( 0 );
913 }
914
915 int gateroute( command, rtmp )
916     unsigned int        command;
917     struct rtmptab      *rtmp;
918 {
919     struct sockaddr_at  dst, gate;
920     unsigned short      net;
921
922     if ( command == RTMP_DEL && ( rtmp->rt_flags & RTMPTAB_ROUTE ) == 0 ) {
923         return( -1 );
924     }
925     if ( command == RTMP_ADD && ( rtmp->rt_flags & RTMPTAB_ROUTE )) {
926         return( -1 );
927     }
928
929     net = ntohs( rtmp->rt_firstnet );
930     /*
931      * Since we will accept routes from gateways who advertise their
932      * address as 0.YY, we must munge the gateway address we give to
933      * the kernel.  Otherwise, we'll get a bunch of routes to the loop
934      * back interface, and who wants that?
935      */
936     memset( &gate, 0, sizeof( struct sockaddr_at ));
937 #ifdef BSD4_4
938     gate.sat_len = sizeof( struct sockaddr_at );
939 #endif /* BSD4_4 */
940     gate.sat_family = AF_APPLETALK;
941     gate.sat_addr.s_net = rtmp->rt_gate->g_sat.sat_addr.s_net;
942     gate.sat_addr.s_node = rtmp->rt_gate->g_sat.sat_addr.s_node;
943     if ( gate.sat_addr.s_net == 0 ) {
944         gate.sat_addr.s_net = net;
945     }
946
947     memset( &dst, 0, sizeof( struct sockaddr_at ));
948 #ifdef BSD4_4
949     dst.sat_len = sizeof( struct sockaddr_at );
950 #endif /* BSD4_4 */
951     dst.sat_family = AF_APPLETALK;
952     dst.sat_addr.s_node = ATADDR_ANYNODE;
953
954     do {
955         dst.sat_addr.s_net = htons( net );
956 #ifndef BSD4_4
957         if ( route( command,
958                     (struct sockaddr *) &dst,
959                     (struct sockaddr *) &gate,
960                     RTF_UP | RTF_GATEWAY )) {
961             LOG(log_error, logtype_atalkd, "route: %u -> %u.%u: %s", net,
962                     ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node,
963                     strerror(errno) );
964             continue;
965         }
966 #else /* ! BSD4_4 */
967         if ( route( command,
968                     (struct sockaddr_at *) &dst,
969                     (struct sockaddr_at *) &gate,
970                     RTF_UP | RTF_GATEWAY )) {
971             LOG(log_error, logtype_atalkd, "route: %u -> %u.%u: %s", net,
972                     ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node, strerror(errno) );
973             continue;
974         }
975 #endif /* ! BSD4_4 */
976     } while ( net++ < ntohs( rtmp->rt_lastnet ));
977
978     if ( command == RTMP_ADD ) {
979         rtmp->rt_flags |= RTMPTAB_ROUTE;
980     }
981     if ( command == RTMP_DEL ) {
982         rtmp->rt_flags &= ~RTMPTAB_ROUTE;
983     }
984
985     return( 0 );
986 }
987
988     struct rtmptab *
989 newrt(const struct interface *iface)
990 {
991     struct rtmptab      *rtmp;
992
993     if (( rtmp = (struct rtmptab *)calloc(1, sizeof(struct rtmptab))) == 0 ) {
994         return( 0 );
995     }
996
997     rtmp->rt_iface = iface;
998     return( rtmp );
999 }