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