Code based on networking/zcip.c in busybox commit 8531d76a15890c2c535908ce888b2e2aed35b172 Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>master
parent
228041893c
commit
d22c338e07
@ -0,0 +1,76 @@ |
||||
------------------------------------------ |
||||
Link-local IP address auto-configuration |
||||
------------------------------------------ |
||||
|
||||
Negotiate with other link-local clients on the local network |
||||
for an address that doesn't require explicit configuration. |
||||
This is especially useful if a DHCP server cannot be guaranteed |
||||
to exist in all environments that the device must operate. |
||||
|
||||
This is an implementation of RFC3927. |
||||
|
||||
---------- |
||||
Commands |
||||
---------- |
||||
|
||||
When CONFIG_CMD_LINK_LOCAL is defined in the board config file, |
||||
the "linklocal" command is available. This running this will |
||||
take approximately 5 seconds while the address is negotiated. |
||||
|
||||
------------------------ |
||||
Environment interation |
||||
------------------------ |
||||
|
||||
The "llipaddr" variable is set with the most recently |
||||
negotiated address and is preferred in future negotiations. |
||||
|
||||
The "ipaddr", "netmask", and "gatewayip" variables are set |
||||
after successful negotiation to enable network access. |
||||
|
||||
------------- |
||||
Limitations |
||||
------------- |
||||
|
||||
RFC3927 requires that addresses are continuously checked to |
||||
avoid conflicts, however this can only happen when the NetLoop |
||||
is getting called. It is possible for a conflict to go undetected |
||||
until a command that accesses the network is executed. |
||||
|
||||
Using NetConsole is one way to ensure that NetLoop is always |
||||
processing packets and monitoring for conflicts. |
||||
|
||||
This is also not a concern if the feature is use to connect |
||||
directly to another machine that may not be running a DHCP server. |
||||
|
||||
---------------- |
||||
Example script |
||||
---------------- |
||||
|
||||
This script allows use of DHCP and/or Link-local controlled |
||||
by env variables. It depends on CONFIG_CMD_LINK_LOCAL, CONFIG_CMD_DHCP, |
||||
and CONFIG_BOOTP_MAY_FAIL. |
||||
If both fail or are disabled, static settings are used. |
||||
|
||||
#define CONFIG_EXTRA_ENV_SETTINGS \ |
||||
"ipconfigcmd=if test \\\"$dhcpenabled\\\" -ne 0;" \ |
||||
"then " \ |
||||
"dhcpfail=0;dhcp || dhcpfail=1;" \ |
||||
"else " \ |
||||
"dhcpfail=-1;" \ |
||||
"fi;" \ |
||||
"if test \\\"$linklocalenabled\\\" -ne 0 -a " \ |
||||
"\\\"$dhcpfail\\\" -ne 0;" \ |
||||
"then " \ |
||||
"linklocal;" \ |
||||
"llfail=0;" \ |
||||
"else " \ |
||||
"llfail=-1;" \ |
||||
"fi;" \ |
||||
"if test \\\"$llfail\\\" -ne 0 -a " \ |
||||
"\\\"$dhcpfail\\\" -ne 0; " \ |
||||
"then " \ |
||||
"setenv ipaddr $sipaddr; " \ |
||||
"setenv netmask $snetmask; " \ |
||||
"setenv gatewayip $sgatewayip; " \ |
||||
"fi;\0" \ |
||||
|
@ -0,0 +1,332 @@ |
||||
/*
|
||||
* RFC3927 ZeroConf IPv4 Link-Local addressing |
||||
* (see <http://www.zeroconf.org/>)
|
||||
* |
||||
* Copied from BusyBox - networking/zcip.c |
||||
* |
||||
* Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) |
||||
* Copyright (C) 2004 by David Brownell |
||||
* Copyright (C) 2010 by Joe Hershberger |
||||
* |
||||
* Licensed under the GPL v2 or later |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <net.h> |
||||
#include "arp.h" |
||||
#include "net_rand.h" |
||||
|
||||
/* We don't need more than 32 bits of the counter */ |
||||
#define MONOTONIC_MS() ((unsigned)get_timer(0) * (1000 / CONFIG_SYS_HZ)) |
||||
|
||||
enum { |
||||
/* 169.254.0.0 */ |
||||
LINKLOCAL_ADDR = 0xa9fe0000, |
||||
|
||||
IN_CLASSB_NET = 0xffff0000, |
||||
IN_CLASSB_HOST = 0x0000ffff, |
||||
|
||||
/* protocol timeout parameters, specified in seconds */ |
||||
PROBE_WAIT = 1, |
||||
PROBE_MIN = 1, |
||||
PROBE_MAX = 2, |
||||
PROBE_NUM = 3, |
||||
MAX_CONFLICTS = 10, |
||||
RATE_LIMIT_INTERVAL = 60, |
||||
ANNOUNCE_WAIT = 2, |
||||
ANNOUNCE_NUM = 2, |
||||
ANNOUNCE_INTERVAL = 2, |
||||
DEFEND_INTERVAL = 10 |
||||
}; |
||||
|
||||
/* States during the configuration process. */ |
||||
static enum ll_state_t { |
||||
PROBE = 0, |
||||
RATE_LIMIT_PROBE, |
||||
ANNOUNCE, |
||||
MONITOR, |
||||
DEFEND, |
||||
DISABLED |
||||
} state = DISABLED; |
||||
|
||||
static IPaddr_t ip; |
||||
static int timeout_ms = -1; |
||||
static unsigned deadline_ms; |
||||
static unsigned conflicts; |
||||
static unsigned nprobes; |
||||
static unsigned nclaims; |
||||
static int ready; |
||||
|
||||
static void link_local_timeout(void); |
||||
|
||||
/**
|
||||
* Pick a random link local IP address on 169.254/16, except that |
||||
* the first and last 256 addresses are reserved. |
||||
*/ |
||||
static IPaddr_t pick(void) |
||||
{ |
||||
unsigned tmp; |
||||
|
||||
do { |
||||
tmp = rand() & IN_CLASSB_HOST; |
||||
} while (tmp > (IN_CLASSB_HOST - 0x0200)); |
||||
return (IPaddr_t) htonl((LINKLOCAL_ADDR + 0x0100) + tmp); |
||||
} |
||||
|
||||
/**
|
||||
* Return milliseconds of random delay, up to "secs" seconds. |
||||
*/ |
||||
static inline unsigned random_delay_ms(unsigned secs) |
||||
{ |
||||
return rand() % (secs * 1000); |
||||
} |
||||
|
||||
static void configure_wait(void) |
||||
{ |
||||
if (timeout_ms == -1) |
||||
return; |
||||
|
||||
/* poll, being ready to adjust current timeout */ |
||||
if (!timeout_ms) |
||||
timeout_ms = random_delay_ms(PROBE_WAIT); |
||||
|
||||
/* set deadline_ms to the point in time when we timeout */ |
||||
deadline_ms = MONOTONIC_MS() + timeout_ms; |
||||
|
||||
debug("...wait %d %s nprobes=%u, nclaims=%u\n", |
||||
timeout_ms, eth_get_name(), nprobes, nclaims); |
||||
|
||||
NetSetTimeout(timeout_ms, link_local_timeout); |
||||
} |
||||
|
||||
void link_local_start(void) |
||||
{ |
||||
ip = getenv_IPaddr("llipaddr"); |
||||
if (ip != 0 && (ip & IN_CLASSB_NET) != LINKLOCAL_ADDR) { |
||||
puts("invalid link address"); |
||||
net_set_state(NETLOOP_FAIL); |
||||
return; |
||||
} |
||||
NetOurSubnetMask = IN_CLASSB_NET; |
||||
|
||||
srand_mac(); |
||||
if (ip == 0) |
||||
ip = pick(); |
||||
|
||||
state = PROBE; |
||||
timeout_ms = 0; |
||||
conflicts = 0; |
||||
nprobes = 0; |
||||
nclaims = 0; |
||||
ready = 0; |
||||
|
||||
configure_wait(); |
||||
} |
||||
|
||||
static void link_local_timeout(void) |
||||
{ |
||||
switch (state) { |
||||
case PROBE: |
||||
/* timeouts in the PROBE state mean no conflicting ARP packets
|
||||
have been received, so we can progress through the states */ |
||||
if (nprobes < PROBE_NUM) { |
||||
nprobes++; |
||||
debug("probe/%u %s@%pI4\n", |
||||
nprobes, eth_get_name(), &ip); |
||||
arp_raw_request(0, NetEtherNullAddr, ip); |
||||
timeout_ms = PROBE_MIN * 1000; |
||||
timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); |
||||
} else { |
||||
/* Switch to announce state */ |
||||
state = ANNOUNCE; |
||||
nclaims = 0; |
||||
debug("announce/%u %s@%pI4\n", |
||||
nclaims, eth_get_name(), &ip); |
||||
arp_raw_request(ip, NetOurEther, ip); |
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
||||
} |
||||
break; |
||||
case RATE_LIMIT_PROBE: |
||||
/* timeouts in the RATE_LIMIT_PROBE state mean no conflicting
|
||||
ARP packets have been received, so we can move immediately |
||||
to the announce state */ |
||||
state = ANNOUNCE; |
||||
nclaims = 0; |
||||
debug("announce/%u %s@%pI4\n", |
||||
nclaims, eth_get_name(), &ip); |
||||
arp_raw_request(ip, NetOurEther, ip); |
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
||||
break; |
||||
case ANNOUNCE: |
||||
/* timeouts in the ANNOUNCE state mean no conflicting ARP
|
||||
packets have been received, so we can progress through |
||||
the states */ |
||||
if (nclaims < ANNOUNCE_NUM) { |
||||
nclaims++; |
||||
debug("announce/%u %s@%pI4\n", |
||||
nclaims, eth_get_name(), &ip); |
||||
arp_raw_request(ip, NetOurEther, ip); |
||||
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
||||
} else { |
||||
/* Switch to monitor state */ |
||||
state = MONITOR; |
||||
printf("Successfully assigned %pI4\n", &ip); |
||||
NetCopyIP(&NetOurIP, &ip); |
||||
ready = 1; |
||||
conflicts = 0; |
||||
timeout_ms = -1; |
||||
/* Never timeout in the monitor state */ |
||||
NetSetTimeout(0, NULL); |
||||
|
||||
/* NOTE: all other exit paths should deconfig ... */ |
||||
net_set_state(NETLOOP_SUCCESS); |
||||
return; |
||||
} |
||||
break; |
||||
case DEFEND: |
||||
/* We won! No ARP replies, so just go back to monitor */ |
||||
state = MONITOR; |
||||
timeout_ms = -1; |
||||
conflicts = 0; |
||||
break; |
||||
default: |
||||
/* Invalid, should never happen. Restart the whole protocol */ |
||||
state = PROBE; |
||||
ip = pick(); |
||||
timeout_ms = 0; |
||||
nprobes = 0; |
||||
nclaims = 0; |
||||
break; |
||||
} |
||||
configure_wait(); |
||||
} |
||||
|
||||
void link_local_receive_arp(struct arp_hdr *arp, int len) |
||||
{ |
||||
int source_ip_conflict; |
||||
int target_ip_conflict; |
||||
|
||||
if (state == DISABLED) |
||||
return; |
||||
|
||||
/* We need to adjust the timeout in case we didn't receive a
|
||||
conflicting packet. */ |
||||
if (timeout_ms > 0) { |
||||
unsigned diff = deadline_ms - MONOTONIC_MS(); |
||||
if ((int)(diff) < 0) { |
||||
/* Current time is greater than the expected timeout
|
||||
time. This should never happen */ |
||||
debug("missed an expected timeout\n"); |
||||
timeout_ms = 0; |
||||
} else { |
||||
debug("adjusting timeout\n"); |
||||
timeout_ms = diff | 1; /* never 0 */ |
||||
} |
||||
} |
||||
/*
|
||||
* XXX Don't bother with ethernet link just yet |
||||
if ((fds[0].revents & POLLIN) == 0) { |
||||
if (fds[0].revents & POLLERR) { |
||||
// FIXME: links routinely go down;
|
||||
// this shouldn't necessarily exit.
|
||||
bb_error_msg("iface %s is down", eth_get_name()); |
||||
if (ready) { |
||||
run(argv, "deconfig", &ip); |
||||
} |
||||
return EXIT_FAILURE; |
||||
} |
||||
continue; |
||||
} |
||||
*/ |
||||
|
||||
debug("%s recv arp type=%d, op=%d,\n", |
||||
eth_get_name(), ntohs(arp->ar_pro), |
||||
ntohs(arp->ar_op)); |
||||
debug("\tsource=%pM %pI4\n", |
||||
&arp->ar_sha, |
||||
&arp->ar_spa); |
||||
debug("\ttarget=%pM %pI4\n", |
||||
&arp->ar_tha, |
||||
&arp->ar_tpa); |
||||
|
||||
if (arp->ar_op != htons(ARPOP_REQUEST) |
||||
&& arp->ar_op != htons(ARPOP_REPLY) |
||||
) { |
||||
configure_wait(); |
||||
return; |
||||
} |
||||
|
||||
source_ip_conflict = 0; |
||||
target_ip_conflict = 0; |
||||
|
||||
if (memcmp(&arp->ar_spa, &ip, ARP_PLEN) == 0 |
||||
&& memcmp(&arp->ar_sha, NetOurEther, ARP_HLEN) != 0 |
||||
) { |
||||
source_ip_conflict = 1; |
||||
} |
||||
if (arp->ar_op == htons(ARPOP_REQUEST) |
||||
&& memcmp(&arp->ar_tpa, &ip, ARP_PLEN) == 0 |
||||
&& memcmp(&arp->ar_tha, NetOurEther, ARP_HLEN) != 0 |
||||
) { |
||||
target_ip_conflict = 1; |
||||
} |
||||
|
||||
debug("state = %d, source ip conflict = %d, target ip conflict = %d\n", |
||||
state, source_ip_conflict, target_ip_conflict); |
||||
switch (state) { |
||||
case PROBE: |
||||
case ANNOUNCE: |
||||
/* When probing or announcing, check for source IP conflicts
|
||||
and other hosts doing ARP probes (target IP conflicts). */ |
||||
if (source_ip_conflict || target_ip_conflict) { |
||||
conflicts++; |
||||
state = PROBE; |
||||
if (conflicts >= MAX_CONFLICTS) { |
||||
debug("%s ratelimit\n", eth_get_name()); |
||||
timeout_ms = RATE_LIMIT_INTERVAL * 1000; |
||||
state = RATE_LIMIT_PROBE; |
||||
} |
||||
|
||||
/* restart the whole protocol */ |
||||
ip = pick(); |
||||
timeout_ms = 0; |
||||
nprobes = 0; |
||||
nclaims = 0; |
||||
} |
||||
break; |
||||
case MONITOR: |
||||
/* If a conflict, we try to defend with a single ARP probe */ |
||||
if (source_ip_conflict) { |
||||
debug("monitor conflict -- defending\n"); |
||||
state = DEFEND; |
||||
timeout_ms = DEFEND_INTERVAL * 1000; |
||||
arp_raw_request(ip, NetOurEther, ip); |
||||
} |
||||
break; |
||||
case DEFEND: |
||||
/* Well, we tried. Start over (on conflict) */ |
||||
if (source_ip_conflict) { |
||||
state = PROBE; |
||||
debug("defend conflict -- starting over\n"); |
||||
ready = 0; |
||||
NetOurIP = 0; |
||||
|
||||
/* restart the whole protocol */ |
||||
ip = pick(); |
||||
timeout_ms = 0; |
||||
nprobes = 0; |
||||
nclaims = 0; |
||||
} |
||||
break; |
||||
default: |
||||
/* Invalid, should never happen. Restart the whole protocol */ |
||||
debug("invalid state -- starting over\n"); |
||||
state = PROBE; |
||||
ip = pick(); |
||||
timeout_ms = 0; |
||||
nprobes = 0; |
||||
nclaims = 0; |
||||
break; |
||||
} |
||||
configure_wait(); |
||||
} |
@ -0,0 +1,24 @@ |
||||
/*
|
||||
* RFC3927 ZeroConf IPv4 Link-Local addressing |
||||
* (see <http://www.zeroconf.org/>)
|
||||
* |
||||
* Copied from BusyBox - networking/zcip.c |
||||
* |
||||
* Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) |
||||
* Copyright (C) 2004 by David Brownell |
||||
* |
||||
* Licensed under the GPL v2 or later |
||||
*/ |
||||
|
||||
#if defined(CONFIG_CMD_LINK_LOCAL) |
||||
|
||||
#ifndef __LINK_LOCAL_H__ |
||||
#define __LINK_LOCAL_H__ |
||||
|
||||
#include <common.h> |
||||
|
||||
void link_local_receive_arp(struct arp_hdr *arp, int len); |
||||
void link_local_start(void); |
||||
|
||||
#endif /* __LINK_LOCAL_H__ */ |
||||
#endif |
Loading…
Reference in new issue