diff options
-rw-r--r-- | regress/sbin/pfctl/Makefile | 3 | ||||
-rw-r--r-- | regress/sbin/pfctl/pf97.in | 4 | ||||
-rw-r--r-- | regress/sbin/pfctl/pf97.ok | 4 | ||||
-rw-r--r-- | sbin/pfctl/parse.y | 50 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 18 | ||||
-rw-r--r-- | sys/net/pf.c | 54 | ||||
-rw-r--r-- | sys/net/pfvar.h | 16 | ||||
-rw-r--r-- | sys/netinet/in_pcb.c | 22 | ||||
-rw-r--r-- | sys/netinet/in_pcb.h | 5 | ||||
-rw-r--r-- | sys/netinet/ip_input.c | 5 | ||||
-rw-r--r-- | sys/netinet/raw_ip.c | 13 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 4 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 4 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 4 | ||||
-rw-r--r-- | sys/sys/mbuf.h | 4 |
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 */ |