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