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