source: rtems/cpukit/libnetworking/netinet/ip_divert.c @ 23bf0865

4.104.114.84.95
Last change on this file since 23bf0865 was 23bf0865, checked in by Ralf Corsepius <ralf.corsepius@…>, on 05/24/05 at 04:25:19

Cosmetics from FreeBSD.

  • Property mode set to 100644
File size: 9.3 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#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
46#include <net/if.h>
47#include <net/route.h>
48
49#include <netinet/in.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52#include <netinet/in_pcb.h>
53#include <netinet/in_var.h>
54#include <netinet/ip_var.h>
55
56/*
57 * Divert sockets
58 */
59
60/*
61 * Allocate enough space to hold a full IP packet
62 */
63#define DIVSNDQ         (65536 + 100)
64#define DIVRCVQ         (65536 + 100)
65
66/* Global variables */
67
68/*
69 * ip_input() and ip_output() set this secret value before calling us to
70 * let us know which divert port to divert a packet to; this is done so
71 * we can use the existing prototype for struct protosw's pr_input().
72 * This is stored in host order.
73 */
74u_short ip_divert_port;
75
76/*
77 * We set this value to a non-zero port number when we want the call to
78 * ip_fw_chk() in ip_input() or ip_output() to ignore ``divert <port>''
79 * chain entries. This is stored in host order.
80 */
81u_short ip_divert_ignore;
82
83/* Internal variables. */
84static struct inpcbhead divcb;
85static struct inpcbinfo divcbinfo;
86
87static u_long   div_sendspace = DIVSNDQ;        /* XXX sysctl ? */
88static u_long   div_recvspace = DIVRCVQ;        /* XXX sysctl ? */
89
90/* Optimization: have this preinitialized */
91static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
92
93/* Internal functions */
94
95static int div_output(struct socket *so,
96                struct mbuf *m, struct mbuf *addr, struct mbuf *control);
97
98/*
99 * Initialize divert connection block queue.
100 */
101void
102div_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 */
122void
123div_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_addrlist;
164                    ifa != NULL; ifa = ifa->ifa_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 */
204static int
205div_output(struct socket *so, struct mbuf *m,
206        struct mbuf *addr, struct mbuf *control)
207{
208        register struct inpcb *const inp = sotoinpcb(so);
209        register struct ip *const ip = mtod(m, struct ip *);
210        struct sockaddr_in *sin = NULL;
211        int error = 0;
212
213        if (control)
214                m_freem(control);               /* XXX */
215        if (addr)
216                sin = mtod(addr, struct sockaddr_in *);
217
218        /* Loopback avoidance option */
219        ip_divert_ignore = ntohs(inp->inp_lport);
220
221        /* Reinject packet into the system as incoming or outgoing */
222        if (!sin || sin->sin_addr.s_addr == 0) {
223                /* Don't allow both user specified and setsockopt options,
224                   and don't allow packet length sizes that will crash */
225                if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
226                     ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
227                        error = EINVAL;
228                        goto cantsend;
229                }
230
231                /* Convert fields to host order for ip_output() */
232                NTOHS(ip->ip_len);
233                NTOHS(ip->ip_off);
234
235                /* Send packet to output processing */
236                ipstat.ips_rawout++;                    /* XXX */
237                error = ip_output(m, inp->inp_options, &inp->inp_route,
238                        (so->so_options & SO_DONTROUTE) |
239                        IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
240        } else {
241                struct ifaddr *ifa;
242
243                /* Find receive interface with the given IP address */
244                sin->sin_port = 0;
245                if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
246                        error = EADDRNOTAVAIL;
247                        goto cantsend;
248                }
249                m->m_pkthdr.rcvif = ifa->ifa_ifp;
250
251                /* Send packet to input processing */
252                ip_input(m);
253        }
254
255        /* Reset for next time (and other packets) */
256        ip_divert_ignore = 0;
257        return error;
258
259cantsend:
260        ip_divert_ignore = 0;
261        m_freem(m);
262        return error;
263}
264
265/*ARGSUSED*/
266int
267div_usrreq(so, req, m, nam, control)
268        register struct socket *so;
269        int req;
270        struct mbuf *m, *nam, *control;
271{
272        register int error = 0;
273        register struct inpcb *inp = sotoinpcb(so);
274        int s;
275
276        if (inp == NULL && req != PRU_ATTACH) {
277                error = EINVAL;
278                goto release;
279        }
280        switch (req) {
281
282        case PRU_ATTACH:
283                if (inp)
284                        panic("div_attach");
285                if ((so->so_state & SS_PRIV) == 0) {
286                        error = EACCES;
287                        break;
288                }
289                s = splnet();
290                error = in_pcballoc(so, &divcbinfo);
291                splx(s);
292                if (error)
293                        break;
294                error = soreserve(so, div_sendspace, div_recvspace);
295                if (error)
296                        break;
297                inp = (struct inpcb *)so->so_pcb;
298                inp->inp_ip_p = (int)nam;       /* XXX */
299                inp->inp_flags |= INP_HDRINCL;
300                /* The socket is always "connected" because
301                   we always know "where" to send the packet */
302                so->so_state |= SS_ISCONNECTED;
303                break;
304
305        case PRU_DISCONNECT:
306                if ((so->so_state & SS_ISCONNECTED) == 0) {
307                        error = ENOTCONN;
308                        break;
309                }
310                /* FALLTHROUGH */
311        case PRU_ABORT:
312                soisdisconnected(so);
313                /* FALLTHROUGH */
314        case PRU_DETACH:
315                if (inp == 0)
316                        panic("div_detach");
317                in_pcbdetach(inp);
318                break;
319
320        case PRU_BIND:
321                s = splnet();
322                error = in_pcbbind(inp, nam);
323                splx(s);
324                break;
325
326        /*
327         * Mark the connection as being incapable of further input.
328         */
329        case PRU_SHUTDOWN:
330                socantsendmore(so);
331                break;
332
333        case PRU_SEND:
334                /* Packet must have a header (but that's about it) */
335                if (m->m_len < sizeof (struct ip) ||
336                    (m = m_pullup(m, sizeof (struct ip))) == 0) {
337                        ipstat.ips_toosmall++;
338                        error = EINVAL;
339                        break;
340                }
341
342                /* Send packet */
343                error = div_output(so, m, nam, control);
344                m = NULL;
345                break;
346
347        case PRU_SOCKADDR:
348                in_setsockaddr(inp, nam);
349                break;
350
351        case PRU_SENSE:
352                /*
353                 * stat: don't bother with a blocksize.
354                 */
355                return (0);
356
357        /*
358         * Not supported.
359         */
360        case PRU_CONNECT:
361        case PRU_CONNECT2:
362        case PRU_CONTROL:
363        case PRU_RCVOOB:
364        case PRU_RCVD:
365        case PRU_LISTEN:
366        case PRU_ACCEPT:
367        case PRU_SENDOOB:
368        case PRU_PEERADDR:
369                error = EOPNOTSUPP;
370                break;
371
372        default:
373                panic("div_usrreq");
374        }
375release:
376        if (m)
377                m_freem(m);
378        return (error);
379}
Note: See TracBrowser for help on using the repository browser.