1/* $OpenBSD: filter.c,v 1.8 2008/06/13 07:25:26 claudio 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 <sys/ioctl.h> 20#include <sys/types.h> 21#include <sys/socket.h> 22 23#include <net/if.h> 24#include <net/pfvar.h> 25#include <netinet/in.h> 26#include <netinet/tcp.h> 27#include <arpa/inet.h> 28 29#include <err.h> 30#include <errno.h> 31#include <libpfctl.h> 32#include <fcntl.h> 33#include <stdio.h> 34#include <string.h> 35#include <unistd.h> 36 37#include "filter.h" 38 39/* From netinet/in.h, but only _KERNEL_ gets them. */ 40#define satosin(sa) ((struct sockaddr_in *)(sa)) 41#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 42 43enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 44 45int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, 46 u_int16_t); 47int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, 48 struct sockaddr_in *); 49int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, 50 struct sockaddr_in6 *); 51 52static struct pfioc_pooladdr pfp; 53static struct pfctl_rule pfrule; 54static char pfanchor[PF_ANCHOR_NAME_SIZE]; 55static char pfanchor_call[PF_ANCHOR_NAME_SIZE]; 56static uint32_t pfticket; 57static uint32_t pfpool_ticket; 58static struct pfioc_trans pft; 59static struct pfioc_trans_e pfte[TRANS_SIZE]; 60static int rule_log; 61static struct pfctl_handle *pfh = NULL; 62static const char *qname, *tagname; 63 64int 65add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 66 struct sockaddr *dst, u_int16_t d_port) 67{ 68 if (!src || !dst || !d_port) { 69 errno = EINVAL; 70 return (-1); 71 } 72 73 if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) 74 return (-1); 75 76 pfrule.direction = dir; 77 if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, 78 pfticket, pfpool_ticket)) 79 return (-1); 80 81 return (0); 82} 83 84int 85add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 86 u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 87 u_int16_t nat_range_high) 88{ 89 if (!src || !dst || !d_port || !nat || !nat_range_low || 90 (src->sa_family != nat->sa_family)) { 91 errno = EINVAL; 92 return (-1); 93 } 94 95 if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 96 return (-1); 97 98 if (nat->sa_family == AF_INET) { 99 memcpy(&pfp.addr.addr.v.a.addr.v4, 100 &satosin(nat)->sin_addr.s_addr, 4); 101 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 102 } else { 103 memcpy(&pfp.addr.addr.v.a.addr.v6, 104 &satosin6(nat)->sin6_addr.s6_addr, 16); 105 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 106 } 107 if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) 108 return (-1); 109 110 pfrule.rpool.proxy_port[0] = nat_range_low; 111 pfrule.rpool.proxy_port[1] = nat_range_high; 112 if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, 113 pfticket, pfpool_ticket)) 114 return (-1); 115 116 return (0); 117} 118 119int 120add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 121 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 122{ 123 if (!src || !dst || !d_port || !rdr || !rdr_port || 124 (src->sa_family != rdr->sa_family)) { 125 errno = EINVAL; 126 return (-1); 127 } 128 129 if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) 130 return (-1); 131 132 if (rdr->sa_family == AF_INET) { 133 memcpy(&pfp.addr.addr.v.a.addr.v4, 134 &satosin(rdr)->sin_addr.s_addr, 4); 135 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 136 } else { 137 memcpy(&pfp.addr.addr.v.a.addr.v6, 138 &satosin6(rdr)->sin6_addr.s6_addr, 16); 139 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 140 } 141 if (ioctl(pfctl_fd(pfh), DIOCADDADDR, &pfp) == -1) 142 return (-1); 143 144 pfrule.rpool.proxy_port[0] = rdr_port; 145 if (pfctl_add_rule_h(pfh, &pfrule, pfanchor, pfanchor_call, 146 pfticket, pfpool_ticket)) 147 return (-1); 148 149 return (0); 150} 151 152int 153do_commit(void) 154{ 155 if (ioctl(pfctl_fd(pfh), DIOCXCOMMIT, &pft) == -1) 156 return (-1); 157 158 return (0); 159} 160 161int 162do_rollback(void) 163{ 164 if (ioctl(pfctl_fd(pfh), DIOCXROLLBACK, &pft) == -1) 165 return (-1); 166 167 return (0); 168} 169 170void 171init_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) 172{ 173 struct pfctl_status *status; 174 175 qname = opt_qname; 176 tagname = opt_tagname; 177 178 if (opt_verbose == 1) 179 rule_log = PF_LOG; 180 else if (opt_verbose == 2) 181 rule_log = PF_LOG_ALL; 182 183 pfh = pfctl_open(PF_DEVICE); 184 if (pfh == NULL) 185 err(1, "pfctl_open"); 186 status = pfctl_get_status_h(pfh); 187 if (status == NULL) 188 err(1, "DIOCGETSTATUS"); 189 if (!status->running) 190 errx(1, "pf is disabled"); 191 192 pfctl_free_status(status); 193} 194 195int 196prepare_commit(u_int32_t id) 197{ 198 char an[PF_ANCHOR_NAME_SIZE]; 199 int i; 200 201 memset(&pft, 0, sizeof pft); 202 pft.size = TRANS_SIZE; 203 pft.esize = sizeof pfte[0]; 204 pft.array = pfte; 205 206 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 207 getpid(), id); 208 for (i = 0; i < TRANS_SIZE; i++) { 209 memset(&pfte[i], 0, sizeof pfte[0]); 210 strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 211 switch (i) { 212 case TRANS_FILTER: 213 pfte[i].rs_num = PF_RULESET_FILTER; 214 break; 215 case TRANS_NAT: 216 pfte[i].rs_num = PF_RULESET_NAT; 217 break; 218 case TRANS_RDR: 219 pfte[i].rs_num = PF_RULESET_RDR; 220 break; 221 default: 222 errno = EINVAL; 223 return (-1); 224 } 225 } 226 227 if (ioctl(pfctl_fd(pfh), DIOCXBEGIN, &pft) == -1) 228 return (-1); 229 230 return (0); 231} 232 233int 234prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 235 struct sockaddr *dst, u_int16_t d_port) 236{ 237 char an[PF_ANCHOR_NAME_SIZE]; 238 239 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 240 (src->sa_family != dst->sa_family)) { 241 errno = EPROTONOSUPPORT; 242 return (-1); 243 } 244 245 memset(&pfp, 0, sizeof pfp); 246 memset(&pfrule, 0, sizeof pfrule); 247 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 248 getpid(), id); 249 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 250 strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); 251 252 switch (rs_num) { 253 case PF_RULESET_FILTER: 254 pfticket = pfte[TRANS_FILTER].ticket; 255 break; 256 case PF_RULESET_NAT: 257 pfticket = pfte[TRANS_NAT].ticket; 258 break; 259 case PF_RULESET_RDR: 260 pfticket = pfte[TRANS_RDR].ticket; 261 break; 262 default: 263 errno = EINVAL; 264 return (-1); 265 } 266 if (pfctl_begin_addrs(pfh, &pfp.ticket)) 267 return (-1); 268 pfpool_ticket = pfp.ticket; 269 270 /* Generic for all rule types. */ 271 pfrule.af = src->sa_family; 272 pfrule.proto = IPPROTO_TCP; 273 pfrule.src.addr.type = PF_ADDR_ADDRMASK; 274 pfrule.dst.addr.type = PF_ADDR_ADDRMASK; 275 if (src->sa_family == AF_INET) { 276 memcpy(&pfrule.src.addr.v.a.addr.v4, 277 &satosin(src)->sin_addr.s_addr, 4); 278 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); 279 memcpy(&pfrule.dst.addr.v.a.addr.v4, 280 &satosin(dst)->sin_addr.s_addr, 4); 281 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); 282 } else { 283 memcpy(&pfrule.src.addr.v.a.addr.v6, 284 &satosin6(src)->sin6_addr.s6_addr, 16); 285 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); 286 memcpy(&pfrule.dst.addr.v.a.addr.v6, 287 &satosin6(dst)->sin6_addr.s6_addr, 16); 288 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); 289 } 290 pfrule.dst.port_op = PF_OP_EQ; 291 pfrule.dst.port[0] = htons(d_port); 292 293 switch (rs_num) { 294 case PF_RULESET_FILTER: 295 /* 296 * pass [quick] [log] inet[6] proto tcp \ 297 * from $src to $dst port = $d_port flags S/SA keep state 298 * (max 1) [queue qname] [tag tagname] 299 */ 300 pfrule.action = PF_PASS; 301 pfrule.quick = 1; 302 pfrule.log = rule_log; 303 pfrule.keep_state = 1; 304 pfrule.flags = TH_SYN; 305 pfrule.flagset = (TH_SYN|TH_ACK); 306 pfrule.max_states = 1; 307 if (qname != NULL) 308 strlcpy(pfrule.qname, qname, sizeof pfrule.qname); 309 if (tagname != NULL) { 310 pfrule.quick = 0; 311 strlcpy(pfrule.tagname, tagname, 312 sizeof pfrule.tagname); 313 } 314 break; 315 case PF_RULESET_NAT: 316 /* 317 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 318 */ 319 pfrule.action = PF_NAT; 320 break; 321 case PF_RULESET_RDR: 322 /* 323 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 324 */ 325 pfrule.action = PF_RDR; 326 break; 327 default: 328 errno = EINVAL; 329 return (-1); 330 } 331 332 return (0); 333} 334 335int 336server_lookup(struct sockaddr *client, struct sockaddr *proxy, 337 struct sockaddr *server) 338{ 339 if (client->sa_family == AF_INET) 340 return (server_lookup4(satosin(client), satosin(proxy), 341 satosin(server))); 342 343 if (client->sa_family == AF_INET6) 344 return (server_lookup6(satosin6(client), satosin6(proxy), 345 satosin6(server))); 346 347 errno = EPROTONOSUPPORT; 348 return (-1); 349} 350 351int 352server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 353 struct sockaddr_in *server) 354{ 355 struct pfctl_natlook_key k = {}; 356 struct pfctl_natlook r = {}; 357 358 k.direction = PF_OUT; 359 k.af = AF_INET; 360 k.proto = IPPROTO_TCP; 361 memcpy(&k.saddr.v4, &client->sin_addr.s_addr, sizeof(k.saddr.v4)); 362 memcpy(&k.daddr.v4, &proxy->sin_addr.s_addr, sizeof(k.daddr.v4)); 363 k.sport = client->sin_port; 364 k.dport = proxy->sin_port; 365 366 if (pfctl_natlook(pfh, &k, &r)) 367 return (-1); 368 369 memset(server, 0, sizeof(struct sockaddr_in)); 370 server->sin_len = sizeof(struct sockaddr_in); 371 server->sin_family = AF_INET; 372 memcpy(&server->sin_addr.s_addr, &r.daddr.v4, 373 sizeof(server->sin_addr.s_addr)); 374 server->sin_port = r.dport; 375 376 return (0); 377} 378 379int 380server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 381 struct sockaddr_in6 *server) 382{ 383 struct pfctl_natlook_key k = {}; 384 struct pfctl_natlook r = {}; 385 386 k.direction = PF_OUT; 387 k.af = AF_INET6; 388 k.proto = IPPROTO_TCP; 389 memcpy(&k.saddr.v6, &client->sin6_addr.s6_addr, sizeof(k.saddr.v6)); 390 memcpy(&k.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof(k.daddr.v6)); 391 k.sport = client->sin6_port; 392 k.dport = proxy->sin6_port; 393 394 if (pfctl_natlook(pfh, &k, &r)) 395 return (-1); 396 397 memset(server, 0, sizeof(struct sockaddr_in6)); 398 server->sin6_len = sizeof(struct sockaddr_in6); 399 server->sin6_family = AF_INET6; 400 memcpy(&server->sin6_addr.s6_addr, &r.daddr.v6, 401 sizeof(server->sin6_addr)); 402 server->sin6_port = r.dport; 403 404 return (0); 405} 406