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