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