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 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 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 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 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 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 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 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 183 g_gate_close_device(void) 184 { 185 186 close(g_gate_devfd); 187 } 188 189 void 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 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 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 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 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 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 * 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 * 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 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 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 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