ip_divert.c revision 67893
1336815Sdim/*
2336815Sdim * Copyright (c) 1982, 1986, 1988, 1993
3336815Sdim *	The Regents of the University of California.  All rights reserved.
4336815Sdim *
5336815Sdim * Redistribution and use in source and binary forms, with or without
6336815Sdim * modification, are permitted provided that the following conditions
7336815Sdim * are met:
8336815Sdim * 1. Redistributions of source code must retain the above copyright
9336815Sdim *    notice, this list of conditions and the following disclaimer.
10336815Sdim * 2. Redistributions in binary form must reproduce the above copyright
11336815Sdim *    notice, this list of conditions and the following disclaimer in the
12336815Sdim *    documentation and/or other materials provided with the distribution.
13336815Sdim * 3. All advertising materials mentioning features or use of this software
14336815Sdim *    must display the following acknowledgement:
15336815Sdim *	This product includes software developed by the University of
16336815Sdim *	California, Berkeley and its contributors.
17336815Sdim * 4. Neither the name of the University nor the names of its contributors
18336815Sdim *    may be used to endorse or promote products derived from this software
19336815Sdim *    without specific prior written permission.
20336815Sdim *
21336815Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22336815Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23336815Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24336815Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25336815Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26336815Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27336815Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28336815Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29336815Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30336815Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31336815Sdim * SUCH DAMAGE.
32336815Sdim *
33336815Sdim * $FreeBSD: head/sys/netinet/ip_divert.c 67893 2000-10-29 16:06:56Z phk $
34336815Sdim */
35336815Sdim
36336815Sdim#include "opt_inet.h"
37336815Sdim#include "opt_ipfw.h"
38336815Sdim#include "opt_ipdivert.h"
39336815Sdim#include "opt_ipsec.h"
40336815Sdim
41336815Sdim#ifndef INET
42336815Sdim#error "IPDIVERT requires INET."
43336815Sdim#endif
44336815Sdim
45336815Sdim#include <sys/param.h>
46336815Sdim#include <sys/kernel.h>
47336815Sdim#include <sys/malloc.h>
48336815Sdim#include <sys/mbuf.h>
49336815Sdim#include <sys/socket.h>
50336815Sdim#include <sys/protosw.h>
51336815Sdim#include <sys/socketvar.h>
52336815Sdim#include <sys/sysctl.h>
53336815Sdim#include <sys/systm.h>
54336815Sdim
55336815Sdim#include <vm/vm_zone.h>
56336815Sdim
57336815Sdim#include <net/if.h>
58336815Sdim#include <net/route.h>
59336815Sdim
60336815Sdim#include <netinet/in.h>
61336815Sdim#include <netinet/in_systm.h>
62336815Sdim#include <netinet/ip.h>
63336815Sdim#include <netinet/in_pcb.h>
64336815Sdim#include <netinet/in_var.h>
65336815Sdim#include <netinet/ip_var.h>
66336815Sdim
67336815Sdim/*
68336815Sdim * Divert sockets
69336815Sdim */
70336815Sdim
71336815Sdim/*
72336815Sdim * Allocate enough space to hold a full IP packet
73336815Sdim */
74336815Sdim#define	DIVSNDQ		(65536 + 100)
75336815Sdim#define	DIVRCVQ		(65536 + 100)
76336815Sdim
77336815Sdim/*
78336815Sdim * A 16 bit cookie is passed to and from the user process.
79336815Sdim * The user process can send it back to help the caller know
80336815Sdim * something about where the packet originally came from.
81336815Sdim *
82336815Sdim * In the case of ipfw, then the cookie is the rule that sent
83336815Sdim * us here. On reinjection is is the rule after which processing
84336815Sdim * should continue. Leaving it the same will make processing start
85336815Sdim * at the rule number after that which sent it here. Setting it to
86336815Sdim * 0 will restart processing at the beginning.
87336815Sdim *
88336815Sdim * For divert_packet(), ip_divert_cookie is an input value only.
89336815Sdim * For div_output(), ip_divert_cookie is an output value only.
90336815Sdim */
91336815Sdimu_int16_t ip_divert_cookie;
92336815Sdim
93336815Sdim/* Internal variables */
94336815Sdimstatic struct inpcbhead divcb;
95336815Sdimstatic struct inpcbinfo divcbinfo;
96336815Sdim
97336815Sdimstatic u_long	div_sendspace = DIVSNDQ;	/* XXX sysctl ? */
98336815Sdimstatic u_long	div_recvspace = DIVRCVQ;	/* XXX sysctl ? */
99336815Sdim
100336815Sdim/* Optimization: have this preinitialized */
101336815Sdimstatic struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
102336815Sdim
103336815Sdim/* Internal functions */
104336815Sdimstatic int div_output(struct socket *so,
105336815Sdim		struct mbuf *m, struct sockaddr *addr, struct mbuf *control);
106336815Sdim
107336815Sdim/*
108336815Sdim * Initialize divert connection block queue.
109336815Sdim */
110336815Sdimvoid
111336815Sdimdiv_init(void)
112336815Sdim{
113336815Sdim	LIST_INIT(&divcb);
114336815Sdim	divcbinfo.listhead = &divcb;
115336815Sdim	/*
116336815Sdim	 * XXX We don't use the hash list for divert IP, but it's easier
117336815Sdim	 * to allocate a one entry hash list than it is to check all
118336815Sdim	 * over the place for hashbase == NULL.
119336815Sdim	 */
120336815Sdim	divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask);
121336815Sdim	divcbinfo.porthashbase = hashinit(1, M_PCB, &divcbinfo.porthashmask);
122336815Sdim	divcbinfo.ipi_zone = zinit("divcb", sizeof(struct inpcb),
123336815Sdim				   maxsockets, ZONE_INTERRUPT, 0);
124336815Sdim}
125336815Sdim
126336815Sdim/*
127336815Sdim * IPPROTO_DIVERT is not a real IP protocol; don't allow any packets
128336815Sdim * with that protocol number to enter the system from the outside.
129336815Sdim */
130336815Sdimvoid
131336815Sdimdiv_input(struct mbuf *m, int off, int proto)
132336815Sdim{
133336815Sdim	ipstat.ips_noproto++;
134336815Sdim	m_freem(m);
135336815Sdim}
136336815Sdim
137336815Sdim/*
138336815Sdim * Divert a packet by passing it up to the divert socket at port 'port'.
139336815Sdim *
140336815Sdim * Setup generic address and protocol structures for div_input routine,
141336815Sdim * then pass them along with mbuf chain.
142336815Sdim */
143336815Sdimvoid
144336815Sdimdivert_packet(struct mbuf *m, int incoming, int port)
145336815Sdim{
146336815Sdim	struct ip *ip;
147336815Sdim	struct inpcb *inp;
148336815Sdim	struct socket *sa;
149336815Sdim	u_int16_t nport;
150336815Sdim
151336815Sdim	/* Sanity check */
152336815Sdim	KASSERT(port != 0, ("%s: port=0", __FUNCTION__));
153336815Sdim
154336815Sdim	/* Record and reset divert cookie */
155336815Sdim	divsrc.sin_port = ip_divert_cookie;
156336815Sdim	ip_divert_cookie = 0;
157336815Sdim
158336815Sdim	/* Assure header */
159336815Sdim	if (m->m_len < sizeof(struct ip) &&
160336815Sdim	    (m = m_pullup(m, sizeof(struct ip))) == 0) {
161336815Sdim		return;
162336815Sdim	}
163336815Sdim	ip = mtod(m, struct ip *);
164336815Sdim
165336815Sdim	/*
166336815Sdim	 * Record receive interface address, if any.
167336815Sdim	 * But only for incoming packets.
168336815Sdim	 */
169336815Sdim	divsrc.sin_addr.s_addr = 0;
170336815Sdim	if (incoming) {
171336815Sdim		struct ifaddr *ifa;
172336815Sdim
173336815Sdim		/* Sanity check */
174336815Sdim		KASSERT((m->m_flags & M_PKTHDR), ("%s: !PKTHDR", __FUNCTION__));
175336815Sdim
176336815Sdim		/* Find IP address for receive interface */
177336815Sdim		for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
178336815Sdim		    ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
179336815Sdim			if (ifa->ifa_addr == NULL)
180336815Sdim				continue;
181336815Sdim			if (ifa->ifa_addr->sa_family != AF_INET)
182336815Sdim				continue;
183336815Sdim			divsrc.sin_addr =
184336815Sdim			    ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
185336815Sdim			break;
186336815Sdim		}
187336815Sdim	}
188336815Sdim	/*
189336815Sdim	 * Record the incoming interface name whenever we have one.
190336815Sdim	 */
191336815Sdim	bzero(&divsrc.sin_zero, sizeof(divsrc.sin_zero));
192336815Sdim	if (m->m_pkthdr.rcvif) {
193336815Sdim		/*
194336815Sdim		 * Hide the actual interface name in there in the
195336815Sdim		 * sin_zero array. XXX This needs to be moved to a
196336815Sdim		 * different sockaddr type for divert, e.g.
197336815Sdim		 * sockaddr_div with multiple fields like
198336815Sdim		 * sockaddr_dl. Presently we have only 7 bytes
199336815Sdim		 * but that will do for now as most interfaces
200336815Sdim		 * are 4 or less + 2 or less bytes for unit.
201336815Sdim		 * There is probably a faster way of doing this,
202336815Sdim		 * possibly taking it from the sockaddr_dl on the iface.
203336815Sdim		 * This solves the problem of a P2P link and a LAN interface
204336815Sdim		 * having the same address, which can result in the wrong
205336815Sdim		 * interface being assigned to the packet when fed back
206336815Sdim		 * into the divert socket. Theoretically if the daemon saves
207336815Sdim		 * and re-uses the sockaddr_in as suggested in the man pages,
208336815Sdim		 * this iface name will come along for the ride.
209336815Sdim		 * (see div_output for the other half of this.)
210336815Sdim		 */
211336815Sdim		snprintf(divsrc.sin_zero, sizeof(divsrc.sin_zero),
212336815Sdim			"%s%d", m->m_pkthdr.rcvif->if_name,
213336815Sdim			m->m_pkthdr.rcvif->if_unit);
214336815Sdim	}
215336815Sdim
216336815Sdim	/* Put packet on socket queue, if any */
217336815Sdim	sa = NULL;
218336815Sdim	nport = htons((u_int16_t)port);
219336815Sdim	for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
220336815Sdim		if (inp->inp_lport == nport)
221336815Sdim			sa = inp->inp_socket;
222336815Sdim	}
223336815Sdim	if (sa) {
224336815Sdim		if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
225336815Sdim				m, (struct mbuf *)0) == 0)
226336815Sdim			m_freem(m);
227336815Sdim		else
228336815Sdim			sorwakeup(sa);
229336815Sdim	} else {
230336815Sdim		m_freem(m);
231336815Sdim		ipstat.ips_noproto++;
232336815Sdim		ipstat.ips_delivered--;
233336815Sdim        }
234336815Sdim}
235336815Sdim
236336815Sdim/*
237336815Sdim * Deliver packet back into the IP processing machinery.
238336815Sdim *
239336815Sdim * If no address specified, or address is 0.0.0.0, send to ip_output();
240336815Sdim * otherwise, send to ip_input() and mark as having been received on
241336815Sdim * the interface with that address.
242336815Sdim */
243336815Sdimstatic int
244336815Sdimdiv_output(so, m, addr, control)
245336815Sdim	struct socket *so;
246336815Sdim	register struct mbuf *m;
247336815Sdim	struct sockaddr *addr;
248336815Sdim	struct mbuf *control;
249336815Sdim{
250336815Sdim	register struct inpcb *const inp = sotoinpcb(so);
251336815Sdim	register struct ip *const ip = mtod(m, struct ip *);
252336815Sdim	struct sockaddr_in *sin = (struct sockaddr_in *)addr;
253336815Sdim	int error = 0;
254336815Sdim
255336815Sdim	if (control)
256336815Sdim		m_freem(control);		/* XXX */
257336815Sdim
258336815Sdim	/* Loopback avoidance and state recovery */
259336815Sdim	if (sin) {
260336815Sdim		int	len = 0;
261336815Sdim		char	*c = sin->sin_zero;
262336815Sdim
263336815Sdim		ip_divert_cookie = sin->sin_port;
264336815Sdim
265336815Sdim		/*
266336815Sdim		 * Find receive interface with the given name or IP address.
267336815Sdim		 * The name is user supplied data so don't trust it's size or
268336815Sdim		 * that it is zero terminated. The name has priority.
269336815Sdim		 * We are presently assuming that the sockaddr_in
270336815Sdim		 * has not been replaced by a sockaddr_div, so we limit it
271336815Sdim		 * to 16 bytes in total. the name is stuffed (if it exists)
272336815Sdim		 * in the sin_zero[] field.
273336815Sdim		 */
274336815Sdim		while (*c++ && (len++ < sizeof(sin->sin_zero)));
275336815Sdim		if ((len > 0) && (len < sizeof(sin->sin_zero)))
276336815Sdim			m->m_pkthdr.rcvif = ifunit(sin->sin_zero);
277336815Sdim	} else {
278336815Sdim		ip_divert_cookie = 0;
279336815Sdim	}
280336815Sdim
281336815Sdim	/* Reinject packet into the system as incoming or outgoing */
282336815Sdim	if (!sin || sin->sin_addr.s_addr == 0) {
283336815Sdim		/*
284336815Sdim		 * Don't allow both user specified and setsockopt options,
285336815Sdim		 * and don't allow packet length sizes that will crash
286336815Sdim		 */
287336815Sdim		if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
288336815Sdim		     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
289336815Sdim			error = EINVAL;
290336815Sdim			goto cantsend;
291336815Sdim		}
292336815Sdim
293336815Sdim		/* Convert fields to host order for ip_output() */
294336815Sdim		NTOHS(ip->ip_len);
295336815Sdim		NTOHS(ip->ip_off);
296336815Sdim
297336815Sdim		/* Send packet to output processing */
298336815Sdim		ipstat.ips_rawout++;			/* XXX */
299336815Sdim		error = ip_output(m, inp->inp_options, &inp->inp_route,
300336815Sdim			(so->so_options & SO_DONTROUTE) |
301336815Sdim			IP_ALLOWBROADCAST | IP_RAWOUTPUT,
302336815Sdim			inp->inp_moptions);
303336815Sdim	} else {
304336815Sdim		struct	ifaddr *ifa;
305336815Sdim
306336815Sdim		/* If no luck with the name above. check by IP address.  */
307336815Sdim		if (m->m_pkthdr.rcvif == NULL) {
308336815Sdim			/*
309336815Sdim			 * Make sure there are no distractions
310336815Sdim			 * for ifa_ifwithaddr. Clear the port and the ifname.
311336815Sdim			 * Maybe zap all 8 bytes at once using a 64bit write?
312336815Sdim			 */
313336815Sdim			bzero(sin->sin_zero, sizeof(sin->sin_zero));
314336815Sdim			/* *((u_int64_t *)sin->sin_zero) = 0; */ /* XXX ?? */
315336815Sdim			sin->sin_port = 0;
316336815Sdim			if (!(ifa = ifa_ifwithaddr((struct sockaddr *) sin))) {
317336815Sdim				error = EADDRNOTAVAIL;
318336815Sdim				goto cantsend;
319336815Sdim			}
320336815Sdim			m->m_pkthdr.rcvif = ifa->ifa_ifp;
321336815Sdim		}
322336815Sdim
323336815Sdim		/* Send packet to input processing */
324336815Sdim		ip_input(m);
325336815Sdim	}
326336815Sdim
327336815Sdim	/* paranoid: Reset for next time (and other packets) */
328336815Sdim	/* almost definitly already done in the ipfw filter but.. */
329336815Sdim	ip_divert_cookie = 0;
330336815Sdim	return error;
331336815Sdim
332336815Sdimcantsend:
333336815Sdim	m_freem(m);
334336815Sdim	ip_divert_cookie = 0;
335336815Sdim	return error;
336336815Sdim}
337336815Sdim
338336815Sdimstatic int
339336815Sdimdiv_attach(struct socket *so, int proto, struct proc *p)
340336815Sdim{
341336815Sdim	struct inpcb *inp;
342336815Sdim	int error, s;
343336815Sdim
344336815Sdim	inp  = sotoinpcb(so);
345336815Sdim	if (inp)
346336815Sdim		panic("div_attach");
347336815Sdim	if (p && (error = suser(p)) != 0)
348336815Sdim		return error;
349336815Sdim
350336815Sdim	error = soreserve(so, div_sendspace, div_recvspace);
351336815Sdim	if (error)
352336815Sdim		return error;
353336815Sdim	s = splnet();
354336815Sdim	error = in_pcballoc(so, &divcbinfo, p);
355336815Sdim	splx(s);
356336815Sdim	if (error)
357336815Sdim		return error;
358336815Sdim	inp = (struct inpcb *)so->so_pcb;
359336815Sdim	inp->inp_ip_p = proto;
360336815Sdim	inp->inp_vflag |= INP_IPV4;
361336815Sdim	inp->inp_flags |= INP_HDRINCL;
362336815Sdim	/* The socket is always "connected" because
363336815Sdim	   we always know "where" to send the packet */
364336815Sdim	so->so_state |= SS_ISCONNECTED;
365336815Sdim#ifdef IPSEC
366336815Sdim	error = ipsec_init_policy(so, &inp->inp_sp);
367336815Sdim	if (error != 0) {
368336815Sdim		in_pcbdetach(inp);
369336815Sdim		return error;
370336815Sdim	}
371336815Sdim#endif /*IPSEC*/
372336815Sdim	return 0;
373336815Sdim}
374336815Sdim
375336815Sdimstatic int
376336815Sdimdiv_detach(struct socket *so)
377336815Sdim{
378336815Sdim	struct inpcb *inp;
379336815Sdim
380336815Sdim	inp = sotoinpcb(so);
381336815Sdim	if (inp == 0)
382336815Sdim		panic("div_detach");
383336815Sdim	in_pcbdetach(inp);
384336815Sdim	return 0;
385336815Sdim}
386336815Sdim
387336815Sdimstatic int
388336815Sdimdiv_abort(struct socket *so)
389336815Sdim{
390336815Sdim	soisdisconnected(so);
391336815Sdim	return div_detach(so);
392336815Sdim}
393336815Sdim
394336815Sdimstatic int
395336815Sdimdiv_disconnect(struct socket *so)
396336815Sdim{
397336815Sdim	if ((so->so_state & SS_ISCONNECTED) == 0)
398336815Sdim		return ENOTCONN;
399336815Sdim	return div_abort(so);
400336815Sdim}
401336815Sdim
402336815Sdimstatic int
403336815Sdimdiv_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
404336815Sdim{
405336815Sdim	struct inpcb *inp;
406336815Sdim	int s;
407336815Sdim	int error;
408336815Sdim
409336815Sdim	s = splnet();
410336815Sdim	inp = sotoinpcb(so);
411336815Sdim	/* in_pcbbind assumes that the socket is a sockaddr_in
412336815Sdim	 * and in_pcbbind requires a valid address. Since divert
413336815Sdim	 * sockets don't we need to make sure the address is
414336815Sdim	 * filled in properly.
415336815Sdim	 * XXX -- divert should not be abusing in_pcbind
416336815Sdim	 * and should probably have its own family.
417336815Sdim	 */
418336815Sdim	if (nam->sa_family != AF_INET) {
419336815Sdim		error = EAFNOSUPPORT;
420336815Sdim	} else {
421336815Sdim		((struct sockaddr_in *)nam)->sin_addr.s_addr = INADDR_ANY;
422336815Sdim		error = in_pcbbind(inp, nam, p);
423336815Sdim	}
424336815Sdim	splx(s);
425336815Sdim	return error;
426336815Sdim}
427336815Sdim
428336815Sdimstatic int
429336815Sdimdiv_shutdown(struct socket *so)
430336815Sdim{
431336815Sdim	socantsendmore(so);
432336815Sdim	return 0;
433336815Sdim}
434336815Sdim
435336815Sdimstatic int
436336815Sdimdiv_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
437336815Sdim	 struct mbuf *control, struct proc *p)
438336815Sdim{
439336815Sdim	/* Packet must have a header (but that's about it) */
440336815Sdim	if (m->m_len < sizeof (struct ip) &&
441336815Sdim	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
442336815Sdim		ipstat.ips_toosmall++;
443336815Sdim		m_freem(m);
444336815Sdim		return EINVAL;
445336815Sdim	}
446336815Sdim
447336815Sdim	/* Send packet */
448336815Sdim	return div_output(so, m, nam, control);
449336815Sdim}
450336815Sdim
451336815Sdimstatic int
452336815Sdimdiv_pcblist(SYSCTL_HANDLER_ARGS)
453336815Sdim{
454336815Sdim	int error, i, n, s;
455336815Sdim	struct inpcb *inp, **inp_list;
456336815Sdim	inp_gen_t gencnt;
457336815Sdim	struct xinpgen xig;
458336815Sdim
459336815Sdim	/*
460336815Sdim	 * The process of preparing the TCB list is too time-consuming and
461336815Sdim	 * resource-intensive to repeat twice on every request.
462336815Sdim	 */
463336815Sdim	if (req->oldptr == 0) {
464336815Sdim		n = divcbinfo.ipi_count;
465336815Sdim		req->oldidx = 2 * (sizeof xig)
466336815Sdim			+ (n + n/8) * sizeof(struct xinpcb);
467		return 0;
468	}
469
470	if (req->newptr != 0)
471		return EPERM;
472
473	/*
474	 * OK, now we're committed to doing something.
475	 */
476	s = splnet();
477	gencnt = divcbinfo.ipi_gencnt;
478	n = divcbinfo.ipi_count;
479	splx(s);
480
481	xig.xig_len = sizeof xig;
482	xig.xig_count = n;
483	xig.xig_gen = gencnt;
484	xig.xig_sogen = so_gencnt;
485	error = SYSCTL_OUT(req, &xig, sizeof xig);
486	if (error)
487		return error;
488
489	inp_list = malloc(n * sizeof *inp_list, M_TEMP, M_WAITOK);
490	if (inp_list == 0)
491		return ENOMEM;
492
493	s = splnet();
494	for (inp = divcbinfo.listhead->lh_first, i = 0; inp && i < n;
495	     inp = inp->inp_list.le_next) {
496		if (inp->inp_gencnt <= gencnt && !prison_xinpcb(req->p, inp))
497			inp_list[i++] = inp;
498	}
499	splx(s);
500	n = i;
501
502	error = 0;
503	for (i = 0; i < n; i++) {
504		inp = inp_list[i];
505		if (inp->inp_gencnt <= gencnt) {
506			struct xinpcb xi;
507			xi.xi_len = sizeof xi;
508			/* XXX should avoid extra copy */
509			bcopy(inp, &xi.xi_inp, sizeof *inp);
510			if (inp->inp_socket)
511				sotoxsocket(inp->inp_socket, &xi.xi_socket);
512			error = SYSCTL_OUT(req, &xi, sizeof xi);
513		}
514	}
515	if (!error) {
516		/*
517		 * Give the user an updated idea of our state.
518		 * If the generation differs from what we told
519		 * her before, she knows that something happened
520		 * while we were processing this request, and it
521		 * might be necessary to retry.
522		 */
523		s = splnet();
524		xig.xig_gen = divcbinfo.ipi_gencnt;
525		xig.xig_sogen = so_gencnt;
526		xig.xig_count = divcbinfo.ipi_count;
527		splx(s);
528		error = SYSCTL_OUT(req, &xig, sizeof xig);
529	}
530	free(inp_list, M_TEMP);
531	return error;
532}
533
534SYSCTL_DECL(_net_inet_divert);
535SYSCTL_PROC(_net_inet_divert, OID_AUTO, pcblist, CTLFLAG_RD, 0, 0,
536	    div_pcblist, "S,xinpcb", "List of active divert sockets");
537
538struct pr_usrreqs div_usrreqs = {
539	div_abort, pru_accept_notsupp, div_attach, div_bind,
540	pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
541	div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
542	pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
543	in_setsockaddr, sosend, soreceive, sopoll
544};
545