ip_divert.c revision 31838
1/*
2 * Copyright (c) 1982, 1986, 1988, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 *	$Id: ip_divert.c,v 1.15 1997/09/14 03:10:39 peter Exp $
34 */
35
36#include <sys/param.h>
37#include <sys/malloc.h>
38#include <sys/mbuf.h>
39#include <sys/socket.h>
40#include <sys/protosw.h>
41#include <sys/socketvar.h>
42#include <sys/systm.h>
43#include <sys/proc.h>
44
45#include <net/if.h>
46#include <net/route.h>
47
48#include <netinet/in.h>
49#include <netinet/in_systm.h>
50#include <netinet/ip.h>
51#include <netinet/in_pcb.h>
52#include <netinet/in_var.h>
53#include <netinet/ip_var.h>
54
55/*
56 * Divert sockets
57 */
58
59/*
60 * Allocate enough space to hold a full IP packet
61 */
62#define	DIVSNDQ		(65536 + 100)
63#define	DIVRCVQ		(65536 + 100)
64
65/* Global variables */
66
67/*
68 * ip_input() and ip_output() set this secret value before calling us to
69 * let us know which divert port to divert a packet to; this is done so
70 * we can use the existing prototype for struct protosw's pr_input().
71 * This is stored in host order.
72 */
73u_short ip_divert_port;
74
75/*
76 * We set this value to a non-zero port number when we want the call to
77 * ip_fw_chk() in ip_input() or ip_output() to ignore ``divert <port>''
78 * chain entries. This is stored in host order.
79 */
80u_short ip_divert_ignore;
81
82/* Internal variables */
83
84static struct inpcbhead divcb;
85static struct inpcbinfo divcbinfo;
86
87static u_long	div_sendspace = DIVSNDQ;	/* XXX sysctl ? */
88static u_long	div_recvspace = DIVRCVQ;	/* XXX sysctl ? */
89
90/* Optimization: have this preinitialized */
91static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
92
93/* Internal functions */
94
95static int div_output(struct socket *so,
96		struct mbuf *m, struct sockaddr *addr, struct mbuf *control);
97
98/*
99 * Initialize divert connection block queue.
100 */
101void
102div_init(void)
103{
104	LIST_INIT(&divcb);
105	divcbinfo.listhead = &divcb;
106	/*
107	 * XXX We don't use the hash list for divert IP, but it's easier
108	 * to allocate a one entry hash list than it is to check all
109	 * over the place for hashbase == NULL.
110	 */
111	divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask);
112}
113
114/*
115 * Setup generic address and protocol structures
116 * for div_input routine, then pass them along with
117 * mbuf chain. ip->ip_len is assumed to have had
118 * the header length (hlen) subtracted out already.
119 * We tell whether the packet was incoming or outgoing
120 * by seeing if hlen == 0, which is a hack.
121 */
122void
123div_input(struct mbuf *m, int hlen)
124{
125	struct ip *ip;
126	struct inpcb *inp;
127	struct socket *sa;
128
129	/* Sanity check */
130	if (ip_divert_port == 0)
131		panic("div_input: port is 0");
132
133	/* Assure header */
134	if (m->m_len < sizeof(struct ip) &&
135	    (m = m_pullup(m, sizeof(struct ip))) == 0) {
136		return;
137	}
138	ip = mtod(m, struct ip *);
139
140	/* Record divert port */
141	divsrc.sin_port = htons(ip_divert_port);
142
143	/* Restore packet header fields */
144	ip->ip_len += hlen;
145	HTONS(ip->ip_len);
146	HTONS(ip->ip_off);
147
148	/* Record receive interface address, if any */
149	divsrc.sin_addr.s_addr = 0;
150	if (hlen) {
151		struct ifaddr *ifa;
152
153#ifdef DIAGNOSTIC
154		/* Sanity check */
155		if (!(m->m_flags & M_PKTHDR))
156			panic("div_input: no pkt hdr");
157#endif
158
159		/* More fields affected by ip_input() */
160		HTONS(ip->ip_id);
161
162		/* Find IP address for recieve interface */
163		for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
164		    ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
165			if (ifa->ifa_addr == NULL)
166				continue;
167			if (ifa->ifa_addr->sa_family != AF_INET)
168				continue;
169			divsrc.sin_addr =
170			    ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
171			break;
172		}
173	}
174
175	/* Put packet on socket queue, if any */
176	sa = NULL;
177	for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
178		if (inp->inp_lport == htons(ip_divert_port))
179			sa = inp->inp_socket;
180	}
181	if (sa) {
182		if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
183				m, (struct mbuf *)0) == 0)
184			m_freem(m);
185		else
186			sorwakeup(sa);
187	} else {
188		m_freem(m);
189		ipstat.ips_noproto++;
190		ipstat.ips_delivered--;
191        }
192}
193
194/*
195 * Deliver packet back into the IP processing machinery.
196 *
197 * If no address specified, or address is 0.0.0.0, send to ip_output();
198 * otherwise, send to ip_input() and mark as having been received on
199 * the interface with that address.
200 *
201 * If no address specified, or dest port is 0, allow packet to divert
202 * back to this socket; otherwise, don't.
203 */
204static int
205div_output(so, m, addr, control)
206	struct socket *so;
207	register struct mbuf *m;
208	struct sockaddr *addr;
209	struct mbuf *control;
210{
211	register struct inpcb *const inp = sotoinpcb(so);
212	register struct ip *const ip = mtod(m, struct ip *);
213	struct sockaddr_in *sin = NULL;
214	int error = 0;
215
216	if (control)
217		m_freem(control);		/* XXX */
218	if (addr)
219		sin = (struct sockaddr_in *)addr;
220
221	/* Loopback avoidance option */
222	ip_divert_ignore = ntohs(inp->inp_lport);
223
224	/* Reinject packet into the system as incoming or outgoing */
225	if (!sin || sin->sin_addr.s_addr == 0) {
226		/* Don't allow both user specified and setsockopt options,
227		   and don't allow packet length sizes that will crash */
228		if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
229		     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
230			error = EINVAL;
231			goto cantsend;
232		}
233
234		/* Convert fields to host order for ip_output() */
235		NTOHS(ip->ip_len);
236		NTOHS(ip->ip_off);
237
238		/* Send packet to output processing */
239		ipstat.ips_rawout++;			/* XXX */
240		error = ip_output(m, inp->inp_options, &inp->inp_route,
241			(so->so_options & SO_DONTROUTE) |
242			IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
243	} else {
244		struct ifaddr *ifa;
245
246		/* Find receive interface with the given IP address */
247		sin->sin_port = 0;
248		if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
249			error = EADDRNOTAVAIL;
250			goto cantsend;
251		}
252		m->m_pkthdr.rcvif = ifa->ifa_ifp;
253
254		/* Send packet to input processing */
255		ip_input(m);
256	}
257
258	/* Reset for next time (and other packets) */
259	ip_divert_ignore = 0;
260	return error;
261
262cantsend:
263	ip_divert_ignore = 0;
264	m_freem(m);
265	return error;
266}
267
268static int
269div_attach(struct socket *so, int proto, struct proc *p)
270{
271	struct inpcb *inp;
272	int error, s;
273
274	inp  = sotoinpcb(so);
275	if (inp)
276		panic("div_attach");
277	if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
278		return error;
279
280	s = splnet();
281	error = in_pcballoc(so, &divcbinfo, p);
282	splx(s);
283	if (error)
284		return error;
285	error = soreserve(so, div_sendspace, div_recvspace);
286	if (error)
287		return error;
288	inp = (struct inpcb *)so->so_pcb;
289	inp->inp_ip_p = proto;
290	inp->inp_flags |= INP_HDRINCL;
291	/* The socket is always "connected" because
292	   we always know "where" to send the packet */
293	so->so_state |= SS_ISCONNECTED;
294	return 0;
295}
296
297static int
298div_detach(struct socket *so)
299{
300	struct inpcb *inp;
301
302	inp = sotoinpcb(so);
303	if (inp == 0)
304		panic("div_detach");
305	in_pcbdetach(inp);
306	return 0;
307}
308
309static int
310div_abort(struct socket *so)
311{
312	soisdisconnected(so);
313	return div_detach(so);
314}
315
316static int
317div_disconnect(struct socket *so)
318{
319	if ((so->so_state & SS_ISCONNECTED) == 0)
320		return ENOTCONN;
321	return div_abort(so);
322}
323
324static int
325div_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
326{
327	struct inpcb *inp;
328	int s;
329	int error;
330
331	s = splnet();
332	inp = sotoinpcb(so);
333	error = in_pcbbind(inp, nam, p);
334	splx(s);
335	return 0;
336}
337
338static int
339div_shutdown(struct socket *so)
340{
341	socantsendmore(so);
342	return 0;
343}
344
345static int
346div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
347	 struct mbuf *control, struct proc *p)
348{
349	/* Packet must have a header (but that's about it) */
350	if (m->m_len < sizeof (struct ip) ||
351	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
352		ipstat.ips_toosmall++;
353		m_freem(m);
354		return EINVAL;
355	}
356
357	/* Send packet */
358	return div_output(so, m, nam, control);
359}
360
361struct pr_usrreqs div_usrreqs = {
362	div_abort, pru_accept_notsupp, div_attach, div_bind,
363	pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
364	div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
365	pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
366	in_setsockaddr, sosend, soreceive, sopoll
367};
368