pf_lb.c revision 240641
1223637Sbz/* $OpenBSD: pf_lb.c,v 1.2 2009/02/12 02:13:15 sthen Exp $ */ 2223637Sbz 3223637Sbz/* 4223637Sbz * Copyright (c) 2001 Daniel Hartmeier 5223637Sbz * Copyright (c) 2002 - 2008 Henning Brauer 6223637Sbz * All rights reserved. 7223637Sbz * 8223637Sbz * Redistribution and use in source and binary forms, with or without 9223637Sbz * modification, are permitted provided that the following conditions 10223637Sbz * are met: 11223637Sbz * 12223637Sbz * - Redistributions of source code must retain the above copyright 13223637Sbz * notice, this list of conditions and the following disclaimer. 14223637Sbz * - Redistributions in binary form must reproduce the above 15223637Sbz * copyright notice, this list of conditions and the following 16223637Sbz * disclaimer in the documentation and/or other materials provided 17223637Sbz * with the distribution. 18223637Sbz * 19223637Sbz * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20223637Sbz * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21223637Sbz * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22223637Sbz * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23223637Sbz * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24223637Sbz * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25223637Sbz * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26223637Sbz * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27223637Sbz * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28223637Sbz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29223637Sbz * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30223637Sbz * POSSIBILITY OF SUCH DAMAGE. 31223637Sbz * 32223637Sbz * Effort sponsored in part by the Defense Advanced Research Projects 33223637Sbz * Agency (DARPA) and Air Force Research Laboratory, Air Force 34223637Sbz * Materiel Command, USAF, under agreement number F30602-01-2-0537. 35223637Sbz * 36223637Sbz */ 37223637Sbz 38223637Sbz#include <sys/cdefs.h> 39223637Sbz__FBSDID("$FreeBSD: head/sys/netpfil/pf/pf_lb.c 240641 2012-09-18 10:54:56Z glebius $"); 40223637Sbz 41223637Sbz#include "opt_pf.h" 42240233Sglebius#include "opt_inet.h" 43240233Sglebius#include "opt_inet6.h" 44223637Sbz 45223637Sbz#include <sys/param.h> 46223637Sbz#include <sys/socket.h> 47223637Sbz#include <sys/sysctl.h> 48223637Sbz 49223637Sbz#include <net/if.h> 50223637Sbz#include <net/pfvar.h> 51223637Sbz#include <net/if_pflog.h> 52240233Sglebius#include <net/pf_mtag.h> 53223637Sbz 54223637Sbz#define DPFPRINTF(n, x) if (V_pf_status.debug >= (n)) printf x 55223637Sbz 56240233Sglebiusstatic void pf_hash(struct pf_addr *, struct pf_addr *, 57223637Sbz struct pf_poolhashkey *, sa_family_t); 58240233Sglebiusstatic struct pf_rule *pf_match_translation(struct pf_pdesc *, struct mbuf *, 59223637Sbz int, int, struct pfi_kif *, 60223637Sbz struct pf_addr *, u_int16_t, struct pf_addr *, 61240641Sglebius uint16_t, int, struct pf_anchor_stackframe *); 62240233Sglebiusstatic int pf_get_sport(sa_family_t, u_int8_t, struct pf_rule *, 63223637Sbz struct pf_addr *, struct pf_addr *, u_int16_t, 64223637Sbz struct pf_addr *, u_int16_t*, u_int16_t, u_int16_t, 65223637Sbz struct pf_src_node **); 66223637Sbz 67223637Sbz#define mix(a,b,c) \ 68223637Sbz do { \ 69223637Sbz a -= b; a -= c; a ^= (c >> 13); \ 70223637Sbz b -= c; b -= a; b ^= (a << 8); \ 71223637Sbz c -= a; c -= b; c ^= (b >> 13); \ 72223637Sbz a -= b; a -= c; a ^= (c >> 12); \ 73223637Sbz b -= c; b -= a; b ^= (a << 16); \ 74223637Sbz c -= a; c -= b; c ^= (b >> 5); \ 75223637Sbz a -= b; a -= c; a ^= (c >> 3); \ 76223637Sbz b -= c; b -= a; b ^= (a << 10); \ 77223637Sbz c -= a; c -= b; c ^= (b >> 15); \ 78223637Sbz } while (0) 79223637Sbz 80223637Sbz/* 81223637Sbz * hash function based on bridge_hash in if_bridge.c 82223637Sbz */ 83240233Sglebiusstatic void 84223637Sbzpf_hash(struct pf_addr *inaddr, struct pf_addr *hash, 85223637Sbz struct pf_poolhashkey *key, sa_family_t af) 86223637Sbz{ 87223637Sbz u_int32_t a = 0x9e3779b9, b = 0x9e3779b9, c = key->key32[0]; 88223637Sbz 89223637Sbz switch (af) { 90223637Sbz#ifdef INET 91223637Sbz case AF_INET: 92223637Sbz a += inaddr->addr32[0]; 93223637Sbz b += key->key32[1]; 94223637Sbz mix(a, b, c); 95223637Sbz hash->addr32[0] = c + key->key32[2]; 96223637Sbz break; 97223637Sbz#endif /* INET */ 98223637Sbz#ifdef INET6 99223637Sbz case AF_INET6: 100223637Sbz a += inaddr->addr32[0]; 101223637Sbz b += inaddr->addr32[2]; 102223637Sbz mix(a, b, c); 103223637Sbz hash->addr32[0] = c; 104223637Sbz a += inaddr->addr32[1]; 105223637Sbz b += inaddr->addr32[3]; 106223637Sbz c += key->key32[1]; 107223637Sbz mix(a, b, c); 108223637Sbz hash->addr32[1] = c; 109223637Sbz a += inaddr->addr32[2]; 110223637Sbz b += inaddr->addr32[1]; 111223637Sbz c += key->key32[2]; 112223637Sbz mix(a, b, c); 113223637Sbz hash->addr32[2] = c; 114223637Sbz a += inaddr->addr32[3]; 115223637Sbz b += inaddr->addr32[0]; 116223637Sbz c += key->key32[3]; 117223637Sbz mix(a, b, c); 118223637Sbz hash->addr32[3] = c; 119223637Sbz break; 120223637Sbz#endif /* INET6 */ 121223637Sbz } 122223637Sbz} 123223637Sbz 124240233Sglebiusstatic struct pf_rule * 125223637Sbzpf_match_translation(struct pf_pdesc *pd, struct mbuf *m, int off, 126223637Sbz int direction, struct pfi_kif *kif, struct pf_addr *saddr, u_int16_t sport, 127240641Sglebius struct pf_addr *daddr, uint16_t dport, int rs_num, 128240641Sglebius struct pf_anchor_stackframe *anchor_stack) 129223637Sbz{ 130223637Sbz struct pf_rule *r, *rm = NULL; 131223637Sbz struct pf_ruleset *ruleset = NULL; 132223637Sbz int tag = -1; 133223637Sbz int rtableid = -1; 134223637Sbz int asd = 0; 135223637Sbz 136223637Sbz r = TAILQ_FIRST(pf_main_ruleset.rules[rs_num].active.ptr); 137223637Sbz while (r && rm == NULL) { 138223637Sbz struct pf_rule_addr *src = NULL, *dst = NULL; 139223637Sbz struct pf_addr_wrap *xdst = NULL; 140223637Sbz 141223637Sbz if (r->action == PF_BINAT && direction == PF_IN) { 142223637Sbz src = &r->dst; 143223637Sbz if (r->rpool.cur != NULL) 144223637Sbz xdst = &r->rpool.cur->addr; 145223637Sbz } else { 146223637Sbz src = &r->src; 147223637Sbz dst = &r->dst; 148223637Sbz } 149223637Sbz 150223637Sbz r->evaluations++; 151223637Sbz if (pfi_kif_match(r->kif, kif) == r->ifnot) 152223637Sbz r = r->skip[PF_SKIP_IFP].ptr; 153223637Sbz else if (r->direction && r->direction != direction) 154223637Sbz r = r->skip[PF_SKIP_DIR].ptr; 155223637Sbz else if (r->af && r->af != pd->af) 156223637Sbz r = r->skip[PF_SKIP_AF].ptr; 157223637Sbz else if (r->proto && r->proto != pd->proto) 158223637Sbz r = r->skip[PF_SKIP_PROTO].ptr; 159223637Sbz else if (PF_MISMATCHAW(&src->addr, saddr, pd->af, 160231852Sbz src->neg, kif, M_GETFIB(m))) 161223637Sbz r = r->skip[src == &r->src ? PF_SKIP_SRC_ADDR : 162223637Sbz PF_SKIP_DST_ADDR].ptr; 163223637Sbz else if (src->port_op && !pf_match_port(src->port_op, 164223637Sbz src->port[0], src->port[1], sport)) 165223637Sbz r = r->skip[src == &r->src ? PF_SKIP_SRC_PORT : 166223637Sbz PF_SKIP_DST_PORT].ptr; 167223637Sbz else if (dst != NULL && 168231852Sbz PF_MISMATCHAW(&dst->addr, daddr, pd->af, dst->neg, NULL, 169231852Sbz M_GETFIB(m))) 170223637Sbz r = r->skip[PF_SKIP_DST_ADDR].ptr; 171223637Sbz else if (xdst != NULL && PF_MISMATCHAW(xdst, daddr, pd->af, 172231852Sbz 0, NULL, M_GETFIB(m))) 173223637Sbz r = TAILQ_NEXT(r, entries); 174223637Sbz else if (dst != NULL && dst->port_op && 175223637Sbz !pf_match_port(dst->port_op, dst->port[0], 176223637Sbz dst->port[1], dport)) 177223637Sbz r = r->skip[PF_SKIP_DST_PORT].ptr; 178240233Sglebius else if (r->match_tag && !pf_match_tag(m, r, &tag, 179240233Sglebius pd->pf_mtag ? pd->pf_mtag->tag : 0)) 180223637Sbz r = TAILQ_NEXT(r, entries); 181223637Sbz else if (r->os_fingerprint != PF_OSFP_ANY && (pd->proto != 182223637Sbz IPPROTO_TCP || !pf_osfp_match(pf_osfp_fingerprint(pd, m, 183223637Sbz off, pd->hdr.tcp), r->os_fingerprint))) 184223637Sbz r = TAILQ_NEXT(r, entries); 185223637Sbz else { 186223637Sbz if (r->tag) 187223637Sbz tag = r->tag; 188223637Sbz if (r->rtableid >= 0) 189223637Sbz rtableid = r->rtableid; 190223637Sbz if (r->anchor == NULL) { 191223637Sbz rm = r; 192223637Sbz } else 193240641Sglebius pf_step_into_anchor(anchor_stack, &asd, 194240641Sglebius &ruleset, rs_num, &r, NULL, NULL); 195223637Sbz } 196223637Sbz if (r == NULL) 197240641Sglebius pf_step_out_of_anchor(anchor_stack, &asd, &ruleset, 198240641Sglebius rs_num, &r, NULL, NULL); 199223637Sbz } 200240233Sglebius 201240233Sglebius if (tag > 0 && pf_tag_packet(m, pd, tag)) 202223637Sbz return (NULL); 203240233Sglebius if (rtableid >= 0) 204240233Sglebius M_SETFIB(m, rtableid); 205240233Sglebius 206223637Sbz if (rm != NULL && (rm->action == PF_NONAT || 207223637Sbz rm->action == PF_NORDR || rm->action == PF_NOBINAT)) 208223637Sbz return (NULL); 209223637Sbz return (rm); 210223637Sbz} 211223637Sbz 212240233Sglebiusstatic int 213223637Sbzpf_get_sport(sa_family_t af, u_int8_t proto, struct pf_rule *r, 214223637Sbz struct pf_addr *saddr, struct pf_addr *daddr, u_int16_t dport, 215223637Sbz struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high, 216223637Sbz struct pf_src_node **sn) 217223637Sbz{ 218223637Sbz struct pf_state_key_cmp key; 219223637Sbz struct pf_addr init_addr; 220223637Sbz u_int16_t cut; 221223637Sbz 222223637Sbz bzero(&init_addr, sizeof(init_addr)); 223223637Sbz if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) 224223637Sbz return (1); 225223637Sbz 226223637Sbz if (proto == IPPROTO_ICMP) { 227223637Sbz low = 1; 228223637Sbz high = 65535; 229223637Sbz } 230223637Sbz 231223637Sbz do { 232223637Sbz key.af = af; 233223637Sbz key.proto = proto; 234223637Sbz PF_ACPY(&key.addr[1], daddr, key.af); 235223637Sbz PF_ACPY(&key.addr[0], naddr, key.af); 236223637Sbz key.port[1] = dport; 237223637Sbz 238223637Sbz /* 239223637Sbz * port search; start random, step; 240223637Sbz * similar 2 portloop in in_pcbbind 241223637Sbz */ 242223637Sbz if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP || 243223637Sbz proto == IPPROTO_ICMP)) { 244223637Sbz key.port[0] = dport; 245223637Sbz if (pf_find_state_all(&key, PF_IN, NULL) == NULL) 246223637Sbz return (0); 247223637Sbz } else if (low == 0 && high == 0) { 248223637Sbz key.port[0] = *nport; 249223637Sbz if (pf_find_state_all(&key, PF_IN, NULL) == NULL) 250223637Sbz return (0); 251223637Sbz } else if (low == high) { 252223637Sbz key.port[0] = htons(low); 253223637Sbz if (pf_find_state_all(&key, PF_IN, NULL) == NULL) { 254223637Sbz *nport = htons(low); 255223637Sbz return (0); 256223637Sbz } 257223637Sbz } else { 258223637Sbz u_int16_t tmp; 259223637Sbz 260223637Sbz if (low > high) { 261223637Sbz tmp = low; 262223637Sbz low = high; 263223637Sbz high = tmp; 264223637Sbz } 265223637Sbz /* low < high */ 266223637Sbz cut = htonl(arc4random()) % (1 + high - low) + low; 267223637Sbz /* low <= cut <= high */ 268223637Sbz for (tmp = cut; tmp <= high; ++(tmp)) { 269223637Sbz key.port[0] = htons(tmp); 270223637Sbz if (pf_find_state_all(&key, PF_IN, NULL) == 271223637Sbz NULL) { 272223637Sbz *nport = htons(tmp); 273223637Sbz return (0); 274223637Sbz } 275223637Sbz } 276223637Sbz for (tmp = cut - 1; tmp >= low; --(tmp)) { 277223637Sbz key.port[0] = htons(tmp); 278223637Sbz if (pf_find_state_all(&key, PF_IN, NULL) == 279223637Sbz NULL) { 280223637Sbz *nport = htons(tmp); 281223637Sbz return (0); 282223637Sbz } 283223637Sbz } 284223637Sbz } 285223637Sbz 286223637Sbz switch (r->rpool.opts & PF_POOL_TYPEMASK) { 287223637Sbz case PF_POOL_RANDOM: 288223637Sbz case PF_POOL_ROUNDROBIN: 289223637Sbz if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) 290223637Sbz return (1); 291223637Sbz break; 292223637Sbz case PF_POOL_NONE: 293223637Sbz case PF_POOL_SRCHASH: 294223637Sbz case PF_POOL_BITMASK: 295223637Sbz default: 296223637Sbz return (1); 297223637Sbz } 298223637Sbz } while (! PF_AEQ(&init_addr, naddr, af) ); 299223637Sbz return (1); /* none available */ 300223637Sbz} 301223637Sbz 302223637Sbzint 303223637Sbzpf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr, 304223637Sbz struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sn) 305223637Sbz{ 306223637Sbz struct pf_pool *rpool = &r->rpool; 307240233Sglebius struct pf_addr *raddr = NULL, *rmask = NULL; 308223637Sbz 309223637Sbz if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR && 310223637Sbz (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { 311240233Sglebius *sn = pf_find_src_node(saddr, r, af, 0); 312223637Sbz if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) { 313223637Sbz PF_ACPY(naddr, &(*sn)->raddr, af); 314223637Sbz if (V_pf_status.debug >= PF_DEBUG_MISC) { 315223637Sbz printf("pf_map_addr: src tracking maps "); 316240233Sglebius pf_print_host(saddr, 0, af); 317223637Sbz printf(" to "); 318223637Sbz pf_print_host(naddr, 0, af); 319223637Sbz printf("\n"); 320223637Sbz } 321223637Sbz return (0); 322223637Sbz } 323223637Sbz } 324223637Sbz 325223637Sbz if (rpool->cur->addr.type == PF_ADDR_NOROUTE) 326223637Sbz return (1); 327223637Sbz if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { 328223637Sbz switch (af) { 329223637Sbz#ifdef INET 330223637Sbz case AF_INET: 331223637Sbz if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 && 332223637Sbz (rpool->opts & PF_POOL_TYPEMASK) != 333223637Sbz PF_POOL_ROUNDROBIN) 334223637Sbz return (1); 335223637Sbz raddr = &rpool->cur->addr.p.dyn->pfid_addr4; 336223637Sbz rmask = &rpool->cur->addr.p.dyn->pfid_mask4; 337223637Sbz break; 338223637Sbz#endif /* INET */ 339223637Sbz#ifdef INET6 340223637Sbz case AF_INET6: 341223637Sbz if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 && 342223637Sbz (rpool->opts & PF_POOL_TYPEMASK) != 343223637Sbz PF_POOL_ROUNDROBIN) 344223637Sbz return (1); 345223637Sbz raddr = &rpool->cur->addr.p.dyn->pfid_addr6; 346223637Sbz rmask = &rpool->cur->addr.p.dyn->pfid_mask6; 347223637Sbz break; 348223637Sbz#endif /* INET6 */ 349223637Sbz } 350223637Sbz } else if (rpool->cur->addr.type == PF_ADDR_TABLE) { 351223637Sbz if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) 352223637Sbz return (1); /* unsupported */ 353223637Sbz } else { 354223637Sbz raddr = &rpool->cur->addr.v.a.addr; 355223637Sbz rmask = &rpool->cur->addr.v.a.mask; 356223637Sbz } 357223637Sbz 358223637Sbz switch (rpool->opts & PF_POOL_TYPEMASK) { 359223637Sbz case PF_POOL_NONE: 360223637Sbz PF_ACPY(naddr, raddr, af); 361223637Sbz break; 362223637Sbz case PF_POOL_BITMASK: 363223637Sbz PF_POOLMASK(naddr, raddr, rmask, saddr, af); 364223637Sbz break; 365223637Sbz case PF_POOL_RANDOM: 366223637Sbz if (init_addr != NULL && PF_AZERO(init_addr, af)) { 367223637Sbz switch (af) { 368223637Sbz#ifdef INET 369223637Sbz case AF_INET: 370223637Sbz rpool->counter.addr32[0] = htonl(arc4random()); 371223637Sbz break; 372223637Sbz#endif /* INET */ 373223637Sbz#ifdef INET6 374223637Sbz case AF_INET6: 375223637Sbz if (rmask->addr32[3] != 0xffffffff) 376223637Sbz rpool->counter.addr32[3] = 377223637Sbz htonl(arc4random()); 378223637Sbz else 379223637Sbz break; 380223637Sbz if (rmask->addr32[2] != 0xffffffff) 381223637Sbz rpool->counter.addr32[2] = 382223637Sbz htonl(arc4random()); 383223637Sbz else 384223637Sbz break; 385223637Sbz if (rmask->addr32[1] != 0xffffffff) 386223637Sbz rpool->counter.addr32[1] = 387223637Sbz htonl(arc4random()); 388223637Sbz else 389223637Sbz break; 390223637Sbz if (rmask->addr32[0] != 0xffffffff) 391223637Sbz rpool->counter.addr32[0] = 392223637Sbz htonl(arc4random()); 393223637Sbz break; 394223637Sbz#endif /* INET6 */ 395223637Sbz } 396223637Sbz PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); 397223637Sbz PF_ACPY(init_addr, naddr, af); 398223637Sbz 399223637Sbz } else { 400223637Sbz PF_AINC(&rpool->counter, af); 401223637Sbz PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af); 402223637Sbz } 403223637Sbz break; 404223637Sbz case PF_POOL_SRCHASH: 405240233Sglebius { 406240233Sglebius unsigned char hash[16]; 407240233Sglebius 408223637Sbz pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af); 409223637Sbz PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af); 410223637Sbz break; 411240233Sglebius } 412223637Sbz case PF_POOL_ROUNDROBIN: 413240233Sglebius { 414240233Sglebius struct pf_pooladdr *acur = rpool->cur; 415240233Sglebius 416240233Sglebius /* 417240233Sglebius * XXXGL: in the round-robin case we need to store 418240233Sglebius * the round-robin machine state in the rule, thus 419240233Sglebius * forwarding thread needs to modify rule. 420240233Sglebius * 421240233Sglebius * This is done w/o locking, because performance is assumed 422240233Sglebius * more important than round-robin precision. 423240233Sglebius * 424240233Sglebius * In the simpliest case we just update the "rpool->cur" 425240233Sglebius * pointer. However, if pool contains tables or dynamic 426240233Sglebius * addresses, then "tblidx" is also used to store machine 427240233Sglebius * state. Since "tblidx" is int, concurrent access to it can't 428240233Sglebius * lead to inconsistence, only to lost of precision. 429240233Sglebius * 430240233Sglebius * Things get worse, if table contains not hosts, but 431240233Sglebius * prefixes. In this case counter also stores machine state, 432240233Sglebius * and for IPv6 address, counter can't be updated atomically. 433240233Sglebius * Probably, using round-robin on a table containing IPv6 434240233Sglebius * prefixes (or even IPv4) would cause a panic. 435240233Sglebius */ 436240233Sglebius 437223637Sbz if (rpool->cur->addr.type == PF_ADDR_TABLE) { 438223637Sbz if (!pfr_pool_get(rpool->cur->addr.p.tbl, 439240233Sglebius &rpool->tblidx, &rpool->counter, af)) 440223637Sbz goto get_addr; 441223637Sbz } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { 442223637Sbz if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, 443240233Sglebius &rpool->tblidx, &rpool->counter, af)) 444223637Sbz goto get_addr; 445223637Sbz } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af)) 446223637Sbz goto get_addr; 447223637Sbz 448223637Sbz try_next: 449240233Sglebius if (TAILQ_NEXT(rpool->cur, entries) == NULL) 450223637Sbz rpool->cur = TAILQ_FIRST(&rpool->list); 451240233Sglebius else 452240233Sglebius rpool->cur = TAILQ_NEXT(rpool->cur, entries); 453223637Sbz if (rpool->cur->addr.type == PF_ADDR_TABLE) { 454223637Sbz rpool->tblidx = -1; 455223637Sbz if (pfr_pool_get(rpool->cur->addr.p.tbl, 456240233Sglebius &rpool->tblidx, &rpool->counter, af)) { 457223637Sbz /* table contains no address of type 'af' */ 458223637Sbz if (rpool->cur != acur) 459223637Sbz goto try_next; 460223637Sbz return (1); 461223637Sbz } 462223637Sbz } else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) { 463223637Sbz rpool->tblidx = -1; 464223637Sbz if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt, 465240233Sglebius &rpool->tblidx, &rpool->counter, af)) { 466223637Sbz /* table contains no address of type 'af' */ 467223637Sbz if (rpool->cur != acur) 468223637Sbz goto try_next; 469223637Sbz return (1); 470223637Sbz } 471223637Sbz } else { 472223637Sbz raddr = &rpool->cur->addr.v.a.addr; 473223637Sbz rmask = &rpool->cur->addr.v.a.mask; 474223637Sbz PF_ACPY(&rpool->counter, raddr, af); 475223637Sbz } 476223637Sbz 477223637Sbz get_addr: 478223637Sbz PF_ACPY(naddr, &rpool->counter, af); 479223637Sbz if (init_addr != NULL && PF_AZERO(init_addr, af)) 480223637Sbz PF_ACPY(init_addr, naddr, af); 481223637Sbz PF_AINC(&rpool->counter, af); 482223637Sbz break; 483240233Sglebius } 484223637Sbz } 485223637Sbz if (*sn != NULL) 486223637Sbz PF_ACPY(&(*sn)->raddr, naddr, af); 487223637Sbz 488223637Sbz if (V_pf_status.debug >= PF_DEBUG_MISC && 489223637Sbz (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) { 490223637Sbz printf("pf_map_addr: selected address "); 491223637Sbz pf_print_host(naddr, 0, af); 492223637Sbz printf("\n"); 493223637Sbz } 494223637Sbz 495223637Sbz return (0); 496223637Sbz} 497223637Sbz 498223637Sbzstruct pf_rule * 499223637Sbzpf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, 500223637Sbz struct pfi_kif *kif, struct pf_src_node **sn, 501223637Sbz struct pf_state_key **skp, struct pf_state_key **nkp, 502223637Sbz struct pf_addr *saddr, struct pf_addr *daddr, 503240641Sglebius uint16_t sport, uint16_t dport, struct pf_anchor_stackframe *anchor_stack) 504223637Sbz{ 505223637Sbz struct pf_rule *r = NULL; 506240233Sglebius struct pf_addr *naddr; 507240233Sglebius uint16_t *nport; 508223637Sbz 509240233Sglebius PF_RULES_RASSERT(); 510240233Sglebius KASSERT(*skp == NULL, ("*skp not NULL")); 511240233Sglebius KASSERT(*nkp == NULL, ("*nkp not NULL")); 512223637Sbz 513223637Sbz if (direction == PF_OUT) { 514223637Sbz r = pf_match_translation(pd, m, off, direction, kif, saddr, 515240641Sglebius sport, daddr, dport, PF_RULESET_BINAT, anchor_stack); 516223637Sbz if (r == NULL) 517223637Sbz r = pf_match_translation(pd, m, off, direction, kif, 518240641Sglebius saddr, sport, daddr, dport, PF_RULESET_NAT, 519240641Sglebius anchor_stack); 520223637Sbz } else { 521223637Sbz r = pf_match_translation(pd, m, off, direction, kif, saddr, 522240641Sglebius sport, daddr, dport, PF_RULESET_RDR, anchor_stack); 523223637Sbz if (r == NULL) 524223637Sbz r = pf_match_translation(pd, m, off, direction, kif, 525240641Sglebius saddr, sport, daddr, dport, PF_RULESET_BINAT, 526240641Sglebius anchor_stack); 527223637Sbz } 528223637Sbz 529240233Sglebius if (r == NULL) 530240233Sglebius return (NULL); 531223637Sbz 532240233Sglebius switch (r->action) { 533240233Sglebius case PF_NONAT: 534240233Sglebius case PF_NOBINAT: 535240233Sglebius case PF_NORDR: 536240233Sglebius return (NULL); 537240233Sglebius } 538223637Sbz 539240233Sglebius *skp = pf_state_key_setup(pd, saddr, daddr, sport, dport); 540240233Sglebius if (*skp == NULL) 541240233Sglebius return (NULL); 542240233Sglebius *nkp = pf_state_key_clone(*skp); 543240233Sglebius if (*nkp == NULL) { 544240233Sglebius uma_zfree(V_pf_state_key_z, skp); 545240233Sglebius *skp = NULL; 546240233Sglebius return (NULL); 547240233Sglebius } 548223637Sbz 549240233Sglebius /* XXX We only modify one side for now. */ 550240233Sglebius naddr = &(*nkp)->addr[1]; 551240233Sglebius nport = &(*nkp)->port[1]; 552240233Sglebius 553240233Sglebius switch (r->action) { 554240233Sglebius case PF_NAT: 555240233Sglebius if (pf_get_sport(pd->af, pd->proto, r, saddr, daddr, dport, 556240233Sglebius naddr, nport, r->rpool.proxy_port[0], 557240233Sglebius r->rpool.proxy_port[1], sn)) { 558240233Sglebius DPFPRINTF(PF_DEBUG_MISC, 559240233Sglebius ("pf: NAT proxy port allocation (%u-%u) failed\n", 560240233Sglebius r->rpool.proxy_port[0], r->rpool.proxy_port[1])); 561240233Sglebius goto notrans; 562240233Sglebius } 563240233Sglebius break; 564240233Sglebius case PF_BINAT: 565240233Sglebius switch (direction) { 566240233Sglebius case PF_OUT: 567240233Sglebius if (r->rpool.cur->addr.type == PF_ADDR_DYNIFTL){ 568240233Sglebius switch (pd->af) { 569223637Sbz#ifdef INET 570240233Sglebius case AF_INET: 571240233Sglebius if (r->rpool.cur->addr.p.dyn-> 572240233Sglebius pfid_acnt4 < 1) 573240233Sglebius goto notrans; 574240233Sglebius PF_POOLMASK(naddr, 575240233Sglebius &r->rpool.cur->addr.p.dyn-> 576240233Sglebius pfid_addr4, 577240233Sglebius &r->rpool.cur->addr.p.dyn-> 578240233Sglebius pfid_mask4, saddr, AF_INET); 579240233Sglebius break; 580223637Sbz#endif /* INET */ 581223637Sbz#ifdef INET6 582240233Sglebius case AF_INET6: 583240233Sglebius if (r->rpool.cur->addr.p.dyn-> 584240233Sglebius pfid_acnt6 < 1) 585240233Sglebius goto notrans; 586240233Sglebius PF_POOLMASK(naddr, 587240233Sglebius &r->rpool.cur->addr.p.dyn-> 588240233Sglebius pfid_addr6, 589240233Sglebius &r->rpool.cur->addr.p.dyn-> 590240233Sglebius pfid_mask6, saddr, AF_INET6); 591240233Sglebius break; 592223637Sbz#endif /* INET6 */ 593240233Sglebius } 594240233Sglebius } else 595240233Sglebius PF_POOLMASK(naddr, 596240233Sglebius &r->rpool.cur->addr.v.a.addr, 597240233Sglebius &r->rpool.cur->addr.v.a.mask, saddr, 598240233Sglebius pd->af); 599240233Sglebius break; 600240233Sglebius case PF_IN: 601240233Sglebius if (r->src.addr.type == PF_ADDR_DYNIFTL) { 602240233Sglebius switch (pd->af) { 603240233Sglebius#ifdef INET 604240233Sglebius case AF_INET: 605240233Sglebius if (r->src.addr.p.dyn-> pfid_acnt4 < 1) 606240233Sglebius goto notrans; 607223637Sbz PF_POOLMASK(naddr, 608240233Sglebius &r->src.addr.p.dyn->pfid_addr4, 609240233Sglebius &r->src.addr.p.dyn->pfid_mask4, 610240233Sglebius daddr, AF_INET); 611240233Sglebius break; 612223637Sbz#endif /* INET */ 613223637Sbz#ifdef INET6 614240233Sglebius case AF_INET6: 615240233Sglebius if (r->src.addr.p.dyn->pfid_acnt6 < 1) 616240233Sglebius goto notrans; 617240233Sglebius PF_POOLMASK(naddr, 618240233Sglebius &r->src.addr.p.dyn->pfid_addr6, 619240233Sglebius &r->src.addr.p.dyn->pfid_mask6, 620240233Sglebius daddr, AF_INET6); 621240233Sglebius break; 622223637Sbz#endif /* INET6 */ 623240233Sglebius } 624240233Sglebius } else 625240233Sglebius PF_POOLMASK(naddr, &r->src.addr.v.a.addr, 626240233Sglebius &r->src.addr.v.a.mask, daddr, pd->af); 627223637Sbz break; 628240233Sglebius } 629240233Sglebius break; 630240233Sglebius case PF_RDR: { 631240233Sglebius if (pf_map_addr(pd->af, r, saddr, naddr, NULL, sn)) 632240233Sglebius goto notrans; 633240233Sglebius if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK) 634240233Sglebius PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask, 635240233Sglebius daddr, pd->af); 636223637Sbz 637240233Sglebius if (r->rpool.proxy_port[1]) { 638240233Sglebius uint32_t tmp_nport; 639223637Sbz 640240233Sglebius tmp_nport = ((ntohs(dport) - ntohs(r->dst.port[0])) % 641240233Sglebius (r->rpool.proxy_port[1] - r->rpool.proxy_port[0] + 642240233Sglebius 1)) + r->rpool.proxy_port[0]; 643223637Sbz 644240233Sglebius /* Wrap around if necessary. */ 645240233Sglebius if (tmp_nport > 65535) 646240233Sglebius tmp_nport -= 65535; 647240233Sglebius *nport = htons((uint16_t)tmp_nport); 648240233Sglebius } else if (r->rpool.proxy_port[0]) 649240233Sglebius *nport = htons(r->rpool.proxy_port[0]); 650240233Sglebius break; 651223637Sbz } 652240233Sglebius default: 653240233Sglebius panic("%s: unknown action %u", __func__, r->action); 654240233Sglebius } 655223637Sbz 656240233Sglebius /* Return success only if translation really happened. */ 657240233Sglebius if (bcmp(*skp, *nkp, sizeof(struct pf_state_key_cmp))) 658240233Sglebius return (r); 659240233Sglebius 660240233Sglebiusnotrans: 661240233Sglebius uma_zfree(V_pf_state_key_z, *nkp); 662240233Sglebius uma_zfree(V_pf_state_key_z, *skp); 663240233Sglebius *skp = *nkp = NULL; 664240233Sglebius 665240233Sglebius return (NULL); 666223637Sbz} 667