Blame view
net/link_local.c
8.44 KB
d22c338e0 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 net: cosmetic: Ch... |
51 |
static struct in_addr ip; |
d22c338e0 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 net: use common r... |
58 |
static unsigned int seed; |
d22c338e0 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 net: cosmetic: Ch... |
66 |
static struct in_addr pick(void) |
d22c338e0 net: Add link-loc... |
67 68 |
{ unsigned tmp; |
049a95a77 net: cosmetic: Ch... |
69 |
struct in_addr ip; |
d22c338e0 net: Add link-loc... |
70 71 |
do { |
99e139d59 net: use common r... |
72 |
tmp = rand_r(&seed) & IN_CLASSB_HOST; |
d22c338e0 net: Add link-loc... |
73 |
} while (tmp > (IN_CLASSB_HOST - 0x0200)); |
049a95a77 net: cosmetic: Ch... |
74 75 |
ip.s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp); return ip; |
d22c338e0 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 net: use common r... |
83 |
return rand_r(&seed) % (secs * 1000); |
d22c338e0 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 net: Allow filter... |
97 98 |
debug_cond(DEBUG_DEV_PKT, "...wait %d %s nprobes=%u, nclaims=%u ", |
8e7ff6773 net: cosmetic: Fi... |
99 |
timeout_ms, eth_get_name(), nprobes, nclaims); |
d22c338e0 net: Add link-loc... |
100 |
|
bc0571fc1 net: cosmetic: Fi... |
101 |
net_set_timeout_handler(timeout_ms, link_local_timeout); |
d22c338e0 net: Add link-loc... |
102 103 104 105 |
} void link_local_start(void) { |
723806cc5 env: Rename some ... |
106 |
ip = env_get_ip("llipaddr"); |
049a95a77 net: cosmetic: Ch... |
107 108 |
if (ip.s_addr != 0 && (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR) { |
d22c338e0 net: Add link-loc... |
109 110 111 112 |
puts("invalid link address"); net_set_state(NETLOOP_FAIL); return; } |
27a0f038a net: link_local: ... |
113 |
net_netmask.s_addr = htonl(IN_CLASSB_NET); |
d22c338e0 net: Add link-loc... |
114 |
|
99e139d59 net: use common r... |
115 |
seed = seed_mac(); |
049a95a77 net: cosmetic: Ch... |
116 |
if (ip.s_addr == 0) |
d22c338e0 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 net: cosmetic: Ch... |
136 |
struct in_addr zero_ip = {.s_addr = 0}; |
d22c338e0 net: Add link-loc... |
137 |
nprobes++; |
4ef8d53ca net: Allow filter... |
138 139 |
debug_cond(DEBUG_LL_STATE, "probe/%u %s@%pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
140 |
nprobes, eth_get_name(), &ip); |
0adb5b761 net: cosmetic: Na... |
141 |
arp_raw_request(zero_ip, net_null_ethaddr, ip); |
d22c338e0 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 net: Allow filter... |
148 149 |
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
150 |
nclaims, eth_get_name(), &ip); |
0adb5b761 net: cosmetic: Na... |
151 |
arp_raw_request(ip, net_ethaddr, ip); |
d22c338e0 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 net: Allow filter... |
161 162 |
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
163 |
nclaims, eth_get_name(), &ip); |
0adb5b761 net: cosmetic: Na... |
164 |
arp_raw_request(ip, net_ethaddr, ip); |
d22c338e0 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 net: Allow filter... |
173 174 |
debug_cond(DEBUG_LL_STATE, "announce/%u %s@%pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
175 |
nclaims, eth_get_name(), &ip); |
0adb5b761 net: cosmetic: Na... |
176 |
arp_raw_request(ip, net_ethaddr, ip); |
d22c338e0 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 net: cosmetic: Ch... |
183 |
net_copy_ip(&net_ip, &ip); |
d22c338e0 net: Add link-loc... |
184 185 186 187 |
ready = 1; conflicts = 0; timeout_ms = -1; /* Never timeout in the monitor state */ |
bc0571fc1 net: cosmetic: Fi... |
188 |
net_set_timeout_handler(0, NULL); |
d22c338e0 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 net: cosmetic: Ch... |
217 |
struct in_addr null_ip = {.s_addr = 0}; |
d22c338e0 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 net: Allow filter... |
229 |
debug_cond(DEBUG_LL_STATE, |
8e7ff6773 net: cosmetic: Fi... |
230 231 |
"missed an expected timeout "); |
d22c338e0 net: Add link-loc... |
232 233 |
timeout_ms = 0; } else { |
4ef8d53ca net: Allow filter... |
234 235 |
debug_cond(DEBUG_INT_STATE, "adjusting timeout "); |
d22c338e0 net: Add link-loc... |
236 237 238 |
timeout_ms = diff | 1; /* never 0 */ } } |
b68411579 net: link_local: ... |
239 240 |
#if 0 /* XXX Don't bother with ethernet link just yet */ |
d22c338e0 net: Add link-loc... |
241 242 |
if ((fds[0].revents & POLLIN) == 0) { if (fds[0].revents & POLLERR) { |
3fe63839f Minor Coding Styl... |
243 244 245 |
/* * FIXME: links routinely go down; */ |
d22c338e0 net: Add link-loc... |
246 |
bb_error_msg("iface %s is down", eth_get_name()); |
8e7ff6773 net: cosmetic: Fi... |
247 |
if (ready) |
d22c338e0 net: Add link-loc... |
248 |
run(argv, "deconfig", &ip); |
d22c338e0 net: Add link-loc... |
249 250 251 252 |
return EXIT_FAILURE; } continue; } |
b68411579 net: link_local: ... |
253 |
#endif |
d22c338e0 net: Add link-loc... |
254 |
|
4ef8d53ca net: Allow filter... |
255 256 |
debug_cond(DEBUG_INT_STATE, "%s recv arp type=%d, op=%d, ", |
8e7ff6773 net: cosmetic: Fi... |
257 258 |
eth_get_name(), ntohs(arp->ar_pro), ntohs(arp->ar_op)); |
4ef8d53ca net: Allow filter... |
259 260 |
debug_cond(DEBUG_INT_STATE, "\tsource=%pM %pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
261 262 |
&arp->ar_sha, &arp->ar_spa); |
4ef8d53ca net: Allow filter... |
263 264 |
debug_cond(DEBUG_INT_STATE, "\ttarget=%pM %pI4 ", |
8e7ff6773 net: cosmetic: Fi... |
265 266 |
&arp->ar_tha, &arp->ar_tpa); |
d22c338e0 net: Add link-loc... |
267 |
|
8e7ff6773 net: cosmetic: Fi... |
268 269 |
if (arp->ar_op != htons(ARPOP_REQUEST) && arp->ar_op != htons(ARPOP_REPLY)) { |
d22c338e0 net: Add link-loc... |
270 271 272 273 274 275 |
configure_wait(); return; } source_ip_conflict = 0; target_ip_conflict = 0; |
0adb5b761 net: cosmetic: Na... |
276 277 |
if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 && memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) |
d22c338e0 net: Add link-loc... |
278 |
source_ip_conflict = 1; |
5da7cf81c 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 net: cosmetic: Na... |
290 |
memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) != 0) { |
d22c338e0 net: Add link-loc... |
291 292 |
target_ip_conflict = 1; } |
4ef8d53ca net: Allow filter... |
293 |
debug_cond(DEBUG_NET_PKT, |
8e7ff6773 net: cosmetic: Fi... |
294 295 296 |
"state = %d, source ip conflict = %d, target ip conflict = " "%d ", state, source_ip_conflict, target_ip_conflict); |
d22c338e0 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 net: cosmetic: Na... |
326 |
arp_raw_request(ip, net_ethaddr, ip); |
d22c338e0 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 net: cosmetic: Ch... |
336 |
net_ip.s_addr = 0; |
d22c338e0 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(); } |