Commit d22c338e07cc98276ea5cc4feaa5a370baa63243

Authored by Joe Hershberger
1 parent 228041893c

net: Add link-local addressing support

Code based on networking/zcip.c in busybox
commit 8531d76a15890c2c535908ce888b2e2aed35b172

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>

Showing 8 changed files with 490 additions and 2 deletions Side-by-side Diff

... ... @@ -785,6 +785,8 @@
785 785 CONFIG_CMD_JFFS2 * JFFS2 Support
786 786 CONFIG_CMD_KGDB * kgdb
787 787 CONFIG_CMD_LDRINFO ldrinfo (display Blackfin loader)
  788 + CONFIG_CMD_LINK_LOCAL * link-local IP address auto-configuration
  789 + (169.254.*.*)
788 790 CONFIG_CMD_LOADB loadb
789 791 CONFIG_CMD_LOADS loads
790 792 CONFIG_CMD_MD5SUM print md5 message digest
... ... @@ -1632,6 +1634,14 @@
1632 1634 that one of the retries will be successful but note that
1633 1635 the DHCP timeout and retry process takes a longer than
1634 1636 this delay.
  1637 +
  1638 + - Link-local IP address negotiation:
  1639 + Negotiate with other link-local clients on the local network
  1640 + for an address that doesn't require explicit configuration.
  1641 + This is especially useful if a DHCP server cannot be guaranteed
  1642 + to exist in all environments that the device must operate.
  1643 +
  1644 + See doc/README.link-local for more information.
1635 1645  
1636 1646 - CDP Options:
1637 1647 CONFIG_CDP_DEVICE_ID
... ... @@ -428,4 +428,35 @@
428 428 );
429 429  
430 430 #endif /* CONFIG_CMD_DNS */
  431 +
  432 +#if defined(CONFIG_CMD_LINK_LOCAL)
  433 +static int do_link_local(cmd_tbl_t *cmdtp, int flag, int argc,
  434 + char * const argv[])
  435 +{
  436 + char tmp[22];
  437 +
  438 + if (NetLoop(LINKLOCAL) < 0)
  439 + return 1;
  440 +
  441 + NetOurGatewayIP = 0;
  442 + ip_to_string(NetOurGatewayIP, tmp);
  443 + setenv("gatewayip", tmp);
  444 +
  445 + ip_to_string(NetOurSubnetMask, tmp);
  446 + setenv("netmask", tmp);
  447 +
  448 + ip_to_string(NetOurIP, tmp);
  449 + setenv("ipaddr", tmp);
  450 + setenv("llipaddr", tmp); /* store this for next time */
  451 +
  452 + return 0;
  453 +}
  454 +
  455 +U_BOOT_CMD(
  456 + linklocal, 1, 1, do_link_local,
  457 + "acquire a network IP address using the link-local protocol",
  458 + ""
  459 +);
  460 +
  461 +#endif /* CONFIG_CMD_LINK_LOCAL */
doc/README.link-local
  1 +------------------------------------------
  2 + Link-local IP address auto-configuration
  3 +------------------------------------------
  4 +
  5 +Negotiate with other link-local clients on the local network
  6 +for an address that doesn't require explicit configuration.
  7 +This is especially useful if a DHCP server cannot be guaranteed
  8 +to exist in all environments that the device must operate.
  9 +
  10 +This is an implementation of RFC3927.
  11 +
  12 +----------
  13 + Commands
  14 +----------
  15 +
  16 +When CONFIG_CMD_LINK_LOCAL is defined in the board config file,
  17 +the "linklocal" command is available. This running this will
  18 +take approximately 5 seconds while the address is negotiated.
  19 +
  20 +------------------------
  21 + Environment interation
  22 +------------------------
  23 +
  24 +The "llipaddr" variable is set with the most recently
  25 +negotiated address and is preferred in future negotiations.
  26 +
  27 +The "ipaddr", "netmask", and "gatewayip" variables are set
  28 +after successful negotiation to enable network access.
  29 +
  30 +-------------
  31 + Limitations
  32 +-------------
  33 +
  34 +RFC3927 requires that addresses are continuously checked to
  35 +avoid conflicts, however this can only happen when the NetLoop
  36 +is getting called. It is possible for a conflict to go undetected
  37 +until a command that accesses the network is executed.
  38 +
  39 +Using NetConsole is one way to ensure that NetLoop is always
  40 +processing packets and monitoring for conflicts.
  41 +
  42 +This is also not a concern if the feature is use to connect
  43 +directly to another machine that may not be running a DHCP server.
  44 +
  45 +----------------
  46 + Example script
  47 +----------------
  48 +
  49 +This script allows use of DHCP and/or Link-local controlled
  50 +by env variables. It depends on CONFIG_CMD_LINK_LOCAL, CONFIG_CMD_DHCP,
  51 +and CONFIG_BOOTP_MAY_FAIL.
  52 +If both fail or are disabled, static settings are used.
  53 +
  54 +#define CONFIG_EXTRA_ENV_SETTINGS \
  55 + "ipconfigcmd=if test \\\"$dhcpenabled\\\" -ne 0;" \
  56 + "then " \
  57 + "dhcpfail=0;dhcp || dhcpfail=1;" \
  58 + "else " \
  59 + "dhcpfail=-1;" \
  60 + "fi;" \
  61 + "if test \\\"$linklocalenabled\\\" -ne 0 -a " \
  62 + "\\\"$dhcpfail\\\" -ne 0;" \
  63 + "then " \
  64 + "linklocal;" \
  65 + "llfail=0;" \
  66 + "else " \
  67 + "llfail=-1;" \
  68 + "fi;" \
  69 + "if test \\\"$llfail\\\" -ne 0 -a " \
  70 + "\\\"$dhcpfail\\\" -ne 0; " \
  71 + "then " \
  72 + "setenv ipaddr $sipaddr; " \
  73 + "setenv netmask $snetmask; " \
  74 + "setenv gatewayip $sgatewayip; " \
  75 + "fi;\0" \
... ... @@ -395,7 +395,7 @@
395 395  
396 396 enum proto_t {
397 397 BOOTP, RARP, ARP, TFTPGET, DHCP, PING, DNS, NFS, CDP, NETCONS, SNTP,
398   - TFTPSRV, TFTPPUT
  398 + TFTPSRV, TFTPPUT, LINKLOCAL
399 399 };
400 400  
401 401 /* from net/net.c */
... ... @@ -32,15 +32,17 @@
32 32 COBJS-$(CONFIG_CMD_CDP) += cdp.o
33 33 COBJS-$(CONFIG_CMD_DNS) += dns.o
34 34 COBJS-$(CONFIG_CMD_NET) += eth.o
  35 +COBJS-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
35 36 COBJS-$(CONFIG_CMD_NET) += net.o
36 37 COBJS-$(CONFIG_BOOTP_RANDOM_DELAY) += net_rand.o
  38 +COBJS-$(CONFIG_CMD_LINK_LOCAL) += net_rand.o
37 39 COBJS-$(CONFIG_CMD_NFS) += nfs.o
38 40 COBJS-$(CONFIG_CMD_PING) += ping.o
39 41 COBJS-$(CONFIG_CMD_RARP) += rarp.o
40 42 COBJS-$(CONFIG_CMD_SNTP) += sntp.o
41 43 COBJS-$(CONFIG_CMD_NET) += tftp.o
42 44  
43   -COBJS := $(COBJS-y)
  45 +COBJS := $(sort $(COBJS-y))
44 46 SRCS := $(COBJS:.o=.c)
45 47 OBJS := $(addprefix $(obj),$(COBJS))
46 48  
  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("...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("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("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("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("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("missed an expected timeout\n");
  220 + timeout_ms = 0;
  221 + } else {
  222 + debug("adjusting timeout\n");
  223 + timeout_ms = diff | 1; /* never 0 */
  224 + }
  225 + }
  226 +/*
  227 + * XXX Don't bother with ethernet link just yet
  228 + if ((fds[0].revents & POLLIN) == 0) {
  229 + if (fds[0].revents & POLLERR) {
  230 + // FIXME: links routinely go down;
  231 + // this shouldn't necessarily exit.
  232 + bb_error_msg("iface %s is down", eth_get_name());
  233 + if (ready) {
  234 + run(argv, "deconfig", &ip);
  235 + }
  236 + return EXIT_FAILURE;
  237 + }
  238 + continue;
  239 + }
  240 +*/
  241 +
  242 + debug("%s recv arp type=%d, op=%d,\n",
  243 + eth_get_name(), ntohs(arp->ar_pro),
  244 + ntohs(arp->ar_op));
  245 + debug("\tsource=%pM %pI4\n",
  246 + &arp->ar_sha,
  247 + &arp->ar_spa);
  248 + debug("\ttarget=%pM %pI4\n",
  249 + &arp->ar_tha,
  250 + &arp->ar_tpa);
  251 +
  252 + if (arp->ar_op != htons(ARPOP_REQUEST)
  253 + && arp->ar_op != htons(ARPOP_REPLY)
  254 + ) {
  255 + configure_wait();
  256 + return;
  257 + }
  258 +
  259 + source_ip_conflict = 0;
  260 + target_ip_conflict = 0;
  261 +
  262 + if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0
  263 + && memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0
  264 + ) {
  265 + source_ip_conflict = 1;
  266 + }
  267 + if (arp->ar_op == htons(ARPOP_REQUEST)
  268 + && memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0
  269 + && memcmp(&arp->ar_tha, NetOurEther, ARP_HLEN) != 0
  270 + ) {
  271 + target_ip_conflict = 1;
  272 + }
  273 +
  274 + debug("state = %d, source ip conflict = %d, target ip conflict = %d\n",
  275 + state, source_ip_conflict, target_ip_conflict);
  276 + switch (state) {
  277 + case PROBE:
  278 + case ANNOUNCE:
  279 + /* When probing or announcing, check for source IP conflicts
  280 + and other hosts doing ARP probes (target IP conflicts). */
  281 + if (source_ip_conflict || target_ip_conflict) {
  282 + conflicts++;
  283 + state = PROBE;
  284 + if (conflicts >= MAX_CONFLICTS) {
  285 + debug("%s ratelimit\n", eth_get_name());
  286 + timeout_ms = RATE_LIMIT_INTERVAL * 1000;
  287 + state = RATE_LIMIT_PROBE;
  288 + }
  289 +
  290 + /* restart the whole protocol */
  291 + ip = pick();
  292 + timeout_ms = 0;
  293 + nprobes = 0;
  294 + nclaims = 0;
  295 + }
  296 + break;
  297 + case MONITOR:
  298 + /* If a conflict, we try to defend with a single ARP probe */
  299 + if (source_ip_conflict) {
  300 + debug("monitor conflict -- defending\n");
  301 + state = DEFEND;
  302 + timeout_ms = DEFEND_INTERVAL * 1000;
  303 + arp_raw_request(ip, NetOurEther, ip);
  304 + }
  305 + break;
  306 + case DEFEND:
  307 + /* Well, we tried. Start over (on conflict) */
  308 + if (source_ip_conflict) {
  309 + state = PROBE;
  310 + debug("defend conflict -- starting over\n");
  311 + ready = 0;
  312 + NetOurIP = 0;
  313 +
  314 + /* restart the whole protocol */
  315 + ip = pick();
  316 + timeout_ms = 0;
  317 + nprobes = 0;
  318 + nclaims = 0;
  319 + }
  320 + break;
  321 + default:
  322 + /* Invalid, should never happen. Restart the whole protocol */
  323 + debug("invalid state -- starting over\n");
  324 + state = PROBE;
  325 + ip = pick();
  326 + timeout_ms = 0;
  327 + nprobes = 0;
  328 + nclaims = 0;
  329 + break;
  330 + }
  331 + configure_wait();
  332 +}
  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 + *
  10 + * Licensed under the GPL v2 or later
  11 + */
  12 +
  13 +#if defined(CONFIG_CMD_LINK_LOCAL)
  14 +
  15 +#ifndef __LINK_LOCAL_H__
  16 +#define __LINK_LOCAL_H__
  17 +
  18 +#include <common.h>
  19 +
  20 +void link_local_receive_arp(struct arp_hdr *arp, int len);
  21 +void link_local_start(void);
  22 +
  23 +#endif /* __LINK_LOCAL_H__ */
  24 +#endif
... ... @@ -23,6 +23,12 @@
23 23 * - name of bootfile
24 24 * Next step: ARP
25 25 *
  26 + * LINK_LOCAL:
  27 + *
  28 + * Prerequisites: - own ethernet address
  29 + * We want: - own IP address
  30 + * Next step: ARP
  31 + *
26 32 * RARP:
27 33 *
28 34 * Prerequisites: - own ethernet address
... ... @@ -89,6 +95,7 @@
89 95 #if defined(CONFIG_CMD_DNS)
90 96 #include "dns.h"
91 97 #endif
  98 +#include "link_local.h"
92 99 #include "nfs.h"
93 100 #include "ping.h"
94 101 #include "rarp.h"
... ... @@ -402,6 +409,11 @@
402 409 DnsStart();
403 410 break;
404 411 #endif
  412 +#if defined(CONFIG_CMD_LINK_LOCAL)
  413 + case LINKLOCAL:
  414 + link_local_start();
  415 + break;
  416 +#endif
405 417 default:
406 418 break;
407 419 }
... ... @@ -1194,6 +1206,7 @@
1194 1206 case BOOTP:
1195 1207 case CDP:
1196 1208 case DHCP:
  1209 + case LINKLOCAL:
1197 1210 if (memcmp(NetOurEther, "\0\0\0\0\0\0", 6) == 0) {
1198 1211 int num = eth_get_dev_index();
1199 1212