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