1 /* $NetBSD: npf.c,v 1.1 2011/02/02 02:20:26 rmind 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 51 static void npf_init_filter(char *, char *, int); 52 static int npf_add_filter(uint32_t, uint8_t, struct sockaddr *, 53 struct sockaddr *, uint16_t); 54 static int npf_add_nat(uint32_t, struct sockaddr *, struct sockaddr *, 55 uint16_t, struct sockaddr *, uint16_t, uint16_t); 56 static int npf_add_rdr(uint32_t, struct sockaddr *, struct sockaddr *, 57 uint16_t, struct sockaddr *, uint16_t); 58 static int npf_server_lookup(struct sockaddr *, struct sockaddr *, 59 struct sockaddr *); 60 static int npf_prepare_commit(uint32_t); 61 static int npf_do_commit(void); 62 static int npf_do_rollback(void); 63 64 const 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 80 typedef 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 88 char * npfopts; 89 90 static LIST_HEAD(, fp_ent) fp_ent_list; 91 static fp_ent_t * fp_ent_hint; 92 static struct sockaddr_in fp_server_sa; 93 static u_int fp_if_idx; 94 static int npf_fd; 95 96 static 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 109 static void 110 ftp_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 120 static fp_ent_t * 121 ftp_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 136 static void 137 npf_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 174 static int 175 npf_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; 195 reset: 196 fpe->fpe_rl = NULL; 197 fpe->fpe_nat = NULL; 198 fpe->fpe_rdr = NULL; 199 return 0; 200 } 201 202 static int 203 npf_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 234 static int 235 npf_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 268 static int 269 npf_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 302 static int 303 npf_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 311 static int 312 npf_do_commit(void) 313 { 314 nl_rule_t *group; 315 fp_ent_t *fpe; 316 pri_t pri; 317 318 group = npf_rule_create(NPF_FP_RULE_TAG, NPF_RULE_PASS | NPF_RULE_IN | 319 NPF_RULE_OUT | NPF_RULE_FINAL, fp_if_idx); 320 if (group == NULL) { 321 return -1; 322 } 323 pri = 1; 324 LIST_FOREACH(fpe, &fp_ent_list, fpe_list) { 325 npf_rule_insert(NULL, group, fpe->fpe_rl, pri++); 326 } 327 npf_update_rule(npf_fd, NPF_FP_RULE_TAG, group); 328 npf_rule_destroy(group); 329 return 0; 330 } 331 332 static int 333 npf_do_rollback(void) 334 { 335 /* None. */ 336 return 0; 337 } 338