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