43914fde5857e6d726b5538f51278a40081da4ef
[openafs.git] / src / rx / FBSD / rx_knet.c
1 /*
2 ****************************************************************************
3 *        Copyright IBM Corporation 1988, 1989 - All Rights Reserved        *
4 *        Copyright Transarc Corporation 1989 - All Rights Reserved         *
5 *                                                                          *
6 * Permission to use, copy, modify, and distribute this software and its    *
7 * documentation for any purpose and without fee is hereby granted,         *
8 * provided that the above copyright notice appear in all copies and        *
9 * that both that copyright notice and this permission notice appear in     *
10 * supporting documentation, and that the name of IBM not be used in        *
11 * advertising or publicity pertaining to distribution of the software      *
12 * without specific, written prior permission.                              *
13 *                                                                          *
14 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL *
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL IBM *
16 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY      *
17 * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER  *
18 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING   *
19 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.    *
20 ****************************************************************************
21 */
22
23 #include <afsconfig.h>
24 #include "../afs/param.h"
25
26 RCSID("$Header$");
27
28 #ifdef AFS_FBSD40_ENV
29 #include "../rx/rx_kcommon.h"
30
31
32 static struct protosw parent_proto;     /* udp proto switch */
33 static void rxk_input (struct mbuf *am, int iphlen);
34 static void rxk_fasttimo (void);
35
36 /* start intercepting basic calls */
37 rxk_init() {
38     register struct protosw *tpro, *last;
39     if (rxk_initDone) return 0;
40
41     last = inetdomain.dom_protoswNPROTOSW;
42     for (tpro = inetdomain.dom_protosw; tpro < last; tpro++)
43       if (tpro->pr_protocol == IPPROTO_UDP) {
44         /* force UDP checksumming on for AFS    */
45          extern int udpcksum;
46          udpcksum = 1;  
47           memcpy(&parent_proto, tpro, sizeof(parent_proto));
48           tpro->pr_input = rxk_input;
49           tpro->pr_fasttimo = rxk_fasttimo;
50           /*
51            * don't bother with pr_drain and pr_ctlinput
52            * until we have something to do
53            */
54           rxk_initDone = 1;
55           return 0;
56       }
57     osi_Panic("inet:no udp");
58 }
59
60
61 static void rxk_input (struct mbuf *am, int iphlen)
62 {
63     void (*tproc)();
64     register unsigned short *tsp;
65     int hdr;
66     struct udphdr *tu;
67     register struct ip *ti;
68     struct udpiphdr *tvu;
69     register int i;
70     char *phandle;
71     afs_int32 code;
72     struct sockaddr_in taddr;
73     int tlen;
74     short port;
75     int data_len, comp_sum;
76
77     SPLVAR;
78     NETPRI;
79
80     /* make sure we have base ip and udp headers in first mbuf */
81     if (iphlen > sizeof (struct ip)) {
82         ip_stripoptions(am, (struct mbuf *)0);
83         iphlen = sizeof (struct ip);
84     }
85
86     if (am->m_len < sizeof(struct udpiphdr)) {
87         am = m_pullup(am, sizeof(struct udpiphdr));
88         if (!am) {
89             USERPRI;
90             return;
91         }
92     }
93
94     ti = mtod(am, struct ip *);
95     /* skip basic ip hdr */
96     tu = (struct udphdr *)(((char *)ti) + sizeof(struct ip)); 
97     
98     /* now read the port out */
99     port = tu->uh_dport;
100
101     if (port) {
102         for(tsp=rxk_ports, i=0; i<MAXRXPORTS;i++) {
103             if (*tsp++ == port) {
104                 /* checksum the packet */
105                 /*
106                  * Make mbuf data length reflect UDP length.
107                  * If not enough data to reflect UDP length, drop.
108                  */
109                 tvu = (struct udpiphdr *)ti;
110                 tlen = ntohs((u_short)tvu->ui_ulen);
111                 if ((int)ti->ip_len != tlen) {
112                     if (tlen > (int)ti->ip_len) {
113                         m_free(am);
114                         USERPRI;
115                         return;
116                     }
117                     m_adj(am, tlen - (int)ti->ip_len);
118                 }
119                 /* deliver packet to rx */
120                 taddr.sin_family = AF_INET;         /* compute source address */
121                 taddr.sin_port = tu->uh_sport;
122                 taddr.sin_addr.s_addr = ti->ip_src.s_addr;
123                 taddr.sin_len = sizeof(taddr);
124                 tvu = (struct udpiphdr *) ti;   /* virtual udp structure, for cksum */
125                 /* handle the checksum.  Note that this code damages the actual ip
126                    header (replacing it with the virtual one, which is the same size),
127                    so we must ensure we get everything out we need, first */
128                 if ( tu->uh_sum != 0) {
129                         /* if the checksum is there, always check it. It's crazy not
130                          * to, unless you can really be sure that your
131                          * underlying network (and interfaces and drivers and
132                          * DMA hardware, etc!) is error-free. First, fill
133                          * in entire virtual ip header. */
134                         memset(tvu->ui_i.ih_x1, 0, 9);
135                         tvu->ui_len = tvu->ui_ulen;
136                         tlen = ntohs((unsigned short)(tvu->ui_ulen));
137                         if (in_cksum(am, sizeof(struct ip) + tlen)) {
138                             /* checksum, including cksum field, doesn't come out 0, so
139                                this packet is bad */
140                             m_freem(am);
141                             USERPRI;
142                             return;
143                         }
144                       }
145
146                 /*
147                  * 28 is IP (20) + UDP (8) header.  ulen includes
148                  * udp header, and we *don't* tell RX about udp
149                  * header either.  So, we remove those 8 as well.
150                  */
151                 data_len = ntohs(tu->uh_ulen);
152                 data_len -= 8;
153                 AFS_RXGLOCK();
154                 if (!(*rxk_GetPacketProc)(&phandle, data_len)) {
155                   if (rx_mb_to_packet(am, m_freem, 28, data_len, phandle)) {
156                     /* XXX should just increment counter here.. */
157                     printf("rx: truncated UDP packet\n");
158                     rxi_FreePacket(phandle);
159                   }
160                   else 
161                     (*rxk_PacketArrivalProc)(phandle, &taddr,
162                                              rxk_portRocks[i], data_len);
163                 }else m_freem(am);
164                 AFS_RXGUNLOCK();
165                 USERPRI;
166                 return;
167                 }
168             }
169         }
170
171     /* if we get here, try to deliver packet to udp */
172     if (tproc = parent_proto.pr_input) (*tproc)(am,iphlen);
173     USERPRI;
174     return;
175 }
176
177
178 /* 
179  * UDP fast timer to raise events for all but Solaris and NCR. 
180  * Called about 5 times per second (at unknown priority?).  Must go to
181  * splnet or obtain global lock before touching anything significant.
182  */
183 static void rxk_fasttimo (void)
184 {
185     void (*tproc)();
186     struct clock temp;
187
188     /* do rx fasttimo processing here */
189     rxevent_RaiseEvents(&temp);
190     if (tproc = parent_proto.pr_fasttimo) (*tproc)();
191 }
192
193
194 /* rx_NetSend - send asize bytes at adata from asocket to host at addr.
195  *
196  * Now, why do we allocate a new buffer when we could theoretically use the one
197  * pointed to by adata?  Because PRU_SEND returns after queueing the message,
198  * not after sending it.  If the sender changes the data after queueing it,
199  * we'd see the already-queued data change.  One attempt to fix this without
200  * adding a copy would be to have this function wait until the datagram is
201  * sent; however this doesn't work well.  In particular, if a host is down, and
202  * an ARP fails to that host, this packet will be queued until the ARP request
203  * comes back, which could be hours later.  We can't block in this routine that
204  * long, since it prevents RPC timeouts from happening.
205  */
206 /* XXX In the brave new world, steal the data bufs out of the rx_packet iovec,
207  * and just queue those.  XXX
208  */
209
210 /* set lock on sockbuf sb; can't call sblock since we're at interrupt level
211  * sometimes */
212 static trysblock(sb)    
213 register struct sockbuf *sb; {
214     AFS_STATCNT(trysblock);
215     if (sb->sb_flags & SB_LOCK){
216         return -1;  /* can't lock socket */
217     }
218     sb->sb_flags |= SB_LOCK;
219     return 0;
220 }
221
222 int 
223 osi_NetSend(asocket, addr, dvec, nvec, asize, istack)
224      register struct socket *asocket;
225      struct iovec *dvec;
226      int nvec;
227      register afs_int32 asize;
228      struct sockaddr_in *addr;
229      int istack;
230 {
231     register struct mbuf *tm, *um;
232     register afs_int32 code;
233     int s;
234     struct mbuf *top = 0;
235     register struct mbuf *m, **mp;
236     int len;
237     char *tdata;
238     caddr_t tpa;
239     int i,tl,rlen;
240     int mlen;
241     int haveGlock;
242
243     AFS_STATCNT(osi_NetSend);
244
245 /* Actually, the Ultrix way is as good as any for us, so we don't bother with
246  * special mbufs any more.  Used to think we could get away with not copying
247  * the data to the interface, but there's no way to tell the caller not to
248  * reuse the buffers after sending, so we lost out on that trick anyway */
249
250     s = splnet();
251     mp = &top;
252     i = 0;
253     tdata = dvec[i].iov_base;
254     tl = dvec[i].iov_len;
255     while (1) {
256         mlen = MLEN;
257         if (top == 0) {
258             MGETHDR(m, M_DONTWAIT, MT_DATA);
259             if (!m) {
260                 splx(s);
261                 return 1;
262             }
263             mlen = MHLEN;
264             m->m_pkthdr.len = 0;
265             m->m_pkthdr.rcvif = (struct ifnet *)0;
266         } else
267         MGET(m, M_DONTWAIT, MT_DATA);
268         if (!m) {
269             /* can't get an mbuf, give up */
270             if (top) m_freem(top);      /* free mbuf list we're building */
271             splx(s);
272             return 1;
273         }
274         /*
275          * WARNING: the `4 * MLEN' is somewhat dubious.  It is better than
276          * `NBPG', which may have no relation to `CLBYTES'.  Also, `CLBYTES'
277          * may be so large that we never use clusters, resulting in far
278          * too many mbufs being used.  It is often better to briefly use
279          * a cluster, even if we are only using a portion of it.  Since
280          * we are on the xmit side, it shouldn't end up sitting on a queue
281          * for a potentially unbounded time (except perhaps if we are talking
282          * to ourself).
283          */
284         if (asize >= 4 * MLEN) {        /* try to get cluster mbuf */
285             register struct mbuf *p;
286
287             /* different algorithms for getting cluster mbuf */
288             MCLGET(m, M_DONTWAIT);
289             if ((m->m_flags & M_EXT) == 0)
290                 goto nopages;
291             mlen = MCLBYTES;
292
293             /* now compute usable size */
294             len = MIN(mlen, asize);
295 /* Should I look at MAPPED_MBUFS??? */
296         } else {
297 nopages:
298             len = MIN(mlen, asize);
299         }
300         m->m_len = 0;
301         *mp = m;        /* XXXX */
302         top->m_pkthdr.len += len;
303         tpa = mtod(m, caddr_t);
304         while (len) {
305           rlen = MIN(len, tl);
306           memcpy(tpa, tdata, rlen);
307           asize -= rlen;
308           len -= rlen;
309           tpa += rlen;
310           m->m_len += rlen;
311           tdata += rlen;
312           tl -= rlen;
313           if (tl <= 0) {
314             i++;
315             if (i > nvec) {
316               /* shouldn't come here! */
317               asize = 0;   /* so we make progress toward completion */
318               break;
319             }
320             tdata = dvec[i].iov_base;
321             tl = dvec[i].iov_len;
322           }
323         }
324         *mp = m;
325         mp = &m->m_next;
326         if (asize <= 0)
327           break;
328     }
329     tm = top;
330
331     tm->m_act = (struct mbuf *) 0;
332
333     /* setup mbuf corresponding to destination address */
334     um = m_get(M_DONTWAIT, MT_SONAME);
335     if (!um) {
336         if (top) m_freem(top);  /* free mbuf chain */
337         /* if this were vfs40, we'd do sbunlock(asocket, &asocket->so_snd), but
338            we don't do the locking at all for vfs40 systems */
339         splx(s);
340         return 1;
341     }
342     memcpy(mtod(um, caddr_t), addr, sizeof(*addr));
343     um->m_len = sizeof(*addr);
344     /* note that udp_usrreq frees funny mbuf.  We hold onto data, but mbuf
345      * around it is gone.  we free address ourselves.  */
346     /*    haveGlock = ISAFS_GLOCK();
347     if (haveGlock) {
348         AFS_GUNLOCK();
349         }  */
350     /* SOCKET_LOCK(asocket); */
351     /* code = (*asocket->so_proto->pr_usrreq)(asocket, PRU_SEND, tm, um, 0); */
352     code = (*asocket->so_proto->pr_usrreqs->pru_send)(asocket, 0, tm, 
353                                                       (struct sockaddr *) addr,
354                                                       um, curproc);
355     /* SOCKET_UNLOCK(asocket); */
356     /* if (haveGlock) {
357         AFS_GLOCK();
358         } */
359     splx(s);
360     m_free(um);
361
362     return code;
363 }
364
365 #endif /* AFS_FBSD40_ENV */