| /* 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 <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> | 
 | #include <ip.h> | 
 |  | 
 | /* | 
 |  *  address resolution tables | 
 |  */ | 
 |  | 
 | enum { | 
 | 	NHASH = (1 << 6), | 
 | 	NCACHE = 256, | 
 |  | 
 | 	AOK = 1, | 
 | 	AWAIT = 2, | 
 | }; | 
 |  | 
 | char *arpstate[] = { | 
 | 	"UNUSED", | 
 | 	"OK", | 
 | 	"WAIT", | 
 | }; | 
 |  | 
 | /* | 
 |  *  one per Fs | 
 |  */ | 
 | struct arp { | 
 | 	qlock_t qlock; | 
 | 	struct Fs *f; | 
 | 	struct arpent *hash[NHASH]; | 
 | 	struct arpent cache[NCACHE]; | 
 | 	struct arpent *rxmt; | 
 | 	struct proc *rxmitp;		/* neib sol re-transmit proc */ | 
 | 	struct rendez rxmtq; | 
 | 	struct block *dropf, *dropl; | 
 | }; | 
 |  | 
 | #define haship(s) ((s)[IPaddrlen-1]%NHASH) | 
 |  | 
 | int ReTransTimer = RETRANS_TIMER; | 
 | static void rxmitproc(void *v); | 
 |  | 
 | void arpinit(struct Fs *f) | 
 | { | 
 | 	f->arp = kzmalloc(sizeof(struct arp), MEM_WAIT); | 
 | 	qlock_init(&f->arp->qlock); | 
 | 	rendez_init(&f->arp->rxmtq); | 
 | 	f->arp->f = f; | 
 | 	f->arp->rxmt = NULL; | 
 | 	f->arp->dropf = f->arp->dropl = NULL; | 
 | 	ktask("rxmitproc", rxmitproc, f->arp); | 
 | } | 
 |  | 
 | /* | 
 |  *  create a new arp entry for an ip address. | 
 |  */ | 
 | static struct arpent *newarp6(struct arp *arp, uint8_t *ip, struct Ipifc *ifc, | 
 |                               int addrxt) | 
 | { | 
 | 	unsigned int t; | 
 | 	struct block *next, *xp; | 
 | 	struct arpent *a, *e, *f, **l; | 
 | 	struct medium *m = ifc->m; | 
 | 	int empty; | 
 |  | 
 | 	/* find oldest entry */ | 
 | 	e = &arp->cache[NCACHE]; | 
 | 	a = arp->cache; | 
 | 	t = a->utime; | 
 | 	for (f = a; f < e; f++) { | 
 | 		if (f->utime < t) { | 
 | 			t = f->utime; | 
 | 			a = f; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* dump waiting packets */ | 
 | 	xp = a->hold; | 
 | 	a->hold = NULL; | 
 |  | 
 | 	if (isv4(a->ip)) { | 
 | 		while (xp) { | 
 | 			next = xp->list; | 
 | 			freeblist(xp); | 
 | 			xp = next; | 
 | 		} | 
 | 	} else { | 
 | 		/* queue icmp unreachable for rxmitproc later, w/o arp lock */ | 
 | 		if (xp) { | 
 | 			if (arp->dropl == NULL) | 
 | 				arp->dropf = xp; | 
 | 			else | 
 | 				arp->dropl->list = xp; | 
 |  | 
 | 			for (next = xp->list; next; next = next->list) | 
 | 				xp = next; | 
 | 			arp->dropl = xp; | 
 | 			rendez_wakeup(&arp->rxmtq); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* take out of current chain */ | 
 | 	l = &arp->hash[haship(a->ip)]; | 
 | 	for (f = *l; f; f = f->hash) { | 
 | 		if (f == a) { | 
 | 			*l = a->hash; | 
 | 			break; | 
 | 		} | 
 | 		l = &f->hash; | 
 | 	} | 
 |  | 
 | 	/* insert into new chain */ | 
 | 	l = &arp->hash[haship(ip)]; | 
 | 	a->hash = *l; | 
 | 	*l = a; | 
 |  | 
 | 	memmove(a->ip, ip, sizeof(a->ip)); | 
 | 	a->utime = NOW; | 
 | 	a->ctime = 0;	/* somewhat of a "last sent time".  0, to trigger a send. */ | 
 | 	a->type = m; | 
 |  | 
 | 	a->rtime = NOW + ReTransTimer; | 
 | 	a->rxtsrem = MAX_MULTICAST_SOLICIT; | 
 | 	a->ifc = ifc; | 
 | 	a->ifcid = ifc->ifcid; | 
 |  | 
 | 	/* put to the end of re-transmit chain; addrxt is 0 when isv4(a->ip) */ | 
 | 	if (!ipismulticast(a->ip) && addrxt) { | 
 | 		l = &arp->rxmt; | 
 | 		empty = (*l == NULL); | 
 |  | 
 | 		for (f = *l; f; f = f->nextrxt) { | 
 | 			if (f == a) { | 
 | 				*l = a->nextrxt; | 
 | 				break; | 
 | 			} | 
 | 			l = &f->nextrxt; | 
 | 		} | 
 | 		for (f = *l; f; f = f->nextrxt) { | 
 | 			l = &f->nextrxt; | 
 | 		} | 
 | 		*l = a; | 
 | 		if (empty) | 
 | 			rendez_wakeup(&arp->rxmtq); | 
 | 	} | 
 |  | 
 | 	a->nextrxt = NULL; | 
 |  | 
 | 	return a; | 
 | } | 
 |  | 
 | /* called with arp qlocked */ | 
 |  | 
 | void cleanarpent(struct arp *arp, struct arpent *a) | 
 | { | 
 | 	struct arpent *f, **l; | 
 |  | 
 | 	a->utime = 0; | 
 | 	a->ctime = 0; | 
 | 	a->type = 0; | 
 | 	a->state = 0; | 
 |  | 
 | 	/* take out of current chain */ | 
 | 	l = &arp->hash[haship(a->ip)]; | 
 | 	for (f = *l; f; f = f->hash) { | 
 | 		if (f == a) { | 
 | 			*l = a->hash; | 
 | 			break; | 
 | 		} | 
 | 		l = &f->hash; | 
 | 	} | 
 |  | 
 | 	/* take out of re-transmit chain */ | 
 | 	l = &arp->rxmt; | 
 | 	for (f = *l; f; f = f->nextrxt) { | 
 | 		if (f == a) { | 
 | 			*l = a->nextrxt; | 
 | 			break; | 
 | 		} | 
 | 		l = &f->nextrxt; | 
 | 	} | 
 | 	a->nextrxt = NULL; | 
 | 	a->hash = NULL; | 
 | 	a->hold = NULL; | 
 | 	a->last = NULL; | 
 | 	a->ifc = NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *  fill in the media address if we have it.  Otherwise return an | 
 |  *  arpent that represents the state of the address resolution FSM | 
 |  *  for ip.  Add the packet to be sent onto the list of packets | 
 |  *  waiting for ip->mac to be resolved. | 
 |  */ | 
 | struct arpent *arpget(struct arp *arp, struct block *bp, int version, | 
 |                       struct Ipifc *ifc, uint8_t *ip, uint8_t *mac) | 
 | { | 
 | 	int hash, len; | 
 | 	struct arpent *a; | 
 | 	struct medium *type = ifc->m; | 
 | 	uint8_t v6ip[IPaddrlen]; | 
 | 	uint16_t *s, *d; | 
 |  | 
 | 	if (version == V4) { | 
 | 		v4tov6(v6ip, ip); | 
 | 		ip = v6ip; | 
 | 	} | 
 |  | 
 | 	qlock(&arp->qlock); | 
 | 	hash = haship(ip); | 
 | 	for (a = arp->hash[hash]; a; a = a->hash) { | 
 | 		if (ipcmp(ip, a->ip) == 0) | 
 | 			if (type == a->type) | 
 | 				break; | 
 | 	} | 
 |  | 
 | 	if (a == NULL) { | 
 | 		a = newarp6(arp, ip, ifc, (version != V4)); | 
 | 		a->state = AWAIT; | 
 | 	} | 
 | 	a->utime = NOW; | 
 | 	if (a->state == AWAIT) { | 
 | 		if (bp != NULL) { | 
 | 			if (a->hold) | 
 | 				a->last->list = bp; | 
 | 			else | 
 | 				a->hold = bp; | 
 | 			a->last = bp; | 
 | 			bp->list = NULL; | 
 | 		} | 
 | 		return a;	/* return with arp qlocked */ | 
 | 	} | 
 |  | 
 | 	s = (uint16_t *)a->mac; | 
 | 	d = (uint16_t *)mac; | 
 | 	len = a->type->maclen / 2; | 
 | 	while (len) { | 
 | 		*d++ = *s++; | 
 | 		len--; | 
 | 	} | 
 |  | 
 | 	/* remove old entries */ | 
 | 	if (NOW - a->ctime > 15 * 60 * 1000) | 
 | 		cleanarpent(arp, a); | 
 |  | 
 | 	qunlock(&arp->qlock); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * called with arp locked | 
 |  */ | 
 | void arprelease(struct arp *arp, struct arpent *a) | 
 | { | 
 | 	qunlock(&arp->qlock); | 
 | } | 
 |  | 
 | /* | 
 |  * Copy out the mac address from the arpent.  Return the | 
 |  * block waiting to get sent to this mac address. | 
 |  * | 
 |  * called with arp locked | 
 |  */ | 
 | struct block *arpresolve(struct arp *arp, struct arpent *a, struct medium *type, | 
 |                          uint8_t *mac) | 
 | { | 
 | 	struct block *bp; | 
 | 	struct arpent *f, **l; | 
 |  | 
 | 	if (!isv4(a->ip)) { | 
 | 		l = &arp->rxmt; | 
 | 		for (f = *l; f; f = f->nextrxt) { | 
 | 			if (f == a) { | 
 | 				*l = a->nextrxt; | 
 | 				break; | 
 | 			} | 
 | 			l = &f->nextrxt; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	memmove(a->mac, mac, type->maclen); | 
 | 	a->type = type; | 
 | 	a->state = AOK; | 
 | 	a->utime = NOW; | 
 | 	bp = a->hold; | 
 | 	a->hold = NULL; | 
 | 	/* brho: it looks like we return the entire hold list, though it might be | 
 | 	 * purged by now via some other crazy arp list management.  our callers | 
 | 	 * can't handle the arp's b->list stuff. */ | 
 | 	assert(!bp->list); | 
 | 	qunlock(&arp->qlock); | 
 |  | 
 | 	return bp; | 
 | } | 
 |  | 
 | void arpenter(struct Fs *fs, int version, uint8_t *ip, uint8_t *mac, int n, | 
 |               int refresh) | 
 | { | 
 | 	ERRSTACK(1); | 
 | 	struct arp *arp; | 
 | 	struct route *r; | 
 | 	struct arpent *a, *f, **l; | 
 | 	struct Ipifc *ifc; | 
 | 	struct medium *type; | 
 | 	struct block *bp, *next; | 
 | 	uint8_t v6ip[IPaddrlen]; | 
 |  | 
 | 	arp = fs->arp; | 
 |  | 
 | 	if (n != 6) { | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	switch (version) { | 
 | 		case V4: | 
 | 			r = v4lookup(fs, ip, NULL); | 
 | 			v4tov6(v6ip, ip); | 
 | 			ip = v6ip; | 
 | 			break; | 
 | 		case V6: | 
 | 			r = v6lookup(fs, ip, NULL); | 
 | 			break; | 
 | 		default: | 
 | 			panic("arpenter: version %d", version); | 
 | 			return;	/* to supress warnings */ | 
 | 	} | 
 |  | 
 | 	if (r == NULL) { | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	ifc = r->rt.ifc; | 
 | 	type = ifc->m; | 
 |  | 
 | 	qlock(&arp->qlock); | 
 | 	for (a = arp->hash[haship(ip)]; a; a = a->hash) { | 
 | 		if (a->type != type || (a->state != AWAIT && a->state != AOK)) | 
 | 			continue; | 
 |  | 
 | 		if (ipcmp(a->ip, ip) == 0) { | 
 | 			a->state = AOK; | 
 | 			memmove(a->mac, mac, type->maclen); | 
 |  | 
 | 			if (version == V6) { | 
 | 				/* take out of re-transmit chain */ | 
 | 				l = &arp->rxmt; | 
 | 				for (f = *l; f; f = f->nextrxt) { | 
 | 					if (f == a) { | 
 | 						*l = a->nextrxt; | 
 | 						break; | 
 | 					} | 
 | 					l = &f->nextrxt; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			a->ifc = ifc; | 
 | 			a->ifcid = ifc->ifcid; | 
 | 			bp = a->hold; | 
 | 			a->hold = NULL; | 
 | 			if (version == V4) | 
 | 				ip += IPv4off; | 
 | 			a->utime = NOW; | 
 | 			a->ctime = a->utime; | 
 | 			qunlock(&arp->qlock); | 
 |  | 
 | 			while (bp) { | 
 | 				next = bp->list; | 
 | 				if (ifc != NULL) { | 
 | 					rlock(&ifc->rwlock); | 
 | 					if (waserror()) { | 
 | 						runlock(&ifc->rwlock); | 
 | 						nexterror(); | 
 | 					} | 
 | 					if (ifc->m != NULL) | 
 | 						ifc->m->bwrite(ifc, bp, version, ip); | 
 | 					else | 
 | 						freeb(bp); | 
 | 					runlock(&ifc->rwlock); | 
 | 					poperror(); | 
 | 				} else | 
 | 					freeb(bp); | 
 | 				bp = next; | 
 | 			} | 
 | 			return; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (refresh == 0) { | 
 | 		a = newarp6(arp, ip, ifc, 0); | 
 | 		a->state = AOK; | 
 | 		a->type = type; | 
 | 		a->ctime = NOW; | 
 | 		memmove(a->mac, mac, type->maclen); | 
 | 	} | 
 |  | 
 | 	qunlock(&arp->qlock); | 
 | } | 
 |  | 
 | int arpwrite(struct Fs *fs, char *s, long len) | 
 | { | 
 | 	int n; | 
 | 	struct route *r; | 
 | 	struct arp *arp; | 
 | 	struct block *bp; | 
 | 	struct arpent *a, *fl, **l; | 
 | 	struct medium *m; | 
 | 	char *f[4], buf[256]; | 
 | 	uint8_t ip[IPaddrlen], mac[MAClen]; | 
 |  | 
 | 	arp = fs->arp; | 
 |  | 
 | 	if (len <= 0) | 
 | 		error(EINVAL, ERROR_FIXME); | 
 | 	if (len > sizeof(buf)) | 
 | 		len = sizeof(buf); | 
 | 	strlcpy(buf, s, sizeof(buf)); | 
 | 	if (len > 0 && buf[len - 2] == '\n') | 
 | 		buf[len - 2] = 0; | 
 |  | 
 | 	n = getfields(buf, f, 4, 1, " "); | 
 | 	if (strcmp(f[0], "flush") == 0) { | 
 | 		qlock(&arp->qlock); | 
 | 		for (a = arp->cache; a < &arp->cache[NCACHE]; a++) { | 
 | 			memset(a->ip, 0, sizeof(a->ip)); | 
 | 			memset(a->mac, 0, sizeof(a->mac)); | 
 | 			a->hash = NULL; | 
 | 			a->state = 0; | 
 | 			a->utime = 0; | 
 | 			while (a->hold != NULL) { | 
 | 				bp = a->hold->list; | 
 | 				freeblist(a->hold); | 
 | 				a->hold = bp; | 
 | 			} | 
 | 		} | 
 | 		memset(arp->hash, 0, sizeof(arp->hash)); | 
 | 		/* clear all pkts on these lists (rxmt, dropf/l) */ | 
 | 		arp->rxmt = NULL; | 
 | 		arp->dropf = NULL; | 
 | 		arp->dropl = NULL; | 
 | 		qunlock(&arp->qlock); | 
 | 	} else if (strcmp(f[0], "add") == 0) { | 
 | 		switch (n) { | 
 | 			default: | 
 | 				error(EINVAL, ERROR_FIXME); | 
 | 			case 3: | 
 | 				parseip(ip, f[1]); | 
 | 				if (isv4(ip)) | 
 | 					r = v4lookup(fs, ip + IPv4off, NULL); | 
 | 				else | 
 | 					r = v6lookup(fs, ip, NULL); | 
 | 				if (r == NULL) | 
 | 					error(EHOSTUNREACH, "Destination unreachable"); | 
 | 				m = r->rt.ifc->m; | 
 | 				n = parsemac(mac, f[2], m->maclen); | 
 | 				break; | 
 | 			case 4: | 
 | 				m = ipfindmedium(f[1]); | 
 | 				if (m == NULL) | 
 | 					error(EINVAL, ERROR_FIXME); | 
 | 				parseip(ip, f[2]); | 
 | 				n = parsemac(mac, f[3], m->maclen); | 
 | 				break; | 
 | 		} | 
 |  | 
 | 		if (m->ares == NULL) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 		m->ares(fs, V6, ip, mac, n, 0); | 
 | 	} else if (strcmp(f[0], "del") == 0) { | 
 | 		if (n != 2) | 
 | 			error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 		parseip(ip, f[1]); | 
 | 		qlock(&arp->qlock); | 
 |  | 
 | 		l = &arp->hash[haship(ip)]; | 
 | 		for (a = *l; a; a = a->hash) { | 
 | 			if (memcmp(ip, a->ip, sizeof(a->ip)) == 0) { | 
 | 				*l = a->hash; | 
 | 				break; | 
 | 			} | 
 | 			l = &a->hash; | 
 | 		} | 
 |  | 
 | 		if (a) { | 
 | 			/* take out of re-transmit chain */ | 
 | 			l = &arp->rxmt; | 
 | 			for (fl = *l; fl; fl = fl->nextrxt) { | 
 | 				if (fl == a) { | 
 | 					*l = a->nextrxt; | 
 | 					break; | 
 | 				} | 
 | 				l = &fl->nextrxt; | 
 | 			} | 
 |  | 
 | 			a->nextrxt = NULL; | 
 | 			a->hash = NULL; | 
 | 			a->hold = NULL; | 
 | 			a->last = NULL; | 
 | 			a->ifc = NULL; | 
 | 			memset(a->ip, 0, sizeof(a->ip)); | 
 | 			memset(a->mac, 0, sizeof(a->mac)); | 
 | 		} | 
 | 		qunlock(&arp->qlock); | 
 | 	} else | 
 | 		error(EINVAL, ERROR_FIXME); | 
 |  | 
 | 	return len; | 
 | } | 
 |  | 
 | enum { | 
 | 	Alinelen = 90, | 
 | }; | 
 |  | 
 | static char *aformat = "%-6.6s %-8.8s %-40.40I %E\n"; | 
 |  | 
 | int arpread(struct arp *arp, char *p, uint32_t offset, int len) | 
 | { | 
 | 	struct arpent *a; | 
 | 	int n; | 
 | 	int left = len; | 
 | 	int amt; | 
 |  | 
 | 	if (offset % Alinelen) | 
 | 		return 0; | 
 |  | 
 | 	offset = offset / Alinelen; | 
 | 	len = len / Alinelen; | 
 |  | 
 | 	n = 0; | 
 | 	for (a = arp->cache; len > 0 && a < &arp->cache[NCACHE]; a++) { | 
 | 		if (a->state == 0) | 
 | 			continue; | 
 | 		if (offset > 0) { | 
 | 			offset--; | 
 | 			continue; | 
 | 		} | 
 | 		len--; | 
 | 		left--; | 
 | 		qlock(&arp->qlock); | 
 | 		amt = snprintf(p + n, left, aformat, a->type->name, arpstate[a->state], | 
 | 		               a->ip, a->mac); | 
 | 		n += amt; | 
 | 		left -= amt; | 
 | 		qunlock(&arp->qlock); | 
 | 	} | 
 |  | 
 | 	return n; | 
 | } | 
 |  | 
 | static uint64_t rxmitsols(struct arp *arp) | 
 | { | 
 | 	unsigned int sflag; | 
 | 	struct block *next, *xp; | 
 | 	struct arpent *a, *b, **l; | 
 | 	struct Fs *f; | 
 | 	uint8_t ipsrc[IPaddrlen]; | 
 | 	struct Ipifc *ifc = NULL; | 
 | 	uint64_t nrxt; | 
 |  | 
 | 	qlock(&arp->qlock); | 
 | 	f = arp->f; | 
 |  | 
 | 	a = arp->rxmt; | 
 | 	if (a == NULL) { | 
 | 		nrxt = 0; | 
 | 		goto dodrops;	/* return nrxt; */ | 
 | 	} | 
 | 	nrxt = a->rtime - NOW; | 
 | 	if (nrxt > 3 * ReTransTimer / 4) | 
 | 		goto dodrops;	/* return nrxt; */ | 
 |  | 
 | 	for (; a; a = a->nextrxt) { | 
 | 		ifc = a->ifc; | 
 | 		assert(ifc != NULL); | 
 | 		if ((a->rxtsrem <= 0) || !(canrlock(&ifc->rwlock)) | 
 | 			|| (a->ifcid != ifc->ifcid)) { | 
 | 			xp = a->hold; | 
 | 			a->hold = NULL; | 
 |  | 
 | 			if (xp) { | 
 | 				if (arp->dropl == NULL) | 
 | 					arp->dropf = xp; | 
 | 				else | 
 | 					arp->dropl->list = xp; | 
 | 			} | 
 |  | 
 | 			cleanarpent(arp, a); | 
 | 		} else | 
 | 			break; | 
 | 	} | 
 | 	if (a == NULL) | 
 | 		goto dodrops; | 
 |  | 
 | 	qunlock(&arp->qlock);	/* for icmpns */ | 
 | 	if ((sflag = ipv6anylocal(ifc, ipsrc)) != SRC_UNSPEC) | 
 | 		icmpns(f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac); | 
 |  | 
 | 	runlock(&ifc->rwlock); | 
 | 	qlock(&arp->qlock); | 
 |  | 
 | 	/* put to the end of re-transmit chain */ | 
 | 	l = &arp->rxmt; | 
 | 	for (b = *l; b; b = b->nextrxt) { | 
 | 		if (b == a) { | 
 | 			*l = a->nextrxt; | 
 | 			break; | 
 | 		} | 
 | 		l = &b->nextrxt; | 
 | 	} | 
 | 	for (b = *l; b; b = b->nextrxt) { | 
 | 		l = &b->nextrxt; | 
 | 	} | 
 | 	*l = a; | 
 | 	a->rxtsrem--; | 
 | 	a->nextrxt = NULL; | 
 | 	a->rtime = NOW + ReTransTimer; | 
 |  | 
 | 	a = arp->rxmt; | 
 | 	if (a == NULL) | 
 | 		nrxt = 0; | 
 | 	else | 
 | 		nrxt = a->rtime - NOW; | 
 |  | 
 | dodrops: | 
 | 	xp = arp->dropf; | 
 | 	arp->dropf = NULL; | 
 | 	arp->dropl = NULL; | 
 | 	qunlock(&arp->qlock); | 
 |  | 
 | 	for (; xp; xp = next) { | 
 | 		next = xp->list; | 
 | 		icmphostunr(f, ifc, xp, icmp6_adr_unreach, 1); | 
 | 	} | 
 |  | 
 | 	return nrxt; | 
 |  | 
 | } | 
 |  | 
 | static int rxready(void *v) | 
 | { | 
 | 	struct arp *arp = (struct arp *)v; | 
 | 	int x; | 
 |  | 
 | 	x = ((arp->rxmt != NULL) || (arp->dropf != NULL)); | 
 |  | 
 | 	return x; | 
 | } | 
 |  | 
 | static void rxmitproc(void *v) | 
 | { | 
 | 	ERRSTACK(2); | 
 | 	struct arp *arp = v; | 
 | 	uint64_t wakeupat; | 
 |  | 
 | 	arp->rxmitp = current; | 
 | 	if (waserror()) { | 
 | 		arp->rxmitp = 0; | 
 | 		poperror(); | 
 | 		warn("arp rxmit ktask exited"); | 
 | 		return; | 
 | 	} | 
 | 	for (;;) { | 
 | 		wakeupat = rxmitsols(arp); | 
 | 		if (wakeupat == 0) | 
 | 			rendez_sleep(&arp->rxmtq, rxready, v); | 
 | 		else if (wakeupat > ReTransTimer / 4) | 
 | 			kthread_usleep(wakeupat * 1000); | 
 | 	} | 
 | 	poperror(); | 
 | } |