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