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