| /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved. | 
 |  * Portions Copyright © 1997-1999 Vita Nuova Limited | 
 |  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited | 
 |  *                                (www.vitanuova.com) | 
 |  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others | 
 |  * | 
 |  * Modified for the Akaros operating system: | 
 |  * Copyright (c) 2013-2014 The Regents of the University of California | 
 |  * Copyright (c) 2013-2015 Google Inc. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
 |  * copies of the Software, and to permit persons to whom the Software is | 
 |  * furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | 
 |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
 |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
 |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | 
 |  * SOFTWARE. */ | 
 |  | 
 | #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> | 
 | #include <net/ip.h> | 
 |  | 
 | typedef struct Etherhdr Etherhdr; | 
 | struct Etherhdr { | 
 | 	uint8_t d[6]; | 
 | 	uint8_t s[6]; | 
 | 	uint8_t t[2]; | 
 | }; | 
 |  | 
 | static uint8_t ipbroadcast[IPaddrlen] = { | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | 	0xff, 0xff, 0xff, 0xff, | 
 | }; | 
 |  | 
 | static uint8_t etherbroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | 
 |  | 
 | static void etherread4(void *a); | 
 | static void etherread6(void *a); | 
 | static void etherbind(struct Ipifc *ifc, int argc, char **argv); | 
 | static void etherunbind(struct Ipifc *ifc); | 
 | static void etherbwrite(struct Ipifc *ifc, struct block *bp, int version, | 
 | 			uint8_t *ip); | 
 | static void etheraddmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * ia); | 
 | static void etherremmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * ia); | 
 | static struct block *multicastarp(struct Fs *f, struct arpent *a, | 
 | 				  struct medium *m, uint8_t *mac); | 
 | static void sendarp(struct Ipifc *ifc, struct arpent *a); | 
 | static void sendgarp(struct Ipifc *ifc, uint8_t * unused_uint8_p_t); | 
 | static int multicastea(uint8_t * ea, uint8_t * ip); | 
 | static void recvarpproc(void *); | 
 | static void resolveaddr6(struct Ipifc *ifc, struct arpent *a); | 
 | static void etherpref2addr(uint8_t * pref, uint8_t * ea); | 
 |  | 
 | struct medium ethermedium = { | 
 | 	.name = "ether", | 
 | 	.hsize = 14, | 
 | 	.mintu = 60, | 
 | 	.maxtu = 1514, | 
 | 	.maclen = 6, | 
 | 	.bind = etherbind, | 
 | 	.unbind = etherunbind, | 
 | 	.bwrite = etherbwrite, | 
 | 	.addmulti = etheraddmulti, | 
 | 	.remmulti = etherremmulti, | 
 | 	.ares = arpenter, | 
 | 	.areg = sendgarp, | 
 | 	.pref2addr = etherpref2addr, | 
 | }; | 
 |  | 
 | struct medium trexmedium = { | 
 | 	.name = "trex", | 
 | 	.hsize = 14, | 
 | 	.mintu = 60, | 
 | 	.maxtu = 1514, | 
 | 	.maclen = 6, | 
 | 	.bind = etherbind, | 
 | 	.unbind = etherunbind, | 
 | 	.bwrite = etherbwrite, | 
 | 	.addmulti = etheraddmulti, | 
 | 	.remmulti = etherremmulti, | 
 | 	.ares = arpenter, | 
 | 	.areg = sendgarp, | 
 | 	.pref2addr = etherpref2addr, | 
 | }; | 
 |  | 
 | typedef struct Etherrock Etherrock; | 
 | struct Etherrock { | 
 | 	struct Fs *f;			/* file system we belong to */ | 
 | 	struct proc *arpp;		/* arp process */ | 
 | 	struct proc *read4p;		/* reading process (v4) */ | 
 | 	struct proc *read6p;		/* reading process (v6) */ | 
 | 	struct chan *mchan4;		/* Data channel for v4 */ | 
 | 	struct chan *achan;		/* Arp channel */ | 
 | 	struct chan *cchan4;		/* Control channel for v4 */ | 
 | 	struct chan *mchan6;		/* Data channel for v6 */ | 
 | 	struct chan *cchan6;		/* Control channel for v6 */ | 
 | }; | 
 |  | 
 | /* | 
 |  *  ethernet arp request | 
 |  */ | 
 | enum { | 
 | 	ETARP = 0x0806, | 
 | 	ETIP4 = 0x0800, | 
 | 	ETIP6 = 0x86DD, | 
 | 	ARPREQUEST = 1, | 
 | 	ARPREPLY = 2, | 
 | }; | 
 |  | 
 | typedef struct Etherarp Etherarp; | 
 | struct Etherarp { | 
 | 	uint8_t d[6]; | 
 | 	uint8_t s[6]; | 
 | 	uint8_t type[2]; | 
 | 	uint8_t hrd[2]; | 
 | 	uint8_t pro[2]; | 
 | 	uint8_t hln; | 
 | 	uint8_t pln; | 
 | 	uint8_t op[2]; | 
 | 	uint8_t sha[6]; | 
 | 	uint8_t spa[4]; | 
 | 	uint8_t tha[6]; | 
 | 	uint8_t tpa[4]; | 
 | }; | 
 |  | 
 | static char *nbmsg = "nonblocking"; | 
 |  | 
 | static unsigned int parsefeat(char *ptr) | 
 | { | 
 | 	unsigned int feat = 0; | 
 |  | 
 | 	if (strstr(ptr, "ipck")) | 
 | 		feat |= NETF_IPCK; | 
 | 	if (strstr(ptr, "udpck")) | 
 | 		feat |= NETF_UDPCK; | 
 | 	if (strstr(ptr, "tcpck")) | 
 | 		feat |= NETF_TCPCK; | 
 | 	if (strstr(ptr, "padmin")) | 
 | 		feat |= NETF_PADMIN; | 
 | 	if (strstr(ptr, "sg")) | 
 | 		feat |= NETF_SG; | 
 | 	if (strstr(ptr, "tso")) | 
 | 		feat |= NETF_TSO; | 
 | 	if (strstr(ptr, "lro")) | 
 | 		feat |= NETF_LRO; | 
 | 	if (strstr(ptr, "rxcsum")) | 
 | 		feat |= NETF_RXCSUM; | 
 | 	return feat; | 
 | } | 
 |  | 
 | /* | 
 |  *  called to bind an IP ifc to an ethernet device | 
 |  *  called with ifc wlock'd | 
 |  */ | 
 | static void etherbind(struct Ipifc *ifc, int argc, char **argv) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct chan *mchan4, *cchan4, *achan, *mchan6, *cchan6; | 
 | 	char *addr, *dir, *buf; | 
 | 	int fd, cfd, n; | 
 | 	char *ptr; | 
 | 	Etherrock *er; | 
 |  | 
 | 	if (argc < 2) | 
 | 		error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 	addr = kmalloc(Maxpath, MEM_WAIT);	//char addr[2*KNAMELEN]; | 
 | 	dir = kmalloc(Maxpath, MEM_WAIT);	//char addr[2*KNAMELEN]; | 
 | 	mchan4 = cchan4 = achan = mchan6 = cchan6 = NULL; | 
 | 	buf = NULL; | 
 | 	if (waserror()) { | 
 | 		if (mchan4 != NULL) | 
 | 			cclose(mchan4); | 
 | 		if (cchan4 != NULL) | 
 | 			cclose(cchan4); | 
 | 		if (achan != NULL) | 
 | 			cclose(achan); | 
 | 		if (mchan6 != NULL) | 
 | 			cclose(mchan6); | 
 | 		if (cchan6 != NULL) | 
 | 			cclose(cchan6); | 
 | 		if (buf != NULL) | 
 | 			kfree(buf); | 
 | 		kfree(addr); | 
 | 		kfree(dir); | 
 | 		nexterror(); | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 *  open ip converstation | 
 | 	 * | 
 | 	 *  the dial will fail if the type is already open on | 
 | 	 *  this device. | 
 | 	 */ | 
 | 	snprintf(addr, Maxpath, "%s!0x800", argv[2]); | 
 | 	fd = kdial(addr, NULL, dir, &cfd); | 
 | 	if (fd < 0) | 
 | 		error(EFAIL, "dial 0x800 failed: %s", get_cur_errbuf()); | 
 | 	mchan4 = commonfdtochan(fd, O_RDWR, 0, 1); | 
 | 	cchan4 = commonfdtochan(cfd, O_RDWR, 0, 1); | 
 | 	sysclose(fd); | 
 | 	sysclose(cfd); | 
 |  | 
 | 	/* | 
 | 	 *  make it non-blocking | 
 | 	 */ | 
 | 	devtab[cchan4->type].write(cchan4, nbmsg, strlen(nbmsg), 0); | 
 |  | 
 | 	/* | 
 | 	 *  get mac address and speed | 
 | 	 */ | 
 | 	snprintf(addr, Maxpath, "%s/stats", dir); | 
 | 	fd = sysopen(addr, O_READ); | 
 | 	if (fd < 0) | 
 | 		error(EFAIL, "can't open ether stats: %s", get_cur_errbuf()); | 
 |  | 
 | 	buf = kzmalloc(512, 0); | 
 | 	n = sysread(fd, buf, 511); | 
 | 	sysclose(fd); | 
 | 	if (n <= 0) | 
 | 		error(EIO, ERROR_FIXME); | 
 | 	buf[n] = 0; | 
 |  | 
 | 	ptr = strstr(buf, "addr: "); | 
 | 	if (!ptr) | 
 | 		error(EIO, ERROR_FIXME); | 
 | 	ptr += 6; | 
 | 	parsemac(ifc->mac, ptr, 6); | 
 |  | 
 | 	ptr = strstr(buf, "feat: "); | 
 | 	if (ptr) { | 
 | 		ptr += 6; | 
 | 		ifc->feat = parsefeat(ptr); | 
 | 	} else { | 
 | 		ifc->feat = 0; | 
 | 	} | 
 | 	/* | 
 | 	 *  open arp conversation | 
 | 	 */ | 
 | 	snprintf(addr, Maxpath, "%s!0x806", argv[2]); | 
 | 	fd = kdial(addr, NULL, NULL, NULL); | 
 | 	if (fd < 0) | 
 | 		error(EFAIL, "dial 0x806 failed: %s", get_cur_errbuf()); | 
 | 	achan = commonfdtochan(fd, O_RDWR, 0, 1); | 
 | 	sysclose(fd); | 
 |  | 
 | 	/* | 
 | 	 *  open ip conversation | 
 | 	 * | 
 | 	 *  the dial will fail if the type is already open on | 
 | 	 *  this device. | 
 | 	 */ | 
 | 	snprintf(addr, Maxpath, "%s!0x86DD", argv[2]); | 
 | 	fd = kdial(addr, NULL, dir, &cfd); | 
 | 	if (fd < 0) | 
 | 		error(EFAIL, "dial 0x86DD failed: %s", get_cur_errbuf()); | 
 | 	mchan6 = commonfdtochan(fd, O_RDWR, 0, 1); | 
 | 	cchan6 = commonfdtochan(cfd, O_RDWR, 0, 1); | 
 | 	sysclose(fd); | 
 | 	sysclose(cfd); | 
 |  | 
 | 	/* | 
 | 	 *  make it non-blocking | 
 | 	 */ | 
 | 	devtab[cchan6->type].write(cchan6, nbmsg, strlen(nbmsg), 0); | 
 |  | 
 | 	er = kzmalloc(sizeof(*er), 0); | 
 | 	er->mchan4 = mchan4; | 
 | 	er->cchan4 = cchan4; | 
 | 	er->achan = achan; | 
 | 	er->mchan6 = mchan6; | 
 | 	er->cchan6 = cchan6; | 
 | 	er->f = ifc->conv->p->f; | 
 | 	ifc->arg = er; | 
 |  | 
 | 	kfree(buf); | 
 | 	kfree(addr); | 
 | 	kfree(dir); | 
 | 	poperror(); | 
 |  | 
 | 	ktask("etherread4", etherread4, ifc); | 
 | 	ktask("recvarpproc", recvarpproc, ifc); | 
 | 	ktask("etherread6", etherread6, ifc); | 
 | } | 
 |  | 
 | /* | 
 |  *  called with ifc wlock'd | 
 |  */ | 
 | static void etherunbind(struct Ipifc *ifc) | 
 | { | 
 | 	Etherrock *er = ifc->arg; | 
 | 	printk("[kernel] etherunbind not supported yet!\n"); | 
 |  | 
 | 	// we'll need to tell the ktasks to exit, maybe via flags and a wakeup | 
 | #if 0 | 
 | 	if (er->read4p) | 
 | 		postnote(er->read4p, 1, "unbind", 0); | 
 | 	if (er->read6p) | 
 | 		postnote(er->read6p, 1, "unbind", 0); | 
 | 	if (er->arpp) | 
 | 		postnote(er->arpp, 1, "unbind", 0); | 
 | #endif | 
 |  | 
 | 	/* wait for readers to die */ | 
 | 	while (er->arpp != 0 || er->read4p != 0 || er->read6p != 0) | 
 | 		cpu_relax(); | 
 | 	kthread_usleep(300 * 1000); | 
 |  | 
 | 	if (er->mchan4 != NULL) | 
 | 		cclose(er->mchan4); | 
 | 	if (er->achan != NULL) | 
 | 		cclose(er->achan); | 
 | 	if (er->cchan4 != NULL) | 
 | 		cclose(er->cchan4); | 
 | 	if (er->mchan6 != NULL) | 
 | 		cclose(er->mchan6); | 
 | 	if (er->cchan6 != NULL) | 
 | 		cclose(er->cchan6); | 
 |  | 
 | 	kfree(er); | 
 | } | 
 |  | 
 | /* | 
 |  * copy ethernet address | 
 |  */ | 
 | static inline void etherfilladdr(uint16_t *pkt, uint16_t *dst, uint16_t *src) | 
 | { | 
 | 	*pkt++ = *dst++; | 
 | 	*pkt++ = *dst++; | 
 | 	*pkt++ = *dst++; | 
 | 	*pkt++ = *src++; | 
 | 	*pkt++ = *src++; | 
 | 	*pkt = *src; | 
 | } | 
 |  | 
 | /* | 
 |  *  called by ipoput with a single block to write with ifc rlock'd | 
 |  */ | 
 | static void etherbwrite(struct Ipifc *ifc, struct block *bp, int version, | 
 | 			uint8_t *ip) | 
 | { | 
 | 	Etherhdr *eh; | 
 | 	struct arpent *a; | 
 | 	uint8_t mac[6]; | 
 | 	Etherrock *er = ifc->arg; | 
 |  | 
 | 	ipifc_trace_block(ifc, bp); | 
 | 	/* get mac address of destination. | 
 | 	 * | 
 | 	 * Locking is tricky here.  If we get arpent 'a' back, the f->arp is | 
 | 	 * qlocked.  if multicastarp returns bp, then it unlocked it for us.  if | 
 | 	 * not, sendarp or resolveaddr6 unlocked it for us.  yikes. */ | 
 | 	a = arpget(er->f->arp, bp, version, ifc, ip, mac); | 
 | 	if (a) { | 
 | 		/* check for broadcast or multicast.  if it is either, this | 
 | 		 * sorts that out and returns the bp for the first packet on the | 
 | 		 * arp's hold list.*/ | 
 | 		bp = multicastarp(er->f, a, ifc->m, mac); | 
 | 		if (bp == NULL) { | 
 | 			switch (version) { | 
 | 				case V4: | 
 | 					sendarp(ifc, a); | 
 | 					break; | 
 | 				case V6: | 
 | 					resolveaddr6(ifc, a); | 
 | 					break; | 
 | 				default: | 
 | 					panic("etherbwrite: version %d", | 
 | 					      version); | 
 | 			} | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* make it a single block with space for the ether header */ | 
 | 	bp = padblock(bp, ifc->m->hsize); | 
 | 	if (bp->next) | 
 | 		bp = concatblock(bp); | 
 | 	eh = (Etherhdr *) bp->rp; | 
 |  | 
 | 	/* copy in mac addresses and ether type */ | 
 | 	etherfilladdr((uint16_t *)bp->rp, (uint16_t *)mac, | 
 | 		      (uint16_t *)ifc->mac); | 
 |  | 
 | 	switch (version) { | 
 | 	case V4: | 
 | 		eh->t[0] = 0x08; | 
 | 		eh->t[1] = 0x00; | 
 | 		devtab[er->mchan4->type].bwrite(er->mchan4, bp, 0); | 
 | 		break; | 
 | 	case V6: | 
 | 		eh->t[0] = 0x86; | 
 | 		eh->t[1] = 0xDD; | 
 | 		devtab[er->mchan6->type].bwrite(er->mchan6, bp, 0); | 
 | 		break; | 
 | 	default: | 
 | 		panic("etherbwrite2: version %d", version); | 
 | 	} | 
 | 	ifc->out++; | 
 | } | 
 |  | 
 | /* | 
 |  *  process to read from the ethernet | 
 |  */ | 
 | static void etherread4(void *a) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct Ipifc *ifc; | 
 | 	struct block *bp; | 
 | 	Etherrock *er; | 
 |  | 
 | 	ifc = a; | 
 | 	er = ifc->arg; | 
 | 	er->read4p = current;	/* hide identity under a rock for unbind */ | 
 | 	if (waserror()) { | 
 | 		er->read4p = 0; | 
 | 		poperror(); | 
 | 		warn("etherread4 returns, probably unexpectedly\n"); | 
 | 		return; | 
 | 	} | 
 | 	for (;;) { | 
 | 		bp = devtab[er->mchan4->type].bread(er->mchan4, 128 * 1024, 0); | 
 | 		if (!canrlock(&ifc->rwlock)) { | 
 | 			freeb(bp); | 
 | 			continue; | 
 | 		} | 
 | 		if (waserror()) { | 
 | 			runlock(&ifc->rwlock); | 
 | 			nexterror(); | 
 | 		} | 
 | 		ifc->in++; | 
 | 		bp->rp += ifc->m->hsize; | 
 | 		if (ifc->lifc == NULL) { | 
 | 			freeb(bp); | 
 | 		} else { | 
 | 			ipifc_trace_block(ifc, bp); | 
 | 			ipiput4(er->f, ifc, bp); | 
 | 		} | 
 | 		runlock(&ifc->rwlock); | 
 | 		poperror(); | 
 | 	} | 
 | 	poperror(); | 
 | } | 
 |  | 
 | /* | 
 |  *  process to read from the ethernet, IPv6 | 
 |  */ | 
 | static void etherread6(void *a) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct Ipifc *ifc; | 
 | 	struct block *bp; | 
 | 	Etherrock *er; | 
 |  | 
 | 	ifc = a; | 
 | 	er = ifc->arg; | 
 | 	er->read6p = current;	/* hide identity under a rock for unbind */ | 
 | 	if (waserror()) { | 
 | 		er->read6p = 0; | 
 | 		warn("etherread6 returns, probably unexpectedly\n"); | 
 | 		poperror(); | 
 | 		return; | 
 | 	} | 
 | 	for (;;) { | 
 | 		bp = devtab[er->mchan6->type].bread(er->mchan6, ifc->maxtu, 0); | 
 | 		if (!canrlock(&ifc->rwlock)) { | 
 | 			freeb(bp); | 
 | 			continue; | 
 | 		} | 
 | 		if (waserror()) { | 
 | 			runlock(&ifc->rwlock); | 
 | 			nexterror(); | 
 | 		} | 
 | 		ifc->in++; | 
 | 		bp->rp += ifc->m->hsize; | 
 | 		if (ifc->lifc == NULL) { | 
 | 			freeb(bp); | 
 | 		} else { | 
 | 			ipifc_trace_block(ifc, bp); | 
 | 			ipiput6(er->f, ifc, bp); | 
 | 		} | 
 | 		runlock(&ifc->rwlock); | 
 | 		poperror(); | 
 | 	} | 
 | 	poperror(); | 
 | } | 
 |  | 
 | static void etheraddmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * unused) | 
 | { | 
 | 	uint8_t mac[6]; | 
 | 	char buf[64]; | 
 | 	Etherrock *er = ifc->arg; | 
 | 	int version; | 
 |  | 
 | 	version = multicastea(mac, a); | 
 | 	snprintf(buf, sizeof(buf), "addmulti %E", mac); | 
 | 	switch (version) { | 
 | 	case V4: | 
 | 		devtab[er->cchan4->type].write(er->cchan4, buf, strlen(buf), 0); | 
 | 		break; | 
 | 	case V6: | 
 | 		devtab[er->cchan6->type].write(er->cchan6, buf, strlen(buf), 0); | 
 | 		break; | 
 | 	default: | 
 | 		panic("etheraddmulti: version %d", version); | 
 | 	} | 
 | } | 
 |  | 
 | static void etherremmulti(struct Ipifc *ifc, uint8_t * a, uint8_t * unused) | 
 | { | 
 | 	uint8_t mac[6]; | 
 | 	char buf[64]; | 
 | 	Etherrock *er = ifc->arg; | 
 | 	int version; | 
 |  | 
 | 	version = multicastea(mac, a); | 
 | 	snprintf(buf, sizeof(buf), "remmulti %E", mac); | 
 | 	switch (version) { | 
 | 	case V4: | 
 | 		devtab[er->cchan4->type].write(er->cchan4, buf, strlen(buf), 0); | 
 | 		break; | 
 | 	case V6: | 
 | 		devtab[er->cchan6->type].write(er->cchan6, buf, strlen(buf), 0); | 
 | 		break; | 
 | 	default: | 
 | 		panic("etherremmulti: version %d", version); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  *  send an ethernet arp | 
 |  *  (only v4, v6 uses the neighbor discovery, rfc1970) | 
 |  * | 
 |  * May drop packets on stale arps. */ | 
 | static void sendarp(struct Ipifc *ifc, struct arpent *a) | 
 | { | 
 | 	int n; | 
 | 	struct block *bp; | 
 | 	Etherarp *e; | 
 | 	Etherrock *er = ifc->arg; | 
 |  | 
 | 	/* don't do anything if it's been less than a second since the last. | 
 | 	 * ctime is set to 0 for the first time through.  we hold the f->arp | 
 | 	 * qlock, so there shouldn't be a problem with another arp request for | 
 | 	 * this same arpent coming down til we update ctime again. */ | 
 | 	if (NOW - a->ctime < 1000) { | 
 | 		arprelease(er->f->arp, a); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* remove all but the last message.  brho: this might be unnecessary. | 
 | 	 * we'll eventually send them.  but they should be quite stale at this | 
 | 	 * point. */ | 
 | 	while ((bp = a->hold) != NULL) { | 
 | 		if (bp == a->last) | 
 | 			break; | 
 | 		a->hold = bp->list; | 
 | 		freeblist(bp); | 
 | 	} | 
 |  | 
 | 	/* update last sent time */ | 
 | 	a->ctime = NOW; | 
 | 	arprelease(er->f->arp, a); | 
 |  | 
 | 	n = sizeof(Etherarp); | 
 | 	if (n < a->type->mintu) | 
 | 		n = a->type->mintu; | 
 | 	bp = block_alloc(n, MEM_WAIT); | 
 | 	memset(bp->rp, 0, n); | 
 | 	e = (Etherarp *) bp->rp; | 
 | 	memmove(e->tpa, a->ip + IPv4off, sizeof(e->tpa)); | 
 | 	ipv4local(ifc, e->spa); | 
 | 	memmove(e->sha, ifc->mac, sizeof(e->sha)); | 
 | 	memset(e->d, 0xff, sizeof(e->d));	/* ethernet broadcast */ | 
 | 	memmove(e->s, ifc->mac, sizeof(e->s)); | 
 |  | 
 | 	hnputs(e->type, ETARP); | 
 | 	hnputs(e->hrd, 1); | 
 | 	hnputs(e->pro, ETIP4); | 
 | 	e->hln = sizeof(e->sha); | 
 | 	e->pln = sizeof(e->spa); | 
 | 	hnputs(e->op, ARPREQUEST); | 
 | 	bp->wp += n; | 
 |  | 
 | 	n = devtab[er->achan->type].bwrite(er->achan, bp, 0); | 
 | 	if (n < 0) | 
 | 		printd("arp: send: %r\n"); | 
 | } | 
 |  | 
 | static void resolveaddr6(struct Ipifc *ifc, struct arpent *a) | 
 | { | 
 | 	int sflag; | 
 | 	struct block *bp; | 
 | 	Etherrock *er = ifc->arg; | 
 | 	uint8_t ipsrc[IPaddrlen]; | 
 |  | 
 | 	/* don't do anything if it's been less than a second since the last */ | 
 | 	if (NOW - a->ctime < ReTransTimer) { | 
 | 		arprelease(er->f->arp, a); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* remove all but the last message */ | 
 | 	while ((bp = a->hold) != NULL) { | 
 | 		if (bp == a->last) | 
 | 			break; | 
 | 		a->hold = bp->list; | 
 | 		freeblist(bp); | 
 | 	} | 
 |  | 
 | 	/* try to keep it around for a second more */ | 
 | 	a->ctime = NOW; | 
 | 	a->rtime = NOW + ReTransTimer; | 
 | 	if (a->rxtsrem <= 0) { | 
 | 		arprelease(er->f->arp, a); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	a->rxtsrem--; | 
 | 	arprelease(er->f->arp, a); | 
 |  | 
 | 	if ((sflag = ipv6anylocal(ifc, ipsrc))) | 
 | 		icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); | 
 | } | 
 |  | 
 | /* | 
 |  *  send a gratuitous arp to refresh arp caches | 
 |  */ | 
 | static void sendgarp(struct Ipifc *ifc, uint8_t * ip) | 
 | { | 
 | 	int n; | 
 | 	struct block *bp; | 
 | 	Etherarp *e; | 
 | 	Etherrock *er = ifc->arg; | 
 |  | 
 | 	/* don't arp for our initial non address */ | 
 | 	if (ipcmp(ip, IPnoaddr) == 0) | 
 | 		return; | 
 |  | 
 | 	n = sizeof(Etherarp); | 
 | 	if (n < ifc->m->mintu) | 
 | 		n = ifc->m->mintu; | 
 | 	bp = block_alloc(n, MEM_WAIT); | 
 | 	memset(bp->rp, 0, n); | 
 | 	e = (Etherarp *) bp->rp; | 
 | 	memmove(e->tpa, ip + IPv4off, sizeof(e->tpa)); | 
 | 	memmove(e->spa, ip + IPv4off, sizeof(e->spa)); | 
 | 	memmove(e->sha, ifc->mac, sizeof(e->sha)); | 
 | 	memset(e->d, 0xff, sizeof(e->d));	/* ethernet broadcast */ | 
 | 	memmove(e->s, ifc->mac, sizeof(e->s)); | 
 |  | 
 | 	hnputs(e->type, ETARP); | 
 | 	hnputs(e->hrd, 1); | 
 | 	hnputs(e->pro, ETIP4); | 
 | 	e->hln = sizeof(e->sha); | 
 | 	e->pln = sizeof(e->spa); | 
 | 	hnputs(e->op, ARPREQUEST); | 
 | 	bp->wp += n; | 
 |  | 
 | 	n = devtab[er->achan->type].bwrite(er->achan, bp, 0); | 
 | 	if (n < 0) | 
 | 		printd("garp: send: %r\n"); | 
 | } | 
 |  | 
 | static void recvarp(struct Ipifc *ifc) | 
 | { | 
 | 	int n; | 
 | 	struct block *ebp, *rbp; | 
 | 	Etherarp *e, *r; | 
 | 	uint8_t ip[IPaddrlen]; | 
 | 	static uint8_t eprinted[4]; | 
 | 	Etherrock *er = ifc->arg; | 
 |  | 
 | 	ebp = devtab[er->achan->type].bread(er->achan, ifc->maxtu, 0); | 
 | 	if (ebp == NULL) { | 
 | 		printd("arp: rcv: %r\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	e = (Etherarp *) ebp->rp; | 
 | 	switch (nhgets(e->op)) { | 
 | 	default: | 
 | 		break; | 
 |  | 
 | 	case ARPREPLY: | 
 | 		/* check for machine using my ip address */ | 
 | 		v4tov6(ip, e->spa); | 
 | 		if (iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)) { | 
 | 			if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0) { | 
 | 				printd("arprep: 0x%E/0x%E also has ip addr %V\n", | 
 | 					   e->s, e->sha, e->spa); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* make sure we're not entering broadcast addresses */ | 
 | 		if (ipcmp(ip, ipbroadcast) == 0 || | 
 | 		    !memcmp(e->sha, etherbroadcast, sizeof(e->sha))) { | 
 | 			printd("arprep: 0x%E/0x%E cannot register broadcast address %I\n", | 
 | 			       e->s, e->sha, e->spa); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0); | 
 | 		break; | 
 |  | 
 | 	case ARPREQUEST: | 
 | 		/* don't answer arps till we know who we are */ | 
 | 		if (ifc->lifc == 0) | 
 | 			break; | 
 |  | 
 | 		/* check for machine using my ip or ether address */ | 
 | 		v4tov6(ip, e->spa); | 
 | 		if (iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)) { | 
 | 			if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0) { | 
 | 				if (memcmp(eprinted, e->spa, sizeof(e->spa))) { | 
 | 					/* print only once */ | 
 | 					printd("arpreq: 0x%E also has ip addr %V\n", | 
 | 					       e->sha, e->spa); | 
 | 					memmove(eprinted, e->spa, | 
 | 						sizeof(e->spa)); | 
 | 				} | 
 | 			} | 
 | 		} else { | 
 | 			if (memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0) { | 
 | 				printd("arpreq: %V also has ether addr %E\n", | 
 | 				       e->spa, e->sha); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* refresh what we know about sender */ | 
 | 		arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1); | 
 |  | 
 | 		/* answer only requests for our address or systems we're | 
 | 		 * proxying for */ | 
 | 		v4tov6(ip, e->tpa); | 
 | 		if (!iplocalonifc(ifc, ip)) | 
 | 			if (!ipproxyifc(er->f, ifc, ip)) | 
 | 				break; | 
 |  | 
 | 		n = sizeof(Etherarp); | 
 | 		if (n < ifc->mintu) | 
 | 			n = ifc->mintu; | 
 | 		rbp = block_alloc(n, MEM_WAIT); | 
 | 		r = (Etherarp *) rbp->rp; | 
 | 		memset(r, 0, sizeof(Etherarp)); | 
 | 		hnputs(r->type, ETARP); | 
 | 		hnputs(r->hrd, 1); | 
 | 		hnputs(r->pro, ETIP4); | 
 | 		r->hln = sizeof(r->sha); | 
 | 		r->pln = sizeof(r->spa); | 
 | 		hnputs(r->op, ARPREPLY); | 
 | 		memmove(r->tha, e->sha, sizeof(r->tha)); | 
 | 		memmove(r->tpa, e->spa, sizeof(r->tpa)); | 
 | 		memmove(r->sha, ifc->mac, sizeof(r->sha)); | 
 | 		memmove(r->spa, e->tpa, sizeof(r->spa)); | 
 | 		memmove(r->d, e->sha, sizeof(r->d)); | 
 | 		memmove(r->s, ifc->mac, sizeof(r->s)); | 
 | 		rbp->wp += n; | 
 |  | 
 | 		n = devtab[er->achan->type].bwrite(er->achan, rbp, 0); | 
 | 		if (n < 0) | 
 | 			printd("arp: write: %r\n"); | 
 | 	} | 
 | 	freeb(ebp); | 
 | } | 
 |  | 
 | static void recvarpproc(void *v) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct Ipifc *ifc = v; | 
 | 	Etherrock *er = ifc->arg; | 
 |  | 
 | 	er->arpp = current; | 
 | 	if (waserror()) { | 
 | 		er->arpp = 0; | 
 | 		warn("recvarpproc returns, probably unexpectedly\n"); | 
 | 		poperror(); | 
 | 		return; | 
 | 	} | 
 | 	for (;;) | 
 | 		recvarp(ifc); | 
 | 	poperror(); | 
 | } | 
 |  | 
 | static int multicastea(uint8_t * ea, uint8_t * ip) | 
 | { | 
 | 	int x; | 
 |  | 
 | 	switch (x = ipismulticast(ip)) { | 
 | 	case V4: | 
 | 		ea[0] = 0x01; | 
 | 		ea[1] = 0x00; | 
 | 		ea[2] = 0x5e; | 
 | 		ea[3] = ip[13] & 0x7f; | 
 | 		ea[4] = ip[14]; | 
 | 		ea[5] = ip[15]; | 
 | 		break; | 
 | 	case V6: | 
 | 		ea[0] = 0x33; | 
 | 		ea[1] = 0x33; | 
 | 		ea[2] = ip[12]; | 
 | 		ea[3] = ip[13]; | 
 | 		ea[4] = ip[14]; | 
 | 		ea[5] = ip[15]; | 
 | 		break; | 
 | 	} | 
 | 	return x; | 
 | } | 
 |  | 
 | /* | 
 |  *  fill in an arp entry for broadcast or multicast | 
 |  *  addresses.  Return the first queued packet for the | 
 |  *  IP address. | 
 |  */ | 
 | static struct block *multicastarp(struct Fs *f, struct arpent *a, | 
 | 				  struct medium *medium, uint8_t *mac) | 
 | { | 
 | 	/* is it broadcast? */ | 
 | 	switch (ipforme(f, a->ip)) { | 
 | 	case Runi: | 
 | 		return NULL; | 
 | 	case Rbcast: | 
 | 		memset(mac, 0xff, 6); | 
 | 		return arpresolve(f->arp, a, medium, mac); | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	/* if multicast, fill in mac */ | 
 | 	switch (multicastea(mac, a->ip)) { | 
 | 	case V4: | 
 | 	case V6: | 
 | 		return arpresolve(f->arp, a, medium, mac); | 
 | 	} | 
 |  | 
 | 	/* let arp take care of it */ | 
 | 	return NULL; | 
 | } | 
 |  | 
 | linker_func_4(ethermediumlink) | 
 | { | 
 | 	addipmedium(ðermedium); | 
 | 	addipmedium(&trexmedium); | 
 | } | 
 |  | 
 | static void etherpref2addr(uint8_t * pref, uint8_t * ea) | 
 | { | 
 | 	pref[8] = ea[0] | 0x2; | 
 | 	pref[9] = ea[1]; | 
 | 	pref[10] = ea[2]; | 
 | 	pref[11] = 0xFF; | 
 | 	pref[12] = 0xFE; | 
 | 	pref[13] = ea[3]; | 
 | 	pref[14] = ea[4]; | 
 | 	pref[15] = ea[5]; | 
 | } |