xref: /plan9/sys/src/cmd/ip/snoopy/icmp6.c (revision e6dcbf51e935975016093545b6eab69976b6e257)
1 #include <u.h>
2 #include <libc.h>
3 #include <ip.h>
4 #include "dat.h"
5 #include "protos.h"
6 
7 typedef struct Hdr	Hdr;
8 struct Hdr
9 {	uchar	type;
10 	uchar	code;
11 	uchar	cksum[2];	/* Checksum */
12 	uchar	data[1];
13 };
14 
15 enum
16 {
17 	ICMP6LEN=	4,
18 };
19 
20 enum
21 {
22 	Ot,	/* type */
23 	Op,	/* next protocol */
24 };
25 
26 static Field p_fields[] =
27 {
28 	{"t",		Fnum,	Ot,	"type",	} ,
29 	{0}
30 };
31 
32 enum
33 {
34 	/* ICMPv6 types */
35 	EchoReply	= 0,
36 	UnreachableV6	= 1,
37 	PacketTooBigV6	= 2,
38 	TimeExceedV6	= 3,
39 	ParamProblemV6	= 4,
40 	Redirect	= 5,
41 	EchoRequest	= 8,
42 	TimeExceed	= 11,
43 	InParmProblem	= 12,
44 	Timestamp	= 13,
45 	TimestampReply	= 14,
46 	InfoRequest	= 15,
47 	InfoReply	= 16,
48 	AddrMaskRequest = 17,
49 	AddrMaskReply   = 18,
50 	EchoRequestV6	= 128,
51 	EchoReplyV6	= 129,
52 	RouterSolicit	= 133,
53 	RouterAdvert	= 134,
54 	NbrSolicit	= 135,
55 	NbrAdvert	= 136,
56 	RedirectV6	= 137,
57 
58 	Maxtype6	= 137,
59 };
60 
61 static Mux p_mux[] =
62 {
63 	{"ip6",	UnreachableV6, },
64 	{"ip6",	RedirectV6, },
65 	{"ip6",	TimeExceedV6, },
66 	{0},
67 };
68 
69 char *icmpmsg6[256] =
70 {
71 [EchoReply]		"EchoReply",
72 [UnreachableV6]		"UnreachableV6",
73 [PacketTooBigV6]	"PacketTooBigV6",
74 [TimeExceedV6]		"TimeExceedV6",
75 [Redirect]		"Redirect",
76 [EchoRequest]		"EchoRequest",
77 [TimeExceed]		"TimeExceed",
78 [InParmProblem]		"InParmProblem",
79 [Timestamp]		"Timestamp",
80 [TimestampReply]	"TimestampReply",
81 [InfoRequest]		"InfoRequest",
82 [InfoReply]		"InfoReply",
83 [AddrMaskRequest]	"AddrMaskRequest",
84 [AddrMaskReply]		"AddrMaskReply",
85 [EchoRequestV6]		"EchoRequestV6",
86 [EchoReplyV6]		"EchoReplyV6",
87 [RouterSolicit]		"RouterSolicit",
88 [RouterAdvert]		"RouterAdvert",
89 [NbrSolicit]		"NbrSolicit",
90 [NbrAdvert]		"NbrAdvert",
91 [RedirectV6]		"RedirectV6",
92 };
93 
94 static char *unreachcode[] =
95 {
96 [0]	"no route to destination",
97 [1]	"comm with destination administratively prohibited",
98 [2]	"icmp unreachable: unassigned error code (2)",
99 [3]	"address unreachable",
100 [4]	"port unreachable",
101 [5]	"icmp unreachable: unknown code",
102 };
103 
104 static char *timexcode[] =
105 {
106 [0]	"hop limit exc",
107 [1]	"reassmbl time exc",
108 [2]	"icmp time exc: unknown code",
109 };
110 
111 static char *parpcode[] =
112 {
113 [0]	"erroneous header field encountered",
114 [1]	"unrecognized Next Header type encountered",
115 [2]	"unrecognized IPv6 option encountered",
116 [3]	"icmp par prob: unknown code",
117 };
118 enum
119 {
120 	sll	= 1,
121 	tll	= 2,
122 	pref	= 3,
123 	redir	= 4,
124 	mtu	= 5,
125 };
126 
127 static char *icmp6opts[256] =
128 {
129 [0]	"unknown opt",
130 [1]	"sll_addr",
131 [2]	"tll_addr",
132 [3]	"pref_opt",
133 [4]	"redirect",
134 [5]	"mtu_opt",
135 };
136 
137 static void
p_compile(Filter * f)138 p_compile(Filter *f)
139 {
140 	if(f->op == '='){
141 		compile_cmp(icmp6.name, f, p_fields);
142 		return;
143 	}
144 	if(strcmp(f->s, "ip6") == 0){
145 		f->pr = p_mux->pr;
146 		f->subop = Op;
147 		return;
148 	}
149 	sysfatal("unknown icmp field or protocol: %s", f->s);
150 }
151 
152 static int
p_filter(Filter * f,Msg * m)153 p_filter(Filter *f, Msg *m)
154 {
155 	Hdr *h;
156 
157 	if(m->pe - m->ps < ICMP6LEN)
158 		return 0;
159 
160 	h = (Hdr*)m->ps;
161 	m->ps += ICMP6LEN;
162 
163 	switch(f->subop){
164 
165 	case Ot:
166 		if(h->type == f->ulv)
167 			return 1;
168 		break;
169 	case Op:
170 		switch(h->type){
171 		case UnreachableV6:
172 		case RedirectV6:
173 		case TimeExceedV6:
174 			m->ps += 4;
175 			return 1;
176 		}
177 	}
178 	return 0;
179 }
180 
181 static char*
opt_seprint(Msg * m)182 opt_seprint(Msg *m)
183 {
184 	int otype, osz, pktsz;
185 	uchar *a;
186 	char *p = m->p;
187 	char *e = m->e;
188 	char *opt;
189 	char optbuf[12];
190 
191 	pktsz = m->pe - m->ps;
192 	a = m->ps;
193 	while (pktsz > 0) {
194 		otype = *a;
195 		opt = icmp6opts[otype];
196 		if(opt == nil){
197 			sprint(optbuf, "0x%ux", otype);
198 			opt = optbuf;
199 		}
200 		osz = (*(a+1)) * 8;
201 
202 		switch (otype) {
203 		default:
204 			p = seprint(p, e, "\n	  option=%s ", opt);
205 			m->pr = &dump;
206 			return p;
207 
208 		case sll:
209 		case tll:
210 			if (pktsz < osz || osz != 8) {
211 				p = seprint(p, e, "\n	  option=%s bad size=%d",
212 					opt, osz);
213 				m->pr = &dump;
214 				return p;
215 			}
216 			p = seprint(p, e, "\n	  option=%s maddr=%E", opt, a+2);
217 			pktsz -= osz;
218 			a += osz;
219 			break;
220 
221 		case pref:
222 			if ((pktsz < osz) || (osz != 32)) {
223 				p = seprint(p, e, "\n	  option=%s: bad size=%d",
224 					opt, osz);
225 				m->pr = &dump;
226 				return p;
227 			}
228 
229 			p = seprint(p, e, "\n	  option=%s pref=%I "
230 				"preflen=%3.3d lflag=%1.1d aflag=%1.1d "
231 				"unused1=%1.1d validlt=%d preflt=%d unused2=%1.1d",
232 				opt,
233 				a+16,
234 				(int) (*(a+2)),
235 				(*(a+3) & (1 << 7)) != 0,
236 				(*(a+3) & (1 << 6)) != 0,
237 				(*(a+3) & 63) != 0,
238 				NetL(a+4),
239 				NetL(a+8),
240 				NetL(a+12)!=0);
241 
242 			pktsz -= osz;
243 			a += osz;
244 			break;
245 
246 		case redir:
247 			if (pktsz < osz) {
248 				p = seprint(p, e, "\n	  option=%s: bad size=%d",
249 					opt, osz);
250 				m->pr = &dump;
251 				return p;
252 			}
253 
254 			p = seprint(p, e, "\n	  option=%s len %d", opt, osz);
255 			a += osz;
256 			m->ps = a;
257 			return p;
258 
259 		case mtu:
260 			if (pktsz < osz || osz != 8) {
261 				p = seprint(p, e, "\n	  option=%s: bad size=%d",
262 					opt, osz);
263 				m->pr = &dump;
264 				return p;
265 			}
266 
267 			p = seprint(p, e, "\n	  option=%s unused=%1.1d mtu=%d",
268 				opt, NetL(a+2) != 0, NetL(a+4));
269 			pktsz -= osz;
270 			a += osz;
271 			break;
272 		}
273 	}
274 
275 	m->ps = a;
276 	return p;
277 }
278 
279 static int
p_seprint(Msg * m)280 p_seprint(Msg *m)
281 {
282 	int i;
283 //	ushort cksum2, cksum;
284 	char *tn;
285 	char *p = m->p;
286 	char *e = m->e;
287 	uchar *a;
288 	Hdr *h;
289 
290 	h = (Hdr*)m->ps;
291 	m->ps += ICMP6LEN;
292 	m->pr = &dump;
293 	a = m->ps;
294 
295 	if(m->pe - m->ps < ICMP6LEN)
296 		return -1;
297 
298 	tn = icmpmsg6[h->type];
299 	if(tn == nil)
300 		p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
301 			h->code, (ushort)NetS(h->cksum));
302 	else
303 		p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
304 			h->code, (ushort)NetS(h->cksum));
305 
306 /*
307 	if(Cflag){
308 		cksum = NetS(h->cksum);
309 		h->cksum[0] = 0;
310 		h->cksum[1] = 0;
311 		cksum2 = ~ptclbsum((uchar*)h, m->pe - m->ps + ICMP6LEN) & 0xffff;
312 		if(cksum != cksum2)
313 			p = seprint(p,e, " !ck=%4.4ux", cksum2);
314 	}
315  */
316 
317 	switch(h->type){
318 
319 	case UnreachableV6:
320 		m->ps += 4;
321 		m->pr = &ip6;
322 		if (h->code >= nelem(unreachcode))
323 			i = nelem(unreachcode)-1;
324 		else
325 			i = h->code;
326 		p = seprint(p, e, " code=%s unused=%1.1d ", unreachcode[i],
327 			NetL(a) != 0);
328 		break;
329 
330 	case PacketTooBigV6:
331 		m->ps += 4;
332 		m->pr = &ip6;
333 		p = seprint(p, e, " mtu=%4.4d ", NetL(a));
334 		break;
335 
336 	case TimeExceedV6:
337 		m->ps += 4;
338 		m->pr = &ip6;
339 		if (h->code >= nelem(timexcode))
340 			i = nelem(timexcode)-1;
341 		else
342 			i = h->code;
343 		p = seprint(p, e, " code=%s unused=%1.1d ", timexcode[i],
344 			NetL(a) != 0);
345 		break;
346 
347 	case ParamProblemV6:
348 		m->ps += 4;
349 		m->pr = &ip6;
350 		if (h->code > nelem(parpcode))
351 			i = nelem(parpcode)-1;
352 		else
353 			i = h->code;
354 		p = seprint(p, e, " code=%s ptr=%2.2ux", parpcode[i], h->data[0]);
355 		break;
356 
357 	case EchoReplyV6:
358 	case EchoRequestV6:
359 		m->ps += 4;
360 		p = seprint(p, e, " id=%ux seq=%ux",
361 			NetS(h->data), NetS(h->data+2));
362 		break;
363 
364 	case RouterSolicit:
365 		m->ps += 4;
366 		m->pr = nil;
367 		m->p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);
368 		p = opt_seprint(m);
369 		break;
370 
371 	case RouterAdvert:
372 		m->ps += 12;
373 		m->pr = nil;
374 		m->p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d "
375 			"unused=%1.1d routerlt=%8.8d reachtime=%d rxmtimer=%d",
376 			(int) *a,
377 			(*(a+1) & (1 << 7)) != 0,
378 			(*(a+1) & (1 << 6)) != 0,
379 			(*(a+1) & 63) != 0,
380 			NetS(a+2),
381 			NetL(a+4),
382 			NetL(a+8));
383 		p = opt_seprint(m);
384 		break;
385 
386 	case NbrSolicit:
387 		m->ps += 20;
388 		m->pr = nil;
389 		m->p = seprint(p, e, " unused=%1.1d targ %I", NetL(a) != 0, a+4);
390 		p = opt_seprint(m);
391 		break;
392 
393 	case NbrAdvert:
394 		m->ps += 20;
395 		m->pr = nil;
396 		m->p = seprint(p, e, " rflag=%1.1d sflag=%1.1d oflag=%1.1d targ=%I",
397 			(*a & (1 << 7)) != 0,
398 			(*a & (1 << 6)) != 0,
399 			(*a & (1 << 5)) != 0,
400 			a+4);
401 		p = opt_seprint(m);
402 		break;
403 
404 	case RedirectV6:
405 		m->ps += 36;
406 		m->pr = &ip6;
407 		m->p = seprint(p, e, " unused=%1.1d targ=%I dest=%I",
408 			NetL(a) != 0, a+4, a+20);
409 		p = opt_seprint(m);
410 		break;
411 
412 	case Timestamp:
413 	case TimestampReply:
414 		m->ps += 12;
415 		p = seprint(p, e, " orig=%ud rcv=%ux xmt=%ux",
416 			NetL(h->data), NetL(h->data+4), NetL(h->data+8));
417 		m->pr = nil;
418 		break;
419 
420 	case InfoRequest:
421 	case InfoReply:
422 		break;
423 
424 	}
425 	m->p = p;
426 	return 0;
427 }
428 
429 Proto icmp6 =
430 {
431 	"icmp6",
432 	p_compile,
433 	p_filter,
434 	p_seprint,
435 	p_mux,
436 	"%lud",
437 	p_fields,
438 	defaultframer,
439 };
440