1 /* 2 * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <sys/ioctl.h> 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 21 #include <net/if.h> 22 #include <net/pfvar.h> 23 #include <netinet/in.h> 24 #include <netinet/tcp.h> 25 #include <arpa/inet.h> 26 27 #include <err.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <stdio.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "filter.h" 35 36 /* From netinet/in.h, but only _KERNEL_ gets them. */ 37 #define satosin(sa) ((struct sockaddr_in *)(sa)) 38 #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 39 40 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 41 42 int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, 43 u_int16_t); 44 int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, 45 struct sockaddr_in *); 46 int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, 47 struct sockaddr_in6 *); 48 49 static struct pfioc_pooladdr pfp; 50 static struct pfioc_rule pfr; 51 static struct pfioc_trans pft; 52 static struct pfioc_trans_e pfte[TRANS_SIZE]; 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) 59 { 60 if (!src || !dst || !d_port) { 61 errno = EINVAL; 62 return (-1); 63 } 64 65 if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -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_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 77 u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 78 u_int16_t nat_range_high) 79 { 80 if (!src || !dst || !d_port || !nat || !nat_range_low || 81 (src->sa_family != nat->sa_family)) { 82 errno = EINVAL; 83 return (-1); 84 } 85 86 if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 87 return (-1); 88 89 if (nat->sa_family == AF_INET) { 90 memcpy(&pfp.addr.addr.v.a.addr.v4, 91 &satosin(nat)->sin_addr.s_addr, 4); 92 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 93 } else { 94 memcpy(&pfp.addr.addr.v.a.addr.v6, 95 &satosin6(nat)->sin6_addr.s6_addr, 16); 96 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 97 } 98 if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 99 return (-1); 100 101 pfr.rule.rpool.proxy_port[0] = nat_range_low; 102 pfr.rule.rpool.proxy_port[1] = nat_range_high; 103 if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 104 return (-1); 105 106 return (0); 107 } 108 109 int 110 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 111 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 112 { 113 if (!src || !dst || !d_port || !rdr || !rdr_port || 114 (src->sa_family != rdr->sa_family)) { 115 errno = EINVAL; 116 return (-1); 117 } 118 119 if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) 120 return (-1); 121 122 if (rdr->sa_family == AF_INET) { 123 memcpy(&pfp.addr.addr.v.a.addr.v4, 124 &satosin(rdr)->sin_addr.s_addr, 4); 125 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 126 } else { 127 memcpy(&pfp.addr.addr.v.a.addr.v6, 128 &satosin6(rdr)->sin6_addr.s6_addr, 16); 129 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 130 } 131 if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 132 return (-1); 133 134 pfr.rule.rpool.proxy_port[0] = rdr_port; 135 if (ioctl(dev, DIOCADDRULE, &pfr) == -1) 136 return (-1); 137 138 return (0); 139 } 140 141 int 142 do_commit(void) 143 { 144 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 145 return (-1); 146 147 return (0); 148 } 149 150 int 151 do_rollback(void) 152 { 153 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 154 return (-1); 155 156 return (0); 157 } 158 159 void 160 init_filter(char *opt_qname, int opt_verbose) 161 { 162 struct pf_status status; 163 164 qname = opt_qname; 165 166 if (opt_verbose == 1) 167 rule_log = PF_LOG; 168 else if (opt_verbose == 2) 169 rule_log = PF_LOG_ALL; 170 171 dev = open("/dev/pf", O_RDWR); 172 if (dev == -1) 173 err(1, "/dev/pf"); 174 if (ioctl(dev, DIOCGETSTATUS, &status) == -1) 175 err(1, "DIOCGETSTATUS"); 176 if (!status.running) 177 errx(1, "pf is disabled"); 178 } 179 180 int 181 prepare_commit(u_int32_t id) 182 { 183 char an[PF_ANCHOR_NAME_SIZE]; 184 int i; 185 186 memset(&pft, 0, sizeof pft); 187 pft.size = TRANS_SIZE; 188 pft.esize = sizeof pfte[0]; 189 pft.array = pfte; 190 191 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 192 getpid(), id); 193 for (i = 0; i < TRANS_SIZE; i++) { 194 memset(&pfte[i], 0, sizeof pfte[0]); 195 strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 196 switch (i) { 197 case TRANS_FILTER: 198 pfte[i].rs_num = PF_RULESET_FILTER; 199 break; 200 case TRANS_NAT: 201 pfte[i].rs_num = PF_RULESET_NAT; 202 break; 203 case TRANS_RDR: 204 pfte[i].rs_num = PF_RULESET_RDR; 205 break; 206 default: 207 errno = EINVAL; 208 return (-1); 209 } 210 } 211 212 if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 213 return (-1); 214 215 return (0); 216 } 217 218 int 219 prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 220 struct sockaddr *dst, u_int16_t d_port) 221 { 222 char an[PF_ANCHOR_NAME_SIZE]; 223 224 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 225 (src->sa_family != dst->sa_family)) { 226 errno = EPROTONOSUPPORT; 227 return (-1); 228 } 229 230 memset(&pfp, 0, sizeof pfp); 231 memset(&pfr, 0, sizeof pfr); 232 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 233 getpid(), id); 234 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 235 strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE); 236 237 switch (rs_num) { 238 case PF_RULESET_FILTER: 239 pfr.ticket = pfte[TRANS_FILTER].ticket; 240 break; 241 case PF_RULESET_NAT: 242 pfr.ticket = pfte[TRANS_NAT].ticket; 243 break; 244 case PF_RULESET_RDR: 245 pfr.ticket = pfte[TRANS_RDR].ticket; 246 break; 247 default: 248 errno = EINVAL; 249 return (-1); 250 } 251 if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 252 return (-1); 253 pfr.pool_ticket = pfp.ticket; 254 255 /* Generic for all rule types. */ 256 pfr.rule.af = src->sa_family; 257 pfr.rule.proto = IPPROTO_TCP; 258 pfr.rule.src.addr.type = PF_ADDR_ADDRMASK; 259 pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK; 260 if (src->sa_family == AF_INET) { 261 memcpy(&pfr.rule.src.addr.v.a.addr.v4, 262 &satosin(src)->sin_addr.s_addr, 4); 263 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4); 264 memcpy(&pfr.rule.dst.addr.v.a.addr.v4, 265 &satosin(dst)->sin_addr.s_addr, 4); 266 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4); 267 } else { 268 memcpy(&pfr.rule.src.addr.v.a.addr.v6, 269 &satosin6(src)->sin6_addr.s6_addr, 16); 270 memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16); 271 memcpy(&pfr.rule.dst.addr.v.a.addr.v6, 272 &satosin6(dst)->sin6_addr.s6_addr, 16); 273 memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16); 274 } 275 pfr.rule.dst.port_op = PF_OP_EQ; 276 pfr.rule.dst.port[0] = htons(d_port); 277 278 switch (rs_num) { 279 case PF_RULESET_FILTER: 280 /* 281 * pass quick [log] inet[6] proto tcp \ 282 * from $src to $dst port = $d_port flags S/SAFR keep state 283 * (max 1) [queue qname] 284 */ 285 pfr.rule.action = PF_PASS; 286 pfr.rule.quick = 1; 287 pfr.rule.log = rule_log; 288 pfr.rule.keep_state = 1; 289 pfr.rule.flags = TH_SYN; 290 pfr.rule.flagset = (TH_SYN|TH_ACK|TH_FIN|TH_RST); 291 pfr.rule.max_states = 1; 292 if (qname != NULL) 293 strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname); 294 break; 295 case PF_RULESET_NAT: 296 /* 297 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 298 */ 299 pfr.rule.action = PF_NAT; 300 break; 301 case PF_RULESET_RDR: 302 /* 303 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 304 */ 305 pfr.rule.action = PF_RDR; 306 break; 307 default: 308 errno = EINVAL; 309 return (-1); 310 } 311 312 return (0); 313 } 314 315 int 316 server_lookup(struct sockaddr *client, struct sockaddr *proxy, 317 struct sockaddr *server) 318 { 319 if (client->sa_family == AF_INET) 320 return (server_lookup4(satosin(client), satosin(proxy), 321 satosin(server))); 322 323 if (client->sa_family == AF_INET6) 324 return (server_lookup6(satosin6(client), satosin6(proxy), 325 satosin6(server))); 326 327 errno = EPROTONOSUPPORT; 328 return (-1); 329 } 330 331 int 332 server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 333 struct sockaddr_in *server) 334 { 335 struct pfioc_natlook pnl; 336 337 memset(&pnl, 0, sizeof pnl); 338 pnl.direction = PF_OUT; 339 pnl.af = AF_INET; 340 pnl.proto = IPPROTO_TCP; 341 memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 342 memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 343 pnl.sport = client->sin_port; 344 pnl.dport = proxy->sin_port; 345 346 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 347 return (-1); 348 349 memset(server, 0, sizeof(struct sockaddr_in)); 350 server->sin_len = sizeof(struct sockaddr_in); 351 server->sin_family = AF_INET; 352 memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 353 sizeof server->sin_addr.s_addr); 354 server->sin_port = pnl.rdport; 355 356 return (0); 357 } 358 359 int 360 server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 361 struct sockaddr_in6 *server) 362 { 363 struct pfioc_natlook pnl; 364 365 memset(&pnl, 0, sizeof pnl); 366 pnl.direction = PF_OUT; 367 pnl.af = AF_INET6; 368 pnl.proto = IPPROTO_TCP; 369 memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 370 memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 371 pnl.sport = client->sin6_port; 372 pnl.dport = proxy->sin6_port; 373 374 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 375 return (-1); 376 377 memset(server, 0, sizeof(struct sockaddr_in6)); 378 server->sin6_len = sizeof(struct sockaddr_in6); 379 server->sin6_family = AF_INET6; 380 memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 381 sizeof server->sin6_addr); 382 server->sin6_port = pnl.rdport; 383 384 return (0); 385 } 386