ggated.c revision 128766
1/*- 2 * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sbin/ggate/ggated/ggated.c 128766 2004-04-30 16:19:50Z pjd $ 27 */ 28 29#include <stdio.h> 30#include <stdlib.h> 31#include <stdint.h> 32#include <unistd.h> 33#include <fcntl.h> 34#include <sys/param.h> 35#include <sys/queue.h> 36#include <sys/endian.h> 37#include <sys/socket.h> 38#include <sys/ioctl.h> 39#include <sys/stat.h> 40#include <sys/disk.h> 41#include <sys/bio.h> 42#include <netinet/in.h> 43#include <netinet/tcp.h> 44#include <arpa/inet.h> 45#include <signal.h> 46#include <err.h> 47#include <errno.h> 48#include <string.h> 49#include <libgen.h> 50#include <syslog.h> 51#include <stdarg.h> 52 53#include <geom/gate/g_gate.h> 54#include "ggate.h" 55 56 57#define G_GATED_EXPORT_FILE "/etc/gg.exports" 58#define G_GATED_DEBUG(...) \ 59 if (g_gate_verbose) { \ 60 printf(__VA_ARGS__); \ 61 printf("\n"); \ 62 } 63 64static const char *exports = G_GATED_EXPORT_FILE; 65static int got_sighup = 0; 66static int nagle = 1; 67static unsigned rcvbuf = G_GATE_RCVBUF; 68static unsigned sndbuf = G_GATE_SNDBUF; 69 70struct export { 71 char *e_path; /* path to device/file */ 72 in_addr_t e_ip; /* remote IP address */ 73 in_addr_t e_mask; /* IP mask */ 74 unsigned e_flags; /* flags (RO/RW) */ 75 SLIST_ENTRY(export) e_next; 76}; 77static SLIST_HEAD(, export) exports_list = 78 SLIST_HEAD_INITIALIZER(&exports_list); 79 80static void 81usage(void) 82{ 83 84 fprintf(stderr, "usage: %s [-nv] [-a address] [-p port] [-R rcvbuf] " 85 "[-S sndbuf] [exports file]\n", getprogname()); 86 exit(EXIT_FAILURE); 87} 88 89static char * 90ip2str(in_addr_t ip) 91{ 92 static char sip[16]; 93 94 snprintf(sip, sizeof(sip), "%u.%u.%u.%u", 95 ((ip >> 24) & 0xff), 96 ((ip >> 16) & 0xff), 97 ((ip >> 8) & 0xff), 98 (ip & 0xff)); 99 return (sip); 100} 101 102static in_addr_t 103countmask(unsigned m) 104{ 105 in_addr_t mask; 106 107 if (m == 0) { 108 mask = 0x0; 109 } else { 110 mask = 1 << (32 - m); 111 mask--; 112 mask = ~mask; 113 } 114 return (mask); 115} 116 117static void 118line_parse(char *line, unsigned lineno) 119{ 120 struct export *ex; 121 char *word, *path, *sflags; 122 unsigned flags, i, vmask; 123 in_addr_t ip, mask; 124 125 ip = mask = flags = vmask = 0; 126 path = NULL; 127 sflags = NULL; 128 129 for (i = 0, word = strtok(line, " \t"); word != NULL; 130 i++, word = strtok(NULL, " \t")) { 131 switch (i) { 132 case 0: /* IP address or host name */ 133 ip = g_gate_str2ip(strsep(&word, "/")); 134 if (ip == INADDR_NONE) { 135 g_gate_xlog("Invalid IP/host name at line %u.", 136 lineno); 137 } 138 ip = ntohl(ip); 139 if (word == NULL) 140 vmask = 32; 141 else { 142 errno = 0; 143 vmask = strtoul(word, NULL, 10); 144 if (vmask == 0 && errno != 0) { 145 g_gate_xlog("Invalid IP mask value at " 146 "line %u.", lineno); 147 } 148 if ((unsigned)vmask > 32) { 149 g_gate_xlog("Invalid IP mask value at line %u.", 150 lineno); 151 } 152 } 153 mask = countmask(vmask); 154 break; 155 case 1: /* flags */ 156 if (strcasecmp("rd", word) == 0 || 157 strcasecmp("ro", word) == 0) { 158 flags = O_RDONLY; 159 } else if (strcasecmp("wo", word) == 0) { 160 flags = O_WRONLY; 161 } else if (strcasecmp("rw", word) == 0) { 162 flags = O_RDWR; 163 } else { 164 g_gate_xlog("Invalid value in flags field at " 165 "line %u.", lineno); 166 } 167 sflags = word; 168 break; 169 case 2: /* path */ 170 if (strlen(word) >= MAXPATHLEN) { 171 g_gate_xlog("Path too long at line %u. ", 172 lineno); 173 } 174 path = word; 175 break; 176 default: 177 g_gate_xlog("Too many arguments at line %u. ", lineno); 178 } 179 } 180 if (i != 3) 181 g_gate_xlog("Too few arguments at line %u.", lineno); 182 183 ex = malloc(sizeof(*ex)); 184 if (ex == NULL) 185 g_gate_xlog("No enough memory."); 186 ex->e_path = strdup(path); 187 if (ex->e_path == NULL) 188 g_gate_xlog("No enough memory."); 189 190 /* Made 'and' here. */ 191 ex->e_ip = (ip & mask); 192 ex->e_mask = mask; 193 ex->e_flags = flags; 194 195 SLIST_INSERT_HEAD(&exports_list, ex, e_next); 196 197 g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", 198 ip2str(ex->e_ip), vmask, path, sflags); 199} 200 201static void 202exports_clear(void) 203{ 204 struct export *ex; 205 206 while (!SLIST_EMPTY(&exports_list)) { 207 ex = SLIST_FIRST(&exports_list); 208 SLIST_REMOVE_HEAD(&exports_list, e_next); 209 free(ex); 210 } 211} 212 213#define EXPORTS_LINE_SIZE 2048 214static void 215exports_get(void) 216{ 217 char buf[EXPORTS_LINE_SIZE], *line; 218 unsigned lineno = 0, objs = 0, len; 219 FILE *fd; 220 221 exports_clear(); 222 223 fd = fopen(exports, "r"); 224 if (fd == NULL) { 225 g_gate_xlog("Cannot open exports file (%s): %s.", exports, 226 strerror(errno)); 227 } 228 229 g_gate_log(LOG_INFO, "Reading exports file (%s).", exports); 230 231 for (;;) { 232 if (fgets(buf, sizeof(buf), fd) == NULL) { 233 if (feof(fd)) 234 break; 235 236 g_gate_xlog("Error while reading exports file: %s.", 237 strerror(errno)); 238 } 239 240 /* Increase line count. */ 241 lineno++; 242 243 /* Skip spaces and tabs. */ 244 for (line = buf; *line == ' ' || *line == '\t'; ++line) 245 ; 246 247 /* Empty line, comment or empty line at the end of file. */ 248 if (*line == '\n' || *line == '#' || *line == '\0') 249 continue; 250 251 len = strlen(line); 252 if (line[len - 1] == '\n') { 253 /* Remove new line char. */ 254 line[len - 1] = '\0'; 255 } else { 256 if (!feof(fd)) 257 g_gate_xlog("Line %u too long.", lineno); 258 } 259 260 line_parse(line, lineno); 261 objs++; 262 } 263 264 fclose(fd); 265 266 if (objs == 0) 267 g_gate_xlog("There are no objects to export."); 268 269 g_gate_log(LOG_INFO, "Exporting %u object(s).", objs); 270} 271 272static struct export * 273exports_find(struct sockaddr *s, const char *path) 274{ 275 struct export *ex; 276 in_addr_t ip; 277 278 ip = htonl(((struct sockaddr_in *)s)->sin_addr.s_addr); 279 SLIST_FOREACH(ex, &exports_list, e_next) { 280 if ((ip & ex->e_mask) != ex->e_ip) 281 continue; 282 if (path != NULL && strcmp(path, ex->e_path) != 0) 283 continue; 284 285 g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); 286 return (ex); 287 } 288 g_gate_log(LOG_INFO, "Unauthorized connection from: %s.", ip2str(ip)); 289 290 return (NULL); 291} 292 293static void 294sendfail(int sfd, int error, const char *fmt, ...) 295{ 296 struct g_gate_sinit sinit; 297 va_list ap; 298 int data; 299 300 sinit.gs_error = error; 301 g_gate_swap2n_sinit(&sinit); 302 data = send(sfd, &sinit, sizeof(sinit), 0); 303 g_gate_swap2h_sinit(&sinit); 304 if (data == -1) { 305 g_gate_xlog("Error while sending initial packet: %s.", 306 strerror(errno)); 307 } 308 if (fmt != NULL) { 309 va_start(ap, fmt); 310 g_gate_xvlog(fmt, ap); 311 /* NOTREACHED */ 312 va_end(ap); 313 } 314 exit(EXIT_FAILURE); 315} 316 317static void 318serve(int sfd, struct sockaddr *s) 319{ 320 struct g_gate_cinit cinit; 321 struct g_gate_sinit sinit; 322 struct g_gate_hdr hdr; 323 struct export *ex; 324 char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ 325 size_t bufsize; 326 int32_t error; 327 int fd, flags; 328 ssize_t data; 329 char *buf; 330 331 g_gate_log(LOG_DEBUG, "Receiving initial packet."); 332 data = recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); 333 g_gate_swap2h_cinit(&cinit); 334 if (data == -1) { 335 g_gate_xlog("Error while receiving initial packet: %s.", 336 strerror(errno)); 337 } 338 339 ex = exports_find(s, cinit.gc_path); 340 if (ex == NULL) { 341 sendfail(sfd, EINVAL, "Requested path isn't exported: %s.", 342 strerror(errno)); 343 } 344 345 error = 0; 346 strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); 347 strlcat(ipmask, "/", sizeof(ipmask)); 348 strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); 349 if ((cinit.gc_flags & G_GATE_FLAG_READONLY) != 0) { 350 if (ex->e_flags == O_WRONLY) { 351 g_gate_log(LOG_ERR, "Read-only access requested, but " 352 "%s (%s) is exported write-only.", ex->e_path, 353 ipmask); 354 error = EPERM; 355 } else { 356 sinit.gs_flags = G_GATE_FLAG_READONLY; 357 } 358 } else if ((cinit.gc_flags & G_GATE_FLAG_WRITEONLY) != 0) { 359 if (ex->e_flags == O_RDONLY) { 360 g_gate_log(LOG_ERR, "Write-only access requested, but " 361 "%s (%s) is exported read-only.", ex->e_path, 362 ipmask); 363 error = EPERM; 364 } else { 365 sinit.gs_flags = G_GATE_FLAG_WRITEONLY; 366 } 367 } else { 368 if (ex->e_flags == O_RDONLY) { 369 g_gate_log(LOG_ERR, "Read-write access requested, but " 370 "%s (%s) is exported read-only.", ex->e_path, 371 ipmask); 372 error = EPERM; 373 } else if (ex->e_flags == O_WRONLY) { 374 g_gate_log(LOG_ERR, "Read-write access requested, but " 375 "%s (%s) is exported write-only.", ex->e_path, 376 ipmask); 377 error = EPERM; 378 } else { 379 sinit.gs_flags = 0; 380 } 381 } 382 if (error != 0) 383 sendfail(sfd, error, NULL); 384 flags = g_gate_openflags(sinit.gs_flags);; 385 fd = open(ex->e_path, flags); 386 if (fd < 0) { 387 sendfail(sfd, errno, "Error while opening %s: %s.", ex->e_path, 388 strerror(errno)); 389 } 390 391 g_gate_log(LOG_DEBUG, "Sending initial packet."); 392 /* 393 * This field isn't used by ggc(8) for now. 394 * It should be used in future when user don't give device size. 395 */ 396 sinit.gs_mediasize = g_gate_mediasize(fd); 397 sinit.gs_sectorsize = g_gate_sectorsize(fd); 398 sinit.gs_error = 0; 399 g_gate_swap2n_sinit(&sinit); 400 data = send(sfd, &sinit, sizeof(sinit), 0); 401 g_gate_swap2h_sinit(&sinit); 402 if (data == -1) { 403 sendfail(sfd, errno, "Error while sending initial packet: %s.", 404 strerror(errno)); 405 } 406 407 bufsize = G_GATE_BUFSIZE_START; 408 buf = malloc(bufsize); 409 if (buf == NULL) 410 g_gate_xlog("No enough memory."); 411 412 g_gate_log(LOG_DEBUG, "New process: %u.", getpid()); 413 414 for (;;) { 415 /* 416 * Receive request. 417 */ 418 data = recv(sfd, &hdr, sizeof(hdr), MSG_WAITALL); 419 if (data == 0) { 420 g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid()); 421 exit(EXIT_SUCCESS); 422 } else if (data == -1) { 423 g_gate_xlog("Error while receiving hdr packet: %s.", 424 strerror(errno)); 425 } else if (data != sizeof(hdr)) { 426 g_gate_xlog("Malformed hdr packet received."); 427 } 428 g_gate_log(LOG_DEBUG, "Received hdr packet."); 429 g_gate_swap2h_hdr(&hdr); 430 431 /* 432 * Increase buffer if there is need to. 433 */ 434 if (hdr.gh_length > bufsize) { 435 bufsize = hdr.gh_length; 436 g_gate_log(LOG_DEBUG, "Increasing buffer to %u.", 437 bufsize); 438 buf = realloc(buf, bufsize); 439 if (buf == NULL) 440 g_gate_xlog("No enough memory."); 441 } 442 443 if (hdr.gh_cmd == BIO_READ) { 444 if (pread(fd, buf, hdr.gh_length, 445 hdr.gh_offset) == -1) { 446 error = errno; 447 g_gate_log(LOG_ERR, "Error while reading data " 448 "(offset=%ju, size=%zu): %s.", 449 (uintmax_t)hdr.gh_offset, 450 (size_t)hdr.gh_length, strerror(error)); 451 } else { 452 error = 0; 453 } 454 hdr.gh_error = error; 455 g_gate_swap2n_hdr(&hdr); 456 if (send(sfd, &hdr, sizeof(hdr), 0) == -1) { 457 g_gate_xlog("Error while sending status: %s.", 458 strerror(errno)); 459 } 460 g_gate_swap2h_hdr(&hdr); 461 /* Send data only if there was no error while pread(). */ 462 if (error == 0) { 463 data = send(sfd, buf, hdr.gh_length, 0); 464 if (data == -1) { 465 g_gate_xlog("Error while sending data: " 466 "%s.", strerror(errno)); 467 } 468 g_gate_log(LOG_DEBUG, "Sent %d bytes " 469 "(offset=%ju, size=%zu).", data, 470 (uintmax_t)hdr.gh_offset, 471 (size_t)hdr.gh_length); 472 } 473 } else /* if (hdr.gh_cmd == BIO_WRITE) */ { 474 g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", 475 hdr.gh_length); 476 data = recv(sfd, buf, hdr.gh_length, MSG_WAITALL); 477 if (data == -1) { 478 g_gate_xlog("Error while receiving data: %s.", 479 strerror(errno)); 480 } 481 if (pwrite(fd, buf, hdr.gh_length, hdr.gh_offset) == -1) { 482 error = errno; 483 g_gate_log(LOG_ERR, "Error while writing data " 484 "(offset=%llu, size=%u): %s.", 485 hdr.gh_offset, hdr.gh_length, 486 strerror(error)); 487 } else { 488 error = 0; 489 } 490 hdr.gh_error = error; 491 g_gate_swap2n_hdr(&hdr); 492 if (send(sfd, &hdr, sizeof(hdr), 0) == -1) { 493 g_gate_xlog("Error while sending status: %s.", 494 strerror(errno)); 495 } 496 g_gate_swap2h_hdr(&hdr); 497 g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%llu, " 498 "size=%u).", data, hdr.gh_offset, hdr.gh_length); 499 } 500 g_gate_log(LOG_DEBUG, "Tick."); 501 } 502} 503 504static void 505huphandler(int sig __unused) 506{ 507 508 got_sighup = 1; 509} 510 511int 512main(int argc, char *argv[]) 513{ 514 struct sockaddr_in serv; 515 struct sockaddr from; 516 in_addr_t bindaddr; 517 socklen_t fromlen; 518 struct timeval tv; 519 int on, sfd, tmpsfd; 520 pid_t childpid; 521 unsigned bsize, port; 522 523 bindaddr = htonl(INADDR_ANY); 524 port = G_GATE_PORT; 525 for (;;) { 526 int ch; 527 528 ch = getopt(argc, argv, "a:hnp:R:S:v"); 529 if (ch == -1) 530 break; 531 switch (ch) { 532 case 'a': 533 bindaddr = g_gate_str2ip(optarg); 534 if (bindaddr == INADDR_NONE) { 535 errx(EXIT_FAILURE, 536 "Invalid IP/host name to bind to."); 537 } 538 break; 539 case 'n': 540 nagle = 0; 541 break; 542 case 'p': 543 errno = 0; 544 port = strtoul(optarg, NULL, 10); 545 if (port == 0 && errno != 0) 546 errx(EXIT_FAILURE, "Invalid port."); 547 break; 548 case 'R': 549 errno = 0; 550 rcvbuf = strtoul(optarg, NULL, 10); 551 if (rcvbuf == 0 && errno != 0) 552 errx(EXIT_FAILURE, "Invalid rcvbuf."); 553 break; 554 case 'S': 555 errno = 0; 556 sndbuf = strtoul(optarg, NULL, 10); 557 if (sndbuf == 0 && errno != 0) 558 errx(EXIT_FAILURE, "Invalid sndbuf."); 559 break; 560 case 'v': 561 g_gate_verbose++; 562 break; 563 case 'h': 564 default: 565 usage(); 566 } 567 } 568 argc -= optind; 569 argv += optind; 570 571 if (argv[0] != NULL) 572 exports = argv[0]; 573 exports_get(); 574 575 if (!g_gate_verbose) { 576 /* Run in daemon mode. */ 577 if (daemon(0, 0) < 0) 578 g_gate_xlog("Can't daemonize: %s", strerror(errno)); 579 } 580 581 signal(SIGCHLD, SIG_IGN); 582 583 sfd = socket(AF_INET, SOCK_STREAM, 0); 584 if (sfd < 0) 585 g_gate_xlog("Can't open stream socket: %s.", strerror(errno)); 586 bzero(&serv, sizeof(serv)); 587 serv.sin_family = AF_INET; 588 serv.sin_addr.s_addr = bindaddr; 589 serv.sin_port = htons(port); 590 on = 1; 591 if (nagle) { 592 if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on, 593 sizeof(on)) < 0) { 594 g_gate_xlog("setsockopt() error: %s.", strerror(errno)); 595 } 596 } 597 if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) 598 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 599 bsize = rcvbuf; 600 if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) < 0) 601 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 602 bsize = sndbuf; 603 if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)) < 0) 604 g_gate_xlog("setsockopt(): %s.", strerror(errno)); 605 tv.tv_sec = 10; 606 tv.tv_usec = 0; 607 if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) || 608 setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { 609 g_gate_xlog("setsockopt() error: %s.", strerror(errno)); 610 } 611 if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) < 0) 612 g_gate_xlog("bind(): %s.", strerror(errno)); 613 if (listen(sfd, 5) < 0) 614 g_gate_xlog("listen(): %s.", strerror(errno)); 615 616 g_gate_log(LOG_INFO, "Listen on port: %d.", port); 617 618 signal(SIGHUP, huphandler); 619 620 for (;;) { 621 fromlen = sizeof(from); 622 tmpsfd = accept(sfd, &from, &fromlen); 623 if (tmpsfd < 0) 624 g_gate_xlog("accept(): %s.", strerror(errno)); 625 626 if (got_sighup) { 627 got_sighup = 0; 628 exports_get(); 629 } 630 631 if (exports_find(&from, NULL) == NULL) { 632 close(tmpsfd); 633 continue; 634 } 635 636 childpid = fork(); 637 if (childpid < 0) { 638 g_gate_xlog("Cannot create child process: %s.", 639 strerror(errno)); 640 } else if (childpid == 0) { 641 close(sfd); 642 serve(tmpsfd, &from); 643 /* NOTREACHED */ 644 } 645 close(tmpsfd); 646 } 647 close(sfd); 648 exit(EXIT_SUCCESS); 649} 650