1223637Sbz/* $OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio 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; 56223637Sbzstatic const char *qname, *tagname; 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 162223637Sbzinit_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) 163171169Smlaier{ 164171169Smlaier struct pf_status status; 165171169Smlaier 166171169Smlaier qname = opt_qname; 167223637Sbz tagname = opt_tagname; 168171169Smlaier 169171169Smlaier if (opt_verbose == 1) 170171169Smlaier rule_log = PF_LOG; 171171169Smlaier else if (opt_verbose == 2) 172171169Smlaier rule_log = PF_LOG_ALL; 173171169Smlaier 174171169Smlaier dev = open("/dev/pf", O_RDWR); 175171169Smlaier if (dev == -1) 176223637Sbz err(1, "open /dev/pf"); 177171169Smlaier if (ioctl(dev, DIOCGETSTATUS, &status) == -1) 178171169Smlaier err(1, "DIOCGETSTATUS"); 179171169Smlaier if (!status.running) 180171169Smlaier errx(1, "pf is disabled"); 181171169Smlaier} 182171169Smlaier 183171169Smlaierint 184171169Smlaierprepare_commit(u_int32_t id) 185171169Smlaier{ 186171169Smlaier char an[PF_ANCHOR_NAME_SIZE]; 187171169Smlaier int i; 188171169Smlaier 189171169Smlaier memset(&pft, 0, sizeof pft); 190171169Smlaier pft.size = TRANS_SIZE; 191171169Smlaier pft.esize = sizeof pfte[0]; 192171169Smlaier pft.array = pfte; 193171169Smlaier 194171169Smlaier snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 195171169Smlaier getpid(), id); 196171169Smlaier for (i = 0; i < TRANS_SIZE; i++) { 197171169Smlaier memset(&pfte[i], 0, sizeof pfte[0]); 198171169Smlaier strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 199171169Smlaier switch (i) { 200171169Smlaier case TRANS_FILTER: 201171169Smlaier pfte[i].rs_num = PF_RULESET_FILTER; 202171169Smlaier break; 203171169Smlaier case TRANS_NAT: 204171169Smlaier pfte[i].rs_num = PF_RULESET_NAT; 205171169Smlaier break; 206171169Smlaier case TRANS_RDR: 207171169Smlaier pfte[i].rs_num = PF_RULESET_RDR; 208171169Smlaier break; 209171169Smlaier default: 210171169Smlaier errno = EINVAL; 211171169Smlaier return (-1); 212171169Smlaier } 213171169Smlaier } 214171169Smlaier 215171169Smlaier if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 216171169Smlaier return (-1); 217171169Smlaier 218171169Smlaier return (0); 219171169Smlaier} 220171169Smlaier 221171169Smlaierint 222171169Smlaierprepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 223171169Smlaier struct sockaddr *dst, u_int16_t d_port) 224171169Smlaier{ 225171169Smlaier char an[PF_ANCHOR_NAME_SIZE]; 226171169Smlaier 227171169Smlaier if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 228171169Smlaier (src->sa_family != dst->sa_family)) { 229171169Smlaier errno = EPROTONOSUPPORT; 230171169Smlaier return (-1); 231171169Smlaier } 232171169Smlaier 233171169Smlaier memset(&pfp, 0, sizeof pfp); 234171169Smlaier memset(&pfr, 0, sizeof pfr); 235171169Smlaier snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 236171169Smlaier getpid(), id); 237171169Smlaier strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 238171169Smlaier strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE); 239171169Smlaier 240171169Smlaier switch (rs_num) { 241171169Smlaier case PF_RULESET_FILTER: 242171169Smlaier pfr.ticket = pfte[TRANS_FILTER].ticket; 243171169Smlaier break; 244171169Smlaier case PF_RULESET_NAT: 245171169Smlaier pfr.ticket = pfte[TRANS_NAT].ticket; 246171169Smlaier break; 247171169Smlaier case PF_RULESET_RDR: 248171169Smlaier pfr.ticket = pfte[TRANS_RDR].ticket; 249171169Smlaier break; 250171169Smlaier default: 251171169Smlaier errno = EINVAL; 252171169Smlaier return (-1); 253171169Smlaier } 254171169Smlaier if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 255171169Smlaier return (-1); 256171169Smlaier pfr.pool_ticket = pfp.ticket; 257171169Smlaier 258171169Smlaier /* Generic for all rule types. */ 259171169Smlaier pfr.rule.af = src->sa_family; 260171169Smlaier pfr.rule.proto = IPPROTO_TCP; 261171169Smlaier pfr.rule.src.addr.type = PF_ADDR_ADDRMASK; 262171169Smlaier pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 263171169Smlaier if (src->sa_family == AF_INET) { 264171169Smlaier memcpy(&pfr.rule.src.addr.v.a.addr.v4, 265171169Smlaier &satosin(src)->sin_addr.s_addr, 4); 266171169Smlaier memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4); 267171169Smlaier memcpy(&pfr.rule.dst.addr.v.a.addr.v4, 268171169Smlaier &satosin(dst)->sin_addr.s_addr, 4); 269171169Smlaier memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4); 270171169Smlaier } else { 271171169Smlaier memcpy(&pfr.rule.src.addr.v.a.addr.v6, 272171169Smlaier &satosin6(src)->sin6_addr.s6_addr, 16); 273171169Smlaier memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16); 274171169Smlaier memcpy(&pfr.rule.dst.addr.v.a.addr.v6, 275171169Smlaier &satosin6(dst)->sin6_addr.s6_addr, 16); 276171169Smlaier memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16); 277171169Smlaier } 278171169Smlaier pfr.rule.dst.port_op = PF_OP_EQ; 279171169Smlaier pfr.rule.dst.port[0] = htons(d_port); 280171169Smlaier 281171169Smlaier switch (rs_num) { 282171169Smlaier case PF_RULESET_FILTER: 283171169Smlaier /* 284223637Sbz * pass [quick] [log] inet[6] proto tcp \ 285171169Smlaier * from $src to $dst port = $d_port flags S/SA keep state 286223637Sbz * (max 1) [queue qname] [tag tagname] 287171169Smlaier */ 288171169Smlaier pfr.rule.action = PF_PASS; 289171169Smlaier pfr.rule.quick = 1; 290171169Smlaier pfr.rule.log = rule_log; 291171169Smlaier pfr.rule.keep_state = 1; 292171169Smlaier pfr.rule.flags = TH_SYN; 293171169Smlaier pfr.rule.flagset = (TH_SYN|TH_ACK); 294171169Smlaier pfr.rule.max_states = 1; 295171169Smlaier if (qname != NULL) 296171169Smlaier strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname); 297223637Sbz if (tagname != NULL) { 298223637Sbz pfr.rule.quick = 0; 299223637Sbz strlcpy(pfr.rule.tagname, tagname, 300223637Sbz sizeof pfr.rule.tagname); 301223637Sbz } 302171169Smlaier break; 303171169Smlaier case PF_RULESET_NAT: 304171169Smlaier /* 305171169Smlaier * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 306171169Smlaier */ 307171169Smlaier pfr.rule.action = PF_NAT; 308171169Smlaier break; 309171169Smlaier case PF_RULESET_RDR: 310171169Smlaier /* 311171169Smlaier * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 312171169Smlaier */ 313171169Smlaier pfr.rule.action = PF_RDR; 314171169Smlaier break; 315171169Smlaier default: 316171169Smlaier errno = EINVAL; 317171169Smlaier return (-1); 318171169Smlaier } 319171169Smlaier 320171169Smlaier return (0); 321171169Smlaier} 322171169Smlaier 323171169Smlaierint 324171169Smlaierserver_lookup(struct sockaddr *client, struct sockaddr *proxy, 325171169Smlaier struct sockaddr *server) 326171169Smlaier{ 327171169Smlaier if (client->sa_family == AF_INET) 328171169Smlaier return (server_lookup4(satosin(client), satosin(proxy), 329171169Smlaier satosin(server))); 330171169Smlaier 331171169Smlaier if (client->sa_family == AF_INET6) 332171169Smlaier return (server_lookup6(satosin6(client), satosin6(proxy), 333171169Smlaier satosin6(server))); 334171169Smlaier 335171169Smlaier errno = EPROTONOSUPPORT; 336171169Smlaier return (-1); 337171169Smlaier} 338171169Smlaier 339171169Smlaierint 340171169Smlaierserver_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 341171169Smlaier struct sockaddr_in *server) 342171169Smlaier{ 343171169Smlaier struct pfioc_natlook pnl; 344171169Smlaier 345171169Smlaier memset(&pnl, 0, sizeof pnl); 346171169Smlaier pnl.direction = PF_OUT; 347171169Smlaier pnl.af = AF_INET; 348171169Smlaier pnl.proto = IPPROTO_TCP; 349171169Smlaier memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 350171169Smlaier memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 351171169Smlaier pnl.sport = client->sin_port; 352171169Smlaier pnl.dport = proxy->sin_port; 353171169Smlaier 354171169Smlaier if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 355171169Smlaier return (-1); 356171169Smlaier 357171169Smlaier memset(server, 0, sizeof(struct sockaddr_in)); 358171169Smlaier server->sin_len = sizeof(struct sockaddr_in); 359171169Smlaier server->sin_family = AF_INET; 360171169Smlaier memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 361171169Smlaier sizeof server->sin_addr.s_addr); 362171169Smlaier server->sin_port = pnl.rdport; 363171169Smlaier 364171169Smlaier return (0); 365171169Smlaier} 366171169Smlaier 367171169Smlaierint 368171169Smlaierserver_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 369171169Smlaier struct sockaddr_in6 *server) 370171169Smlaier{ 371171169Smlaier struct pfioc_natlook pnl; 372171169Smlaier 373171169Smlaier memset(&pnl, 0, sizeof pnl); 374171169Smlaier pnl.direction = PF_OUT; 375171169Smlaier pnl.af = AF_INET6; 376171169Smlaier pnl.proto = IPPROTO_TCP; 377171169Smlaier memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 378171169Smlaier memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 379171169Smlaier pnl.sport = client->sin6_port; 380171169Smlaier pnl.dport = proxy->sin6_port; 381171169Smlaier 382171169Smlaier if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 383171169Smlaier return (-1); 384171169Smlaier 385171169Smlaier memset(server, 0, sizeof(struct sockaddr_in6)); 386171169Smlaier server->sin6_len = sizeof(struct sockaddr_in6); 387171169Smlaier server->sin6_family = AF_INET6; 388171169Smlaier memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 389171169Smlaier sizeof server->sin6_addr); 390171169Smlaier server->sin6_port = pnl.rdport; 391171169Smlaier 392171169Smlaier return (0); 393171169Smlaier} 394