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 47 enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE }; 48 49 int prepare_rule(u_int32_t, int, struct sockaddr *, struct sockaddr *, 50 u_int16_t, u_int8_t); 51 int server_lookup4(struct sockaddr_in *, struct sockaddr_in *, 52 struct sockaddr_in *, u_int8_t); 53 int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *, 54 struct sockaddr_in6 *, u_int8_t); 55 56 static struct pfioc_pooladdr pfp; 57 static struct pfctl_rule pfrule; 58 static uint32_t pfticket; 59 static uint32_t pfpool_ticket; 60 static char pfanchor[PF_ANCHOR_NAME_SIZE]; 61 static char pfanchor_call[PF_ANCHOR_NAME_SIZE]; 62 static struct pfioc_trans pft; 63 static struct pfioc_trans_e pfte[TRANS_SIZE]; 64 static int dev, rule_log; 65 static struct pfctl_handle *pfh = NULL; 66 static char *qname; 67 68 int 69 add_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 88 int 89 add_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(dev, 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 123 int 124 add_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(dev, 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 156 int 157 do_commit(void) 158 { 159 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 160 return (-1); 161 162 return (0); 163 } 164 165 int 166 do_rollback(void) 167 { 168 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 169 return (-1); 170 171 return (0); 172 } 173 174 void 175 init_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 dev = open("/dev/pf", O_RDWR); 187 if (dev == -1) { 188 syslog(LOG_ERR, "can't open /dev/pf"); 189 exit(1); 190 } 191 pfh = pfctl_open(PF_DEVICE); 192 if (pfh == NULL) { 193 syslog(LOG_ERR, "can't pfctl_open()"); 194 exit(1); 195 } 196 status = pfctl_get_status(dev); 197 if (status == NULL) { 198 syslog(LOG_ERR, "DIOCGETSTATUS"); 199 exit(1); 200 } 201 if (!status->running) { 202 syslog(LOG_ERR, "pf is disabled"); 203 exit(1); 204 } 205 206 pfctl_free_status(status); 207 } 208 209 int 210 prepare_commit(u_int32_t id) 211 { 212 char an[PF_ANCHOR_NAME_SIZE]; 213 int i; 214 215 memset(&pft, 0, sizeof pft); 216 pft.size = TRANS_SIZE; 217 pft.esize = sizeof pfte[0]; 218 pft.array = pfte; 219 220 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 221 getpid(), id); 222 for (i = 0; i < TRANS_SIZE; i++) { 223 memset(&pfte[i], 0, sizeof pfte[0]); 224 strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 225 switch (i) { 226 case TRANS_FILTER: 227 pfte[i].rs_num = PF_RULESET_FILTER; 228 break; 229 case TRANS_NAT: 230 pfte[i].rs_num = PF_RULESET_NAT; 231 break; 232 case TRANS_RDR: 233 pfte[i].rs_num = PF_RULESET_RDR; 234 break; 235 default: 236 errno = EINVAL; 237 return (-1); 238 } 239 } 240 241 if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 242 return (-1); 243 244 return (0); 245 } 246 247 int 248 prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 249 struct sockaddr *dst, u_int16_t d_port, u_int8_t proto) 250 { 251 char an[PF_ANCHOR_NAME_SIZE]; 252 253 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 254 (src->sa_family != dst->sa_family)) { 255 errno = EPROTONOSUPPORT; 256 return (-1); 257 } 258 259 memset(&pfp, 0, sizeof pfp); 260 memset(&pfrule, 0, sizeof pfrule); 261 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 262 getpid(), id); 263 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 264 strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); 265 266 switch (rs_num) { 267 case PF_RULESET_FILTER: 268 pfticket = pfte[TRANS_FILTER].ticket; 269 break; 270 case PF_RULESET_NAT: 271 pfticket = pfte[TRANS_NAT].ticket; 272 break; 273 case PF_RULESET_RDR: 274 pfticket = pfte[TRANS_RDR].ticket; 275 break; 276 default: 277 errno = EINVAL; 278 return (-1); 279 } 280 if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 281 return (-1); 282 pfpool_ticket = pfp.ticket; 283 284 /* Generic for all rule types. */ 285 pfrule.af = src->sa_family; 286 pfrule.proto = proto; 287 pfrule.src.addr.type = PF_ADDR_ADDRMASK; 288 pfrule.dst.addr.type = PF_ADDR_ADDRMASK; 289 if (src->sa_family == AF_INET) { 290 memcpy(&pfrule.src.addr.v.a.addr.v4, 291 &satosin(src)->sin_addr.s_addr, 4); 292 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); 293 memcpy(&pfrule.dst.addr.v.a.addr.v4, 294 &satosin(dst)->sin_addr.s_addr, 4); 295 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); 296 } else { 297 memcpy(&pfrule.src.addr.v.a.addr.v6, 298 &satosin6(src)->sin6_addr.s6_addr, 16); 299 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); 300 memcpy(&pfrule.dst.addr.v.a.addr.v6, 301 &satosin6(dst)->sin6_addr.s6_addr, 16); 302 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); 303 } 304 pfrule.dst.port_op = PF_OP_EQ; 305 pfrule.dst.port[0] = htons(d_port); 306 307 switch (rs_num) { 308 case PF_RULESET_FILTER: 309 /* 310 * pass quick [log] inet[6] proto tcp \ 311 * from $src to $dst port = $d_port flags S/SAFR keep state 312 * (max 1) [queue qname] 313 */ 314 pfrule.action = PF_PASS; 315 pfrule.quick = 1; 316 pfrule.log = rule_log; 317 pfrule.keep_state = 1; 318 #ifdef __FreeBSD__ 319 pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0); 320 pfrule.flagset = (proto == IPPROTO_TCP ? 321 (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0); 322 #else 323 pfrule.flags = (proto == IPPROTO_TCP ? TH_SYN : NULL); 324 pfrule.flagset = (proto == IPPROTO_TCP ? 325 (TH_SYN|TH_ACK|TH_FIN|TH_RST) : NULL); 326 #endif 327 pfrule.max_states = 1; 328 if (qname != NULL) 329 strlcpy(pfrule.qname, qname, sizeof pfrule.qname); 330 break; 331 case PF_RULESET_NAT: 332 /* 333 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 334 */ 335 pfrule.action = PF_NAT; 336 break; 337 case PF_RULESET_RDR: 338 /* 339 * rdr inet[6] proto tcp from $src to $dst port $d_port -> $rdr 340 */ 341 pfrule.action = PF_RDR; 342 break; 343 default: 344 errno = EINVAL; 345 return (-1); 346 } 347 348 return (0); 349 } 350 351 int 352 server_lookup(struct sockaddr *client, struct sockaddr *proxy, 353 struct sockaddr *server, u_int8_t proto) 354 { 355 if (client->sa_family == AF_INET) 356 return (server_lookup4(satosin(client), satosin(proxy), 357 satosin(server), proto)); 358 359 if (client->sa_family == AF_INET6) 360 return (server_lookup6(satosin6(client), satosin6(proxy), 361 satosin6(server), proto)); 362 363 errno = EPROTONOSUPPORT; 364 return (-1); 365 } 366 367 int 368 server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy, 369 struct sockaddr_in *server, u_int8_t proto) 370 { 371 struct pfioc_natlook pnl; 372 373 memset(&pnl, 0, sizeof pnl); 374 pnl.direction = PF_OUT; 375 pnl.af = AF_INET; 376 pnl.proto = proto; 377 memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4); 378 memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4); 379 pnl.sport = client->sin_port; 380 pnl.dport = proxy->sin_port; 381 382 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 383 return (-1); 384 385 memset(server, 0, sizeof(struct sockaddr_in)); 386 server->sin_len = sizeof(struct sockaddr_in); 387 server->sin_family = AF_INET; 388 memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4, 389 sizeof server->sin_addr.s_addr); 390 server->sin_port = pnl.rdport; 391 392 return (0); 393 } 394 395 int 396 server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy, 397 struct sockaddr_in6 *server, u_int8_t proto) 398 { 399 struct pfioc_natlook pnl; 400 401 memset(&pnl, 0, sizeof pnl); 402 pnl.direction = PF_OUT; 403 pnl.af = AF_INET6; 404 pnl.proto = proto; 405 memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6); 406 memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6); 407 pnl.sport = client->sin6_port; 408 pnl.dport = proxy->sin6_port; 409 410 if (ioctl(dev, DIOCNATLOOK, &pnl) == -1) 411 return (-1); 412 413 memset(server, 0, sizeof(struct sockaddr_in6)); 414 server->sin6_len = sizeof(struct sockaddr_in6); 415 server->sin6_family = AF_INET6; 416 memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6, 417 sizeof server->sin6_addr); 418 server->sin6_port = pnl.rdport; 419 420 return (0); 421 } 422