icmp.c (9491B)
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 10 typedef struct Icmp { 11 uchar vihl; /* Version and header length */ 12 uchar tos; /* Type of service */ 13 uchar length[2]; /* packet length */ 14 uchar id[2]; /* Identification */ 15 uchar frag[2]; /* Fragment information */ 16 uchar ttl; /* Time to live */ 17 uchar proto; /* Protocol */ 18 uchar ipcksum[2]; /* Header checksum */ 19 uchar src[4]; /* Ip source */ 20 uchar dst[4]; /* Ip destination */ 21 uchar type; 22 uchar code; 23 uchar cksum[2]; 24 uchar icmpid[2]; 25 uchar seq[2]; 26 uchar data[1]; 27 } Icmp; 28 29 enum { /* Packet Types */ 30 EchoReply = 0, 31 Unreachable = 3, 32 SrcQuench = 4, 33 Redirect = 5, 34 EchoRequest = 8, 35 TimeExceed = 11, 36 InParmProblem = 12, 37 Timestamp = 13, 38 TimestampReply = 14, 39 InfoRequest = 15, 40 InfoReply = 16, 41 AddrMaskRequest = 17, 42 AddrMaskReply = 18, 43 44 Maxtype = 18, 45 }; 46 47 enum 48 { 49 MinAdvise = 24, /* minimum needed for us to advise another protocol */ 50 }; 51 52 char *icmpnames[Maxtype+1] = 53 { 54 [EchoReply] "EchoReply", 55 [Unreachable] "Unreachable", 56 [SrcQuench] "SrcQuench", 57 [Redirect] "Redirect", 58 [EchoRequest] "EchoRequest", 59 [TimeExceed] "TimeExceed", 60 [InParmProblem] "InParmProblem", 61 [Timestamp] "Timestamp", 62 [TimestampReply] "TimestampReply", 63 [InfoRequest] "InfoRequest", 64 [InfoReply] "InfoReply", 65 [AddrMaskRequest] "AddrMaskRequest", 66 [AddrMaskReply ] "AddrMaskReply ", 67 }; 68 69 enum { 70 IP_ICMPPROTO = 1, 71 ICMP_IPSIZE = 20, 72 ICMP_HDRSIZE = 8, 73 }; 74 75 enum 76 { 77 InMsgs, 78 InErrors, 79 OutMsgs, 80 CsumErrs, 81 LenErrs, 82 HlenErrs, 83 84 Nstats, 85 }; 86 87 static char *statnames[Nstats] = 88 { 89 [InMsgs] "InMsgs", 90 [InErrors] "InErrors", 91 [OutMsgs] "OutMsgs", 92 [CsumErrs] "CsumErrs", 93 [LenErrs] "LenErrs", 94 [HlenErrs] "HlenErrs", 95 }; 96 97 typedef struct Icmppriv Icmppriv; 98 struct Icmppriv 99 { 100 ulong stats[Nstats]; 101 102 /* message counts */ 103 ulong in[Maxtype+1]; 104 ulong out[Maxtype+1]; 105 }; 106 107 static void icmpkick(void *x, Block*); 108 109 static void 110 icmpcreate(Conv *c) 111 { 112 c->rq = qopen(64*1024, Qmsg, 0, c); 113 c->wq = qbypass(icmpkick, c); 114 } 115 116 extern char* 117 icmpconnect(Conv *c, char **argv, int argc) 118 { 119 char *e; 120 121 e = Fsstdconnect(c, argv, argc); 122 if(e != nil) 123 return e; 124 Fsconnected(c, e); 125 126 return nil; 127 } 128 129 extern int 130 icmpstate(Conv *c, char *state, int n) 131 { 132 USED(c); 133 return snprint(state, n, "%s qin %d qout %d\n", 134 "Datagram", 135 c->rq ? qlen(c->rq) : 0, 136 c->wq ? qlen(c->wq) : 0 137 ); 138 } 139 140 extern char* 141 icmpannounce(Conv *c, char **argv, int argc) 142 { 143 char *e; 144 145 e = Fsstdannounce(c, argv, argc); 146 if(e != nil) 147 return e; 148 Fsconnected(c, nil); 149 150 return nil; 151 } 152 153 extern void 154 icmpclose(Conv *c) 155 { 156 qclose(c->rq); 157 qclose(c->wq); 158 ipmove(c->laddr, IPnoaddr); 159 ipmove(c->raddr, IPnoaddr); 160 c->lport = 0; 161 } 162 163 static void 164 icmpkick(void *x, Block *bp) 165 { 166 Conv *c = x; 167 Icmp *p; 168 Icmppriv *ipriv; 169 170 if(bp == nil) 171 return; 172 173 if(blocklen(bp) < ICMP_IPSIZE + ICMP_HDRSIZE){ 174 freeblist(bp); 175 return; 176 } 177 p = (Icmp *)(bp->rp); 178 p->vihl = IP_VER4; 179 ipriv = c->p->priv; 180 if(p->type <= Maxtype) 181 ipriv->out[p->type]++; 182 183 v6tov4(p->dst, c->raddr); 184 v6tov4(p->src, c->laddr); 185 p->proto = IP_ICMPPROTO; 186 hnputs(p->icmpid, c->lport); 187 memset(p->cksum, 0, sizeof(p->cksum)); 188 hnputs(p->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); 189 ipriv->stats[OutMsgs]++; 190 ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil); 191 } 192 193 extern void 194 icmpttlexceeded(Fs *f, uchar *ia, Block *bp) 195 { 196 Block *nbp; 197 Icmp *p, *np; 198 199 p = (Icmp *)bp->rp; 200 201 netlog(f, Logicmp, "sending icmpttlexceeded -> %V\n", p->src); 202 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8); 203 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8; 204 np = (Icmp *)nbp->rp; 205 np->vihl = IP_VER4; 206 memmove(np->dst, p->src, sizeof(np->dst)); 207 v6tov4(np->src, ia); 208 memmove(np->data, bp->rp, ICMP_IPSIZE + 8); 209 np->type = TimeExceed; 210 np->code = 0; 211 np->proto = IP_ICMPPROTO; 212 hnputs(np->icmpid, 0); 213 hnputs(np->seq, 0); 214 memset(np->cksum, 0, sizeof(np->cksum)); 215 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); 216 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil); 217 218 } 219 220 static void 221 icmpunreachable(Fs *f, Block *bp, int code, int seq) 222 { 223 Block *nbp; 224 Icmp *p, *np; 225 int i; 226 uchar addr[IPaddrlen]; 227 228 p = (Icmp *)bp->rp; 229 230 /* only do this for unicast sources and destinations */ 231 v4tov6(addr, p->dst); 232 i = ipforme(f, addr); 233 if((i&Runi) == 0) 234 return; 235 v4tov6(addr, p->src); 236 i = ipforme(f, addr); 237 if(i != 0 && (i&Runi) == 0) 238 return; 239 240 netlog(f, Logicmp, "sending icmpnoconv -> %V\n", p->src); 241 nbp = allocb(ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8); 242 nbp->wp += ICMP_IPSIZE + ICMP_HDRSIZE + ICMP_IPSIZE + 8; 243 np = (Icmp *)nbp->rp; 244 np->vihl = IP_VER4; 245 memmove(np->dst, p->src, sizeof(np->dst)); 246 memmove(np->src, p->dst, sizeof(np->src)); 247 memmove(np->data, bp->rp, ICMP_IPSIZE + 8); 248 np->type = Unreachable; 249 np->code = code; 250 np->proto = IP_ICMPPROTO; 251 hnputs(np->icmpid, 0); 252 hnputs(np->seq, seq); 253 memset(np->cksum, 0, sizeof(np->cksum)); 254 hnputs(np->cksum, ptclcsum(nbp, ICMP_IPSIZE, blocklen(nbp) - ICMP_IPSIZE)); 255 ipoput4(f, nbp, 0, MAXTTL, DFLTTOS, nil); 256 } 257 258 extern void 259 icmpnoconv(Fs *f, Block *bp) 260 { 261 icmpunreachable(f, bp, 3, 0); 262 } 263 264 extern void 265 icmpcantfrag(Fs *f, Block *bp, int mtu) 266 { 267 icmpunreachable(f, bp, 4, mtu); 268 } 269 270 static void 271 goticmpkt(Proto *icmp, Block *bp) 272 { 273 Conv **c, *s; 274 Icmp *p; 275 uchar dst[IPaddrlen]; 276 ushort recid; 277 278 p = (Icmp *) bp->rp; 279 v4tov6(dst, p->src); 280 recid = nhgets(p->icmpid); 281 282 for(c = icmp->conv; *c; c++) { 283 s = *c; 284 if(s->lport == recid) 285 if(ipcmp(s->raddr, dst) == 0){ 286 bp = concatblock(bp); 287 if(bp != nil) 288 qpass(s->rq, bp); 289 return; 290 } 291 } 292 freeblist(bp); 293 } 294 295 static Block * 296 mkechoreply(Block *bp) 297 { 298 Icmp *q; 299 uchar ip[4]; 300 301 q = (Icmp *)bp->rp; 302 q->vihl = IP_VER4; 303 memmove(ip, q->src, sizeof(q->dst)); 304 memmove(q->src, q->dst, sizeof(q->src)); 305 memmove(q->dst, ip, sizeof(q->dst)); 306 q->type = EchoReply; 307 memset(q->cksum, 0, sizeof(q->cksum)); 308 hnputs(q->cksum, ptclcsum(bp, ICMP_IPSIZE, blocklen(bp) - ICMP_IPSIZE)); 309 310 return bp; 311 } 312 313 static char *unreachcode[] = 314 { 315 [0] "net unreachable", 316 [1] "host unreachable", 317 [2] "protocol unreachable", 318 [3] "port unreachable", 319 [4] "fragmentation needed and DF set", 320 [5] "source route failed", 321 }; 322 323 static void 324 icmpiput(Proto *icmp, Ipifc* __, Block *bp) 325 { 326 int n, iplen; 327 Icmp *p; 328 Block *r; 329 Proto *pr; 330 char *msg; 331 char m2[128]; 332 Icmppriv *ipriv; 333 334 ipriv = icmp->priv; 335 336 ipriv->stats[InMsgs]++; 337 338 p = (Icmp *)bp->rp; 339 netlog(icmp->f, Logicmp, "icmpiput %d %d\n", p->type, p->code); 340 n = blocklen(bp); 341 if(n < ICMP_IPSIZE+ICMP_HDRSIZE){ 342 ipriv->stats[InErrors]++; 343 ipriv->stats[HlenErrs]++; 344 netlog(icmp->f, Logicmp, "icmp hlen %d\n", n); 345 goto raise; 346 } 347 iplen = nhgets(p->length); 348 if(iplen > n || ((uint)iplen % 1)){ 349 ipriv->stats[LenErrs]++; 350 ipriv->stats[InErrors]++; 351 netlog(icmp->f, Logicmp, "icmp length %d\n", iplen); 352 goto raise; 353 } 354 if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){ 355 ipriv->stats[InErrors]++; 356 ipriv->stats[CsumErrs]++; 357 netlog(icmp->f, Logicmp, "icmp checksum error\n"); 358 goto raise; 359 } 360 if(p->type <= Maxtype) 361 ipriv->in[p->type]++; 362 363 switch(p->type) { 364 case EchoRequest: 365 if (iplen < n) 366 bp = trimblock(bp, 0, iplen); 367 r = mkechoreply(bp); 368 ipriv->out[EchoReply]++; 369 ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil); 370 break; 371 case Unreachable: 372 if(p->code > 5) 373 msg = unreachcode[1]; 374 else 375 msg = unreachcode[p->code]; 376 377 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; 378 if(blocklen(bp) < MinAdvise){ 379 ipriv->stats[LenErrs]++; 380 goto raise; 381 } 382 p = (Icmp *)bp->rp; 383 pr = Fsrcvpcolx(icmp->f, p->proto); 384 if(pr != nil && pr->advise != nil) { 385 (*pr->advise)(pr, bp, msg); 386 return; 387 } 388 389 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE; 390 goticmpkt(icmp, bp); 391 break; 392 case TimeExceed: 393 if(p->code == 0){ 394 sprint(m2, "ttl exceeded at %V", p->src); 395 396 bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE; 397 if(blocklen(bp) < MinAdvise){ 398 ipriv->stats[LenErrs]++; 399 goto raise; 400 } 401 p = (Icmp *)bp->rp; 402 pr = Fsrcvpcolx(icmp->f, p->proto); 403 if(pr != nil && pr->advise != nil) { 404 (*pr->advise)(pr, bp, m2); 405 return; 406 } 407 bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE; 408 } 409 410 goticmpkt(icmp, bp); 411 break; 412 default: 413 goticmpkt(icmp, bp); 414 break; 415 } 416 return; 417 418 raise: 419 freeblist(bp); 420 } 421 422 void 423 icmpadvise(Proto *icmp, Block *bp, char *msg) 424 { 425 Conv **c, *s; 426 Icmp *p; 427 uchar dst[IPaddrlen]; 428 ushort recid; 429 430 p = (Icmp *) bp->rp; 431 v4tov6(dst, p->dst); 432 recid = nhgets(p->icmpid); 433 434 for(c = icmp->conv; *c; c++) { 435 s = *c; 436 if(s->lport == recid) 437 if(ipcmp(s->raddr, dst) == 0){ 438 qhangup(s->rq, msg); 439 qhangup(s->wq, msg); 440 break; 441 } 442 } 443 freeblist(bp); 444 } 445 446 int 447 icmpstats(Proto *icmp, char *buf, int len) 448 { 449 Icmppriv *priv; 450 char *p, *e; 451 int i; 452 453 priv = icmp->priv; 454 p = buf; 455 e = p+len; 456 for(i = 0; i < Nstats; i++) 457 p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]); 458 for(i = 0; i <= Maxtype; i++){ 459 if(icmpnames[i]) 460 p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]); 461 else 462 p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]); 463 } 464 return p - buf; 465 } 466 467 void 468 icmpinit(Fs *fs) 469 { 470 Proto *icmp; 471 472 icmp = smalloc(sizeof(Proto)); 473 icmp->priv = smalloc(sizeof(Icmppriv)); 474 icmp->name = "icmp"; 475 icmp->connect = icmpconnect; 476 icmp->announce = icmpannounce; 477 icmp->state = icmpstate; 478 icmp->create = icmpcreate; 479 icmp->close = icmpclose; 480 icmp->rcv = icmpiput; 481 icmp->stats = icmpstats; 482 icmp->ctl = nil; 483 icmp->advise = icmpadvise; 484 icmp->gc = nil; 485 icmp->ipproto = IP_ICMPPROTO; 486 icmp->nc = 128; 487 icmp->ptclsize = 0; 488 489 Fsproto(fs, icmp); 490 }