xref: /freebsd/sbin/ggate/shared/ggate.c (revision f7c32ed617858bcd22f8d1b03199099d50125721)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/param.h>
36 #include <sys/disk.h>
37 #include <sys/stat.h>
38 #include <sys/endian.h>
39 #include <sys/socket.h>
40 #include <sys/linker.h>
41 #include <sys/module.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 <strings.h>
50 #include <libgen.h>
51 #include <libutil.h>
52 #include <netdb.h>
53 #include <syslog.h>
54 #include <stdarg.h>
55 #include <stdint.h>
56 #include <libgeom.h>
57 
58 #include <geom/gate/g_gate.h>
59 #include "ggate.h"
60 
61 
62 int g_gate_devfd = -1;
63 int g_gate_verbose = 0;
64 
65 
66 void
67 g_gate_vlog(int priority, const char *message, va_list ap)
68 {
69 
70 	if (g_gate_verbose) {
71 		const char *prefix;
72 
73 		switch (priority) {
74 		case LOG_ERR:
75 			prefix = "error";
76 			break;
77 		case LOG_WARNING:
78 			prefix = "warning";
79 			break;
80 		case LOG_NOTICE:
81 			prefix = "notice";
82 			break;
83 		case LOG_INFO:
84 			prefix = "info";
85 			break;
86 		case LOG_DEBUG:
87 			prefix = "debug";
88 			break;
89 		default:
90 			prefix = "unknown";
91 		}
92 
93 		printf("%s: ", prefix);
94 		vprintf(message, ap);
95 		printf("\n");
96 	} else {
97 		if (priority != LOG_DEBUG)
98 			vsyslog(priority, message, ap);
99 	}
100 }
101 
102 void
103 g_gate_log(int priority, const char *message, ...)
104 {
105 	va_list ap;
106 
107 	va_start(ap, message);
108 	g_gate_vlog(priority, message, ap);
109 	va_end(ap);
110 }
111 
112 void
113 g_gate_xvlog(const char *message, va_list ap)
114 {
115 
116 	g_gate_vlog(LOG_ERR, message, ap);
117 	g_gate_vlog(LOG_ERR, "Exiting.", ap);
118 	exit(EXIT_FAILURE);
119 }
120 
121 void
122 g_gate_xlog(const char *message, ...)
123 {
124 	va_list ap;
125 
126 	va_start(ap, message);
127 	g_gate_xvlog(message, ap);
128 	/* NOTREACHED */
129 	va_end(ap);
130 	exit(EXIT_FAILURE);
131 }
132 
133 off_t
134 g_gate_mediasize(int fd)
135 {
136 	off_t mediasize;
137 	struct stat sb;
138 
139 	if (fstat(fd, &sb) == -1)
140 		g_gate_xlog("fstat(): %s.", strerror(errno));
141 	if (S_ISCHR(sb.st_mode)) {
142 		if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) {
143 			g_gate_xlog("Can't get media size: %s.",
144 			    strerror(errno));
145 		}
146 	} else if (S_ISREG(sb.st_mode)) {
147 		mediasize = sb.st_size;
148 	} else {
149 		g_gate_xlog("Unsupported file system object.");
150 	}
151 	return (mediasize);
152 }
153 
154 unsigned
155 g_gate_sectorsize(int fd)
156 {
157 	unsigned secsize;
158 	struct stat sb;
159 
160 	if (fstat(fd, &sb) == -1)
161 		g_gate_xlog("fstat(): %s.", strerror(errno));
162 	if (S_ISCHR(sb.st_mode)) {
163 		if (ioctl(fd, DIOCGSECTORSIZE, &secsize) == -1) {
164 			g_gate_xlog("Can't get sector size: %s.",
165 			    strerror(errno));
166 		}
167 	} else if (S_ISREG(sb.st_mode)) {
168 		secsize = 512;
169 	} else {
170 		g_gate_xlog("Unsupported file system object.");
171 	}
172 	return (secsize);
173 }
174 
175 void
176 g_gate_open_device(void)
177 {
178 
179 	g_gate_devfd = open("/dev/" G_GATE_CTL_NAME, O_RDWR);
180 	if (g_gate_devfd == -1)
181 		err(EXIT_FAILURE, "open(/dev/%s)", G_GATE_CTL_NAME);
182 }
183 
184 void
185 g_gate_close_device(void)
186 {
187 
188 	close(g_gate_devfd);
189 }
190 
191 void
192 g_gate_ioctl(unsigned long req, void *data)
193 {
194 
195 	if (ioctl(g_gate_devfd, req, data) == -1) {
196 		g_gate_xlog("%s: ioctl(/dev/%s): %s.", getprogname(),
197 		    G_GATE_CTL_NAME, strerror(errno));
198 	}
199 }
200 
201 void
202 g_gate_destroy(int unit, int force)
203 {
204 	struct g_gate_ctl_destroy ggio;
205 
206 	ggio.gctl_version = G_GATE_VERSION;
207 	ggio.gctl_unit = unit;
208 	ggio.gctl_force = force;
209 	g_gate_ioctl(G_GATE_CMD_DESTROY, &ggio);
210 }
211 
212 void
213 g_gate_load_module(void)
214 {
215 
216 	if (modfind("g_gate") == -1) {
217 		/* Not present in kernel, try loading it. */
218 		if (kldload("geom_gate") == -1 || modfind("g_gate") == -1) {
219 			if (errno != EEXIST) {
220 				errx(EXIT_FAILURE,
221 				    "geom_gate module not available!");
222 			}
223 		}
224 	}
225 }
226 
227 /*
228  * When we send from ggatec packets larger than 32kB, performance drops
229  * significantly (eg. to 256kB/s over 1Gbit/s link). This is not a problem
230  * when data is send from ggated. I don't know why, so for now I limit
231  * size of packets send from ggatec to 32kB by defining MAX_SEND_SIZE
232  * in ggatec Makefile.
233  */
234 #ifndef	MAX_SEND_SIZE
235 #define	MAX_SEND_SIZE	MAXPHYS
236 #endif
237 ssize_t
238 g_gate_send(int s, const void *buf, size_t len, int flags)
239 {
240 	ssize_t done = 0, done2;
241 	const unsigned char *p = buf;
242 
243 	while (len > 0) {
244 		done2 = send(s, p, MIN(len, MAX_SEND_SIZE), flags);
245 		if (done2 == 0)
246 			break;
247 		else if (done2 == -1) {
248 			if (errno == EAGAIN) {
249 				printf("%s: EAGAIN\n", __func__);
250 				continue;
251 			}
252 			done = -1;
253 			break;
254 		}
255 		done += done2;
256 		p += done2;
257 		len -= done2;
258 	}
259 	return (done);
260 }
261 
262 ssize_t
263 g_gate_recv(int s, void *buf, size_t len, int flags)
264 {
265 	ssize_t done;
266 
267 	do {
268 		done = recv(s, buf, len, flags);
269 	} while (done == -1 && errno == EAGAIN);
270 	return (done);
271 }
272 
273 int nagle = 1;
274 unsigned rcvbuf = G_GATE_RCVBUF;
275 unsigned sndbuf = G_GATE_SNDBUF;
276 
277 void
278 g_gate_socket_settings(int sfd)
279 {
280 	struct timeval tv;
281 	int bsize, on;
282 
283 	/* Socket settings. */
284 	on = 1;
285 	if (nagle) {
286 		if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on,
287 		    sizeof(on)) == -1) {
288 			g_gate_xlog("setsockopt() error: %s.", strerror(errno));
289 		}
290 	}
291 	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
292 		g_gate_xlog("setsockopt(SO_REUSEADDR): %s.", strerror(errno));
293 	bsize = rcvbuf;
294 	if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == -1)
295 		g_gate_xlog("setsockopt(SO_RCVBUF): %s.", strerror(errno));
296 	bsize = sndbuf;
297 	if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)) == -1)
298 		g_gate_xlog("setsockopt(SO_SNDBUF): %s.", strerror(errno));
299 	tv.tv_sec = 8;
300 	tv.tv_usec = 0;
301 	if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
302 		g_gate_log(LOG_ERR, "setsockopt(SO_SNDTIMEO) error: %s.",
303 		    strerror(errno));
304 	}
305 	if (setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
306 		g_gate_log(LOG_ERR, "setsockopt(SO_RCVTIMEO) error: %s.",
307 		    strerror(errno));
308 	}
309 }
310 
311 #ifdef LIBGEOM
312 static struct gclass *
313 find_class(struct gmesh *mesh, const char *name)
314 {
315 	struct gclass *class;
316 
317 	LIST_FOREACH(class, &mesh->lg_class, lg_class) {
318 		if (strcmp(class->lg_name, name) == 0)
319 			return (class);
320 	}
321 	return (NULL);
322 }
323 
324 static const char *
325 get_conf(struct ggeom *gp, const char *name)
326 {
327 	struct gconfig *conf;
328 
329 	LIST_FOREACH(conf, &gp->lg_config, lg_config) {
330 		if (strcmp(conf->lg_name, name) == 0)
331 			return (conf->lg_val);
332 	}
333 	return (NULL);
334 }
335 
336 static void
337 show_config(struct ggeom *gp, int verbose)
338 {
339 	struct gprovider *pp;
340 	char buf[5];
341 
342 	pp = LIST_FIRST(&gp->lg_provider);
343 	if (pp == NULL)
344 		return;
345 	if (!verbose) {
346 		printf("%s\n", pp->lg_name);
347 		return;
348 	}
349 	printf("       NAME: %s\n", pp->lg_name);
350 	printf("       info: %s\n", get_conf(gp, "info"));
351 	printf("     access: %s\n", get_conf(gp, "access"));
352 	printf("    timeout: %s\n", get_conf(gp, "timeout"));
353 	printf("queue_count: %s\n", get_conf(gp, "queue_count"));
354 	printf(" queue_size: %s\n", get_conf(gp, "queue_size"));
355 	printf(" references: %s\n", get_conf(gp, "ref"));
356 	humanize_number(buf, sizeof(buf), (int64_t)pp->lg_mediasize, "",
357 	    HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
358 	printf("  mediasize: %jd (%s)\n", (intmax_t)pp->lg_mediasize, buf);
359 	printf(" sectorsize: %u\n", pp->lg_sectorsize);
360 	printf("       mode: %s\n", pp->lg_mode);
361 	printf("\n");
362 }
363 
364 void
365 g_gate_list(int unit, int verbose)
366 {
367 	struct gmesh mesh;
368 	struct gclass *class;
369 	struct ggeom *gp;
370 	char name[64];
371 	int error;
372 
373 	error = geom_gettree(&mesh);
374 	if (error != 0)
375 		exit(EXIT_FAILURE);
376 	class = find_class(&mesh, G_GATE_CLASS_NAME);
377 	if (class == NULL) {
378 		geom_deletetree(&mesh);
379 		exit(EXIT_SUCCESS);
380 	}
381 	if (unit >= 0) {
382 		snprintf(name, sizeof(name), "%s%d", G_GATE_PROVIDER_NAME,
383 		    unit);
384 	}
385 	LIST_FOREACH(gp, &class->lg_geom, lg_geom) {
386 		if (unit != -1 && strcmp(gp->lg_name, name) != 0)
387 			continue;
388 		show_config(gp, verbose);
389 	}
390 	geom_deletetree(&mesh);
391 	exit(EXIT_SUCCESS);
392 }
393 #endif	/* LIBGEOM */
394 
395 in_addr_t
396 g_gate_str2ip(const char *str)
397 {
398 	struct hostent *hp;
399 	in_addr_t ip;
400 
401 	ip = inet_addr(str);
402 	if (ip != INADDR_NONE) {
403 		/* It is a valid IP address. */
404 		return (ip);
405 	}
406 	/* Check if it is a valid host name. */
407 	hp = gethostbyname(str);
408 	if (hp == NULL)
409 		return (INADDR_NONE);
410 	return (((struct in_addr *)(void *)hp->h_addr)->s_addr);
411 }
412