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