source: rtems/cpukit/libnetworking/netinet/ip_divert.c @ b25b88e7

4.104.11
Last change on this file since b25b88e7 was b25b88e7, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 28, 2010 at 5:50:29 AM

Add HAVE_CONFIG_H support to let files receive configure defines.

  • Property mode set to 100644
File size: 9.4 KB
Line 
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: src/sys/netinet/ip_divert.c,v 1.113 2005/05/13 11:44:37 glebius Exp $
30 */
31
32/*
33 *      $Id$
34 */
35
36#ifdef HAVE_CONFIG_H
37#include "config.h"
38#endif
39
40#include <sys/param.h>
41#include <rtems/bsd/sys/queue.h>
42#include <sys/malloc.h>
43#include <sys/mbuf.h>
44#include <sys/socket.h>
45#include <sys/protosw.h>
46#include <sys/socketvar.h>
47#include <errno.h>
48#include <sys/systm.h>
49
50#include <net/if.h>
51#include <net/route.h>
52
53#include <netinet/in.h>
54#include <netinet/in_systm.h>
55#include <netinet/ip.h>
56#include <netinet/in_pcb.h>
57#include <netinet/in_var.h>
58#include <netinet/ip_var.h>
59
60/*
61 * Divert sockets
62 */
63
64/*
65 * Allocate enough space to hold a full IP packet
66 */
67#define DIVSNDQ         (65536 + 100)
68#define DIVRCVQ         (65536 + 100)
69
70/* Global variables */
71
72/*
73 * ip_input() and ip_output() set this secret value before calling us to
74 * let us know which divert port to divert a packet to; this is done so
75 * we can use the existing prototype for struct protosw's pr_input().
76 * This is stored in host order.
77 */
78u_short ip_divert_port;
79
80/*
81 * We set this value to a non-zero port number when we want the call to
82 * ip_fw_chk() in ip_input() or ip_output() to ignore ``divert <port>''
83 * chain entries. This is stored in host order.
84 */
85u_short ip_divert_ignore;
86
87/* Internal variables. */
88static struct inpcbhead divcb;
89static struct inpcbinfo divcbinfo;
90
91static u_long   div_sendspace = DIVSNDQ;        /* XXX sysctl ? */
92static u_long   div_recvspace = DIVRCVQ;        /* XXX sysctl ? */
93
94/* Optimization: have this preinitialized */
95static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET, 0, { 0 }, { 0 } };
96
97/* Internal functions */
98
99static int div_output(struct socket *so,
100                struct mbuf *m, struct mbuf *addr, struct mbuf *control);
101
102/*
103 * Initialize divert connection block queue.
104 */
105void
106div_init(void)
107{
108        LIST_INIT(&divcb);
109        divcbinfo.listhead = &divcb;
110        /*
111         * XXX We don't use the hash list for divert IP, but it's easier
112         * to allocate a one entry hash list than it is to check all
113         * over the place for hashbase == NULL.
114         */
115        divcbinfo.hashbase = hashinit(1, M_PCB, &divcbinfo.hashmask);
116}
117
118/*
119 * Setup generic address and protocol structures
120 * for div_input routine, then pass them along with
121 * mbuf chain. ip->ip_len is assumed to have had
122 * the header length (hlen) subtracted out already.
123 * We tell whether the packet was incoming or outgoing
124 * by seeing if hlen == 0, which is a hack.
125 */
126void
127div_input(struct mbuf *m, int hlen)
128{
129        struct ip *ip;
130        struct inpcb *inp;
131        struct socket *sa;
132
133        /* Sanity check */
134        if (ip_divert_port == 0)
135                panic("div_input: port is 0");
136
137        /* Assure header */
138        if (m->m_len < sizeof(struct ip) &&
139            (m = m_pullup(m, sizeof(struct ip))) == 0) {
140                return;
141        }
142        ip = mtod(m, struct ip *);
143
144        /* Record divert port */
145        divsrc.sin_port = htons(ip_divert_port);
146
147        /* Restore packet header fields */
148        ip->ip_len += hlen;
149        HTONS(ip->ip_len);
150        HTONS(ip->ip_off);
151
152        /* Record receive interface address, if any */
153        divsrc.sin_addr.s_addr = 0;
154        if (hlen) {
155                struct ifaddr *ifa;
156
157#ifdef DIAGNOSTIC
158                /* Sanity check */
159                if (!(m->m_flags & M_PKTHDR))
160                        panic("div_input: no pkt hdr");
161#endif
162
163                /* More fields affected by ip_input() */
164                HTONS(ip->ip_id);
165
166                /* Find IP address for recieve interface */
167                for (ifa = m->m_pkthdr.rcvif->if_addrlist;
168                    ifa != NULL; ifa = ifa->ifa_next) {
169                        if (ifa->ifa_addr == NULL)
170                                continue;
171                        if (ifa->ifa_addr->sa_family != AF_INET)
172                                continue;
173                        divsrc.sin_addr =
174                            ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
175                        break;
176                }
177        }
178
179        /* Put packet on socket queue, if any */
180        sa = NULL;
181        for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
182                if (inp->inp_lport == htons(ip_divert_port))
183                        sa = inp->inp_socket;
184        }
185        if (sa) {
186                if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
187                                m, (struct mbuf *)0) == 0)
188                        m_freem(m);
189                else
190                        sorwakeup(sa);
191        } else {
192                m_freem(m);
193                ipstat.ips_noproto++;
194                ipstat.ips_delivered--;
195        }
196}
197
198/*
199 * Deliver packet back into the IP processing machinery.
200 *
201 * If no address specified, or address is 0.0.0.0, send to ip_output();
202 * otherwise, send to ip_input() and mark as having been received on
203 * the interface with that address.
204 *
205 * If no address specified, or dest port is 0, allow packet to divert
206 * back to this socket; otherwise, don't.
207 */
208static int
209div_output(struct socket *so, struct mbuf *m,
210        struct mbuf *addr, struct mbuf *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
263cantsend:
264        ip_divert_ignore = 0;
265        m_freem(m);
266        return error;
267}
268
269/*ARGSUSED*/
270int
271div_usrreq(
272        struct socket *so,
273        int req,
274        struct mbuf *m,
275        struct mbuf *nam, 
276        struct mbuf *control )
277{
278        register int error = 0;
279        register struct inpcb *inp = sotoinpcb(so);
280        int s;
281
282        if (inp == NULL && req != PRU_ATTACH) {
283                error = EINVAL;
284                goto release;
285        }
286        switch (req) {
287
288        case PRU_ATTACH:
289                if (inp)
290                        panic("div_attach");
291                if ((so->so_state & SS_PRIV) == 0) {
292                        error = EACCES;
293                        break;
294                }
295                s = splnet();
296                error = in_pcballoc(so, &divcbinfo);
297                splx(s);
298                if (error)
299                        break;
300                error = soreserve(so, div_sendspace, div_recvspace);
301                if (error)
302                        break;
303                inp = (struct inpcb *)so->so_pcb;
304                inp->inp_ip_p = (intptr_t)nam;  /* XXX */
305                inp->inp_flags |= INP_HDRINCL;
306                /* The socket is always "connected" because
307                   we always know "where" to send the packet */
308                so->so_state |= SS_ISCONNECTED;
309                break;
310
311        case PRU_DISCONNECT:
312                if ((so->so_state & SS_ISCONNECTED) == 0) {
313                        error = ENOTCONN;
314                        break;
315                }
316                /* FALLTHROUGH */
317        case PRU_ABORT:
318                soisdisconnected(so);
319                /* FALLTHROUGH */
320        case PRU_DETACH:
321                if (inp == 0)
322                        panic("div_detach");
323                in_pcbdetach(inp);
324                break;
325
326        case PRU_BIND:
327                s = splnet();
328                error = in_pcbbind(inp, nam);
329                splx(s);
330                break;
331
332        /*
333         * Mark the connection as being incapable of further input.
334         */
335        case PRU_SHUTDOWN:
336                socantsendmore(so);
337                break;
338
339        case PRU_SEND:
340                /* Packet must have a header (but that's about it) */
341                if (m->m_len < sizeof (struct ip) ||
342                    (m = m_pullup(m, sizeof (struct ip))) == 0) {
343                        ipstat.ips_toosmall++;
344                        error = EINVAL;
345                        break;
346                }
347
348                /* Send packet */
349                error = div_output(so, m, nam, control); 
350                m = NULL;
351                break;
352
353        case PRU_SOCKADDR:
354                in_setsockaddr(inp, nam);
355                break;
356
357        case PRU_SENSE:
358                /*
359                 * stat: don't bother with a blocksize.
360                 */
361                return (0);
362
363        /*
364         * Not supported.
365         */
366        case PRU_CONNECT:
367        case PRU_CONNECT2:
368        case PRU_CONTROL:
369        case PRU_RCVOOB:
370        case PRU_RCVD:
371        case PRU_LISTEN:
372        case PRU_ACCEPT:
373        case PRU_SENDOOB:
374        case PRU_PEERADDR:
375                error = EOPNOTSUPP;
376                break;
377
378        default:
379                panic("div_usrreq");
380        }
381release:
382        if (m)
383                m_freem(m);
384        return (error);
385}
Note: See TracBrowser for help on using the repository browser.