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