filter.c revision 171169
1171169Smlaier/* $OpenBSD: filter.c,v 1.5 2006/12/01 07:31:21 camield Exp $ */ 2171169Smlaier 3171169Smlaier/* 4171169Smlaier * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> 5171169Smlaier * 6171169Smlaier * Permission to use, copy, modify, and distribute this software for any 7171169Smlaier * purpose with or without fee is hereby granted, provided that the above 8171169Smlaier * copyright notice and this permission notice appear in all copies. 9171169Smlaier * 10171169Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11171169Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12171169Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13171169Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14171169Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15171169Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16171169Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17171169Smlaier */ 18171169Smlaier 19171169Smlaier#include <sys/ioctl.h> 20171169Smlaier#include <sys/types.h> 21171169Smlaier#include <sys/socket.h> 22171169Smlaier 23171169Smlaier#include <net/if.h> 24171169Smlaier#include <net/pfvar.h> 25171169Smlaier#include <netinet/in.h> 26171169Smlaier#include <netinet/tcp.h> 27171169Smlaier#include <arpa/inet.h> 28171169Smlaier 29171169Smlaier#include <err.h> 30171169Smlaier#include <errno.h> 31171169Smlaier#include <fcntl.h> 32171169Smlaier#include <stdio.h> 33171169Smlaier#include <string.h> 34171169Smlaier#include <unistd.h> 35171169Smlaier 36171169Smlaier#include "filter.h" 37171169Smlaier 38171169Smlaier/* From netinet/in.h, but only _KERNEL_ gets them. */ 39171169Smlaier#define satosin(sa) ((struct sockaddr_in *)(sa)) 40171169Smlaier#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 41171169Smlaier 42171169Smlaierenum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 43171169Smlaier 44171169Smlaierint prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, 45171169Smlaier u_int16_t); 46171169Smlaierint server_lookup4(struct sockaddr_in *, struct sockaddr_in *, 47171169Smlaier struct sockaddr_in *); 48171169Smlaierint server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, 49171169Smlaier struct sockaddr_in6 *); 50171169Smlaier 51171169Smlaierstatic struct pfioc_pooladdr pfp; 52171169Smlaierstatic struct pfioc_rule pfr; 53171169Smlaierstatic struct pfioc_trans pft; 54171169Smlaierstatic struct pfioc_trans_e pfte[TRANS_SIZE]; 55171169Smlaierstatic int dev, rule_log; 56171169Smlaierstatic char *qname; 57171169Smlaier 58171169Smlaierint 59171169Smlaieradd_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 60171169Smlaier struct sockaddr *dst, u_int16_t d_port) 61171169Smlaier{ 62171169Smlaier if (!src || !dst || !d_port) { 63171169Smlaier errno = EINVAL; 64171169Smlaier return (-1); 65171169Smlaier } 66171169Smlaier 67171169Smlaier if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) 68171169Smlaier return (-1); 69171169Smlaier 70171169Smlaier pfr.rule.direction = dir; 71171169Smlaier if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 72171169Smlaier return (-1); 73171169Smlaier 74171169Smlaier return (0); 75171169Smlaier} 76171169Smlaier 77171169Smlaierint 78171169Smlaieradd_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 79171169Smlaier u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 80171169Smlaier u_int16_t nat_range_high) 81171169Smlaier{ 82171169Smlaier if (!src || !dst || !d_port || !nat || !nat_range_low || 83171169Smlaier (src->sa_family != nat->sa_family)) { 84171169Smlaier errno = EINVAL; 85171169Smlaier return (-1); 86171169Smlaier } 87171169Smlaier 88171169Smlaier if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 89171169Smlaier return (-1); 90171169Smlaier 91171169Smlaier if (nat->sa_family == AF_INET) { 92171169Smlaier memcpy(&pfp.addr.addr.v.a.addr.v4, 93171169Smlaier &satosin(nat)->sin_addr.s_addr, 4); 94171169Smlaier memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 95171169Smlaier } else { 96171169Smlaier memcpy(&pfp.addr.addr.v.a.addr.v6, 97171169Smlaier &satosin6(nat)->sin6_addr.s6_addr, 16); 98171169Smlaier memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 99171169Smlaier } 100171169Smlaier if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 101171169Smlaier return (-1); 102171169Smlaier 103171169Smlaier pfr.rule.rpool.proxy_port[0] = nat_range_low; 104171169Smlaier pfr.rule.rpool.proxy_port[1] = nat_range_high; 105171169Smlaier if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 106171169Smlaier return (-1); 107171169Smlaier 108171169Smlaier return (0); 109171169Smlaier} 110171169Smlaier 111171169Smlaierint 112171169Smlaieradd_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 113171169Smlaier u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 114171169Smlaier{ 115171169Smlaier if (!src || !dst || !d_port || !rdr || !rdr_port || 116171169Smlaier (src->sa_family != rdr->sa_family)) { 117171169Smlaier errno = EINVAL; 118171169Smlaier return (-1); 119171169Smlaier } 120171169Smlaier 121171169Smlaier if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) 122171169Smlaier return (-1); 123171169Smlaier 124171169Smlaier if (rdr->sa_family == AF_INET) { 125171169Smlaier memcpy(&pfp.addr.addr.v.a.addr.v4, 126171169Smlaier &satosin(rdr)->sin_addr.s_addr, 4); 127171169Smlaier memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 128171169Smlaier } else { 129171169Smlaier memcpy(&pfp.addr.addr.v.a.addr.v6, 130171169Smlaier &satosin6(rdr)->sin6_addr.s6_addr, 16); 131171169Smlaier memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 132171169Smlaier } 133171169Smlaier if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 134171169Smlaier return (-1); 135171169Smlaier 136171169Smlaier pfr.rule.rpool.proxy_port[0] = rdr_port; 137171169Smlaier if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 138171169Smlaier return (-1); 139171169Smlaier 140171169Smlaier return (0); 141171169Smlaier} 142171169Smlaier 143171169Smlaierint 144171169Smlaierdo_commit(void) 145171169Smlaier{ 146171169Smlaier if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 147171169Smlaier return (-1); 148171169Smlaier 149171169Smlaier return (0); 150171169Smlaier} 151171169Smlaier 152171169Smlaierint 153171169Smlaierdo_rollback(void) 154171169Smlaier{ 155171169Smlaier if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 156171169Smlaier return (-1); 157171169Smlaier 158171169Smlaier return (0); 159171169Smlaier} 160171169Smlaier 161171169Smlaiervoid 162171169Smlaierinit_filter(char *opt_qname, int opt_verbose) 163171169Smlaier{ 164171169Smlaier struct pf_status status; 165171169Smlaier 166171169Smlaier qname = opt_qname; 167171169Smlaier 168171169Smlaier if (opt_verbose == 1) 169171169Smlaier rule_log = PF_LOG; 170171169Smlaier else if (opt_verbose == 2) 171171169Smlaier rule_log = PF_LOG_ALL; 172171169Smlaier 173171169Smlaier dev = open("/dev/pf", O_RDWR); 174171169Smlaier if (dev == -1) 175171169Smlaier err(1, "/dev/pf"); 176171169Smlaier if (ioctl(dev, DIOCGETSTATUS, &status) == -1) 177171169Smlaier err(1, "DIOCGETSTATUS"); 178171169Smlaier if (!status.running) 179171169Smlaier errx(1, "pf is disabled"); 180171169Smlaier} 181171169Smlaier 182171169Smlaierint 183171169Smlaierprepare_commit(u_int32_t id) 184171169Smlaier{ 185171169Smlaier char an[PF_ANCHOR_NAME_SIZE]; 186171169Smlaier int i; 187171169Smlaier 188171169Smlaier memset(&pft, 0, sizeof pft); 189171169Smlaier pft.size = TRANS_SIZE; 190171169Smlaier pft.esize = sizeof pfte[0]; 191171169Smlaier pft.array = pfte; 192171169Smlaier 193171169Smlaier snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 194171169Smlaier getpid(), id); 195171169Smlaier for (i = 0; i < TRANS_SIZE; i++) { 196171169Smlaier memset(&pfte[i], 0, sizeof pfte[0]); 197171169Smlaier strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 198171169Smlaier switch (i) { 199171169Smlaier case TRANS_FILTER: 200171169Smlaier pfte[i].rs_num = PF_RULESET_FILTER; 201171169Smlaier break; 202171169Smlaier case TRANS_NAT: 203171169Smlaier pfte[i].rs_num = PF_RULESET_NAT; 204171169Smlaier break; 205171169Smlaier case TRANS_RDR: 206171169Smlaier pfte[i].rs_num = PF_RULESET_RDR; 207171169Smlaier break; 208171169Smlaier default: 209171169Smlaier errno = EINVAL; 210171169Smlaier return (-1); 211171169Smlaier } 212171169Smlaier } 213171169Smlaier 214171169Smlaier if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 215171169Smlaier return (-1); 216171169Smlaier 217171169Smlaier return (0); 218171169Smlaier} 219171169Smlaier 220171169Smlaierint 221171169Smlaierprepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 222171169Smlaier struct sockaddr *dst, u_int16_t d_port) 223171169Smlaier{ 224171169Smlaier char an[PF_ANCHOR_NAME_SIZE]; 225171169Smlaier 226171169Smlaier if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 227171169Smlaier (src->sa_family != dst->sa_family)) { 228171169Smlaier errno = EPROTONOSUPPORT; 229171169Smlaier return (-1); 230171169Smlaier } 231171169Smlaier 232171169Smlaier memset(&pfp, 0, sizeof pfp); 233171169Smlaier memset(&pfr, 0, sizeof pfr); 234171169Smlaier snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 235171169Smlaier getpid(), id); 236171169Smlaier strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 237171169Smlaier strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE); 238171169Smlaier 239171169Smlaier switch (rs_num) { 240171169Smlaier case PF_RULESET_FILTER: 241171169Smlaier pfr.ticket = pfte[TRANS_FILTER].ticket; 242171169Smlaier break; 243171169Smlaier case PF_RULESET_NAT: 244171169Smlaier pfr.ticket = pfte[TRANS_NAT].ticket; 245171169Smlaier break; 246171169Smlaier case PF_RULESET_RDR: 247171169Smlaier pfr.ticket = pfte[TRANS_RDR].ticket; 248171169Smlaier break; 249171169Smlaier default: 250171169Smlaier errno = EINVAL; 251171169Smlaier return (-1); 252171169Smlaier } 253171169Smlaier if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 254171169Smlaier return (-1); 255171169Smlaier pfr.pool_ticket = pfp.ticket; 256171169Smlaier 257171169Smlaier /* Generic for all rule types. */ 258171169Smlaier pfr.rule.af = src->sa_family; 259171169Smlaier pfr.rule.proto = IPPROTO_TCP; 260171169Smlaier pfr.rule.src.addr.type = PF_ADDR_ADDRMASK; 261171169Smlaier pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 262171169Smlaier if (src->sa_family == AF_INET) { 263171169Smlaier memcpy(&pfr.rule.src.addr.v.a.addr.v4, 264171169Smlaier &satosin(src)->sin_addr.s_addr, 4); 265171169Smlaier memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4); 266171169Smlaier memcpy(&pfr.rule.dst.addr.v.a.addr.v4, 267171169Smlaier &satosin(dst)->sin_addr.s_addr, 4); 268171169Smlaier memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4); 269171169Smlaier } else { 270171169Smlaier memcpy(&pfr.rule.src.addr.v.a.addr.v6, 271171169Smlaier &satosin6(src)->sin6_addr.s6_addr, 16); 272171169Smlaier memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16); 273171169Smlaier memcpy(&pfr.rule.dst.addr.v.a.addr.v6, 274171169Smlaier &satosin6(dst)->sin6_addr.s6_addr, 16); 275171169Smlaier memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16); 276171169Smlaier } 277171169Smlaier pfr.rule.dst.port_op = PF_OP_EQ; 278171169Smlaier pfr.rule.dst.port[0] = htons(d_port); 279171169Smlaier 280171169Smlaier switch (rs_num) { 281171169Smlaier case PF_RULESET_FILTER: 282171169Smlaier /* 283171169Smlaier * pass quick [log] inet[6] proto tcp \ 284171169Smlaier * from $src to $dst port = $d_port flags S/SA keep state 285171169Smlaier * (max 1) [queue qname] 286171169Smlaier */ 287171169Smlaier pfr.rule.action = PF_PASS; 288171169Smlaier pfr.rule.quick = 1; 289171169Smlaier pfr.rule.log = rule_log; 290171169Smlaier pfr.rule.keep_state = 1; 291171169Smlaier pfr.rule.flags = TH_SYN; 292171169Smlaier pfr.rule.flagset = (TH_SYN|TH_ACK); 293171169Smlaier pfr.rule.max_states = 1; 294171169Smlaier if (qname != NULL) 295171169Smlaier strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname); 296171169Smlaier break; 297171169Smlaier case PF_RULESET_NAT: 298171169Smlaier /* 299171169Smlaier * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 300171169Smlaier */ 301171169Smlaier pfr.rule.action = PF_NAT; 302171169Smlaier break; 303171169Smlaier case PF_RULESET_RDR: 304171169Smlaier /* 305171169Smlaier * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 306171169Smlaier */ 307171169Smlaier pfr.rule.action = PF_RDR; 308171169Smlaier break; 309171169Smlaier default: 310171169Smlaier errno = EINVAL; 311171169Smlaier return (-1); 312171169Smlaier } 313171169Smlaier 314171169Smlaier return (0); 315171169Smlaier} 316171169Smlaier 317171169Smlaierint 318171169Smlaierserver_lookup(struct sockaddr *client, struct sockaddr *proxy, 319171169Smlaier struct sockaddr *server) 320171169Smlaier{ 321171169Smlaier if (client->sa_family == AF_INET) 322171169Smlaier return (server_lookup4(satosin(client), satosin(proxy), 323171169Smlaier satosin(server))); 324171169Smlaier 325171169Smlaier if (client->sa_family == AF_INET6) 326171169Smlaier return (server_lookup6(satosin6(client), satosin6(proxy), 327171169Smlaier satosin6(server))); 328171169Smlaier 329171169Smlaier errno = EPROTONOSUPPORT; 330171169Smlaier return (-1); 331171169Smlaier} 332171169Smlaier 333171169Smlaierint 334171169Smlaierserver_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 335171169Smlaier struct sockaddr_in *server) 336171169Smlaier{ 337171169Smlaier struct pfioc_natlook pnl; 338171169Smlaier 339171169Smlaier memset(&pnl, 0, sizeof pnl); 340171169Smlaier pnl.direction = PF_OUT; 341171169Smlaier pnl.af = AF_INET; 342171169Smlaier pnl.proto = IPPROTO_TCP; 343171169Smlaier memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 344171169Smlaier memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 345171169Smlaier pnl.sport = client->sin_port; 346171169Smlaier pnl.dport = proxy->sin_port; 347171169Smlaier 348171169Smlaier if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 349171169Smlaier return (-1); 350171169Smlaier 351171169Smlaier memset(server, 0, sizeof(struct sockaddr_in)); 352171169Smlaier server->sin_len = sizeof(struct sockaddr_in); 353171169Smlaier server->sin_family = AF_INET; 354171169Smlaier memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 355171169Smlaier sizeof server->sin_addr.s_addr); 356171169Smlaier server->sin_port = pnl.rdport; 357171169Smlaier 358171169Smlaier return (0); 359171169Smlaier} 360171169Smlaier 361171169Smlaierint 362171169Smlaierserver_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 363171169Smlaier struct sockaddr_in6 *server) 364171169Smlaier{ 365171169Smlaier struct pfioc_natlook pnl; 366171169Smlaier 367171169Smlaier memset(&pnl, 0, sizeof pnl); 368171169Smlaier pnl.direction = PF_OUT; 369171169Smlaier pnl.af = AF_INET6; 370171169Smlaier pnl.proto = IPPROTO_TCP; 371171169Smlaier memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 372171169Smlaier memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 373171169Smlaier pnl.sport = client->sin6_port; 374171169Smlaier pnl.dport = proxy->sin6_port; 375171169Smlaier 376171169Smlaier if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 377171169Smlaier return (-1); 378171169Smlaier 379171169Smlaier memset(server, 0, sizeof(struct sockaddr_in6)); 380171169Smlaier server->sin6_len = sizeof(struct sockaddr_in6); 381171169Smlaier server->sin6_family = AF_INET6; 382171169Smlaier memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 383171169Smlaier sizeof server->sin6_addr); 384171169Smlaier server->sin6_port = pnl.rdport; 385171169Smlaier 386171169Smlaier return (0); 387171169Smlaier} 388