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