ip_divert.c revision 32821
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.17 1998/01/08 23:41:50 eivind 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	divcbinfo.porthashbase = hashinit(1, M_PCB, &divcbinfo.porthashmask);
119}
120
121/*
122 * Setup generic address and protocol structures
123 * for div_input routine, then pass them along with
124 * mbuf chain. ip->ip_len is assumed to have had
125 * the header length (hlen) subtracted out already.
126 * We tell whether the packet was incoming or outgoing
127 * by seeing if hlen == 0, which is a hack.
128 */
129void
130div_input(struct mbuf *m, int hlen)
131{
132	struct ip *ip;
133	struct inpcb *inp;
134	struct socket *sa;
135
136	/* Sanity check */
137	if (ip_divert_port == 0)
138		panic("div_input: port is 0");
139
140	/* Assure header */
141	if (m->m_len < sizeof(struct ip) &&
142	    (m = m_pullup(m, sizeof(struct ip))) == 0) {
143		return;
144	}
145	ip = mtod(m, struct ip *);
146
147	/* Record divert port */
148	divsrc.sin_port = htons(ip_divert_port);
149
150	/* Restore packet header fields */
151	ip->ip_len += hlen;
152	HTONS(ip->ip_len);
153	HTONS(ip->ip_off);
154
155	/* Record receive interface address, if any */
156	divsrc.sin_addr.s_addr = 0;
157	if (hlen) {
158		struct ifaddr *ifa;
159
160#ifdef DIAGNOSTIC
161		/* Sanity check */
162		if (!(m->m_flags & M_PKTHDR))
163			panic("div_input: no pkt hdr");
164#endif
165
166		/* More fields affected by ip_input() */
167		HTONS(ip->ip_id);
168
169		/* Find IP address for recieve interface */
170		for (ifa = m->m_pkthdr.rcvif->if_addrhead.tqh_first;
171		    ifa != NULL; ifa = ifa->ifa_link.tqe_next) {
172			if (ifa->ifa_addr == NULL)
173				continue;
174			if (ifa->ifa_addr->sa_family != AF_INET)
175				continue;
176			divsrc.sin_addr =
177			    ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
178			break;
179		}
180	}
181
182	/* Put packet on socket queue, if any */
183	sa = NULL;
184	for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
185		if (inp->inp_lport == htons(ip_divert_port))
186			sa = inp->inp_socket;
187	}
188	if (sa) {
189		if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
190				m, (struct mbuf *)0) == 0)
191			m_freem(m);
192		else
193			sorwakeup(sa);
194	} else {
195		m_freem(m);
196		ipstat.ips_noproto++;
197		ipstat.ips_delivered--;
198        }
199}
200
201/*
202 * Deliver packet back into the IP processing machinery.
203 *
204 * If no address specified, or address is 0.0.0.0, send to ip_output();
205 * otherwise, send to ip_input() and mark as having been received on
206 * the interface with that address.
207 *
208 * If no address specified, or dest port is 0, allow packet to divert
209 * back to this socket; otherwise, don't.
210 */
211static int
212div_output(so, m, addr, control)
213	struct socket *so;
214	register struct mbuf *m;
215	struct sockaddr *addr;
216	struct mbuf *control;
217{
218	register struct inpcb *const inp = sotoinpcb(so);
219	register struct ip *const ip = mtod(m, struct ip *);
220	struct sockaddr_in *sin = NULL;
221	int error = 0;
222
223	if (control)
224		m_freem(control);		/* XXX */
225	if (addr)
226		sin = (struct sockaddr_in *)addr;
227
228	/* Loopback avoidance option */
229	ip_divert_ignore = ntohs(inp->inp_lport);
230
231	/* Reinject packet into the system as incoming or outgoing */
232	if (!sin || sin->sin_addr.s_addr == 0) {
233		/* Don't allow both user specified and setsockopt options,
234		   and don't allow packet length sizes that will crash */
235		if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
236		     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
237			error = EINVAL;
238			goto cantsend;
239		}
240
241		/* Convert fields to host order for ip_output() */
242		NTOHS(ip->ip_len);
243		NTOHS(ip->ip_off);
244
245		/* Send packet to output processing */
246		ipstat.ips_rawout++;			/* XXX */
247		error = ip_output(m, inp->inp_options, &inp->inp_route,
248			(so->so_options & SO_DONTROUTE) |
249			IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
250	} else {
251		struct ifaddr *ifa;
252
253		/* Find receive interface with the given IP address */
254		sin->sin_port = 0;
255		if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
256			error = EADDRNOTAVAIL;
257			goto cantsend;
258		}
259		m->m_pkthdr.rcvif = ifa->ifa_ifp;
260
261		/* Send packet to input processing */
262		ip_input(m);
263	}
264
265	/* Reset for next time (and other packets) */
266	ip_divert_ignore = 0;
267	return error;
268
269cantsend:
270	ip_divert_ignore = 0;
271	m_freem(m);
272	return error;
273}
274
275static int
276div_attach(struct socket *so, int proto, struct proc *p)
277{
278	struct inpcb *inp;
279	int error, s;
280
281	inp  = sotoinpcb(so);
282	if (inp)
283		panic("div_attach");
284	if (p && (error = suser(p->p_ucred, &p->p_acflag)) != 0)
285		return error;
286
287	s = splnet();
288	error = in_pcballoc(so, &divcbinfo, p);
289	splx(s);
290	if (error)
291		return error;
292	error = soreserve(so, div_sendspace, div_recvspace);
293	if (error)
294		return error;
295	inp = (struct inpcb *)so->so_pcb;
296	inp->inp_ip_p = proto;
297	inp->inp_flags |= INP_HDRINCL;
298	/* The socket is always "connected" because
299	   we always know "where" to send the packet */
300	so->so_state |= SS_ISCONNECTED;
301	return 0;
302}
303
304static int
305div_detach(struct socket *so)
306{
307	struct inpcb *inp;
308
309	inp = sotoinpcb(so);
310	if (inp == 0)
311		panic("div_detach");
312	in_pcbdetach(inp);
313	return 0;
314}
315
316static int
317div_abort(struct socket *so)
318{
319	soisdisconnected(so);
320	return div_detach(so);
321}
322
323static int
324div_disconnect(struct socket *so)
325{
326	if ((so->so_state & SS_ISCONNECTED) == 0)
327		return ENOTCONN;
328	return div_abort(so);
329}
330
331static int
332div_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
333{
334	struct inpcb *inp;
335	int s;
336	int error;
337
338	s = splnet();
339	inp = sotoinpcb(so);
340	error = in_pcbbind(inp, nam, p);
341	splx(s);
342	return 0;
343}
344
345static int
346div_shutdown(struct socket *so)
347{
348	socantsendmore(so);
349	return 0;
350}
351
352static int
353div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
354	 struct mbuf *control, struct proc *p)
355{
356	/* Packet must have a header (but that's about it) */
357	if (m->m_len < sizeof (struct ip) ||
358	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
359		ipstat.ips_toosmall++;
360		m_freem(m);
361		return EINVAL;
362	}
363
364	/* Send packet */
365	return div_output(so, m, nam, control);
366}
367
368struct pr_usrreqs div_usrreqs = {
369	div_abort, pru_accept_notsupp, div_attach, div_bind,
370	pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
371	div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
372	pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
373	in_setsockaddr, sosend, soreceive, sopoll
374};
375