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 62 int g_gate_devfd = -1; 63 int g_gate_verbose = 0; 64 65 66 void 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 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 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 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 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 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 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 185 g_gate_close_device(void) 186 { 187 188 close(g_gate_devfd); 189 } 190 191 void 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 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 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 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 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 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 * 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 * 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 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 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 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