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