Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "u.h"
2
#include "../port/lib.h"
3
#include "mem.h"
4
#include "dat.h"
5
#include "fns.h"
6
#include "../port/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 %s (%d) %d\n",
340
		(p->type < nelem(icmpnames)? icmpnames[p->type]: ""),
341
		p->type, p->code);
342
	n = blocklen(bp);
343
	if(n < ICMP_IPSIZE+ICMP_HDRSIZE){
344
		ipriv->stats[InErrors]++;
345
		ipriv->stats[HlenErrs]++;
346
		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
347
		goto raise;
348
	}
349
	iplen = nhgets(p->length);
350
	if(iplen > n){
351
		ipriv->stats[LenErrs]++;
352
		ipriv->stats[InErrors]++;
353
		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
354
		goto raise;
355
	}
356
	if(ptclcsum(bp, ICMP_IPSIZE, iplen - ICMP_IPSIZE)){
357
		ipriv->stats[InErrors]++;
358
		ipriv->stats[CsumErrs]++;
359
		netlog(icmp->f, Logicmp, "icmp checksum error\n");
360
		goto raise;
361
	}
362
	if(p->type <= Maxtype)
363
		ipriv->in[p->type]++;
364
 
365
	switch(p->type) {
366
	case EchoRequest:
367
		if (iplen < n)
368
			bp = trimblock(bp, 0, iplen);
369
		r = mkechoreply(concatblock(bp));
370
		ipriv->out[EchoReply]++;
371
		ipoput4(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
372
		break;
373
	case Unreachable:
374
		if(p->code > 5)
375
			msg = unreachcode[1];
376
		else
377
			msg = unreachcode[p->code];
378
 
379
		bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
380
		if(blocklen(bp) < MinAdvise){
381
			ipriv->stats[LenErrs]++;
382
			goto raise;
383
		}
384
		p = (Icmp *)bp->rp;
385
		pr = Fsrcvpcolx(icmp->f, p->proto);
386
		if(pr != nil && pr->advise != nil) {
387
			(*pr->advise)(pr, bp, msg);
388
			return;
389
		}
390
 
391
		bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
392
		goticmpkt(icmp, bp);
393
		break;
394
	case TimeExceed:
395
		if(p->code == 0){
396
			snprint(m2, sizeof m2, "ttl exceeded at %V", p->src);
397
 
398
			bp->rp += ICMP_IPSIZE+ICMP_HDRSIZE;
399
			if(blocklen(bp) < MinAdvise){
400
				ipriv->stats[LenErrs]++;
401
				goto raise;
402
			}
403
			p = (Icmp *)bp->rp;
404
			pr = Fsrcvpcolx(icmp->f, p->proto);
405
			if(pr != nil && pr->advise != nil) {
406
				(*pr->advise)(pr, bp, m2);
407
				return;
408
			}
409
			bp->rp -= ICMP_IPSIZE+ICMP_HDRSIZE;
410
		}
411
 
412
		goticmpkt(icmp, bp);
413
		break;
414
	default:
415
		goticmpkt(icmp, bp);
416
		break;
417
	}
418
	return;
419
 
420
raise:
421
	freeblist(bp);
422
}
423
 
424
void
425
icmpadvise(Proto *icmp, Block *bp, char *msg)
426
{
427
	Conv	**c, *s;
428
	Icmp	*p;
429
	uchar	dst[IPaddrlen];
430
	ushort	recid;
431
 
432
	p = (Icmp *) bp->rp;
433
	v4tov6(dst, p->dst);
434
	recid = nhgets(p->icmpid);
435
 
436
	for(c = icmp->conv; *c; c++) {
437
		s = *c;
438
		if(s->lport == recid)
439
		if(ipcmp(s->raddr, dst) == 0){
440
			qhangup(s->rq, msg);
441
			qhangup(s->wq, msg);
442
			break;
443
		}
444
	}
445
	freeblist(bp);
446
}
447
 
448
int
449
icmpstats(Proto *icmp, char *buf, int len)
450
{
451
	Icmppriv *priv;
452
	char *p, *e;
453
	int i;
454
 
455
	priv = icmp->priv;
456
	p = buf;
457
	e = p+len;
458
	for(i = 0; i < Nstats; i++)
459
		p = seprint(p, e, "%s: %lud\n", statnames[i], priv->stats[i]);
460
	for(i = 0; i <= Maxtype; i++){
461
		if(icmpnames[i])
462
			p = seprint(p, e, "%s: %lud %lud\n", icmpnames[i], priv->in[i], priv->out[i]);
463
		else
464
			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i], priv->out[i]);
465
	}
466
	return p - buf;
467
}
468
 
469
void
470
icmpinit(Fs *fs)
471
{
472
	Proto *icmp;
473
 
474
	icmp = smalloc(sizeof(Proto));
475
	icmp->priv = smalloc(sizeof(Icmppriv));
476
	icmp->name = "icmp";
477
	icmp->connect = icmpconnect;
478
	icmp->announce = icmpannounce;
479
	icmp->state = icmpstate;
480
	icmp->create = icmpcreate;
481
	icmp->close = icmpclose;
482
	icmp->rcv = icmpiput;
483
	icmp->stats = icmpstats;
484
	icmp->ctl = nil;
485
	icmp->advise = icmpadvise;
486
	icmp->gc = nil;
487
	icmp->ipproto = IP_ICMPPROTO;
488
	icmp->nc = 128;
489
	icmp->ptclsize = 0;
490
 
491
	Fsproto(fs, icmp);
492
}