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
60 int g_gate_devfd = -1;
61 int g_gate_verbose = 0;
62
63
64 void
g_gate_vlog(int priority,const char * message,va_list ap)65 g_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
100 void
g_gate_log(int priority,const char * message,...)101 g_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
110 void
g_gate_xvlog(const char * message,va_list ap)111 g_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
119 void
g_gate_xlog(const char * message,...)120 g_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
131 off_t
g_gate_mediasize(int fd)132 g_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
152 unsigned
g_gate_sectorsize(int fd)153 g_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
173 void
g_gate_open_device(void)174 g_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
182 void
g_gate_close_device(void)183 g_gate_close_device(void)
184 {
185
186 close(g_gate_devfd);
187 }
188
189 void
g_gate_ioctl(unsigned long req,void * data)190 g_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
199 void
g_gate_destroy(int unit,int force)200 g_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
210 void
g_gate_load_module(void)211 g_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
235 ssize_t
g_gate_send(int s,const void * buf,size_t len,int flags)236 g_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
260 ssize_t
g_gate_recv(int s,void * buf,size_t len,int flags)261 g_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
271 int nagle = 1;
272 unsigned rcvbuf = G_GATE_RCVBUF;
273 unsigned sndbuf = G_GATE_SNDBUF;
274
275 void
g_gate_socket_settings(int sfd)276 g_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
310 static struct gclass *
find_class(struct gmesh * mesh,const char * name)311 find_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
322 static const char *
get_conf(struct ggeom * gp,const char * name)323 get_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
334 static void
show_config(struct ggeom * gp,int verbose)335 show_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
362 void
g_gate_list(int unit,int verbose)363 g_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
393 in_addr_t
g_gate_str2ip(const char * str)394 g_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