1/*
2 * $Id: nbp.c,v 1.13 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 <netatalk/at.h>
26#include <atalk/ddp.h>
27#include <atalk/atp.h>
28#include <atalk/nbp.h>
29#include <atalk/util.h>
30
31#ifdef __svr4__
32#include <sys/sockio.h>
33#endif /* __svr4__ */
34
35#include "atserv.h"
36#include "interface.h"
37#include "list.h"
38#include "rtmp.h"
39#include "gate.h"
40#include "zip.h"
41#include "nbp.h"
42#include "multicast.h"
43
44extern int  transition;
45
46struct nbptab   *nbptab = NULL;
47
48static
49void nbp_ack( int fd, int nh_op, int nh_id, struct sockaddr_at *to)
50{
51    struct nbphdr   nh;
52    char        *data, packet[ SZ_NBPHDR + 1 ];
53
54    nh.nh_op = nh_op;
55    nh.nh_cnt = 0;
56    nh.nh_id = nh_id;
57    data = packet;
58    *data++ = DDPTYPE_NBP;
59    memcpy( data, &nh, SZ_NBPHDR );
60    data += SZ_NBPHDR;
61    if ( sendto( fd, packet, data - packet, 0, (struct sockaddr *)to,
62                 sizeof( struct sockaddr_at )) < 0 ) {
63        LOG(log_error, logtype_atalkd, "sendto: %s", strerror(errno) );
64    }
65}
66
67int nbp_packet(struct atport *ap, struct sockaddr_at *from, char *data, int len)
68{
69    struct nbphdr   nh;
70    struct nbptuple nt;
71    struct nbpnve   nn;
72    struct sockaddr_at  sat;
73    struct nbptab   *ntab;
74    struct ziptab   *zt=NULL;
75    struct interface    *iface;
76    struct list     *l;
77    struct rtmptab  *rtmp;
78    char        *end, *nbpop, *zonep, packet[ ATP_BUFSIZ ];
79    int         n, i, cc, locallkup;
80    u_char      tmplen;
81
82    end = data + len;
83    if ( data >= end ) {
84        LOG(log_info, logtype_atalkd, "nbp_packet malformed packet" );
85        return 1;
86    }
87    if ( *data++ != DDPTYPE_NBP ) {
88        LOG(log_info, logtype_atalkd, "nbp_packet bad ddp type" );
89        return 1;
90    }
91
92    if ( data + SZ_NBPHDR + SZ_NBPTUPLE > end ) {
93        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
94        return 1;
95    }
96    memcpy( &nh, data, SZ_NBPHDR );
97    nbpop = data;           /* remember for fwd and brrq */
98    data += SZ_NBPHDR;
99    if ( nh.nh_cnt != 1 ) {
100        LOG(log_info, logtype_atalkd, "nbp_packet: bad tuple count (%d/%d)", nh.nh_cnt,
101            nh.nh_op );
102        return 1;
103    }
104
105    memcpy( &nt, data, SZ_NBPTUPLE );
106    data += SZ_NBPTUPLE;
107
108    memset( &nn.nn_sat, 0, sizeof( struct sockaddr_at ));
109#ifdef BSD4_4
110    nn.nn_sat.sat_len = sizeof( struct sockaddr_at );
111#endif /* BSD4_4 */
112    nn.nn_sat.sat_family = AF_APPLETALK;
113    nn.nn_sat.sat_addr.s_net = nt.nt_net;
114    nn.nn_sat.sat_addr.s_node = nt.nt_node;
115    nn.nn_sat.sat_port = nt.nt_port;
116
117    /* object */
118    tmplen = (u_char) *data;
119    if ( data >= end || tmplen > 32 || data + tmplen > end ) {
120        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
121        return 1;
122    }
123    nn.nn_objlen = tmplen;
124    data++;
125    memcpy( nn.nn_obj, data, nn.nn_objlen );
126    data += nn.nn_objlen;
127
128    /* type */
129    tmplen = (u_char) *data;
130    if ( data >= end || tmplen > 32 || data + tmplen > end ) {
131        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
132        return 1;
133    }
134    nn.nn_typelen = tmplen;
135    data++;
136    memcpy( nn.nn_type, data, nn.nn_typelen );
137    data += nn.nn_typelen;
138
139    /* zone */
140    tmplen = (u_char) *data;
141    if ( data >= end || tmplen > 32 || data + tmplen > end ) {
142        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
143        return 1;
144    }
145    zonep = data;           /* remember for fwd */
146    nn.nn_zonelen = tmplen;
147    data++;
148    memcpy( nn.nn_zone, data, nn.nn_zonelen );
149    data += nn.nn_zonelen;
150
151    if ( data != end ) {
152        LOG(log_info, logtype_atalkd, "nbp_packet: malformed packet" );
153        return 1;
154    }
155
156    locallkup = 0;
157    switch ( nh.nh_op ) {
158
159    case NBPOP_RGSTR :
160        /*
161         * Find the ziptab entry for the zone we're trying to register in.
162         */
163        if ( nn.nn_zonelen == 0 ||
164             ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
165            if ( interfaces->i_next->i_rt->rt_zt ) {
166                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
167            } else {
168                zt = NULL;
169            }
170        } else {
171            for ( zt = ziptab; zt; zt = zt->zt_next ) {
172                if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
173                                                                    nn.nn_zone, zt->zt_len ) == 0 ) {
174                    break;
175                }
176            }
177            if ( zt == NULL ) {
178                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
179                return 0;
180            }
181        }
182
183        /*
184         * Observe that we don't have to do any local-zone verification
185         * if the zone aleady has a multicast address set.
186         */
187        if ( zt != NULL && zt->zt_bcast == NULL ) {
188            /*
189             * Check if zone is associated with any of our local interfaces.
190             */
191            for ( iface = interfaces; iface; iface = iface->i_next ) {
192                for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
193                    if ( zt == (struct ziptab *)l->l_data ) {
194                        break;
195                    }
196                }
197                if ( l != NULL ) {
198                    break;
199                }
200            }
201            if ( iface == NULL ) {
202                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
203                return 0;
204            }
205
206            /* calculate and save multicast address */
207            if (zone_bcast(zt) < 0) {
208                LOG(log_error, logtype_atalkd, "nbp_packet: zone_bcast");
209                return -1;
210            }
211
212            for ( iface = interfaces; iface; iface = iface->i_next ) {
213                if (( iface->i_flags & IFACE_PHASE2 ) == 0 ) {
214                    continue;
215                }
216                for ( l = iface->i_rt->rt_zt; l; l = l->l_next ) {
217                    if ( zt == (struct ziptab *)l->l_data ) {
218                        /* add multicast */
219                        if (addmulti(iface->i_name, zt->zt_bcast) < 0) {
220                            LOG(log_error, logtype_atalkd, "nbp_packet: addmulti: %s",
221                                strerror(errno) );
222                            return -1;
223                        }
224                    }
225                }
226            }
227        }
228
229        if (( ntab = (struct nbptab *)malloc( sizeof( struct nbptab )))
230            == NULL ) {
231            LOG(log_error, logtype_atalkd, "nbp_packet: malloc: %s", strerror(errno) );
232            return -1;
233        }
234        memcpy( &ntab->nt_nve, &nn, sizeof( struct nbpnve ));
235        ntab->nt_iface = ap->ap_iface;
236        ntab->nt_next = nbptab;
237        ntab->nt_prev = NULL;
238        if ( nbptab ) {
239            nbptab->nt_prev = ntab;
240        }
241        nbptab = ntab;
242
243        nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
244        break;
245
246    case NBPOP_UNRGSTR :
247        /* deal with local zone info */
248        if (( nn.nn_zonelen == 1 && *nn.nn_zone == '*' ) ||
249            ( nn.nn_zonelen == 0 )) {
250            locallkup = 1;
251            if ( interfaces->i_next->i_rt->rt_zt ) {
252                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
253            } else {
254                zt = NULL;
255            }
256        }
257
258        /* remove from our data, perhaps removing a multicast address */
259        for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
260            if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
261                 strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
262                                 nn.nn_objlen )) {
263                continue;
264            }
265            if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
266                 strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
267                                 nn.nn_typelen )) {
268                continue;
269            }
270            /*
271             * I *think* we really do check the zone, here.
272             *
273             * i changed it to better handle local zone cases as well.
274             * -- asun
275             */
276
277            /* match local zones */
278            if (locallkup) {
279                /* ntab is also local zone */
280                if (( ntab->nt_nve.nn_zonelen == 1 &&
281                      *ntab->nt_nve.nn_zone == '*' ) ||
282                    (ntab->nt_nve.nn_zonelen == 0))
283                    break;
284
285                /* ntab is default zone */
286                if (zt && (zt->zt_len == ntab->nt_nve.nn_zonelen) &&
287                    (strndiacasecmp(ntab->nt_nve.nn_zone, zt->zt_name,
288                                    zt->zt_len) == 0)) {
289                    break;
290                }
291            }
292
293            /* match particular zone */
294            if ((ntab->nt_nve.nn_zonelen == nn.nn_zonelen) &&
295                (strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
296                                 nn.nn_zonelen ) == 0)) {
297                break;
298            }
299        }
300        if ( ntab == NULL ) {
301            nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
302            return 0;
303        }
304
305        if ( ntab->nt_next != NULL ) {
306            ntab->nt_next->nt_prev = ntab->nt_prev;
307        }
308        if ( ntab->nt_prev != NULL ) {
309            ntab->nt_prev->nt_next = ntab->nt_next;
310        }
311        if ( ntab == nbptab ) {
312            nbptab = ntab->nt_next;
313        }
314
315        /*
316         * Check for another nbptab entry with the same zone.  If
317         * there isn't one, find the ziptab entry for the zone and
318         * remove the multicast address from the appropriate interfaces.
319         * XXX
320         */
321
322        nbp_ack( ap->ap_fd, NBPOP_OK, (int)nh.nh_id, from );
323        break;
324
325    case NBPOP_BRRQ :
326        /*
327         * Couple of things:  1. Unless we have the -t flag (which is sort
328         * of a misnomer, since you need it if you're doing any phase 1
329         * work), always send NBPOP_FWD.  2. If we get a zone of '*',
330         * and we know what the sender meant by '*', we copy the real
331         * zone into the packet.
332         */
333        if ( nn.nn_zonelen == 0 ||
334             ( nn.nn_zonelen == 1 && *nn.nn_zone == '*' )) {
335            iface = ap->ap_iface;
336            if ( iface && iface->i_rt->rt_zt ) {
337                zt = (struct ziptab *)iface->i_rt->rt_zt->l_data;
338            } else if ( interfaces->i_next->i_rt->rt_zt ) {
339                zt = (struct ziptab *)interfaces->i_next->i_rt->rt_zt->l_data;
340            } else {
341                zt = NULL;
342            }
343
344            /*
345             * Copy zone into packet.  Note that we're changing len, data, and
346             * nbpop.  Later, we'll use ( data - len ) to mean the beginning
347             * of this packet.
348             */
349            if ( zt ) {
350                memcpy( packet, data - len, len );
351                nbpop = packet + ( len - ( data - nbpop ));
352                data = packet + ( len - ( data - zonep ));
353                *data++ = zt->zt_len;
354                memcpy( data, zt->zt_name, zt->zt_len );
355                data += zt->zt_len;
356                len = data - packet;
357            }
358        } else {
359            for ( zt = ziptab; zt; zt = zt->zt_next ) {
360                if ( zt->zt_len == nn.nn_zonelen && strndiacasecmp( zt->zt_name,
361                                                                    nn.nn_zone, zt->zt_len ) == 0 ) {
362                    break;
363                }
364            }
365            if ( zt == NULL ) {
366                nbp_ack( ap->ap_fd, NBPOP_ERROR, (int)nh.nh_id, from );
367                return 0;
368            }
369        }
370
371        /*
372         * If we've got no zones, send out LKUP on the local net.
373         * Otherwise, look through the zone table.
374         */
375        if ( zt == NULL ) {
376#ifdef BSD4_4
377            sat.sat_len = sizeof( struct sockaddr_at );
378#endif /* BSD4_4 */
379            sat.sat_family = AF_APPLETALK;
380            sat.sat_port = ap->ap_port;
381
382            nh.nh_op = NBPOP_LKUP;
383            memcpy( nbpop, &nh, SZ_NBPHDR );
384            sat.sat_addr.s_net = 0;         /* XXX */
385            sat.sat_addr.s_node = ATADDR_BCAST;
386
387            /* Find the first non-loopback ap */
388            for ( iface = interfaces; iface; iface = iface->i_next ) {
389                if ((( iface->i_flags & IFACE_LOOPBACK ) == 0) &&
390                    (iface == ap->ap_iface ||
391                     (iface->i_flags & IFACE_ISROUTER))) {
392                    break;
393                }
394            }
395            if ( iface == NULL ) {
396                return 0;
397            }
398            for ( ap = iface->i_ports; ap; ap = ap->ap_next ) {
399                if ( ap->ap_packet == nbp_packet ) {
400                    break;
401                }
402            }
403            if (ap == NULL)
404                return 0;
405
406            if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)&sat,
407                         sizeof( struct sockaddr_at )) < 0 ) {
408                LOG(log_error, logtype_atalkd, "nbp brrq sendto: %s", strerror(errno) );
409            }
410
411            locallkup = 1;
412        } else {
413#ifdef BSD4_4
414            sat.sat_len = sizeof( struct sockaddr_at );
415#endif /* BSD4_4 */
416            sat.sat_family = AF_APPLETALK;
417            sat.sat_port = ap->ap_port;
418            for ( l = zt->zt_rt; l; l = l->l_next ) {
419                rtmp = (struct rtmptab *)l->l_data;
420
421                if ( rtmp->rt_gate == NULL ) {
422                    for ( iface = interfaces; iface;
423                          iface = iface->i_next ) {
424                        if ( iface->i_rt == rtmp ) {
425                            break;
426                        }
427                    }
428                    if ( !iface ) {
429                        LOG(log_error, logtype_atalkd, "nbp_packet: \
430Can't find route's interface!" );
431                        return -1;
432                    }
433                    ap = iface->i_ports;
434                } else {
435                    ap = rtmp->rt_gate->g_iface->i_ports;
436                }
437                for ( ; ap; ap = ap->ap_next ) {
438                    if ( ap->ap_packet == nbp_packet ) {
439                        break;
440                    }
441                }
442                if ( !ap ) {
443                    LOG(log_error, logtype_atalkd, "nbp_packet: Can't find port!" );
444                    return -1;
445                }
446
447                if ( transition &&
448                     ( rtmp->rt_flags & RTMPTAB_EXTENDED ) == 0 ) {
449                    if ( rtmp->rt_gate == NULL ) {
450                        locallkup = 1;
451                    }
452                    nh.nh_op = NBPOP_LKUP;
453                    memcpy( nbpop, &nh, SZ_NBPHDR );
454                    sat.sat_addr.s_net = rtmp->rt_firstnet;
455                    sat.sat_addr.s_node = ATADDR_BCAST;
456                } else {
457                    if ( rtmp->rt_gate == NULL ) {
458                        nh.nh_op = NBPOP_LKUP;
459                        memcpy( nbpop, &nh, SZ_NBPHDR );
460                        sat.sat_addr.s_net = 0;
461                        sat.sat_addr.s_node = ATADDR_BCAST;
462                        locallkup = 1;
463                    } else {
464                        nh.nh_op = NBPOP_FWD;
465                        memcpy( nbpop, &nh, SZ_NBPHDR );
466                        sat.sat_addr.s_net = rtmp->rt_firstnet;
467                        sat.sat_addr.s_node = 0;
468                    }
469                }
470
471                if ( sendto( ap->ap_fd, data - len, len, 0,
472                             (struct sockaddr *)&sat,
473                             sizeof( struct sockaddr_at )) < 0 ) {
474                    LOG(log_error, logtype_atalkd, "nbp brrq sendto %u.%u: %s",
475                        ntohs( sat.sat_addr.s_net ), sat.sat_addr.s_node,
476                        strerror(errno) );
477                    continue;
478                }
479            }
480        }
481
482        if ( !locallkup ) {
483            break;
484        }
485        /*FALL THROUGH*/
486
487    case NBPOP_FWD :
488        /* send lkup on net. we need to make sure we're a router. */
489        if ( !locallkup && (ap->ap_iface->i_flags & IFACE_ISROUTER)) {
490            nh.nh_op = NBPOP_LKUP;
491            memcpy( nbpop, &nh, SZ_NBPHDR );
492            from->sat_addr.s_net = 0;
493            from->sat_addr.s_node = ATADDR_BCAST;
494            if ( sendto( ap->ap_fd, data - len, len, 0, (struct sockaddr *)from,
495                         sizeof( struct sockaddr_at )) < 0 ) {
496                LOG(log_error, logtype_atalkd, "nbp fwd sendto %u.%u: %s",
497                    ntohs( from->sat_addr.s_net ), from->sat_addr.s_node,
498                    strerror(errno) );
499                return 0;
500            }
501        }
502        /*FALL THROUGH*/
503
504    case NBPOP_LKUP :
505        /* search our data */
506        n = i = 0;
507        data = packet + 1 + SZ_NBPHDR;
508        end = packet + sizeof( packet );
509
510        for ( ntab = nbptab; ntab; ntab = ntab->nt_next ) {
511            /* don't send out entries if we don't want to route. */
512            if ((ap->ap_iface != ntab->nt_iface) &&
513                (ntab->nt_iface->i_flags & IFACE_ISROUTER) == 0) {
514                continue;
515            }
516
517            if ( nn.nn_objlen != 1 || *nn.nn_obj != '=' ) {
518                if ( ntab->nt_nve.nn_objlen != nn.nn_objlen ||
519                     strndiacasecmp( ntab->nt_nve.nn_obj, nn.nn_obj,
520                                     nn.nn_objlen )) {
521                    continue;
522                }
523            }
524
525            if ( nn.nn_typelen != 1 || *nn.nn_type != '=' ) {
526                if ( ntab->nt_nve.nn_typelen != nn.nn_typelen ||
527                     strndiacasecmp( ntab->nt_nve.nn_type, nn.nn_type,
528                                     nn.nn_typelen )) {
529                    continue;
530                }
531            }
532
533            if ( nn.nn_zonelen != 0 &&
534                 ( nn.nn_zonelen != 1 || *nn.nn_zone != '*' )) {
535                if ( ntab->nt_nve.nn_zonelen == 0 ||
536                     ( ntab->nt_nve.nn_zonelen == 1 &&
537                       *ntab->nt_nve.nn_zone == '*' )) {
538                    if ( interfaces->i_next->i_rt->rt_zt ) {
539                        zt = (struct ziptab *)interfaces->i_next->i_rt->
540                            rt_zt->l_data;
541                        if ( zt->zt_len != nn.nn_zonelen ||
542                             strndiacasecmp( zt->zt_name, nn.nn_zone,
543                                             zt->zt_len )) {
544                            continue;
545                        }
546                    }
547                } else {
548                    if ( ntab->nt_nve.nn_zonelen != nn.nn_zonelen ||
549                         strndiacasecmp( ntab->nt_nve.nn_zone, nn.nn_zone,
550                                         nn.nn_zonelen )) {
551                        continue;
552                    }
553                }
554            }
555
556            /*
557             * Another tuple won't fit. Send what we've already
558             * got, and start the next packet.
559             */
560            if ( n > 14 || data + SZ_NBPTUPLE + 3 + ntab->nt_nve.nn_objlen +
561                 ntab->nt_nve.nn_typelen + ntab->nt_nve.nn_zonelen > end ) {
562                nh.nh_op = NBPOP_LKUPREPLY;
563                nh.nh_cnt = n;
564                cc = data - packet;
565                data = packet;
566                *data++ = DDPTYPE_NBP;
567                memcpy( data, &nh, SZ_NBPHDR );
568
569                if ( sendto( ap->ap_fd, packet, cc, 0,
570                             (struct sockaddr *)&nn.nn_sat,
571                             sizeof( struct sockaddr_at )) < 0 ) {
572                    LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
573                        ntohs( nn.nn_sat.sat_addr.s_net ),
574                        nn.nn_sat.sat_addr.s_node,
575                        strerror(errno) );
576                    return 0;
577                }
578
579                n = 0;
580                data = packet + 1 + SZ_NBPHDR;
581                end = packet + sizeof( packet );
582            }
583
584            nt.nt_net = ntab->nt_nve.nn_sat.sat_addr.s_net;
585            nt.nt_node = ntab->nt_nve.nn_sat.sat_addr.s_node;
586            nt.nt_port = ntab->nt_nve.nn_sat.sat_port;
587            /*
588             * Right now, we'll just give each name a unique enum.  In
589             * the future, we might need to actually assign and save
590             * an enum, based on the associated address.  For the moment,
591             * the enums will be unique and constant, since the order
592             * is fixed.
593             */
594            nt.nt_enum = i++;
595
596            memcpy( data, &nt, SZ_NBPTUPLE );
597            data += SZ_NBPTUPLE;
598
599            *data++ = ntab->nt_nve.nn_objlen;
600            memcpy( data, ntab->nt_nve.nn_obj, ntab->nt_nve.nn_objlen );
601            data += ntab->nt_nve.nn_objlen;
602
603            *data++ = ntab->nt_nve.nn_typelen;
604            memcpy(data, ntab->nt_nve.nn_type, ntab->nt_nve.nn_typelen );
605            data += ntab->nt_nve.nn_typelen;
606
607            /*
608             * Macs won't see something with a zone of 0 length.  We
609             * will always return '*' instead.  Perhaps we should
610             * unconditionally return the real zone?
611             */
612            if ( ntab->nt_nve.nn_zonelen ) {
613                *data++ = ntab->nt_nve.nn_zonelen;
614                memcpy( data, ntab->nt_nve.nn_zone, ntab->nt_nve.nn_zonelen );
615                data += ntab->nt_nve.nn_zonelen;
616            } else {
617                *data++ = 1;
618                *data++ = '*';
619            }
620
621            n++;
622        }
623
624        if ( n != 0 ) {
625            nh.nh_op = NBPOP_LKUPREPLY;
626            nh.nh_cnt = n;
627            cc = data - packet;
628            data = packet;
629            *data++ = DDPTYPE_NBP;
630            memcpy( data, &nh, SZ_NBPHDR );
631
632            if ( sendto( ap->ap_fd, packet, cc, 0,
633                         (struct sockaddr *)&nn.nn_sat,
634                         sizeof( struct sockaddr_at )) < 0 ) {
635                LOG(log_error, logtype_atalkd, "nbp lkup sendto %u.%u: %s",
636                    ntohs( nn.nn_sat.sat_addr.s_net ),
637                    nn.nn_sat.sat_addr.s_node,
638                    strerror(errno) );
639                return 0;
640            }
641        }
642        break;
643
644    default :
645        LOG(log_info, logtype_atalkd, "nbp_packet: bad op (%d)", nh.nh_op );
646        return 1;
647    }
648
649    return 0;
650}
651