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