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