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
62int g_gate_devfd = -1;
63int g_gate_verbose = 0;
64
65
66void
67g_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
102void
103g_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
112void
113g_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
121void
122g_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
133off_t
134g_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
154unsigned
155g_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
175void
176g_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
184void
185g_gate_close_device(void)
186{
187
188	close(g_gate_devfd);
189}
190
191void
192g_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
201void
202g_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
212void
213g_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
237ssize_t
238g_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
262ssize_t
263g_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
273int nagle = 1;
274unsigned rcvbuf = G_GATE_RCVBUF;
275unsigned sndbuf = G_GATE_SNDBUF;
276
277void
278g_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
312static struct gclass *
313find_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
324static const char *
325get_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
336static void
337show_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
364void
365g_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
395in_addr_t
396g_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