vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

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 }