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