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