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