ip_divert.c revision 82884
139287Ssos/*
239643Syokota * Copyright (c) 1982, 1986, 1988, 1993
339287Ssos *	The Regents of the University of California.  All rights reserved.
439287Ssos *
5146736Sdelphij * Redistribution and use in source and binary forms, with or without
6146736Sdelphij * modification, are permitted provided that the following conditions
7146736Sdelphij * are met:
839287Ssos * 1. Redistributions of source code must retain the above copyright
939287Ssos *    notice, this list of conditions and the following disclaimer.
1039287Ssos * 2. Redistributions in binary form must reproduce the above copyright
1139287Ssos *    notice, this list of conditions and the following disclaimer in the
1239643Syokota *    documentation and/or other materials provided with the distribution.
1339643Syokota * 3. All advertising materials mentioning features or use of this software
1439287Ssos *    must display the following acknowledgement:
1539287Ssos *	This product includes software developed by the University of
1639287Ssos *	California, Berkeley and its contributors.
1739287Ssos * 4. Neither the name of the University nor the names of its contributors
1839643Syokota *    may be used to endorse or promote products derived from this software
1939643Syokota *    without specific prior written permission.
2039643Syokota *
2139643Syokota * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2239643Syokota * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2339643Syokota * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2439643Syokota * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2539643Syokota * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2639643Syokota * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2739643Syokota * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2839287Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2939287Ssos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30119420Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31119420Sobrien * SUCH DAMAGE.
32119420Sobrien *
33162711Sru * $FreeBSD: head/sys/netinet/ip_divert.c 82884 2001-09-03 20:03:55Z julian $
3439287Ssos */
3539287Ssos
3639287Ssos#include "opt_inet.h"
3739287Ssos#include "opt_ipfw.h"
3851404Syokota#include "opt_ipdivert.h"
3939287Ssos#include "opt_ipsec.h"
4039287Ssos
4139287Ssos#ifndef INET
4266834Sphk#error "IPDIVERT requires INET."
4366834Sphk#endif
4491140Stanimura
4591140Stanimura#include <sys/param.h>
4691140Stanimura#include <sys/kernel.h>
4791140Stanimura#include <sys/malloc.h>
4891140Stanimura#include <sys/mbuf.h>
4939287Ssos#include <sys/socket.h>
5042504Syokota#include <sys/protosw.h>
5142504Syokota#include <sys/socketvar.h>
5239287Ssos#include <sys/sysctl.h>
5378161Speter#include <sys/systm.h>
5478161Speter
5542504Syokota#include <vm/vm_zone.h>
5648104Syokota
5742504Syokota#include <net/if.h>
5842504Syokota#include <net/route.h>
5942504Syokota
6042504Syokota#include <netinet/in.h>
6148104Syokota#include <netinet/in_systm.h>
6242504Syokota#include <netinet/ip.h>
6342504Syokota#include <netinet/in_pcb.h>
6442504Syokota#include <netinet/in_var.h>
6542504Syokota#include <netinet/ip_var.h>
6642504Syokota
6742504Syokota/*
6842504Syokota * Divert sockets
6942504Syokota */
7048104Syokota
7142504Syokota/*
7242504Syokota * Allocate enough space to hold a full IP packet
7342504Syokota */
7442504Syokota#define	DIVSNDQ		(65536 + 100)
7542504Syokota#define	DIVRCVQ		(65536 + 100)
7642504Syokota
7742504Syokota/*
7842504Syokota * A 16 bit cookie is passed to and from the user process.
7942504Syokota * The user process can send it back to help the caller know
8042504Syokota * something about where the packet originally came from.
8139287Ssos *
8242504Syokota * In the case of ipfw, then the cookie is the rule that sent
8342504Syokota * us here. On reinjection is is the rule after which processing
8448104Syokota * should continue. Leaving it the same will make processing start
8548104Syokota * at the rule number after that which sent it here. Setting it to
8648104Syokota * 0 will restart processing at the beginning.
8748104Syokota *
8848104Syokota * For divert_packet(), ip_divert_cookie is an input value only.
8948104Syokota * For div_output(), ip_divert_cookie is an output value only.
9048104Syokota */
9148104Syokotau_int16_t ip_divert_cookie;
9248104Syokota
9348104Syokota/* Internal variables */
9448104Syokotastatic struct inpcbhead divcb;
9548104Syokotastatic struct inpcbinfo divcbinfo;
9648104Syokota
9748104Syokotastatic u_long	div_sendspace = DIVSNDQ;	/* XXX sysctl ? */
9848104Syokotastatic u_long	div_recvspace = DIVRCVQ;	/* XXX sysctl ? */
99132107Sstefanf
10048104Syokota/* Optimization: have this preinitialized */
10148104Syokotastatic struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
10248104Syokota
10348104Syokota/* Internal functions */
10448104Syokotastatic int div_output(struct socket *so,
10548104Syokota		struct mbuf *m, struct sockaddr *addr, struct mbuf *control);
10639287Ssos
10748104Syokota/*
10848104Syokota * Initialize divert connection block queue.
10948104Syokota */
11048104Syokotavoid
11148104Syokotadiv_init(void)
11248104Syokota{
11348104Syokota	LIST_INIT(&divcb);
11448104Syokota	divcbinfo.listhead = &divcb;
11548104Syokota	/*
11648104Syokota	 * XXX We don't use the hash list for divert IP, but it's easier
11748104Syokota	 * to allocate a one entry hash list than it is to check all
11848104Syokota	 * over the place for hashbase == NULL.
11948104Syokota	 */
12048104Syokota	divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask);
12148104Syokota	divcbinfo.porthashbase = hashinit(1, M_PCB, &divcbinfo.porthashmask);
12248104Syokota	divcbinfo.ipi_zone = zinit("divcb", sizeof(struct inpcb),
12348104Syokota				   maxsockets, ZONE_INTERRUPT, 0);
12448104Syokota}
12548104Syokota
12648104Syokota/*
12748104Syokota * IPPROTO_DIVERT is not a real IP protocol; don't allow any packets
12848104Syokota * with that protocol number to enter the system from the outside.
12948104Syokota */
13048104Syokotavoid
13148104Syokotadiv_input(struct mbuf *m, int off)
13248104Syokota{
13348104Syokota	ipstat.ips_noproto++;
13448104Syokota	m_freem(m);
13539287Ssos}
13639287Ssos
137149640Srodrigc/*
13839287Ssos * Divert a packet by passing it up to the divert socket at port 'port'.
13939287Ssos *
140242529Sed * Setup generic address and protocol structures for div_input routine,
14148104Syokota * then pass them along with mbuf chain.
14248667Syokota */
14339287Ssosvoid
14439287Ssosdivert_packet(struct mbuf *m, int incoming, int port)
14539287Ssos{
146174985Swkoszek	struct ip *ip;
14739287Ssos	struct inpcb *inp;
14848104Syokota	struct socket *sa;
14939287Ssos	u_int16_t nport;
150149828Srodrigc
151149828Srodrigc	/* Sanity check */
15239287Ssos	KASSERT(port != 0, ("%s: port=0", __FUNCTION__));
15339287Ssos
154216079Sjkim	/* Record and reset divert cookie */
15539287Ssos	divsrc.sin_port = ip_divert_cookie;
156216079Sjkim	ip_divert_cookie = 0;
15739287Ssos
158216079Sjkim	/* Assure header */
15939287Ssos	if (m->m_len < sizeof(struct ip) &&
16048104Syokota	    (m = m_pullup(m, sizeof(struct ip))) == 0) {
161216079Sjkim		return;
162216079Sjkim	}
163216079Sjkim	ip = mtod(m, struct ip *);
164216079Sjkim
165216079Sjkim	/*
166216079Sjkim	 * Record receive interface address, if any.
167216079Sjkim	 * But only for incoming packets.
168216079Sjkim	 */
169216079Sjkim	divsrc.sin_addr.s_addr = 0;
17048104Syokota	if (incoming) {
171216079Sjkim		struct ifaddr *ifa;
172216079Sjkim
173216079Sjkim		/* Sanity check */
174216079Sjkim		KASSERT((m->m_flags & M_PKTHDR), ("%s: !PKTHDR", __FUNCTION__));
175216079Sjkim
176216079Sjkim		/* Find IP address for receive interface */
177216079Sjkim		TAILQ_FOREACH(ifa, &m->m_pkthdr.rcvif->if_addrhead, ifa_link) {
17848104Syokota			if (ifa->ifa_addr == NULL)
179216079Sjkim				continue;
18048104Syokota			if (ifa->ifa_addr->sa_family != AF_INET)
18139287Ssos				continue;
18239287Ssos			divsrc.sin_addr =
18339287Ssos			    ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
18439287Ssos			break;
18539287Ssos		}
18639287Ssos	}
18739287Ssos	/*
18839287Ssos	 * Record the incoming interface name whenever we have one.
18939287Ssos	 */
19039287Ssos	bzero(&divsrc.sin_zero, sizeof(divsrc.sin_zero));
19139287Ssos	if (m->m_pkthdr.rcvif) {
19239287Ssos		/*
19356043Syokota		 * Hide the actual interface name in there in the
19448104Syokota		 * sin_zero array. XXX This needs to be moved to a
19548104Syokota		 * different sockaddr type for divert, e.g.
19648104Syokota		 * sockaddr_div with multiple fields like
19748104Syokota		 * sockaddr_dl. Presently we have only 7 bytes
19839287Ssos		 * but that will do for now as most interfaces
19951394Syokota		 * are 4 or less + 2 or less bytes for unit.
20051394Syokota		 * There is probably a faster way of doing this,
20151394Syokota		 * possibly taking it from the sockaddr_dl on the iface.
20251394Syokota		 * This solves the problem of a P2P link and a LAN interface
20348667Syokota		 * having the same address, which can result in the wrong
20439287Ssos		 * interface being assigned to the packet when fed back
20539287Ssos		 * into the divert socket. Theoretically if the daemon saves
20639287Ssos		 * and re-uses the sockaddr_in as suggested in the man pages,
20739287Ssos		 * this iface name will come along for the ride.
20858872Syokota		 * (see div_output for the other half of this.)
20958872Syokota		 */
21039287Ssos		snprintf(divsrc.sin_zero, sizeof(divsrc.sin_zero),
21139287Ssos			"%s%d", m->m_pkthdr.rcvif->if_name,
21239287Ssos			m->m_pkthdr.rcvif->if_unit);
21342504Syokota	}
21442504Syokota
21539287Ssos	/* Put packet on socket queue, if any */
21639287Ssos	sa = NULL;
21748104Syokota	nport = htons((u_int16_t)port);
21848104Syokota	LIST_FOREACH(inp, &divcb, inp_list) {
219149640Srodrigc		if (inp->inp_lport == nport)
22039287Ssos			sa = inp->inp_socket;
22139287Ssos	}
22239287Ssos	if (sa) {
22356043Syokota		if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
22448104Syokota				m, (struct mbuf *)0) == 0)
22548104Syokota			m_freem(m);
22648104Syokota		else
22748104Syokota			sorwakeup(sa);
22848667Syokota	} else {
22948104Syokota		m_freem(m);
23039287Ssos		ipstat.ips_noproto++;
23139287Ssos		ipstat.ips_delivered--;
23248104Syokota        }
23339287Ssos}
23439287Ssos
23539287Ssos/*
23639287Ssos * Deliver packet back into the IP processing machinery.
23739287Ssos *
238242529Sed * If no address specified, or address is 0.0.0.0, send to ip_output();
239242529Sed * otherwise, send to ip_input() and mark as having been received on
240242529Sed * the interface with that address.
24139287Ssos */
24239287Ssosstatic int
24339287Ssosdiv_output(so, m, addr, control)
24439287Ssos	struct socket *so;
24539287Ssos	register struct mbuf *m;
24639287Ssos	struct sockaddr *addr;
24748104Syokota	struct mbuf *control;
24848104Syokota{
24948104Syokota	register struct inpcb *const inp = sotoinpcb(so);
25039287Ssos	register struct ip *const ip = mtod(m, struct ip *);
251242529Sed	struct sockaddr_in *sin = (struct sockaddr_in *)addr;
25239287Ssos	int error = 0;
25339287Ssos
25439287Ssos	if (control)
255174985Swkoszek		m_freem(control);		/* XXX */
25639287Ssos
25739287Ssos	/* Loopback avoidance and state recovery */
25839287Ssos	if (sin) {
25939287Ssos		int	len = 0;
26039287Ssos		char	*c = sin->sin_zero;
26139287Ssos
26239287Ssos		ip_divert_cookie = sin->sin_port;
26339287Ssos
26439287Ssos		/*
26556043Syokota		 * Find receive interface with the given name or IP address.
26648104Syokota		 * The name is user supplied data so don't trust it's size or
26748104Syokota		 * that it is zero terminated. The name has priority.
26848104Syokota		 * We are presently assuming that the sockaddr_in
26948104Syokota		 * has not been replaced by a sockaddr_div, so we limit it
27039287Ssos		 * to 16 bytes in total. the name is stuffed (if it exists)
27158872Syokota		 * in the sin_zero[] field.
27258872Syokota		 */
27339287Ssos		while (*c++ && (len++ < sizeof(sin->sin_zero)));
27450447Syokota		if ((len > 0) && (len < sizeof(sin->sin_zero)))
27550447Syokota			m->m_pkthdr.rcvif = ifunit(sin->sin_zero);
27650447Syokota	} else {
27750447Syokota		ip_divert_cookie = 0;
27842504Syokota	}
27942504Syokota
28039287Ssos	/* Reinject packet into the system as incoming or outgoing */
28139287Ssos	if (!sin || sin->sin_addr.s_addr == 0) {
28248104Syokota		/*
28356328Syokota		 * Don't allow both user specified and setsockopt options,
28448104Syokota		 * and don't allow packet length sizes that will crash
28539287Ssos		 */
28648104Syokota		if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
28748104Syokota		     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
28856043Syokota			error = EINVAL;
28939287Ssos			goto cantsend;
29039287Ssos		}
29148104Syokota
29239287Ssos		/* Convert fields to host order for ip_output() */
29339287Ssos		NTOHS(ip->ip_len);
29439287Ssos		NTOHS(ip->ip_off);
29539287Ssos
29639287Ssos		/* Send packet to output processing */
29739287Ssos		ipstat.ips_rawout++;			/* XXX */
298242529Sed		error = ip_output(m, inp->inp_options, &inp->inp_route,
299242529Sed			(so->so_options & SO_DONTROUTE) |
300242529Sed			IP_ALLOWBROADCAST | IP_RAWOUTPUT,
30139287Ssos			inp->inp_moptions);
30248104Syokota	} else {
30339287Ssos		struct	ifaddr *ifa;
30439287Ssos
30539287Ssos		/* If no luck with the name above. check by IP address.  */
30639287Ssos		if (m->m_pkthdr.rcvif == NULL) {
307149640Srodrigc			/*
30839287Ssos			 * Make sure there are no distractions
30948104Syokota			 * for ifa_ifwithaddr. Clear the port and the ifname.
31048104Syokota			 * Maybe zap all 8 bytes at once using a 64bit write?
31148104Syokota			 */
31239287Ssos			bzero(sin->sin_zero, sizeof(sin->sin_zero));
313242529Sed			/* *((u_int64_t *)sin->sin_zero) = 0; */ /* XXX ?? */
31448104Syokota			sin->sin_port = 0;
31548667Syokota			if (!(ifa = ifa_ifwithaddr((struct sockaddr *) sin))) {
31639287Ssos				error = EADDRNOTAVAIL;
31739287Ssos				goto cantsend;
31839287Ssos			}
319174985Swkoszek			m->m_pkthdr.rcvif = ifa->ifa_ifp;
32039287Ssos		}
32139287Ssos
32239287Ssos		/* Send packet to input processing */
32356328Syokota		ip_input(m);
32439287Ssos	}
325216079Sjkim
32639287Ssos	/* paranoid: Reset for next time (and other packets) */
327216079Sjkim	/* almost definitly already done in the ipfw filter but.. */
32839287Ssos	ip_divert_cookie = 0;
329216079Sjkim	return error;
33039287Ssos
33148104Syokotacantsend:
332216079Sjkim	m_freem(m);
333216079Sjkim	ip_divert_cookie = 0;
334216079Sjkim	return error;
335216079Sjkim}
336216079Sjkim
337216079Sjkimstatic int
338216079Sjkimdiv_attach(struct socket *so, int proto, struct proc *p)
339216079Sjkim{
340216079Sjkim	struct inpcb *inp;
34148104Syokota	int error, s;
342216079Sjkim
343216079Sjkim	inp  = sotoinpcb(so);
344216079Sjkim	if (inp)
345216079Sjkim		panic("div_attach");
346216079Sjkim	if (p && (error = suser(p)) != 0)
347216079Sjkim		return error;
348216079Sjkim
34948104Syokota	error = soreserve(so, div_sendspace, div_recvspace);
350216079Sjkim	if (error)
35148104Syokota		return error;
35239287Ssos	s = splnet();
35339287Ssos	error = in_pcballoc(so, &divcbinfo, p);
35439287Ssos	splx(s);
35539287Ssos	if (error)
35639287Ssos		return error;
35739591Syokota	inp = (struct inpcb *)so->so_pcb;
35839591Syokota	inp->inp_ip_p = proto;
35939591Syokota	inp->inp_vflag |= INP_IPV4;
360204281Sjkim	inp->inp_flags |= INP_HDRINCL;
36139591Syokota	/* The socket is always "connected" because
36239591Syokota	   we always know "where" to send the packet */
36339287Ssos	so->so_state |= SS_ISCONNECTED;
36439287Ssos	return 0;
36539287Ssos}
36639287Ssos
36739287Ssosstatic int
36839287Ssosdiv_detach(struct socket *so)
36939287Ssos{
37056043Syokota	struct inpcb *inp;
37148104Syokota
37248104Syokota	inp = sotoinpcb(so);
37348104Syokota	if (inp == 0)
37448104Syokota		panic("div_detach");
37556043Syokota	in_pcbdetach(inp);
37656043Syokota	return 0;
37756043Syokota}
37856043Syokota
37956043Syokotastatic int
38056043Syokotadiv_abort(struct socket *so)
38156043Syokota{
38239287Ssos	soisdisconnected(so);
38351394Syokota	return div_detach(so);
38451394Syokota}
38551394Syokota
38651394Syokotastatic int
38748667Syokotadiv_disconnect(struct socket *so)
38858872Syokota{
38958872Syokota	if ((so->so_state & SS_ISCONNECTED) == 0)
39039287Ssos		return ENOTCONN;
39139287Ssos	return div_abort(so);
39239287Ssos}
39339287Ssos
39448104Syokotastatic int
39548104Syokotadiv_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
396149640Srodrigc{
39739287Ssos	struct inpcb *inp;
39839287Ssos	int s;
39939287Ssos	int error;
40056043Syokota
40148104Syokota	s = splnet();
40248104Syokota	inp = sotoinpcb(so);
40348104Syokota	/* in_pcbbind assumes that the socket is a sockaddr_in
40448104Syokota	 * and in_pcbbind requires a valid address. Since divert
40548667Syokota	 * sockets don't we need to make sure the address is
40648104Syokota	 * filled in properly.
40739287Ssos	 * XXX -- divert should not be abusing in_pcbind
40839287Ssos	 * and should probably have its own family.
40948104Syokota	 */
41056043Syokota	if (nam->sa_family != AF_INET) {
41148104Syokota		error = EAFNOSUPPORT;
41248104Syokota	} else {
41339287Ssos		((struct sockaddr_in *)nam)->sin_addr.s_addr = INADDR_ANY;
41439287Ssos		error = in_pcbbind(inp, nam, p);
41539287Ssos	}
41639287Ssos	splx(s);
41739287Ssos	return error;
418242529Sed}
419242529Sed
420242529Sedstatic int
42139287Ssosdiv_shutdown(struct socket *so)
42248104Syokota{
42339287Ssos	socantsendmore(so);
42439287Ssos	return 0;
425204281Sjkim}
426204281Sjkim
427204281Sjkimstatic int
428204281Sjkimdiv_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
429204281Sjkim	 struct mbuf *control, struct proc *p)
430204281Sjkim{
431204281Sjkim	/* Packet must have a header (but that's about it) */
432204281Sjkim	if (m->m_len < sizeof (struct ip) &&
433204281Sjkim	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
434204281Sjkim		ipstat.ips_toosmall++;
435204281Sjkim		m_freem(m);
436204281Sjkim		return EINVAL;
437204281Sjkim	}
438204281Sjkim
439204281Sjkim	/* Send packet */
440204281Sjkim	return div_output(so, m, nam, control);
441204281Sjkim}
442204281Sjkim
443204281Sjkimstatic int
444204281Sjkimdiv_pcblist(SYSCTL_HANDLER_ARGS)
445204281Sjkim{
446204281Sjkim	int error, i, n, s;
447204281Sjkim	struct inpcb *inp, **inp_list;
448204281Sjkim	inp_gen_t gencnt;
449204281Sjkim	struct xinpgen xig;
450204281Sjkim
451204281Sjkim	/*
452204281Sjkim	 * The process of preparing the TCB list is too time-consuming and
453204281Sjkim	 * resource-intensive to repeat twice on every request.
454204281Sjkim	 */
455204281Sjkim	if (req->oldptr == 0) {
456204281Sjkim		n = divcbinfo.ipi_count;
457204281Sjkim		req->oldidx = 2 * (sizeof xig)
458204281Sjkim			+ (n + n/8) * sizeof(struct xinpcb);
459204281Sjkim		return 0;
460204281Sjkim	}
461204281Sjkim
462204281Sjkim	if (req->newptr != 0)
463204281Sjkim		return EPERM;
464204281Sjkim
465204281Sjkim	/*
466204281Sjkim	 * OK, now we're committed to doing something.
46748104Syokota	 */
46848104Syokota	s = splnet();
469174985Swkoszek	gencnt = divcbinfo.ipi_gencnt;
47048104Syokota	n = divcbinfo.ipi_count;
47139287Ssos	splx(s);
472181905Sed
47339287Ssos	xig.xig_len = sizeof xig;
47439287Ssos	xig.xig_count = n;
47548104Syokota	xig.xig_gen = gencnt;
47648104Syokota	xig.xig_sogen = so_gencnt;
47748104Syokota	error = SYSCTL_OUT(req, &xig, sizeof xig);
47839287Ssos	if (error)
47939287Ssos		return error;
480162711Sru
481162711Sru	inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK);
482162711Sru	if (inp_list == 0)
483162711Sru		return ENOMEM;
48439287Ssos
485181905Sed	s = splnet();
48648104Syokota	for (inp = LIST_FIRST(divcbinfo.listhead), i = 0; inp && i < n;
48748104Syokota	     inp = LIST_NEXT(inp, inp_list)) {
48848104Syokota		if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp))
48948104Syokota			inp_list[i++] = inp;
49048104Syokota	}
49139287Ssos	splx(s);
49239287Ssos	n = i;
49339287Ssos
49448104Syokota	error = 0;
49548104Syokota	for (i = 0; i < n; i++) {
49648104Syokota		inp = inp_list[i];
49748104Syokota		if (inp->inp_gencnt <= gencnt) {
49839287Ssos			struct xinpcb xi;
49948104Syokota			xi.xi_len = sizeof xi;
50048104Syokota			/* XXX should avoid extra copy */
50139287Ssos			bcopy(inp, &xi.xi_inp, sizeof *inp);
50248104Syokota			if (inp->inp_socket)
50348104Syokota				sotoxsocket(inp->inp_socket, &xi.xi_socket);
50448104Syokota			error = SYSCTL_OUT(req, &xi, sizeof xi);
50548104Syokota		}
50648104Syokota	}
50748104Syokota	if (!error) {
50848104Syokota		/*
50948104Syokota		 * Give the user an updated idea of our state.
51048104Syokota		 * If the generation differs from what we told
51148104Syokota		 * her before, she knows that something happened
51248104Syokota		 * while we were processing this request, and it
51348104Syokota		 * might be necessary to retry.
51448104Syokota		 */
51548104Syokota		s = splnet();
51648104Syokota		xig.xig_gen = divcbinfo.ipi_gencnt;
51748104Syokota		xig.xig_sogen = so_gencnt;
51848104Syokota		xig.xig_count = divcbinfo.ipi_count;
51942504Syokota		splx(s);
52048104Syokota		error = SYSCTL_OUT(req, &xig, sizeof xig);
52142504Syokota	}
52242504Syokota	free(inp_list, M_TEMP);
52348104Syokota	return error;
52448104Syokota}
52548104Syokota
52648104SyokotaSYSCTL_DECL(_net_inet_divert);
52748104SyokotaSYSCTL_PROC(_net_inet_divert, OID_AUTO, pcblist, CTLFLAG_RD, 0, 0,
52848104Syokota	    div_pcblist, "S,xinpcb", "List of active divert sockets");
52948104Syokota
53048104Syokotastruct pr_usrreqs div_usrreqs = {
53148104Syokota	div_abort, pru_accept_notsupp, div_attach, div_bind,
53248104Syokota	pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
53348104Syokota	div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
53448104Syokota	pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
53539287Ssos	in_setsockaddr, sosend, soreceive, sopoll
53648104Syokota};
53748104Syokota