| #include <vfs.h> | 
 | #include <kfs.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <kref.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <error.h> | 
 | #include <cpio.h> | 
 | #include <pmap.h> | 
 | #include <smp.h> | 
 |  | 
 | char *v6hdrtypes[Maxhdrtype] = { | 
 | 	[HBH] "HopbyHop", | 
 | 	[ICMP] "ICMP", | 
 | 	[IGMP] "IGMP", | 
 | 	[GGP] "GGP", | 
 | 	[IPINIP] "IP", | 
 | 	[ST] "ST", | 
 | 	[TCP] "TCP", | 
 | 	[UDP] "UDP", | 
 | 	[ISO_TP4] "ISO_TP4", | 
 | 	[RH] "Routinghdr", | 
 | 	[FH] "Fraghdr", | 
 | 	[IDRP] "IDRP", | 
 | 	[RSVP] "RSVP", | 
 | 	[AH] "Authhdr", | 
 | 	[ESP] "ESP", | 
 | 	[ICMPv6] "ICMPv6", | 
 | 	[NNH] "Nonexthdr", | 
 | 	[ISO_IP] "ISO_IP", | 
 | 	[IGRP] "IGRP", | 
 | 	[OSPF] "OSPF", | 
 | }; | 
 |  | 
 | /* | 
 |  *  well known IPv6 addresses | 
 |  */ | 
 | uint8_t v6Unspecified[IPaddrlen] = { | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6loopback[IPaddrlen] = { | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x01 | 
 | }; | 
 |  | 
 | uint8_t v6linklocal[IPaddrlen] = { | 
 | 	0xfe, 0x80, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6linklocalmask[IPaddrlen] = { | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | int v6llpreflen = 8;			/* link-local prefix length in bytes */ | 
 |  | 
 | uint8_t v6multicast[IPaddrlen] = { | 
 | 	0xff, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6multicastmask[IPaddrlen] = { | 
 | 	0xff, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | int v6mcpreflen = 1;			/* multicast prefix length */ | 
 |  | 
 | uint8_t v6allnodesN[IPaddrlen] = { | 
 | 	0xff, 0x01, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x01 | 
 | }; | 
 |  | 
 | uint8_t v6allroutersN[IPaddrlen] = { | 
 | 	0xff, 0x01, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x02 | 
 | }; | 
 |  | 
 | uint8_t v6allnodesNmask[IPaddrlen] = { | 
 | 	0xff, 0xff, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | int v6aNpreflen = 2;			/* all nodes (N) prefix */ | 
 |  | 
 | uint8_t v6allnodesL[IPaddrlen] = { | 
 | 	0xff, 0x02, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x01 | 
 | }; | 
 |  | 
 | uint8_t v6allroutersL[IPaddrlen] = { | 
 | 	0xff, 0x02, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x02 | 
 | }; | 
 |  | 
 | uint8_t v6allnodesLmask[IPaddrlen] = { | 
 | 	0xff, 0xff, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0 | 
 | }; | 
 |  | 
 | int v6aLpreflen = 2;			/* all nodes (L) prefix */ | 
 |  | 
 | uint8_t v6solicitednode[IPaddrlen] = { | 
 | 	0xff, 0x02, 0, 0, | 
 | 	0, 0, 0, 0, | 
 | 	0, 0, 0, 0x01, | 
 | 	0xff, 0, 0, 0 | 
 | }; | 
 |  | 
 | uint8_t v6solicitednodemask[IPaddrlen] = { | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0x0, 0x0, 0x0 | 
 | }; | 
 |  | 
 | int v6snpreflen = 13; | 
 |  | 
 | uint16_t ptclcsum(struct block *bp, int offset, int len) | 
 | { | 
 | 	uint8_t *addr; | 
 | 	uint32_t losum, hisum; | 
 | 	uint16_t csum; | 
 | 	int odd, blocklen, x; | 
 |  | 
 | 	/* Correct to front of data area */ | 
 | 	while (bp != NULL && offset && offset >= BLEN(bp)) { | 
 | 		offset -= BLEN(bp); | 
 | 		bp = bp->next; | 
 | 	} | 
 | 	if (bp == NULL) | 
 | 		return 0; | 
 |  | 
 | 	addr = bp->rp + offset; | 
 | 	blocklen = BLEN(bp) - offset; | 
 |  | 
 | 	if (bp->next == NULL) { | 
 | 		if (blocklen < len) | 
 | 			len = blocklen; | 
 | 		return ~ptclbsum(addr, len) & 0xffff; | 
 | 	} | 
 |  | 
 | 	losum = 0; | 
 | 	hisum = 0; | 
 |  | 
 | 	odd = 0; | 
 | 	while (len) { | 
 | 		x = blocklen; | 
 | 		if (len < x) | 
 | 			x = len; | 
 |  | 
 | 		csum = ptclbsum(addr, x); | 
 | 		if (odd) | 
 | 			hisum += csum; | 
 | 		else | 
 | 			losum += csum; | 
 | 		odd = (odd + x) & 1; | 
 | 		len -= x; | 
 |  | 
 | 		bp = bp->next; | 
 | 		if (bp == NULL) | 
 | 			break; | 
 | 		blocklen = BLEN(bp); | 
 | 		addr = bp->rp; | 
 | 	} | 
 |  | 
 | 	losum += hisum >> 8; | 
 | 	losum += (hisum & 0xff) << 8; | 
 | 	while ((csum = losum >> 16) != 0) | 
 | 		losum = csum + (losum & 0xffff); | 
 |  | 
 | 	return ~losum & 0xffff; | 
 | } | 
 |  | 
 | enum { | 
 | 	Isprefix = 16, | 
 | }; | 
 |  | 
 | void ipv62smcast(uint8_t * smcast, uint8_t * a) | 
 | { | 
 | 	assert(IPaddrlen == 16); | 
 | 	memmove(smcast, v6solicitednode, IPaddrlen); | 
 | 	smcast[13] = a[13]; | 
 | 	smcast[14] = a[14]; | 
 | 	smcast[15] = a[15]; | 
 | } | 
 |  | 
 | /* | 
 |  *  parse a hex mac address | 
 |  */ | 
 | int parsemac(uint8_t * to, char *from, int len) | 
 | { | 
 | 	char nip[4]; | 
 | 	char *p; | 
 | 	int i; | 
 |  | 
 | 	p = from; | 
 | 	memset(to, 0, len); | 
 | 	for (i = 0; i < len; i++) { | 
 | 		if (p[0] == '\0' || p[1] == '\0') | 
 | 			break; | 
 |  | 
 | 		nip[0] = p[0]; | 
 | 		nip[1] = p[1]; | 
 | 		nip[2] = '\0'; | 
 | 		p += 2; | 
 |  | 
 | 		to[i] = strtoul(nip, 0, 16); | 
 | 		if (*p == ':') | 
 | 			p++; | 
 | 	} | 
 | 	return i; | 
 | } | 
 |  | 
 | /* | 
 |  *  hashing tcp, udp, ... connections | 
 |  *  gcc weirdness: it gave a bogus result until I split the | 
 |  *  %= out. | 
 |  */ | 
 | uint32_t iphash(uint8_t * sa, uint16_t sp, uint8_t * da, uint16_t dp) | 
 | { | 
 | 	uint32_t ret; | 
 | 	ret = (sa[IPaddrlen - 1] << 24) ^ (sp << 16) ^ (da[IPaddrlen - 1] << 8) ^ | 
 | 			dp; | 
 | 	ret %= Nhash; | 
 | 	return ret; | 
 | } | 
 |  | 
 | void iphtadd(struct Ipht *ht, struct conv *c) | 
 | { | 
 | 	uint32_t hv; | 
 | 	struct iphash *h; | 
 |  | 
 | 	hv = iphash(c->raddr, c->rport, c->laddr, c->lport); | 
 | 	h = kzmalloc(sizeof(*h), 0); | 
 | 	if (ipcmp(c->raddr, IPnoaddr) != 0) | 
 | 		h->match = IPmatchexact; | 
 | 	else { | 
 | 		if (ipcmp(c->laddr, IPnoaddr) != 0) { | 
 | 			if (c->lport == 0) | 
 | 				h->match = IPmatchaddr; | 
 | 			else | 
 | 				h->match = IPmatchpa; | 
 | 		} else { | 
 | 			if (c->lport == 0) | 
 | 				h->match = IPmatchany; | 
 | 			else | 
 | 				h->match = IPmatchport; | 
 | 		} | 
 | 	} | 
 | 	h->c = c; | 
 |  | 
 | 	spin_lock(&ht->lock); | 
 | 	h->next = ht->tab[hv]; | 
 | 	ht->tab[hv] = h; | 
 | 	spin_unlock(&ht->lock); | 
 | } | 
 |  | 
 | void iphtrem(struct Ipht *ht, struct conv *c) | 
 | { | 
 | 	uint32_t hv; | 
 | 	struct iphash **l, *h; | 
 |  | 
 | 	hv = iphash(c->raddr, c->rport, c->laddr, c->lport); | 
 | 	spin_lock(&ht->lock); | 
 | 	for (l = &ht->tab[hv]; (*l) != NULL; l = &(*l)->next) | 
 | 		if ((*l)->c == c) { | 
 | 			h = *l; | 
 | 			(*l) = h->next; | 
 | 			kfree(h); | 
 | 			break; | 
 | 		} | 
 | 	spin_unlock(&ht->lock); | 
 | } | 
 |  | 
 | /* look for a matching conversation with the following precedence | 
 |  *	connected && raddr,rport,laddr,lport | 
 |  *	announced && laddr,lport | 
 |  *	announced && *,lport | 
 |  *	announced && laddr,* | 
 |  *	announced && *,* | 
 |  */ | 
 | struct conv *iphtlook(struct Ipht *ht, uint8_t * sa, uint16_t sp, uint8_t * da, | 
 | 					  uint16_t dp) | 
 | { | 
 | 	uint32_t hv; | 
 | 	struct iphash *h; | 
 | 	struct conv *c; | 
 | 	/* exact 4 pair match (connection) */ | 
 | 	hv = iphash(sa, sp, da, dp); | 
 | 	spin_lock(&ht->lock); | 
 | 	for (h = ht->tab[hv]; h != NULL; h = h->next) { | 
 | 		if (h->match != IPmatchexact) | 
 | 			continue; | 
 | 		c = h->c; | 
 | 		if (sp == c->rport && dp == c->lport | 
 | 			&& ipcmp(sa, c->raddr) == 0 && ipcmp(da, c->laddr) == 0) { | 
 | 			spin_unlock(&ht->lock); | 
 | 			return c; | 
 | 		} | 
 | 	} | 
 | 	/* match local address and port */ | 
 | 	hv = iphash(IPnoaddr, 0, da, dp); | 
 | 	for (h = ht->tab[hv]; h != NULL; h = h->next) { | 
 | 		if (h->match != IPmatchpa) | 
 | 			continue; | 
 | 		c = h->c; | 
 | 		if (dp == c->lport && ipcmp(da, c->laddr) == 0) { | 
 | 			spin_unlock(&ht->lock); | 
 | 			return c; | 
 | 		} | 
 | 	} | 
 | 	/* match just port */ | 
 | 	hv = iphash(IPnoaddr, 0, IPnoaddr, dp); | 
 | 	for (h = ht->tab[hv]; h != NULL; h = h->next) { | 
 | 		if (h->match != IPmatchport) | 
 | 			continue; | 
 | 		c = h->c; | 
 | 		if (dp == c->lport) { | 
 | 			spin_unlock(&ht->lock); | 
 | 			return c; | 
 | 		} | 
 | 	} | 
 | 	/* match local address */ | 
 | 	hv = iphash(IPnoaddr, 0, da, 0); | 
 | 	for (h = ht->tab[hv]; h != NULL; h = h->next) { | 
 | 		if (h->match != IPmatchaddr) | 
 | 			continue; | 
 | 		c = h->c; | 
 | 		if (ipcmp(da, c->laddr) == 0) { | 
 | 			spin_unlock(&ht->lock); | 
 | 			return c; | 
 | 		} | 
 | 	} | 
 | 	/* look for something that matches anything */ | 
 | 	hv = iphash(IPnoaddr, 0, IPnoaddr, 0); | 
 | 	for (h = ht->tab[hv]; h != NULL; h = h->next) { | 
 | 		if (h->match != IPmatchany) | 
 | 			continue; | 
 | 		c = h->c; | 
 | 		spin_unlock(&ht->lock); | 
 | 		return c; | 
 | 	} | 
 | 	spin_unlock(&ht->lock); | 
 | 	return NULL; | 
 | } |