1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2023 Igor Ostapenko <pm@igoro.pro>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /* Used by divert(4) related tests */
29
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdbool.h>
33 #include <err.h>
34 #include <sysexits.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41
42
43 struct context {
44 unsigned short divert_port;
45 bool divert_back;
46
47 int fd;
48 struct sockaddr_in sin;
49 socklen_t sin_len;
50 char pkt[IP_MAXPACKET];
51 ssize_t pkt_n;
52 };
53
54 static void
init(struct context * c)55 init(struct context *c)
56 {
57 c->fd = socket(PF_DIVERT, SOCK_RAW, 0);
58 if (c->fd == -1)
59 errx(EX_OSERR, "init: Cannot create divert socket.");
60
61 memset(&c->sin, 0, sizeof(c->sin));
62 c->sin.sin_family = AF_INET;
63 c->sin.sin_port = htons(c->divert_port);
64 c->sin.sin_addr.s_addr = INADDR_ANY;
65 c->sin_len = sizeof(struct sockaddr_in);
66
67 if (bind(c->fd, (struct sockaddr *) &c->sin, c->sin_len) != 0)
68 errx(EX_OSERR, "init: Cannot bind divert socket.");
69 }
70
71 static ssize_t
recv_pkt(struct context * c)72 recv_pkt(struct context *c)
73 {
74 fd_set readfds;
75 struct timeval timeout;
76 int s;
77
78 FD_ZERO(&readfds);
79 FD_SET(c->fd, &readfds);
80 timeout.tv_sec = 3;
81 timeout.tv_usec = 0;
82
83 s = select(c->fd + 1, &readfds, 0, 0, &timeout);
84 if (s == -1)
85 errx(EX_IOERR, "recv_pkt: select() errors.");
86 if (s != 1) /* timeout */
87 return (-1);
88
89 c->pkt_n = recvfrom(c->fd, c->pkt, sizeof(c->pkt), 0,
90 (struct sockaddr *) &c->sin, &c->sin_len);
91 if (c->pkt_n == -1)
92 errx(EX_IOERR, "recv_pkt: recvfrom() errors.");
93
94 return (c->pkt_n);
95 }
96
97 static void
send_pkt(struct context * c)98 send_pkt(struct context *c)
99 {
100 ssize_t n;
101
102 n = sendto(c->fd, c->pkt, c->pkt_n, 0,
103 (struct sockaddr *) &c->sin, c->sin_len);
104 if (n == -1)
105 err(EX_IOERR, "send_pkt: sendto() errors");
106 if (n != c->pkt_n)
107 errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.",
108 n, c->pkt_n);
109 }
110
111 int
main(int argc,char * argv[])112 main(int argc, char *argv[])
113 {
114 struct context c;
115 int npkt;
116
117 if (argc < 2)
118 errx(EX_USAGE,
119 "Usage: %s <divert-port> [divert-back]", argv[0]);
120
121 memset(&c, 0, sizeof(struct context));
122
123 c.divert_port = (unsigned short) strtol(argv[1], NULL, 10);
124 if (c.divert_port == 0)
125 errx(EX_USAGE, "divert port is not defined.");
126
127 if (argc >= 3 && strcmp(argv[2], "divert-back") == 0)
128 c.divert_back = true;
129
130
131 init(&c);
132
133 npkt = 0;
134 while (recv_pkt(&c) > 0) {
135 if (c.divert_back)
136 send_pkt(&c);
137 npkt++;
138 if (npkt >= 10)
139 break;
140 }
141
142 if (npkt != 1)
143 errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt);
144
145 return (EXIT_SUCCESS);
146 }
147