19b86b272SIgor Ostapenko /*- 29b86b272SIgor Ostapenko * SPDX-License-Identifier: BSD-2-Clause 39b86b272SIgor Ostapenko * 49b86b272SIgor Ostapenko * Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro> 59b86b272SIgor Ostapenko * 69b86b272SIgor Ostapenko * Redistribution and use in source and binary forms, with or without 79b86b272SIgor Ostapenko * modification, are permitted provided that the following conditions 89b86b272SIgor Ostapenko * are met: 99b86b272SIgor Ostapenko * 1. Redistributions of source code must retain the above copyright 109b86b272SIgor Ostapenko * notice, this list of conditions and the following disclaimer. 119b86b272SIgor Ostapenko * 2. Redistributions in binary form must reproduce the above copyright 129b86b272SIgor Ostapenko * notice, this list of conditions and the following disclaimer in the 139b86b272SIgor Ostapenko * documentation and/or other materials provided with the distribution. 149b86b272SIgor Ostapenko * 159b86b272SIgor Ostapenko * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 169b86b272SIgor Ostapenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 179b86b272SIgor Ostapenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 189b86b272SIgor Ostapenko * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 199b86b272SIgor Ostapenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 209b86b272SIgor Ostapenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 219b86b272SIgor Ostapenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 229b86b272SIgor Ostapenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 239b86b272SIgor Ostapenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 249b86b272SIgor Ostapenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 259b86b272SIgor Ostapenko * SUCH DAMAGE. 269b86b272SIgor Ostapenko */ 279b86b272SIgor Ostapenko 289b86b272SIgor Ostapenko /* Used by divert(4) related tests */ 299b86b272SIgor Ostapenko 309b86b272SIgor Ostapenko #include <errno.h> 319b86b272SIgor Ostapenko #include <stdlib.h> 329b86b272SIgor Ostapenko #include <stdbool.h> 339b86b272SIgor Ostapenko #include <err.h> 349b86b272SIgor Ostapenko #include <sysexits.h> 359b86b272SIgor Ostapenko #include <string.h> 369b86b272SIgor Ostapenko 379b86b272SIgor Ostapenko #include <sys/types.h> 389b86b272SIgor Ostapenko #include <sys/socket.h> 399b86b272SIgor Ostapenko #include <netinet/in.h> 409b86b272SIgor Ostapenko #include <netinet/ip.h> 419b86b272SIgor Ostapenko 429b86b272SIgor Ostapenko 439b86b272SIgor Ostapenko struct context { 449b86b272SIgor Ostapenko unsigned short divert_port; 459b86b272SIgor Ostapenko bool divert_back; 469b86b272SIgor Ostapenko 479b86b272SIgor Ostapenko int fd; 489b86b272SIgor Ostapenko struct sockaddr_in sin; 499b86b272SIgor Ostapenko socklen_t sin_len; 509b86b272SIgor Ostapenko char pkt[IP_MAXPACKET]; 519b86b272SIgor Ostapenko ssize_t pkt_n; 529b86b272SIgor Ostapenko }; 539b86b272SIgor Ostapenko 549b86b272SIgor Ostapenko static void 559b86b272SIgor Ostapenko init(struct context *c) 569b86b272SIgor Ostapenko { 579b86b272SIgor Ostapenko c->fd = socket(PF_DIVERT, SOCK_RAW, 0); 589b86b272SIgor Ostapenko if (c->fd == -1) 599b86b272SIgor Ostapenko errx(EX_OSERR, "init: Cannot create divert socket."); 609b86b272SIgor Ostapenko 619b86b272SIgor Ostapenko memset(&c->sin, 0, sizeof(c->sin)); 629b86b272SIgor Ostapenko c->sin.sin_family = AF_INET; 639b86b272SIgor Ostapenko c->sin.sin_port = htons(c->divert_port); 649b86b272SIgor Ostapenko c->sin.sin_addr.s_addr = INADDR_ANY; 659b86b272SIgor Ostapenko c->sin_len = sizeof(struct sockaddr_in); 669b86b272SIgor Ostapenko 679b86b272SIgor Ostapenko if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0) 689b86b272SIgor Ostapenko errx(EX_OSERR, "init: Cannot bind divert socket."); 699b86b272SIgor Ostapenko } 709b86b272SIgor Ostapenko 719b86b272SIgor Ostapenko static ssize_t 729b86b272SIgor Ostapenko recv_pkt(struct context *c) 739b86b272SIgor Ostapenko { 749b86b272SIgor Ostapenko fd_set readfds; 759b86b272SIgor Ostapenko struct timeval timeout; 769b86b272SIgor Ostapenko int s; 779b86b272SIgor Ostapenko 789b86b272SIgor Ostapenko FD_ZERO(&readfds); 799b86b272SIgor Ostapenko FD_SET(c->fd, &readfds); 809b86b272SIgor Ostapenko timeout.tv_sec = 3; 819b86b272SIgor Ostapenko timeout.tv_usec = 0; 829b86b272SIgor Ostapenko 839b86b272SIgor Ostapenko s = select(c->fd + 1, &readfds, 0, 0, &timeout); 849b86b272SIgor Ostapenko if (s == -1) 859b86b272SIgor Ostapenko errx(EX_IOERR, "recv_pkt: select() errors."); 86*b3e7d4b6SIgor Ostapenko if (s != 1) /* timeout */ 87*b3e7d4b6SIgor Ostapenko return (-1); 889b86b272SIgor Ostapenko 899b86b272SIgor Ostapenko c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0, 909b86b272SIgor Ostapenko (struct sockaddr *) &c->sin, &c->sin_len); 919b86b272SIgor Ostapenko if (c->pkt_n == -1) 929b86b272SIgor Ostapenko errx(EX_IOERR, "recv_pkt: recvfrom() errors."); 939b86b272SIgor Ostapenko 949b86b272SIgor Ostapenko return (c->pkt_n); 959b86b272SIgor Ostapenko } 969b86b272SIgor Ostapenko 979b86b272SIgor Ostapenko static void 989b86b272SIgor Ostapenko send_pkt(struct context *c) 999b86b272SIgor Ostapenko { 1009b86b272SIgor Ostapenko ssize_t n; 1019b86b272SIgor Ostapenko 1029b86b272SIgor Ostapenko n = sendto(c->fd, c->pkt, c->pkt_n, 0, 1039b86b272SIgor Ostapenko (struct sockaddr *) &c->sin, c->sin_len); 104*b3e7d4b6SIgor Ostapenko if (n == -1) 105*b3e7d4b6SIgor Ostapenko err(EX_IOERR, "send_pkt: sendto() errors"); 1069b86b272SIgor Ostapenko if (n != c->pkt_n) 1079b86b272SIgor Ostapenko errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.", 1089b86b272SIgor Ostapenko n, c->pkt_n); 1099b86b272SIgor Ostapenko } 1109b86b272SIgor Ostapenko 1119b86b272SIgor Ostapenko int 1129b86b272SIgor Ostapenko main(int argc, char *argv[]) 1139b86b272SIgor Ostapenko { 1149b86b272SIgor Ostapenko struct context c; 1159b86b272SIgor Ostapenko int npkt; 1169b86b272SIgor Ostapenko 1179b86b272SIgor Ostapenko if (argc < 2) 1189b86b272SIgor Ostapenko errx(EX_USAGE, 1199b86b272SIgor Ostapenko "Usage: %s <divert-port> [divert-back]", argv[0]); 1209b86b272SIgor Ostapenko 1219b86b272SIgor Ostapenko memset(&c, 0, sizeof(struct context)); 1229b86b272SIgor Ostapenko 1239b86b272SIgor Ostapenko c.divert_port = (unsigned short) strtol(argv[1], NULL, 10); 1249b86b272SIgor Ostapenko if (c.divert_port == 0) 1259b86b272SIgor Ostapenko errx(EX_USAGE, "divert port is not defined."); 1269b86b272SIgor Ostapenko 1279b86b272SIgor Ostapenko if (argc >= 3 && strcmp(argv[2], "divert-back") == 0) 1289b86b272SIgor Ostapenko c.divert_back = true; 1299b86b272SIgor Ostapenko 1309b86b272SIgor Ostapenko 1319b86b272SIgor Ostapenko init(&c); 1329b86b272SIgor Ostapenko 1339b86b272SIgor Ostapenko npkt = 0; 1349b86b272SIgor Ostapenko while (recv_pkt(&c) > 0) { 1359b86b272SIgor Ostapenko if (c.divert_back) 1369b86b272SIgor Ostapenko send_pkt(&c); 1379b86b272SIgor Ostapenko npkt++; 1389b86b272SIgor Ostapenko if (npkt >= 10) 1399b86b272SIgor Ostapenko break; 1409b86b272SIgor Ostapenko } 1419b86b272SIgor Ostapenko 1429b86b272SIgor Ostapenko if (npkt != 1) 1439b86b272SIgor Ostapenko errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt); 1449b86b272SIgor Ostapenko 145*b3e7d4b6SIgor Ostapenko return (EXIT_SUCCESS); 1469b86b272SIgor Ostapenko } 147