ip_divert.c revision 21673
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 *	$FreeBSD: head/sys/netinet/ip_divert.c 21673 1997-01-14 07:20:47Z jkh $
34 */
35
36#include <sys/param.h>
37#include <sys/queue.h>
38#include <sys/malloc.h>
39#include <sys/mbuf.h>
40#include <sys/socket.h>
41#include <sys/protosw.h>
42#include <sys/socketvar.h>
43#include <sys/errno.h>
44#include <sys/systm.h>
45
46#include <net/if.h>
47#include <net/route.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52#include <netinet/in_pcb.h>
53#include <netinet/in_var.h>
54#include <netinet/ip_var.h>
55
56/*
57 * Divert sockets
58 */
59
60/*
61 * Allocate enough space to hold a full IP packet
62 */
63#define	DIVSNDQ		(65536 + 100)
64#define	DIVRCVQ		(65536 + 100)
65
66/* Global variables */
67
68/*
69 * ip_input() and ip_output() set this secret value before calling us to
70 * let us know which divert port to divert a packet to; this is done so
71 * we can use the existing prototype for struct protosw's pr_input().
72 * This is stored in host order.
73 */
74u_short ip_divert_port;
75
76/*
77 * We set this value to a non-zero port number when we want the call to
78 * ip_fw_chk() in ip_input() or ip_output() to ignore ``divert <port>''
79 * chain entries. This is stored in host order.
80 */
81u_short ip_divert_ignore;
82
83/* Internal variables */
84
85static struct inpcbhead divcb;
86static struct inpcbinfo divcbinfo;
87
88static u_long	div_sendspace = DIVSNDQ;	/* XXX sysctl ? */
89static u_long	div_recvspace = DIVRCVQ;	/* XXX sysctl ? */
90
91/* Optimization: have this preinitialized */
92static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
93
94/* Internal functions */
95
96static int div_output(struct socket *so,
97		struct mbuf *m, struct mbuf *addr, struct mbuf *control);
98
99/*
100 * Initialize divert connection block queue.
101 */
102void
103div_init(void)
104{
105	LIST_INIT(&divcb);
106	divcbinfo.listhead = &divcb;
107	/*
108	 * XXX We don't use the hash list for divert IP, but it's easier
109	 * to allocate a one entry hash list than it is to check all
110	 * over the place for hashbase == NULL.
111	 */
112	divcbinfo.hashbase = phashinit(1, M_PCB, &divcbinfo.hashsize);
113}
114
115/*
116 * Setup generic address and protocol structures
117 * for div_input routine, then pass them along with
118 * mbuf chain. ip->ip_len is assumed to have had
119 * the header length (hlen) subtracted out already.
120 * We tell whether the packet was incoming or outgoing
121 * by seeing if hlen == 0, which is a hack.
122 */
123void
124div_input(struct mbuf *m, int hlen)
125{
126	register struct ip *ip = mtod(m, struct ip *);
127	register struct inpcb *inp;
128	register struct socket *sa;
129
130	/* Sanity check */
131	if (ip_divert_port == 0)
132		panic("div_input");
133
134	/* Record divert port */
135	divsrc.sin_port = htons(ip_divert_port);
136
137	/* Restore packet header fields */
138	ip->ip_len += hlen;
139	HTONS(ip->ip_len);
140	HTONS(ip->ip_off);
141
142	/* Record receive interface address, if any */
143	divsrc.sin_addr.s_addr = 0;
144	if (hlen) {
145		struct ifaddr *ifa;
146
147		/* More fields affected by ip_input() */
148		HTONS(ip->ip_id);
149
150		/* Find IP address for recieve interface */
151		for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
152		    ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
153			if (ifa->ifa_addr == NULL)
154				continue;
155			if (ifa->ifa_addr->sa_family != AF_INET)
156				continue;
157			divsrc.sin_addr =
158			    ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
159			break;
160		}
161	}
162
163	/* Put packet on socket queue, if any */
164	sa = NULL;
165	for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
166		if (inp->inp_lport == htons(ip_divert_port))
167			sa = inp->inp_socket;
168	}
169	if (sa) {
170		if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
171				m, (struct mbuf *)0) == 0)
172			m_freem(m);
173		else
174			sorwakeup(sa);
175	} else {
176		m_freem(m);
177		ipstat.ips_noproto++;
178		ipstat.ips_delivered--;
179        }
180}
181
182/*
183 * Deliver packet back into the IP processing machinery.
184 *
185 * If no address specified, or address is 0.0.0.0, send to ip_output();
186 * otherwise, send to ip_input() and mark as having been received on
187 * the interface with that address.
188 *
189 * If no address specified, or dest port is 0, allow packet to divert
190 * back to this socket; otherwise, don't.
191 */
192static int
193div_output(so, m, addr, control)
194	struct socket *so;
195	register struct mbuf *m;
196	struct mbuf *addr, *control;
197{
198	register struct inpcb *const inp = sotoinpcb(so);
199	register struct ip *const ip = mtod(m, struct ip *);
200	struct sockaddr_in *sin = NULL;
201	int error = 0;
202
203	if (control)
204		m_freem(control);		/* XXX */
205	if (addr)
206		sin = mtod(addr, struct sockaddr_in *);
207
208	/* Loopback avoidance option */
209	if (sin && sin->sin_port)
210		ip_divert_ignore = ntohs(inp->inp_lport);
211
212	/* Reinject packet into the system as incoming or outgoing */
213	if (!sin || sin->sin_addr.s_addr == 0) {
214		/* Don't allow both user specified and setsockopt options,
215		   and don't allow packet length sizes that will crash */
216		if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
217		     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
218			error = EINVAL;
219			goto cantsend;
220		}
221
222		/* Convert fields to host order for ip_output() */
223		NTOHS(ip->ip_len);
224		NTOHS(ip->ip_off);
225
226		/* Send packet to output processing */
227		ipstat.ips_rawout++;			/* XXX */
228		error = ip_output(m, inp->inp_options, &inp->inp_route,
229			(so->so_options & SO_DONTROUTE) |
230			IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
231	} else {
232		struct ifaddr *ifa;
233
234		/* Find receive interface with the given IP address */
235		sin->sin_port = 0;
236		if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
237			error = EADDRNOTAVAIL;
238			goto cantsend;
239		}
240		m->m_pkthdr.rcvif = ifa->ifa_ifp;
241
242		/* Send packet to input processing */
243		ip_input(m);
244	}
245
246	/* Reset for next time (and other packets) */
247	ip_divert_ignore = 0;
248	return error;
249
250cantsend:
251	ip_divert_ignore = 0;
252	m_freem(m);
253	return error;
254}
255
256/*ARGSUSED*/
257int
258div_usrreq(so, req, m, nam, control)
259	register struct socket *so;
260	int req;
261	struct mbuf *m, *nam, *control;
262{
263	register int error = 0;
264	register struct inpcb *inp = sotoinpcb(so);
265	int s = 0;
266
267	if (inp == NULL && req != PRU_ATTACH) {
268		error = EINVAL;
269		goto release;
270	}
271	switch (req) {
272
273	case PRU_ATTACH:
274		if (inp)
275			panic("div_attach");
276		if ((so->so_state & SS_PRIV) == 0) {
277			error = EACCES;
278			break;
279		}
280		if ((error = soreserve(so, div_sendspace, div_recvspace)) ||
281		    (error = in_pcballoc(so, &divcbinfo)))
282			break;
283		inp = (struct inpcb *)so->so_pcb;
284		inp->inp_ip.ip_p = (int)nam;	/* XXX */
285		inp->inp_flags |= INP_HDRINCL;
286		/* The socket is always "connected" because
287		   we always know "where" to send the packet */
288		so->so_state |= SS_ISCONNECTED;
289		break;
290
291	case PRU_DISCONNECT:
292		if ((so->so_state & SS_ISCONNECTED) == 0) {
293			error = ENOTCONN;
294			break;
295		}
296		/* FALLTHROUGH */
297	case PRU_ABORT:
298		soisdisconnected(so);
299		/* FALLTHROUGH */
300	case PRU_DETACH:
301		if (inp == 0)
302			panic("div_detach");
303		in_pcbdetach(inp);
304		break;
305
306	case PRU_BIND:
307		s = splnet();
308		error = in_pcbbind(inp, nam);
309		splx(s);
310		break;
311
312	/*
313	 * Mark the connection as being incapable of further input.
314	 */
315	case PRU_SHUTDOWN:
316		socantsendmore(so);
317		break;
318
319	case PRU_SEND:
320		/* Packet must have a header (but that's about it) */
321		if (m->m_len < sizeof (struct ip) ||
322		    (m = m_pullup(m, sizeof (struct ip))) == 0) {
323			ipstat.ips_toosmall++;
324			error = EINVAL;
325			break;
326		}
327
328		/* Send packet */
329		error = div_output(so, m, nam, control);
330		m = NULL;
331		break;
332
333	case PRU_SOCKADDR:
334		in_setsockaddr(inp, nam);
335		break;
336
337	case PRU_SENSE:
338		/*
339		 * stat: don't bother with a blocksize.
340		 */
341		return (0);
342
343	/*
344	 * Not supported.
345	 */
346	case PRU_CONNECT:
347	case PRU_CONNECT2:
348	case PRU_CONTROL:
349	case PRU_RCVOOB:
350	case PRU_RCVD:
351	case PRU_LISTEN:
352	case PRU_ACCEPT:
353	case PRU_SENDOOB:
354	case PRU_PEERADDR:
355		error = EOPNOTSUPP;
356		break;
357
358	default:
359		panic("div_usrreq");
360	}
361release:
362	if (m)
363		m_freem(m);
364	return (error);
365}
366