xref: /freebsd/sys/netinet/ip_divert.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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.14 1997/09/13 15:40:55 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  */
73 u_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  */
80 u_short ip_divert_ignore;
81 
82 /* Internal variables */
83 
84 static struct inpcbhead divcb;
85 static struct inpcbinfo divcbinfo;
86 
87 static u_long	div_sendspace = DIVSNDQ;	/* XXX sysctl ? */
88 static u_long	div_recvspace = DIVRCVQ;	/* XXX sysctl ? */
89 
90 /* Optimization: have this preinitialized */
91 static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
92 
93 /* Internal functions */
94 
95 static 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  */
101 void
102 div_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  */
122 void
123 div_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  */
204 static int
205 div_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 
262 cantsend:
263 	ip_divert_ignore = 0;
264 	m_freem(m);
265 	return error;
266 }
267 
268 static int
269 div_attach(struct socket *so, int proto, struct proc *p)
270 {
271 	struct inpcb *inp;
272 	int error;
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 	if ((error = soreserve(so, div_sendspace, div_recvspace)) ||
281 	    (error = in_pcballoc(so, &divcbinfo, p)))
282 		return error;
283 	inp = (struct inpcb *)so->so_pcb;
284 	inp->inp_ip_p = proto;
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 	return 0;
290 }
291 
292 static int
293 div_detach(struct socket *so)
294 {
295 	struct inpcb *inp;
296 
297 	inp = sotoinpcb(so);
298 	if (inp == 0)
299 		panic("div_detach");
300 	in_pcbdetach(inp);
301 	return 0;
302 }
303 
304 static int
305 div_abort(struct socket *so)
306 {
307 	soisdisconnected(so);
308 	return div_detach(so);
309 }
310 
311 static int
312 div_disconnect(struct socket *so)
313 {
314 	if ((so->so_state & SS_ISCONNECTED) == 0)
315 		return ENOTCONN;
316 	return div_abort(so);
317 }
318 
319 static int
320 div_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
321 {
322 	struct inpcb *inp;
323 	int s;
324 	int error;
325 
326 	s = splnet();
327 	inp = sotoinpcb(so);
328 	error = in_pcbbind(inp, nam, p);
329 	splx(s);
330 	return 0;
331 }
332 
333 static int
334 div_shutdown(struct socket *so)
335 {
336 	socantsendmore(so);
337 	return 0;
338 }
339 
340 static int
341 div_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
342 	 struct mbuf *control, struct proc *p)
343 {
344 	/* Packet must have a header (but that's about it) */
345 	if (m->m_len < sizeof (struct ip) ||
346 	    (m = m_pullup(m, sizeof (struct ip))) == 0) {
347 		ipstat.ips_toosmall++;
348 		m_freem(m);
349 		return EINVAL;
350 	}
351 
352 	/* Send packet */
353 	return div_output(so, m, nam, control);
354 }
355 
356 struct pr_usrreqs div_usrreqs = {
357 	div_abort, pru_accept_notsupp, div_attach, div_bind,
358 	pru_connect_notsupp, pru_connect2_notsupp, in_control, div_detach,
359 	div_disconnect, pru_listen_notsupp, in_setpeeraddr, pru_rcvd_notsupp,
360 	pru_rcvoob_notsupp, div_send, pru_sense_null, div_shutdown,
361 	in_setsockaddr, sosend, soreceive, sopoll
362 };
363