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