1/* $NetBSD: npf.c,v 1.1.8.1 2013/01/07 16:51:07 riz Exp $ */ 2 3/* 4 * Copyright (c) 2011 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 36#include <arpa/inet.h> 37#include <net/if.h> 38#include <net/pfvar.h> 39#include <net/npf_ncode.h> 40#include <npf.h> 41 42#include <stdlib.h> 43#include <string.h> 44#include <fcntl.h> 45#include <errno.h> 46#include <err.h> 47#include <assert.h> 48 49#include "filter.h" 50 51static void npf_init_filter(char *, char *, int); 52static int npf_add_filter(uint32_t, uint8_t, struct sockaddr *, 53 struct sockaddr *, uint16_t); 54static int npf_add_nat(uint32_t, struct sockaddr *, struct sockaddr *, 55 uint16_t, struct sockaddr *, uint16_t, uint16_t); 56static int npf_add_rdr(uint32_t, struct sockaddr *, struct sockaddr *, 57 uint16_t, struct sockaddr *, uint16_t); 58static int npf_server_lookup(struct sockaddr *, struct sockaddr *, 59 struct sockaddr *); 60static int npf_prepare_commit(uint32_t); 61static int npf_do_commit(void); 62static int npf_do_rollback(void); 63 64const ftp_proxy_ops_t npf_fprx_ops = { 65 .init_filter = npf_init_filter, 66 .add_filter = npf_add_filter, 67 .add_nat = npf_add_nat, 68 .add_rdr = npf_add_rdr, 69 .server_lookup = npf_server_lookup, 70 .prepare_commit = npf_prepare_commit, 71 .do_commit = npf_do_commit, 72 .do_rollback = npf_do_rollback 73}; 74 75#define sa_to_32(sa) (((struct sockaddr_in *)sa)->sin_addr.s_addr) 76 77#define NPF_DEV_PATH "/dev/npf" 78#define NPF_FP_RULE_TAG "ftp-proxy" 79 80typedef struct fp_ent { 81 LIST_ENTRY(fp_ent) fpe_list; 82 uint32_t fpe_id; 83 nl_rule_t * fpe_rl; 84 nl_rule_t * fpe_nat; 85 nl_rule_t * fpe_rdr; 86} fp_ent_t; 87 88char * npfopts; 89 90static LIST_HEAD(, fp_ent) fp_ent_list; 91static fp_ent_t * fp_ent_hint; 92static struct sockaddr_in fp_server_sa; 93static u_int fp_if_idx; 94static int npf_fd; 95 96static uint32_t ncode[ ] = { 97 /* from <shost> to <dhost> port <dport> */ 98 NPF_OPCODE_IP4MASK, 0x01, 0xdeadbeef, 0xffffffff, 99 NPF_OPCODE_BNE, 15, 100 NPF_OPCODE_IP4MASK, 0x00, 0xdeadbeef, 0xffffffff, 101 NPF_OPCODE_BNE, 9, 102 NPF_OPCODE_TCP_PORTS, 0x00, 0xdeadbeef, 103 NPF_OPCODE_BNE, 4, 104 /* Success (0x0) and failure (0xff) paths. */ 105 NPF_OPCODE_RET, 0x00, 106 NPF_OPCODE_RET, 0xff 107}; 108 109static void 110ftp_proxy_modify_nc(in_addr_t shost, in_addr_t dhost, in_port_t dport) 111{ 112 /* Source address to match. */ 113 ncode[2] = shost; 114 /* Destination address to match. */ 115 ncode[8] = shost; 116 /* Destination port to match. */ 117 ncode[14] = ((uint32_t)dport << 16) | dport; 118} 119 120static fp_ent_t * 121ftp_proxy_lookup(uint32_t id) 122{ 123 fp_ent_t *fpe; 124 125 /* Look for FTP proxy entry. First, try hint (last used). */ 126 if (fp_ent_hint && fp_ent_hint->fpe_id == id) { 127 return fp_ent_hint; 128 } 129 LIST_FOREACH(fpe, &fp_ent_list, fpe_list) { 130 if (fpe->fpe_id == id) 131 break; 132 } 133 return fpe; 134} 135 136static void 137npf_init_filter(char *opt_qname, char *opt_tagname, int opt_verbose) 138{ 139 char *netif = npfopts, *saddr, *port; 140 141 /* XXX get rid of this */ 142 saddr = strchr(netif, ':'); 143 if (saddr == NULL) { 144 errx(EXIT_FAILURE, "invalid -N option string: %s", npfopts); 145 } 146 *saddr++ = '\0'; 147 if (saddr == NULL || (port = strchr(saddr, ':')) == NULL) { 148 errx(EXIT_FAILURE, "invalid -N option string: %s", npfopts); 149 } 150 *port++ = '\0'; 151 if (port == NULL) { 152 errx(EXIT_FAILURE, "invalid -N option string: %s", npfopts); 153 } 154 155 fp_if_idx = if_nametoindex(netif); 156 if (fp_if_idx == 0) { 157 errx(EXIT_FAILURE, "invalid network interface '%s'", netif); 158 } 159 160 memset(&fp_server_sa, 0, sizeof(struct sockaddr_in)); 161 fp_server_sa.sin_len = sizeof(struct sockaddr_in); 162 fp_server_sa.sin_family = AF_INET; 163 fp_server_sa.sin_addr.s_addr = inet_addr(saddr); 164 fp_server_sa.sin_port = htons(atoi(port)); 165 166 npf_fd = open(NPF_DEV_PATH, O_RDONLY); 167 if (npf_fd == -1) { 168 err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH); 169 } 170 LIST_INIT(&fp_ent_list); 171 fp_ent_hint = NULL; 172} 173 174static int 175npf_prepare_commit(uint32_t id) 176{ 177 fp_ent_t *fpe; 178 179 /* Check if already exists. */ 180 fpe = ftp_proxy_lookup(id); 181 if (fpe) { 182 /* Destroy existing rules and reset the values. */ 183 npf_rule_destroy(fpe->fpe_rl); 184 npf_rule_destroy(fpe->fpe_nat); 185 npf_rule_destroy(fpe->fpe_rdr); 186 goto reset; 187 } 188 /* Create a new one, if not found. */ 189 fpe = malloc(sizeof(fp_ent_t)); 190 if (fpe == NULL) { 191 return -1; 192 } 193 LIST_INSERT_HEAD(&fp_ent_list, fpe, fpe_list); 194 fpe->fpe_id = id; 195reset: 196 fpe->fpe_rl = NULL; 197 fpe->fpe_nat = NULL; 198 fpe->fpe_rdr = NULL; 199 return 0; 200} 201 202static int 203npf_add_filter(uint32_t id, uint8_t pf_dir, struct sockaddr *src, 204 struct sockaddr *dst, uint16_t dport) 205{ 206 fp_ent_t *fpe; 207 nl_rule_t *rl; 208 int di; 209 210 if (!src || !dst || !dport) { 211 errno = EINVAL; 212 return -1; 213 } 214 fpe = ftp_proxy_lookup(id); 215 assert(fpe != NULL); 216 217 di = (pf_dir == PF_OUT) ? NPF_RULE_OUT : NPF_RULE_IN; 218 rl = npf_rule_create(NULL, di | NPF_RULE_PASS | NPF_RULE_FINAL, 0); 219 if (rl == NULL) { 220 errno = ENOMEM; 221 return -1; 222 } 223 ftp_proxy_modify_nc(sa_to_32(src), sa_to_32(dst), htons(dport)); 224 errno = npf_rule_setcode(rl, NPF_CODE_NCODE, ncode, sizeof(ncode)); 225 if (errno) { 226 npf_rule_destroy(rl); 227 return -1; 228 } 229 assert(fpe->fpe_rl == NULL); 230 fpe->fpe_rl = rl; 231 return 0; 232} 233 234static int 235npf_add_nat(uint32_t id, struct sockaddr *src, struct sockaddr *dst, 236 uint16_t dport, struct sockaddr *snat, uint16_t plow, uint16_t phigh) 237{ 238 fp_ent_t *fpe; 239 nl_nat_t *nt; 240 npf_addr_t addr; 241 242 if (!src || !dst || !dport || !snat || !plow || 243 (src->sa_family != snat->sa_family)) { 244 errno = EINVAL; 245 return (-1); 246 } 247 fpe = ftp_proxy_lookup(id); 248 assert(fpe != NULL); 249 250 memcpy(&addr, &sa_to_32(snat), sizeof(struct in_addr)); 251 nt = npf_nat_create(NPF_NATOUT, NPF_NAT_PORTS | NPF_NAT_PORTMAP, 0, 252 &addr, AF_INET, htons(plow)); 253 if (nt == NULL) { 254 errno = ENOMEM; 255 return -1; 256 } 257 ftp_proxy_modify_nc(sa_to_32(src), sa_to_32(dst), htons(dport)); 258 errno = npf_rule_setcode(nt, NPF_CODE_NCODE, ncode, sizeof(ncode)); 259 if (errno) { 260 npf_rule_destroy(nt); 261 return -1; 262 } 263 assert(fpe->fpe_nat == NULL); 264 fpe->fpe_nat = nt; 265 return 0; 266} 267 268static int 269npf_add_rdr(uint32_t id, struct sockaddr *src, struct sockaddr *dst, 270 uint16_t dport, struct sockaddr *rdr, uint16_t rdr_port) 271{ 272 fp_ent_t *fpe; 273 nl_nat_t *nt; 274 npf_addr_t addr; 275 276 if (!src || !dst || !dport || !rdr || !rdr_port || 277 (src->sa_family != rdr->sa_family)) { 278 errno = EINVAL; 279 return -1; 280 } 281 fpe = ftp_proxy_lookup(id); 282 assert(fpe != NULL); 283 284 memcpy(&addr, &sa_to_32(rdr), sizeof(struct in_addr)); 285 nt = npf_nat_create(NPF_NATIN, NPF_NAT_PORTS, 0, 286 &addr, AF_INET, htons(rdr_port)); 287 if (nt == NULL) { 288 errno = ENOMEM; 289 return -1; 290 } 291 ftp_proxy_modify_nc(sa_to_32(src), sa_to_32(dst), htons(dport)); 292 errno = npf_rule_setcode(nt, NPF_CODE_NCODE, ncode, sizeof(ncode)); 293 if (errno) { 294 npf_rule_destroy(nt); 295 return -1; 296 } 297 assert(fpe->fpe_rdr == NULL); 298 fpe->fpe_rdr = nt; 299 return 0; 300} 301 302static int 303npf_server_lookup(struct sockaddr *c, struct sockaddr *proxy, 304 struct sockaddr *server) 305{ 306 307 memcpy(server, &fp_server_sa, sizeof(struct sockaddr_in)); 308 return 0; 309} 310 311static int 312npf_do_commit(void) 313{ 314#if 0 315 nl_rule_t *group; 316 fp_ent_t *fpe; 317 pri_t pri; 318 319 group = npf_rule_create(NPF_FP_RULE_TAG, NPF_RULE_PASS | NPF_RULE_IN | 320 NPF_RULE_OUT | NPF_RULE_FINAL, fp_if_idx); 321 if (group == NULL) { 322 return -1; 323 } 324 pri = 1; 325 LIST_FOREACH(fpe, &fp_ent_list, fpe_list) { 326 npf_rule_insert(NULL, group, fpe->fpe_rl, pri++); 327 } 328 npf_update_rule(npf_fd, NPF_FP_RULE_TAG, group); 329 npf_rule_destroy(group); 330 return 0; 331#else 332 errno = ENOTSUP; 333 return -1; 334#endif 335} 336 337static int 338npf_do_rollback(void) 339{ 340 /* None. */ 341 return 0; 342} 343