diff options
author | Michele Marchetto <michele@cvs.openbsd.org> | 2009-10-04 16:08:38 +0000 |
---|---|---|
committer | Michele Marchetto <michele@cvs.openbsd.org> | 2009-10-04 16:08:38 +0000 |
commit | 82b30916c9515794d7aca77e8c937fc5e0455586 (patch) | |
tree | ce06c3cb91a134e4d7d4cb69f04155cf3c8ba2dc | |
parent | dd5c2f75fe5e092eb892c438f7f87faa2e57fcb6 (diff) |
Add (again) support for divert sockets. They allow you to:
- queue packets from pf(4) to a userspace application
- reinject packets from the application into the kernel stack.
The divert socket can be bound to a special "divert port" and will
receive every packet diverted to that port by pf(4).
The pf syntax is pretty simple, e.g.:
pass on em0 inet proto tcp from any to any port 80 divert-packet port 1
A lot of discussion have happened since my last commit that resulted
in many changes and improvements.
I would *really* like to thank everyone who took part in the discussion
especially canacar@ who spotted out which are the limitations of this approach.
OpenBSD divert(4) is meant to be compatible with software running on
top of FreeBSD's divert sockets even though they are pretty different and will
become even more with time.
discusses with many, but mainly reyk@ canacar@ deraadt@ dlg@ claudio@ beck@
tested by reyk@ and myself
ok reyk@ claudio@ beck@
manpage help and ok by jmc@
-rw-r--r-- | etc/protocols | 3 | ||||
-rw-r--r-- | lib/libc/gen/sysctl.3 | 10 | ||||
-rw-r--r-- | sbin/pfctl/parse.y | 27 | ||||
-rw-r--r-- | sbin/pfctl/pfctl_parser.c | 4 | ||||
-rw-r--r-- | sbin/sysctl/sysctl.8 | 8 | ||||
-rw-r--r-- | sbin/sysctl/sysctl.c | 18 | ||||
-rw-r--r-- | share/man/man4/Makefile | 4 | ||||
-rw-r--r-- | share/man/man4/divert.4 | 80 | ||||
-rw-r--r-- | share/man/man5/pf.conf.5 | 15 | ||||
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/net/pf.c | 20 | ||||
-rw-r--r-- | sys/net/pfvar.h | 6 | ||||
-rw-r--r-- | sys/netinet/in.h | 26 | ||||
-rw-r--r-- | sys/netinet/in_proto.c | 14 | ||||
-rw-r--r-- | sys/netinet/ip_divert.c | 335 | ||||
-rw-r--r-- | sys/netinet/ip_divert.h | 65 | ||||
-rw-r--r-- | sys/sys/mbuf.h | 3 | ||||
-rw-r--r-- | usr.bin/netstat/inet.c | 40 | ||||
-rw-r--r-- | usr.bin/netstat/main.c | 5 | ||||
-rw-r--r-- | usr.bin/netstat/netstat.h | 3 |
20 files changed, 658 insertions, 31 deletions
diff --git a/etc/protocols b/etc/protocols index d522dc536d5..4458af375e6 100644 --- a/etc/protocols +++ b/etc/protocols @@ -1,7 +1,7 @@ # # Internet (IP) protocols # -# $OpenBSD: protocols,v 1.21 2004/05/07 16:27:23 henning Exp $ +# $OpenBSD: protocols,v 1.22 2009/10/04 16:08:37 michele Exp $ # # Updated based on RFC 1340, Assigned Numbers (July 1992). # See also http://www.iana.org/assignments/protocol-numbers @@ -142,3 +142,4 @@ fc 133 FC # Fibre Channel rsvp-e2e-ignore 134 RSVP-E2E-IGNORE pfsync 240 PFSYNC # PF Synchronization reserved 255 Reserved # +divert 258 DIVERT # Divert pseudo-protocol [non IANA] diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3 index 439a8325751..e75bb503167 100644 --- a/lib/libc/gen/sysctl.3 +++ b/lib/libc/gen/sysctl.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.3,v 1.189 2009/09/08 17:52:17 michele Exp $ +.\" $OpenBSD: sysctl.3,v 1.190 2009/10/04 16:08:37 michele Exp $ .\" .\" Copyright (c) 1993 .\" The Regents of the University of California. All rights reserved. @@ -27,7 +27,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: September 8 2009 $ +.Dd $Mdocdate: October 4 2009 $ .Dt SYSCTL 3 .Os .Sh NAME @@ -1128,6 +1128,8 @@ The currently defined protocols and names are: .It carp allow integer yes .It carp log integer yes .It carp preempt integer yes +.It divert recvspace integer yes +.It divert sendspace integer yes .It esp enable integer yes .It esp udpencap integer yes .It esp udpencap_port integer yes @@ -1240,6 +1242,10 @@ another active master. If set to any other value, carp will become master of the virtual host if it believes it can send advertisements more frequently than the current master. Disabled by default. +.It Li divert.recvspace +Returns the default divert receive buffer size. +.It Li divert.sendspace +Returns the default divert send buffer size. .It Li esp.enable If set to 1, enable the Encapsulating Security Payload .Pq ESP diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index 31553f93497..c6d1a164bb5 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.569 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: parse.y,v 1.570 2009/10/04 16:08:37 michele Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -266,7 +266,7 @@ struct filter_opts { struct { struct node_host *addr; u_int16_t port; - } divert; + } divert, divert_packet; struct redirspec nat; struct redirspec rdr; @@ -461,7 +461,7 @@ int parseport(char *, struct range *r, int); %token STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE %token MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW %token TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS -%token DIVERTTO DIVERTREPLY NATTO RDRTO +%token DIVERTTO DIVERTREPLY DIVERTPACKET NATTO RDRTO %token <v.string> STRING %token <v.number> NUMBER %token <v.i> PORTBINARY @@ -2094,6 +2094,7 @@ pfrule : action dir logquick interface af proto fromto $8.divert.addr->addr.v.a.addr; } } + r.divert_packet.port = $8.divert_packet.port; expand_rule(&r, 0, $4, &$8.nat, &$8.rdr, $6, $7.src_os, $7.src.host, $7.src.port, $7.dst.host, $7.dst.port, @@ -2227,6 +2228,21 @@ filter_opt : USER uids { | DIVERTREPLY { filter_opts.divert.port = 1; /* some random value */ } + | DIVERTPACKET PORT number { + /* + * If IP reassembly was not turned off, also + * forcibly enable TCP reassembly by default. + */ + if (pf->reassemble & PF_REASS_ENABLED) + filter_opts.marker |= FOM_SCRUB_TCP; + + if ($3 < 1 || $3 > 65535) { + yyerror("invalid divert port"); + YYERROR; + } + + filter_opts.divert_packet.port = htons($3); + } | SCRUB '(' scrub_opts ')' { filter_opts.nodf = $3.nodf; filter_opts.minttl = $3.minttl; @@ -3914,6 +3930,10 @@ rule_consistent(struct pf_rule *r, int anchor_call) yyerror("divert is not supported on match rules"); problems++; } + if (r->divert_packet.port) { + yyerror("divert is not supported on match rules"); + problems++; + } if (r->rt) { yyerror("route-to, reply-to, dup-to and fastroute " "must not be used on match rules"); @@ -4836,6 +4856,7 @@ lookup(char *s) { "code", CODE}, { "crop", FRAGCROP}, { "debug", DEBUG}, + { "divert-packet", DIVERTPACKET}, { "divert-reply", DIVERTREPLY}, { "divert-to", DIVERTTO}, { "drop", DROP}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 5ec44787ac6..72e3739cd0e 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfctl_parser.c,v 1.248 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: pfctl_parser.c,v 1.249 2009/10/04 16:08:37 michele Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -1015,6 +1015,8 @@ print_rule(struct pf_rule *r, const char *anchor_call, int verbose) printf(" port %u", ntohs(r->divert.port)); } } + if (r->divert_packet.port) + printf(" divert-packet port %u", ntohs(r->divert_packet.port)); if (!anchor_call[0] && !TAILQ_EMPTY(&r->nat.list)) { printf (" nat-to "); print_pool(&r->nat, r->nat.proxy_port[0], diff --git a/sbin/sysctl/sysctl.8 b/sbin/sysctl/sysctl.8 index 2dc2a555fd3..80898888e41 100644 --- a/sbin/sysctl/sysctl.8 +++ b/sbin/sysctl/sysctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sysctl.8,v 1.151 2009/09/08 17:52:17 michele Exp $ +.\" $OpenBSD: sysctl.8,v 1.152 2009/10/04 16:08:37 michele Exp $ .\" $NetBSD: sysctl.8,v 1.4 1995/09/30 07:12:49 thorpej Exp $ .\" .\" Copyright (c) 1993 @@ -30,7 +30,7 @@ .\" .\" @(#)sysctl.8 8.2 (Berkeley) 5/9/95 .\" -.Dd $Mdocdate: September 8 2009 $ +.Dd $Mdocdate: October 4 2009 $ .Dt SYSCTL 8 .Os .Sh NAME @@ -210,6 +210,8 @@ not all of the variables are relevant to all architectures. .It vm.maxslp integer no .It vm.uspace integer no .It fs.posix.setuid integer yes +.It net.inet.divert.recvspace integer yes +.It net.inet.divert.sendspace integer yes .It net.inet.ip.forwarding integer yes .It net.inet.ip.redirect integer yes .It net.inet.ip.ttl integer yes @@ -444,6 +446,8 @@ definitions for third level virtual memory identifiers .It Aq Pa netinet/in.h definitions for third level IPv4/v6 identifiers and fourth level IPv4/v6 identifiers +.It Aq Pa netinet/ip_divert.h +definitions for fourth level divert identifiers .It Aq Pa netinet/icmp_var.h definitions for fourth level ICMP identifiers .It Aq Pa netinet6/icmp6.h diff --git a/sbin/sysctl/sysctl.c b/sbin/sysctl/sysctl.c index 3ce2fd6f8ca..8ca864e29b0 100644 --- a/sbin/sysctl/sysctl.c +++ b/sbin/sysctl/sysctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sysctl.c,v 1.164 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: sysctl.c,v 1.165 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: sysctl.c,v 1.9 1995/09/30 07:12:50 thorpej Exp $ */ /* @@ -40,7 +40,7 @@ static const char copyright[] = #if 0 static const char sccsid[] = "@(#)sysctl.c 8.5 (Berkeley) 5/9/95"; #else -static const char rcsid[] = "$OpenBSD: sysctl.c,v 1.164 2009/09/08 17:52:17 michele Exp $"; +static const char rcsid[] = "$OpenBSD: sysctl.c,v 1.165 2009/10/04 16:08:37 michele Exp $"; #endif #endif /* not lint */ @@ -82,6 +82,7 @@ static const char rcsid[] = "$OpenBSD: sysctl.c,v 1.164 2009/09/08 17:52:17 mich #include <netinet/ip_gre.h> #include <netinet/ip_ipcomp.h> #include <netinet/ip_carp.h> +#include <netinet/ip_divert.h> #include <net/pfvar.h> #include <net/if_pfsync.h> @@ -549,7 +550,8 @@ parse(char *string, int flags) (mib[2] == IPPROTO_IPCOMP && mib[3] == IPCOMPCTL_STATS) || (mib[2] == IPPROTO_ICMP && mib[3] == ICMPCTL_STATS) || (mib[2] == IPPROTO_CARP && mib[3] == CARPCTL_STATS) || - (mib[2] == IPPROTO_PFSYNC && mib[3] == PFSYNCCTL_STATS)) { + (mib[2] == IPPROTO_PFSYNC && mib[3] == PFSYNCCTL_STATS) || + (mib[2] == IPPROTO_DIVERT && mib[3] == DIVERTCTL_STATS)) { if (flags == 0) return; warnx("use netstat to view %s information", @@ -1322,6 +1324,7 @@ struct ctlname mobileipname[] = MOBILEIPCTL_NAMES; struct ctlname ipcompname[] = IPCOMPCTL_NAMES; struct ctlname carpname[] = CARPCTL_NAMES; struct ctlname pfsyncname[] = PFSYNCCTL_NAMES; +struct ctlname divertname[] = DIVERTCTL_NAMES; struct ctlname bpfname[] = CTL_NET_BPF_NAMES; struct ctlname ifqname[] = CTL_IFQ_NAMES; struct list inetlist = { inetname, IPPROTO_MAXID }; @@ -1576,6 +1579,15 @@ struct list inetvars[] = { { 0, 0 }, { 0, 0 }, { pfsyncname, PFSYNCCTL_MAXID }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { divertname, DIVERTCTL_MAXID }, }; struct list bpflist = { bpfname, NET_BPF_MAXID }; struct list ifqlist = { ifqname, IFQCTL_MAXID }; diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 0ccde289aba..589db098346 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.492 2009/10/03 20:14:34 kettenis Exp $ +# $OpenBSD: Makefile,v 1.493 2009/10/04 16:08:37 michele Exp $ MAN= aac.4 ac97.4 acphy.4 \ acpi.4 acpiac.4 acpiasus.4 acpibat.4 acpibtn.4 acpicpu.4 acpidock.4 \ @@ -15,7 +15,7 @@ MAN= aac.4 ac97.4 acphy.4 \ boca.4 bpf.4 brgphy.4 bridge.4 btkbd.4 btms.4 btsco.4 bwi.4 \ cac.4 cas.4 cardbus.4 carp.4 ccd.4 cd.4 cdce.4 cdcef.4 che.4 cfxga.4 \ ch.4 ciphy.4 ciss.4 clcs.4 clct.4 cmpci.4 cnw.4 \ - com.4 crypto.4 cue.4 cy.4 cz.4 dc.4 dcphy.4 ddb.4 de.4 dpt.4 \ + com.4 crypto.4 cue.4 cy.4 cz.4 dc.4 dcphy.4 ddb.4 de.4 divert.4 dpt.4 \ drm.4 eap.4 ec.4 eephy.4 ef.4 eg.4 ehci.4 eisa.4 el.4 em.4 \ emu.4 enc.4 endrun.4 envy.4 ep.4 epic.4 esa.4 \ eso.4 ess.4 et.4 etphy.4 ex.4 exphy.4 \ diff --git a/share/man/man4/divert.4 b/share/man/man4/divert.4 new file mode 100644 index 00000000000..df96bbac1b9 --- /dev/null +++ b/share/man/man4/divert.4 @@ -0,0 +1,80 @@ +.\" $OpenBSD: divert.4,v 1.3 2009/10/04 16:08:37 michele Exp $ +.\" +.\" Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" + +.Dd $Mdocdate: October 4 2009 $ +.Dt DIVERT 4 +.Os +.Sh NAME +.Nm divert +.Nd Kernel packet diversion mechanism +.Sh SYNOPSIS +.Fd #include <sys/types.h> +.Fd #include <sys/socket.h> +.Fd #include <netinet/in.h> +.Ft int +.Fn socket AF_INET SOCK_RAW IPPROTO_DIVERT +.Sh DESCRIPTION +Divert sockets can be bound through +.Xr bind 2 +to a divert port and they will receive every packet +diverted to that port by +.Xr pf 4 . +Divert ports have their own number space, completely +separated from +.Xr tcp 4 +and +.Xr udp 4 +ones. +Consult +.Xr pf.conf 5 +for the correct syntax. +Packets can also be reinjected into the divert socket, in which case they +re-enter kernel packet processing skipping +.Xr pf 4 +filters, avoiding loops. +.Pp +Diverted packets can be read via +.Xr read 2 , +.Xr recv 2 , +or +.Xr recvfrom 2 +from the divert socket. +.Xr pf 4 +will reassemble the IP packets by default before sending them to the divert +socket. +In addition, TCP reassembling is enabled for packet divert rules, see +.Xr pf.conf 5 +for details. +Writing to a divert socket can be achieved using +.Xr sendto 2 +and it will skip +.Xr pf 4 +filters to avoid loops. +.Pp +If +.Xr pf 4 +diverts packets but there are no divert sockets listening, +the packets are dropped. +.Sh SEE ALSO +.Xr socket 2 , +.Xr ip 4 , +.Xr pf.conf 5 , +.Sh HISTORY +The +.Nm +protocol first appeared in +.Ox 4.7. diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 index 8c51829f3ca..51c97d61fe7 100644 --- a/share/man/man5/pf.conf.5 +++ b/share/man/man5/pf.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pf.conf.5,v 1.459 2009/09/25 14:08:04 jmc Exp $ +.\" $OpenBSD: pf.conf.5,v 1.460 2009/10/04 16:08:37 michele Exp $ .\" .\" Copyright (c) 2002, Daniel Hartmeier .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: September 25 2009 $ +.Dd $Mdocdate: October 4 2009 $ .Dt PF.CONF 5 .Os .Sh NAME @@ -436,6 +436,17 @@ The implicit rule that is used when a packet does not match any rules does not allow IP options. .Pp +.It Ar divert-packet Aq Ar port +Used to send matching packets to +.Xr divert 4 +sockets bound to port +.Ar port . +If the default option of fragment reassembly is enabled, scrubbing with +.Ar reassemble tcp +is also enabled for +.Ar divert-packet +rules. +.Pp .It Ar divert-reply Used to receive replies for sockets that are bound to addresses which are not local to the machine. diff --git a/sys/conf/files b/sys/conf/files index 3a56607f91e..d068b1c666d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.477 2009/10/03 19:51:53 kettenis Exp $ +# $OpenBSD: files,v 1.478 2009/10/04 16:08:37 michele Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -831,6 +831,7 @@ file netinet/igmp.c inet file netinet/in.c inet file netinet/in_pcb.c inet file netinet/in_proto.c inet +file netinet/ip_divert.c inet & pf file netinet/ip_icmp.c inet file netinet/ip_id.c inet file netinet/ip_input.c inet diff --git a/sys/net/pf.c b/sys/net/pf.c index f8994dc86ec..269dc8c2581 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.662 2009/09/16 12:28:19 henning Exp $ */ +/* $OpenBSD: pf.c,v 1.663 2009/10/04 16:08:37 michele Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -75,6 +75,7 @@ #include <netinet/udp_var.h> #include <netinet/icmp_var.h> #include <netinet/if_ether.h> +#include <netinet/ip_divert.h> #include <dev/rndvar.h> #include <net/pfvar.h> @@ -5382,6 +5383,9 @@ pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, if (m->m_pkthdr.pf.flags & PF_TAG_GENERATED) return (PF_PASS); + if (m->m_pkthdr.pf.flags & PF_TAG_DIVERTED_PACKET) + return (PF_PASS); + /* packet reassembly here if 1) enabled 2) we deal with a fragment */ h = mtod(m, struct ip *); if (pf_status.reass && (h->ip_off & htons(IP_MF | IP_OFFMASK)) && @@ -5601,6 +5605,15 @@ done: } } + if (action == PF_PASS && r->divert_packet.port) { + struct pf_divert *divert; + + if ((divert = pf_get_divert(m))) + divert->port = r->divert_packet.port; + + action = PF_DIVERT; + } + if (log) { struct pf_rule *lr; struct pf_rule_item *ri; @@ -5682,6 +5695,11 @@ done: *m0 = NULL; action = PF_PASS; break; + case PF_DIVERT: + divert_packet(m, dir); + *m0 = NULL; + action = PF_PASS; + break; default: /* pf_route can free the mbuf causing *m0 to become NULL */ if (r->rt) diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index d0328ba566a..9559bec1fa8 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar.h,v 1.293 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: pfvar.h,v 1.294 2009/10/04 16:08:37 michele Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -60,7 +60,7 @@ struct ip6_hdr; enum { PF_INOUT, PF_IN, PF_OUT }; enum { PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT, PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER, - PF_MATCH }; + PF_MATCH, PF_DIVERT }; enum { PF_RULESET_FILTER, PF_RULESET_NAT, PF_RULESET_BINAT, PF_RULESET_RDR, PF_RULESET_MAX }; enum { PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT, @@ -622,7 +622,7 @@ struct pf_rule { struct { struct pf_addr addr; u_int16_t port; - } divert; + } divert, divert_packet; }; /* rule flags */ diff --git a/sys/netinet/in.h b/sys/netinet/in.h index 3e9669682a2..bd7a2289ca0 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in.h,v 1.81 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: in.h,v 1.82 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */ /* @@ -78,6 +78,10 @@ #define IPPROTO_MAX 256 +/* Only used internally, so it can be outside the range of valid IP protocols */ +#define IPPROTO_DIVERT 258 /* Divert sockets */ + + /* * From FreeBSD: * @@ -326,7 +330,7 @@ struct ip_mreq { * Third level is protocol number. * Fourth level is desired variable within that protocol. */ -#define IPPROTO_MAXID (IPPROTO_PFSYNC + 1) /* don't list to IPPROTO_MAX */ +#define IPPROTO_MAXID (IPPROTO_DIVERT + 1) /* don't list to IPPROTO_MAX */ #define CTL_IPPROTO_NAMES { \ { "ip", CTLTYPE_NODE }, \ @@ -570,6 +574,24 @@ struct ip_mreq { { 0, 0 }, \ { 0, 0 }, \ { "pfsync", CTLTYPE_NODE }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { 0, 0 }, \ + { "divert", CTLTYPE_NODE }, \ } /* diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c index e76139e3074..c90b80afa61 100644 --- a/sys/netinet/in_proto.c +++ b/sys/netinet/in_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_proto.c,v 1.50 2009/09/08 17:52:17 michele Exp $ */ +/* $OpenBSD: in_proto.c,v 1.51 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: in_proto.c,v 1.14 1996/02/18 18:58:32 christos Exp $ */ /* @@ -171,6 +171,11 @@ #include <net/if_pfsync.h> #endif +#include "pf.h" +#if NPF > 0 +#include <netinet/ip_divert.h> +#endif + extern struct domain inetdomain; struct protosw inetsw[] = { @@ -286,6 +291,13 @@ struct protosw inetsw[] = { 0, 0, 0, 0, pfsync_sysctl }, #endif /* NPFSYNC > 0 */ +#if NPF > 0 +{ SOCK_RAW, &inetdomain, IPPROTO_DIVERT, PR_ATOMIC|PR_ADDR, + divert_input, 0, 0, rip_ctloutput, + divert_usrreq, + divert_init, 0, 0, 0, divert_sysctl +}, +#endif /* NPF > 0 */ /* raw wildcard */ { SOCK_RAW, &inetdomain, 0, PR_ATOMIC|PR_ADDR, rip_input, rip_output, 0, rip_ctloutput, diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c new file mode 100644 index 00000000000..b47b0059845 --- /dev/null +++ b/sys/netinet/ip_divert.c @@ -0,0 +1,335 @@ +/* $OpenBSD: ip_divert.c,v 1.3 2009/10/04 16:08:37 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/route.h> +#include <net/netisr.h> +#include <net/pfvar.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/ip_var.h> +#include <netinet/in_pcb.h> +#include <netinet/ip_divert.h> + +struct inpcbtable divbtable; +struct divstat divstat; + +#ifndef DIVERT_SENDSPACE +#define DIVERT_SENDSPACE (65536 + 100) +#endif +u_int divert_sendspace = DIVERT_SENDSPACE; +#ifndef DIVERT_RECVSPACE +#define DIVERT_RECVSPACE (65536 + 100) +#endif +u_int divert_recvspace = DIVERT_RECVSPACE; + +#ifndef DIVERTHASHSIZE +#define DIVERTHASHSIZE 128 +#endif + +int *divertctl_vars[DIVERTCTL_MAXID] = DIVERTCTL_VARS; + +int divbhashsize = DIVERTHASHSIZE; + +void divert_detach(struct inpcb *); + +void +divert_init() +{ + in_pcbinit(&divbtable, divbhashsize); +} + +void +divert_input(struct mbuf *m, ...) +{ + m_freem(m); +} + +int +divert_output(struct mbuf *m, ...) +{ + struct inpcb *inp; + struct ifqueue *inq; + struct mbuf *nam, *control; + struct sockaddr_in *sin; + struct socket *so; + struct ifaddr *ifa; + int s, error = 0; + va_list ap; + + va_start(ap, m); + inp = va_arg(ap, struct inpcb *); + nam = va_arg(ap, struct mbuf *); + control = va_arg(ap, struct mbuf *); + va_end(ap); + + m->m_pkthdr.rcvif = NULL; + m->m_nextpkt = NULL; + m->m_pkthdr.rdomain = inp->inp_rdomain; + + if (control) + m_freem(control); + + sin = mtod(nam, struct sockaddr_in *); + so = inp->inp_socket; + + m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET; + + if (sin->sin_addr.s_addr != INADDR_ANY) { + ifa = ifa_ifwithaddr((struct sockaddr *)sin, 0); + if (ifa == NULL) { + divstat.divs_errors++; + m_freem(m); + return (EADDRNOTAVAIL); + } + m->m_pkthdr.rcvif = ifa->ifa_ifp; + + inq = &ipintrq; + + s = splnet(); + IF_INPUT_ENQUEUE(inq, m); + schednetisr(NETISR_IP); + splx(s); + } else { + error = ip_output(m, (void *)NULL, &inp->inp_route, + ((so->so_options & SO_DONTROUTE) ? IP_ROUTETOIF : 0) + | IP_ALLOWBROADCAST | IP_RAWOUTPUT, (void *)NULL, + (void *)NULL); + } + + divstat.divs_opackets++; + return (error); +} + +void +divert_packet(struct mbuf *m, int dir) +{ + struct inpcb *inp; + struct socket *sa = NULL; + struct sockaddr_in addr; + struct pf_divert *pd; + + divstat.divs_ipackets++; + + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == NULL) { + divstat.divs_errors++; + return; + } + + pd = pf_find_divert(m); + if (pd == NULL) { + divstat.divs_errors++; + m_freem(m); + return; + } + + bzero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_len = sizeof(addr); + + if (dir == PF_IN) { + struct ifaddr *ifa; + struct ifnet *ifp; + + ifp = m->m_pkthdr.rcvif; + TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + addr.sin_addr.s_addr = ((struct sockaddr_in *) + ifa->ifa_addr)->sin_addr.s_addr; + break; + } + } + + CIRCLEQ_FOREACH(inp, &divbtable.inpt_queue, inp_queue) { + if (inp->inp_lport != pd->port) + continue; + + sa = inp->inp_socket; + if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&addr, + m, NULL) == 0) { + divstat.divs_fullsock++; + m_freem(m); + return; + } else + sorwakeup(inp->inp_socket); + break; + } + + if (sa == NULL) { + divstat.divs_noport++; + m_freem(m); + } +} + +/*ARGSUSED*/ +int +divert_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *addr, + struct mbuf *control, struct proc *p) +{ + struct inpcb *inp = sotoinpcb(so); + int error = 0; + int s; + + if (req == PRU_CONTROL) { + return (in_control(so, (u_long)m, (caddr_t)addr, + (struct ifnet *)control)); + } + if (inp == NULL && req != PRU_ATTACH) { + error = EINVAL; + goto release; + } + switch (req) { + + case PRU_ATTACH: + if (inp != NULL) { + error = EINVAL; + break; + } + if ((so->so_state & SS_PRIV) == 0) { + error = EACCES; + break; + } + s = splsoftnet(); + error = in_pcballoc(so, &divbtable); + splx(s); + if (error) + break; + + error = soreserve(so, divert_sendspace, divert_recvspace); + if (error) + break; + ((struct inpcb *) so->so_pcb)->inp_flags |= INP_HDRINCL; + break; + + case PRU_DETACH: + divert_detach(inp); + break; + + case PRU_BIND: + s = splsoftnet(); + error = in_pcbbind(inp, addr, p); + splx(s); + break; + + case PRU_SHUTDOWN: + socantsendmore(so); + break; + + case PRU_SEND: + return (divert_output(m, inp, addr, control)); + + case PRU_ABORT: + soisdisconnected(so); + divert_detach(inp); + break; + + case PRU_SOCKADDR: + in_setsockaddr(inp, addr); + break; + + case PRU_PEERADDR: + in_setpeeraddr(inp, addr); + break; + + case PRU_SENSE: + return (0); + + case PRU_LISTEN: + case PRU_CONNECT: + case PRU_CONNECT2: + case PRU_ACCEPT: + case PRU_DISCONNECT: + case PRU_SENDOOB: + case PRU_FASTTIMO: + case PRU_SLOWTIMO: + case PRU_PROTORCV: + case PRU_PROTOSEND: + error = EOPNOTSUPP; + break; + + case PRU_RCVD: + case PRU_RCVOOB: + return (EOPNOTSUPP); /* do not free mbuf's */ + + default: + panic("divert_usrreq"); + } + +release: + if (control) { + m_freem(control); + } + if (m) + m_freem(m); + return (error); +} + +void +divert_detach(struct inpcb *inp) +{ + int s = splsoftnet(); + + in_pcbdetach(inp); + splx(s); +} + +/* + * Sysctl for divert variables. + */ +int +divert_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, + size_t newlen) +{ + /* All sysctl names at this level are terminal. */ + if (namelen != 1) + return (ENOTDIR); + + switch (name[0]) { + case DIVERTCTL_SENDSPACE: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &divert_sendspace)); + case DIVERTCTL_RECVSPACE: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &divert_recvspace)); + case DIVERTCTL_STATS: + if (newp != NULL) + return (EPERM); + return (sysctl_struct(oldp, oldlenp, newp, newlen, + &divstat, sizeof(divstat))); + default: + if (name[0] < DIVERTCTL_MAXID) + return sysctl_int_arr(divertctl_vars, name, namelen, + oldp, oldlenp, newp, newlen); + + return (ENOPROTOOPT); + } + /* NOTREACHED */ +} diff --git a/sys/netinet/ip_divert.h b/sys/netinet/ip_divert.h new file mode 100644 index 00000000000..4e1b05f7695 --- /dev/null +++ b/sys/netinet/ip_divert.h @@ -0,0 +1,65 @@ +/* $OpenBSD: ip_divert.h,v 1.3 2009/10/04 16:08:37 michele Exp $ */ + +/* + * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IP_DIVERT_H_ +#define _IP_DIVERT_H_ + +struct divstat { + u_long divs_ipackets; /* total input packets */ + u_long divs_noport; /* no socket on port */ + u_long divs_fullsock; /* not delivered, input socket full */ + u_long divs_opackets; /* total output packets */ + u_long divs_errors; /* generic errors */ +}; + +/* + * Names for divert sysctl objects + */ +#define DIVERTCTL_RECVSPACE 1 /* receive buffer space */ +#define DIVERTCTL_SENDSPACE 2 /* send buffer space */ +#define DIVERTCTL_STATS 3 /* divert statistics */ +#define DIVERTCTL_MAXID 4 + +#define DIVERTCTL_NAMES { \ + { 0, 0 }, \ + { "recvspace", CTLTYPE_INT }, \ + { "sendspace", CTLTYPE_INT }, \ + { "stats", CTLTYPE_STRUCT } \ +} + +#define DIVERTCTL_VARS { \ + NULL, \ + &divert_recvspace, \ + &divert_sendspace, \ + NULL \ +} + +#ifdef _KERNEL +extern struct inpcbtable divbtable; +extern struct divstat divstat; + +void divert_init(void); +void divert_input(struct mbuf *, ...); +void divert_packet(struct mbuf *, int); +int divert_output(struct mbuf *, ...); +int divert_sysctl(int *, u_int, void *, size_t *, void *, size_t); +int divert_usrreq(struct socket *, + int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *); + +#endif /* _KERNEL */ +#endif /* _IP_DIVERT_H_ */ diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h index 09572fc3540..6773c4abf83 100644 --- a/sys/sys/mbuf.h +++ b/sys/sys/mbuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: mbuf.h,v 1.136 2009/09/13 14:42:52 krw Exp $ */ +/* $OpenBSD: mbuf.h,v 1.137 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $ */ /* @@ -90,6 +90,7 @@ struct pkthdr_pf { #define PF_TAG_FRAGCACHE 0x02 #define PF_TAG_TRANSLATE_LOCALHOST 0x04 #define PF_TAG_DIVERTED 0x08 +#define PF_TAG_DIVERTED_PACKET 0x10 /* record/packet header in first mbuf of chain; valid if M_PKTHDR set */ struct pkthdr { diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 8c1140ef8b0..873a1a2074e 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: inet.c,v 1.110 2009/02/07 15:06:04 chl Exp $ */ +/* $OpenBSD: inet.c,v 1.111 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: inet.c,v 1.14 1995/10/03 21:42:37 thorpej Exp $ */ /* @@ -65,6 +65,7 @@ #include <netinet/ip_ipcomp.h> #include <netinet/ip_ether.h> #include <netinet/ip_carp.h> +#include <netinet/ip_divert.h> #include <net/if.h> #include <net/pfvar.h> #include <net/if_pfsync.h> @@ -173,12 +174,12 @@ protopr0(u_long off, char *name, int af) printf(" (including servers)"); putchar('\n'); if (Aflag) - printf("%-*.*s %-5.5s %-6.6s %-6.6s %-18.18s %-18.18s %s\n", + printf("%-*.*s %-6.6s %-6.6s %-6.6s %-18.18s %-18.18s %s\n", PLEN, PLEN, "PCB", "Proto", "Recv-Q", "Send-Q", "Local Address", "Foreign Address", "(state)"); else - printf("%-5.5s %-6.6s %-6.6s %-22.22s %-22.22s %s\n", + printf("%-6.6s %-6.6s %-6.6s %-22.22s %-22.22s %s\n", "Proto", "Recv-Q", "Send-Q", "Local Address", "Foreign Address", "(state)"); @@ -196,7 +197,7 @@ protopr0(u_long off, char *name, int af) name = namebuf; } else name = name0; - printf("%-5.5s %6ld %6ld ", name, sockb.so_rcv.sb_cc, + printf("%-6.6s %6ld %6ld ", name, sockb.so_rcv.sb_cc, sockb.so_snd.sb_cc); if (inpcb.inp_flags & INP_IPV6) { inet6print(&inpcb.inp_laddr6, (int)inpcb.inp_lport, @@ -463,6 +464,37 @@ ip_stats(char *name) #undef p1 } +/* + * Dump DIVERT statistics structure. + */ +void +div_stats(char *name) +{ + struct divstat divstat; + int mib[] = { CTL_NET, AF_INET, IPPROTO_DIVERT, DIVERTCTL_STATS }; + size_t len = sizeof(divstat); + + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), + &divstat, &len, NULL, 0) == -1) { + if (errno != ENOPROTOOPT) + warn(name); + return; + } + + printf("%s:\n", name); +#define p(f, m) if (divstat.f || sflag <= 1) \ + printf(m, divstat.f, plural(divstat.f)) +#define p1(f, m) if (divstat.f || sflag <= 1) \ + printf(m, divstat.f) + p(divs_ipackets, "\t%lu total packet%s received\n"); + p1(divs_noport, "\t%lu dropped due to no socket\n"); + p1(divs_fullsock, "\t%lu dropped due to full socket buffers\n"); + p(divs_opackets, "\t%lu packet%s output\n"); + p1(divs_errors, "\t%lu errors\n"); +#undef p +#undef p1 +} + static char *icmpnames[ICMP_MAXTYPE + 1] = { "echo reply", "#1", diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c index 8662a82be53..4fe7e9c64c6 100644 --- a/usr.bin/netstat/main.c +++ b/usr.bin/netstat/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.79 2009/05/07 15:51:53 claudio Exp $ */ +/* $OpenBSD: main.c,v 1.80 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: main.c,v 1.9 1996/05/07 02:55:02 thorpej Exp $ */ /* @@ -87,6 +87,8 @@ struct nlist nl[] = { { "_rawcbtable" }, #define N_RAWIP6TABLE 14 { "_rawin6pcbtable" }, +#define N_DIVBTABLE 15 + { "_divbtable" }, { ""} }; @@ -101,6 +103,7 @@ struct protox { { N_TCBTABLE, protopr, tcp_stats, tcp_dump, "tcp" }, { N_UDBTABLE, protopr, udp_stats, NULL, "udp" }, { N_RAWIPTABLE, protopr, ip_stats, NULL, "ip" }, + { N_DIVBTABLE, protopr, div_stats, NULL, "divert" }, { -1, NULL, icmp_stats, NULL, "icmp" }, { -1, NULL, igmp_stats, NULL, "igmp" }, { -1, NULL, ah_stats, NULL, "ah" }, diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h index 251721ccbf6..6e23a1e3cb5 100644 --- a/usr.bin/netstat/netstat.h +++ b/usr.bin/netstat/netstat.h @@ -1,4 +1,4 @@ -/* $OpenBSD: netstat.h,v 1.50 2009/05/07 15:51:53 claudio Exp $ */ +/* $OpenBSD: netstat.h,v 1.51 2009/10/04 16:08:37 michele Exp $ */ /* $NetBSD: netstat.h,v 1.6 1996/05/07 02:55:05 thorpej Exp $ */ /* @@ -74,6 +74,7 @@ void ip6protopr(u_long, char *); void tcp_stats(char *); void udp_stats(char *); void ip_stats(char *); +void div_stats(char *); void icmp_stats(char *); void igmp_stats(char *); void pim_stats(char *); |