1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 Pawel Jakub Dawidek <[email protected]>
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
g_gate_vlog(int priority,const char * message,va_list ap)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
g_gate_log(int priority,const char * message,...)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
g_gate_xvlog(const char * message,va_list ap)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
g_gate_xlog(const char * message,...)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
g_gate_mediasize(int fd)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
g_gate_sectorsize(int fd)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
g_gate_open_device(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
g_gate_close_device(void)185 g_gate_close_device(void)
186 {
187
188 close(g_gate_devfd);
189 }
190
191 void
g_gate_ioctl(unsigned long req,void * data)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
g_gate_destroy(int unit,int force)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
g_gate_load_module(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
g_gate_send(int s,const void * buf,size_t len,int flags)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
g_gate_recv(int s,void * buf,size_t len,int flags)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
g_gate_socket_settings(int sfd)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 *
find_class(struct gmesh * mesh,const char * name)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 *
get_conf(struct ggeom * gp,const char * name)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
show_config(struct ggeom * gp,int verbose)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
g_gate_list(int unit,int verbose)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
g_gate_str2ip(const char * str)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