1 1.3 maxv /* $NetBSD: npf.c,v 1.3 2019/08/13 09:48:24 maxv Exp $ */ 2 1.1 rmind 3 1.1 rmind /* 4 1.3 maxv * Copyright (c) 2011, 2019 The NetBSD Foundation, Inc. 5 1.1 rmind * All rights reserved. 6 1.1 rmind * 7 1.1 rmind * Redistribution and use in source and binary forms, with or without 8 1.1 rmind * modification, are permitted provided that the following conditions 9 1.1 rmind * are met: 10 1.1 rmind * 1. Redistributions of source code must retain the above copyright 11 1.1 rmind * notice, this list of conditions and the following disclaimer. 12 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 rmind * notice, this list of conditions and the following disclaimer in the 14 1.1 rmind * documentation and/or other materials provided with the distribution. 15 1.1 rmind * 16 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 rmind * POSSIBILITY OF SUCH DAMAGE. 27 1.1 rmind */ 28 1.1 rmind 29 1.1 rmind #include <sys/param.h> 30 1.1 rmind #include <sys/types.h> 31 1.1 rmind #include <sys/queue.h> 32 1.1 rmind 33 1.1 rmind #include <netinet/in.h> 34 1.1 rmind #include <netinet/in_systm.h> 35 1.3 maxv #include <netinet/ip.h> 36 1.3 maxv #include <netinet/tcp.h> 37 1.1 rmind 38 1.1 rmind #include <arpa/inet.h> 39 1.1 rmind #include <net/if.h> 40 1.1 rmind #include <net/pfvar.h> 41 1.3 maxv #include <net/bpf.h> 42 1.3 maxv 43 1.3 maxv #define NPF_BPFCOP 44 1.1 rmind #include <npf.h> 45 1.1 rmind 46 1.1 rmind #include <stdlib.h> 47 1.1 rmind #include <string.h> 48 1.3 maxv #include <stddef.h> 49 1.1 rmind #include <fcntl.h> 50 1.1 rmind #include <errno.h> 51 1.1 rmind #include <err.h> 52 1.1 rmind #include <assert.h> 53 1.1 rmind 54 1.1 rmind #include "filter.h" 55 1.1 rmind 56 1.1 rmind static void npf_init_filter(char *, char *, int); 57 1.1 rmind static int npf_add_filter(uint32_t, uint8_t, struct sockaddr *, 58 1.1 rmind struct sockaddr *, uint16_t); 59 1.1 rmind static int npf_add_nat(uint32_t, struct sockaddr *, struct sockaddr *, 60 1.1 rmind uint16_t, struct sockaddr *, uint16_t, uint16_t); 61 1.1 rmind static int npf_add_rdr(uint32_t, struct sockaddr *, struct sockaddr *, 62 1.1 rmind uint16_t, struct sockaddr *, uint16_t); 63 1.1 rmind static int npf_server_lookup(struct sockaddr *, struct sockaddr *, 64 1.1 rmind struct sockaddr *); 65 1.1 rmind static int npf_prepare_commit(uint32_t); 66 1.1 rmind static int npf_do_commit(void); 67 1.1 rmind static int npf_do_rollback(void); 68 1.1 rmind 69 1.1 rmind const ftp_proxy_ops_t npf_fprx_ops = { 70 1.1 rmind .init_filter = npf_init_filter, 71 1.1 rmind .add_filter = npf_add_filter, 72 1.1 rmind .add_nat = npf_add_nat, 73 1.1 rmind .add_rdr = npf_add_rdr, 74 1.1 rmind .server_lookup = npf_server_lookup, 75 1.1 rmind .prepare_commit = npf_prepare_commit, 76 1.1 rmind .do_commit = npf_do_commit, 77 1.1 rmind .do_rollback = npf_do_rollback 78 1.1 rmind }; 79 1.1 rmind 80 1.1 rmind #define sa_to_32(sa) (((struct sockaddr_in *)sa)->sin_addr.s_addr) 81 1.1 rmind 82 1.1 rmind #define NPF_DEV_PATH "/dev/npf" 83 1.1 rmind 84 1.1 rmind typedef struct fp_ent { 85 1.1 rmind LIST_ENTRY(fp_ent) fpe_list; 86 1.1 rmind uint32_t fpe_id; 87 1.1 rmind nl_rule_t * fpe_rl; 88 1.1 rmind nl_rule_t * fpe_nat; 89 1.1 rmind nl_rule_t * fpe_rdr; 90 1.1 rmind } fp_ent_t; 91 1.1 rmind 92 1.1 rmind char * npfopts; 93 1.1 rmind 94 1.1 rmind static LIST_HEAD(, fp_ent) fp_ent_list; 95 1.1 rmind static struct sockaddr_in fp_server_sa; 96 1.3 maxv static char * fp_ifname; 97 1.1 rmind static int npf_fd; 98 1.1 rmind 99 1.3 maxv #define NPF_BPF_SUCCESS ((u_int)-1) 100 1.3 maxv #define NPF_BPF_FAILURE 0 101 1.3 maxv 102 1.3 maxv #define INSN_IPSRC 5 103 1.3 maxv #define INSN_IPDST 7 104 1.3 maxv #define INSN_DPORT 10 105 1.3 maxv 106 1.3 maxv static struct bpf_insn insns[13] = { 107 1.3 maxv /* Want IPv4. */ 108 1.3 maxv BPF_STMT(BPF_LD+BPF_MEM, BPF_MW_IPVER), 109 1.3 maxv BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 4, 0, 10), 110 1.3 maxv /* Want TCP. */ 111 1.3 maxv BPF_STMT(BPF_LD+BPF_MEM, BPF_MW_L4PROTO), 112 1.3 maxv BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 8), 113 1.3 maxv /* Check source IP. */ 114 1.3 maxv BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct ip, ip_src)), 115 1.3 maxv BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xDEADBEEF, 0, 6), 116 1.3 maxv /* Check destination IP. */ 117 1.3 maxv BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct ip, ip_dst)), 118 1.3 maxv BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xDEADBEEF, 0, 4), 119 1.3 maxv /* Check port. */ 120 1.3 maxv BPF_STMT(BPF_LDX+BPF_MEM, BPF_MW_L4OFF), 121 1.3 maxv BPF_STMT(BPF_LD+BPF_H+BPF_IND, offsetof(struct tcphdr, th_dport)), 122 1.3 maxv BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xDEADBEEF, 0, 1), 123 1.3 maxv /* Success. */ 124 1.3 maxv BPF_STMT(BPF_RET+BPF_K, NPF_BPF_SUCCESS), 125 1.3 maxv /* Failure. */ 126 1.3 maxv BPF_STMT(BPF_RET+BPF_K, NPF_BPF_FAILURE), 127 1.1 rmind }; 128 1.1 rmind 129 1.1 rmind static void 130 1.3 maxv modify_bytecode(in_addr_t shost, in_addr_t dhost, in_port_t dport) 131 1.1 rmind { 132 1.3 maxv /* 133 1.3 maxv * Replace the 0xDEADBEEF by actual values. Note that BPF is in 134 1.3 maxv * host order. 135 1.3 maxv */ 136 1.3 maxv 137 1.1 rmind /* Source address to match. */ 138 1.3 maxv insns[INSN_IPSRC].k = shost; 139 1.1 rmind /* Destination address to match. */ 140 1.3 maxv insns[INSN_IPDST].k = dhost; 141 1.1 rmind /* Destination port to match. */ 142 1.3 maxv insns[INSN_DPORT].k = dport; 143 1.1 rmind } 144 1.1 rmind 145 1.1 rmind static fp_ent_t * 146 1.3 maxv proxy_lookup(uint32_t id) 147 1.1 rmind { 148 1.1 rmind fp_ent_t *fpe; 149 1.1 rmind 150 1.1 rmind LIST_FOREACH(fpe, &fp_ent_list, fpe_list) { 151 1.1 rmind if (fpe->fpe_id == id) 152 1.1 rmind break; 153 1.1 rmind } 154 1.3 maxv 155 1.1 rmind return fpe; 156 1.1 rmind } 157 1.1 rmind 158 1.1 rmind static void 159 1.1 rmind npf_init_filter(char *opt_qname, char *opt_tagname, int opt_verbose) 160 1.1 rmind { 161 1.1 rmind char *netif = npfopts, *saddr, *port; 162 1.3 maxv int kernver, idx; 163 1.1 rmind 164 1.1 rmind /* XXX get rid of this */ 165 1.3 maxv if ((saddr = strchr(netif, ':')) == NULL) { 166 1.1 rmind errx(EXIT_FAILURE, "invalid -N option string: %s", npfopts); 167 1.1 rmind } 168 1.1 rmind *saddr++ = '\0'; 169 1.3 maxv if ((port = strchr(saddr, ':')) == NULL) { 170 1.1 rmind errx(EXIT_FAILURE, "invalid -N option string: %s", npfopts); 171 1.1 rmind } 172 1.1 rmind *port++ = '\0'; 173 1.1 rmind 174 1.3 maxv fp_ifname = netif; 175 1.3 maxv idx = if_nametoindex(fp_ifname); 176 1.3 maxv if (idx == 0) { 177 1.3 maxv errx(EXIT_FAILURE, "invalid network interface '%s'", fp_ifname); 178 1.1 rmind } 179 1.1 rmind 180 1.1 rmind memset(&fp_server_sa, 0, sizeof(struct sockaddr_in)); 181 1.1 rmind fp_server_sa.sin_len = sizeof(struct sockaddr_in); 182 1.1 rmind fp_server_sa.sin_family = AF_INET; 183 1.1 rmind fp_server_sa.sin_addr.s_addr = inet_addr(saddr); 184 1.1 rmind fp_server_sa.sin_port = htons(atoi(port)); 185 1.1 rmind 186 1.1 rmind npf_fd = open(NPF_DEV_PATH, O_RDONLY); 187 1.1 rmind if (npf_fd == -1) { 188 1.1 rmind err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH); 189 1.1 rmind } 190 1.3 maxv if (ioctl(npf_fd, IOC_NPF_VERSION, &kernver) == -1) { 191 1.3 maxv err(EXIT_FAILURE, "ioctl failed on IOC_NPF_VERSION"); 192 1.3 maxv } 193 1.3 maxv if (kernver != NPF_VERSION) { 194 1.3 maxv errx(EXIT_FAILURE, 195 1.3 maxv "incompatible NPF interface version (%d, kernel %d)\n" 196 1.3 maxv "Hint: update %s?", NPF_VERSION, kernver, 197 1.3 maxv kernver > NPF_VERSION ? "userland" : "kernel"); 198 1.3 maxv } 199 1.3 maxv 200 1.1 rmind LIST_INIT(&fp_ent_list); 201 1.1 rmind } 202 1.1 rmind 203 1.1 rmind static int 204 1.1 rmind npf_prepare_commit(uint32_t id) 205 1.1 rmind { 206 1.1 rmind fp_ent_t *fpe; 207 1.1 rmind 208 1.1 rmind /* Check if already exists. */ 209 1.3 maxv fpe = proxy_lookup(id); 210 1.1 rmind if (fpe) { 211 1.1 rmind /* Destroy existing rules and reset the values. */ 212 1.1 rmind npf_rule_destroy(fpe->fpe_rl); 213 1.1 rmind npf_rule_destroy(fpe->fpe_nat); 214 1.1 rmind npf_rule_destroy(fpe->fpe_rdr); 215 1.1 rmind goto reset; 216 1.1 rmind } 217 1.3 maxv 218 1.1 rmind /* Create a new one, if not found. */ 219 1.1 rmind fpe = malloc(sizeof(fp_ent_t)); 220 1.3 maxv if (fpe == NULL) 221 1.1 rmind return -1; 222 1.1 rmind LIST_INSERT_HEAD(&fp_ent_list, fpe, fpe_list); 223 1.1 rmind fpe->fpe_id = id; 224 1.3 maxv 225 1.1 rmind reset: 226 1.1 rmind fpe->fpe_rl = NULL; 227 1.1 rmind fpe->fpe_nat = NULL; 228 1.1 rmind fpe->fpe_rdr = NULL; 229 1.1 rmind return 0; 230 1.1 rmind } 231 1.1 rmind 232 1.1 rmind static int 233 1.1 rmind npf_add_filter(uint32_t id, uint8_t pf_dir, struct sockaddr *src, 234 1.1 rmind struct sockaddr *dst, uint16_t dport) 235 1.1 rmind { 236 1.1 rmind fp_ent_t *fpe; 237 1.1 rmind nl_rule_t *rl; 238 1.1 rmind int di; 239 1.1 rmind 240 1.1 rmind if (!src || !dst || !dport) { 241 1.1 rmind errno = EINVAL; 242 1.1 rmind return -1; 243 1.1 rmind } 244 1.3 maxv fpe = proxy_lookup(id); 245 1.1 rmind assert(fpe != NULL); 246 1.1 rmind 247 1.1 rmind di = (pf_dir == PF_OUT) ? NPF_RULE_OUT : NPF_RULE_IN; 248 1.3 maxv rl = npf_rule_create(NULL, di | NPF_RULE_PASS | NPF_RULE_FINAL, NULL); 249 1.1 rmind if (rl == NULL) { 250 1.1 rmind errno = ENOMEM; 251 1.1 rmind return -1; 252 1.1 rmind } 253 1.3 maxv 254 1.3 maxv modify_bytecode(sa_to_32(src), sa_to_32(dst), dport); 255 1.3 maxv errno = npf_rule_setcode(rl, NPF_CODE_BPF, insns, sizeof(insns)); 256 1.1 rmind if (errno) { 257 1.1 rmind npf_rule_destroy(rl); 258 1.1 rmind return -1; 259 1.1 rmind } 260 1.3 maxv 261 1.1 rmind assert(fpe->fpe_rl == NULL); 262 1.1 rmind fpe->fpe_rl = rl; 263 1.1 rmind return 0; 264 1.1 rmind } 265 1.1 rmind 266 1.3 maxv /* 267 1.3 maxv * Note: we don't use plow and phigh. In NPF they are not per-rule, but global, 268 1.3 maxv * under the "portmap.min_port" and "portmap.max_port" params. 269 1.3 maxv */ 270 1.1 rmind static int 271 1.1 rmind npf_add_nat(uint32_t id, struct sockaddr *src, struct sockaddr *dst, 272 1.1 rmind uint16_t dport, struct sockaddr *snat, uint16_t plow, uint16_t phigh) 273 1.1 rmind { 274 1.3 maxv npf_addr_t addr; 275 1.1 rmind fp_ent_t *fpe; 276 1.1 rmind nl_nat_t *nt; 277 1.1 rmind 278 1.1 rmind if (!src || !dst || !dport || !snat || !plow || 279 1.1 rmind (src->sa_family != snat->sa_family)) { 280 1.1 rmind errno = EINVAL; 281 1.3 maxv return -1; 282 1.1 rmind } 283 1.3 maxv fpe = proxy_lookup(id); 284 1.1 rmind assert(fpe != NULL); 285 1.1 rmind 286 1.3 maxv memset(&addr, 0, sizeof(npf_addr_t)); 287 1.1 rmind memcpy(&addr, &sa_to_32(snat), sizeof(struct in_addr)); 288 1.3 maxv 289 1.3 maxv nt = npf_nat_create(NPF_NATOUT, NPF_NAT_PORTS | NPF_NAT_PORTMAP, NULL); 290 1.1 rmind if (nt == NULL) { 291 1.1 rmind errno = ENOMEM; 292 1.1 rmind return -1; 293 1.1 rmind } 294 1.3 maxv errno = npf_nat_setaddr(nt, AF_INET, &addr, 0); 295 1.3 maxv if (errno) { 296 1.3 maxv goto err; 297 1.3 maxv } 298 1.3 maxv 299 1.3 maxv modify_bytecode(sa_to_32(src), sa_to_32(dst), dport); 300 1.3 maxv errno = npf_rule_setcode(nt, NPF_CODE_BPF, insns, sizeof(insns)); 301 1.1 rmind if (errno) { 302 1.3 maxv goto err; 303 1.1 rmind } 304 1.3 maxv 305 1.1 rmind assert(fpe->fpe_nat == NULL); 306 1.1 rmind fpe->fpe_nat = nt; 307 1.1 rmind return 0; 308 1.3 maxv 309 1.3 maxv err: 310 1.3 maxv npf_rule_destroy(nt); 311 1.3 maxv return -1; 312 1.1 rmind } 313 1.1 rmind 314 1.1 rmind static int 315 1.1 rmind npf_add_rdr(uint32_t id, struct sockaddr *src, struct sockaddr *dst, 316 1.1 rmind uint16_t dport, struct sockaddr *rdr, uint16_t rdr_port) 317 1.1 rmind { 318 1.3 maxv npf_addr_t addr; 319 1.1 rmind fp_ent_t *fpe; 320 1.1 rmind nl_nat_t *nt; 321 1.1 rmind 322 1.1 rmind if (!src || !dst || !dport || !rdr || !rdr_port || 323 1.1 rmind (src->sa_family != rdr->sa_family)) { 324 1.1 rmind errno = EINVAL; 325 1.1 rmind return -1; 326 1.1 rmind } 327 1.3 maxv fpe = proxy_lookup(id); 328 1.1 rmind assert(fpe != NULL); 329 1.1 rmind 330 1.3 maxv memset(&addr, 0, sizeof(npf_addr_t)); 331 1.1 rmind memcpy(&addr, &sa_to_32(rdr), sizeof(struct in_addr)); 332 1.3 maxv 333 1.3 maxv nt = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS, NULL); 334 1.1 rmind if (nt == NULL) { 335 1.1 rmind errno = ENOMEM; 336 1.1 rmind return -1; 337 1.1 rmind } 338 1.3 maxv errno = npf_nat_setaddr(nt, AF_INET, &addr, 0); 339 1.1 rmind if (errno) { 340 1.3 maxv goto err; 341 1.3 maxv } 342 1.3 maxv errno = npf_nat_setport(nt, htons(rdr_port)); 343 1.3 maxv if (errno) { 344 1.3 maxv goto err; 345 1.3 maxv } 346 1.3 maxv 347 1.3 maxv modify_bytecode(sa_to_32(src), sa_to_32(dst), dport); 348 1.3 maxv errno = npf_rule_setcode(nt, NPF_CODE_BPF, insns, sizeof(insns)); 349 1.3 maxv if (errno) { 350 1.3 maxv goto err; 351 1.1 rmind } 352 1.3 maxv 353 1.1 rmind assert(fpe->fpe_rdr == NULL); 354 1.1 rmind fpe->fpe_rdr = nt; 355 1.1 rmind return 0; 356 1.3 maxv 357 1.3 maxv err: 358 1.3 maxv npf_rule_destroy(nt); 359 1.3 maxv return -1; 360 1.1 rmind } 361 1.1 rmind 362 1.1 rmind static int 363 1.3 maxv npf_server_lookup(struct sockaddr *client, struct sockaddr *proxy, 364 1.1 rmind struct sockaddr *server) 365 1.1 rmind { 366 1.1 rmind 367 1.1 rmind memcpy(server, &fp_server_sa, sizeof(struct sockaddr_in)); 368 1.1 rmind return 0; 369 1.1 rmind } 370 1.1 rmind 371 1.1 rmind static int 372 1.1 rmind npf_do_commit(void) 373 1.1 rmind { 374 1.3 maxv nl_config_t *ncf; 375 1.1 rmind nl_rule_t *group; 376 1.1 rmind fp_ent_t *fpe; 377 1.1 rmind pri_t pri; 378 1.1 rmind 379 1.3 maxv ncf = npf_config_create(); 380 1.3 maxv if (ncf == NULL) { 381 1.3 maxv errno = ENOMEM; 382 1.3 maxv return -1; 383 1.3 maxv } 384 1.3 maxv 385 1.3 maxv group = npf_rule_create(NULL, NPF_RULE_GROUP | NPF_RULE_PASS | 386 1.3 maxv NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_FINAL, fp_ifname); 387 1.1 rmind if (group == NULL) { 388 1.3 maxv errno = ENOMEM; 389 1.3 maxv goto err; 390 1.1 rmind } 391 1.3 maxv 392 1.1 rmind pri = 1; 393 1.1 rmind LIST_FOREACH(fpe, &fp_ent_list, fpe_list) { 394 1.3 maxv if (fpe->fpe_rl == NULL) { 395 1.3 maxv /* Empty. */ 396 1.3 maxv continue; 397 1.3 maxv } 398 1.3 maxv npf_rule_setprio(fpe->fpe_rl, pri++); 399 1.3 maxv npf_rule_insert(NULL, group, fpe->fpe_rl); 400 1.3 maxv /* 401 1.3 maxv * XXX: Mmh, aren't we supposed to insert fpe_nat and fpe_rdr 402 1.3 maxv * too here? 403 1.3 maxv */ 404 1.3 maxv } 405 1.3 maxv npf_rule_insert(ncf, NULL, group); 406 1.3 maxv 407 1.3 maxv errno = npf_config_submit(ncf, npf_fd, NULL); 408 1.3 maxv if (errno != 0) 409 1.3 maxv goto err; 410 1.3 maxv 411 1.3 maxv npf_config_destroy(ncf); 412 1.1 rmind return 0; 413 1.3 maxv 414 1.3 maxv err: 415 1.3 maxv if (ncf != NULL) 416 1.3 maxv npf_config_destroy(ncf); 417 1.2 rmind return -1; 418 1.1 rmind } 419 1.1 rmind 420 1.1 rmind static int 421 1.1 rmind npf_do_rollback(void) 422 1.1 rmind { 423 1.1 rmind /* None. */ 424 1.1 rmind return 0; 425 1.1 rmind } 426