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