summaryrefslogtreecommitdiff
path: root/sys/net/pf_lb.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2011-10-13 18:23:41 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2011-10-13 18:23:41 +0000
commitd0ab30fdb97104766da99fa8da4eb3371d2d4163 (patch)
tree1b5cd770f1597f4f741778c5f55e6f431f1f22fc /sys/net/pf_lb.c
parent35252f0dafa4ccce1148cc18897ba319f6c982cd (diff)
Since the IPv6 madness is not enough introduce NAT64 -- which is actually
"af-to" a generic IP version translator for pf(4). Not everything perfect yet but lets fix these things in the tree. Insane amount of work done by sperreault@, mikeb@ and reyk@. Looked over by mcbride@ henning@ and myself at eurobsdcon. OK mcbride@ and general put it in from deraadt@
Diffstat (limited to 'sys/net/pf_lb.c')
-rw-r--r--sys/net/pf_lb.c144
1 files changed, 139 insertions, 5 deletions
diff --git a/sys/net/pf_lb.c b/sys/net/pf_lb.c
index bd9e78f7745..6082cb905ef 100644
--- a/sys/net/pf_lb.c
+++ b/sys/net/pf_lb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf_lb.c,v 1.18 2011/09/18 11:17:57 miod Exp $ */
+/* $OpenBSD: pf_lb.c,v 1.19 2011/10/13 18:23:40 claudio Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -103,6 +103,8 @@ void pf_hash(struct pf_addr *, struct pf_addr *,
int pf_get_sport(struct pf_pdesc *, struct pf_rule *,
struct pf_addr *, u_int16_t *, u_int16_t,
u_int16_t, struct pf_src_node **);
+int pf_get_transaddr_af(struct pf_rule *,
+ struct pf_pdesc *, struct pf_src_node **);
int pf_islinklocal(sa_family_t, struct pf_addr *);
#define mix(a,b,c) \
@@ -172,7 +174,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
u_int16_t cut;
bzero(&init_addr, sizeof(init_addr));
- if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
+ if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
PF_SN_NAT))
return (1);
@@ -186,7 +188,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
}
do {
- key.af = pd->af;
+ key.af = pd->naf;
key.proto = pd->proto;
key.rdomain = pd->rdomain;
PF_ACPY(&key.addr[0], &pd->ndaddr, key.af);
@@ -251,7 +253,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
case PF_POOL_RANDOM:
case PF_POOL_ROUNDROBIN:
case PF_POOL_LEASTSTATES:
- if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr,
+ if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
&init_addr, sn, &r->nat, PF_SN_NAT))
return (1);
break;
@@ -261,7 +263,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
default:
return (1);
}
- } while (! PF_AEQ(&init_addr, naddr, pd->af) );
+ } while (! PF_AEQ(&init_addr, naddr, pd->naf) );
return (1); /* none available */
}
@@ -580,6 +582,9 @@ pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_addr naddr;
u_int16_t nport = 0;
+ if (pd->af != pd->naf)
+ return (pf_get_transaddr_af(r, pd, sns));
+
if (r->nat.addr.type != PF_ADDR_NONE) {
/* XXX is this right? what if rtable is changed at the same
* XXX time? where do I need to figure out the sport? */
@@ -628,6 +633,135 @@ pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
}
int
+pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
+ struct pf_src_node **sns)
+{
+ struct pf_addr ndaddr, nsaddr, naddr;
+ u_int16_t nport = 0;
+ int prefixlen = 96;
+
+ if (pf_status.debug >= LOG_NOTICE) {
+ log(LOG_NOTICE, "pf: af-to %s %s, ",
+ pd->naf == AF_INET ? "inet" : "inet6",
+ r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
+ pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
+ addlog(" -> ");
+ pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
+ addlog("\n");
+ }
+
+ if (r->nat.addr.type == PF_ADDR_NONE)
+ panic("pf_get_transaddr_af: no nat pool for source address");
+
+ /* get source address and port */
+ if (pf_get_sport(pd, r, &nsaddr, &nport,
+ r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
+ DPFPRINTF(LOG_NOTICE,
+ "pf: af-to NAT proxy port allocation (%u-%u) failed",
+ r->nat.proxy_port[0],
+ r->nat.proxy_port[1]);
+ return (-1);
+ }
+ pd->nsport = nport;
+
+ if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
+ if (pd->dir == PF_IN) {
+ NTOHS(pd->ndport);
+ if (pd->ndport == ICMP6_ECHO_REQUEST)
+ pd->ndport = ICMP_ECHO;
+ else if (pd->ndport == ICMP6_ECHO_REPLY)
+ pd->ndport = ICMP_ECHOREPLY;
+ HTONS(pd->ndport);
+ } else {
+ NTOHS(pd->nsport);
+ if (pd->nsport == ICMP6_ECHO_REQUEST)
+ pd->nsport = ICMP_ECHO;
+ else if (pd->nsport == ICMP6_ECHO_REPLY)
+ pd->nsport = ICMP_ECHOREPLY;
+ HTONS(pd->nsport);
+ }
+ } else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
+ if (pd->dir == PF_IN) {
+ NTOHS(pd->ndport);
+ if (pd->ndport == ICMP_ECHO)
+ pd->ndport = ICMP6_ECHO_REQUEST;
+ else if (pd->ndport == ICMP_ECHOREPLY)
+ pd->ndport = ICMP6_ECHO_REPLY;
+ HTONS(pd->ndport);
+ } else {
+ NTOHS(pd->nsport);
+ if (pd->nsport == ICMP_ECHO)
+ pd->nsport = ICMP6_ECHO_REQUEST;
+ else if (pd->nsport == ICMP_ECHOREPLY)
+ pd->nsport = ICMP6_ECHO_REPLY;
+ HTONS(pd->nsport);
+ }
+ }
+
+ /* get the destination address and port */
+ if (r->rdr.addr.type != PF_ADDR_NONE) {
+ if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
+ &r->rdr, PF_SN_RDR))
+ return (-1);
+ if (r->rdr.proxy_port[0])
+ pd->ndport = htons(r->rdr.proxy_port[0]);
+
+ if (pd->naf == AF_INET) {
+ /* The prefix is the IPv4 rdr address */
+ prefixlen = in_mask2len((struct in_addr *)
+ &r->rdr.addr.v.a.mask);
+ inet_nat46(pd->naf, &pd->ndaddr,
+ &ndaddr, &naddr, prefixlen);
+ } else {
+ /* The prefix is the IPv6 rdr address */
+ prefixlen =
+ in6_mask2len((struct in6_addr *)
+ &r->rdr.addr.v.a.mask, NULL);
+ inet_nat64(pd->naf, &pd->ndaddr,
+ &ndaddr, &naddr, prefixlen);
+ }
+ } else {
+ if (pd->naf == AF_INET) {
+ /* The prefix is the IPv6 dst address */
+ prefixlen =
+ in6_mask2len((struct in6_addr *)
+ &r->dst.addr.v.a.mask, NULL);
+ if (prefixlen < 32)
+ prefixlen = 96;
+ inet_nat64(pd->naf, &pd->ndaddr,
+ &ndaddr, &pd->ndaddr, prefixlen);
+ } else {
+ /*
+ * The prefix is the IPv6 nat address
+ * (that was stored in pd->nsaddr)
+ */
+ prefixlen = in6_mask2len((struct in6_addr *)
+ &r->nat.addr.v.a.mask, NULL);
+ if (prefixlen > 96)
+ prefixlen = 96;
+ inet_nat64(pd->naf, &pd->ndaddr,
+ &ndaddr, &nsaddr, prefixlen);
+ }
+ }
+
+ PF_ACPY(&pd->nsaddr, &nsaddr, pd->naf);
+ PF_ACPY(&pd->ndaddr, &ndaddr, pd->naf);
+
+ if (pf_status.debug >= LOG_NOTICE) {
+ log(LOG_NOTICE, "pf: af-to %s %s done, prefixlen %d, ",
+ pd->naf == AF_INET ? "inet" : "inet6",
+ r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
+ prefixlen);
+ pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
+ addlog(" -> ");
+ pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
+ addlog("\n");
+ }
+
+ return (0);
+}
+
+int
pf_postprocess_addr(struct pf_state *cur) {
struct pf_rule *nr;