icmp.c 9.8 KB

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