Initial import from FreeBSD RELENG_4:
[games.git] / sys / netproto / ns / ns_input.c
1 /*
2  * Copyright (c) 1984, 1985, 1986, 1987, 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  *      @(#)ns_input.c  8.1 (Berkeley) 6/10/93
34  * $FreeBSD: src/sys/netns/ns_input.c,v 1.13 2000/02/13 03:32:04 peter Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/domain.h>
42 #include <sys/protosw.h>
43 #include <sys/socket.h>
44 #include <sys/socketvar.h>
45 #include <sys/errno.h>
46 #include <sys/time.h>
47 #include <sys/kernel.h>
48
49 #include <net/if.h>
50 #include <net/route.h>
51 #include <net/raw_cb.h>
52 #include <net/netisr.h>
53 #include <net/intrq.h>
54
55 #include <netns/ns.h>
56 #include <netns/ns_if.h>
57 #include <netns/ns_pcb.h>
58 #include <netns/idp.h>
59 #include <netns/idp_var.h>
60 #include <netns/ns_error.h>
61
62 /*
63  * NS initialization.
64  */
65 union ns_host   ns_thishost;
66 union ns_host   ns_zerohost;
67 union ns_host   ns_broadhost;
68 union ns_net    ns_zeronet;
69 union ns_net    ns_broadnet;
70 struct sockaddr_ns ns_netmask, ns_hostmask;
71
72 static u_short allones[] = {-1, -1, -1};
73
74 struct nspcb nspcb;
75 struct nspcb nsrawpcb;
76
77 int     nsqmaxlen = IFQ_MAXLEN;
78
79 int     idpcksum = 1;
80 long    ns_pexseq;
81
82 const int       nsintrq_present = 1;
83
84 ns_init()
85 {
86         extern struct timeval time;
87
88         ns_broadhost = * (union ns_host *) allones;
89         ns_broadnet = * (union ns_net *) allones;
90         nspcb.nsp_next = nspcb.nsp_prev = &nspcb;
91         nsrawpcb.nsp_next = nsrawpcb.nsp_prev = &nsrawpcb;
92         nsintrq.ifq_maxlen = nsqmaxlen;
93         ns_pexseq = time.tv_usec;
94         ns_netmask.sns_len = 6;
95         ns_netmask.sns_addr.x_net = ns_broadnet;
96         ns_hostmask.sns_len = 12;
97         ns_hostmask.sns_addr.x_net = ns_broadnet;
98         ns_hostmask.sns_addr.x_host = ns_broadhost;
99         register_netisr(NETISR_NS, nsintr);
100 }
101
102 /*
103  * Idp input routine.  Pass to next level.
104  */
105 int nsintr_getpck = 0;
106 int nsintr_swtch = 0;
107 void
108 nsintr(void)
109 {
110         register struct idp *idp;
111         register struct mbuf *m;
112         register struct nspcb *nsp;
113         register int i;
114         int len, s, error;
115         char oddpacketp;
116
117 next:
118         /*
119          * Get next datagram off input queue and get IDP header
120          * in first mbuf.
121          */
122         s = splimp();
123         IF_DEQUEUE(&nsintrq, m);
124         splx(s);
125         nsintr_getpck++;
126         if (m == 0)
127                 return;
128         if ((m->m_flags & M_EXT || m->m_len < sizeof (struct idp)) &&
129             (m = m_pullup(m, sizeof (struct idp))) == 0) {
130                 idpstat.idps_toosmall++;
131                 goto next;
132         }
133
134         /*
135          * Give any raw listeners a crack at the packet
136          */
137         for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) {
138                 struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL);
139                 if (m1) idp_input(m1, nsp);
140         }
141
142         idp = mtod(m, struct idp *);
143         len = ntohs(idp->idp_len);
144         if (oddpacketp = len & 1) {
145                 len++;          /* If this packet is of odd length,
146                                    preserve garbage byte for checksum */
147         }
148
149         /*
150          * Check that the amount of data in the buffers
151          * is as at least much as the IDP header would have us expect.
152          * Trim mbufs if longer than we expect.
153          * Drop packet if shorter than we expect.
154          */
155         if (m->m_pkthdr.len < len) {
156                 idpstat.idps_tooshort++;
157                 goto bad;
158         }
159         if (m->m_pkthdr.len > len) {
160                 if (m->m_len == m->m_pkthdr.len) {
161                         m->m_len = len;
162                         m->m_pkthdr.len = len;
163                 } else
164                         m_adj(m, len - m->m_pkthdr.len);
165         }
166         if (idpcksum && ((i = idp->idp_sum)!=0xffff)) {
167                 idp->idp_sum = 0;
168                 if (i != (idp->idp_sum = ns_cksum(m, len))) {
169                         idpstat.idps_badsum++;
170                         idp->idp_sum = i;
171                         if (ns_hosteqnh(ns_thishost, idp->idp_dna.x_host))
172                                 error = NS_ERR_BADSUM;
173                         else
174                                 error = NS_ERR_BADSUM_T;
175                         ns_error(m, error, 0);
176                         goto next;
177                 }
178         }
179         /*
180          * Is this a directed broadcast?
181          */
182         if (ns_hosteqnh(ns_broadhost,idp->idp_dna.x_host)) {
183                 if ((!ns_neteq(idp->idp_dna, idp->idp_sna)) &&
184                     (!ns_neteqnn(idp->idp_dna.x_net, ns_broadnet)) &&
185                     (!ns_neteqnn(idp->idp_sna.x_net, ns_zeronet)) &&
186                     (!ns_neteqnn(idp->idp_dna.x_net, ns_zeronet)) ) {
187                         /*
188                          * Look to see if I need to eat this packet.
189                          * Algorithm is to forward all young packets
190                          * and prematurely age any packets which will
191                          * by physically broadcasted.
192                          * Any very old packets eaten without forwarding
193                          * would die anyway.
194                          *
195                          * Suggestion of Bill Nesheim, Cornell U.
196                          */
197                         if (idp->idp_tc < NS_MAXHOPS) {
198                                 idp_forward(m);
199                                 goto next;
200                         }
201                 }
202         /*
203          * Is this our packet? If not, forward.
204          */
205         } else if (!ns_hosteqnh(ns_thishost,idp->idp_dna.x_host)) {
206                 idp_forward(m);
207                 goto next;
208         }
209         /*
210          * Locate pcb for datagram.
211          */
212         nsp = ns_pcblookup(&idp->idp_sna, idp->idp_dna.x_port, NS_WILDCARD);
213         /*
214          * Switch out to protocol's input routine.
215          */
216         nsintr_swtch++;
217         if (nsp) {
218                 if (oddpacketp) {
219                         m_adj(m, -1);
220                 }
221                 if ((nsp->nsp_flags & NSP_ALL_PACKETS)==0)
222                         switch (idp->idp_pt) {
223
224                             case NSPROTO_SPP:
225                                     spp_input(m, nsp);
226                                     goto next;
227
228                             case NSPROTO_ERROR:
229                                     ns_err_input(m);
230                                     goto next;
231                         }
232                 idp_input(m, nsp);
233         } else {
234                 ns_error(m, NS_ERR_NOSOCK, 0);
235         }
236         goto next;
237
238 bad:
239         m_freem(m);
240         goto next;
241 }
242
243 u_char nsctlerrmap[PRC_NCMDS] = {
244         ECONNABORTED,   ECONNABORTED,   0,              0,
245         0,              0,              EHOSTDOWN,      EHOSTUNREACH,
246         ENETUNREACH,    EHOSTUNREACH,   ECONNREFUSED,   ECONNREFUSED,
247         EMSGSIZE,       0,              0,              0,
248         0,              0,              0,              0
249 };
250
251 int idp_donosocks = 1;
252
253 idp_ctlinput(cmd, arg)
254         int cmd;
255         caddr_t arg;
256 {
257         struct ns_addr *ns;
258         struct nspcb *nsp;
259         struct ns_errp *errp;
260         int idp_abort();
261         extern struct nspcb *idp_drop();
262         int type;
263
264         if (cmd < 0 || cmd > PRC_NCMDS)
265                 return;
266         if (nsctlerrmap[cmd] == 0)
267                 return;         /* XXX */
268         type = NS_ERR_UNREACH_HOST;
269         switch (cmd) {
270                 struct sockaddr_ns *sns;
271
272         case PRC_IFDOWN:
273         case PRC_HOSTDEAD:
274         case PRC_HOSTUNREACH:
275                 sns = (struct sockaddr_ns *)arg;
276                 if (sns->sns_family != AF_NS)
277                         return;
278                 ns = &sns->sns_addr;
279                 break;
280
281         default:
282                 errp = (struct ns_errp *)arg;
283                 ns = &errp->ns_err_idp.idp_dna;
284                 type = errp->ns_err_num;
285                 type = ntohs((u_short)type);
286         }
287         switch (type) {
288
289         case NS_ERR_UNREACH_HOST:
290                 ns_pcbnotify(ns, (int)nsctlerrmap[cmd], idp_abort, (long)0);
291                 break;
292
293         case NS_ERR_NOSOCK:
294                 nsp = ns_pcblookup(ns, errp->ns_err_idp.idp_sna.x_port,
295                         NS_WILDCARD);
296                 if(nsp && idp_donosocks && ! ns_nullhost(nsp->nsp_faddr))
297                         (void) idp_drop(nsp, (int)nsctlerrmap[cmd]);
298         }
299 }
300
301 int     idpprintfs = 0;
302 int     idpforwarding = 1;
303 /*
304  * Forward a packet.  If some error occurs return the sender
305  * an error packet.  Note we can't always generate a meaningful
306  * error message because the NS errors don't have a large enough repetoire
307  * of codes and types.
308  */
309 struct route idp_droute;
310 struct route idp_sroute;
311
312 idp_forward(m)
313 struct mbuf *m;
314 {
315         register struct idp *idp = mtod(m, struct idp *);
316         register int error, type, code;
317         struct mbuf *mcopy = NULL;
318         int agedelta = 1;
319         int flags = NS_FORWARDING;
320         int ok_there = 0;
321         int ok_back = 0;
322
323         if (idpprintfs) {
324                 printf("forward: src ");
325                 ns_printhost(&idp->idp_sna);
326                 printf(", dst ");
327                 ns_printhost(&idp->idp_dna);
328                 printf("hop count %d\n", idp->idp_tc);
329         }
330         if (idpforwarding == 0) {
331                 /* can't tell difference between net and host */
332                 type = NS_ERR_UNREACH_HOST, code = 0;
333                 goto senderror;
334         }
335         idp->idp_tc++;
336         if (idp->idp_tc > NS_MAXHOPS) {
337                 type = NS_ERR_TOO_OLD, code = 0;
338                 goto senderror;
339         }
340         /*
341          * Save at most 42 bytes of the packet in case
342          * we need to generate an NS error message to the src.
343          */
344         mcopy = m_copy(m, 0, imin((int)ntohs(idp->idp_len), 42));
345
346         if ((ok_there = idp_do_route(&idp->idp_dna,&idp_droute))==0) {
347                 type = NS_ERR_UNREACH_HOST, code = 0;
348                 goto senderror;
349         }
350         /*
351          * Here we think about  forwarding  broadcast packets,
352          * so we try to insure that it doesn't go back out
353          * on the interface it came in on.  Also, if we
354          * are going to physically broadcast this, let us
355          * age the packet so we can eat it safely the second time around.
356          */
357         if (idp->idp_dna.x_host.c_host[0] & 0x1) {
358                 struct ns_ifaddr *ia = ns_iaonnetof(&idp->idp_dna);
359                 struct ifnet *ifp;
360                 if (ia) {
361                         /* I'm gonna hafta eat this packet */
362                         agedelta += NS_MAXHOPS - idp->idp_tc;
363                         idp->idp_tc = NS_MAXHOPS;
364                 }
365                 if ((ok_back = idp_do_route(&idp->idp_sna,&idp_sroute))==0) {
366                         /* error = ENETUNREACH; He'll never get it! */
367                         m_freem(m);
368                         goto cleanup;
369                 }
370                 if (idp_droute.ro_rt &&
371                     (ifp=idp_droute.ro_rt->rt_ifp) &&
372                     idp_sroute.ro_rt &&
373                     (ifp!=idp_sroute.ro_rt->rt_ifp)) {
374                         flags |= NS_ALLOWBROADCAST;
375                 } else {
376                         type = NS_ERR_UNREACH_HOST, code = 0;
377                         goto senderror;
378                 }
379         }
380         /* need to adjust checksum */
381         if (idp->idp_sum!=0xffff) {
382                 union bytes {
383                         u_char c[4];
384                         u_short s[2];
385                         long l;
386                 } x;
387                 register int shift;
388                 x.l = 0; x.c[0] = agedelta;
389                 shift = (((((int)ntohs(idp->idp_len))+1)>>1)-2) & 0xf;
390                 x.l = idp->idp_sum + (x.s[0] << shift);
391                 x.l = x.s[0] + x.s[1];
392                 x.l = x.s[0] + x.s[1];
393                 if (x.l==0xffff) idp->idp_sum = 0; else idp->idp_sum = x.l;
394         }
395         if ((error = ns_output(m, &idp_droute, flags)) &&
396             (mcopy!=NULL)) {
397                 idp = mtod(mcopy, struct idp *);
398                 type = NS_ERR_UNSPEC_T, code = 0;
399                 switch (error) {
400
401                 case ENETUNREACH:
402                 case EHOSTDOWN:
403                 case EHOSTUNREACH:
404                 case ENETDOWN:
405                 case EPERM:
406                         type = NS_ERR_UNREACH_HOST;
407                         break;
408
409                 case EMSGSIZE:
410                         type = NS_ERR_TOO_BIG;
411                         code = 576; /* too hard to figure out mtu here */
412                         break;
413
414                 case ENOBUFS:
415                         type = NS_ERR_UNSPEC_T;
416                         break;
417                 }
418                 mcopy = NULL;
419         senderror:
420                 ns_error(m, type, code);
421         }
422 cleanup:
423         if (ok_there)
424                 idp_undo_route(&idp_droute);
425         if (ok_back)
426                 idp_undo_route(&idp_sroute);
427         if (mcopy != NULL)
428                 m_freem(mcopy);
429 }
430
431 idp_do_route(src, ro)
432 struct ns_addr *src;
433 struct route *ro;
434 {
435
436         struct sockaddr_ns *dst;
437
438         bzero((caddr_t)ro, sizeof (*ro));
439         dst = (struct sockaddr_ns *)&ro->ro_dst;
440
441         dst->sns_len = sizeof(*dst);
442         dst->sns_family = AF_NS;
443         dst->sns_addr = *src;
444         dst->sns_addr.x_port = 0;
445         rtalloc(ro);
446         if (ro->ro_rt == 0 || ro->ro_rt->rt_ifp == 0) {
447                 return (0);
448         }
449         ro->ro_rt->rt_use++;
450         return (1);
451 }
452
453 idp_undo_route(ro)
454 register struct route *ro;
455 {
456         if (ro->ro_rt) {RTFREE(ro->ro_rt);}
457 }
458
459 ns_watch_output(m, ifp)
460 struct mbuf *m;
461 struct ifnet *ifp;
462 {
463         register struct nspcb *nsp;
464         register struct ifaddr *ifa;
465         /*
466          * Give any raw listeners a crack at the packet
467          */
468         for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) {
469                 struct mbuf *m0 = m_copy(m, 0, (int)M_COPYALL);
470                 if (m0) {
471                         register struct idp *idp;
472
473                         M_PREPEND(m0, sizeof (*idp), M_DONTWAIT);
474                         if (m0 == NULL)
475                                 continue;
476                         idp = mtod(m0, struct idp *);
477                         idp->idp_sna.x_net = ns_zeronet;
478                         idp->idp_sna.x_host = ns_thishost;
479                         if (ifp && (ifp->if_flags & IFF_POINTOPOINT))
480                             for(ifa = ifp->if_addrlist; ifa;
481                                                 ifa = ifa->ifa_next) {
482                                 if (ifa->ifa_addr->sa_family==AF_NS) {
483                                     idp->idp_sna = IA_SNS(ifa)->sns_addr;
484                                     break;
485                                 }
486                             }
487                         idp->idp_len = ntohl(m0->m_pkthdr.len);
488                         idp_input(m0, nsp);
489                 }
490         }
491 }