reindent-20030715
[openafs.git] / src / rx / IRIX / rx_knet.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include "afs/param.h"
12
13 RCSID
14     ("$Header$");
15
16 #include "rx/rx_kcommon.h"
17 #include "h/tcp-param.h"
18 /* This must be loaded after proc.h to avoid macro collision with a variable*/
19 #include "netinet/udp_var.h"
20
21
22
23
24 #ifdef RXK_LISTENER_ENV
25 /* osi_NetReceive
26  * OS dependent part of kernel RX listener thread.
27  *
28  * Arguments:
29  *      so      socket to receive on, typically rx_socket
30  *      from    pointer to a sockaddr_in. 
31  *      iov     array of iovecs to fill in.
32  *      iovcnt  how many iovecs there are.
33  *      lengthp IN/OUT in: total space available in iovecs. out: size of read.
34  *
35  * Return
36  * 0 if successful
37  * error code (such as EINTR) if not
38  *
39  * Environment
40  *      Note that the maximum number of iovecs is 2 + RX_MAXWVECS. This is
41  *      so we have a little space to look for packets larger than 
42  *      rx_maxReceiveSize.
43  */
44 int rxk_lastSocketError = 0;
45 int rxk_nSocketErrors = 0;
46 int rxk_nSignalsCleared = 0;
47
48 int
49 osi_NetReceive(osi_socket so, struct sockaddr_in *addr, struct iovec *dvec,
50                int nvecs, int *alength)
51 {
52     struct uio tuio;
53     int code;
54     struct mbuf *maddr = NULL;
55     struct sockaddr_in *taddr;
56     struct iovec tmpvec[RX_MAXWVECS + 2];
57 #ifdef AFS_SGI65_ENV
58     bhv_desc_t bhv;
59     BHV_PDATA(&bhv) = (void *)so;
60 #endif
61
62     tuio.uio_iov = tmpvec;
63     tuio.uio_iovcnt = nvecs;
64     tuio.uio_offset = 0;
65     tuio.uio_segflg = AFS_UIOSYS;
66     tuio.uio_fmode = 0;
67     tuio.uio_resid = *alength;
68     tuio.uio_pio = 0;
69     tuio.uio_pbuf = 0;
70
71     if (nvecs > RX_MAXWVECS + 2) {
72         osi_Panic("Too many (%d) iovecs passed to osi_NetReceive\n", nvecs);
73     }
74     memcpy(tmpvec, (char *)dvec, (RX_MAXWVECS + 1) * sizeof(struct iovec));
75 #ifdef AFS_SGI65_ENV
76     code = soreceive(&bhv, &maddr, &tuio, NULL, NULL);
77 #else
78     code = soreceive(so, &maddr, &tuio, NULL, NULL);
79 #endif
80
81     if (code) {
82 #ifdef AFS_SGI65_ENV
83         /* Clear the error before using the socket again. I've tried being nice
84          * and blocking SIGKILL and SIGSTOP from the kernel, but they get
85          * delivered anyway. So, time to be crude and just clear the signals
86          * pending on this thread.
87          */
88         if (code == EINTR) {
89             uthread_t *ut = curuthread;
90             int s;
91             s = ut_lock(ut);
92             sigemptyset(&ut->ut_sig);
93             ut->ut_cursig = 0;
94             thread_interrupt_clear(UT_TO_KT(ut), 1);
95             ut_unlock(ut, s);
96             rxk_nSignalsCleared++;
97         }
98 #endif
99         /* Clear the error before using the socket again. */
100         so->so_error = 0;
101         rxk_lastSocketError = code;
102         rxk_nSocketErrors++;
103         if (maddr)
104             m_freem(maddr);
105     } else {
106         *alength = *alength - tuio.uio_resid;
107         if (maddr) {
108             memcpy((char *)addr, (char *)mtod(maddr, struct sockaddr_in *),
109                    sizeof(struct sockaddr_in));
110             m_freem(maddr);
111         } else {
112             return -1;
113         }
114     }
115     return code;
116 }
117 #else /* RXK_LISTENER_ENV */
118
119 static struct protosw parent_proto;     /* udp proto switch */
120
121 /*
122  * RX input, fast timer and initialization routines. 
123  */
124
125 #ifdef AFS_SGI64_ENV
126 static void
127 rxk_input(struct mbuf *am, struct ifnet *aif, struct ipsec *spec)
128 #else
129 static void
130 rxk_input(struct mbuf *am, struct ifnet *aif)
131 #endif
132 {
133     void (*tproc) ();
134     register unsigned short *tsp;
135     int hdr;
136     struct udphdr *tu;
137     register struct ip *ti;
138     struct udpiphdr *tvu;
139     register int i;
140     char *phandle;
141     struct sockaddr_in taddr;
142     int tlen;
143     short port;
144     int data_len;
145
146     /* make sure we have base ip and udp headers in first mbuf */
147     if (am->m_off > MMAXOFF || am->m_len < 28) {
148         am = m_pullup(am, 28);
149         if (!am)
150             return;
151     }
152
153     hdr = (mtod(am, struct ip *))->ip_hl;
154     if (hdr > 5) {
155         /* pull up more, the IP hdr is bigger than usual */
156         if (am->m_len < (8 + (hdr << 2))) {
157             am = m_pullup(am, 8 + (hdr << 2));
158             if (!am)
159                 return;
160         }
161         ti = mtod(am, struct ip *);     /* recompute, since m_pullup allocates new mbuf */
162         tu = (struct udphdr *)(((char *)ti) + (hdr << 2));      /* skip ip hdr */
163     } else {
164         ti = mtod(am, struct ip *);
165         tu = (struct udphdr *)(((char *)ti) + 20);      /* skip basic ip hdr */
166     }
167     /* now read the port out */
168     port = tu->uh_dport;
169
170     if (port) {
171         for (tsp = rxk_ports, i = 0; i < MAXRXPORTS; i++) {
172             if (*tsp++ == port) {
173                 /* checksum the packet */
174                 if (hdr > 5) {
175                     ip_stripoptions(am, (struct mbuf *)0);      /* get rid of anything we don't need */
176                     tu = (struct udphdr *)(((char *)ti) + 20);
177                 }
178                 /*
179                  * Make mbuf data length reflect UDP length.
180                  * If not enough data to reflect UDP length, drop.
181                  */
182                 tvu = (struct udpiphdr *)ti;
183                 tlen = ntohs((u_short) tvu->ui_ulen);
184                 if ((int)ti->ip_len != tlen) {
185                     if (tlen > (int)ti->ip_len) {
186                         m_free(am);
187                         return;
188                     }
189                     m_adj(am, tlen - (int)ti->ip_len);
190                 }
191                 /* deliver packet to rx */
192                 taddr.sin_family = AF_INET;     /* compute source address */
193                 taddr.sin_port = tu->uh_sport;
194                 taddr.sin_addr.s_addr = ti->ip_src.s_addr;
195                 /* handle the checksum.  Note that this code damages the actual ip
196                  * header (replacing it with the virtual one, which is the same size),
197                  * so we must ensure we get everything out we need, first */
198                 if (tu->uh_sum != 0) {
199                     /* if the checksum is there, always check it. It's crazy not
200                      * to, unless you can really be sure that your
201                      * underlying network (and interfaces and drivers and
202                      * DMA hardware, etc!) is error-free. First, fill
203                      * in entire virtual ip header. */
204                     tvu->ui_next = 0;
205                     tvu->ui_prev = 0;
206                     tvu->ui_x1 = 0;
207                     tvu->ui_len = tvu->ui_ulen;
208                     tlen = ntohs((unsigned short)(tvu->ui_ulen));
209                     if ((!(am->m_flags & M_CKSUMMED))
210                         && in_cksum(am, sizeof(struct ip) + tlen)) {
211                         /* checksum, including cksum field, doesn't come out 0, so
212                          * this packet is bad */
213                         m_freem(am);
214                         return;
215                     }
216                 }
217
218                 /*
219                  * 28 is IP (20) + UDP (8) header.  ulen includes
220                  * udp header, and we *don't* tell RX about udp
221                  * header either.  So, we remove those 8 as well.
222                  */
223                 data_len = ntohs(tu->uh_ulen);
224                 data_len -= 8;
225                 if (!(*rxk_GetPacketProc) (&phandle, data_len)) {
226                     if (rx_mb_to_packet(am, m_freem, 28, data_len, phandle)) {
227                         /* XXX should just increment counter here.. */
228                         printf("rx: truncated UDP packet\n");
229                         rxi_FreePacket(phandle);
230                     } else
231                         (*rxk_PacketArrivalProc) (phandle, &taddr,
232                                                   rxk_portRocks[i], data_len);
233                 } else
234                     m_freem(am);
235                 return;
236             }
237         }
238     }
239
240     /* if we get here, try to deliver packet to udp */
241     if (tproc = parent_proto.pr_input)
242         (*tproc) (am, aif);
243     return;
244 }
245
246 /* 
247  * UDP fast timer to raise events for all but Solaris and NCR. 
248  * Called about 5 times per second (at unknown priority?).  Must go to
249  * splnet or obtain global lock before touching anything significant.
250  */
251 static void
252 rxk_fasttimo(void)
253 {
254     int (*tproc) ();
255     struct clock temp;
256
257     /* do rx fasttimo processing here */
258     rxevent_RaiseEvents(&temp);
259     if (tproc = parent_proto.pr_fasttimo)
260         (*tproc) ();
261 }
262
263
264 /* start intercepting basic calls */
265 void
266 rxk_init(void)
267 {
268     register struct protosw *tpro, *last;
269     if (rxk_initDone)
270         return;
271
272     last = inetdomain.dom_protoswNPROTOSW;
273     for (tpro = inetdomain.dom_protosw; tpro < last; tpro++) {
274         if (tpro->pr_protocol == IPPROTO_UDP) {
275             memcpy(&parent_proto, tpro, sizeof(parent_proto));
276             tpro->pr_input = rxk_input;
277             tpro->pr_fasttimo = rxk_fasttimo;
278             rxk_initDone = 1;
279             return;
280         }
281     }
282     osi_Panic("inet:no udp");
283 }
284 #endif /* RXK_LISTENER_ENV */
285
286 /*
287  * RX IP address routines.
288  */
289
290 static afs_uint32 myNetAddrs[ADDRSPERSITE];
291 static int myNetMTUs[ADDRSPERSITE];
292 static int myNetFlags[ADDRSPERSITE];
293 static int numMyNetAddrs = 0;
294
295 /* This version doesn't even begin to handle iterative requests, but then
296  * we don't yet use them anyway. Fix this when rxi_InitPeerParams is changed
297  * to find a true maximum.
298  */
299 static int
300 rxi_MatchIfnet(struct hashbucket *h, caddr_t key, caddr_t arg1, caddr_t arg2)
301 {
302     afs_uint32 ppaddr = *(afs_uint32 *) key;
303     int match_value = *(int *)arg1;
304     struct in_ifaddr *ifa = (struct in_ifaddr *)h;
305     struct sockaddr_in *sin;
306
307     if ((ppaddr & ifa->ia_netmask) == ifa->ia_net) {
308         if ((ppaddr & ifa->ia_subnetmask) == ifa->ia_subnet) {
309             sin = IA_SIN(ifa);
310             if (sin->sin_addr.s_addr == ppaddr) {       /* ie, ME!!!  */
311                 match_value = 4;
312                 *(struct in_ifaddr **)arg2 = ifa;
313             }
314             if (match_value < 3) {
315                 *(struct in_ifaddr **)arg2 = ifa;
316                 match_value = 3;
317             }
318         } else {
319             if (match_value < 2) {
320                 *(struct in_ifaddr **)arg2 = ifa;
321                 match_value = 2;
322             }
323         }
324     }
325     *(int *)arg1 = match_value;
326     return 0;
327 }
328
329
330 struct ifnet *
331 rxi_FindIfnet(afs_uint32 addr, afs_uint32 * maskp)
332 {
333     afs_uint32 ppaddr;
334     int match_value = 0;
335     struct in_ifaddr *ifad;
336
337     if (numMyNetAddrs == 0)
338         (void)rxi_GetIFInfo();
339
340     ppaddr = ntohl(addr);
341     ifad = (struct in_ifaddr *)&hashinfo_inaddr;
342
343     (void)hash_enum(&hashinfo_inaddr, rxi_MatchIfnet, HTF_INET,
344                     (caddr_t) & ppaddr, (caddr_t) & match_value,
345                     (caddr_t) & ifad);
346
347     if (match_value) {
348         if (maskp)
349             *maskp = ifad->ia_subnetmask;
350         return ifad->ia_ifp;
351     } else
352         return NULL;
353 }
354
355 static int
356 rxi_EnumGetIfInfo(struct hashbucket *h, caddr_t key, caddr_t arg1,
357                   caddr_t arg2)
358 {
359     int different = *(int *)arg1;
360     int i = *(int *)arg2;
361     struct in_ifaddr *iap = (struct in_ifaddr *)h;
362     struct ifnet *ifnp;
363     afs_uint32 ifinaddr;
364     afs_uint32 rxmtu;
365
366     if (i >= ADDRSPERSITE)
367         return 0;
368
369     ifnp = iap->ia_ifp;
370     rxmtu = (ifnp->if_mtu - RX_IPUDP_SIZE);
371     ifinaddr = ntohl(iap->ia_addr.sin_addr.s_addr);
372     if (myNetAddrs[i] != ifinaddr) {
373         myNetAddrs[i] = ifinaddr;
374         myNetMTUs[i] = rxmtu;
375         different++;
376         *(int *)arg1 = different;
377     }
378     rxmtu = rxmtu * rxi_nRecvFrags + ((rxi_nRecvFrags - 1) * UDP_HDR_SIZE);
379     if ((ifinaddr != 0x7f000001) && (rxmtu > rx_maxReceiveSize)) {
380         rx_maxReceiveSize = MIN(RX_MAX_PACKET_SIZE, rxmtu);
381         rx_maxReceiveSize = MIN(rx_maxReceiveSize, rx_maxReceiveSizeUser);
382     }
383
384     *(int *)arg2 = i + 1;
385     return 0;
386 }
387
388 int
389 rxi_GetIFInfo()
390 {
391     int i = 0;
392     int different = 0;
393
394     /* SGI 6.2 does not have a pointer from the ifnet to the list of
395      * of addresses (if_addrlist). So it's more efficient to run the
396      * in_ifaddr list and use the back pointers to the ifnet struct's.
397      */
398     (void)hash_enum(&hashinfo_inaddr, rxi_EnumGetIfInfo, HTF_INET, NULL,
399                     (caddr_t) & different, (caddr_t) & i);
400
401     rx_maxJumboRecvSize =
402         RX_HEADER_SIZE + rxi_nDgramPackets * RX_JUMBOBUFFERSIZE +
403         (rxi_nDgramPackets - 1) * RX_JUMBOHEADERSIZE;
404     rx_maxJumboRecvSize = MAX(rx_maxJumboRecvSize, rx_maxReceiveSize);
405
406     return different;
407 }
408
409 /* osi_NetSend - from the now defunct afs_osinet.c */
410 #ifdef DEBUG
411 #undef DEBUG
412 #endif
413 #ifdef MP
414 #define _MP_NETLOCKS
415 #endif
416
417 #ifdef AFS_SGI65_ENV
418 osi_NetSend(asocket, addr, dvec, nvec, asize, istack)
419      register struct osi_socket *asocket;
420      struct iovec *dvec;
421      int nvec;
422      register afs_int32 asize;
423      struct sockaddr_in *addr;
424      int istack;
425 {
426     int code;
427     struct iovec tvecs[RX_MAXWVECS + 1];
428     struct iovec *iovp;
429     struct uio tuio;
430     struct mbuf *to;
431     int i;
432     bhv_desc_t bhv;
433
434     if (nvec > RX_MAXWVECS + 1) {
435         osi_Panic("osi_NetSend: %d: Too many iovecs.\n", nvec);
436     }
437     memcpy((char *)tvecs, (char *)dvec, nvec * sizeof(struct iovec));
438
439     tuio.uio_iov = tvecs;
440     tuio.uio_iovcnt = nvec;
441     tuio.uio_segflg = UIO_SYSSPACE;
442     tuio.uio_offset = 0;
443     tuio.uio_sigpipe = 0;
444     tuio.uio_pio = 0;
445     tuio.uio_pbuf = 0;
446
447     tuio.uio_resid = 0;
448     for (i = 0, iovp = tvecs; i < nvec; i++, iovp++)
449         tuio.uio_resid += iovp->iov_len;
450
451
452     to = m_get(M_WAIT, MT_SONAME);
453     to->m_len = sizeof(struct sockaddr_in);
454     memcpy(mtod(to, caddr_t), (char *)addr, to->m_len);
455
456     BHV_PDATA(&bhv) = (void *)asocket;
457     code = sosend(&bhv, to, &tuio, 0, NULL);
458
459     m_freem(to);
460     return code;
461 }
462 #else /* AFS_SGI65_ENV */
463
464 int
465 dummy_sblock(struct sockbuf *a, int b, struct socket *c, int *d, int e)
466 {
467     afs_warn
468         ("sblock was called before it was installed. Install proper afsd.\n");
469 }
470
471 void
472 dummy_sbunlock(struct sockbuf *a, int b, struct socket *c, int d)
473 {
474     afs_warn
475         ("sbunlock was called before it was installed. Install proper afsd.\n");
476 }
477
478 int (*afs_sblockp) (struct sockbuf *, int, struct socket *, int *, int) =
479     dummy_sblock;
480 void (*afs_sbunlockp) (struct sockbuf *, int, struct socket *, int) =
481     dummy_sbunlock;
482 #define AFS_SBUNLOCK(SB, EV, SO, O) (*afs_sbunlockp)(SB, EV, SO, O)
483
484 /* osi_NetSend - send asize bytes at adata from asocket to host at addr.
485  *
486  * Now, why do we allocate a new buffer when we could theoretically use the one
487  * pointed to by adata?  Because PRU_SEND returns after queueing the message,
488  * not after sending it.  If the sender changes the data after queueing it,
489  * we'd see the already-queued data change.  One attempt to fix this without
490  * adding a copy would be to have this function wait until the datagram is
491  * sent; however this doesn't work well.  In particular, if a host is down, and
492  * an ARP fails to that host, this packet will be queued until the ARP request
493  * comes back, which could be hours later.  We can't block in this routine that
494  * long, since it prevents RPC timeouts from happening.
495  */
496 /* XXX In the brave new world, steal the data bufs out of the rx_packet iovec,
497  * and just queue those.  XXX
498  */
499 int
500 osi_NetSend(asocket, addr, dvec, nvec, asize, istack)
501      register struct socket *asocket;
502      struct iovec *dvec;
503      int nvec;
504      register afs_int32 asize;
505      struct sockaddr_in *addr;
506      int istack;
507 {
508     register struct mbuf *tm, *um;
509     register afs_int32 code;
510     int s;
511     struct mbuf *top = 0;
512     register struct mbuf *m, **mp;
513     int len;
514     char *tdata;
515     caddr_t tpa;
516     int i, tl, rlen;
517
518     NETSPL_DECL(s1)
519         AFS_STATCNT(osi_NetSend);
520
521     (*afs_sblockp) (&asocket->so_snd, NETEVENT_SODOWN, asocket, &s1, istack);
522
523     s = splnet();
524     mp = &top;
525     i = 0;
526     tdata = dvec[i].iov_base;
527     tl = dvec[i].iov_len;
528     while (1) {
529         if ((m = m_vget(M_DONTWAIT, MIN(asize, VCL_MAX), MT_DATA)) == NULL) {
530             if (top)
531                 m_freem(top);
532             splx(s);
533             AFS_SBUNLOCK(&asocket->so_snd, NETEVENT_SODOWN, asocket, s1);
534             return 1;
535         }
536         len = MIN(m->m_len, asize);
537         m->m_len = 0;
538         tpa = mtod(m, caddr_t);
539         while (len) {
540             rlen = MIN(len, tl);
541             memcpy(tpa, tdata, rlen);
542             asize -= rlen;
543             len -= rlen;
544             tpa += rlen;
545             m->m_len += rlen;
546             tdata += rlen;
547             tl -= rlen;
548             if (tl <= 0) {
549                 i++;
550                 if (i > nvec) {
551                     /* shouldn't come here! */
552                     asize = 0;  /* so we make progress toward completion */
553                     break;
554                 }
555                 tdata = dvec[i].iov_base;
556                 tl = dvec[i].iov_len;
557             }
558         }
559         *mp = m;
560         mp = &m->m_next;
561         if (asize <= 0)
562             break;
563     }
564     tm = top;
565
566     tm->m_act = NULL;
567
568     /* setup mbuf corresponding to destination address */
569     um = m_get(M_DONTWAIT, MT_SONAME);
570     if (!um) {
571         if (top)
572             m_freem(top);       /* free mbuf chain */
573         /* if this were vfs40, we'd do sbunlock(asocket, &asocket->so_snd), but
574          * we don't do the locking at all for vfs40 systems */
575         splx(s);
576         AFS_SBUNLOCK(&asocket->so_snd, NETEVENT_SODOWN, asocket, s1);
577         return 1;
578     }
579     memcpy(mtod(um, caddr_t), addr, sizeof(*addr));
580     um->m_len = sizeof(*addr);
581     /* note that udp_usrreq frees funny mbuf.  We hold onto data, but mbuf
582      * around it is gone.  we free address ourselves.  */
583     code = (*asocket->so_proto->pr_usrreq) (asocket, PRU_SEND, tm, um, 0);
584     splx(s);
585     m_free(um);
586     AFS_SBUNLOCK(&asocket->so_snd, NETEVENT_SODOWN, asocket, s1);
587
588     return code;
589 }
590 #endif /* AFS_SGI65_ENV */