Blame view

net/link_local.c 8.44 KB
d22c338e0   Joe Hershberger   net: Add link-loc...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  /*
   * RFC3927 ZeroConf IPv4 Link-Local addressing
   * (see <http://www.zeroconf.org/>)
   *
   * Copied from BusyBox - networking/zcip.c
   *
   * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
   * Copyright (C) 2004 by David Brownell
   * Copyright (C) 2010 by Joe Hershberger
   *
   * Licensed under the GPL v2 or later
   */
  
  #include <common.h>
  #include <net.h>
  #include "arp.h"
  #include "net_rand.h"
  
  /* We don't need more than 32 bits of the counter */
  #define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ))
  
  enum {
  /* 169.254.0.0 */
  	LINKLOCAL_ADDR = 0xa9fe0000,
  
  	IN_CLASSB_NET = 0xffff0000,
  	IN_CLASSB_HOST = 0x0000ffff,
  
  /* protocol timeout parameters, specified in seconds */
  	PROBE_WAIT = 1,
  	PROBE_MIN = 1,
  	PROBE_MAX = 2,
  	PROBE_NUM = 3,
  	MAX_CONFLICTS = 10,
  	RATE_LIMIT_INTERVAL = 60,
  	ANNOUNCE_WAIT = 2,
  	ANNOUNCE_NUM = 2,
  	ANNOUNCE_INTERVAL = 2,
  	DEFEND_INTERVAL = 10
  };
  
  /* States during the configuration process. */
  static enum ll_state_t {
  	PROBE = 0,
  	RATE_LIMIT_PROBE,
  	ANNOUNCE,
  	MONITOR,
  	DEFEND,
  	DISABLED
  } state = DISABLED;
049a95a77   Joe Hershberger   net: cosmetic: Ch...
51
  static struct in_addr ip;
d22c338e0   Joe Hershberger   net: Add link-loc...
52
53
54
55
56
57
  static int timeout_ms = -1;
  static unsigned deadline_ms;
  static unsigned conflicts;
  static unsigned nprobes;
  static unsigned nclaims;
  static int ready;
99e139d59   Michael Walle   net: use common r...
58
  static unsigned int seed;
d22c338e0   Joe Hershberger   net: Add link-loc...
59
60
61
62
63
64
65
  
  static void link_local_timeout(void);
  
  /**
   * Pick a random link local IP address on 169.254/16, except that
   * the first and last 256 addresses are reserved.
   */
049a95a77   Joe Hershberger   net: cosmetic: Ch...
66
  static struct in_addr pick(void)
d22c338e0   Joe Hershberger   net: Add link-loc...
67
68
  {
  	unsigned tmp;
049a95a77   Joe Hershberger   net: cosmetic: Ch...
69
  	struct in_addr ip;
d22c338e0   Joe Hershberger   net: Add link-loc...
70
71
  
  	do {
99e139d59   Michael Walle   net: use common r...
72
  		tmp = rand_r(&seed) & IN_CLASSB_HOST;
d22c338e0   Joe Hershberger   net: Add link-loc...
73
  	} while (tmp > (IN_CLASSB_HOST - 0x0200));
049a95a77   Joe Hershberger   net: cosmetic: Ch...
74
75
  	ip.s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
  	return ip;
d22c338e0   Joe Hershberger   net: Add link-loc...
76
77
78
79
80
81
82
  }
  
  /**
   * Return milliseconds of random delay, up to "secs" seconds.
   */
  static inline unsigned random_delay_ms(unsigned secs)
  {
99e139d59   Michael Walle   net: use common r...
83
  	return rand_r(&seed) % (secs * 1000);
d22c338e0   Joe Hershberger   net: Add link-loc...
84
85
86
87
88
89
90
91
92
93
94
95
96
  }
  
  static void configure_wait(void)
  {
  	if (timeout_ms == -1)
  		return;
  
  	/* poll, being ready to adjust current timeout */
  	if (!timeout_ms)
  		timeout_ms = random_delay_ms(PROBE_WAIT);
  
  	/* set deadline_ms to the point in time when we timeout */
  	deadline_ms = MONOTONIC_MS() + timeout_ms;
4ef8d53ca   Joe Hershberger   net: Allow filter...
97
98
  	debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
99
  		   timeout_ms, eth_get_name(), nprobes, nclaims);
d22c338e0   Joe Hershberger   net: Add link-loc...
100

bc0571fc1   Joe Hershberger   net: cosmetic: Fi...
101
  	net_set_timeout_handler(timeout_ms, link_local_timeout);
d22c338e0   Joe Hershberger   net: Add link-loc...
102
103
104
105
  }
  
  void link_local_start(void)
  {
723806cc5   Simon Glass   env: Rename some ...
106
  	ip = env_get_ip("llipaddr");
049a95a77   Joe Hershberger   net: cosmetic: Ch...
107
108
  	if (ip.s_addr != 0 &&
  	    (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) {
d22c338e0   Joe Hershberger   net: Add link-loc...
109
110
111
112
  		puts("invalid link address");
  		net_set_state(NETLOOP_FAIL);
  		return;
  	}
27a0f038a   Alexandre Messier   net: link_local: ...
113
  	net_netmask.s_addr = htonl(IN_CLASSB_NET);
d22c338e0   Joe Hershberger   net: Add link-loc...
114

99e139d59   Michael Walle   net: use common r...
115
  	seed = seed_mac();
049a95a77   Joe Hershberger   net: cosmetic: Ch...
116
  	if (ip.s_addr == 0)
d22c338e0   Joe Hershberger   net: Add link-loc...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  		ip = pick();
  
  	state = PROBE;
  	timeout_ms = 0;
  	conflicts = 0;
  	nprobes = 0;
  	nclaims = 0;
  	ready = 0;
  
  	configure_wait();
  }
  
  static void link_local_timeout(void)
  {
  	switch (state) {
  	case PROBE:
  		/* timeouts in the PROBE state mean no conflicting ARP packets
  		   have been received, so we can progress through the states */
  		if (nprobes < PROBE_NUM) {
049a95a77   Joe Hershberger   net: cosmetic: Ch...
136
  			struct in_addr zero_ip = {.s_addr = 0};
d22c338e0   Joe Hershberger   net: Add link-loc...
137
  			nprobes++;
4ef8d53ca   Joe Hershberger   net: Allow filter...
138
139
  			debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
140
  				   nprobes, eth_get_name(), &ip);
0adb5b761   Joe Hershberger   net: cosmetic: Na...
141
  			arp_raw_request(zero_ip, net_null_ethaddr, ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
142
143
144
145
146
147
  			timeout_ms = PROBE_MIN * 1000;
  			timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
  		} else {
  			/* Switch to announce state */
  			state = ANNOUNCE;
  			nclaims = 0;
4ef8d53ca   Joe Hershberger   net: Allow filter...
148
149
  			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
150
  				   nclaims, eth_get_name(), &ip);
0adb5b761   Joe Hershberger   net: cosmetic: Na...
151
  			arp_raw_request(ip, net_ethaddr, ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
152
153
154
155
156
157
158
159
160
  			timeout_ms = ANNOUNCE_INTERVAL * 1000;
  		}
  		break;
  	case RATE_LIMIT_PROBE:
  		/* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
  		   ARP packets have been received, so we can move immediately
  		   to the announce state */
  		state = ANNOUNCE;
  		nclaims = 0;
4ef8d53ca   Joe Hershberger   net: Allow filter...
161
162
  		debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
163
  			   nclaims, eth_get_name(), &ip);
0adb5b761   Joe Hershberger   net: cosmetic: Na...
164
  		arp_raw_request(ip, net_ethaddr, ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
165
166
167
168
169
170
171
172
  		timeout_ms = ANNOUNCE_INTERVAL * 1000;
  		break;
  	case ANNOUNCE:
  		/* timeouts in the ANNOUNCE state mean no conflicting ARP
  		   packets have been received, so we can progress through
  		   the states */
  		if (nclaims < ANNOUNCE_NUM) {
  			nclaims++;
4ef8d53ca   Joe Hershberger   net: Allow filter...
173
174
  			debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
175
  				   nclaims, eth_get_name(), &ip);
0adb5b761   Joe Hershberger   net: cosmetic: Na...
176
  			arp_raw_request(ip, net_ethaddr, ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
177
178
179
180
181
182
  			timeout_ms = ANNOUNCE_INTERVAL * 1000;
  		} else {
  			/* Switch to monitor state */
  			state = MONITOR;
  			printf("Successfully assigned %pI4
  ", &ip);
049a95a77   Joe Hershberger   net: cosmetic: Ch...
183
  			net_copy_ip(&net_ip, &ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
184
185
186
187
  			ready = 1;
  			conflicts = 0;
  			timeout_ms = -1;
  			/* Never timeout in the monitor state */
bc0571fc1   Joe Hershberger   net: cosmetic: Fi...
188
  			net_set_timeout_handler(0, NULL);
d22c338e0   Joe Hershberger   net: Add link-loc...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  
  			/* NOTE: all other exit paths should deconfig ... */
  			net_set_state(NETLOOP_SUCCESS);
  			return;
  		}
  		break;
  	case DEFEND:
  		/* We won!  No ARP replies, so just go back to monitor */
  		state = MONITOR;
  		timeout_ms = -1;
  		conflicts = 0;
  		break;
  	default:
  		/* Invalid, should never happen.  Restart the whole protocol */
  		state = PROBE;
  		ip = pick();
  		timeout_ms = 0;
  		nprobes = 0;
  		nclaims = 0;
  		break;
  	}
  	configure_wait();
  }
  
  void link_local_receive_arp(struct arp_hdr *arp, int len)
  {
  	int source_ip_conflict;
  	int target_ip_conflict;
049a95a77   Joe Hershberger   net: cosmetic: Ch...
217
  	struct in_addr null_ip = {.s_addr = 0};
d22c338e0   Joe Hershberger   net: Add link-loc...
218
219
220
221
222
223
224
225
226
227
228
  
  	if (state == DISABLED)
  		return;
  
  	/* We need to adjust the timeout in case we didn't receive a
  	   conflicting packet. */
  	if (timeout_ms > 0) {
  		unsigned diff = deadline_ms - MONOTONIC_MS();
  		if ((int)(diff) < 0) {
  			/* Current time is greater than the expected timeout
  			   time. This should never happen */
4ef8d53ca   Joe Hershberger   net: Allow filter...
229
  			debug_cond(DEBUG_LL_STATE,
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
230
231
  				   "missed an expected timeout
  ");
d22c338e0   Joe Hershberger   net: Add link-loc...
232
233
  			timeout_ms = 0;
  		} else {
4ef8d53ca   Joe Hershberger   net: Allow filter...
234
235
  			debug_cond(DEBUG_INT_STATE, "adjusting timeout
  ");
d22c338e0   Joe Hershberger   net: Add link-loc...
236
237
238
  			timeout_ms = diff | 1; /* never 0 */
  		}
  	}
b68411579   benoit.thebaudeau@advans   net: link_local: ...
239
240
  #if 0
   /* XXX Don't bother with ethernet link just yet */
d22c338e0   Joe Hershberger   net: Add link-loc...
241
242
  	if ((fds[0].revents & POLLIN) == 0) {
  		if (fds[0].revents & POLLERR) {
3fe63839f   Wolfgang Denk   Minor Coding Styl...
243
244
245
  			/*
  			 * FIXME: links routinely go down;
  			 */
d22c338e0   Joe Hershberger   net: Add link-loc...
246
  			bb_error_msg("iface %s is down", eth_get_name());
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
247
  			if (ready)
d22c338e0   Joe Hershberger   net: Add link-loc...
248
  				run(argv, "deconfig", &ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
249
250
251
252
  			return EXIT_FAILURE;
  		}
  		continue;
  	}
b68411579   benoit.thebaudeau@advans   net: link_local: ...
253
  #endif
d22c338e0   Joe Hershberger   net: Add link-loc...
254

4ef8d53ca   Joe Hershberger   net: Allow filter...
255
256
  	debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d,
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
257
258
  		   eth_get_name(), ntohs(arp->ar_pro),
  		   ntohs(arp->ar_op));
4ef8d53ca   Joe Hershberger   net: Allow filter...
259
260
  	debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
261
262
  		   &arp->ar_sha,
  		   &arp->ar_spa);
4ef8d53ca   Joe Hershberger   net: Allow filter...
263
264
  	debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4
  ",
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
265
266
  		   &arp->ar_tha,
  		   &arp->ar_tpa);
d22c338e0   Joe Hershberger   net: Add link-loc...
267

8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
268
269
  	if (arp->ar_op != htons(ARPOP_REQUEST) &&
  	    arp->ar_op != htons(ARPOP_REPLY)) {
d22c338e0   Joe Hershberger   net: Add link-loc...
270
271
272
273
274
275
  		configure_wait();
  		return;
  	}
  
  	source_ip_conflict = 0;
  	target_ip_conflict = 0;
0adb5b761   Joe Hershberger   net: cosmetic: Na...
276
277
  	if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 &&
  	    memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0)
d22c338e0   Joe Hershberger   net: Add link-loc...
278
  		source_ip_conflict = 1;
5da7cf81c   Joe Hershberger   net: Correct chec...
279
280
281
282
283
284
285
286
287
288
289
  
  	/*
  	 * According to RFC 3927, section 2.2.1:
  	 * Check if packet is an ARP probe by checking for a null source IP
  	 * then check that target IP is equal to ours and source hw addr
  	 * is not equal to ours. This condition should cause a conflict only
  	 * during probe.
  	 */
  	if (arp->ar_op == htons(ARPOP_REQUEST) &&
  	    memcmp(&arp->ar_spa, &null_ip, ARP_PLEN) == 0 &&
  	    memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 &&
0adb5b761   Joe Hershberger   net: cosmetic: Na...
290
  	    memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) {
d22c338e0   Joe Hershberger   net: Add link-loc...
291
292
  		target_ip_conflict = 1;
  	}
4ef8d53ca   Joe Hershberger   net: Allow filter...
293
  	debug_cond(DEBUG_NET_PKT,
8e7ff6773   Joe Hershberger   net: cosmetic: Fi...
294
295
296
  		   "state = %d, source ip conflict = %d, target ip conflict = "
  		   "%d
  ", state, source_ip_conflict, target_ip_conflict);
d22c338e0   Joe Hershberger   net: Add link-loc...
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  	switch (state) {
  	case PROBE:
  	case ANNOUNCE:
  		/* When probing or announcing, check for source IP conflicts
  		   and other hosts doing ARP probes (target IP conflicts). */
  		if (source_ip_conflict || target_ip_conflict) {
  			conflicts++;
  			state = PROBE;
  			if (conflicts >= MAX_CONFLICTS) {
  				debug("%s ratelimit
  ", eth_get_name());
  				timeout_ms = RATE_LIMIT_INTERVAL * 1000;
  				state = RATE_LIMIT_PROBE;
  			}
  
  			/* restart the whole protocol */
  			ip = pick();
  			timeout_ms = 0;
  			nprobes = 0;
  			nclaims = 0;
  		}
  		break;
  	case MONITOR:
  		/* If a conflict, we try to defend with a single ARP probe */
  		if (source_ip_conflict) {
  			debug("monitor conflict -- defending
  ");
  			state = DEFEND;
  			timeout_ms = DEFEND_INTERVAL * 1000;
0adb5b761   Joe Hershberger   net: cosmetic: Na...
326
  			arp_raw_request(ip, net_ethaddr, ip);
d22c338e0   Joe Hershberger   net: Add link-loc...
327
328
329
330
331
332
333
334
335
  		}
  		break;
  	case DEFEND:
  		/* Well, we tried.  Start over (on conflict) */
  		if (source_ip_conflict) {
  			state = PROBE;
  			debug("defend conflict -- starting over
  ");
  			ready = 0;
049a95a77   Joe Hershberger   net: cosmetic: Ch...
336
  			net_ip.s_addr = 0;
d22c338e0   Joe Hershberger   net: Add link-loc...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  
  			/* restart the whole protocol */
  			ip = pick();
  			timeout_ms = 0;
  			nprobes = 0;
  			nclaims = 0;
  		}
  		break;
  	default:
  		/* Invalid, should never happen.  Restart the whole protocol */
  		debug("invalid state -- starting over
  ");
  		state = PROBE;
  		ip = pick();
  		timeout_ms = 0;
  		nprobes = 0;
  		nclaims = 0;
  		break;
  	}
  	configure_wait();
  }