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