xref: /inferno-os/os/ip/ipaux.c (revision 8eb013de88b69d0256e7d37913ab46510908b510)
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 #include	"ip.h"
8 #include  "ipv6.h"
9 
10 /*
11  *  well known IP addresses
12  */
13 uchar IPv4bcast[IPaddrlen] = {
14 	0, 0, 0, 0,
15 	0, 0, 0, 0,
16 	0, 0, 0xff, 0xff,
17 	0xff, 0xff, 0xff, 0xff
18 };
19 uchar IPv4allsys[IPaddrlen] = {
20 	0, 0, 0, 0,
21 	0, 0, 0, 0,
22 	0, 0, 0xff, 0xff,
23 	0xe0, 0, 0, 0x01
24 };
25 uchar IPv4allrouter[IPaddrlen] = {
26 	0, 0, 0, 0,
27 	0, 0, 0, 0,
28 	0, 0, 0xff, 0xff,
29 	0xe0, 0, 0, 0x02
30 };
31 uchar IPallbits[IPaddrlen] = {
32 	0xff, 0xff, 0xff, 0xff,
33 	0xff, 0xff, 0xff, 0xff,
34 	0xff, 0xff, 0xff, 0xff,
35 	0xff, 0xff, 0xff, 0xff
36 };
37 
38 uchar IPnoaddr[IPaddrlen];
39 
40 /*
41  *  prefix of all v4 addresses
42  */
43 uchar v4prefix[IPaddrlen] = {
44 	0, 0, 0, 0,
45 	0, 0, 0, 0,
46 	0, 0, 0xff, 0xff,
47 	0, 0, 0, 0
48 };
49 
50 
51 char *v6hdrtypes[Maxhdrtype] =
52 {
53 	[HBH]		"HopbyHop",
54 	[ICMP]		"ICMP",
55 	[IGMP]		"IGMP",
56 	[GGP]		"GGP",
57 	[IPINIP]		"IP",
58 	[ST]		"ST",
59 	[TCP]		"TCP",
60 	[UDP]		"UDP",
61 	[ISO_TP4]	"ISO_TP4",
62 	[RH]		"Routinghdr",
63 	[FH]		"Fraghdr",
64 	[IDRP]		"IDRP",
65 	[RSVP]		"RSVP",
66 	[AH]		"Authhdr",
67 	[ESP]		"ESP",
68 	[ICMPv6]	"ICMPv6",
69 	[NNH]		"Nonexthdr",
70 	[ISO_IP]	"ISO_IP",
71 	[IGRP]		"IGRP",
72 	[OSPF]		"OSPF",
73 };
74 
75 /*
76  *  well known IPv6 addresses
77  */
78 uchar v6Unspecified[IPaddrlen] = {
79 	0, 0, 0, 0,
80 	0, 0, 0, 0,
81 	0, 0, 0, 0,
82 	0, 0, 0, 0
83 };
84 uchar v6loopback[IPaddrlen] = {
85 	0, 0, 0, 0,
86 	0, 0, 0, 0,
87 	0, 0, 0, 0,
88 	0, 0, 0, 0x01
89 };
90 uchar v6linklocal[IPaddrlen] = {
91 	0xfe, 0x80, 0, 0,
92 	0, 0, 0, 0,
93 	0, 0, 0, 0,
94 	0, 0, 0, 0
95 };
96 uchar v6linklocalmask[IPaddrlen] = {
97 	0xff, 0xff, 0xff, 0xff,
98 	0xff, 0xff, 0xff, 0xff,
99 	0, 0, 0, 0,
100 	0, 0, 0, 0
101 };
102 int v6llpreflen = 8;	// link-local prefix length
103 uchar v6sitelocal[IPaddrlen] = {
104 	0xfe, 0xc0, 0, 0,
105 	0, 0, 0, 0,
106 	0, 0, 0, 0,
107 	0, 0, 0, 0
108 };
109 uchar v6sitelocalmask[IPaddrlen] = {
110 	0xff, 0xff, 0xff, 0xff,
111 	0xff, 0xff, 0xff, 0xff,
112 	0, 0, 0, 0,
113 	0, 0, 0, 0
114 };
115 int v6slpreflen = 6;	// site-local prefix length
116 uchar v6glunicast[IPaddrlen] = {
117 	0x08, 0, 0, 0,
118 	0, 0, 0, 0,
119 	0, 0, 0, 0,
120 	0, 0, 0, 0
121 };
122 uchar v6multicast[IPaddrlen] = {
123 	0xff, 0, 0, 0,
124 	0, 0, 0, 0,
125 	0, 0, 0, 0,
126 	0, 0, 0, 0
127 };
128 uchar v6multicastmask[IPaddrlen] = {
129 	0xff, 0, 0, 0,
130 	0, 0, 0, 0,
131 	0, 0, 0, 0,
132 	0, 0, 0, 0
133 };
134 int v6mcpreflen = 1;	// multicast prefix length
135 uchar v6allnodesN[IPaddrlen] = {
136 	0xff, 0x01, 0, 0,
137 	0, 0, 0, 0,
138 	0, 0, 0, 0,
139 	0, 0, 0, 0x01
140 };
141 uchar v6allnodesNmask[IPaddrlen] = {
142 	0xff, 0xff, 0, 0,
143 	0, 0, 0, 0,
144 	0, 0, 0, 0,
145 	0, 0, 0, 0
146 };
147 int v6aNpreflen = 2;	// all nodes (N) prefix
148 uchar v6allnodesL[IPaddrlen] = {
149 	0xff, 0x02, 0, 0,
150 	0, 0, 0, 0,
151 	0, 0, 0, 0,
152 	0, 0, 0, 0x01
153 };
154 uchar v6allnodesLmask[IPaddrlen] = {
155 	0xff, 0xff, 0, 0,
156 	0, 0, 0, 0,
157 	0, 0, 0, 0,
158 	0, 0, 0, 0
159 };
160 int v6aLpreflen = 2;	// all nodes (L) prefix
161 uchar v6allroutersN[IPaddrlen] = {
162 	0xff, 0x01, 0, 0,
163 	0, 0, 0, 0,
164 	0, 0, 0, 0,
165 	0, 0, 0, 0x02
166 };
167 uchar v6allroutersL[IPaddrlen] = {
168 	0xff, 0x02, 0, 0,
169 	0, 0, 0, 0,
170 	0, 0, 0, 0,
171 	0, 0, 0, 0x02
172 };
173 uchar v6allroutersS[IPaddrlen] = {
174 	0xff, 0x05, 0, 0,
175 	0, 0, 0, 0,
176 	0, 0, 0, 0,
177 	0, 0, 0, 0x02
178 };
179 uchar v6solicitednode[IPaddrlen] = {
180 	0xff, 0x02, 0, 0,
181 	0, 0, 0, 0,
182 	0, 0, 0, 0x01,
183 	0xff, 0, 0, 0
184 };
185 uchar v6solicitednodemask[IPaddrlen] = {
186 	0xff, 0xff, 0xff, 0xff,
187 	0xff, 0xff, 0xff, 0xff,
188 	0xff, 0xff, 0xff, 0xff,
189 	0xff, 0x0, 0x0, 0x0
190 };
191 int v6snpreflen = 13;
192 
193 
194 
195 
196 ushort
ptclcsum(Block * bp,int offset,int len)197 ptclcsum(Block *bp, int offset, int len)
198 {
199 	uchar *addr;
200 	ulong losum, hisum;
201 	ushort csum;
202 	int odd, blocklen, x;
203 
204 	/* Correct to front of data area */
205 	while(bp != nil && offset && offset >= BLEN(bp)) {
206 		offset -= BLEN(bp);
207 		bp = bp->next;
208 	}
209 	if(bp == nil)
210 		return 0;
211 
212 	addr = bp->rp + offset;
213 	blocklen = BLEN(bp) - offset;
214 
215 	if(bp->next == nil) {
216 		if(blocklen < len)
217 			len = blocklen;
218 		return ~ptclbsum(addr, len) & 0xffff;
219 	}
220 
221 	losum = 0;
222 	hisum = 0;
223 
224 	odd = 0;
225 	while(len) {
226 		x = blocklen;
227 		if(len < x)
228 			x = len;
229 
230 		csum = ptclbsum(addr, x);
231 		if(odd)
232 			hisum += csum;
233 		else
234 			losum += csum;
235 		odd = (odd+x) & 1;
236 		len -= x;
237 
238 		bp = bp->next;
239 		if(bp == nil)
240 			break;
241 		blocklen = BLEN(bp);
242 		addr = bp->rp;
243 	}
244 
245 	losum += hisum>>8;
246 	losum += (hisum&0xff)<<8;
247 	while((csum = losum>>16) != 0)
248 		losum = csum + (losum & 0xffff);
249 
250 	return ~losum & 0xffff;
251 }
252 
253 enum
254 {
255 	Isprefix= 16,
256 };
257 
258 static uchar prefixvals[256] =
259 {
260 [0x00] 0 | Isprefix,
261 [0x80] 1 | Isprefix,
262 [0xC0] 2 | Isprefix,
263 [0xE0] 3 | Isprefix,
264 [0xF0] 4 | Isprefix,
265 [0xF8] 5 | Isprefix,
266 [0xFC] 6 | Isprefix,
267 [0xFE] 7 | Isprefix,
268 [0xFF] 8 | Isprefix,
269 };
270 
271 int
eipfmt(Fmt * f)272 eipfmt(Fmt *f)
273 {
274 	char buf[5*8];
275 	static char *efmt = "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux";
276 	static char *ifmt = "%d.%d.%d.%d";
277 	uchar *p, ip[16];
278 	ulong *lp;
279 	ushort s;
280 	int i, j, n, eln, eli;
281 
282 	switch(f->r) {
283 	case 'E':		/* Ethernet address */
284 		p = va_arg(f->args, uchar*);
285 		return fmtprint(f, efmt, p[0], p[1], p[2], p[3], p[4], p[5]);
286 
287 	case 'I':		/* Ip address */
288 		p = va_arg(f->args, uchar*);
289 common:
290 		if(memcmp(p, v4prefix, 12) == 0)
291 			return fmtprint(f, ifmt, p[12], p[13], p[14], p[15]);
292 
293 		/* find longest elision */
294 		eln = eli = -1;
295 		for(i = 0; i < 16; i += 2){
296 			for(j = i; j < 16; j += 2)
297 				if(p[j] != 0 || p[j+1] != 0)
298 					break;
299 			if(j > i && j - i > eln){
300 				eli = i;
301 				eln = j - i;
302 			}
303 		}
304 
305 		/* print with possible elision */
306 		n = 0;
307 		for(i = 0; i < 16; i += 2){
308 			if(i == eli){
309 				n += sprint(buf+n, "::");
310 				i += eln;
311 				if(i >= 16)
312 					break;
313 			} else if(i != 0)
314 				n += sprint(buf+n, ":");
315 			s = (p[i]<<8) + p[i+1];
316 			n += sprint(buf+n, "%ux", s);
317 		}
318 		return fmtstrcpy(f, buf);
319 
320 	case 'i':		/* v6 address as 4 longs */
321 		lp = va_arg(f->args, ulong*);
322 		for(i = 0; i < 4; i++)
323 			hnputl(ip+4*i, *lp++);
324 		p = ip;
325 		goto common;
326 
327 	case 'V':		/* v4 ip address */
328 		p = va_arg(f->args, uchar*);
329 		return fmtprint(f, ifmt, p[0], p[1], p[2], p[3]);
330 
331 	case 'M':		/* ip mask */
332 		p = va_arg(f->args, uchar*);
333 
334 		/* look for a prefix mask */
335 		for(i = 0; i < 16; i++)
336 			if(p[i] != 0xff)
337 				break;
338 		if(i < 16){
339 			if((prefixvals[p[i]] & Isprefix) == 0)
340 				goto common;
341 			for(j = i+1; j < 16; j++)
342 				if(p[j] != 0)
343 					goto common;
344 			n = 8*i + (prefixvals[p[i]] & ~Isprefix);
345 		} else
346 			n = 8*16;
347 
348 		/* got one, use /xx format */
349 		return fmtprint(f, "/%d", n);
350 	}
351 	return fmtstrcpy(f, "(eipfmt)");
352 }
353 
354 #define CLASS(p) ((*(uchar*)(p))>>6)
355 
356 extern char*
v4parseip(uchar * to,char * from)357 v4parseip(uchar *to, char *from)
358 {
359 	int i;
360 	char *p;
361 
362 	p = from;
363 	for(i = 0; i < 4 && *p; i++){
364 		to[i] = strtoul(p, &p, 0);
365 		if(*p == '.')
366 			p++;
367 	}
368 	switch(CLASS(to)){
369 	case 0:	/* class A - 1 uchar net */
370 	case 1:
371 		if(i == 3){
372 			to[3] = to[2];
373 			to[2] = to[1];
374 			to[1] = 0;
375 		} else if(i == 2){
376 			to[3] = to[1];
377 			to[1] = 0;
378 		}
379 		break;
380 	case 2:	/* class B - 2 uchar net */
381 		if(i == 3){
382 			to[3] = to[2];
383 			to[2] = 0;
384 		}
385 		break;
386 	}
387 	return p;
388 }
389 
390 int
isv4(uchar * ip)391 isv4(uchar *ip)
392 {
393 	return memcmp(ip, v4prefix, IPv4off) == 0;
394 }
395 
396 
397 /*
398  *  the following routines are unrolled with no memset's to speed
399  *  up the usual case
400  */
401 void
v4tov6(uchar * v6,uchar * v4)402 v4tov6(uchar *v6, uchar *v4)
403 {
404 	v6[0] = 0;
405 	v6[1] = 0;
406 	v6[2] = 0;
407 	v6[3] = 0;
408 	v6[4] = 0;
409 	v6[5] = 0;
410 	v6[6] = 0;
411 	v6[7] = 0;
412 	v6[8] = 0;
413 	v6[9] = 0;
414 	v6[10] = 0xff;
415 	v6[11] = 0xff;
416 	v6[12] = v4[0];
417 	v6[13] = v4[1];
418 	v6[14] = v4[2];
419 	v6[15] = v4[3];
420 }
421 
422 int
v6tov4(uchar * v4,uchar * v6)423 v6tov4(uchar *v4, uchar *v6)
424 {
425 	if(v6[0] == 0
426 	&& v6[1] == 0
427 	&& v6[2] == 0
428 	&& v6[3] == 0
429 	&& v6[4] == 0
430 	&& v6[5] == 0
431 	&& v6[6] == 0
432 	&& v6[7] == 0
433 	&& v6[8] == 0
434 	&& v6[9] == 0
435 	&& v6[10] == 0xff
436 	&& v6[11] == 0xff)
437 	{
438 		v4[0] = v6[12];
439 		v4[1] = v6[13];
440 		v4[2] = v6[14];
441 		v4[3] = v6[15];
442 		return 0;
443 	} else {
444 		memset(v4, 0, 4);
445 		return -1;
446 	}
447 }
448 
449 ulong
parseip(uchar * to,char * from)450 parseip(uchar *to, char *from)
451 {
452 	int i, elipsis = 0, v4 = 1;
453 	ulong x;
454 	char *p, *op;
455 
456 	memset(to, 0, IPaddrlen);
457 	p = from;
458 	for(i = 0; i < 16 && *p; i+=2){
459 		op = p;
460 		x = strtoul(p, &p, 16);
461 		if(*p == '.' || (*p == 0 && i == 0)){
462 			p = v4parseip(to+i, op);
463 			i += 4;
464 			break;
465 		} else {
466 			to[i] = x>>8;
467 			to[i+1] = x;
468 		}
469 		if(*p == ':'){
470 			v4 = 0;
471 			if(*++p == ':'){
472 				elipsis = i+2;
473 				p++;
474 			}
475 		}
476 	}
477 	if(i < 16){
478 		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
479 		memset(&to[elipsis], 0, 16-i);
480 	}
481 	if(v4){
482 		to[10] = to[11] = 0xff;
483 		return nhgetl(to+12);
484 	} else
485 		return 6;
486 }
487 
488 /*
489  *  hack to allow ip v4 masks to be entered in the old
490  *  style
491  */
492 ulong
parseipmask(uchar * to,char * from)493 parseipmask(uchar *to, char *from)
494 {
495 	ulong x;
496 	int i;
497 	uchar *p;
498 
499 	if(*from == '/'){
500 		/* as a number of prefix bits */
501 		i = atoi(from+1);
502 		if(i < 0)
503 			i = 0;
504 		if(i > 128)
505 			i = 128;
506 		memset(to, 0, IPaddrlen);
507 		for(p = to; i >= 8; i -= 8)
508 			*p++ = 0xff;
509 		if(i > 0)
510 			*p = ~((1<<(8-i))-1);
511 		x = nhgetl(to+IPv4off);
512 	} else {
513 		/* as a straight bit mask */
514 		x = parseip(to, from);
515 		if(memcmp(to, v4prefix, IPv4off) == 0)
516 			memset(to, 0xff, IPv4off);
517 	}
518 	return x;
519 }
520 
521 void
maskip(uchar * from,uchar * mask,uchar * to)522 maskip(uchar *from, uchar *mask, uchar *to)
523 {
524 	int i;
525 
526 	for(i = 0; i < IPaddrlen; i++)
527 		to[i] = from[i] & mask[i];
528 }
529 
530 uchar classmask[4][16] = {
531 	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
532 	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0x00,0x00,0x00,
533 	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0x00,0x00,
534 	0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0xff,  0xff,0xff,0xff,0x00,
535 };
536 
537 uchar*
defmask(uchar * ip)538 defmask(uchar *ip)
539 {
540 	if(isv4(ip))
541 		return classmask[ip[IPv4off]>>6];
542 	else {
543 		if(ipcmp(ip, v6loopback) == 0)
544 			return IPallbits;
545 		else if(memcmp(ip, v6linklocal, v6llpreflen) == 0)
546 			return v6linklocalmask;
547 		else if(memcmp(ip, v6sitelocal, v6slpreflen) == 0)
548 			return v6sitelocalmask;
549 		else if(memcmp(ip, v6solicitednode, v6snpreflen) == 0)
550 			return v6solicitednodemask;
551 		else if(memcmp(ip, v6multicast, v6mcpreflen) == 0)
552 			return v6multicastmask;
553 		return IPallbits;
554 	}
555 }
556 
557 void
ipv62smcast(uchar * smcast,uchar * a)558 ipv62smcast(uchar *smcast, uchar *a)
559 {
560 	assert(IPaddrlen == 16);
561 	memmove(smcast, v6solicitednode, IPaddrlen);
562 	smcast[13] = a[13];
563 	smcast[14] = a[14];
564 	smcast[15] = a[15];
565 }
566 
567 
568 /*
569  *  parse a hex mac address
570  */
571 int
parsemac(uchar * to,char * from,int len)572 parsemac(uchar *to, char *from, int len)
573 {
574 	char nip[4];
575 	char *p;
576 	int i;
577 
578 	p = from;
579 	memset(to, 0, len);
580 	for(i = 0; i < len; i++){
581 		if(p[0] == '\0' || p[1] == '\0')
582 			break;
583 
584 		nip[0] = p[0];
585 		nip[1] = p[1];
586 		nip[2] = '\0';
587 		p += 2;
588 
589 		to[i] = strtoul(nip, 0, 16);
590 		if(*p == ':')
591 			p++;
592 	}
593 	return i;
594 }
595 
596 /*
597  *  hashing tcp, udp, ... connections
598  */
599 ulong
iphash(uchar * sa,ushort sp,uchar * da,ushort dp)600 iphash(uchar *sa, ushort sp, uchar *da, ushort dp)
601 {
602 	return ((sa[IPaddrlen-1]<<24) ^ (sp << 16) ^ (da[IPaddrlen-1]<<8) ^ dp ) % Nhash;
603 }
604 
605 void
iphtadd(Ipht * ht,Conv * c)606 iphtadd(Ipht *ht, Conv *c)
607 {
608 	ulong hv;
609 	Iphash *h;
610 
611 	hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
612 	h = smalloc(sizeof(*h));
613 	if(ipcmp(c->raddr, IPnoaddr) != 0)
614 		h->match = IPmatchexact;
615 	else {
616 		if(ipcmp(c->laddr, IPnoaddr) != 0){
617 			if(c->lport == 0)
618 				h->match = IPmatchaddr;
619 			else
620 				h->match = IPmatchpa;
621 		} else {
622 			if(c->lport == 0)
623 				h->match = IPmatchany;
624 			else
625 				h->match = IPmatchport;
626 		}
627 	}
628 	h->c = c;
629 
630 	lock(ht);
631 	h->next = ht->tab[hv];
632 	ht->tab[hv] = h;
633 	unlock(ht);
634 }
635 
636 void
iphtrem(Ipht * ht,Conv * c)637 iphtrem(Ipht *ht, Conv *c)
638 {
639 	ulong hv;
640 	Iphash **l, *h;
641 
642 	hv = iphash(c->raddr, c->rport, c->laddr, c->lport);
643 	lock(ht);
644 	for(l = &ht->tab[hv]; (*l) != nil; l = &(*l)->next)
645 		if((*l)->c == c){
646 			h = *l;
647 			(*l) = h->next;
648 			free(h);
649 			break;
650 		}
651 	unlock(ht);
652 }
653 
654 /* look for a matching conversation with the following precedence
655  *	connected && raddr,rport,laddr,lport
656  *	announced && laddr,lport
657  *	announced && *,lport
658  *	announced && laddr,*
659  *	announced && *,*
660  */
661 Conv*
iphtlook(Ipht * ht,uchar * sa,ushort sp,uchar * da,ushort dp)662 iphtlook(Ipht *ht, uchar *sa, ushort sp, uchar *da, ushort dp)
663 {
664 	ulong hv;
665 	Iphash *h;
666 	Conv *c;
667 
668 	/* exact 4 pair match (connection) */
669 	hv = iphash(sa, sp, da, dp);
670 	lock(ht);
671 	for(h = ht->tab[hv]; h != nil; h = h->next){
672 		if(h->match != IPmatchexact)
673 			continue;
674 		c = h->c;
675 		if(sp == c->rport && dp == c->lport
676 		&& ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0){
677 			unlock(ht);
678 			return c;
679 		}
680 	}
681 
682 	/* match local address and port */
683 	hv = iphash(IPnoaddr, 0, da, dp);
684 	for(h = ht->tab[hv]; h != nil; h = h->next){
685 		if(h->match != IPmatchpa)
686 			continue;
687 		c = h->c;
688 		if(dp == c->lport && ipcmp(da, c->laddr) == 0){
689 			unlock(ht);
690 			return c;
691 		}
692 	}
693 
694 	/* match just port */
695 	hv = iphash(IPnoaddr, 0, IPnoaddr, dp);
696 	for(h = ht->tab[hv]; h != nil; h = h->next){
697 		if(h->match != IPmatchport)
698 			continue;
699 		c = h->c;
700 		if(dp == c->lport){
701 			unlock(ht);
702 			return c;
703 		}
704 	}
705 
706 	/* match local address */
707 	hv = iphash(IPnoaddr, 0, da, 0);
708 	for(h = ht->tab[hv]; h != nil; h = h->next){
709 		if(h->match != IPmatchaddr)
710 			continue;
711 		c = h->c;
712 		if(ipcmp(da, c->laddr) == 0){
713 			unlock(ht);
714 			return c;
715 		}
716 	}
717 
718 	/* look for something that matches anything */
719 	hv = iphash(IPnoaddr, 0, IPnoaddr, 0);
720 	for(h = ht->tab[hv]; h != nil; h = h->next){
721 		if(h->match != IPmatchany)
722 			continue;
723 		c = h->c;
724 		unlock(ht);
725 		return c;
726 	}
727 	unlock(ht);
728 	return nil;
729 }
730