xref: /OK3568_Linux_fs/u-boot/net/link_local.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * RFC3927 ZeroConf IPv4 Link-Local addressing
3*4882a593Smuzhiyun  * (see <http://www.zeroconf.org/>)
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copied from BusyBox - networking/zcip.c
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
8*4882a593Smuzhiyun  * Copyright (C) 2004 by David Brownell
9*4882a593Smuzhiyun  * Copyright (C) 2010 by Joe Hershberger
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * Licensed under the GPL v2 or later
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <common.h>
15*4882a593Smuzhiyun #include <net.h>
16*4882a593Smuzhiyun #include "arp.h"
17*4882a593Smuzhiyun #include "net_rand.h"
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /* We don't need more than 32 bits of the counter */
20*4882a593Smuzhiyun #define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ))
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun enum {
23*4882a593Smuzhiyun /* 169.254.0.0 */
24*4882a593Smuzhiyun 	LINKLOCAL_ADDR = 0xa9fe0000,
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	IN_CLASSB_NET = 0xffff0000,
27*4882a593Smuzhiyun 	IN_CLASSB_HOST = 0x0000ffff,
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun /* protocol timeout parameters, specified in seconds */
30*4882a593Smuzhiyun 	PROBE_WAIT = 1,
31*4882a593Smuzhiyun 	PROBE_MIN = 1,
32*4882a593Smuzhiyun 	PROBE_MAX = 2,
33*4882a593Smuzhiyun 	PROBE_NUM = 3,
34*4882a593Smuzhiyun 	MAX_CONFLICTS = 10,
35*4882a593Smuzhiyun 	RATE_LIMIT_INTERVAL = 60,
36*4882a593Smuzhiyun 	ANNOUNCE_WAIT = 2,
37*4882a593Smuzhiyun 	ANNOUNCE_NUM = 2,
38*4882a593Smuzhiyun 	ANNOUNCE_INTERVAL = 2,
39*4882a593Smuzhiyun 	DEFEND_INTERVAL = 10
40*4882a593Smuzhiyun };
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /* States during the configuration process. */
43*4882a593Smuzhiyun static enum ll_state_t {
44*4882a593Smuzhiyun 	PROBE = 0,
45*4882a593Smuzhiyun 	RATE_LIMIT_PROBE,
46*4882a593Smuzhiyun 	ANNOUNCE,
47*4882a593Smuzhiyun 	MONITOR,
48*4882a593Smuzhiyun 	DEFEND,
49*4882a593Smuzhiyun 	DISABLED
50*4882a593Smuzhiyun } state = DISABLED;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun static struct in_addr ip;
53*4882a593Smuzhiyun static int timeout_ms = -1;
54*4882a593Smuzhiyun static unsigned deadline_ms;
55*4882a593Smuzhiyun static unsigned conflicts;
56*4882a593Smuzhiyun static unsigned nprobes;
57*4882a593Smuzhiyun static unsigned nclaims;
58*4882a593Smuzhiyun static int ready;
59*4882a593Smuzhiyun static unsigned int seed;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun static void link_local_timeout(void);
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun /**
64*4882a593Smuzhiyun  * Pick a random link local IP address on 169.254/16, except that
65*4882a593Smuzhiyun  * the first and last 256 addresses are reserved.
66*4882a593Smuzhiyun  */
pick(void)67*4882a593Smuzhiyun static struct in_addr pick(void)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	unsigned tmp;
70*4882a593Smuzhiyun 	struct in_addr ip;
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun 	do {
73*4882a593Smuzhiyun 		tmp = rand_r(&seed) & IN_CLASSB_HOST;
74*4882a593Smuzhiyun 	} while (tmp > (IN_CLASSB_HOST - 0x0200));
75*4882a593Smuzhiyun 	ip.s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
76*4882a593Smuzhiyun 	return ip;
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /**
80*4882a593Smuzhiyun  * Return milliseconds of random delay, up to "secs" seconds.
81*4882a593Smuzhiyun  */
random_delay_ms(unsigned secs)82*4882a593Smuzhiyun static inline unsigned random_delay_ms(unsigned secs)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun 	return rand_r(&seed) % (secs * 1000);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
configure_wait(void)87*4882a593Smuzhiyun static void configure_wait(void)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	if (timeout_ms == -1)
90*4882a593Smuzhiyun 		return;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	/* poll, being ready to adjust current timeout */
93*4882a593Smuzhiyun 	if (!timeout_ms)
94*4882a593Smuzhiyun 		timeout_ms = random_delay_ms(PROBE_WAIT);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	/* set deadline_ms to the point in time when we timeout */
97*4882a593Smuzhiyun 	deadline_ms = MONOTONIC_MS() + timeout_ms;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u\n",
100*4882a593Smuzhiyun 		   timeout_ms, eth_get_name(), nprobes, nclaims);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	net_set_timeout_handler(timeout_ms, link_local_timeout);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
link_local_start(void)105*4882a593Smuzhiyun void link_local_start(void)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	ip = env_get_ip("llipaddr");
108*4882a593Smuzhiyun 	if (ip.s_addr != 0 &&
109*4882a593Smuzhiyun 	    (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) {
110*4882a593Smuzhiyun 		puts("invalid link address");
111*4882a593Smuzhiyun 		net_set_state(NETLOOP_FAIL);
112*4882a593Smuzhiyun 		return;
113*4882a593Smuzhiyun 	}
114*4882a593Smuzhiyun 	net_netmask.s_addr = htonl(IN_CLASSB_NET);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	seed = seed_mac();
117*4882a593Smuzhiyun 	if (ip.s_addr == 0)
118*4882a593Smuzhiyun 		ip = pick();
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	state = PROBE;
121*4882a593Smuzhiyun 	timeout_ms = 0;
122*4882a593Smuzhiyun 	conflicts = 0;
123*4882a593Smuzhiyun 	nprobes = 0;
124*4882a593Smuzhiyun 	nclaims = 0;
125*4882a593Smuzhiyun 	ready = 0;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	configure_wait();
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
link_local_timeout(void)130*4882a593Smuzhiyun static void link_local_timeout(void)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	switch (state) {
133*4882a593Smuzhiyun 	case PROBE:
134*4882a593Smuzhiyun 		/* timeouts in the PROBE state mean no conflicting ARP packets
135*4882a593Smuzhiyun 		   have been received, so we can progress through the states */
136*4882a593Smuzhiyun 		if (nprobes < PROBE_NUM) {
137*4882a593Smuzhiyun 			struct in_addr zero_ip = {.s_addr = 0};
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 			nprobes++;
140*4882a593Smuzhiyun 			debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4\n",
141*4882a593Smuzhiyun 				   nprobes, eth_get_name(), &ip);
142*4882a593Smuzhiyun 			arp_raw_request(zero_ip, net_null_ethaddr, ip);
143*4882a593Smuzhiyun 			timeout_ms = PROBE_MIN * 1000;
144*4882a593Smuzhiyun 			timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
145*4882a593Smuzhiyun 		} else {
146*4882a593Smuzhiyun 			/* Switch to announce state */
147*4882a593Smuzhiyun 			state = ANNOUNCE;
148*4882a593Smuzhiyun 			nclaims = 0;
149*4882a593Smuzhiyun 			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
150*4882a593Smuzhiyun 				   nclaims, eth_get_name(), &ip);
151*4882a593Smuzhiyun 			arp_raw_request(ip, net_ethaddr, ip);
152*4882a593Smuzhiyun 			timeout_ms = ANNOUNCE_INTERVAL * 1000;
153*4882a593Smuzhiyun 		}
154*4882a593Smuzhiyun 		break;
155*4882a593Smuzhiyun 	case RATE_LIMIT_PROBE:
156*4882a593Smuzhiyun 		/* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
157*4882a593Smuzhiyun 		   ARP packets have been received, so we can move immediately
158*4882a593Smuzhiyun 		   to the announce state */
159*4882a593Smuzhiyun 		state = ANNOUNCE;
160*4882a593Smuzhiyun 		nclaims = 0;
161*4882a593Smuzhiyun 		debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
162*4882a593Smuzhiyun 			   nclaims, eth_get_name(), &ip);
163*4882a593Smuzhiyun 		arp_raw_request(ip, net_ethaddr, ip);
164*4882a593Smuzhiyun 		timeout_ms = ANNOUNCE_INTERVAL * 1000;
165*4882a593Smuzhiyun 		break;
166*4882a593Smuzhiyun 	case ANNOUNCE:
167*4882a593Smuzhiyun 		/* timeouts in the ANNOUNCE state mean no conflicting ARP
168*4882a593Smuzhiyun 		   packets have been received, so we can progress through
169*4882a593Smuzhiyun 		   the states */
170*4882a593Smuzhiyun 		if (nclaims < ANNOUNCE_NUM) {
171*4882a593Smuzhiyun 			nclaims++;
172*4882a593Smuzhiyun 			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4\n",
173*4882a593Smuzhiyun 				   nclaims, eth_get_name(), &ip);
174*4882a593Smuzhiyun 			arp_raw_request(ip, net_ethaddr, ip);
175*4882a593Smuzhiyun 			timeout_ms = ANNOUNCE_INTERVAL * 1000;
176*4882a593Smuzhiyun 		} else {
177*4882a593Smuzhiyun 			/* Switch to monitor state */
178*4882a593Smuzhiyun 			state = MONITOR;
179*4882a593Smuzhiyun 			printf("Successfully assigned %pI4\n", &ip);
180*4882a593Smuzhiyun 			net_copy_ip(&net_ip, &ip);
181*4882a593Smuzhiyun 			ready = 1;
182*4882a593Smuzhiyun 			conflicts = 0;
183*4882a593Smuzhiyun 			timeout_ms = -1;
184*4882a593Smuzhiyun 			/* Never timeout in the monitor state */
185*4882a593Smuzhiyun 			net_set_timeout_handler(0, NULL);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 			/* NOTE: all other exit paths should deconfig ... */
188*4882a593Smuzhiyun 			net_set_state(NETLOOP_SUCCESS);
189*4882a593Smuzhiyun 			return;
190*4882a593Smuzhiyun 		}
191*4882a593Smuzhiyun 		break;
192*4882a593Smuzhiyun 	case DEFEND:
193*4882a593Smuzhiyun 		/* We won!  No ARP replies, so just go back to monitor */
194*4882a593Smuzhiyun 		state = MONITOR;
195*4882a593Smuzhiyun 		timeout_ms = -1;
196*4882a593Smuzhiyun 		conflicts = 0;
197*4882a593Smuzhiyun 		break;
198*4882a593Smuzhiyun 	default:
199*4882a593Smuzhiyun 		/* Invalid, should never happen.  Restart the whole protocol */
200*4882a593Smuzhiyun 		state = PROBE;
201*4882a593Smuzhiyun 		ip = pick();
202*4882a593Smuzhiyun 		timeout_ms = 0;
203*4882a593Smuzhiyun 		nprobes = 0;
204*4882a593Smuzhiyun 		nclaims = 0;
205*4882a593Smuzhiyun 		break;
206*4882a593Smuzhiyun 	}
207*4882a593Smuzhiyun 	configure_wait();
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun 
link_local_receive_arp(struct arp_hdr * arp,int len)210*4882a593Smuzhiyun void link_local_receive_arp(struct arp_hdr *arp, int len)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	int source_ip_conflict;
213*4882a593Smuzhiyun 	int target_ip_conflict;
214*4882a593Smuzhiyun 	struct in_addr null_ip = {.s_addr = 0};
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	if (state == DISABLED)
217*4882a593Smuzhiyun 		return;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	/* We need to adjust the timeout in case we didn't receive a
220*4882a593Smuzhiyun 	   conflicting packet. */
221*4882a593Smuzhiyun 	if (timeout_ms > 0) {
222*4882a593Smuzhiyun 		unsigned diff = deadline_ms - MONOTONIC_MS();
223*4882a593Smuzhiyun 		if ((int)(diff) < 0) {
224*4882a593Smuzhiyun 			/* Current time is greater than the expected timeout
225*4882a593Smuzhiyun 			   time. This should never happen */
226*4882a593Smuzhiyun 			debug_cond(DEBUG_LL_STATE,
227*4882a593Smuzhiyun 				   "missed an expected timeout\n");
228*4882a593Smuzhiyun 			timeout_ms = 0;
229*4882a593Smuzhiyun 		} else {
230*4882a593Smuzhiyun 			debug_cond(DEBUG_INT_STATE, "adjusting timeout\n");
231*4882a593Smuzhiyun 			timeout_ms = diff | 1; /* never 0 */
232*4882a593Smuzhiyun 		}
233*4882a593Smuzhiyun 	}
234*4882a593Smuzhiyun #if 0
235*4882a593Smuzhiyun  /* XXX Don't bother with ethernet link just yet */
236*4882a593Smuzhiyun 	if ((fds[0].revents & POLLIN) == 0) {
237*4882a593Smuzhiyun 		if (fds[0].revents & POLLERR) {
238*4882a593Smuzhiyun 			/*
239*4882a593Smuzhiyun 			 * FIXME: links routinely go down;
240*4882a593Smuzhiyun 			 */
241*4882a593Smuzhiyun 			bb_error_msg("iface %s is down", eth_get_name());
242*4882a593Smuzhiyun 			if (ready)
243*4882a593Smuzhiyun 				run(argv, "deconfig", &ip);
244*4882a593Smuzhiyun 			return EXIT_FAILURE;
245*4882a593Smuzhiyun 		}
246*4882a593Smuzhiyun 		continue;
247*4882a593Smuzhiyun 	}
248*4882a593Smuzhiyun #endif
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,\n",
251*4882a593Smuzhiyun 		   eth_get_name(), ntohs(arp->ar_pro),
252*4882a593Smuzhiyun 		   ntohs(arp->ar_op));
253*4882a593Smuzhiyun 	debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4\n",
254*4882a593Smuzhiyun 		   &arp->ar_sha,
255*4882a593Smuzhiyun 		   &arp->ar_spa);
256*4882a593Smuzhiyun 	debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4\n",
257*4882a593Smuzhiyun 		   &arp->ar_tha,
258*4882a593Smuzhiyun 		   &arp->ar_tpa);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	if (arp->ar_op != htons(ARPOP_REQUEST) &&
261*4882a593Smuzhiyun 	    arp->ar_op != htons(ARPOP_REPLY)) {
262*4882a593Smuzhiyun 		configure_wait();
263*4882a593Smuzhiyun 		return;
264*4882a593Smuzhiyun 	}
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	source_ip_conflict = 0;
267*4882a593Smuzhiyun 	target_ip_conflict = 0;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 &&
270*4882a593Smuzhiyun 	    memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0)
271*4882a593Smuzhiyun 		source_ip_conflict = 1;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	/*
274*4882a593Smuzhiyun 	 * According to RFC 3927, section 2.2.1:
275*4882a593Smuzhiyun 	 * Check if packet is an ARP probe by checking for a null source IP
276*4882a593Smuzhiyun 	 * then check that target IP is equal to ours and source hw addr
277*4882a593Smuzhiyun 	 * is not equal to ours. This condition should cause a conflict only
278*4882a593Smuzhiyun 	 * during probe.
279*4882a593Smuzhiyun 	 */
280*4882a593Smuzhiyun 	if (arp->ar_op == htons(ARPOP_REQUEST) &&
281*4882a593Smuzhiyun 	    memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 &&
282*4882a593Smuzhiyun 	    memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 &&
283*4882a593Smuzhiyun 	    memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) {
284*4882a593Smuzhiyun 		target_ip_conflict = 1;
285*4882a593Smuzhiyun 	}
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 	debug_cond(DEBUG_NET_PKT,
288*4882a593Smuzhiyun 		   "state = %d, source ip conflict = %d, target ip conflict = "
289*4882a593Smuzhiyun 		   "%d\n", state, source_ip_conflict, target_ip_conflict);
290*4882a593Smuzhiyun 	switch (state) {
291*4882a593Smuzhiyun 	case PROBE:
292*4882a593Smuzhiyun 	case ANNOUNCE:
293*4882a593Smuzhiyun 		/* When probing or announcing, check for source IP conflicts
294*4882a593Smuzhiyun 		   and other hosts doing ARP probes (target IP conflicts). */
295*4882a593Smuzhiyun 		if (source_ip_conflict || target_ip_conflict) {
296*4882a593Smuzhiyun 			conflicts++;
297*4882a593Smuzhiyun 			state = PROBE;
298*4882a593Smuzhiyun 			if (conflicts >= MAX_CONFLICTS) {
299*4882a593Smuzhiyun 				debug("%s ratelimit\n", eth_get_name());
300*4882a593Smuzhiyun 				timeout_ms = RATE_LIMIT_INTERVAL * 1000;
301*4882a593Smuzhiyun 				state = RATE_LIMIT_PROBE;
302*4882a593Smuzhiyun 			}
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 			/* restart the whole protocol */
305*4882a593Smuzhiyun 			ip = pick();
306*4882a593Smuzhiyun 			timeout_ms = 0;
307*4882a593Smuzhiyun 			nprobes = 0;
308*4882a593Smuzhiyun 			nclaims = 0;
309*4882a593Smuzhiyun 		}
310*4882a593Smuzhiyun 		break;
311*4882a593Smuzhiyun 	case MONITOR:
312*4882a593Smuzhiyun 		/* If a conflict, we try to defend with a single ARP probe */
313*4882a593Smuzhiyun 		if (source_ip_conflict) {
314*4882a593Smuzhiyun 			debug("monitor conflict -- defending\n");
315*4882a593Smuzhiyun 			state = DEFEND;
316*4882a593Smuzhiyun 			timeout_ms = DEFEND_INTERVAL * 1000;
317*4882a593Smuzhiyun 			arp_raw_request(ip, net_ethaddr, ip);
318*4882a593Smuzhiyun 		}
319*4882a593Smuzhiyun 		break;
320*4882a593Smuzhiyun 	case DEFEND:
321*4882a593Smuzhiyun 		/* Well, we tried.  Start over (on conflict) */
322*4882a593Smuzhiyun 		if (source_ip_conflict) {
323*4882a593Smuzhiyun 			state = PROBE;
324*4882a593Smuzhiyun 			debug("defend conflict -- starting over\n");
325*4882a593Smuzhiyun 			ready = 0;
326*4882a593Smuzhiyun 			net_ip.s_addr = 0;
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 			/* restart the whole protocol */
329*4882a593Smuzhiyun 			ip = pick();
330*4882a593Smuzhiyun 			timeout_ms = 0;
331*4882a593Smuzhiyun 			nprobes = 0;
332*4882a593Smuzhiyun 			nclaims = 0;
333*4882a593Smuzhiyun 		}
334*4882a593Smuzhiyun 		break;
335*4882a593Smuzhiyun 	default:
336*4882a593Smuzhiyun 		/* Invalid, should never happen.  Restart the whole protocol */
337*4882a593Smuzhiyun 		debug("invalid state -- starting over\n");
338*4882a593Smuzhiyun 		state = PROBE;
339*4882a593Smuzhiyun 		ip = pick();
340*4882a593Smuzhiyun 		timeout_ms = 0;
341*4882a593Smuzhiyun 		nprobes = 0;
342*4882a593Smuzhiyun 		nclaims = 0;
343*4882a593Smuzhiyun 		break;
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun 	configure_wait();
346*4882a593Smuzhiyun }
347