1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Gleb Smirnoff <glebius@FreeBSD.org>
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 AUTHOR 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 AUTHOR 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 #include <sys/param.h>
29 #include <sys/ioctl.h>
30 #include <net/if.h>
31 #include <net/pfil.h>
32
33 #include <err.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 static int dev;
41
42 static const char * const typenames[] = {
43 [PFIL_TYPE_IP4] = "IPv4",
44 [PFIL_TYPE_IP6] = "IPv6",
45 [PFIL_TYPE_ETHERNET] = "Ethernet",
46 };
47
48 static void listheads(int argc, char *argv[]);
49 static void listhooks(int argc, char *argv[]);
50 static void hook(int argc, char *argv[]);
51 static void help(void);
52
53 static const struct cmd {
54 const char *cmd_name;
55 void (*cmd_func)(int argc, char *argv[]);
56 } cmds[] = {
57 { "heads", listheads },
58 { "hooks", listhooks },
59 { "link", hook },
60 { "unlink", hook },
61 { NULL, NULL },
62 };
63
64 int
main(int argc __unused,char * argv[]__unused)65 main(int argc __unused, char *argv[] __unused)
66 {
67 int cmd = -1;
68
69 if (--argc == 0)
70 help();
71 argv++;
72
73 for (int i = 0; cmds[i].cmd_name != NULL; i++)
74 if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
75 if (cmd != -1)
76 errx(1, "ambiguous command: %s", argv[0]);
77 cmd = i;
78 }
79 if (cmd == -1)
80 errx(1, "unknown command: %s", argv[0]);
81
82 dev = open("/dev/" PFILDEV, O_RDWR);
83 if (dev == -1)
84 err(1, "open(%s)", "/dev/" PFILDEV);
85
86 (*cmds[cmd].cmd_func)(argc, argv);
87
88 return (0);
89 }
90
91 static void
help(void)92 help(void)
93 {
94
95 fprintf(stderr, "usage: %s (heads|hooks|link|unlink)\n", getprogname());
96 exit(0);
97 }
98
99 static void
listheads(int argc __unused,char * argv[]__unused)100 listheads(int argc __unused, char *argv[] __unused)
101 {
102 struct pfilioc_list plh;
103 u_int nheads, nhooks, i;
104 int j, h;
105
106 plh.pio_nheads = 0;
107 plh.pio_nhooks = 0;
108 if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0)
109 err(1, "ioctl(PFILIOC_LISTHEADS)");
110
111 retry:
112 plh.pio_heads = calloc(plh.pio_nheads, sizeof(struct pfilioc_head));
113 if (plh.pio_heads == NULL)
114 err(1, "malloc");
115 plh.pio_hooks = calloc(plh.pio_nhooks, sizeof(struct pfilioc_hook));
116 if (plh.pio_hooks == NULL)
117 err(1, "malloc");
118
119 nheads = plh.pio_nheads;
120 nhooks = plh.pio_nhooks;
121
122 if (ioctl(dev, PFILIOC_LISTHEADS, &plh) != 0)
123 err(1, "ioctl(PFILIOC_LISTHEADS)");
124
125 if (plh.pio_nheads > nheads || plh.pio_nhooks > nhooks) {
126 free(plh.pio_heads);
127 free(plh.pio_hooks);
128 goto retry;
129 }
130
131 #define FMTHD "%16s %8s\n"
132 #define FMTHK "%29s %16s:%s\n"
133 printf("%16s %8s %3s %16s\n", "Intercept point", "Type", "Dir", "Hook");
134 for (i = 0, h = 0; i < plh.pio_nheads; i++) {
135 printf(FMTHD, plh.pio_heads[i].pio_name,
136 typenames[plh.pio_heads[i].pio_type]);
137 for (j = 0; j < plh.pio_heads[i].pio_nhooksin; j++, h++)
138 printf(FMTHK, "In", plh.pio_hooks[h].pio_module,
139 plh.pio_hooks[h].pio_ruleset);
140 for (j = 0; j < plh.pio_heads[i].pio_nhooksout; j++, h++)
141 printf(FMTHK, "Out", plh.pio_hooks[h].pio_module,
142 plh.pio_hooks[h].pio_ruleset);
143 }
144 }
145
146 static void
listhooks(int argc __unused,char * argv[]__unused)147 listhooks(int argc __unused, char *argv[] __unused)
148 {
149 struct pfilioc_list plh;
150 u_int nhooks, i;
151
152 plh.pio_nhooks = 0;
153 if (ioctl(dev, PFILIOC_LISTHOOKS, &plh) != 0)
154 err(1, "ioctl(PFILIOC_LISTHOOKS)");
155 retry:
156 plh.pio_hooks = calloc(plh.pio_nhooks, sizeof(struct pfilioc_hook));
157 if (plh.pio_hooks == NULL)
158 err(1, "malloc");
159
160 nhooks = plh.pio_nhooks;
161
162 if (ioctl(dev, PFILIOC_LISTHOOKS, &plh) != 0)
163 err(1, "ioctl(PFILIOC_LISTHOOKS)");
164
165 if (plh.pio_nhooks > nhooks) {
166 free(plh.pio_hooks);
167 goto retry;
168 }
169
170 printf("%16s %16s %8s\n", "Hook", "", "Type");
171 for (i = 0; i < plh.pio_nhooks; i++) {
172 printf("%16s:%-16s %8s\n", plh.pio_hooks[i].pio_module,
173 plh.pio_hooks[i].pio_ruleset,
174 typenames[plh.pio_hooks[i].pio_type]);
175 }
176 }
177
178 static void
hook(int argc,char * argv[])179 hook(int argc, char *argv[])
180 {
181 struct pfilioc_link req;
182 int c;
183 char *ruleset;
184
185 if (argv[0][0] == 'u')
186 req.pio_flags = PFIL_UNLINK;
187 else
188 req.pio_flags = 0;
189
190 while ((c = getopt(argc, argv, "ioa")) != -1)
191 switch (c) {
192 case 'i':
193 req.pio_flags |= PFIL_IN;
194 break;
195 case 'o':
196 req.pio_flags |= PFIL_OUT;
197 break;
198 case 'a':
199 req.pio_flags |= PFIL_APPEND;
200 break;
201 default:
202 help();
203 }
204
205 if (!PFIL_DIR(req.pio_flags))
206 help();
207
208 argc -= optind;
209 argv += optind;
210
211 if (argc != 2)
212 help();
213
214 /* link mod:ruleset head */
215 if ((ruleset = strchr(argv[0], ':')) == NULL)
216 help();
217 *ruleset = '\0';
218 ruleset++;
219
220 strlcpy(req.pio_name, argv[1], sizeof(req.pio_name));
221 strlcpy(req.pio_module, argv[0], sizeof(req.pio_module));
222 strlcpy(req.pio_ruleset, ruleset, sizeof(req.pio_ruleset));
223
224 if (ioctl(dev, PFILIOC_LINK, &req) != 0)
225 err(1, "ioctl(PFILIOC_LINK)");
226 }
227