1 /* $OpenBSD: filter.c,v 1.2 2015/01/21 21:50:33 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <syslog.h> 20 21 #include <sys/ioctl.h> 22 #include <sys/types.h> 23 #include <sys/socket.h> 24 25 #include <netinet/in.h> 26 #include <netinet/tcp.h> 27 #include <arpa/inet.h> 28 #include <net/if.h> 29 #include <net/pfvar.h> 30 31 #include <err.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include "filter.h" 40 41 /* From netinet/in.h, but only _KERNEL_ gets them. */ 42 #define satosin(sa) ((struct sockaddr_in *)(sa)) 43 #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 44 45 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 46 47 int prepare_rule(u_int32_t, struct sockaddr *, struct sockaddr *, 48 u_int16_t, u_int8_t); 49 50 static struct pfioc_rule pfr; 51 static struct pfioc_trans pft; 52 static struct pfioc_trans_e pfte; 53 static int dev, rule_log; 54 static char *qname; 55 56 int 57 add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 58 struct sockaddr *dst, u_int16_t d_port, u_int8_t proto) 59 { 60 if (!src || !dst || !d_port || !proto) { 61 errno = EINVAL; 62 return (-1); 63 } 64 65 if (prepare_rule(id, src, dst, d_port, proto) == -1) 66 return (-1); 67 68 pfr.rule.direction = dir; 69 if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 70 return (-1); 71 72 return (0); 73 } 74 75 int 76 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 77 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto) 78 { 79 if (!src || !dst || !d_port || !rdr || !rdr_port || !proto || 80 (src->sa_family != rdr->sa_family)) { 81 errno = EINVAL; 82 return (-1); 83 } 84 85 if (prepare_rule(id, src, dst, d_port, proto) == -1) 86 return (-1); 87 88 pfr.rule.rdr.addr.type = PF_ADDR_ADDRMASK; 89 if (rdr->sa_family == AF_INET) { 90 memcpy(&pfr.rule.rdr.addr.v.a.addr.v4, 91 &satosin(rdr)->sin_addr.s_addr, 4); 92 memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 4); 93 } else { 94 memcpy(&pfr.rule.rdr.addr.v.a.addr.v6, 95 &satosin6(rdr)->sin6_addr.s6_addr, 16); 96 memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 16); 97 } 98 99 pfr.rule.rdr.proxy_port[0] = rdr_port; 100 if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 101 return (-1); 102 103 return (0); 104 } 105 106 int 107 do_commit(void) 108 { 109 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 110 return (-1); 111 112 return (0); 113 } 114 115 int 116 do_rollback(void) 117 { 118 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 119 return (-1); 120 121 return (0); 122 } 123 124 void 125 init_filter(char *opt_qname, int opt_verbose) 126 { 127 struct pf_status status; 128 129 qname = opt_qname; 130 131 if (opt_verbose == 1) 132 rule_log = PF_LOG; 133 else if (opt_verbose == 2) 134 rule_log = PF_LOG_ALL; 135 136 dev = open("/dev/pf", O_RDWR); 137 if (dev == -1) { 138 syslog(LOG_ERR, "can't open /dev/pf"); 139 exit(1); 140 } 141 if (ioctl(dev, DIOCGETSTATUS, &status) == -1) { 142 syslog(LOG_ERR, "DIOCGETSTATUS"); 143 exit(1); 144 } 145 if (!status.running) { 146 syslog(LOG_ERR, "pf is disabled"); 147 exit(1); 148 } 149 } 150 151 int 152 prepare_commit(u_int32_t id) 153 { 154 memset(&pft, 0, sizeof pft); 155 memset(&pfte, 0, sizeof pfte); 156 pft.size = 1; 157 pft.esize = sizeof pfte; 158 pft.array = &pfte; 159 160 snprintf(pfte.anchor, PF_ANCHOR_NAME_SIZE, 161 "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id); 162 pfte.type = PF_TRANS_RULESET; 163 164 if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 165 return (-1); 166 167 return (0); 168 } 169 170 int 171 prepare_rule(u_int32_t id, struct sockaddr *src, 172 struct sockaddr *dst, u_int16_t d_port, u_int8_t proto) 173 { 174 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 175 (src->sa_family != dst->sa_family)) { 176 errno = EPROTONOSUPPORT; 177 return (-1); 178 } 179 180 memset(&pfr, 0, sizeof pfr); 181 snprintf(pfr.anchor, PF_ANCHOR_NAME_SIZE, 182 "%s/%d.%08x", FTP_PROXY_ANCHOR, getpid(), id); 183 184 pfr.ticket = pfte.ticket; 185 186 /* Generic for all rule types. */ 187 pfr.rule.af = src->sa_family; 188 pfr.rule.proto = proto; 189 pfr.rule.src.addr.type = PF_ADDR_ADDRMASK; 190 pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 191 pfr.rule.rdr.addr.type = PF_ADDR_NONE; 192 pfr.rule.nat.addr.type = PF_ADDR_NONE; 193 194 if (src->sa_family == AF_INET) { 195 memcpy(&pfr.rule.src.addr.v.a.addr.v4, 196 &satosin(src)->sin_addr.s_addr, 4); 197 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4); 198 memcpy(&pfr.rule.dst.addr.v.a.addr.v4, 199 &satosin(dst)->sin_addr.s_addr, 4); 200 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4); 201 } else { 202 memcpy(&pfr.rule.src.addr.v.a.addr.v6, 203 &satosin6(src)->sin6_addr.s6_addr, 16); 204 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16); 205 memcpy(&pfr.rule.dst.addr.v.a.addr.v6, 206 &satosin6(dst)->sin6_addr.s6_addr, 16); 207 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16); 208 } 209 pfr.rule.dst.port_op = PF_OP_EQ; 210 pfr.rule.dst.port[0] = htons(d_port); 211 #ifdef notyet 212 pfr.rule.rule_flag = PFRULE_ONCE; 213 #endif 214 pfr.rule.action = PF_PASS; 215 pfr.rule.quick = 1; 216 pfr.rule.log = rule_log; 217 pfr.rule.keep_state = 1; 218 pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0); 219 pfr.rule.flagset = (proto == IPPROTO_TCP ? 220 (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0); 221 #ifdef notyet 222 pfr.rule.max_states = 1; 223 #endif 224 if (qname != NULL) 225 strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname); 226 227 return (0); 228 } 229