rdisc.c revision 313379eb6b9da55f7371adef39a92153a0707d4a
1/* 2 * Rdisc (this program) was developed by Sun Microsystems, Inc. and is 3 * provided for unrestricted use provided that this legend is included on 4 * all tape media and as a part of the software program in whole or part. 5 * Users may copy or modify Rdisc without charge, and they may freely 6 * distribute it. 7 * 8 * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE 9 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR 10 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. 11 * 12 * Rdisc is provided with no support and without any obligation on the 13 * part of Sun Microsystems, Inc. to assist in its use, correction, 14 * modification or enhancement. 15 * 16 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE 17 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC 18 * OR ANY PART THEREOF. 19 * 20 * In no event will Sun Microsystems, Inc. be liable for any lost revenue 21 * or profits or other special, indirect and consequential damages, even if 22 * Sun has been advised of the possibility of such damages. 23 * 24 * Sun Microsystems, Inc. 25 * 2550 Garcia Avenue 26 * Mountain View, California 94043 27 */ 28#include <stdio.h> 29#include <errno.h> 30#include <signal.h> 31#include <unistd.h> 32#include <stdlib.h> 33#include <sys/types.h> 34#include <sys/time.h> 35/* Do not use "improved" glibc version! */ 36#include <linux/limits.h> 37 38#include <sys/param.h> 39#include <sys/socket.h> 40#include <sys/file.h> 41#include <malloc.h> 42 43#include <sys/ioctl.h> 44#include <linux/if.h> 45#include <linux/route.h> 46 47#include <netinet/in.h> 48#include <netinet/ip.h> 49#include <netinet/ip_icmp.h> 50 51/* 52 * The next include contains all defs and structures for multicast 53 * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code 54 * is ever used because it does not support multicast 55 * Fraser Gardiner - Sun Microsystems Australia 56 */ 57 58#include <netdb.h> 59#include <arpa/inet.h> 60 61#include <string.h> 62#include <syslog.h> 63 64#include "SNAPSHOT.h" 65 66struct interface 67{ 68 struct in_addr address; /* Used to identify the interface */ 69 struct in_addr localaddr; /* Actual address if the interface */ 70 int preference; 71 int flags; 72 struct in_addr bcastaddr; 73 struct in_addr remoteaddr; 74 struct in_addr netmask; 75 int ifindex; 76 char name[IFNAMSIZ]; 77}; 78 79/* 80 * TBD 81 * Use 255.255.255.255 for broadcasts - not the interface broadcast 82 * address. 83 */ 84 85#define ALLIGN(ptr) (ptr) 86 87static int join(int sock, struct sockaddr_in *sin); 88static void solicitor(struct sockaddr_in *); 89#ifdef RDISC_SERVER 90static void advertise(struct sockaddr_in *, int lft); 91#endif 92static char *pr_name(struct in_addr addr); 93static void pr_pack(char *buf, int cc, struct sockaddr_in *from); 94static void age_table(int time); 95static void record_router(struct in_addr router, int preference, int ttl); 96static void add_route(struct in_addr addr); 97static void del_route(struct in_addr addr); 98static void rtioctl(struct in_addr addr, int op); 99static int support_multicast(void); 100static int sendbcast(int s, char *packet, int packetlen); 101static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *); 102static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp); 103static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp); 104static int is_directly_connected(struct in_addr in); 105static void initlog(void); 106static void discard_table(void); 107static void init(void); 108 109#define ICMP_ROUTER_ADVERTISEMENT 9 110#define ICMP_ROUTER_SOLICITATION 10 111 112#define ALL_HOSTS_ADDRESS "224.0.0.1" 113#define ALL_ROUTERS_ADDRESS "224.0.0.2" 114 115#define MAXIFS 32 116 117#if !defined(__GLIBC__) || __GLIBC__ < 2 118/* For router advertisement */ 119struct icmp_ra 120{ 121 u_char icmp_type; /* type of message, see below */ 122 u_char icmp_code; /* type sub code */ 123 u_short icmp_cksum; /* ones complement cksum of struct */ 124 u_char icmp_num_addrs; 125 u_char icmp_wpa; /* Words per address */ 126 short icmp_lifetime; 127}; 128 129struct icmp_ra_addr 130{ 131 __u32 ira_addr; 132 __u32 ira_preference; 133}; 134#else 135#define icmp_ra icmp 136#endif 137 138/* Router constants */ 139#define MAX_INITIAL_ADVERT_INTERVAL 16 140#define MAX_INITIAL_ADVERTISEMENTS 3 141#define MAX_RESPONSE_DELAY 2 /* Not used */ 142 143/* Host constants */ 144#define MAX_SOLICITATIONS 3 145#define SOLICITATION_INTERVAL 3 146#define MAX_SOLICITATION_DELAY 1 /* Not used */ 147 148#define INELIGIBLE_PREF 0x80000000 /* Maximum negative */ 149 150#define MAX_ADV_INT 600 151 152/* Statics */ 153static int num_interfaces; 154 155static struct interface *interfaces; 156static int interfaces_size; /* Number of elements in interfaces */ 157 158 159#define MAXPACKET 4096 /* max packet size */ 160 161/* fraser */ 162int debugfile; 163 164const char usage[] = 165"Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n" 166#ifdef RDISC_SERVER 167" rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>]\n" 168" [send_address] [receive_address]\n" 169#endif 170; 171 172 173int s; /* Socket file descriptor */ 174struct sockaddr_in whereto;/* Address to send to */ 175 176/* Common variables */ 177int verbose = 0; 178int debug = 0; 179int trace = 0; 180int solicit = 0; 181int ntransmitted = 0; 182int nreceived = 0; 183int forever = 0; /* Never give up on host. If 0 defer fork until 184 * first response. 185 */ 186 187#ifdef RDISC_SERVER 188/* Router variables */ 189int responder; 190int max_adv_int = MAX_ADV_INT; 191int min_adv_int; 192int lifetime; 193int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL; 194int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS; 195int preference = 0; /* Setable with -p option */ 196#endif 197 198/* Host variables */ 199int max_solicitations = MAX_SOLICITATIONS; 200unsigned int solicitation_interval = SOLICITATION_INTERVAL; 201int best_preference = 1; /* Set to record only the router(s) with the 202 best preference in the kernel. Not set 203 puts all routes in the kernel. */ 204 205 206static void graceful_finish(void); 207static void finish(void); 208static void timer(void); 209static void initifs(void); 210static u_short in_cksum(u_short *addr, int len); 211 212static int logging = 0; 213 214#define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \ 215 else fprintf(stderr, fmt); }) 216#define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \ 217 else fprintf(stderr, fmt); }) 218#define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \ 219 else fprintf(stderr, fmt); }) 220static void logperror(char *str); 221 222static __inline__ int isbroadcast(struct sockaddr_in *sin) 223{ 224 return (sin->sin_addr.s_addr == INADDR_BROADCAST); 225} 226 227static __inline__ int ismulticast(struct sockaddr_in *sin) 228{ 229 return IN_CLASSD(ntohl(sin->sin_addr.s_addr)); 230} 231 232static void prusage(void) 233{ 234 fputs(usage, stderr); 235 exit(1); 236} 237 238void do_fork(void) 239{ 240 int t; 241 pid_t pid; 242 long open_max; 243 244 if (trace) 245 return; 246 if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) { 247 if (errno == 0) { 248 (void) fprintf(stderr, "OPEN_MAX is not supported\n"); 249 } 250 else { 251 (void) fprintf(stderr, "sysconf() error\n"); 252 } 253 exit(1); 254 } 255 256 257 if ((pid=fork()) != 0) 258 exit(0); 259 260 for (t = 0; t < open_max; t++) 261 if (t != s) 262 close(t); 263 264 setsid(); 265 initlog(); 266} 267 268void signal_setup(int signo, void (*handler)(void)) 269{ 270 struct sigaction sa; 271 272 memset(&sa, 0, sizeof(sa)); 273 274 sa.sa_handler = (void (*)(int))handler; 275#ifdef SA_INTERRUPT 276 sa.sa_flags = SA_INTERRUPT; 277#endif 278 sigaction(signo, &sa, NULL); 279} 280 281/* 282 * M A I N 283 */ 284char *sendaddress, *recvaddress; 285 286int main(int argc, char **argv) 287{ 288 struct sockaddr_in from; 289 char **av = argv; 290 struct sockaddr_in *to = &whereto; 291 struct sockaddr_in joinaddr; 292 sigset_t sset, sset_empty; 293#ifdef RDISC_SERVER 294 int val; 295 296 min_adv_int =( max_adv_int * 3 / 4); 297 lifetime = (3*max_adv_int); 298#endif 299 300 argc--, av++; 301 while (argc > 0 && *av[0] == '-') { 302 while (*++av[0]) { 303 switch (*av[0]) { 304 case 'd': 305 debug = 1; 306 break; 307 case 't': 308 trace = 1; 309 break; 310 case 'v': 311 verbose++; 312 break; 313 case 's': 314 solicit = 1; 315 break; 316#ifdef RDISC_SERVER 317 case 'r': 318 responder = 1; 319 break; 320#endif 321 case 'a': 322 best_preference = 0; 323 break; 324 case 'b': 325 best_preference = 1; 326 break; 327 case 'f': 328 forever = 1; 329 break; 330 case 'V': 331 printf("rdisc utility, iputils-%s\n", SNAPSHOT); 332 exit(0); 333#ifdef RDISC_SERVER 334 case 'T': 335 argc--, av++; 336 if (argc != 0) { 337 val = strtol(av[0], (char **)NULL, 0); 338 if (val < 4 || val > 1800) { 339 (void) fprintf(stderr, 340 "Bad Max Advertizement Interval\n"); 341 exit(1); 342 } 343 max_adv_int = val; 344 min_adv_int =( max_adv_int * 3 / 4); 345 lifetime = (3*max_adv_int); 346 } else { 347 prusage(); 348 /* NOTREACHED*/ 349 } 350 goto next; 351 case 'p': 352 argc--, av++; 353 if (argc != 0) { 354 val = strtol(av[0], (char **)NULL, 0); 355 preference = val; 356 } else { 357 prusage(); 358 /* NOTREACHED*/ 359 } 360 goto next; 361#endif 362 default: 363 prusage(); 364 /* NOTREACHED*/ 365 } 366 } 367#ifdef RDISC_SERVER 368next: 369#endif 370 argc--, av++; 371 } 372 if( argc < 1) { 373 if (support_multicast()) { 374 sendaddress = ALL_ROUTERS_ADDRESS; 375#ifdef RDISC_SERVER 376 if (responder) 377 sendaddress = ALL_HOSTS_ADDRESS; 378#endif 379 } else 380 sendaddress = "255.255.255.255"; 381 } else { 382 sendaddress = av[0]; 383 argc--; 384 } 385 386 if (argc < 1) { 387 if (support_multicast()) { 388 recvaddress = ALL_HOSTS_ADDRESS; 389#ifdef RDISC_SERVER 390 if (responder) 391 recvaddress = ALL_ROUTERS_ADDRESS; 392#endif 393 } else 394 recvaddress = "255.255.255.255"; 395 } else { 396 recvaddress = av[0]; 397 argc--; 398 } 399 if (argc != 0) { 400 (void) fprintf(stderr, "Extra parameters\n"); 401 prusage(); 402 /* NOTREACHED */ 403 } 404 405#ifdef RDISC_SERVER 406 if (solicit && responder) { 407 prusage(); 408 /* NOTREACHED */ 409 } 410#endif 411 412 if (!(solicit && !forever)) { 413 do_fork(); 414/* 415 * Added the next line to stop forking a second time 416 * Fraser Gardiner - Sun Microsystems Australia 417 */ 418 forever = 1; 419 } 420 421 memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) ); 422 to->sin_family = AF_INET; 423 to->sin_addr.s_addr = inet_addr(sendaddress); 424 425 memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) ); 426 joinaddr.sin_family = AF_INET; 427 joinaddr.sin_addr.s_addr = inet_addr(recvaddress); 428 429#ifdef RDISC_SERVER 430 if (responder) 431 srandom((int)gethostid()); 432#endif 433 434 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { 435 logperror("socket"); 436 exit(5); 437 } 438 439 setlinebuf( stdout ); 440 441 signal_setup(SIGINT, finish ); 442 signal_setup(SIGTERM, graceful_finish ); 443 signal_setup(SIGHUP, initifs ); 444 signal_setup(SIGALRM, timer ); 445 446 sigemptyset(&sset); 447 sigemptyset(&sset_empty); 448 sigaddset(&sset, SIGALRM); 449 sigaddset(&sset, SIGHUP); 450 sigaddset(&sset, SIGTERM); 451 sigaddset(&sset, SIGINT); 452 453 init(); 454 if (join(s, &joinaddr) < 0) { 455 logerr("Failed joining addresses\n"); 456 exit (2); 457 } 458 459 timer(); /* start things going */ 460 461 for (;;) { 462 u_char packet[MAXPACKET]; 463 int len = sizeof (packet); 464 socklen_t fromlen = sizeof (from); 465 int cc; 466 467 cc=recvfrom(s, (char *)packet, len, 0, 468 (struct sockaddr *)&from, &fromlen); 469 if (cc<0) { 470 if (errno == EINTR) 471 continue; 472 logperror("recvfrom"); 473 continue; 474 } 475 476 sigprocmask(SIG_SETMASK, &sset, NULL); 477 pr_pack( (char *)packet, cc, &from ); 478 sigprocmask(SIG_SETMASK, &sset_empty, NULL); 479 } 480 /*NOTREACHED*/ 481} 482 483#define TIMER_INTERVAL 3 484#define GETIFCONF_TIMER 30 485 486static int left_until_advertise; 487 488/* Called every TIMER_INTERVAL */ 489void timer() 490{ 491 static int time; 492 static int left_until_getifconf; 493 static int left_until_solicit; 494 495 496 time += TIMER_INTERVAL; 497 498 left_until_getifconf -= TIMER_INTERVAL; 499 left_until_advertise -= TIMER_INTERVAL; 500 left_until_solicit -= TIMER_INTERVAL; 501 502 if (left_until_getifconf < 0) { 503 initifs(); 504 left_until_getifconf = GETIFCONF_TIMER; 505 } 506#ifdef RDISC_SERVER 507 if (responder && left_until_advertise <= 0) { 508 ntransmitted++; 509 advertise(&whereto, lifetime); 510 if (ntransmitted < initial_advertisements) 511 left_until_advertise = initial_advert_interval; 512 else 513 left_until_advertise = min_adv_int + 514 ((max_adv_int - min_adv_int) * 515 (random() % 1000)/1000); 516 } else 517#endif 518 if (solicit && left_until_solicit <= 0) { 519 ntransmitted++; 520 solicitor(&whereto); 521 if (ntransmitted < max_solicitations) 522 left_until_solicit = solicitation_interval; 523 else { 524 solicit = 0; 525 if (!forever && nreceived == 0) 526 exit(5); 527 } 528 } 529 age_table(TIMER_INTERVAL); 530 alarm(TIMER_INTERVAL); 531} 532 533/* 534 * S O L I C I T O R 535 * 536 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet. 537 * The IP packet will be added on by the kernel. 538 */ 539void 540solicitor(struct sockaddr_in *sin) 541{ 542 static u_char outpack[MAXPACKET]; 543 struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack); 544 int packetlen, i; 545 546 if (verbose) { 547 logtrace("Sending solicitation to %s\n", 548 pr_name(sin->sin_addr)); 549 } 550 icp->type = ICMP_ROUTER_SOLICITATION; 551 icp->code = 0; 552 icp->checksum = 0; 553 icp->un.gateway = 0; /* Reserved */ 554 packetlen = 8; 555 556 /* Compute ICMP checksum here */ 557 icp->checksum = in_cksum( (u_short *)icp, packetlen ); 558 559 if (isbroadcast(sin)) 560 i = sendbcast(s, (char *)outpack, packetlen); 561 else if (ismulticast(sin)) 562 i = sendmcast(s, (char *)outpack, packetlen, sin); 563 else 564 i = sendto( s, (char *)outpack, packetlen, 0, 565 (struct sockaddr *)sin, sizeof(struct sockaddr)); 566 567 if( i < 0 || i != packetlen ) { 568 if( i<0 ) { 569 logperror("solicitor:sendto"); 570 } 571 logerr("wrote %s %d chars, ret=%d\n", 572 sendaddress, packetlen, i ); 573 } 574} 575 576#ifdef RDISC_SERVER 577/* 578 * A V E R T I S E 579 * 580 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet. 581 * The IP packet will be added on by the kernel. 582 */ 583void 584advertise(struct sockaddr_in *sin, int lft) 585{ 586 static u_char outpack[MAXPACKET]; 587 struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack); 588 struct icmp_ra_addr *ap; 589 int packetlen, i, cc; 590 591 if (verbose) { 592 logtrace("Sending advertisement to %s\n", 593 pr_name(sin->sin_addr)); 594 } 595 596 for (i = 0; i < num_interfaces; i++) { 597 rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT; 598 rap->icmp_code = 0; 599 rap->icmp_cksum = 0; 600 rap->icmp_num_addrs = 0; 601 rap->icmp_wpa = 2; 602 rap->icmp_lifetime = htons(lft); 603 packetlen = 8; 604 605 /* 606 * TODO handle multiple logical interfaces per 607 * physical interface. (increment with rap->icmp_wpa * 4 for 608 * each address.) 609 */ 610 ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN); 611 ap->ira_addr = interfaces[i].localaddr.s_addr; 612 ap->ira_preference = htonl(interfaces[i].preference); 613 packetlen += rap->icmp_wpa * 4; 614 rap->icmp_num_addrs++; 615 616 /* Compute ICMP checksum here */ 617 rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen ); 618 619 if (isbroadcast(sin)) 620 cc = sendbcastif(s, (char *)outpack, packetlen, 621 &interfaces[i]); 622 else if (ismulticast(sin)) 623 cc = sendmcastif( s, (char *)outpack, packetlen, sin, 624 &interfaces[i]); 625 else { 626 struct interface *ifp = &interfaces[i]; 627 /* 628 * Verify that the interface matches the destination 629 * address. 630 */ 631 if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) == 632 (ifp->address.s_addr & ifp->netmask.s_addr)) { 633 if (debug) { 634 logdebug("Unicast to %s ", 635 pr_name(sin->sin_addr)); 636 logdebug("on interface %s, %s\n", 637 ifp->name, 638 pr_name(ifp->address)); 639 } 640 cc = sendto( s, (char *)outpack, packetlen, 0, 641 (struct sockaddr *)sin, 642 sizeof(struct sockaddr)); 643 } else 644 cc = packetlen; 645 } 646 if( cc < 0 || cc != packetlen ) { 647 if (cc < 0) { 648 logperror("sendto"); 649 } else { 650 logerr("wrote %s %d chars, ret=%d\n", 651 sendaddress, packetlen, cc ); 652 } 653 } 654 } 655} 656#endif 657 658/* 659 * P R _ T Y P E 660 * 661 * Convert an ICMP "type" field to a printable string. 662 */ 663char * 664pr_type(int t) 665{ 666 static char *ttab[] = { 667 "Echo Reply", 668 "ICMP 1", 669 "ICMP 2", 670 "Dest Unreachable", 671 "Source Quench", 672 "Redirect", 673 "ICMP 6", 674 "ICMP 7", 675 "Echo", 676 "Router Advertise", 677 "Router Solicitation", 678 "Time Exceeded", 679 "Parameter Problem", 680 "Timestamp", 681 "Timestamp Reply", 682 "Info Request", 683 "Info Reply", 684 "Netmask Request", 685 "Netmask Reply" 686 }; 687 688 if ( t < 0 || t > 16 ) 689 return("OUT-OF-RANGE"); 690 691 return(ttab[t]); 692} 693 694/* 695 * P R _ N A M E 696 * 697 * Return a string name for the given IP address. 698 */ 699char *pr_name(struct in_addr addr) 700{ 701 struct hostent *phe; 702 static char buf[80]; 703 704 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET); 705 if (phe == NULL) 706 return( inet_ntoa(addr)); 707 snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr)); 708 return(buf); 709} 710 711/* 712 * P R _ P A C K 713 * 714 * Print out the packet, if it came from us. This logic is necessary 715 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets 716 * which arrive ('tis only fair). This permits multiple copies of this 717 * program to be run without having intermingled output (or statistics!). 718 */ 719void 720pr_pack(char *buf, int cc, struct sockaddr_in *from) 721{ 722 struct iphdr *ip; 723 struct icmphdr *icp; 724 int i; 725 int hlen; 726 727 ip = (struct iphdr *) ALLIGN(buf); 728 hlen = ip->ihl << 2; 729 if (cc < hlen + 8) { 730 if (verbose) 731 logtrace("packet too short (%d bytes) from %s\n", cc, 732 pr_name(from->sin_addr)); 733 return; 734 } 735 cc -= hlen; 736 icp = (struct icmphdr *)ALLIGN(buf + hlen); 737 738 switch (icp->type) { 739 case ICMP_ROUTER_ADVERTISEMENT: 740 { 741 struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp); 742 struct icmp_ra_addr *ap; 743 744#ifdef RDISC_SERVER 745 if (responder) 746 break; 747#endif 748 749 /* TBD verify that the link is multicast or broadcast */ 750 /* XXX Find out the link it came in over? */ 751 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) { 752 if (verbose) 753 logtrace("ICMP %s from %s: Bad checksum\n", 754 pr_type((int)rap->icmp_type), 755 pr_name(from->sin_addr)); 756 return; 757 } 758 if (rap->icmp_code != 0) { 759 if (verbose) 760 logtrace("ICMP %s from %s: Code = %d\n", 761 pr_type((int)rap->icmp_type), 762 pr_name(from->sin_addr), 763 rap->icmp_code); 764 return; 765 } 766 if (rap->icmp_num_addrs < 1) { 767 if (verbose) 768 logtrace("ICMP %s from %s: No addresses\n", 769 pr_type((int)rap->icmp_type), 770 pr_name(from->sin_addr)); 771 return; 772 } 773 if (rap->icmp_wpa < 2) { 774 if (verbose) 775 logtrace("ICMP %s from %s: Words/addr = %d\n", 776 pr_type((int)rap->icmp_type), 777 pr_name(from->sin_addr), 778 rap->icmp_wpa); 779 return; 780 } 781 if ((unsigned)cc < 782 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) { 783 if (verbose) 784 logtrace("ICMP %s from %s: Too short %d, %d\n", 785 pr_type((int)rap->icmp_type), 786 pr_name(from->sin_addr), 787 cc, 788 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4); 789 return; 790 } 791 792 if (verbose) 793 logtrace("ICMP %s from %s, lifetime %d\n", 794 pr_type((int)rap->icmp_type), 795 pr_name(from->sin_addr), 796 ntohs(rap->icmp_lifetime)); 797 798 /* Check that at least one router address is a neighboor 799 * on the arriving link. 800 */ 801 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) { 802 struct in_addr ina; 803 ap = (struct icmp_ra_addr *) 804 ALLIGN(buf + hlen + 8 + 805 i * rap->icmp_wpa * 4); 806 ina.s_addr = ap->ira_addr; 807 if (verbose) 808 logtrace("\taddress %s, preference 0x%x\n", 809 pr_name(ina), 810 (unsigned int)ntohl(ap->ira_preference)); 811 if (is_directly_connected(ina)) 812 record_router(ina, 813 ntohl(ap->ira_preference), 814 ntohs(rap->icmp_lifetime)); 815 } 816 nreceived++; 817 if (!forever) { 818 do_fork(); 819 forever = 1; 820/* 821 * The next line was added so that the alarm is set for the new procces 822 * Fraser Gardiner Sun Microsystems Australia 823 */ 824 (void) alarm(TIMER_INTERVAL); 825 } 826 break; 827 } 828 829#ifdef RDISC_SERVER 830 case ICMP_ROUTER_SOLICITATION: 831 { 832 struct sockaddr_in sin; 833 834 if (!responder) 835 break; 836 837 /* TBD verify that the link is multicast or broadcast */ 838 /* XXX Find out the link it came in over? */ 839 840 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) { 841 if (verbose) 842 logtrace("ICMP %s from %s: Bad checksum\n", 843 pr_type((int)icp->type), 844 pr_name(from->sin_addr)); 845 return; 846 } 847 if (icp->code != 0) { 848 if (verbose) 849 logtrace("ICMP %s from %s: Code = %d\n", 850 pr_type((int)icp->type), 851 pr_name(from->sin_addr), 852 icp->code); 853 return; 854 } 855 856 if (cc < ICMP_MINLEN) { 857 if (verbose) 858 logtrace("ICMP %s from %s: Too short %d, %d\n", 859 pr_type((int)icp->type), 860 pr_name(from->sin_addr), 861 cc, 862 ICMP_MINLEN); 863 return; 864 } 865 866 if (verbose) 867 logtrace("ICMP %s from %s\n", 868 pr_type((int)icp->type), 869 pr_name(from->sin_addr)); 870 871 /* Check that ip_src is either a neighboor 872 * on the arriving link or 0. 873 */ 874 sin.sin_family = AF_INET; 875 if (ip->saddr == 0) { 876 /* If it was sent to the broadcast address we respond 877 * to the broadcast address. 878 */ 879 if (IN_CLASSD(ntohl(ip->daddr))) 880 sin.sin_addr.s_addr = htonl(0xe0000001); 881 else 882 sin.sin_addr.s_addr = INADDR_BROADCAST; 883 /* Restart the timer when we broadcast */ 884 left_until_advertise = min_adv_int + 885 ((max_adv_int - min_adv_int) 886 * (random() % 1000)/1000); 887 } else { 888 sin.sin_addr.s_addr = ip->saddr; 889 if (!is_directly_connected(sin.sin_addr)) { 890 if (verbose) 891 logtrace("ICMP %s from %s: source not directly connected\n", 892 pr_type((int)icp->type), 893 pr_name(from->sin_addr)); 894 break; 895 } 896 } 897 nreceived++; 898 ntransmitted++; 899 advertise(&sin, lifetime); 900 break; 901 } 902#endif 903 } 904} 905 906 907/* 908 * I N _ C K S U M 909 * 910 * Checksum routine for Internet Protocol family headers (C Version) 911 * 912 */ 913#if BYTE_ORDER == LITTLE_ENDIAN 914# define ODDBYTE(v) (v) 915#elif BYTE_ORDER == BIG_ENDIAN 916# define ODDBYTE(v) ((u_short)(v) << 8) 917#else 918# define ODDBYTE(v) htons((u_short)(v) << 8) 919#endif 920 921u_short in_cksum(u_short *addr, int len) 922{ 923 register int nleft = len; 924 register u_short *w = addr; 925 register u_short answer; 926 register int sum = 0; 927 928 /* 929 * Our algorithm is simple, using a 32 bit accumulator (sum), 930 * we add sequential 16 bit words to it, and at the end, fold 931 * back all the carry bits from the top 16 bits into the lower 932 * 16 bits. 933 */ 934 while( nleft > 1 ) { 935 sum += *w++; 936 nleft -= 2; 937 } 938 939 /* mop up an odd byte, if necessary */ 940 if( nleft == 1 ) 941 sum += ODDBYTE(*(u_char *)w); /* le16toh() may be unavailable on old systems */ 942 943 /* 944 * add back carry outs from top 16 bits to low 16 bits 945 */ 946 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 947 sum += (sum >> 16); /* add carry */ 948 answer = ~sum; /* truncate to 16 bits */ 949 return (answer); 950} 951 952/* 953 * F I N I S H 954 * 955 * Print out statistics, and give up. 956 * Heavily buffered STDIO is used here, so that all the statistics 957 * will be written with 1 sys-write call. This is nice when more 958 * than one copy of the program is running on a terminal; it prevents 959 * the statistics output from becomming intermingled. 960 */ 961void 962finish() 963{ 964#ifdef RDISC_SERVER 965 if (responder) { 966 /* Send out a packet with a preference so that all 967 * hosts will know that we are dead. 968 * 969 * Wrong comment, wrong code. 970 * ttl must be set to 0 instead. --ANK 971 */ 972 logerr("terminated\n"); 973 ntransmitted++; 974 advertise(&whereto, 0); 975 } 976#endif 977 logtrace("\n----%s rdisc Statistics----\n", sendaddress ); 978 logtrace("%d packets transmitted, ", ntransmitted ); 979 logtrace("%d packets received, ", nreceived ); 980 logtrace("\n"); 981 (void) fflush(stdout); 982 exit(0); 983} 984 985void 986graceful_finish() 987{ 988 discard_table(); 989 finish(); 990 exit(0); 991} 992 993 994/* From libc/rpc/pmap_rmt.c */ 995 996int 997sendbcast(int s, char *packet, int packetlen) 998{ 999 int i, cc; 1000 1001 for (i = 0; i < num_interfaces; i++) { 1002 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 1003 continue; 1004 cc = sendbcastif(s, packet, packetlen, &interfaces[i]); 1005 if (cc!= packetlen) { 1006 return (cc); 1007 } 1008 } 1009 return (packetlen); 1010} 1011 1012int 1013sendbcastif(int s, char *packet, int packetlen, struct interface *ifp) 1014{ 1015 int on; 1016 int cc; 1017 struct sockaddr_in baddr; 1018 1019 baddr.sin_family = AF_INET; 1020 baddr.sin_addr = ifp->bcastaddr; 1021 if (debug) 1022 logdebug("Broadcast to %s\n", 1023 pr_name(baddr.sin_addr)); 1024 on = 1; 1025 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)); 1026 cc = sendto(s, packet, packetlen, 0, 1027 (struct sockaddr *)&baddr, sizeof (struct sockaddr)); 1028 if (cc!= packetlen) { 1029 logperror("sendbcast: sendto"); 1030 logerr("Cannot send broadcast packet to %s\n", 1031 pr_name(baddr.sin_addr)); 1032 } 1033 on = 0; 1034 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on)); 1035 return (cc); 1036} 1037 1038int 1039sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin) 1040{ 1041 int i, cc; 1042 1043 for (i = 0; i < num_interfaces; i++) { 1044 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0) 1045 continue; 1046 cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]); 1047 if (cc!= packetlen) { 1048 return (cc); 1049 } 1050 } 1051 return (packetlen); 1052} 1053 1054int 1055sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, 1056 struct interface *ifp) 1057{ 1058 int cc; 1059 struct ip_mreqn mreq; 1060 1061 memset(&mreq, 0, sizeof(mreq)); 1062 mreq.imr_ifindex = ifp->ifindex; 1063 mreq.imr_address = ifp->localaddr; 1064 if (debug) 1065 logdebug("Multicast to interface %s, %s\n", 1066 ifp->name, 1067 pr_name(mreq.imr_address)); 1068 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, 1069 (char *)&mreq, 1070 sizeof(mreq)) < 0) { 1071 logperror("setsockopt (IP_MULTICAST_IF)"); 1072 logerr("Cannot send multicast packet over interface %s, %s\n", 1073 ifp->name, 1074 pr_name(mreq.imr_address)); 1075 return (-1); 1076 } 1077 cc = sendto(s, packet, packetlen, 0, 1078 (struct sockaddr *)sin, sizeof (struct sockaddr)); 1079 if (cc!= packetlen) { 1080 logperror("sendmcast: sendto"); 1081 logerr("Cannot send multicast packet over interface %s, %s\n", 1082 ifp->name, pr_name(mreq.imr_address)); 1083 } 1084 return (cc); 1085} 1086 1087void 1088init() 1089{ 1090 initifs(); 1091#ifdef RDISC_SERVER 1092 { 1093 int i; 1094 for (i = 0; i < interfaces_size; i++) 1095 interfaces[i].preference = preference; 1096 } 1097#endif 1098} 1099 1100void 1101initifs() 1102{ 1103 int sock; 1104 struct ifconf ifc; 1105 struct ifreq ifreq, *ifr; 1106 struct sockaddr_in *sin; 1107 int n, i; 1108 char *buf; 1109 int numifs; 1110 unsigned bufsize; 1111 1112 sock = socket(AF_INET, SOCK_DGRAM, 0); 1113 if (sock < 0) { 1114 logperror("initifs: socket"); 1115 return; 1116 } 1117#ifdef SIOCGIFNUM 1118 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) { 1119 numifs = MAXIFS; 1120 } 1121#else 1122 numifs = MAXIFS; 1123#endif 1124 bufsize = numifs * sizeof(struct ifreq); 1125 buf = (char *)malloc(bufsize); 1126 if (buf == NULL) { 1127 logerr("out of memory\n"); 1128 (void) close(sock); 1129 return; 1130 } 1131 if (interfaces != NULL) 1132 (void) free(interfaces); 1133 interfaces = (struct interface *)ALLIGN(malloc(numifs * 1134 sizeof(struct interface))); 1135 if (interfaces == NULL) { 1136 logerr("out of memory\n"); 1137 (void) close(sock); 1138 (void) free(buf); 1139 return; 1140 } 1141 interfaces_size = numifs; 1142 1143 ifc.ifc_len = bufsize; 1144 ifc.ifc_buf = buf; 1145 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { 1146 logperror("initifs: ioctl (get interface configuration)"); 1147 (void) close(sock); 1148 (void) free(buf); 1149 return; 1150 } 1151 ifr = ifc.ifc_req; 1152 for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) { 1153 ifreq = *ifr; 1154 if (strlen(ifreq.ifr_name) >= IFNAMSIZ) 1155 continue; 1156 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) { 1157 logperror("initifs: ioctl (get interface flags)"); 1158 continue; 1159 } 1160 if (ifr->ifr_addr.sa_family != AF_INET) 1161 continue; 1162 if ((ifreq.ifr_flags & IFF_UP) == 0) 1163 continue; 1164 if (ifreq.ifr_flags & IFF_LOOPBACK) 1165 continue; 1166 if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0) 1167 continue; 1168 strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1); 1169 1170 sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr); 1171 interfaces[i].localaddr = sin->sin_addr; 1172 interfaces[i].flags = ifreq.ifr_flags; 1173 interfaces[i].netmask.s_addr = (__u32)0xffffffff; 1174 if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) { 1175 logperror("initifs: ioctl (get ifindex)"); 1176 continue; 1177 } 1178 interfaces[i].ifindex = ifreq.ifr_ifindex; 1179 if (ifreq.ifr_flags & IFF_POINTOPOINT) { 1180 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { 1181 logperror("initifs: ioctl (get destination addr)"); 1182 continue; 1183 } 1184 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); 1185 /* A pt-pt link is identified by the remote address */ 1186 interfaces[i].address = sin->sin_addr; 1187 interfaces[i].remoteaddr = sin->sin_addr; 1188 /* Simulate broadcast for pt-pt */ 1189 interfaces[i].bcastaddr = sin->sin_addr; 1190 interfaces[i].flags |= IFF_BROADCAST; 1191 } else { 1192 /* Non pt-pt links are identified by the local address */ 1193 interfaces[i].address = interfaces[i].localaddr; 1194 interfaces[i].remoteaddr = interfaces[i].address; 1195 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) { 1196 logperror("initifs: ioctl (get netmask)"); 1197 continue; 1198 } 1199 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); 1200 interfaces[i].netmask = sin->sin_addr; 1201 if (ifreq.ifr_flags & IFF_BROADCAST) { 1202 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) { 1203 logperror("initifs: ioctl (get broadcast address)"); 1204 continue; 1205 } 1206 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr); 1207 interfaces[i].bcastaddr = sin->sin_addr; 1208 } 1209 } 1210#ifdef notdef 1211 if (debug) 1212 logdebug("Found interface %s, flags 0x%x\n", 1213 pr_name(interfaces[i].localaddr), 1214 interfaces[i].flags); 1215#endif 1216 i++; 1217 } 1218 num_interfaces = i; 1219#ifdef notdef 1220 if (debug) 1221 logdebug("Found %d interfaces\n", num_interfaces); 1222#endif 1223 (void) close(sock); 1224 (void) free(buf); 1225} 1226 1227int 1228join(int sock, struct sockaddr_in *sin) 1229{ 1230 int i, j; 1231 struct ip_mreqn mreq; 1232 int joined[num_interfaces]; 1233 1234 memset(joined, 0, sizeof(joined)); 1235 1236 if (isbroadcast(sin)) 1237 return (0); 1238 1239 mreq.imr_multiaddr = sin->sin_addr; 1240 for (i = 0; i < num_interfaces; i++) { 1241 for (j = 0; j < i; j++) { 1242 if (joined[j] == interfaces[i].ifindex) 1243 break; 1244 } 1245 if (j != i) 1246 continue; 1247 1248 mreq.imr_ifindex = interfaces[i].ifindex; 1249 mreq.imr_address.s_addr = 0; 1250 1251 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 1252 (char *)&mreq, sizeof(mreq)) < 0) { 1253 logperror("setsockopt (IP_ADD_MEMBERSHIP)"); 1254 return (-1); 1255 } 1256 1257 joined[i] = interfaces[i].ifindex; 1258 } 1259 return (0); 1260} 1261 1262int support_multicast() 1263{ 1264 int sock; 1265 u_char ttl = 1; 1266 1267 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1268 if (sock < 0) { 1269 logperror("support_multicast: socket"); 1270 return (0); 1271 } 1272 1273 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, 1274 (char *)&ttl, sizeof(ttl)) < 0) { 1275 (void) close(sock); 1276 return (0); 1277 } 1278 (void) close(sock); 1279 return (1); 1280} 1281 1282int 1283is_directly_connected(struct in_addr in) 1284{ 1285 int i; 1286 1287 for (i = 0; i < num_interfaces; i++) { 1288 /* Check that the subnetwork numbers match */ 1289 1290 if ((in.s_addr & interfaces[i].netmask.s_addr ) == 1291 (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr)) 1292 return (1); 1293 } 1294 return (0); 1295} 1296 1297/* 1298 * TABLES 1299 */ 1300struct table { 1301 struct in_addr router; 1302 int preference; 1303 int remaining_time; 1304 int in_kernel; 1305 struct table *next; 1306}; 1307 1308struct table *table; 1309 1310struct table * 1311find_router(struct in_addr addr) 1312{ 1313 struct table *tp; 1314 1315 tp = table; 1316 while (tp) { 1317 if (tp->router.s_addr == addr.s_addr) 1318 return (tp); 1319 tp = tp->next; 1320 } 1321 return (NULL); 1322} 1323 1324int max_preference(void) 1325{ 1326 struct table *tp; 1327 int max = (int)INELIGIBLE_PREF; 1328 1329 tp = table; 1330 while (tp) { 1331 if (tp->preference > max) 1332 max = tp->preference; 1333 tp = tp->next; 1334 } 1335 return (max); 1336} 1337 1338 1339/* Note: this might leave the kernel with no default route for a short time. */ 1340void 1341age_table(int time) 1342{ 1343 struct table **tpp, *tp; 1344 int recalculate_max = 0; 1345 int max = max_preference(); 1346 1347 tpp = &table; 1348 while (*tpp != NULL) { 1349 tp = *tpp; 1350 tp->remaining_time -= time; 1351 if (tp->remaining_time <= 0) { 1352 *tpp = tp->next; 1353 if (tp->in_kernel) 1354 del_route(tp->router); 1355 if (best_preference && 1356 tp->preference == max) 1357 recalculate_max++; 1358 free((char *)tp); 1359 } else { 1360 tpp = &tp->next; 1361 } 1362 } 1363 if (recalculate_max) { 1364 int max = max_preference(); 1365 1366 if (max != INELIGIBLE_PREF) { 1367 tp = table; 1368 while (tp) { 1369 if (tp->preference == max && !tp->in_kernel) { 1370 add_route(tp->router); 1371 tp->in_kernel++; 1372 } 1373 tp = tp->next; 1374 } 1375 } 1376 } 1377} 1378 1379void discard_table(void) 1380{ 1381 struct table **tpp, *tp; 1382 1383 tpp = &table; 1384 while (*tpp != NULL) { 1385 tp = *tpp; 1386 *tpp = tp->next; 1387 if (tp->in_kernel) 1388 del_route(tp->router); 1389 free((char *)tp); 1390 } 1391} 1392 1393 1394void 1395record_router(struct in_addr router, int preference, int ttl) 1396{ 1397 struct table *tp; 1398 int old_max = max_preference(); 1399 int changed_up = 0; /* max preference could have increased */ 1400 int changed_down = 0; /* max preference could have decreased */ 1401 1402 if (ttl < 4) 1403 preference = INELIGIBLE_PREF; 1404 1405 if (debug) 1406 logdebug("Recording %s, ttl %d, preference 0x%x\n", 1407 pr_name(router), 1408 ttl, 1409 preference); 1410 tp = find_router(router); 1411 if (tp) { 1412 if (tp->preference > preference && 1413 tp->preference == old_max) 1414 changed_down++; 1415 else if (preference > tp->preference) 1416 changed_up++; 1417 tp->preference = preference; 1418 tp->remaining_time = ttl; 1419 } else { 1420 if (preference > old_max) 1421 changed_up++; 1422 tp = (struct table *)ALLIGN(malloc(sizeof(struct table))); 1423 if (tp == NULL) { 1424 logerr("Out of memory\n"); 1425 return; 1426 } 1427 tp->router = router; 1428 tp->preference = preference; 1429 tp->remaining_time = ttl; 1430 tp->in_kernel = 0; 1431 tp->next = table; 1432 table = tp; 1433 } 1434 if (!tp->in_kernel && 1435 (!best_preference || tp->preference == max_preference()) && 1436 tp->preference != INELIGIBLE_PREF) { 1437 add_route(tp->router); 1438 tp->in_kernel++; 1439 } 1440 if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) { 1441 del_route(tp->router); 1442 tp->in_kernel = 0; 1443 } 1444 if (best_preference && changed_down) { 1445 /* Check if we should add routes */ 1446 int new_max = max_preference(); 1447 if (new_max != INELIGIBLE_PREF) { 1448 tp = table; 1449 while (tp) { 1450 if (tp->preference == new_max && 1451 !tp->in_kernel) { 1452 add_route(tp->router); 1453 tp->in_kernel++; 1454 } 1455 tp = tp->next; 1456 } 1457 } 1458 } 1459 if (best_preference && (changed_up || changed_down)) { 1460 /* Check if we should remove routes already in the kernel */ 1461 int new_max = max_preference(); 1462 tp = table; 1463 while (tp) { 1464 if (tp->preference < new_max && tp->in_kernel) { 1465 del_route(tp->router); 1466 tp->in_kernel = 0; 1467 } 1468 tp = tp->next; 1469 } 1470 } 1471} 1472 1473void 1474add_route(struct in_addr addr) 1475{ 1476 if (debug) 1477 logdebug("Add default route to %s\n", pr_name(addr)); 1478 rtioctl(addr, SIOCADDRT); 1479} 1480 1481void 1482del_route(struct in_addr addr) 1483{ 1484 if (debug) 1485 logdebug("Delete default route to %s\n", pr_name(addr)); 1486 rtioctl(addr, SIOCDELRT); 1487} 1488 1489void 1490rtioctl(struct in_addr addr, int op) 1491{ 1492 int sock; 1493 struct rtentry rt; 1494 struct sockaddr_in *sin; 1495 1496 memset((char *)&rt, 0, sizeof(struct rtentry)); 1497 rt.rt_dst.sa_family = AF_INET; 1498 rt.rt_gateway.sa_family = AF_INET; 1499 rt.rt_genmask.sa_family = AF_INET; 1500 sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway); 1501 sin->sin_addr = addr; 1502 rt.rt_flags = RTF_UP | RTF_GATEWAY; 1503 1504 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 1505 if (sock < 0) { 1506 logperror("rtioctl: socket"); 1507 return; 1508 } 1509 if (ioctl(sock, op, (char *)&rt) < 0) { 1510 if (!(op == SIOCADDRT && errno == EEXIST)) 1511 logperror("ioctl (add/delete route)"); 1512 } 1513 (void) close(sock); 1514} 1515 1516/* 1517 * LOGGER 1518 */ 1519 1520void initlog(void) 1521{ 1522 logging++; 1523 openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON); 1524} 1525 1526 1527void 1528logperror(char *str) 1529{ 1530 if (logging) 1531 syslog(LOG_ERR, "%s: %m", str); 1532 else 1533 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno)); 1534} 1535