net: Add network_offset to blocks
[akaros.git] / kern / src / net / udp.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #define DEBUG
30 #include <vfs.h>
31 #include <kfs.h>
32 #include <slab.h>
33 #include <kmalloc.h>
34 #include <kref.h>
35 #include <string.h>
36 #include <stdio.h>
37 #include <assert.h>
38 #include <error.h>
39 #include <cpio.h>
40 #include <pmap.h>
41 #include <smp.h>
42 #include <net/ip.h>
43
44 #include <vfs.h>
45 #include <kfs.h>
46 #include <slab.h>
47 #include <kmalloc.h>
48 #include <kref.h>
49 #include <string.h>
50 #include <stdio.h>
51 #include <assert.h>
52 #include <error.h>
53 #include <cpio.h>
54 #include <pmap.h>
55 #include <smp.h>
56 #include <net/ip.h>
57
58 #define DPRINT if(0)print
59
60 enum {
61         UDP_UDPHDR_SZ = 8,
62
63         UDP4_PHDR_OFF = 8,
64         UDP4_PHDR_SZ = 12,
65         UDP4_IPHDR_SZ = 20,
66         UDP6_IPHDR_SZ = 40,
67         UDP6_PHDR_SZ = 40,
68         UDP6_PHDR_OFF = 0,
69
70         IP_UDPPROTO = 17,
71         UDP_USEAD7 = 52,
72         UDP_USEAD6 = 36,
73
74         Udprxms = 200,
75         Udptickms = 100,
76         Udpmaxxmit = 10,
77 };
78
79 typedef struct Udp4hdr Udp4hdr;
80 struct Udp4hdr {
81         /* ip header */
82         uint8_t vihl;                           /* Version and header length */
83         uint8_t tos;                            /* Type of service */
84         uint8_t length[2];                      /* packet length */
85         uint8_t id[2];                          /* Identification */
86         uint8_t frag[2];                        /* Fragment information */
87         uint8_t Unused;
88         uint8_t udpproto;                       /* Protocol */
89         uint8_t udpplen[2];                     /* Header plus data length */
90         uint8_t udpsrc[IPv4addrlen];    /* Ip source */
91         uint8_t udpdst[IPv4addrlen];    /* Ip destination */
92
93         /* udp header */
94         uint8_t udpsport[2];            /* Source port */
95         uint8_t udpdport[2];            /* Destination port */
96         uint8_t udplen[2];                      /* data length */
97         uint8_t udpcksum[2];            /* Checksum */
98 };
99
100 typedef struct Udp6hdr Udp6hdr;
101 struct Udp6hdr {
102         uint8_t viclfl[4];
103         uint8_t len[2];
104         uint8_t nextheader;
105         uint8_t hoplimit;
106         uint8_t udpsrc[IPaddrlen];
107         uint8_t udpdst[IPaddrlen];
108
109         /* udp header */
110         uint8_t udpsport[2];            /* Source port */
111         uint8_t udpdport[2];            /* Destination port */
112         uint8_t udplen[2];                      /* data length */
113         uint8_t udpcksum[2];            /* Checksum */
114 };
115
116 /* MIB II counters */
117 typedef struct Udpstats Udpstats;
118 struct Udpstats {
119         uint32_t udpInDatagrams;
120         uint32_t udpNoPorts;
121         uint32_t udpInErrors;
122         uint32_t udpOutDatagrams;
123 };
124
125 typedef struct Udppriv Udppriv;
126 struct Udppriv {
127         struct Ipht ht;
128
129         /* MIB counters */
130         Udpstats ustats;
131
132         /* non-MIB stats */
133         uint32_t csumerr;                       /* checksum errors */
134         uint32_t lenerr;                        /* short packet */
135 };
136
137 void (*etherprofiler) (char *name, int qlen);
138 void udpkick(void *x, struct block *bp);
139
140 /*
141  *  protocol specific part of Conv
142  */
143 typedef struct Udpcb Udpcb;
144 struct Udpcb {
145         uint8_t headers;
146 };
147
148 static void udpconnect(struct conv *c, char **argv, int argc)
149 {
150         Udppriv *upriv;
151
152         upriv = c->p->priv;
153         Fsstdconnect(c, argv, argc);
154         Fsconnected(c, 0);
155
156         iphtadd(&upriv->ht, c);
157 }
158
159 static void udpbind(struct conv *c, char **argv, int argc)
160 {
161         Udppriv *upriv;
162
163         upriv = c->p->priv;
164         Fsstdbind(c, argv, argc);
165         iphtadd(&upriv->ht, c);
166 }
167
168 static int udpstate(struct conv *c, char *state, int n)
169 {
170         return snprintf(state, n, "%s qin %d qout %d\n",
171                                         c->inuse ? "Open" : "Closed",
172                                         c->rq ? qlen(c->rq) : 0, c->wq ? qlen(c->wq) : 0);
173 }
174
175 static void udpannounce(struct conv *c, char **argv, int argc)
176 {
177         Udppriv *upriv;
178
179         upriv = c->p->priv;
180         Fsstdannounce(c, argv, argc);
181         Fsconnected(c, NULL);
182         iphtadd(&upriv->ht, c);
183 }
184
185 static void udpbypass(struct conv *cv, char **argv, int argc)
186 {
187         Udppriv *upriv = cv->p->priv;
188
189         Fsstdbypass(cv, argv, argc);
190         iphtadd(&upriv->ht, cv);
191 }
192
193 static void udpcreate(struct conv *c)
194 {
195         c->rq = qopen(128 * 1024, Qmsg, 0, 0);
196         c->wq = qbypass(udpkick, c);
197 }
198
199 static void udpclose(struct conv *c)
200 {
201         Udpcb *ucb;
202         Udppriv *upriv;
203
204         upriv = c->p->priv;
205         iphtrem(&upriv->ht, c);
206
207         c->state = 0;
208         qclose(c->rq);
209         qclose(c->wq);
210         qclose(c->eq);
211         ipmove(c->laddr, IPnoaddr);
212         ipmove(c->raddr, IPnoaddr);
213         c->lport = 0;
214         c->rport = 0;
215
216         ucb = (Udpcb *) c->ptcl;
217         ucb->headers = 0;
218
219         qunlock(&c->qlock);
220 }
221
222 void udpkick(void *x, struct block *bp)
223 {
224         struct conv *c = x;
225         Udp4hdr *uh4;
226         Udp6hdr *uh6;
227         uint16_t rport;
228         uint8_t laddr[IPaddrlen], raddr[IPaddrlen];
229         Udpcb *ucb;
230         int dlen, ptcllen;
231         Udppriv *upriv;
232         struct Fs *f;
233         int version;
234         struct conv *rc;
235
236         upriv = c->p->priv;
237         assert(upriv);
238         f = c->p->f;
239
240         netlog(c->p->f, Logudp, "udp: kick\n");
241         if (bp == NULL)
242                 return;
243
244         ucb = (Udpcb *) c->ptcl;
245         switch (ucb->headers) {
246                 case 7:
247                         /* get user specified addresses */
248                         bp = pullupblock(bp, UDP_USEAD7);
249                         if (bp == NULL)
250                                 return;
251                         ipmove(raddr, bp->rp);
252                         bp->rp += IPaddrlen;
253                         ipmove(laddr, bp->rp);
254                         bp->rp += IPaddrlen;
255                         /* pick interface closest to dest */
256                         if (ipforme(f, laddr) != Runi)
257                                 findlocalip(f, laddr, raddr);
258                         bp->rp += IPaddrlen;    /* Ignore ifc address */
259                         rport = nhgets(bp->rp);
260                         bp->rp += 2 + 2;        /* Ignore local port */
261                         break;
262                 case 6:
263                         /* get user specified addresses */
264                         bp = pullupblock(bp, UDP_USEAD6);
265                         if (bp == NULL)
266                                 return;
267                         ipmove(raddr, bp->rp);
268                         bp->rp += IPaddrlen;
269                         ipmove(laddr, bp->rp);
270                         bp->rp += IPaddrlen;
271                         /* pick interface closest to dest */
272                         if (ipforme(f, laddr) != Runi)
273                                 findlocalip(f, laddr, raddr);
274                         rport = nhgets(bp->rp);
275                         bp->rp += 2 + 2;        /* Ignore local port */
276                         break;
277                 default:
278                         rport = 0;
279                         break;
280         }
281
282         if (ucb->headers) {
283                 if (memcmp(laddr, v4prefix, IPv4off) == 0 ||
284                         ipcmp(laddr, IPnoaddr) == 0)
285                         version = V4;
286                 else
287                         version = V6;
288         } else {
289                 if ((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
290                          memcmp(c->laddr, v4prefix, IPv4off) == 0)
291                         || ipcmp(c->raddr, IPnoaddr) == 0)
292                         version = V4;
293                 else
294                         version = V6;
295         }
296
297         dlen = blocklen(bp);
298
299         /* fill in pseudo header and compute checksum */
300         switch (version) {
301                 case V4:
302                         bp = padblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ);
303                         if (bp == NULL)
304                                 return;
305
306                         uh4 = (Udp4hdr *) (bp->rp);
307                         ptcllen = dlen + UDP_UDPHDR_SZ;
308                         uh4->Unused = 0;
309                         uh4->udpproto = IP_UDPPROTO;
310                         uh4->frag[0] = 0;
311                         uh4->frag[1] = 0;
312                         hnputs(uh4->udpplen, ptcllen);
313                         if (ucb->headers) {
314                                 v6tov4(uh4->udpdst, raddr);
315                                 hnputs(uh4->udpdport, rport);
316                                 v6tov4(uh4->udpsrc, laddr);
317                                 rc = NULL;
318                         } else {
319                                 v6tov4(uh4->udpdst, c->raddr);
320                                 hnputs(uh4->udpdport, c->rport);
321                                 if (ipcmp(c->laddr, IPnoaddr) == 0)
322                                         findlocalip(f, c->laddr, c->raddr);
323                                 v6tov4(uh4->udpsrc, c->laddr);
324                                 rc = c;
325                         }
326                         hnputs(uh4->udpsport, c->lport);
327                         hnputs(uh4->udplen, ptcllen);
328                         uh4->udpcksum[0] = 0;
329                         uh4->udpcksum[1] = 0;
330                         hnputs(uh4->udpcksum,
331                                    ~ptclcsum(bp, UDP4_PHDR_OFF, UDP4_PHDR_SZ));
332                         bp->checksum_start = UDP4_IPHDR_SZ;
333                         bp->checksum_offset = uh4->udpcksum - uh4->udpsport;
334                         bp->network_offset = 0;
335                         bp->transport_offset = offsetof(Udp4hdr, udpsport);
336                         bp->flag |= Budpck;
337                         uh4->vihl = IP_VER4;
338                         ipoput4(f, bp, 0, c->ttl, c->tos, rc);
339                         break;
340
341                 case V6:
342                         bp = padblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ);
343                         if (bp == NULL)
344                                 return;
345
346                         // using the v6 ip header to create pseudo header
347                         // first then reset it to the normal ip header
348                         uh6 = (Udp6hdr *) (bp->rp);
349                         memset(uh6, 0, 8);
350                         ptcllen = dlen + UDP_UDPHDR_SZ;
351                         hnputl(uh6->viclfl, ptcllen);
352                         uh6->hoplimit = IP_UDPPROTO;
353                         if (ucb->headers) {
354                                 ipmove(uh6->udpdst, raddr);
355                                 hnputs(uh6->udpdport, rport);
356                                 ipmove(uh6->udpsrc, laddr);
357                                 rc = NULL;
358                         } else {
359                                 ipmove(uh6->udpdst, c->raddr);
360                                 hnputs(uh6->udpdport, c->rport);
361                                 if (ipcmp(c->laddr, IPnoaddr) == 0)
362                                         findlocalip(f, c->laddr, c->raddr);
363                                 ipmove(uh6->udpsrc, c->laddr);
364                                 rc = c;
365                         }
366                         hnputs(uh6->udpsport, c->lport);
367                         hnputs(uh6->udplen, ptcllen);
368                         uh6->udpcksum[0] = 0;
369                         uh6->udpcksum[1] = 0;
370                         hnputs(uh6->udpcksum,
371                                    ptclcsum(bp, UDP6_PHDR_OFF,
372                                                         dlen + UDP_UDPHDR_SZ + UDP6_PHDR_SZ));
373                         memset(uh6, 0, 8);
374                         bp->network_offset = 0;
375                         bp->transport_offset = offsetof(Udp6hdr, udpsport);
376                         uh6->viclfl[0] = IP_VER6;
377                         hnputs(uh6->len, ptcllen);
378                         uh6->nextheader = IP_UDPPROTO;
379                         ipoput6(f, bp, 0, c->ttl, c->tos, rc);
380                         break;
381
382                 default:
383                         panic("udpkick: version %d", version);
384         }
385         upriv->ustats.udpOutDatagrams++;
386 }
387
388 void udpiput(struct Proto *udp, struct Ipifc *ifc, struct block *bp)
389 {
390         int len;
391         Udp4hdr *uh4;
392         Udp6hdr *uh6;
393         struct conv *c;
394         Udpcb *ucb;
395         uint8_t raddr[IPaddrlen], laddr[IPaddrlen];
396         uint16_t rport, lport;
397         Udppriv *upriv;
398         struct Fs *f;
399         int version;
400         int ottl, oviclfl, olen;
401         uint8_t *p;
402
403         upriv = udp->priv;
404         f = udp->f;
405         upriv->ustats.udpInDatagrams++;
406
407         uh4 = (Udp4hdr *) (bp->rp);
408         version = ((uh4->vihl & 0xF0) == IP_VER6) ? V6 : V4;
409
410         /*
411          * Put back pseudo header for checksum
412          * (remember old values for icmpnoconv())
413          */
414         switch (version) {
415                 case V4:
416                         ottl = uh4->Unused;
417                         uh4->Unused = 0;
418                         len = nhgets(uh4->udplen);
419                         olen = nhgets(uh4->udpplen);
420                         hnputs(uh4->udpplen, len);
421
422                         v4tov6(raddr, uh4->udpsrc);
423                         v4tov6(laddr, uh4->udpdst);
424                         lport = nhgets(uh4->udpdport);
425                         rport = nhgets(uh4->udpsport);
426
427                         if (!(bp->flag & Budpck) &&
428                             (uh4->udpcksum[0] || uh4->udpcksum[1]) &&
429                             ptclcsum(bp, UDP4_PHDR_OFF, len + UDP4_PHDR_SZ)) {
430                                 upriv->ustats.udpInErrors++;
431                                 netlog(f, Logudp, "udp: checksum error %I\n",
432                                        raddr);
433                                 printd("udp: checksum error %I\n", raddr);
434                                 freeblist(bp);
435                                 return;
436                         }
437                         uh4->Unused = ottl;
438                         hnputs(uh4->udpplen, olen);
439                         break;
440                 case V6:
441                         uh6 = (Udp6hdr *) (bp->rp);
442                         len = nhgets(uh6->udplen);
443                         oviclfl = nhgetl(uh6->viclfl);
444                         olen = nhgets(uh6->len);
445                         ottl = uh6->hoplimit;
446                         ipmove(raddr, uh6->udpsrc);
447                         ipmove(laddr, uh6->udpdst);
448                         lport = nhgets(uh6->udpdport);
449                         rport = nhgets(uh6->udpsport);
450                         memset(uh6, 0, 8);
451                         hnputl(uh6->viclfl, len);
452                         uh6->hoplimit = IP_UDPPROTO;
453                         if (ptclcsum(bp, UDP6_PHDR_OFF, len + UDP6_PHDR_SZ)) {
454                                 upriv->ustats.udpInErrors++;
455                                 netlog(f, Logudp, "udp: checksum error %I\n", raddr);
456                                 printd("udp: checksum error %I\n", raddr);
457                                 freeblist(bp);
458                                 return;
459                         }
460                         hnputl(uh6->viclfl, oviclfl);
461                         hnputs(uh6->len, olen);
462                         uh6->nextheader = IP_UDPPROTO;
463                         uh6->hoplimit = ottl;
464                         break;
465                 default:
466                         panic("udpiput: version %d", version);
467                         return; /* to avoid a warning */
468         }
469
470         c = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
471         if (c == NULL) {
472                 /* no converstation found */
473                 upriv->ustats.udpNoPorts++;
474                 netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport,
475                            laddr, lport);
476
477                 switch (version) {
478                         case V4:
479                                 icmpnoconv(f, bp);
480                                 break;
481                         case V6:
482                                 icmphostunr(f, ifc, bp, icmp6_port_unreach, 0);
483                                 break;
484                         default:
485                                 panic("udpiput2: version %d", version);
486                 }
487
488                 freeblist(bp);
489                 return;
490         }
491
492         if (c->state == Bypass) {
493                 bypass_or_drop(c, bp);
494                 return;
495         }
496
497         ucb = (Udpcb *) c->ptcl;
498
499         if (c->state == Announced) {
500                 if (ucb->headers == 0) {
501                         /* create a new conversation */
502                         if (ipforme(f, laddr) != Runi) {
503                                 switch (version) {
504                                         case V4:
505                                                 v4tov6(laddr, ifc->lifc->local);
506                                                 break;
507                                         case V6:
508                                                 ipmove(laddr, ifc->lifc->local);
509                                                 break;
510                                         default:
511                                                 panic("udpiput3: version %d", version);
512                                 }
513                         }
514                         qlock(&udp->qlock);
515                         c = Fsnewcall(c, raddr, rport, laddr, lport, version);
516                         qunlock(&udp->qlock);
517                         if (c == NULL) {
518                                 freeblist(bp);
519                                 return;
520                         }
521                         iphtadd(&upriv->ht, c);
522                         ucb = (Udpcb *) c->ptcl;
523                 }
524         }
525
526         qlock(&c->qlock);
527
528         /*
529          * Trim the packet down to data size
530          */
531         len -= UDP_UDPHDR_SZ;
532         switch (version) {
533                 case V4:
534                         bp = trimblock(bp, UDP4_IPHDR_SZ + UDP_UDPHDR_SZ, len);
535                         break;
536                 case V6:
537                         bp = trimblock(bp, UDP6_IPHDR_SZ + UDP_UDPHDR_SZ, len);
538                         break;
539                 default:
540                         bp = NULL;
541                         panic("udpiput4: version %d", version);
542         }
543         if (bp == NULL) {
544                 qunlock(&c->qlock);
545                 netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport,
546                            laddr, lport);
547                 upriv->lenerr++;
548                 return;
549         }
550
551         netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport,
552                    laddr, lport, len);
553
554         switch (ucb->headers) {
555                 case 7:
556                         /* pass the src address */
557                         bp = padblock(bp, UDP_USEAD7);
558                         p = bp->rp;
559                         ipmove(p, raddr);
560                         p += IPaddrlen;
561                         ipmove(p, laddr);
562                         p += IPaddrlen;
563                         ipmove(p, ifc->lifc->local);
564                         p += IPaddrlen;
565                         hnputs(p, rport);
566                         p += 2;
567                         hnputs(p, lport);
568                         break;
569                 case 6:
570                         /* pass the src address */
571                         bp = padblock(bp, UDP_USEAD6);
572                         p = bp->rp;
573                         ipmove(p, raddr);
574                         p += IPaddrlen;
575                         ipmove(p, ipforme(f, laddr) == Runi ? laddr : ifc->lifc->local);
576                         p += IPaddrlen;
577                         hnputs(p, rport);
578                         p += 2;
579                         hnputs(p, lport);
580                         break;
581         }
582
583         if (bp->next)
584                 bp = concatblock(bp);
585
586         if (qfull(c->rq)) {
587                 qunlock(&c->qlock);
588                 netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport,
589                            laddr, lport);
590                 freeblist(bp);
591                 return;
592         }
593
594         qpass(c->rq, bp);
595         qunlock(&c->qlock);
596
597 }
598
599 static void udpctl(struct conv *c, char **f, int n)
600 {
601         Udpcb *ucb = (Udpcb*)c->ptcl;
602
603         if ((n == 1) && strcmp(f[0], "oldheaders") == 0)
604                 ucb->headers = 6;
605         else if ((n == 1) && strcmp(f[0], "headers") == 0)
606                 ucb->headers = 7;
607         else
608                 error(EINVAL, "unknown command to %s", __func__);
609 }
610
611 void udpadvise(struct Proto *udp, struct block *bp, char *msg)
612 {
613         Udp4hdr *h4;
614         Udp6hdr *h6;
615         uint8_t source[IPaddrlen], dest[IPaddrlen];
616         uint16_t psource, pdest;
617         struct conv *s, **p;
618         int version;
619
620         h4 = (Udp4hdr *) (bp->rp);
621         version = ((h4->vihl & 0xF0) == IP_VER6) ? V6 : V4;
622
623         switch (version) {
624                 case V4:
625                         v4tov6(dest, h4->udpdst);
626                         v4tov6(source, h4->udpsrc);
627                         psource = nhgets(h4->udpsport);
628                         pdest = nhgets(h4->udpdport);
629                         break;
630                 case V6:
631                         h6 = (Udp6hdr *) (bp->rp);
632                         ipmove(dest, h6->udpdst);
633                         ipmove(source, h6->udpsrc);
634                         psource = nhgets(h6->udpsport);
635                         pdest = nhgets(h6->udpdport);
636                         break;
637                 default:
638                         panic("udpadvise: version %d", version);
639                         return; /* to avoid a warning */
640         }
641
642         /* Look for a connection */
643         for (p = udp->conv; *p; p++) {
644                 s = *p;
645                 if (s->rport == pdest)
646                         if (s->lport == psource)
647                                 if (ipcmp(s->raddr, dest) == 0)
648                                         if (ipcmp(s->laddr, source) == 0) {
649                                                 if (s->ignoreadvice)
650                                                         break;
651                                                 qlock(&s->qlock);
652                                                 qhangup(s->rq, msg);
653                                                 qhangup(s->wq, msg);
654                                                 qunlock(&s->qlock);
655                                                 freeblist(bp);
656                                                 return;
657                                         }
658         }
659         freeblist(bp);
660 }
661
662 int udpstats(struct Proto *udp, char *buf, int len)
663 {
664         Udppriv *upriv;
665         char *p, *e;
666
667         upriv = udp->priv;
668         p = buf;
669         e = p + len;
670         p = seprintf(p, e, "InDatagrams: %u\n", upriv->ustats.udpInDatagrams);
671         p = seprintf(p, e, "NoPorts: %u\n", upriv->ustats.udpNoPorts);
672         p = seprintf(p, e, "InErrors: %u\n", upriv->ustats.udpInErrors);
673         p = seprintf(p, e, "OutDatagrams: %u\n", upriv->ustats.udpOutDatagrams);
674         return p - buf;
675 }
676
677 void udpinit(struct Fs *fs)
678 {
679         struct Proto *udp;
680
681         udp = kzmalloc(sizeof(struct Proto), 0);
682         udp->priv = kzmalloc(sizeof(Udppriv), 0);
683         udp->name = "udp";
684         udp->connect = udpconnect;
685         udp->bind = udpbind;
686         udp->announce = udpannounce;
687         udp->bypass = udpbypass;
688         udp->ctl = udpctl;
689         udp->state = udpstate;
690         udp->create = udpcreate;
691         udp->close = udpclose;
692         udp->rcv = udpiput;
693         udp->advise = udpadvise;
694         udp->stats = udpstats;
695         udp->ipproto = IP_UDPPROTO;
696         udp->nc = 4096;
697         udp->ptclsize = sizeof(Udpcb);
698
699         Fsproto(fs, udp);
700 }