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