initial-freebsd-port-work-20010414
[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 "../afs/param.h"
24 #ifdef AFS_FBSD40_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);
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                         memset(tvu->ui_i.ih_x1, 0, 9);
131                         tvu->ui_len = tvu->ui_ulen;
132                         tlen = ntohs((unsigned short)(tvu->ui_ulen));
133                         if (in_cksum(am, sizeof(struct ip) + tlen)) {
134                             /* checksum, including cksum field, doesn't come out 0, so
135                                this packet is bad */
136                             m_freem(am);
137                             USERPRI;
138                             return;
139                         }
140                       }
141
142                 /*
143                  * 28 is IP (20) + UDP (8) header.  ulen includes
144                  * udp header, and we *don't* tell RX about udp
145                  * header either.  So, we remove those 8 as well.
146                  */
147                 data_len = ntohs(tu->uh_ulen);
148                 data_len -= 8;
149                 AFS_RXGLOCK();
150                 if (!(*rxk_GetPacketProc)(&phandle, data_len)) {
151                   if (rx_mb_to_packet(am, m_freem, 28, data_len, phandle)) {
152                     /* XXX should just increment counter here.. */
153                     printf("rx: truncated UDP packet\n");
154                     rxi_FreePacket(phandle);
155                   }
156                   else 
157                     (*rxk_PacketArrivalProc)(phandle, &taddr,
158                                              rxk_portRocks[i], data_len);
159                 }else m_freem(am);
160                 AFS_RXGUNLOCK();
161                 USERPRI;
162                 return;
163                 }
164             }
165         }
166
167     /* if we get here, try to deliver packet to udp */
168     if (tproc = parent_proto.pr_input) (*tproc)(am,iphlen);
169     USERPRI;
170     return;
171 }
172
173
174 /* 
175  * UDP fast timer to raise events for all but Solaris and NCR. 
176  * Called about 5 times per second (at unknown priority?).  Must go to
177  * splnet or obtain global lock before touching anything significant.
178  */
179 static void rxk_fasttimo (void)
180 {
181     void (*tproc)();
182     struct clock temp;
183
184     /* do rx fasttimo processing here */
185     rxevent_RaiseEvents(&temp);
186     if (tproc = parent_proto.pr_fasttimo) (*tproc)();
187 }
188
189
190 /* rx_NetSend - send asize bytes at adata from asocket to host at addr.
191  *
192  * Now, why do we allocate a new buffer when we could theoretically use the one
193  * pointed to by adata?  Because PRU_SEND returns after queueing the message,
194  * not after sending it.  If the sender changes the data after queueing it,
195  * we'd see the already-queued data change.  One attempt to fix this without
196  * adding a copy would be to have this function wait until the datagram is
197  * sent; however this doesn't work well.  In particular, if a host is down, and
198  * an ARP fails to that host, this packet will be queued until the ARP request
199  * comes back, which could be hours later.  We can't block in this routine that
200  * long, since it prevents RPC timeouts from happening.
201  */
202 /* XXX In the brave new world, steal the data bufs out of the rx_packet iovec,
203  * and just queue those.  XXX
204  */
205
206 /* set lock on sockbuf sb; can't call sblock since we're at interrupt level
207  * sometimes */
208 static trysblock(sb)    
209 register struct sockbuf *sb; {
210     AFS_STATCNT(trysblock);
211     if (sb->sb_flags & SB_LOCK){
212         return -1;  /* can't lock socket */
213     }
214     sb->sb_flags |= SB_LOCK;
215     return 0;
216 }
217
218 int 
219 osi_NetSend(asocket, addr, dvec, nvec, asize, istack)
220      register struct socket *asocket;
221      struct iovec *dvec;
222      int nvec;
223      register afs_int32 asize;
224      struct sockaddr_in *addr;
225      int istack;
226 {
227     register struct mbuf *tm, *um;
228     register afs_int32 code;
229     int s;
230     struct mbuf *top = 0;
231     register struct mbuf *m, **mp;
232     int len;
233     char *tdata;
234     caddr_t tpa;
235     int i,tl,rlen;
236     int mlen;
237     int haveGlock;
238
239     AFS_STATCNT(osi_NetSend);
240
241 /* Actually, the Ultrix way is as good as any for us, so we don't bother with
242  * special mbufs any more.  Used to think we could get away with not copying
243  * the data to the interface, but there's no way to tell the caller not to
244  * reuse the buffers after sending, so we lost out on that trick anyway */
245
246     s = splnet();
247     mp = &top;
248     i = 0;
249     tdata = dvec[i].iov_base;
250     tl = dvec[i].iov_len;
251     while (1) {
252         mlen = MLEN;
253         if (top == 0) {
254             MGETHDR(m, M_DONTWAIT, MT_DATA);
255             if (!m) {
256                 splx(s);
257                 return 1;
258             }
259             mlen = MHLEN;
260             m->m_pkthdr.len = 0;
261             m->m_pkthdr.rcvif = (struct ifnet *)0;
262         } else
263         MGET(m, M_DONTWAIT, MT_DATA);
264         if (!m) {
265             /* can't get an mbuf, give up */
266             if (top) m_freem(top);      /* free mbuf list we're building */
267             splx(s);
268             return 1;
269         }
270         /*
271          * WARNING: the `4 * MLEN' is somewhat dubious.  It is better than
272          * `NBPG', which may have no relation to `CLBYTES'.  Also, `CLBYTES'
273          * may be so large that we never use clusters, resulting in far
274          * too many mbufs being used.  It is often better to briefly use
275          * a cluster, even if we are only using a portion of it.  Since
276          * we are on the xmit side, it shouldn't end up sitting on a queue
277          * for a potentially unbounded time (except perhaps if we are talking
278          * to ourself).
279          */
280         if (asize >= 4 * MLEN) {        /* try to get cluster mbuf */
281             register struct mbuf *p;
282
283             /* different algorithms for getting cluster mbuf */
284             MCLGET(m, M_DONTWAIT);
285             if ((m->m_flags & M_EXT) == 0)
286                 goto nopages;
287             mlen = MCLBYTES;
288
289             /* now compute usable size */
290             len = MIN(mlen, asize);
291 /* Should I look at MAPPED_MBUFS??? */
292         } else {
293 nopages:
294             len = MIN(mlen, asize);
295         }
296         m->m_len = 0;
297         *mp = m;        /* XXXX */
298         top->m_pkthdr.len += len;
299         tpa = mtod(m, caddr_t);
300         while (len) {
301           rlen = MIN(len, tl);
302           bcopy(tdata, tpa, rlen);
303           asize -= rlen;
304           len -= rlen;
305           tpa += rlen;
306           m->m_len += rlen;
307           tdata += rlen;
308           tl -= rlen;
309           if (tl <= 0) {
310             i++;
311             if (i > nvec) {
312               /* shouldn't come here! */
313               asize = 0;   /* so we make progress toward completion */
314               break;
315             }
316             tdata = dvec[i].iov_base;
317             tl = dvec[i].iov_len;
318           }
319         }
320         *mp = m;
321         mp = &m->m_next;
322         if (asize <= 0)
323           break;
324     }
325     tm = top;
326
327     tm->m_act = (struct mbuf *) 0;
328
329     /* setup mbuf corresponding to destination address */
330     um = m_get(M_DONTWAIT, MT_SONAME);
331     if (!um) {
332         if (top) m_freem(top);  /* free mbuf chain */
333         /* if this were vfs40, we'd do sbunlock(asocket, &asocket->so_snd), but
334            we don't do the locking at all for vfs40 systems */
335         splx(s);
336         return 1;
337     }
338     bcopy(addr, mtod(um, caddr_t), sizeof(*addr));
339     um->m_len = sizeof(*addr);
340     /* note that udp_usrreq frees funny mbuf.  We hold onto data, but mbuf
341      * around it is gone.  we free address ourselves.  */
342     /*    haveGlock = ISAFS_GLOCK();
343     if (haveGlock) {
344         AFS_GUNLOCK();
345         }  */
346     /* SOCKET_LOCK(asocket); */
347     /* code = (*asocket->so_proto->pr_usrreq)(asocket, PRU_SEND, tm, um, 0); */
348     code = (*asocket->so_proto->pr_usrreqs->pru_send)(asocket, 0, tm, 
349                                                       (struct sockaddr *) addr,
350                                                       um, curproc);
351     /* SOCKET_UNLOCK(asocket); */
352     /* if (haveGlock) {
353         AFS_GLOCK();
354         } */
355     splx(s);
356     m_free(um);
357
358     return code;
359 }
360
361 #endif /* AFS_FBSD40_ENV */