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