1248590Smm/* $OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio Exp $ */ 2248590Smm 3248590Smm/* 4248590Smm * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> 5248590Smm * 6248590Smm * Permission to use, copy, modify, and distribute this software for any 7248590Smm * purpose with or without fee is hereby granted, provided that the above 8248590Smm * copyright notice and this permission notice appear in all copies. 9248590Smm * 10248590Smm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11248590Smm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12248590Smm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13248590Smm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14248590Smm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15248590Smm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16248590Smm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17248590Smm */ 18248590Smm 19248590Smm#include <sys/ioctl.h> 20248590Smm#include <sys/types.h> 21248590Smm#include <sys/socket.h> 22248590Smm 23248590Smm#include <net/if.h> 24248590Smm#include <net/pfvar.h> 25248590Smm#include <netinet/in.h> 26248590Smm#include <netinet/tcp.h> 27248590Smm#include <arpa/inet.h> 28248590Smm 29248590Smm#include <err.h> 30248590Smm#include <errno.h> 31248590Smm#include <fcntl.h> 32248590Smm#include <stdio.h> 33248590Smm#include <string.h> 34248590Smm#include <unistd.h> 35248590Smm 36248590Smm#include "filter.h" 37248590Smm 38248590Smm/* From netinet/in.h, but only _KERNEL_ gets them. */ 39248590Smm#define satosin(sa) ((struct sockaddr_in *)(sa)) 40248590Smm#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 41248590Smm 42248590Smmenum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 43248590Smm 44248590Smmint prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, 45248590Smm u_int16_t); 46248590Smmint server_lookup4(struct sockaddr_in *, struct sockaddr_in *, 47248590Smm struct sockaddr_in *); 48248590Smmint server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, 49248590Smm struct sockaddr_in6 *); 50248590Smm 51248590Smmstatic struct pfioc_pooladdr pfp; 52248590Smmstatic struct pfioc_rule pfr; 53248590Smmstatic struct pfioc_trans pft; 54248590Smmstatic struct pfioc_trans_e pfte[TRANS_SIZE]; 55248590Smmstatic int dev, rule_log; 56248590Smmstatic const char *qname, *tagname; 57248590Smm 58248590Smmint 59248590Smmadd_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 60248590Smm struct sockaddr *dst, u_int16_t d_port) 61248590Smm{ 62248590Smm if (!src || !dst || !d_port) { 63248590Smm errno = EINVAL; 64248590Smm return (-1); 65248590Smm } 66248590Smm 67248590Smm if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) 68248590Smm return (-1); 69248590Smm 70248590Smm pfr.rule.direction = dir; 71248590Smm if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 72248590Smm return (-1); 73248590Smm 74248590Smm return (0); 75248590Smm} 76248590Smm 77248590Smmint 78248590Smmadd_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 79248590Smm u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 80248590Smm u_int16_t nat_range_high) 81248590Smm{ 82248590Smm if (!src || !dst || !d_port || !nat || !nat_range_low || 83248590Smm (src->sa_family != nat->sa_family)) { 84248590Smm errno = EINVAL; 85248590Smm return (-1); 86248590Smm } 87248590Smm 88302001Smm if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 89248590Smm return (-1); 90248590Smm 91248590Smm if (nat->sa_family == AF_INET) { 92248590Smm memcpy(&pfp.addr.addr.v.a.addr.v4, 93248590Smm &satosin(nat)->sin_addr.s_addr, 4); 94248590Smm memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 95248590Smm } else { 96248590Smm memcpy(&pfp.addr.addr.v.a.addr.v6, 97248590Smm &satosin6(nat)->sin6_addr.s6_addr, 16); 98248590Smm memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 99248590Smm } 100248590Smm if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 101248590Smm return (-1); 102248590Smm 103248590Smm pfr.rule.rpool.proxy_port[0] = nat_range_low; 104248590Smm pfr.rule.rpool.proxy_port[1] = nat_range_high; 105248590Smm if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 106248590Smm return (-1); 107248590Smm 108248590Smm return (0); 109248590Smm} 110248590Smm 111248590Smmint 112248590Smmadd_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 113248590Smm u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 114248590Smm{ 115248590Smm if (!src || !dst || !d_port || !rdr || !rdr_port || 116248590Smm (src->sa_family != rdr->sa_family)) { 117248590Smm errno = EINVAL; 118248590Smm return (-1); 119248590Smm } 120248590Smm 121248590Smm if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) 122248590Smm return (-1); 123248590Smm 124248590Smm if (rdr->sa_family == AF_INET) { 125248590Smm memcpy(&pfp.addr.addr.v.a.addr.v4, 126248590Smm &satosin(rdr)->sin_addr.s_addr, 4); 127248590Smm memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 128248590Smm } else { 129248590Smm memcpy(&pfp.addr.addr.v.a.addr.v6, 130248590Smm &satosin6(rdr)->sin6_addr.s6_addr, 16); 131248590Smm memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 132248590Smm } 133248590Smm if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 134248590Smm return (-1); 135248590Smm 136248590Smm pfr.rule.rpool.proxy_port[0] = rdr_port; 137248590Smm if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 138248590Smm return (-1); 139248590Smm 140248590Smm return (0); 141248590Smm} 142248590Smm 143248590Smmint 144248590Smmdo_commit(void) 145248590Smm{ 146248590Smm if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 147248590Smm return (-1); 148248590Smm 149248590Smm return (0); 150248590Smm} 151248590Smm 152248590Smmint 153248590Smmdo_rollback(void) 154248590Smm{ 155248590Smm if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 156248590Smm return (-1); 157248590Smm 158248590Smm return (0); 159248590Smm} 160248590Smm 161248590Smmvoid 162248590Smminit_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) 163248590Smm{ 164248590Smm struct pf_status status; 165248590Smm 166248590Smm qname = opt_qname; 167248590Smm tagname = opt_tagname; 168248590Smm 169248590Smm if (opt_verbose == 1) 170248590Smm rule_log = PF_LOG; 171248590Smm else if (opt_verbose == 2) 172248590Smm rule_log = PF_LOG_ALL; 173248590Smm 174248590Smm dev = open("/dev/pf", O_RDWR); 175248590Smm if (dev == -1) 176302001Smm err(1, "open /dev/pf"); 177248590Smm if (ioctl(dev, DIOCGETSTATUS, &status) == -1) 178248590Smm err(1, "DIOCGETSTATUS"); 179248590Smm if (!status.running) 180248590Smm errx(1, "pf is disabled"); 181248590Smm} 182248590Smm 183248590Smmint 184248590Smmprepare_commit(u_int32_t id) 185248590Smm{ 186248590Smm char an[PF_ANCHOR_NAME_SIZE]; 187248590Smm int i; 188248590Smm 189248590Smm memset(&pft, 0, sizeof pft); 190248590Smm pft.size = TRANS_SIZE; 191248590Smm pft.esize = sizeof pfte[0]; 192248590Smm pft.array = pfte; 193248590Smm 194248590Smm snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 195248590Smm getpid(), id); 196248590Smm for (i = 0; i < TRANS_SIZE; i++) { 197248590Smm memset(&pfte[i], 0, sizeof pfte[0]); 198248590Smm strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 199248590Smm switch (i) { 200248590Smm case TRANS_FILTER: 201248590Smm pfte[i].rs_num = PF_RULESET_FILTER; 202248590Smm break; 203248590Smm case TRANS_NAT: 204248590Smm pfte[i].rs_num = PF_RULESET_NAT; 205248590Smm break; 206248590Smm case TRANS_RDR: 207248590Smm pfte[i].rs_num = PF_RULESET_RDR; 208248590Smm break; 209248590Smm default: 210248590Smm errno = EINVAL; 211248590Smm return (-1); 212248590Smm } 213248590Smm } 214248590Smm 215248590Smm if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 216248590Smm return (-1); 217248590Smm 218248590Smm return (0); 219248590Smm} 220248590Smm 221248590Smmint 222248590Smmprepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 223248590Smm struct sockaddr *dst, u_int16_t d_port) 224248590Smm{ 225248590Smm char an[PF_ANCHOR_NAME_SIZE]; 226248590Smm 227248590Smm if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 228248590Smm (src->sa_family != dst->sa_family)) { 229248590Smm errno = EPROTONOSUPPORT; 230248590Smm return (-1); 231248590Smm } 232248590Smm 233248590Smm memset(&pfp, 0, sizeof pfp); 234248590Smm memset(&pfr, 0, sizeof pfr); 235248590Smm snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 236248590Smm getpid(), id); 237248590Smm strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 238248590Smm strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE); 239248590Smm 240248590Smm switch (rs_num) { 241248590Smm case PF_RULESET_FILTER: 242248590Smm pfr.ticket = pfte[TRANS_FILTER].ticket; 243248590Smm break; 244248590Smm case PF_RULESET_NAT: 245248590Smm pfr.ticket = pfte[TRANS_NAT].ticket; 246248590Smm break; 247248590Smm case PF_RULESET_RDR: 248248590Smm pfr.ticket = pfte[TRANS_RDR].ticket; 249248590Smm break; 250248590Smm default: 251248590Smm errno = EINVAL; 252248590Smm return (-1); 253248590Smm } 254248590Smm if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 255248590Smm return (-1); 256248590Smm pfr.pool_ticket = pfp.ticket; 257248590Smm 258248590Smm /* Generic for all rule types. */ 259248590Smm pfr.rule.af = src->sa_family; 260248590Smm pfr.rule.proto = IPPROTO_TCP; 261248590Smm pfr.rule.src.addr.type = PF_ADDR_ADDRMASK; 262248590Smm pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 263248590Smm if (src->sa_family == AF_INET) { 264248590Smm memcpy(&pfr.rule.src.addr.v.a.addr.v4, 265248590Smm &satosin(src)->sin_addr.s_addr, 4); 266248590Smm memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4); 267248590Smm memcpy(&pfr.rule.dst.addr.v.a.addr.v4, 268248590Smm &satosin(dst)->sin_addr.s_addr, 4); 269248590Smm memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4); 270248590Smm } else { 271248590Smm memcpy(&pfr.rule.src.addr.v.a.addr.v6, 272248590Smm &satosin6(src)->sin6_addr.s6_addr, 16); 273248590Smm memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16); 274248590Smm memcpy(&pfr.rule.dst.addr.v.a.addr.v6, 275248590Smm &satosin6(dst)->sin6_addr.s6_addr, 16); 276248590Smm memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16); 277248590Smm } 278248590Smm pfr.rule.dst.port_op = PF_OP_EQ; 279248590Smm pfr.rule.dst.port[0] = htons(d_port); 280248590Smm 281248590Smm switch (rs_num) { 282248590Smm case PF_RULESET_FILTER: 283248590Smm /* 284248590Smm * pass [quick] [log] inet[6] proto tcp \ 285248590Smm * from $src to $dst port = $d_port flags S/SA keep state 286248590Smm * (max 1) [queue qname] [tag tagname] 287248590Smm */ 288248590Smm pfr.rule.action = PF_PASS; 289248590Smm pfr.rule.quick = 1; 290248590Smm pfr.rule.log = rule_log; 291248590Smm pfr.rule.keep_state = 1; 292248590Smm pfr.rule.flags = TH_SYN; 293248590Smm pfr.rule.flagset = (TH_SYN|TH_ACK); 294248590Smm pfr.rule.max_states = 1; 295248590Smm if (qname != NULL) 296248590Smm strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname); 297248590Smm if (tagname != NULL) { 298248590Smm pfr.rule.quick = 0; 299248590Smm strlcpy(pfr.rule.tagname, tagname, 300248590Smm sizeof pfr.rule.tagname); 301248590Smm } 302248590Smm break; 303248590Smm case PF_RULESET_NAT: 304248590Smm /* 305248590Smm * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 306248590Smm */ 307248590Smm pfr.rule.action = PF_NAT; 308248590Smm break; 309248590Smm case PF_RULESET_RDR: 310248590Smm /* 311248590Smm * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 312248590Smm */ 313248590Smm pfr.rule.action = PF_RDR; 314248590Smm break; 315248590Smm default: 316248590Smm errno = EINVAL; 317248590Smm return (-1); 318248590Smm } 319248590Smm 320248590Smm return (0); 321248590Smm} 322248590Smm 323248590Smmint 324248590Smmserver_lookup(struct sockaddr *client, struct sockaddr *proxy, 325248590Smm struct sockaddr *server) 326248590Smm{ 327248590Smm if (client->sa_family == AF_INET) 328248590Smm return (server_lookup4(satosin(client), satosin(proxy), 329248590Smm satosin(server))); 330248590Smm 331248590Smm if (client->sa_family == AF_INET6) 332248590Smm return (server_lookup6(satosin6(client), satosin6(proxy), 333248590Smm satosin6(server))); 334248590Smm 335248590Smm errno = EPROTONOSUPPORT; 336248590Smm return (-1); 337248590Smm} 338248590Smm 339248590Smmint 340248590Smmserver_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 341248590Smm struct sockaddr_in *server) 342248590Smm{ 343248590Smm struct pfioc_natlook pnl; 344248590Smm 345248590Smm memset(&pnl, 0, sizeof pnl); 346248590Smm pnl.direction = PF_OUT; 347248590Smm pnl.af = AF_INET; 348248590Smm pnl.proto = IPPROTO_TCP; 349248590Smm memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 350248590Smm memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 351248590Smm pnl.sport = client->sin_port; 352248590Smm pnl.dport = proxy->sin_port; 353248590Smm 354248590Smm if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 355248590Smm return (-1); 356248590Smm 357248590Smm memset(server, 0, sizeof(struct sockaddr_in)); 358248590Smm server->sin_len = sizeof(struct sockaddr_in); 359248590Smm server->sin_family = AF_INET; 360248590Smm memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 361248590Smm sizeof server->sin_addr.s_addr); 362248590Smm server->sin_port = pnl.rdport; 363248590Smm 364248590Smm return (0); 365248590Smm} 366248590Smm 367248590Smmint 368248590Smmserver_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 369248590Smm struct sockaddr_in6 *server) 370248590Smm{ 371248590Smm struct pfioc_natlook pnl; 372248590Smm 373248590Smm memset(&pnl, 0, sizeof pnl); 374248590Smm pnl.direction = PF_OUT; 375248590Smm pnl.af = AF_INET6; 376248590Smm pnl.proto = IPPROTO_TCP; 377248590Smm memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 378248590Smm memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 379248590Smm pnl.sport = client->sin6_port; 380248590Smm pnl.dport = proxy->sin6_port; 381248590Smm 382248590Smm if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 383248590Smm return (-1); 384248590Smm 385248590Smm memset(server, 0, sizeof(struct sockaddr_in6)); 386248590Smm server->sin6_len = sizeof(struct sockaddr_in6); 387248590Smm server->sin6_family = AF_INET6; 388248590Smm memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 389248590Smm sizeof server->sin6_addr); 390248590Smm server->sin6_port = pnl.rdport; 391248590Smm 392248590Smm return (0); 393248590Smm} 394248590Smm