xref: /freebsd/tests/sys/netpfil/pf/divapp.c (revision abd87254)
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 tests like divert-to.sh */
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
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
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
98 send_pkt(struct context *c)
99 {
100 	ssize_t n;
101 	char errstr[32];
102 
103 	n = sendto(c->fd, c->pkt, c->pkt_n, 0,
104 	    (struct sockaddr *) &c->sin, c->sin_len);
105 	if (n == -1) {
106 		strerror_r(errno, errstr, sizeof(errstr));
107 		errx(EX_IOERR, "send_pkt: sendto() errors: %d %s.", errno, errstr);
108 	}
109 	if (n != c->pkt_n)
110 		errx(EX_IOERR, "send_pkt: sendto() sent %zd of %zd bytes.",
111 		    n, c->pkt_n);
112 }
113 
114 int
115 main(int argc, char *argv[])
116 {
117 	struct context c;
118 	int npkt;
119 
120 	if (argc < 2)
121 		errx(EX_USAGE,
122 		    "Usage: %s <divert-port> [divert-back]", argv[0]);
123 
124 	memset(&c, 0, sizeof(struct context));
125 
126 	c.divert_port = (unsigned short) strtol(argv[1], NULL, 10);
127 	if (c.divert_port == 0)
128 		errx(EX_USAGE, "divert port is not defined.");
129 
130 	if (argc >= 3 && strcmp(argv[2], "divert-back") == 0)
131 		c.divert_back = true;
132 
133 
134 	init(&c);
135 
136 	npkt = 0;
137 	while (recv_pkt(&c) > 0) {
138 		if (c.divert_back)
139 			send_pkt(&c);
140 		npkt++;
141 		if (npkt >= 10)
142 			break;
143 	}
144 
145 	if (npkt != 1)
146 		errx(EXIT_FAILURE, "%d: npkt=%d.", c.divert_port, npkt);
147 
148 	return EXIT_SUCCESS;
149 }
150