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 dev, rule_log; 61static const char *qname, *tagname; 62 63int 64add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 65 struct sockaddr *dst, u_int16_t d_port) 66{ 67 if (!src || !dst || !d_port) { 68 errno = EINVAL; 69 return (-1); 70 } 71 72 if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) 73 return (-1); 74 75 pfrule.direction = dir; 76 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 77 pfticket, pfpool_ticket)) 78 return (-1); 79 80 return (0); 81} 82 83int 84add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 85 u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 86 u_int16_t nat_range_high) 87{ 88 if (!src || !dst || !d_port || !nat || !nat_range_low || 89 (src->sa_family != nat->sa_family)) { 90 errno = EINVAL; 91 return (-1); 92 } 93 94 if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 95 return (-1); 96 97 if (nat->sa_family == AF_INET) { 98 memcpy(&pfp.addr.addr.v.a.addr.v4, 99 &satosin(nat)->sin_addr.s_addr, 4); 100 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 101 } else { 102 memcpy(&pfp.addr.addr.v.a.addr.v6, 103 &satosin6(nat)->sin6_addr.s6_addr, 16); 104 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 105 } 106 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 107 pfticket, pfpool_ticket)) 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(dev, &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 (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 142 pfticket, pfpool_ticket)) 143 return (-1); 144 145 pfrule.rpool.proxy_port[0] = rdr_port; 146 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 147 pfticket, pfpool_ticket)) 148 return (-1); 149 150 return (0); 151} 152 153int 154do_commit(void) 155{ 156 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 157 return (-1); 158 159 return (0); 160} 161 162int 163do_rollback(void) 164{ 165 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 166 return (-1); 167 168 return (0); 169} 170 171void 172init_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) 173{ 174 struct pf_status status; 175 176 qname = opt_qname; 177 tagname = opt_tagname; 178 179 if (opt_verbose == 1) 180 rule_log = PF_LOG; 181 else if (opt_verbose == 2) 182 rule_log = PF_LOG_ALL; 183 184 dev = open("/dev/pf", O_RDWR); 185 if (dev == -1) 186 err(1, "open /dev/pf"); 187 if (ioctl(dev, DIOCGETSTATUS, &status) == -1) 188 err(1, "DIOCGETSTATUS"); 189 if (!status.running) 190 errx(1, "pf is disabled"); 191} 192 193int 194prepare_commit(u_int32_t id) 195{ 196 char an[PF_ANCHOR_NAME_SIZE]; 197 int i; 198 199 memset(&pft, 0, sizeof pft); 200 pft.size = TRANS_SIZE; 201 pft.esize = sizeof pfte[0]; 202 pft.array = pfte; 203 204 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 205 getpid(), id); 206 for (i = 0; i < TRANS_SIZE; i++) { 207 memset(&pfte[i], 0, sizeof pfte[0]); 208 strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 209 switch (i) { 210 case TRANS_FILTER: 211 pfte[i].rs_num = PF_RULESET_FILTER; 212 break; 213 case TRANS_NAT: 214 pfte[i].rs_num = PF_RULESET_NAT; 215 break; 216 case TRANS_RDR: 217 pfte[i].rs_num = PF_RULESET_RDR; 218 break; 219 default: 220 errno = EINVAL; 221 return (-1); 222 } 223 } 224 225 if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 226 return (-1); 227 228 return (0); 229} 230 231int 232prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 233 struct sockaddr *dst, u_int16_t d_port) 234{ 235 char an[PF_ANCHOR_NAME_SIZE]; 236 237 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 238 (src->sa_family != dst->sa_family)) { 239 errno = EPROTONOSUPPORT; 240 return (-1); 241 } 242 243 memset(&pfp, 0, sizeof pfp); 244 memset(&pfrule, 0, sizeof pfrule); 245 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 246 getpid(), id); 247 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 248 strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); 249 250 switch (rs_num) { 251 case PF_RULESET_FILTER: 252 pfticket = pfte[TRANS_FILTER].ticket; 253 break; 254 case PF_RULESET_NAT: 255 pfticket = pfte[TRANS_NAT].ticket; 256 break; 257 case PF_RULESET_RDR: 258 pfticket = pfte[TRANS_RDR].ticket; 259 break; 260 default: 261 errno = EINVAL; 262 return (-1); 263 } 264 if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 265 return (-1); 266 pfpool_ticket = pfp.ticket; 267 268 /* Generic for all rule types. */ 269 pfrule.af = src->sa_family; 270 pfrule.proto = IPPROTO_TCP; 271 pfrule.src.addr.type = PF_ADDR_ADDRMASK; 272 pfrule.dst.addr.type = PF_ADDR_ADDRMASK; 273 if (src->sa_family == AF_INET) { 274 memcpy(&pfrule.src.addr.v.a.addr.v4, 275 &satosin(src)->sin_addr.s_addr, 4); 276 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); 277 memcpy(&pfrule.dst.addr.v.a.addr.v4, 278 &satosin(dst)->sin_addr.s_addr, 4); 279 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); 280 } else { 281 memcpy(&pfrule.src.addr.v.a.addr.v6, 282 &satosin6(src)->sin6_addr.s6_addr, 16); 283 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); 284 memcpy(&pfrule.dst.addr.v.a.addr.v6, 285 &satosin6(dst)->sin6_addr.s6_addr, 16); 286 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); 287 } 288 pfrule.dst.port_op = PF_OP_EQ; 289 pfrule.dst.port[0] = htons(d_port); 290 291 switch (rs_num) { 292 case PF_RULESET_FILTER: 293 /* 294 * pass [quick] [log] inet[6] proto tcp \ 295 * from $src to $dst port = $d_port flags S/SA keep state 296 * (max 1) [queue qname] [tag tagname] 297 */ 298 pfrule.action = PF_PASS; 299 pfrule.quick = 1; 300 pfrule.log = rule_log; 301 pfrule.keep_state = 1; 302 pfrule.flags = TH_SYN; 303 pfrule.flagset = (TH_SYN|TH_ACK); 304 pfrule.max_states = 1; 305 if (qname != NULL) 306 strlcpy(pfrule.qname, qname, sizeof pfrule.qname); 307 if (tagname != NULL) { 308 pfrule.quick = 0; 309 strlcpy(pfrule.tagname, tagname, 310 sizeof pfrule.tagname); 311 } 312 break; 313 case PF_RULESET_NAT: 314 /* 315 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 316 */ 317 pfrule.action = PF_NAT; 318 break; 319 case PF_RULESET_RDR: 320 /* 321 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 322 */ 323 pfrule.action = PF_RDR; 324 break; 325 default: 326 errno = EINVAL; 327 return (-1); 328 } 329 330 return (0); 331} 332 333int 334server_lookup(struct sockaddr *client, struct sockaddr *proxy, 335 struct sockaddr *server) 336{ 337 if (client->sa_family == AF_INET) 338 return (server_lookup4(satosin(client), satosin(proxy), 339 satosin(server))); 340 341 if (client->sa_family == AF_INET6) 342 return (server_lookup6(satosin6(client), satosin6(proxy), 343 satosin6(server))); 344 345 errno = EPROTONOSUPPORT; 346 return (-1); 347} 348 349int 350server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 351 struct sockaddr_in *server) 352{ 353 struct pfioc_natlook pnl; 354 355 memset(&pnl, 0, sizeof pnl); 356 pnl.direction = PF_OUT; 357 pnl.af = AF_INET; 358 pnl.proto = IPPROTO_TCP; 359 memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 360 memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 361 pnl.sport = client->sin_port; 362 pnl.dport = proxy->sin_port; 363 364 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 365 return (-1); 366 367 memset(server, 0, sizeof(struct sockaddr_in)); 368 server->sin_len = sizeof(struct sockaddr_in); 369 server->sin_family = AF_INET; 370 memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 371 sizeof server->sin_addr.s_addr); 372 server->sin_port = pnl.rdport; 373 374 return (0); 375} 376 377int 378server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 379 struct sockaddr_in6 *server) 380{ 381 struct pfioc_natlook pnl; 382 383 memset(&pnl, 0, sizeof pnl); 384 pnl.direction = PF_OUT; 385 pnl.af = AF_INET6; 386 pnl.proto = IPPROTO_TCP; 387 memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 388 memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 389 pnl.sport = client->sin6_port; 390 pnl.dport = proxy->sin6_port; 391 392 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 393 return (-1); 394 395 memset(server, 0, sizeof(struct sockaddr_in6)); 396 server->sin6_len = sizeof(struct sockaddr_in6); 397 server->sin6_family = AF_INET6; 398 memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 399 sizeof server->sin6_addr); 400 server->sin6_port = pnl.rdport; 401 402 return (0); 403} 404