udp.c (12404B)
1 #include "u.h" 2 #include "lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "error.h" 7 8 #include "ip.h" 9 #include "ipv6.h" 10 11 12 #define DPRINT if(0)print 13 14 enum 15 { 16 UDP_UDPHDR_SZ = 8, 17 18 UDP4_PHDR_OFF = 8, 19 UDP4_PHDR_SZ = 12, 20 UDP4_IPHDR_SZ = 20, 21 UDP6_IPHDR_SZ = 40, 22 UDP6_PHDR_SZ = 40, 23 UDP6_PHDR_OFF = 0, 24 25 IP_UDPPROTO = 17, 26 UDP_USEAD7 = 52, 27 28 Udprxms = 200, 29 Udptickms = 100, 30 Udpmaxxmit = 10, 31 }; 32 33 typedef struct Udp4hdr Udp4hdr; 34 struct Udp4hdr 35 { 36 /* ip header */ 37 uchar vihl; /* Version and header length */ 38 uchar tos; /* Type of service */ 39 uchar length[2]; /* packet length */ 40 uchar id[2]; /* Identification */ 41 uchar frag[2]; /* Fragment information */ 42 uchar Unused; 43 uchar udpproto; /* Protocol */ 44 uchar udpplen[2]; /* Header plus data length */ 45 uchar udpsrc[IPv4addrlen]; /* Ip source */ 46 uchar udpdst[IPv4addrlen]; /* Ip destination */ 47 48 /* udp header */ 49 uchar udpsport[2]; /* Source port */ 50 uchar udpdport[2]; /* Destination port */ 51 uchar udplen[2]; /* data length */ 52 uchar udpcksum[2]; /* Checksum */ 53 }; 54 55 typedef struct Udp6hdr Udp6hdr; 56 struct Udp6hdr { 57 uchar viclfl[4]; 58 uchar len[2]; 59 uchar nextheader; 60 uchar hoplimit; 61 uchar udpsrc[IPaddrlen]; 62 uchar udpdst[IPaddrlen]; 63 64 /* udp header */ 65 uchar udpsport[2]; /* Source port */ 66 uchar udpdport[2]; /* Destination port */ 67 uchar udplen[2]; /* data length */ 68 uchar udpcksum[2]; /* Checksum */ 69 }; 70 71 /* MIB II counters */ 72 typedef struct Udpstats Udpstats; 73 struct Udpstats 74 { 75 ulong udpInDatagrams; 76 ulong udpNoPorts; 77 ulong udpInErrors; 78 ulong udpOutDatagrams; 79 }; 80 81 typedef struct Udppriv Udppriv; 82 struct Udppriv 83 { 84 Ipht ht; 85 86 /* MIB counters */ 87 Udpstats ustats; 88 89 /* non-MIB stats */ 90 ulong csumerr; /* checksum errors */ 91 ulong lenerr; /* short packet */ 92 }; 93 94 void (*etherprofiler)(char *name, int qlen); 95 void udpkick(void *x, Block *bp); 96 97 /* 98 * protocol specific part of Conv 99 */ 100 typedef struct Udpcb Udpcb; 101 struct Udpcb 102 { 103 QLock qlock; 104 uchar headers; 105 }; 106 107 static char* 108 udpconnect(Conv *c, char **argv, int argc) 109 { 110 char *e; 111 Udppriv *upriv; 112 113 upriv = c->p->priv; 114 e = Fsstdconnect(c, argv, argc); 115 Fsconnected(c, e); 116 if(e != nil) 117 return e; 118 119 iphtadd(&upriv->ht, c); 120 return nil; 121 } 122 123 124 static int 125 udpstate(Conv *c, char *state, int n) 126 { 127 return snprint(state, n, "%s qin %d qout %d\n", 128 c->inuse ? "Open" : "Closed", 129 c->rq ? qlen(c->rq) : 0, 130 c->wq ? qlen(c->wq) : 0 131 ); 132 } 133 134 static char* 135 udpannounce(Conv *c, char** argv, int argc) 136 { 137 char *e; 138 Udppriv *upriv; 139 140 upriv = c->p->priv; 141 e = Fsstdannounce(c, argv, argc); 142 if(e != nil) 143 return e; 144 Fsconnected(c, nil); 145 iphtadd(&upriv->ht, c); 146 147 return nil; 148 } 149 150 static void 151 udpcreate(Conv *c) 152 { 153 c->rq = qopen(128*1024, Qmsg, 0, 0); 154 c->wq = qbypass(udpkick, c); 155 } 156 157 static void 158 udpclose(Conv *c) 159 { 160 Udpcb *ucb; 161 Udppriv *upriv; 162 163 upriv = c->p->priv; 164 iphtrem(&upriv->ht, c); 165 166 c->state = 0; 167 qclose(c->rq); 168 qclose(c->wq); 169 qclose(c->eq); 170 ipmove(c->laddr, IPnoaddr); 171 ipmove(c->raddr, IPnoaddr); 172 c->lport = 0; 173 c->rport = 0; 174 175 ucb = (Udpcb*)c->ptcl; 176 ucb->headers = 0; 177 } 178 179 void 180 udpkick(void *x, Block *bp) 181 { 182 Conv *c = x; 183 Udp4hdr *uh4; 184 Udp6hdr *uh6; 185 ushort rport; 186 uchar laddr[IPaddrlen], raddr[IPaddrlen]; 187 Udpcb *ucb; 188 int dlen, ptcllen; 189 Udppriv *upriv; 190 Fs *f; 191 int version; 192 Conv *rc; 193 194 upriv = c->p->priv; 195 f = c->p->f; 196 197 netlog(c->p->f, Logudp, "udp: kick\n"); 198 if(bp == nil) 199 return; 200 201 ucb = (Udpcb*)c->ptcl; 202 switch(ucb->headers) { 203 case 7: 204 /* get user specified addresses */ 205 bp = pullupblock(bp, UDP_USEAD7); 206 if(bp == nil) 207 return; 208 ipmove(raddr, bp->rp); 209 bp->rp += IPaddrlen; 210 ipmove(laddr, bp->rp); 211 bp->rp += IPaddrlen; 212 /* pick interface closest to dest */ 213 if(ipforme(f, laddr) != Runi) 214 findlocalip(f, laddr, raddr); 215 bp->rp += IPaddrlen; /* Ignore ifc address */ 216 rport = nhgets(bp->rp); 217 bp->rp += 2+2; /* Ignore local port */ 218 break; 219 default: 220 rport = 0; 221 break; 222 } 223 224 if(ucb->headers) { 225 if(memcmp(laddr, v4prefix, IPv4off) == 0 226 || ipcmp(laddr, IPnoaddr) == 0) 227 version = 4; 228 else 229 version = 6; 230 } else { 231 if( (memcmp(c->raddr, v4prefix, IPv4off) == 0 && 232 memcmp(c->laddr, v4prefix, IPv4off) == 0) 233 || ipcmp(c->raddr, IPnoaddr) == 0) 234 version = 4; 235 else 236 version = 6; 237 } 238 239 dlen = blocklen(bp); 240 241 /* fill in pseudo header and compute checksum */ 242 switch(version){ 243 case V4: 244 bp = padblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ); 245 if(bp == nil) 246 return; 247 248 uh4 = (Udp4hdr *)(bp->rp); 249 ptcllen = dlen + UDP_UDPHDR_SZ; 250 uh4->Unused = 0; 251 uh4->udpproto = IP_UDPPROTO; 252 uh4->frag[0] = 0; 253 uh4->frag[1] = 0; 254 hnputs(uh4->udpplen, ptcllen); 255 if(ucb->headers) { 256 v6tov4(uh4->udpdst, raddr); 257 hnputs(uh4->udpdport, rport); 258 v6tov4(uh4->udpsrc, laddr); 259 rc = nil; 260 } else { 261 v6tov4(uh4->udpdst, c->raddr); 262 hnputs(uh4->udpdport, c->rport); 263 if(ipcmp(c->laddr, IPnoaddr) == 0) 264 findlocalip(f, c->laddr, c->raddr); 265 v6tov4(uh4->udpsrc, c->laddr); 266 rc = c; 267 } 268 hnputs(uh4->udpsport, c->lport); 269 hnputs(uh4->udplen, ptcllen); 270 uh4->udpcksum[0] = 0; 271 uh4->udpcksum[1] = 0; 272 hnputs(uh4->udpcksum, 273 ptclcsum(bp, UDP4_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP4_PHDR_SZ)); 274 uh4->vihl = IP_VER4; 275 ipoput4(f, bp, 0, c->ttl, c->tos, rc); 276 break; 277 278 case V6: 279 bp = padblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ); 280 if(bp == nil) 281 return; 282 283 /* 284 * using the v6 ip header to create pseudo header 285 * first then reset it to the normal ip header 286 */ 287 uh6 = (Udp6hdr *)(bp->rp); 288 memset(uh6, 0, 8); 289 ptcllen = dlen + UDP_UDPHDR_SZ; 290 hnputl(uh6->viclfl, ptcllen); 291 uh6->hoplimit = IP_UDPPROTO; 292 if(ucb->headers) { 293 ipmove(uh6->udpdst, raddr); 294 hnputs(uh6->udpdport, rport); 295 ipmove(uh6->udpsrc, laddr); 296 rc = nil; 297 } else { 298 ipmove(uh6->udpdst, c->raddr); 299 hnputs(uh6->udpdport, c->rport); 300 if(ipcmp(c->laddr, IPnoaddr) == 0) 301 findlocalip(f, c->laddr, c->raddr); 302 ipmove(uh6->udpsrc, c->laddr); 303 rc = c; 304 } 305 hnputs(uh6->udpsport, c->lport); 306 hnputs(uh6->udplen, ptcllen); 307 uh6->udpcksum[0] = 0; 308 uh6->udpcksum[1] = 0; 309 hnputs(uh6->udpcksum, 310 ptclcsum(bp, UDP6_PHDR_OFF, dlen+UDP_UDPHDR_SZ+UDP6_PHDR_SZ)); 311 memset(uh6, 0, 8); 312 uh6->viclfl[0] = IP_VER6; 313 hnputs(uh6->len, ptcllen); 314 uh6->nextheader = IP_UDPPROTO; 315 ipoput6(f, bp, 0, c->ttl, c->tos, rc); 316 break; 317 318 default: 319 panic("udpkick: version %d", version); 320 } 321 upriv->ustats.udpOutDatagrams++; 322 } 323 324 void 325 udpiput(Proto *udp, Ipifc *ifc, Block *bp) 326 { 327 int len; 328 Udp4hdr *uh4; 329 Udp6hdr *uh6; 330 Conv *c; 331 Udpcb *ucb; 332 uchar raddr[IPaddrlen], laddr[IPaddrlen]; 333 ushort rport, lport; 334 Udppriv *upriv; 335 Fs *f; 336 int version; 337 int ottl, oviclfl, olen; 338 uchar *p; 339 340 upriv = udp->priv; 341 f = udp->f; 342 upriv->ustats.udpInDatagrams++; 343 344 uh4 = (Udp4hdr*)(bp->rp); 345 version = ((uh4->vihl&0xF0)==IP_VER6) ? 6 : 4; 346 347 /* Put back pseudo header for checksum 348 * (remember old values for icmpnoconv()) */ 349 switch(version) { 350 case V4: 351 ottl = uh4->Unused; 352 uh4->Unused = 0; 353 len = nhgets(uh4->udplen); 354 olen = nhgets(uh4->udpplen); 355 hnputs(uh4->udpplen, len); 356 357 v4tov6(raddr, uh4->udpsrc); 358 v4tov6(laddr, uh4->udpdst); 359 lport = nhgets(uh4->udpdport); 360 rport = nhgets(uh4->udpsport); 361 362 if(nhgets(uh4->udpcksum)) { 363 if(ptclcsum(bp, UDP4_PHDR_OFF, len+UDP4_PHDR_SZ)) { 364 upriv->ustats.udpInErrors++; 365 netlog(f, Logudp, "udp: checksum error %I\n", raddr); 366 DPRINT("udp: checksum error %I\n", raddr); 367 freeblist(bp); 368 return; 369 } 370 } 371 uh4->Unused = ottl; 372 hnputs(uh4->udpplen, olen); 373 break; 374 case V6: 375 uh6 = (Udp6hdr*)(bp->rp); 376 len = nhgets(uh6->udplen); 377 oviclfl = nhgetl(uh6->viclfl); 378 olen = nhgets(uh6->len); 379 ottl = uh6->hoplimit; 380 ipmove(raddr, uh6->udpsrc); 381 ipmove(laddr, uh6->udpdst); 382 lport = nhgets(uh6->udpdport); 383 rport = nhgets(uh6->udpsport); 384 memset(uh6, 0, 8); 385 hnputl(uh6->viclfl, len); 386 uh6->hoplimit = IP_UDPPROTO; 387 if(ptclcsum(bp, UDP6_PHDR_OFF, len+UDP6_PHDR_SZ)) { 388 upriv->ustats.udpInErrors++; 389 netlog(f, Logudp, "udp: checksum error %I\n", raddr); 390 DPRINT("udp: checksum error %I\n", raddr); 391 freeblist(bp); 392 return; 393 } 394 hnputl(uh6->viclfl, oviclfl); 395 hnputs(uh6->len, olen); 396 uh6->nextheader = IP_UDPPROTO; 397 uh6->hoplimit = ottl; 398 break; 399 default: 400 panic("udpiput: version %d", version); 401 return; /* to avoid a warning */ 402 } 403 404 QLOCK(udp); 405 406 c = iphtlook(&upriv->ht, raddr, rport, laddr, lport); 407 if(c == nil){ 408 /* no conversation found */ 409 upriv->ustats.udpNoPorts++; 410 QUNLOCK(udp); 411 netlog(f, Logudp, "udp: no conv %I!%d -> %I!%d\n", raddr, rport, 412 laddr, lport); 413 414 switch(version){ 415 case V4: 416 icmpnoconv(f, bp); 417 break; 418 case V6: 419 icmphostunr(f, ifc, bp, Icmp6_port_unreach, 0); 420 break; 421 default: 422 panic("udpiput2: version %d", version); 423 } 424 425 freeblist(bp); 426 return; 427 } 428 ucb = (Udpcb*)c->ptcl; 429 430 if(c->state == Announced){ 431 if(ucb->headers == 0){ 432 /* create a new conversation */ 433 if(ipforme(f, laddr) != Runi) { 434 switch(version){ 435 case V4: 436 v4tov6(laddr, ifc->lifc->local); 437 break; 438 case V6: 439 ipmove(laddr, ifc->lifc->local); 440 break; 441 default: 442 panic("udpiput3: version %d", version); 443 } 444 } 445 c = Fsnewcall(c, raddr, rport, laddr, lport, version); 446 if(c == nil){ 447 QUNLOCK(udp); 448 freeblist(bp); 449 return; 450 } 451 iphtadd(&upriv->ht, c); 452 ucb = (Udpcb*)c->ptcl; 453 } 454 } 455 456 QLOCK(c); 457 QUNLOCK(udp); 458 459 /* 460 * Trim the packet down to data size 461 */ 462 len -= UDP_UDPHDR_SZ; 463 switch(version){ 464 case V4: 465 bp = trimblock(bp, UDP4_IPHDR_SZ+UDP_UDPHDR_SZ, len); 466 break; 467 case V6: 468 bp = trimblock(bp, UDP6_IPHDR_SZ+UDP_UDPHDR_SZ, len); 469 break; 470 default: 471 bp = nil; 472 panic("udpiput4: version %d", version); 473 } 474 if(bp == nil){ 475 QUNLOCK(c); 476 netlog(f, Logudp, "udp: len err %I.%d -> %I.%d\n", raddr, rport, 477 laddr, lport); 478 upriv->lenerr++; 479 return; 480 } 481 482 netlog(f, Logudpmsg, "udp: %I.%d -> %I.%d l %d\n", raddr, rport, 483 laddr, lport, len); 484 485 switch(ucb->headers){ 486 case 7: 487 /* pass the src address */ 488 bp = padblock(bp, UDP_USEAD7); 489 p = bp->rp; 490 ipmove(p, raddr); p += IPaddrlen; 491 ipmove(p, laddr); p += IPaddrlen; 492 ipmove(p, ifc->lifc->local); p += IPaddrlen; 493 hnputs(p, rport); p += 2; 494 hnputs(p, lport); 495 break; 496 } 497 498 if(bp->next) 499 bp = concatblock(bp); 500 501 if(qfull(c->rq)){ 502 QUNLOCK(c); 503 netlog(f, Logudp, "udp: qfull %I.%d -> %I.%d\n", raddr, rport, 504 laddr, lport); 505 freeblist(bp); 506 return; 507 } 508 509 qpass(c->rq, bp); 510 QUNLOCK(c); 511 512 } 513 514 char* 515 udpctl(Conv *c, char **f, int n) 516 { 517 Udpcb *ucb; 518 519 ucb = (Udpcb*)c->ptcl; 520 if(n == 1){ 521 if(strcmp(f[0], "headers") == 0){ 522 ucb->headers = 7; /* new headers format */ 523 return nil; 524 } 525 } 526 return "unknown control request"; 527 } 528 529 void 530 udpadvise(Proto *udp, Block *bp, char *msg) 531 { 532 Udp4hdr *h4; 533 Udp6hdr *h6; 534 uchar source[IPaddrlen], dest[IPaddrlen]; 535 ushort psource, pdest; 536 Conv *s, **p; 537 int version; 538 539 h4 = (Udp4hdr*)(bp->rp); 540 version = ((h4->vihl&0xF0)==IP_VER6) ? 6 : 4; 541 542 switch(version) { 543 case V4: 544 v4tov6(dest, h4->udpdst); 545 v4tov6(source, h4->udpsrc); 546 psource = nhgets(h4->udpsport); 547 pdest = nhgets(h4->udpdport); 548 break; 549 case V6: 550 h6 = (Udp6hdr*)(bp->rp); 551 ipmove(dest, h6->udpdst); 552 ipmove(source, h6->udpsrc); 553 psource = nhgets(h6->udpsport); 554 pdest = nhgets(h6->udpdport); 555 break; 556 default: 557 panic("udpadvise: version %d", version); 558 return; /* to avoid a warning */ 559 } 560 561 /* Look for a connection */ 562 QLOCK(udp); 563 for(p = udp->conv; *p; p++) { 564 s = *p; 565 if(s->rport == pdest) 566 if(s->lport == psource) 567 if(ipcmp(s->raddr, dest) == 0) 568 if(ipcmp(s->laddr, source) == 0){ 569 if(s->ignoreadvice) 570 break; 571 QLOCK(s); 572 QUNLOCK(udp); 573 qhangup(s->rq, msg); 574 qhangup(s->wq, msg); 575 QUNLOCK(s); 576 freeblist(bp); 577 return; 578 } 579 } 580 QUNLOCK(udp); 581 freeblist(bp); 582 } 583 584 int 585 udpstats(Proto *udp, char *buf, int len) 586 { 587 Udppriv *upriv; 588 589 upriv = udp->priv; 590 return snprint(buf, len, "InDatagrams: %lud\nNoPorts: %lud\nInErrors: %lud\nOutDatagrams: %lud\n", 591 upriv->ustats.udpInDatagrams, 592 upriv->ustats.udpNoPorts, 593 upriv->ustats.udpInErrors, 594 upriv->ustats.udpOutDatagrams); 595 } 596 597 void 598 udpinit(Fs *fs) 599 { 600 Proto *udp; 601 602 udp = smalloc(sizeof(Proto)); 603 udp->priv = smalloc(sizeof(Udppriv)); 604 udp->name = "udp"; 605 udp->connect = udpconnect; 606 udp->announce = udpannounce; 607 udp->ctl = udpctl; 608 udp->state = udpstate; 609 udp->create = udpcreate; 610 udp->close = udpclose; 611 udp->rcv = udpiput; 612 udp->advise = udpadvise; 613 udp->stats = udpstats; 614 udp->ipproto = IP_UDPPROTO; 615 udp->nc = Nchans; 616 udp->ptclsize = sizeof(Udpcb); 617 618 Fsproto(fs, udp); 619 }