summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--regress/sbin/pfctl/Makefile3
-rw-r--r--regress/sbin/pfctl/pf97.in4
-rw-r--r--regress/sbin/pfctl/pf97.ok4
-rw-r--r--sbin/pfctl/parse.y50
-rw-r--r--sbin/pfctl/pfctl_parser.c18
-rw-r--r--sys/net/pf.c54
-rw-r--r--sys/net/pfvar.h16
-rw-r--r--sys/netinet/in_pcb.c22
-rw-r--r--sys/netinet/in_pcb.h5
-rw-r--r--sys/netinet/ip_input.c5
-rw-r--r--sys/netinet/raw_ip.c13
-rw-r--r--sys/netinet/tcp_input.c4
-rw-r--r--sys/netinet/tcp_usrreq.c4
-rw-r--r--sys/netinet/udp_usrreq.c4
-rw-r--r--sys/sys/mbuf.h4
15 files changed, 187 insertions, 23 deletions
diff --git a/regress/sbin/pfctl/Makefile b/regress/sbin/pfctl/Makefile
index f92d2b6652c..0e50487367f 100644
--- a/regress/sbin/pfctl/Makefile
+++ b/regress/sbin/pfctl/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.202 2008/05/08 00:44:57 mpf Exp $
+# $OpenBSD: Makefile,v 1.203 2008/05/09 02:44:55 markus Exp $
# TARGETS
# pf: feed pfNN.in through pfctl and check wether the output matches pfNN.ok
@@ -15,6 +15,7 @@ PFTESTS=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
PFTESTS+=28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
PFTESTS+=51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
PFTESTS+=74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
+PFTESTS+=97
PFFAIL=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 23 24 25 27
PFFAIL+=28 29 30 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
PFSIMPLE=1 2
diff --git a/regress/sbin/pfctl/pf97.in b/regress/sbin/pfctl/pf97.in
new file mode 100644
index 00000000000..8f3d9415a96
--- /dev/null
+++ b/regress/sbin/pfctl/pf97.in
@@ -0,0 +1,4 @@
+pass in on em0 inet proto tcp from any to any port 220:230 divert-to 127.0.0.1 22
+pass out on em0 inet proto tcp from any to any port 220:230 divert-reply
+pass in on em0 inet proto 103 divert-to 127.0.0.1 103
+pass on em0 inet proto tcp from any to any port 80 divert-to 127.0.0.1 8080
diff --git a/regress/sbin/pfctl/pf97.ok b/regress/sbin/pfctl/pf97.ok
new file mode 100644
index 00000000000..66377f1c422
--- /dev/null
+++ b/regress/sbin/pfctl/pf97.ok
@@ -0,0 +1,4 @@
+pass in on em0 inet proto tcp from any to any port 220:230 flags S/SA keep state divert-to 127.0.0.1 22
+pass out on em0 inet proto tcp from any to any port 220:230 flags S/SA keep state divert-reply
+pass in on em0 inet proto pim all keep state divert-to 127.0.0.1 103
+pass on em0 inet proto tcp from any to any port = www flags S/SA keep state divert-to 127.0.0.1 8080
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index be585533527..2c4a2ce81d7 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.543 2008/05/08 08:08:36 deraadt Exp $ */
+/* $OpenBSD: parse.y,v 1.544 2008/05/09 02:44:54 markus Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -232,6 +232,10 @@ struct filter_opts {
char *match_tag;
u_int8_t match_tag_not;
u_int rtableid;
+ struct {
+ struct node_host *addr;
+ u_int16_t port;
+ } divert;
} filter_opts;
struct antispoof_opts {
@@ -436,6 +440,7 @@ typedef struct {
%token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
%token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH
%token TAGGED TAG IFBOUND FLOATING STATEPOLICY ROUTE SETTOS
+%token DIVERTTO DIVERTREPLY
%token <v.string> STRING
%token <v.number> NUMBER
%token <v.i> PORTBINARY
@@ -2177,6 +2182,30 @@ pfrule : action dir logquick interface route af proto fromto
}
free($9.queues.pqname);
}
+ if ((r.divert.port = htons($9.divert.port))) {
+ if (r.direction == PF_OUT) {
+ if ($9.divert.addr) {
+ yyerror("address specified "
+ "for outgoing divert");
+ YYERROR;
+ }
+ bzero(&r.divert.addr,
+ sizeof(r.divert.addr));
+ } else {
+ if (!$9.divert.addr) {
+ yyerror("no address specified "
+ "for incoming divert");
+ YYERROR;
+ }
+ if ($9.divert.addr->af != r.af) {
+ yyerror("address family "
+ "mismatch for divert");
+ YYERROR;
+ }
+ r.divert.addr =
+ $9.divert.addr->addr.v.a.addr;
+ }
+ }
expand_rule(&r, $4, $5.host, $7, $8.src_os,
$8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
@@ -2293,6 +2322,23 @@ filter_opt : USER uids {
}
filter_opts.rtableid = $2;
}
+ | DIVERTTO STRING number {
+ if ((filter_opts.divert.addr = host($2)) == NULL) {
+ yyerror("could not parse divert address: %s",
+ $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ filter_opts.divert.port = $3;
+ if (!filter_opts.divert.port) {
+ yyerror("invalid divert port: %d", $3);
+ YYERROR;
+ }
+ }
+ | DIVERTREPLY {
+ filter_opts.divert.port = 1; /* some random value */
+ }
;
probability : STRING {
@@ -5173,6 +5219,8 @@ lookup(char *s)
{ "code", CODE},
{ "crop", FRAGCROP},
{ "debug", DEBUG},
+ { "divert-reply", DIVERTREPLY},
+ { "divert-to", DIVERTTO},
{ "drop", DROP},
{ "drop-ovl", FRAGDROP},
{ "dup-to", DUPTO},
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 59dac6f9a60..932eb357b0f 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfctl_parser.c,v 1.236 2008/05/07 06:23:30 markus Exp $ */
+/* $OpenBSD: pfctl_parser.c,v 1.237 2008/05/09 02:44:55 markus Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -983,6 +983,22 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose)
}
if (r->rtableid != -1)
printf(" rtable %u", r->rtableid);
+ if (r->divert.port) {
+ if (PF_AZERO(&r->divert.addr, r->af)) {
+ printf(" divert-reply");
+ } else {
+ /* XXX cut&paste from print_addr */
+ char buf[48];
+
+ printf(" divert-to ");
+ if (inet_ntop(r->af, &r->divert.addr, buf,
+ sizeof(buf)) == NULL)
+ printf("?");
+ else
+ printf("%s", buf);
+ printf(" %u", ntohs(r->divert.port));
+ }
+ }
if (!anchor_call[0] && (r->action == PF_NAT ||
r->action == PF_BINAT || r->action == PF_RDR)) {
printf(" -> ");
diff --git a/sys/net/pf.c b/sys/net/pf.c
index a76f3c7fbe8..42cb24c54f9 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pf.c,v 1.572 2008/05/07 07:07:29 markus Exp $ */
+/* $OpenBSD: pf.c,v 1.573 2008/05/09 02:44:54 markus Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -218,6 +218,7 @@ void pf_set_rt_ifp(struct pf_state *,
struct pf_addr *);
int pf_check_proto_cksum(struct mbuf *, int, int,
u_int8_t, sa_family_t);
+struct pf_divert *pf_get_divert(struct mbuf *);
int pf_addr_wrap_neq(struct pf_addr_wrap *,
struct pf_addr_wrap *);
struct pf_state *pf_find_state(struct pfi_kif *,
@@ -2653,7 +2654,8 @@ pf_socket_lookup(int direction, struct pf_pdesc *pd)
case AF_INET:
inp = in_pcbhashlookup(tb, saddr->v4, sport, daddr->v4, dport);
if (inp == NULL) {
- inp = in_pcblookup_listen(tb, daddr->v4, dport, 0);
+ inp = in_pcblookup_listen(tb, daddr->v4, dport, 0,
+ NULL);
if (inp == NULL)
return (-1);
}
@@ -5456,6 +5458,34 @@ pf_check_proto_cksum(struct mbuf *m, int off, int len, u_int8_t p,
return (0);
}
+struct pf_divert *
+pf_find_divert(struct mbuf *m)
+{
+ struct m_tag *mtag;
+
+ if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL)
+ return (NULL);
+
+ return ((struct pf_divert *)(mtag + 1));
+}
+
+struct pf_divert *
+pf_get_divert(struct mbuf *m)
+{
+ struct m_tag *mtag;
+
+ if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) {
+ mtag = m_tag_get(PACKET_TAG_PF_DIVERT, sizeof(struct pf_divert),
+ M_NOWAIT);
+ if (mtag == NULL)
+ return (NULL);
+ bzero(mtag + 1, sizeof(struct pf_divert));
+ m_tag_prepend(m, mtag);
+ }
+
+ return ((struct pf_divert *)(mtag + 1));
+}
+
#ifdef INET
int
pf_test(int dir, struct ifnet *ifp, struct mbuf **m0,
@@ -5674,6 +5704,16 @@ done:
(ntohl(pd.dst->v4.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST;
+ if (dir == PF_IN && action == PF_PASS && r->divert.port) {
+ struct pf_divert *divert;
+
+ if ((divert = pf_get_divert(m))) {
+ m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED;
+ divert->port = r->divert.port;
+ divert->addr.ipv4 = r->divert.addr.v4;
+ }
+ }
+
if (log) {
struct pf_rule *lr;
@@ -6050,6 +6090,16 @@ done:
IN6_IS_ADDR_LOOPBACK(&pd.dst->v6))
m->m_pkthdr.pf.flags |= PF_TAG_TRANSLATE_LOCALHOST;
+ if (dir == PF_IN && action == PF_PASS && r->divert.port) {
+ struct pf_divert *divert;
+
+ if ((divert = pf_get_divert(m))) {
+ m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED;
+ divert->port = r->divert.port;
+ divert->addr.ipv6 = r->divert.addr.v6;
+ }
+ }
+
if (log) {
struct pf_rule *lr;
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index b186c9b2556..340071c3577 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pfvar.h,v 1.264 2008/05/08 08:05:16 deraadt Exp $ */
+/* $OpenBSD: pfvar.h,v 1.265 2008/05/09 02:44:54 markus Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
@@ -598,6 +598,11 @@ struct pf_rule {
#define PF_FLUSH 0x01
#define PF_FLUSH_GLOBAL 0x02
u_int8_t flush;
+
+ struct {
+ struct pf_addr addr;
+ u_int16_t port;
+ } divert;
};
/* rule flags */
@@ -1284,6 +1289,14 @@ struct pf_tagname {
int ref;
};
+struct pf_divert {
+ union {
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ } addr;
+ u_int16_t port;
+};
+
#define PFFRAG_FRENT_HIWAT 5000 /* Number of fragment entries */
#define PFFRAG_FRAG_HIWAT 1000 /* Number of fragmented packets */
#define PFFRAG_FRCENT_HIWAT 50000 /* Number of fragment cache entries */
@@ -1592,6 +1605,7 @@ extern void pf_addrcpy(struct pf_addr *, struct pf_addr *,
u_int8_t);
void pf_rm_rule(struct pf_rulequeue *,
struct pf_rule *);
+struct pf_divert *pf_find_divert(struct mbuf *);
#ifdef INET
int pf_test(int, struct ifnet *, struct mbuf **, struct ether_header *);
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index ae5c16d9994..e3e8d58bdfa 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.c,v 1.94 2008/05/07 05:14:21 claudio Exp $ */
+/* $OpenBSD: in_pcb.c,v 1.95 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */
/*
@@ -68,6 +68,8 @@
* Research Laboratory (NRL).
*/
+#include "pf.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
@@ -80,6 +82,7 @@
#include <net/if.h>
#include <net/route.h>
+#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -1009,17 +1012,24 @@ in6_pcbhashlookup(table, faddr, fport_arg, laddr, lport_arg)
* *.* <-> *.lport
*/
struct inpcb *
-in_pcblookup_listen(table, laddr, lport_arg, reverse)
- struct inpcbtable *table;
- struct in_addr laddr;
- u_int lport_arg;
- int reverse;
+in_pcblookup_listen(struct inpcbtable *table, struct in_addr laddr,
+ u_int lport_arg, int reverse, struct mbuf *m)
{
struct inpcbhead *head;
struct in_addr *key1, *key2;
struct inpcb *inp;
u_int16_t lport = lport_arg;
+#if NPF
+ if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
+ struct pf_divert *divert;
+
+ if ((divert = pf_find_divert(m)) == NULL)
+ return (NULL);
+ key1 = key2 = &divert->addr.ipv4;
+ lport = divert->port;
+ } else
+#endif
if (reverse) {
key1 = &zeroin_addr;
key2 = &laddr;
diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h
index 3f7d2deab79..14ccabc67f2 100644
--- a/sys/netinet/in_pcb.h
+++ b/sys/netinet/in_pcb.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: in_pcb.h,v 1.59 2007/02/22 20:23:07 millert Exp $ */
+/* $OpenBSD: in_pcb.h,v 1.60 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */
/*
@@ -247,7 +247,8 @@ struct inpcb *
in_pcbhashlookup(struct inpcbtable *, struct in_addr,
u_int, struct in_addr, u_int);
struct inpcb *
- in_pcblookup_listen(struct inpcbtable *, struct in_addr, u_int, int);
+ in_pcblookup_listen(struct inpcbtable *, struct in_addr, u_int, int,
+ struct mbuf *);
#ifdef INET6
struct inpcb *
in6_pcbhashlookup(struct inpcbtable *, struct in6_addr *,
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 05b65ae94ef..93007462da2 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip_input.c,v 1.158 2008/04/24 11:36:38 dlg Exp $ */
+/* $OpenBSD: ip_input.c,v 1.159 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */
/*
@@ -389,6 +389,9 @@ ipv4_input(m)
(ia->ia_ifp->if_flags & IFF_UP))
goto ours;
+ if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED)
+ goto ours;
+
if (IN_MULTICAST(ip->ip_dst.s_addr)) {
struct in_multi *inm;
#ifdef MROUTING
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index a92c9870bf3..3b621e57678 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: raw_ip.c,v 1.41 2008/05/02 06:49:32 ckuethe Exp $ */
+/* $OpenBSD: raw_ip.c,v 1.42 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */
/*
@@ -77,6 +77,7 @@
#include <net/if.h>
#include <net/route.h>
+#include <net/pfvar.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
@@ -131,6 +132,16 @@ rip_input(struct mbuf *m, ...)
#endif
if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
continue;
+#if NPF
+ if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) {
+ struct pf_divert *divert;
+
+ if ((divert = pf_find_divert(m)) == NULL)
+ continue;
+ if (inp->inp_laddr.s_addr != divert->addr.ipv4.s_addr)
+ continue;
+ } else
+#endif
if (inp->inp_laddr.s_addr &&
inp->inp_laddr.s_addr != ip->ip_dst.s_addr)
continue;
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index fa3bfc2eeff..ab26e50f401 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_input.c,v 1.213 2008/05/06 08:47:35 markus Exp $ */
+/* $OpenBSD: tcp_input.c,v 1.214 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */
/*
@@ -620,7 +620,7 @@ findpcb:
#endif /* INET6 */
case AF_INET:
inp = in_pcblookup_listen(&tcbtable,
- ip->ip_dst, th->th_dport, inpl_flags);
+ ip->ip_dst, th->th_dport, inpl_flags, m);
break;
}
/*
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 2e1750e00af..165bcdbecb3 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tcp_usrreq.c,v 1.95 2007/12/13 20:00:53 reyk Exp $ */
+/* $OpenBSD: tcp_usrreq.c,v 1.96 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */
/*
@@ -867,7 +867,7 @@ tcp_ident(oldp, oldlenp, newp, newlen, dodrop)
#endif
case AF_INET:
inp = in_pcblookup_listen(&tcbtable,
- lin->sin_addr, lin->sin_port, 0);
+ lin->sin_addr, lin->sin_port, 0, NULL);
break;
}
}
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 2244f620efb..50d05b14710 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_usrreq.c,v 1.116 2008/05/02 06:49:32 ckuethe Exp $ */
+/* $OpenBSD: udp_usrreq.c,v 1.117 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
@@ -549,7 +549,7 @@ udp_input(struct mbuf *m, ...)
} else
#endif /* INET6 */
inp = in_pcblookup_listen(&udbtable,
- ip->ip_dst, uh->uh_dport, inpl_reverse);
+ ip->ip_dst, uh->uh_dport, inpl_reverse, m);
if (inp == 0) {
udpstat.udps_noport++;
if (m->m_flags & (M_BCAST | M_MCAST)) {
diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index ada55c24ede..d247ff31f44 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: mbuf.h,v 1.96 2007/11/28 14:04:26 deraadt Exp $ */
+/* $OpenBSD: mbuf.h,v 1.97 2008/05/09 02:44:54 markus Exp $ */
/* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */
/*
@@ -87,6 +87,7 @@ struct pkthdr_pf {
#define PF_TAG_GENERATED 0x01
#define PF_TAG_FRAGCACHE 0x02
#define PF_TAG_TRANSLATE_LOCALHOST 0x04
+#define PF_TAG_DIVERTED 0x08
/* record/packet header in first mbuf of chain; valid if M_PKTHDR set */
struct pkthdr {
@@ -507,6 +508,7 @@ struct m_tag *m_tag_next(struct mbuf *, struct m_tag *);
#define PACKET_TAG_GRE 9 /* GRE processing done */
#define PACKET_TAG_IN_PACKET_CHECKSUM 10 /* NIC checksumming done */
#define PACKET_TAG_DLT 17 /* data link layer type */
+#define PACKET_TAG_PF_DIVERT 18 /* pf(4) diverted packet */
#ifdef MBTYPES
int mbtypes[] = { /* XXX */