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