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