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 const char *qname, *tagname; 62 63 int 64 add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src, 65 struct sockaddr *dst, u_int16_t d_port) 66 { 67 if (!src || !dst || !d_port) { 68 errno = EINVAL; 69 return (-1); 70 } 71 72 if (prepare_rule(id, PF_RULESET_FILTER, src, dst, d_port) == -1) 73 return (-1); 74 75 pfrule.direction = dir; 76 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 77 pfticket, pfpool_ticket)) 78 return (-1); 79 80 return (0); 81 } 82 83 int 84 add_nat(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 85 u_int16_t d_port, struct sockaddr *nat, u_int16_t nat_range_low, 86 u_int16_t nat_range_high) 87 { 88 if (!src || !dst || !d_port || !nat || !nat_range_low || 89 (src->sa_family != nat->sa_family)) { 90 errno = EINVAL; 91 return (-1); 92 } 93 94 if (prepare_rule(id, PF_RULESET_NAT, src, dst, d_port) == -1) 95 return (-1); 96 97 if (nat->sa_family == AF_INET) { 98 memcpy(&pfp.addr.addr.v.a.addr.v4, 99 &satosin(nat)->sin_addr.s_addr, 4); 100 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 101 } else { 102 memcpy(&pfp.addr.addr.v.a.addr.v6, 103 &satosin6(nat)->sin6_addr.s6_addr, 16); 104 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 105 } 106 if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 107 return (-1); 108 109 pfrule.rpool.proxy_port[0] = nat_range_low; 110 pfrule.rpool.proxy_port[1] = nat_range_high; 111 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 112 pfticket, pfpool_ticket)) 113 return (-1); 114 115 return (0); 116 } 117 118 int 119 add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst, 120 u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port) 121 { 122 if (!src || !dst || !d_port || !rdr || !rdr_port || 123 (src->sa_family != rdr->sa_family)) { 124 errno = EINVAL; 125 return (-1); 126 } 127 128 if (prepare_rule(id, PF_RULESET_RDR, src, dst, d_port) == -1) 129 return (-1); 130 131 if (rdr->sa_family == AF_INET) { 132 memcpy(&pfp.addr.addr.v.a.addr.v4, 133 &satosin(rdr)->sin_addr.s_addr, 4); 134 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 4); 135 } else { 136 memcpy(&pfp.addr.addr.v.a.addr.v6, 137 &satosin6(rdr)->sin6_addr.s6_addr, 16); 138 memset(&pfp.addr.addr.v.a.mask.addr8, 255, 16); 139 } 140 if (ioctl(dev, DIOCADDADDR, &pfp) == -1) 141 return (-1); 142 143 pfrule.rpool.proxy_port[0] = rdr_port; 144 if (pfctl_add_rule(dev, &pfrule, pfanchor, pfanchor_call, 145 pfticket, pfpool_ticket)) 146 return (-1); 147 148 return (0); 149 } 150 151 int 152 do_commit(void) 153 { 154 if (ioctl(dev, DIOCXCOMMIT, &pft) == -1) 155 return (-1); 156 157 return (0); 158 } 159 160 int 161 do_rollback(void) 162 { 163 if (ioctl(dev, DIOCXROLLBACK, &pft) == -1) 164 return (-1); 165 166 return (0); 167 } 168 169 void 170 init_filter(const char *opt_qname, const char *opt_tagname, int opt_verbose) 171 { 172 struct pfctl_status *status; 173 174 qname = opt_qname; 175 tagname = opt_tagname; 176 177 if (opt_verbose == 1) 178 rule_log = PF_LOG; 179 else if (opt_verbose == 2) 180 rule_log = PF_LOG_ALL; 181 182 dev = open("/dev/pf", O_RDWR); 183 if (dev == -1) 184 err(1, "open /dev/pf"); 185 status = pfctl_get_status(dev); 186 if (status == NULL) 187 err(1, "DIOCGETSTATUS"); 188 if (!status->running) 189 errx(1, "pf is disabled"); 190 191 pfctl_free_status(status); 192 } 193 194 int 195 prepare_commit(u_int32_t id) 196 { 197 char an[PF_ANCHOR_NAME_SIZE]; 198 int i; 199 200 memset(&pft, 0, sizeof pft); 201 pft.size = TRANS_SIZE; 202 pft.esize = sizeof pfte[0]; 203 pft.array = pfte; 204 205 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 206 getpid(), id); 207 for (i = 0; i < TRANS_SIZE; i++) { 208 memset(&pfte[i], 0, sizeof pfte[0]); 209 strlcpy(pfte[i].anchor, an, PF_ANCHOR_NAME_SIZE); 210 switch (i) { 211 case TRANS_FILTER: 212 pfte[i].rs_num = PF_RULESET_FILTER; 213 break; 214 case TRANS_NAT: 215 pfte[i].rs_num = PF_RULESET_NAT; 216 break; 217 case TRANS_RDR: 218 pfte[i].rs_num = PF_RULESET_RDR; 219 break; 220 default: 221 errno = EINVAL; 222 return (-1); 223 } 224 } 225 226 if (ioctl(dev, DIOCXBEGIN, &pft) == -1) 227 return (-1); 228 229 return (0); 230 } 231 232 int 233 prepare_rule(u_int32_t id, int rs_num, struct sockaddr *src, 234 struct sockaddr *dst, u_int16_t d_port) 235 { 236 char an[PF_ANCHOR_NAME_SIZE]; 237 238 if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) || 239 (src->sa_family != dst->sa_family)) { 240 errno = EPROTONOSUPPORT; 241 return (-1); 242 } 243 244 memset(&pfp, 0, sizeof pfp); 245 memset(&pfrule, 0, sizeof pfrule); 246 snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR, 247 getpid(), id); 248 strlcpy(pfp.anchor, an, PF_ANCHOR_NAME_SIZE); 249 strlcpy(pfanchor, an, PF_ANCHOR_NAME_SIZE); 250 251 switch (rs_num) { 252 case PF_RULESET_FILTER: 253 pfticket = pfte[TRANS_FILTER].ticket; 254 break; 255 case PF_RULESET_NAT: 256 pfticket = pfte[TRANS_NAT].ticket; 257 break; 258 case PF_RULESET_RDR: 259 pfticket = pfte[TRANS_RDR].ticket; 260 break; 261 default: 262 errno = EINVAL; 263 return (-1); 264 } 265 if (ioctl(dev, DIOCBEGINADDRS, &pfp) == -1) 266 return (-1); 267 pfpool_ticket = pfp.ticket; 268 269 /* Generic for all rule types. */ 270 pfrule.af = src->sa_family; 271 pfrule.proto = IPPROTO_TCP; 272 pfrule.src.addr.type = PF_ADDR_ADDRMASK; 273 pfrule.dst.addr.type = PF_ADDR_ADDRMASK; 274 if (src->sa_family == AF_INET) { 275 memcpy(&pfrule.src.addr.v.a.addr.v4, 276 &satosin(src)->sin_addr.s_addr, 4); 277 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 4); 278 memcpy(&pfrule.dst.addr.v.a.addr.v4, 279 &satosin(dst)->sin_addr.s_addr, 4); 280 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 4); 281 } else { 282 memcpy(&pfrule.src.addr.v.a.addr.v6, 283 &satosin6(src)->sin6_addr.s6_addr, 16); 284 memset(&pfrule.src.addr.v.a.mask.addr8, 255, 16); 285 memcpy(&pfrule.dst.addr.v.a.addr.v6, 286 &satosin6(dst)->sin6_addr.s6_addr, 16); 287 memset(&pfrule.dst.addr.v.a.mask.addr8, 255, 16); 288 } 289 pfrule.dst.port_op = PF_OP_EQ; 290 pfrule.dst.port[0] = htons(d_port); 291 292 switch (rs_num) { 293 case PF_RULESET_FILTER: 294 /* 295 * pass [quick] [log] inet[6] proto tcp \ 296 * from $src to $dst port = $d_port flags S/SA keep state 297 * (max 1) [queue qname] [tag tagname] 298 */ 299 pfrule.action = PF_PASS; 300 pfrule.quick = 1; 301 pfrule.log = rule_log; 302 pfrule.keep_state = 1; 303 pfrule.flags = TH_SYN; 304 pfrule.flagset = (TH_SYN|TH_ACK); 305 pfrule.max_states = 1; 306 if (qname != NULL) 307 strlcpy(pfrule.qname, qname, sizeof pfrule.qname); 308 if (tagname != NULL) { 309 pfrule.quick = 0; 310 strlcpy(pfrule.tagname, tagname, 311 sizeof pfrule.tagname); 312 } 313 break; 314 case PF_RULESET_NAT: 315 /* 316 * nat inet[6] proto tcp from $src to $dst port $d_port -> $nat 317 */ 318 pfrule.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 pfrule.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) 337 { 338 if (client->sa_family == AF_INET) 339 return (server_lookup4(satosin(client), satosin(proxy), 340 satosin(server))); 341 342 if (client->sa_family == AF_INET6) 343 return (server_lookup6(satosin6(client), satosin6(proxy), 344 satosin6(server))); 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) 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 = IPPROTO_TCP; 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) 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 = IPPROTO_TCP; 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