1 2 /* 3 * Copyright (C) 2012 by Darren Reed. 4 * 5 * See the IPFILTER.LICENCE file for details on licencing. 6 */ 7 #include <stdio.h> 8 #include <unistd.h> 9 #include <string.h> 10 #include <fcntl.h> 11 #include <errno.h> 12 #if !defined(__SVR4) && !defined(__GNUC__) 13 #include <strings.h> 14 #endif 15 #include <sys/types.h> 16 #include <sys/param.h> 17 #include <sys/file.h> 18 #include <stdlib.h> 19 #include <stddef.h> 20 #include <sys/socket.h> 21 #include <sys/ioctl.h> 22 #include <netinet/in.h> 23 #include <netinet/in_systm.h> 24 #include <sys/time.h> 25 #include <net/if.h> 26 #include <netinet/ip.h> 27 #include <netdb.h> 28 #include <arpa/nameser.h> 29 #include <resolv.h> 30 #include "ipf.h" 31 #include "netinet/ipl.h" 32 33 34 #ifndef IPF_SAVEDIR 35 # define IPF_SAVEDIR "/var/db/ipf" 36 #endif 37 #ifndef IPF_NATFILE 38 # define IPF_NATFILE "ipnat.ipf" 39 #endif 40 #ifndef IPF_STATEFILE 41 # define IPF_STATEFILE "ipstate.ipf" 42 #endif 43 44 #if !defined(__SVR4) && defined(__GNUC__) 45 extern char *index(const char *, int); 46 #endif 47 48 extern char *optarg; 49 extern int optind; 50 51 int main(int, char *[]); 52 void usage(void); 53 int changestateif(char *, char *); 54 int changenatif(char *, char *); 55 int readstate(int, char *); 56 int readnat(int, char *); 57 int writestate(int, char *); 58 int opendevice(char *); 59 void closedevice(int); 60 int setlock(int, int); 61 int writeall(char *); 62 int readall(char *); 63 int writenat(int, char *); 64 65 int opts = 0; 66 char *progname; 67 68 69 void usage() 70 { 71 fprintf(stderr, "usage: %s [-nv] -l\n", progname); 72 fprintf(stderr, "usage: %s [-nv] -u\n", progname); 73 fprintf(stderr, "usage: %s [-nv] [-d <dir>] -R\n", progname); 74 fprintf(stderr, "usage: %s [-nv] [-d <dir>] -W\n", progname); 75 fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -r\n", progname); 76 fprintf(stderr, "usage: %s [-nNSv] [-f <file>] -w\n", progname); 77 fprintf(stderr, "usage: %s [-nNSv] -f <filename> -i <if1>,<if2>\n", 78 progname); 79 exit(1); 80 } 81 82 83 /* 84 * Change interface names in state information saved out to disk. 85 */ 86 int changestateif(char *ifs, char *fname) 87 { 88 int fd, olen, nlen, rw; 89 ipstate_save_t ips; 90 off_t pos; 91 char *s; 92 93 s = strchr(ifs, ','); 94 if (!s) 95 usage(); 96 *s++ = '\0'; 97 nlen = strlen(s); 98 olen = strlen(ifs); 99 if (nlen >= sizeof(ips.ips_is.is_ifname) || 100 olen >= sizeof(ips.ips_is.is_ifname)) 101 usage(); 102 103 fd = open(fname, O_RDWR); 104 if (fd == -1) { 105 perror("open"); 106 exit(1); 107 } 108 109 for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) { 110 rw = 0; 111 if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) { 112 strcpy(ips.ips_is.is_ifname[0], s); 113 rw = 1; 114 } 115 if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) { 116 strcpy(ips.ips_is.is_ifname[1], s); 117 rw = 1; 118 } 119 if (!strncmp(ips.ips_is.is_ifname[2], ifs, olen + 1)) { 120 strcpy(ips.ips_is.is_ifname[2], s); 121 rw = 1; 122 } 123 if (!strncmp(ips.ips_is.is_ifname[3], ifs, olen + 1)) { 124 strcpy(ips.ips_is.is_ifname[3], s); 125 rw = 1; 126 } 127 if (rw == 1) { 128 if (lseek(fd, pos, SEEK_SET) != pos) { 129 perror("lseek"); 130 exit(1); 131 } 132 if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) { 133 perror("write"); 134 exit(1); 135 } 136 } 137 pos = lseek(fd, 0, SEEK_CUR); 138 } 139 close(fd); 140 141 return (0); 142 } 143 144 145 /* 146 * Change interface names in NAT information saved out to disk. 147 */ 148 int changenatif(char *ifs, char *fname) 149 { 150 int fd, olen, nlen, rw; 151 nat_save_t ipn; 152 nat_t *nat; 153 off_t pos; 154 char *s; 155 156 s = strchr(ifs, ','); 157 if (!s) 158 usage(); 159 *s++ = '\0'; 160 nlen = strlen(s); 161 olen = strlen(ifs); 162 nat = &ipn.ipn_nat; 163 if (nlen >= sizeof(nat->nat_ifnames[0]) || 164 olen >= sizeof(nat->nat_ifnames[0])) 165 usage(); 166 167 fd = open(fname, O_RDWR); 168 if (fd == -1) { 169 perror("open"); 170 exit(1); 171 } 172 173 for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) { 174 rw = 0; 175 if (!strncmp(nat->nat_ifnames[0], ifs, olen + 1)) { 176 strcpy(nat->nat_ifnames[0], s); 177 rw = 1; 178 } 179 if (!strncmp(nat->nat_ifnames[1], ifs, olen + 1)) { 180 strcpy(nat->nat_ifnames[1], s); 181 rw = 1; 182 } 183 if (rw == 1) { 184 if (lseek(fd, pos, SEEK_SET) != pos) { 185 perror("lseek"); 186 exit(1); 187 } 188 if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) { 189 perror("write"); 190 exit(1); 191 } 192 } 193 pos = lseek(fd, 0, SEEK_CUR); 194 } 195 close(fd); 196 197 return (0); 198 } 199 200 201 int main(int argc, char *argv[]) 202 { 203 int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0; 204 char *dirname = NULL, *filename = NULL, *ifs = NULL; 205 206 progname = argv[0]; 207 while ((c = getopt(argc, argv, "d:f:i:lNnSRruvWw")) != -1) 208 switch (c) 209 { 210 case 'd' : 211 if ((set == 0) && !dirname && !filename) 212 dirname = optarg; 213 else 214 usage(); 215 break; 216 case 'f' : 217 if ((set != 0) && !dirname && !filename) 218 filename = optarg; 219 else 220 usage(); 221 break; 222 case 'i' : 223 ifs = optarg; 224 set = 1; 225 break; 226 case 'l' : 227 if (filename || dirname || set) 228 usage(); 229 lock = 1; 230 set = 1; 231 break; 232 case 'n' : 233 opts |= OPT_DONOTHING; 234 break; 235 case 'N' : 236 if ((ns >= 0) || dirname || (rw != -1) || set) 237 usage(); 238 ns = 0; 239 set = 1; 240 break; 241 case 'r' : 242 if (dirname || (rw != -1) || (ns == -1)) 243 usage(); 244 rw = 0; 245 set = 1; 246 break; 247 case 'R' : 248 rw = 2; 249 set = 1; 250 break; 251 case 'S' : 252 if ((ns >= 0) || dirname || (rw != -1) || set) 253 usage(); 254 ns = 1; 255 set = 1; 256 break; 257 case 'u' : 258 if (filename || dirname || set) 259 usage(); 260 lock = 0; 261 set = 1; 262 break; 263 case 'v' : 264 opts |= OPT_VERBOSE; 265 break; 266 case 'w' : 267 if (dirname || (rw != -1) || (ns == -1)) 268 usage(); 269 rw = 1; 270 set = 1; 271 break; 272 case 'W' : 273 rw = 3; 274 set = 1; 275 break; 276 case '?' : 277 default : 278 usage(); 279 } 280 281 if (ifs) { 282 if (!filename || ns < 0) 283 usage(); 284 if (ns == 0) 285 return (changenatif(ifs, filename)); 286 else 287 return (changestateif(ifs, filename)); 288 } 289 290 if ((ns >= 0) || (lock >= 0)) { 291 if (lock >= 0) 292 devfd = opendevice(NULL); 293 else if (ns >= 0) { 294 if (ns == 1) 295 devfd = opendevice(IPSTATE_NAME); 296 else if (ns == 0) 297 devfd = opendevice(IPNAT_NAME); 298 } 299 if (devfd == -1) 300 exit(1); 301 } 302 303 if (lock >= 0) 304 err = setlock(devfd, lock); 305 else if (rw >= 0) { 306 if (rw & 1) { /* WRITE */ 307 if (rw & 2) 308 err = writeall(dirname); 309 else { 310 if (ns == 0) 311 err = writenat(devfd, filename); 312 else if (ns == 1) 313 err = writestate(devfd, filename); 314 } 315 } else { 316 if (rw & 2) 317 err = readall(dirname); 318 else { 319 if (ns == 0) 320 err = readnat(devfd, filename); 321 else if (ns == 1) 322 err = readstate(devfd, filename); 323 } 324 } 325 } 326 return (err); 327 } 328 329 330 int opendevice(char *ipfdev) 331 { 332 int fd = -1; 333 334 if (opts & OPT_DONOTHING) 335 return (-2); 336 337 if (!ipfdev) 338 ipfdev = IPL_NAME; 339 340 if ((fd = open(ipfdev, O_RDWR)) == -1) 341 if ((fd = open(ipfdev, O_RDONLY)) == -1) 342 perror("open device"); 343 return (fd); 344 } 345 346 347 void closedevice(int fd) 348 { 349 close(fd); 350 } 351 352 353 int setlock(int fd, int lock) 354 { 355 if (opts & OPT_VERBOSE) 356 printf("Turn lock %s\n", lock ? "on" : "off"); 357 if (!(opts & OPT_DONOTHING)) { 358 if (ioctl(fd, SIOCSTLCK, &lock) == -1) { 359 perror("SIOCSTLCK"); 360 return (1); 361 } 362 if (opts & OPT_VERBOSE) 363 printf("Lock now %s\n", lock ? "on" : "off"); 364 } 365 return (0); 366 } 367 368 369 int writestate(int fd, char *file) 370 { 371 ipstate_save_t ips, *ipsp; 372 ipfobj_t obj; 373 int wfd = -1; 374 375 if (!file) 376 file = IPF_STATEFILE; 377 378 wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); 379 if (wfd == -1) { 380 fprintf(stderr, "%s ", file); 381 perror("state:open"); 382 return (1); 383 } 384 385 ipsp = &ips; 386 bzero((char *)&obj, sizeof(obj)); 387 bzero((char *)ipsp, sizeof(ips)); 388 389 obj.ipfo_rev = IPFILTER_VERSION; 390 obj.ipfo_size = sizeof(*ipsp); 391 obj.ipfo_type = IPFOBJ_STATESAVE; 392 obj.ipfo_ptr = ipsp; 393 394 do { 395 396 if (opts & OPT_VERBOSE) 397 printf("Getting state from addr %p\n", ips.ips_next); 398 if (ioctl(fd, SIOCSTGET, &obj)) { 399 if (errno == ENOENT) 400 break; 401 perror("state:SIOCSTGET"); 402 close(wfd); 403 return (1); 404 } 405 if (opts & OPT_VERBOSE) 406 printf("Got state next %p\n", ips.ips_next); 407 if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) { 408 perror("state:write"); 409 close(wfd); 410 return (1); 411 } 412 } while (ips.ips_next != NULL); 413 close(wfd); 414 415 return (0); 416 } 417 418 419 int readstate(int fd, char *file) 420 { 421 ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL; 422 int sfd = -1, i; 423 ipfobj_t obj; 424 425 if (!file) 426 file = IPF_STATEFILE; 427 428 sfd = open(file, O_RDONLY, 0600); 429 if (sfd == -1) { 430 fprintf(stderr, "%s ", file); 431 perror("open"); 432 return (1); 433 } 434 435 bzero((char *)&ips, sizeof(ips)); 436 437 /* 438 * 1. Read all state information in. 439 */ 440 do { 441 i = read(sfd, &ips, sizeof(ips)); 442 if (i == -1) { 443 perror("read"); 444 goto freeipshead; 445 } 446 if (i == 0) 447 break; 448 if (i != sizeof(ips)) { 449 fprintf(stderr, "state:incomplete read: %d != %d\n", 450 i, (int)sizeof(ips)); 451 goto freeipshead; 452 } 453 is = (ipstate_save_t *)malloc(sizeof(*is)); 454 if (is == NULL) { 455 fprintf(stderr, "malloc failed\n"); 456 goto freeipshead; 457 } 458 459 bcopy((char *)&ips, (char *)is, sizeof(ips)); 460 461 /* 462 * Check to see if this is the first state entry that will 463 * reference a particular rule and if so, flag it as such 464 * else just adjust the rule pointer to become a pointer to 465 * the other. We do this so we have a means later for tracking 466 * who is referencing us when we get back the real pointer 467 * in is_rule after doing the ioctl. 468 */ 469 for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next) 470 if (is1->ips_rule == is->ips_rule) 471 break; 472 if (is1 == NULL) 473 is->ips_is.is_flags |= SI_NEWFR; 474 else 475 is->ips_rule = (void *)&is1->ips_rule; 476 477 /* 478 * Use a tail-queue type list (add things to the end).. 479 */ 480 is->ips_next = NULL; 481 if (!ipshead) 482 ipshead = is; 483 if (ipstail) 484 ipstail->ips_next = is; 485 ipstail = is; 486 } while (1); 487 488 close(sfd); 489 490 obj.ipfo_rev = IPFILTER_VERSION; 491 obj.ipfo_size = sizeof(*is); 492 obj.ipfo_type = IPFOBJ_STATESAVE; 493 494 while ((is = ipshead) != NULL) { 495 if (opts & OPT_VERBOSE) 496 printf("Loading new state table entry\n"); 497 if (is->ips_is.is_flags & SI_NEWFR) { 498 if (opts & OPT_VERBOSE) 499 printf("Loading new filter rule\n"); 500 } 501 502 obj.ipfo_ptr = is; 503 if (!(opts & OPT_DONOTHING)) 504 if (ioctl(fd, SIOCSTPUT, &obj)) { 505 perror("SIOCSTPUT"); 506 goto freeipshead; 507 } 508 509 if (is->ips_is.is_flags & SI_NEWFR) { 510 if (opts & OPT_VERBOSE) 511 printf("Real rule addr %p\n", is->ips_rule); 512 for (is1 = is->ips_next; is1; is1 = is1->ips_next) 513 if (is1->ips_rule == (frentry_t *)&is->ips_rule) 514 is1->ips_rule = is->ips_rule; 515 } 516 517 ipshead = is->ips_next; 518 free(is); 519 } 520 521 return (0); 522 523 freeipshead: 524 while ((is = ipshead) != NULL) { 525 ipshead = is->ips_next; 526 free(is); 527 } 528 if (sfd != -1) 529 close(sfd); 530 return (1); 531 } 532 533 534 int readnat(int fd, char *file) 535 { 536 nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL; 537 ipfobj_t obj; 538 int nfd, i; 539 nat_t *nat; 540 char *s; 541 int n; 542 543 nfd = -1; 544 in = NULL; 545 ipnhead = NULL; 546 ipntail = NULL; 547 548 if (!file) 549 file = IPF_NATFILE; 550 551 nfd = open(file, O_RDONLY); 552 if (nfd == -1) { 553 fprintf(stderr, "%s ", file); 554 perror("nat:open"); 555 return (1); 556 } 557 558 bzero((char *)&ipn, sizeof(ipn)); 559 560 /* 561 * 1. Read all state information in. 562 */ 563 do { 564 i = read(nfd, &ipn, sizeof(ipn)); 565 if (i == -1) { 566 perror("read"); 567 goto freenathead; 568 } 569 if (i == 0) 570 break; 571 if (i != sizeof(ipn)) { 572 fprintf(stderr, "nat:incomplete read: %d != %d\n", 573 i, (int)sizeof(ipn)); 574 goto freenathead; 575 } 576 577 in = (nat_save_t *)malloc(ipn.ipn_dsize); 578 if (in == NULL) { 579 fprintf(stderr, "nat:cannot malloc nat save atruct\n"); 580 goto freenathead; 581 } 582 583 if (ipn.ipn_dsize > sizeof(ipn)) { 584 n = ipn.ipn_dsize - sizeof(ipn); 585 if (n > 0) { 586 s = in->ipn_data + sizeof(in->ipn_data); 587 i = read(nfd, s, n); 588 if (i == 0) 589 break; 590 if (i != n) { 591 fprintf(stderr, 592 "nat:incomplete read: %d != %d\n", 593 i, n); 594 goto freenathead; 595 } 596 } 597 } 598 bcopy((char *)&ipn, (char *)in, sizeof(ipn)); 599 600 /* 601 * Check to see if this is the first NAT entry that will 602 * reference a particular rule and if so, flag it as such 603 * else just adjust the rule pointer to become a pointer to 604 * the other. We do this so we have a means later for tracking 605 * who is referencing us when we get back the real pointer 606 * in is_rule after doing the ioctl. 607 */ 608 nat = &in->ipn_nat; 609 if (nat->nat_fr != NULL) { 610 for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next) 611 if (in1->ipn_rule == nat->nat_fr) 612 break; 613 if (in1 == NULL) 614 nat->nat_flags |= SI_NEWFR; 615 else 616 nat->nat_fr = &in1->ipn_fr; 617 } 618 619 /* 620 * Use a tail-queue type list (add things to the end).. 621 */ 622 in->ipn_next = NULL; 623 if (!ipnhead) 624 ipnhead = in; 625 if (ipntail) 626 ipntail->ipn_next = in; 627 ipntail = in; 628 } while (1); 629 630 close(nfd); 631 nfd = -1; 632 633 obj.ipfo_rev = IPFILTER_VERSION; 634 obj.ipfo_type = IPFOBJ_NATSAVE; 635 636 while ((in = ipnhead) != NULL) { 637 if (opts & OPT_VERBOSE) 638 printf("Loading new NAT table entry\n"); 639 nat = &in->ipn_nat; 640 if (nat->nat_flags & SI_NEWFR) { 641 if (opts & OPT_VERBOSE) 642 printf("Loading new filter rule\n"); 643 } 644 645 obj.ipfo_ptr = in; 646 obj.ipfo_size = in->ipn_dsize; 647 if (!(opts & OPT_DONOTHING)) 648 if (ioctl(fd, SIOCSTPUT, &obj)) { 649 fprintf(stderr, "in=%p:", in); 650 perror("SIOCSTPUT"); 651 return (1); 652 } 653 654 if (nat->nat_flags & SI_NEWFR) { 655 if (opts & OPT_VERBOSE) 656 printf("Real rule addr %p\n", nat->nat_fr); 657 for (in1 = in->ipn_next; in1; in1 = in1->ipn_next) 658 if (in1->ipn_rule == &in->ipn_fr) 659 in1->ipn_rule = nat->nat_fr; 660 } 661 662 ipnhead = in->ipn_next; 663 free(in); 664 } 665 666 return (0); 667 668 freenathead: 669 while ((in = ipnhead) != NULL) { 670 ipnhead = in->ipn_next; 671 free(in); 672 } 673 if (nfd != -1) 674 close(nfd); 675 return (1); 676 } 677 678 679 int writenat(int fd, char *file) 680 { 681 nat_save_t *ipnp = NULL, *next = NULL; 682 ipfobj_t obj; 683 int nfd = -1; 684 natget_t ng; 685 686 if (!file) 687 file = IPF_NATFILE; 688 689 nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600); 690 if (nfd == -1) { 691 fprintf(stderr, "%s ", file); 692 perror("nat:open"); 693 return (1); 694 } 695 696 obj.ipfo_rev = IPFILTER_VERSION; 697 obj.ipfo_type = IPFOBJ_NATSAVE; 698 699 do { 700 if (opts & OPT_VERBOSE) 701 printf("Getting nat from addr %p\n", ipnp); 702 ng.ng_ptr = next; 703 ng.ng_sz = 0; 704 if (ioctl(fd, SIOCSTGSZ, &ng)) { 705 perror("nat:SIOCSTGSZ"); 706 close(nfd); 707 if (ipnp != NULL) 708 free(ipnp); 709 return (1); 710 } 711 712 if (opts & OPT_VERBOSE) 713 printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr); 714 715 if (ng.ng_sz == 0) 716 break; 717 718 if (!ipnp) 719 ipnp = malloc(ng.ng_sz); 720 else 721 ipnp = realloc((char *)ipnp, ng.ng_sz); 722 if (!ipnp) { 723 fprintf(stderr, 724 "malloc for %d bytes failed\n", ng.ng_sz); 725 break; 726 } 727 728 bzero((char *)ipnp, ng.ng_sz); 729 obj.ipfo_size = ng.ng_sz; 730 obj.ipfo_ptr = ipnp; 731 ipnp->ipn_dsize = ng.ng_sz; 732 ipnp->ipn_next = next; 733 if (ioctl(fd, SIOCSTGET, &obj)) { 734 if (errno == ENOENT) 735 break; 736 perror("nat:SIOCSTGET"); 737 close(nfd); 738 free(ipnp); 739 return (1); 740 } 741 742 if (opts & OPT_VERBOSE) 743 printf("Got nat next %p ipn_dsize %d ng_sz %d\n", 744 ipnp->ipn_next, ipnp->ipn_dsize, ng.ng_sz); 745 if (write(nfd, ipnp, ipnp->ipn_dsize) != ipnp->ipn_dsize) { 746 perror("nat:write"); 747 close(nfd); 748 free(ipnp); 749 return (1); 750 } 751 next = ipnp->ipn_next; 752 } while (ipnp && next); 753 if (ipnp != NULL) 754 free(ipnp); 755 close(nfd); 756 757 return (0); 758 } 759 760 761 int writeall(char *dirname) 762 { 763 int fd, devfd; 764 765 if (!dirname) 766 dirname = IPF_SAVEDIR; 767 768 if (chdir(dirname)) { 769 fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname); 770 perror("chdir(IPF_SAVEDIR)"); 771 return (1); 772 } 773 774 fd = opendevice(NULL); 775 if (fd == -1) 776 return (1); 777 if (setlock(fd, 1)) { 778 close(fd); 779 return (1); 780 } 781 782 devfd = opendevice(IPSTATE_NAME); 783 if (devfd == -1) 784 goto bad; 785 if (writestate(devfd, NULL)) 786 goto bad; 787 close(devfd); 788 789 devfd = opendevice(IPNAT_NAME); 790 if (devfd == -1) 791 goto bad; 792 if (writenat(devfd, NULL)) 793 goto bad; 794 close(devfd); 795 796 if (setlock(fd, 0)) { 797 close(fd); 798 return (1); 799 } 800 801 close(fd); 802 return (0); 803 804 bad: 805 setlock(fd, 0); 806 close(fd); 807 return (1); 808 } 809 810 811 int readall(char *dirname) 812 { 813 int fd, devfd; 814 815 if (!dirname) 816 dirname = IPF_SAVEDIR; 817 818 if (chdir(dirname)) { 819 perror("chdir(IPF_SAVEDIR)"); 820 return (1); 821 } 822 823 fd = opendevice(NULL); 824 if (fd == -1) 825 return (1); 826 if (setlock(fd, 1)) { 827 close(fd); 828 return (1); 829 } 830 831 devfd = opendevice(IPSTATE_NAME); 832 if (devfd == -1) 833 return (1); 834 if (readstate(devfd, NULL)) 835 return (1); 836 close(devfd); 837 838 devfd = opendevice(IPNAT_NAME); 839 if (devfd == -1) 840 return (1); 841 if (readnat(devfd, NULL)) 842 return (1); 843 close(devfd); 844 845 if (setlock(fd, 0)) { 846 close(fd); 847 return (1); 848 } 849 850 return (0); 851 } 852