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