]> arthur.barton.de Git - netatalk.git/blob - etc/atalkd/rtmp.c
remove most sparse warning 'Using plain integer as NULL pointer'
[netatalk.git] / etc / atalkd / rtmp.c
1 /*
2  * $Id: rtmp.c,v 1.16 2009-10-14 02:24:05 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 rtmptab      *rtmp;
404     char                *end, packet[ ATP_BUFSIZ ];
405     int                 cc;
406
407     end = data + len;
408
409     if ( data >= end ) {
410         LOG(log_info, logtype_atalkd, "rtmp_packet no data" );
411         return 1;
412     }
413
414     iface = ap->ap_iface;
415
416     /* ignore our own packets */
417     if ( from->sat_addr.s_net == iface->i_addr.sat_addr.s_net &&
418             from->sat_addr.s_node == iface->i_addr.sat_addr.s_node  ) {
419         return 0;
420     }
421
422     switch( *data++ ) {
423     case DDPTYPE_RTMPRD :
424         /*
425          * Response and Data.
426          */
427         if ( data + sizeof( struct rtmprdhdr ) > end ) {
428             LOG(log_info, logtype_atalkd, "rtmp_packet no data header" );
429             return 1;
430         }
431         memcpy( &rh, data, sizeof( struct rtmprdhdr ));
432         data += sizeof( struct rtmprdhdr );
433
434         /* check rh address against from address */
435         if ( rh.rh_nodelen != 8 ) {
436             LOG(log_info, logtype_atalkd, "rtmp_packet bad node len (%d)", rh.rh_nodelen );
437             return 1;
438         }
439         if (( from->sat_addr.s_net != 0 && 
440               from->sat_addr.s_net != rh.rh_net ) ||
441               from->sat_addr.s_node != rh.rh_node ) {
442             LOG(log_info, logtype_atalkd, "rtmp_packet address mismatch" );
443             return 1;
444         }
445
446         if (( iface->i_flags & ( IFACE_ADDR|IFACE_CONFIG )) == IFACE_ADDR ) {
447             if ( iface->i_flags & IFACE_NOROUTER ) {
448                 /* remove addr to loopback route */
449                 if ((cc = looproute( iface, RTMP_DEL )) < 0) {
450                   LOG(log_error, logtype_atalkd, "rtmp_packet: looproute");
451                   return -1;
452                 } 
453
454                 if (cc)
455                   LOG(log_error, logtype_atalkd, "rtmp_packet: can't remove loopback: %s",
456                           strerror(errno) );
457
458                 iface->i_flags &= ~IFACE_NOROUTER;
459                 iface->i_time = 0;
460                 LOG(log_info, logtype_atalkd, "rtmp_packet router has become available" );
461             }
462             if ( iface->i_flags & IFACE_PHASE1 ) {
463               if (rtmp_config( &rh, iface ) < 0) {
464                   LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_config");
465                   return -1;
466               }
467             } else if (zip_getnetinfo( iface ) < 0) {
468               LOG(log_error, logtype_atalkd, "rtmp_packet: zip_getnetinfo");
469               return -1;
470             }
471             return 0;
472         }
473
474         if (( iface->i_flags & IFACE_CONFIG ) == 0 ) {
475             return 0;
476         }
477
478         /*
479          * Parse first tuple.  For phase 2, verify that net is correct.
480          */
481         if ( data + SZ_RTMPTUPLE > end ) {
482             LOG(log_info, logtype_atalkd, "rtmp_packet missing first tuple" );
483             return 1;
484         }
485         memcpy( &rt, data, SZ_RTMPTUPLE );
486         data += SZ_RTMPTUPLE;
487
488         if ( rt.rt_net == 0 ) {
489             if ( rt.rt_dist != 0x82 ) {
490                 LOG(log_info, logtype_atalkd, "rtmp_packet bad phase 1 version" );
491                 return 1;
492             }
493
494             /*
495              * Grab the next tuple, since we don't want to pass the version
496              * number to the parsing code.  We're assuming that there are
497              * no extended tuples in this packet.
498              */
499             if ( data + SZ_RTMPTUPLE > end ) {
500                 LOG(log_info, logtype_atalkd, "rtmp_packet missing second tuple" );
501                 return 1;
502             }
503             memcpy( &rt, data, SZ_RTMPTUPLE );
504             data += SZ_RTMPTUPLE;
505         } else if ( rt.rt_dist & 0x80 ) {
506             if ( data + SZ_RTMPTUPLE > end ) {
507                 LOG(log_info, logtype_atalkd, "rtmp_packet missing first range-end" );
508                 return 1;
509             }
510             memcpy( &xrt, data, SZ_RTMPTUPLE );
511             data += SZ_RTMPTUPLE;
512
513             if ( xrt.rt_dist != 0x82 ) {
514                 LOG(log_info, logtype_atalkd, "rtmp_packet bad phase 2 version" );
515                 return 1;
516             }
517
518             /*
519              * Check for net range conflict.
520              */
521             if ( rt.rt_net != iface->i_rt->rt_firstnet ||
522                     xrt.rt_net != iface->i_rt->rt_lastnet ) {
523                 LOG(log_info, logtype_atalkd, "rtmp_packet interface mismatch" );
524                 return 1;
525             }
526         } else {
527 #ifdef PHASE1NET
528             /*
529              * Gatorboxes put a net number in the first tuple, even on
530              * phase 1 nets.  This is wrong, but since we've got it, we
531              * might just as well check it.
532             if ( rt.rt_net != iface->i_rt->rt_firstnet ||
533                     rt.rt_net != iface->i_rt->rt_lastnet ) {
534                 LOG(log_info, logtype_atalkd, "rtmp_packet phase 1 interface mismatch" );
535                 return 1;
536             }
537              */
538 #else /* PHASE1NET */
539             LOG(log_info, logtype_atalkd, "rtmp_packet bad first tuple" );
540             return 1;
541 #endif /* PHASE1NET */
542         }
543
544         /*
545          * Find gateway.
546          */
547         for ( gate = iface->i_gate; gate; gate = gate->g_next ) {
548             if ( gate->g_sat.sat_addr.s_net == from->sat_addr.s_net &&
549                     gate->g_sat.sat_addr.s_node == from->sat_addr.s_node ) {
550                 break;
551             }
552         }
553         if ( !gate ) {  /* new gateway */
554             if (( gate = (struct gate *)malloc( sizeof( struct gate ))) == NULL ) {
555                 LOG(log_error, logtype_atalkd, "rtmp_packet: malloc: %s", strerror(errno) );
556                 return -1;
557             }
558             gate->g_next = iface->i_gate;
559             gate->g_prev = NULL;
560             gate->g_rt = NULL;
561             gate->g_iface = iface;      /* need this? */
562             gate->g_sat = *from;
563             if ( iface->i_gate ) {
564                 iface->i_gate->g_prev = gate;
565             }
566             iface->i_gate = gate;
567             LOG(log_info, logtype_atalkd, "rtmp_packet gateway %u.%u up",
568                     ntohs( gate->g_sat.sat_addr.s_net ),
569                     gate->g_sat.sat_addr.s_node );
570         }
571
572         /*
573          * Reset the timeout on this gateway.  We'll remove the gateway
574          * entry, if the timeout gets to RTMPTAB_BAD.
575          */
576         gate->g_state = RTMPTAB_GOOD;
577
578         /*
579          * Parse remaining tuples.
580          */
581         for (;;) {
582             /*
583              * Is route on this gateway?
584              */
585             for ( rtmp = gate->g_rt; rtmp; rtmp = rtmp->rt_next ) {
586                 if ( ntohs( rtmp->rt_firstnet ) <= ntohs( rt.rt_net ) &&
587                         ntohs( rtmp->rt_lastnet ) >= ntohs( rt.rt_net )) {
588                     break;
589                 }
590                 if (( rt.rt_dist & 0x80 ) &&
591                         ntohs( rtmp->rt_firstnet ) <= ntohs( xrt.rt_net ) &&
592                         ntohs( rtmp->rt_lastnet ) >= ntohs( xrt.rt_net )) {
593                     break;
594                 }
595             }
596
597             if ( rtmp ) {       /* found it */
598                 /*
599                  * Check for range conflicts.  (This is getting a little
600                  * ugly.)
601                  */
602                 if ( rtmp->rt_firstnet != rt.rt_net ) {
603                     LOG(log_info, logtype_atalkd, "rtmp_packet firstnet mismatch %u!=%u",
604                             ntohs( rtmp->rt_firstnet ), ntohs( rt.rt_net ));
605                     return 1;
606                 }
607                 if ( rt.rt_dist & 0x80 ) {
608                     if (( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
609                         LOG(log_info, logtype_atalkd, "rtmp_packet extended mismatch %u",
610                                 ntohs( rtmp->rt_firstnet ));
611                         return 1;
612                     }
613                     if ( rtmp->rt_lastnet != xrt.rt_net ) {
614                         LOG(log_info, logtype_atalkd, "rtmp_packet lastnet mismatch %u!=%u",
615                             ntohs( rtmp->rt_lastnet ), ntohs( xrt.rt_net ));
616                         return 1;
617                     }
618                 } else {
619                     if ( rtmp->rt_flags & RTMPTAB_EXTENDED ) {
620                         LOG(log_info, logtype_atalkd, "rtmp_packet !extended mismatch %u",
621                                 ntohs( rtmp->rt_firstnet ));
622                         return 1;
623                     }
624                     if ( rtmp->rt_lastnet != rt.rt_net ) {
625                         LOG(log_info, logtype_atalkd, "rtmp_packet lastnet mismatch %u!=%u",
626                             ntohs( rtmp->rt_lastnet ), ntohs( rt.rt_net ));
627                         return 1;
628                     }
629                 }
630
631                 rtmp->rt_state = RTMPTAB_GOOD;
632
633                 /*
634                  * Check hop count.  If the count has changed, update
635                  * the routing database.
636                  */
637                 if (( rtmp->rt_hops != ( rt.rt_dist & 0x7f ) + 1 ) &&
638                         ( rtmp->rt_hops != RTMPHOPS_POISON ||
639                         ( rt.rt_dist & 0x7f ) + 1 <= RTMPHOPS_MAX )) {
640                     if ( rtmp->rt_iprev ) {     /* route is in use */
641                         if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
642                             /*
643                              * If this was POISON, we've deleted it from
644                              * the kernel.  Add it back in.
645                              */
646                             if ( rtmp->rt_hops == RTMPHOPS_POISON ) {
647                                 gateroute( RTMP_ADD, rtmp );
648                             }
649                             rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
650                         } else {
651                             /*
652                              * Hop count has gone up for this route.
653                              * Search for a new best route.  If we can't
654                              * find one, just keep this route.  "poison"
655                              * route are deleted in as_timer().
656                              */
657                             if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
658                                 rtmp->rt_hops = RTMPHOPS_POISON;
659                             } else {
660                                 rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
661                             }
662                             if (rtmp_replace( rtmp ) < 0) {
663                               LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_replace");
664                               return -1;
665                             }
666                         }
667                     } else {                    /* route not in use */
668                         rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
669                         if ( rtmp->rt_hops > ( rt.rt_dist & 0x7f ) + 1 ) {
670                           if (rtmp_new( rtmp ) < 0) {
671                               LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_new");
672                               return -1;
673                           }
674                         }
675                     }
676                 }
677
678                 /*
679                  * Make the *next* node the head, since
680                  * we're not likely to be asked for the same tuple twice
681                  * in a row.
682                  */
683                 if ( rtmp->rt_next != NULL ) {
684                     gate->g_rt->rt_prev->rt_next = gate->g_rt;
685                     gate->g_rt = rtmp->rt_next;
686                     rtmp->rt_next = NULL;
687                 }
688             } else if (( rt.rt_dist & 0x7f ) + 1 > RTMPHOPS_MAX ) {
689                 LOG(log_info, logtype_atalkd, "rtmp_packet bad hop count from %u.%u for %u",
690                         ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
691                         ntohs( rt.rt_net ));
692             } else {            /* new for router */
693                 if (( rtmp = newrt(iface)) == NULL ) {
694                     LOG(log_error, logtype_atalkd, "rtmp_packet: newrt: %s", strerror(errno) );
695                     return -1;
696                 }
697                 rtmp->rt_firstnet = rt.rt_net;
698                 if ( rt.rt_dist & 0x80 ) {
699                     rtmp->rt_lastnet = xrt.rt_net;
700                     rtmp->rt_flags = RTMPTAB_EXTENDED;
701                 } else {
702                     rtmp->rt_lastnet = rt.rt_net;
703                 }
704                 rtmp->rt_hops = ( rt.rt_dist & 0x7f ) + 1;
705                 rtmp->rt_state = RTMPTAB_GOOD;
706                 rtmp->rt_gate = gate;
707
708                 /*
709                  * Add rtmptab entry to end of list (leave head alone).
710                  */
711                 if ( gate->g_rt == NULL ) {
712                     rtmp->rt_prev = rtmp;
713                     gate->g_rt = rtmp;
714                 } else {
715                     rtmp->rt_prev = gate->g_rt->rt_prev;
716                     gate->g_rt->rt_prev->rt_next = rtmp;
717                     gate->g_rt->rt_prev = rtmp;
718                 }
719                 
720                 if (rtmp_new( rtmp ) < 0) {
721                     LOG(log_error, logtype_atalkd, "rtmp_packet: rtmp_new");
722                     return -1;
723                 }
724             }
725
726             if ( data + SZ_RTMPTUPLE > end ) {
727                 break;
728             }
729             memcpy( &rt, data, SZ_RTMPTUPLE );
730             data += SZ_RTMPTUPLE;
731             if ( rt.rt_dist & 0x80 ) {
732                 if ( data + SZ_RTMPTUPLE > end ) {
733                     LOG(log_info, logtype_atalkd, "rtmp_packet missing range-end" );
734                     return 1;
735                 }
736                 memcpy( &xrt, data, SZ_RTMPTUPLE );
737                 data += SZ_RTMPTUPLE;
738             }
739         }
740
741         /*
742          * Make sure we've processed the whole packet.
743          */
744         if ( data != end ) {
745             LOG(log_info, logtype_atalkd, "rtmp_packet length and count mismatch" );
746         }
747         break;
748
749     case DDPTYPE_RTMPR :
750         /*
751          * Request and RDR.
752          */
753         if (((iface->i_flags & IFACE_ISROUTER) == 0) ||
754             iface->i_rt->rt_zt == NULL ||
755             ( iface->i_flags & IFACE_CONFIG ) == 0 ) {
756             return 0;
757         }
758         if ( *data == 1 ) {
759             data = packet;
760             *data++ = DDPTYPE_RTMPRD;
761             rh.rh_net = iface->i_addr.sat_addr.s_net;
762             rh.rh_nodelen = 8;
763             rh.rh_node = iface->i_addr.sat_addr.s_node;
764             memcpy( data, &rh, sizeof( struct rtmp_head ));
765             data += sizeof( struct rtmp_head );
766
767             if ( iface->i_flags & IFACE_PHASE2 ) {
768                 rt.rt_net = iface->i_rt->rt_firstnet;
769                 rt.rt_dist = 0x80;
770                 memcpy( data, &rt, SZ_RTMPTUPLE );
771                 data += SZ_RTMPTUPLE;
772
773                 rt.rt_net = iface->i_rt->rt_lastnet;
774                 rt.rt_dist = 0x82;
775                 memcpy( data, &rt, SZ_RTMPTUPLE );
776                 data += SZ_RTMPTUPLE;
777             }
778             if ( sendto( ap->ap_fd, packet, data - packet, 0,
779                     (struct sockaddr *)from,
780                     sizeof( struct sockaddr_at )) < 0 ) {
781                 LOG(log_error, logtype_atalkd, "as_timer sendto: %s", strerror(errno) );
782             }
783         } else if ( *data == 2 || *data == 3 ) {
784 #ifdef DEBUG
785             printf( "rtmp_packet rdr (%d) from %u.%u\n",
786                     *data, ntohs( from->sat_addr.s_net ),
787                     from->sat_addr.s_node );
788 #endif /* DEBUG */
789         } else {
790             LOG(log_info, logtype_atalkd, "rtmp_packet unknown request from %u.%u",
791                     ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
792         }
793         break;
794
795     default :
796         LOG(log_info, logtype_atalkd, "rtmp_packet bad ddp type from %u.%u",
797                     ntohs( from->sat_addr.s_net ), from->sat_addr.s_node );
798         return 0;
799     }
800
801     return 0;
802 }
803
804 int rtmp_request( struct interface *iface)
805 {
806     struct sockaddr_at  sat;
807     struct atport       *ap;
808     char                *data, packet[ 2 ];
809
810     LOG(log_info, logtype_atalkd, "rtmp_request for %s", iface->i_name );
811
812     for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
813         if ( ap->ap_packet == rtmp_packet ) {
814             break;
815         }
816     }
817     if ( ap == NULL ) {
818         LOG(log_error, logtype_atalkd, "rtmp_request can't find rtmp socket!" );
819         return -1;
820     }
821
822     data = packet;
823     *data++ = DDPTYPE_RTMPR;
824     *data++ = RTMPROP_REQUEST;
825
826     /*
827      * There is a problem with the net zero "hint" hack.
828      */
829     memset( &sat, 0, sizeof( struct sockaddr_at ));
830 #ifdef BSD4_4
831     sat.sat_len = sizeof( struct sockaddr_at );
832 #endif /* BSD4_4 */
833     sat.sat_family = AF_APPLETALK;
834     sat.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
835     sat.sat_addr.s_node = ATADDR_BCAST;
836     sat.sat_port = ap->ap_port;
837     if ( sendto( ap->ap_fd, packet, data - packet, 0, (struct sockaddr *)&sat,
838             sizeof( struct sockaddr_at )) < 0 ) {
839         LOG(log_error, logtype_atalkd, "rtmp_request sendto: %s", strerror(errno) );
840         return -1;
841     }
842     return 0;
843 }
844
845
846 int looproute(struct interface *iface, unsigned int cmd)
847 {
848     struct sockaddr_at  dst, loop;
849
850     if ( cmd == RTMP_DEL && ( iface->i_flags & IFACE_LOOP ) == 0 ) {
851         LOG(log_error, logtype_atalkd, "looproute panic no route" );
852         return -1;
853     }
854
855     if ( cmd == RTMP_ADD && ( iface->i_flags & IFACE_LOOP )) {
856         LOG(log_error, logtype_atalkd, "looproute panic two routes" );
857         return -1;
858     }
859
860     memset( &dst, 0, sizeof( struct sockaddr_at ));
861 #ifdef BSD4_4
862     dst.sat_len = sizeof( struct sockaddr_at );
863 #endif /* BSD4_4 */
864     dst.sat_family = AF_APPLETALK;
865     dst.sat_addr.s_net = iface->i_addr.sat_addr.s_net;
866     dst.sat_addr.s_node = iface->i_addr.sat_addr.s_node;
867     memset( &loop, 0, sizeof( struct sockaddr_at ));
868 #ifdef BSD4_4
869     loop.sat_len = sizeof( struct sockaddr_at );
870 #endif /* BSD4_4 */
871     loop.sat_family = AF_APPLETALK;
872     loop.sat_addr.s_net = htons( ATADDR_ANYNET );
873     loop.sat_addr.s_node = ATADDR_ANYNODE;
874
875 #ifndef BSD4_4
876     if ( route( cmd,
877                 (struct sockaddr *) &dst,
878                 (struct sockaddr *) &loop,
879                 RTF_UP | RTF_HOST ) ) {
880         return( 1 );
881     }
882 #else /* ! BSD4_4 */
883     if ( route( cmd,
884                 (struct sockaddr_at *) &dst,
885                 (struct sockaddr_at *) &loop,
886                 RTF_UP | RTF_HOST ) ) {
887         return ( 1);
888     }
889 #endif /* BSD4_4 */
890     if ( cmd == RTMP_ADD ) {
891         iface->i_flags |= IFACE_LOOP;
892     }
893     if ( cmd == RTMP_DEL ) {
894         iface->i_flags &= ~IFACE_LOOP;
895     }
896     return( 0 );
897 }
898
899 int gateroute(unsigned int command, struct rtmptab *rtmp)
900 {
901     struct sockaddr_at  dst, gate;
902     unsigned short      net;
903
904     if ( command == RTMP_DEL && ( rtmp->rt_flags & RTMPTAB_ROUTE ) == 0 ) {
905         return( -1 );
906     }
907     if ( command == RTMP_ADD && ( rtmp->rt_flags & RTMPTAB_ROUTE )) {
908         return( -1 );
909     }
910
911     net = ntohs( rtmp->rt_firstnet );
912     /*
913      * Since we will accept routes from gateways who advertise their
914      * address as 0.YY, we must munge the gateway address we give to
915      * the kernel.  Otherwise, we'll get a bunch of routes to the loop
916      * back interface, and who wants that?
917      */
918     memset( &gate, 0, sizeof( struct sockaddr_at ));
919 #ifdef BSD4_4
920     gate.sat_len = sizeof( struct sockaddr_at );
921 #endif /* BSD4_4 */
922     gate.sat_family = AF_APPLETALK;
923     gate.sat_addr.s_net = rtmp->rt_gate->g_sat.sat_addr.s_net;
924     gate.sat_addr.s_node = rtmp->rt_gate->g_sat.sat_addr.s_node;
925     if ( gate.sat_addr.s_net == 0 ) {
926         gate.sat_addr.s_net = net;
927     }
928
929     memset( &dst, 0, sizeof( struct sockaddr_at ));
930 #ifdef BSD4_4
931     dst.sat_len = sizeof( struct sockaddr_at );
932 #endif /* BSD4_4 */
933     dst.sat_family = AF_APPLETALK;
934     dst.sat_addr.s_node = ATADDR_ANYNODE;
935
936     do {
937         dst.sat_addr.s_net = htons( net );
938 #ifndef BSD4_4
939         if ( route( command,
940                     (struct sockaddr *) &dst,
941                     (struct sockaddr *) &gate,
942                     RTF_UP | RTF_GATEWAY )) {
943             LOG(log_error, logtype_atalkd, "route: %u -> %u.%u: %s", net,
944                     ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node,
945                     strerror(errno) );
946             continue;
947         }
948 #else /* ! BSD4_4 */
949         if ( route( command,
950                     (struct sockaddr_at *) &dst,
951                     (struct sockaddr_at *) &gate,
952                     RTF_UP | RTF_GATEWAY )) {
953             LOG(log_error, logtype_atalkd, "route: %u -> %u.%u: %s", net,
954                     ntohs( gate.sat_addr.s_net ), gate.sat_addr.s_node, strerror(errno) );
955             continue;
956         }
957 #endif /* ! BSD4_4 */
958     } while ( net++ < ntohs( rtmp->rt_lastnet ));
959
960     if ( command == RTMP_ADD ) {
961         rtmp->rt_flags |= RTMPTAB_ROUTE;
962     }
963     if ( command == RTMP_DEL ) {
964         rtmp->rt_flags &= ~RTMPTAB_ROUTE;
965     }
966
967     return( 0 );
968 }
969
970     struct rtmptab *
971 newrt(const struct interface *iface)
972 {
973     struct rtmptab      *rtmp;
974
975     if (( rtmp = (struct rtmptab *)calloc(1, sizeof(struct rtmptab))) == NULL ) {
976         return( NULL );
977     }
978
979     rtmp->rt_iface = iface;
980     return( rtmp );
981 }