23 |
// - avoid silent script failures, especially under load... |
// - avoid silent script failures, especially under load... |
24 |
// - link status monitoring (restart on link-up; stop on link-down) |
// - link status monitoring (restart on link-up; stop on link-down) |
25 |
|
|
|
#include "busybox.h" |
|
|
#include <syslog.h> |
|
|
#include <poll.h> |
|
|
#include <sys/wait.h> |
|
26 |
#include <netinet/ether.h> |
#include <netinet/ether.h> |
27 |
#include <net/ethernet.h> |
#include <net/ethernet.h> |
28 |
#include <net/if.h> |
#include <net/if.h> |
29 |
#include <net/if_arp.h> |
#include <net/if_arp.h> |
|
|
|
30 |
#include <linux/if_packet.h> |
#include <linux/if_packet.h> |
31 |
#include <linux/sockios.h> |
#include <linux/sockios.h> |
32 |
|
|
33 |
|
#include "libbb.h" |
34 |
|
#include <syslog.h> |
35 |
|
|
36 |
|
/* We don't need more than 32 bits of the counter */ |
37 |
|
#define MONOTONIC_US() ((unsigned)monotonic_us()) |
38 |
|
|
39 |
struct arp_packet { |
struct arp_packet { |
40 |
struct ether_header hdr; |
struct ether_header eth; |
41 |
struct ether_arp arp; |
struct ether_arp arp; |
42 |
} ATTRIBUTE_PACKED; |
} PACKED; |
43 |
|
|
44 |
enum { |
enum { |
45 |
/* 169.254.0.0 */ |
/* 169.254.0.0 */ |
67 |
DEFEND |
DEFEND |
68 |
}; |
}; |
69 |
|
|
70 |
#define VDBG(fmt,args...) \ |
#define VDBG(...) do { } while (0) |
71 |
do { } while (0) |
|
72 |
|
|
73 |
|
enum { |
74 |
|
sock_fd = 3 |
75 |
|
}; |
76 |
|
|
77 |
|
struct globals { |
78 |
|
struct sockaddr saddr; |
79 |
|
struct ether_addr eth_addr; |
80 |
|
}; |
81 |
|
#define G (*(struct globals*)&bb_common_bufsiz1) |
82 |
|
#define saddr (G.saddr ) |
83 |
|
#define eth_addr (G.eth_addr) |
84 |
|
|
|
static unsigned opts; |
|
|
#define FOREGROUND (opts & 1) |
|
|
#define QUIT (opts & 2) |
|
85 |
|
|
86 |
/** |
/** |
87 |
* Pick a random link local IP address on 169.254/16, except that |
* Pick a random link local IP address on 169.254/16, except that |
88 |
* the first and last 256 addresses are reserved. |
* the first and last 256 addresses are reserved. |
89 |
*/ |
*/ |
90 |
static void pick(struct in_addr *ip) |
static uint32_t pick(void) |
91 |
{ |
{ |
92 |
unsigned tmp; |
unsigned tmp; |
93 |
|
|
|
/* use cheaper math than lrand48() mod N */ |
|
94 |
do { |
do { |
95 |
tmp = (lrand48() >> 16) & IN_CLASSB_HOST; |
tmp = rand() & IN_CLASSB_HOST; |
96 |
} while (tmp > (IN_CLASSB_HOST - 0x0200)); |
} while (tmp > (IN_CLASSB_HOST - 0x0200)); |
97 |
ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp); |
return htonl((LINKLOCAL_ADDR + 0x0100) + tmp); |
98 |
} |
} |
99 |
|
|
|
/* TODO: we need a flag to direct bb_[p]error_msg output to stderr. */ |
|
|
|
|
100 |
/** |
/** |
101 |
* Broadcast an ARP packet. |
* Broadcast an ARP packet. |
102 |
*/ |
*/ |
103 |
static void arp(int fd, struct sockaddr *saddr, int op, |
static void arp( |
104 |
const struct ether_addr *source_addr, struct in_addr source_ip, |
/* int op, - always ARPOP_REQUEST */ |
105 |
const struct ether_addr *target_addr, struct in_addr target_ip) |
/* const struct ether_addr *source_eth, - always ð_addr */ |
106 |
|
struct in_addr source_ip, |
107 |
|
const struct ether_addr *target_eth, struct in_addr target_ip) |
108 |
{ |
{ |
109 |
|
enum { op = ARPOP_REQUEST }; |
110 |
|
#define source_eth (ð_addr) |
111 |
|
|
112 |
struct arp_packet p; |
struct arp_packet p; |
113 |
memset(&p, 0, sizeof(p)); |
memset(&p, 0, sizeof(p)); |
114 |
|
|
115 |
// ether header |
// ether header |
116 |
p.hdr.ether_type = htons(ETHERTYPE_ARP); |
p.eth.ether_type = htons(ETHERTYPE_ARP); |
117 |
memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN); |
memcpy(p.eth.ether_shost, source_eth, ETH_ALEN); |
118 |
memset(p.hdr.ether_dhost, 0xff, ETH_ALEN); |
memset(p.eth.ether_dhost, 0xff, ETH_ALEN); |
119 |
|
|
120 |
// arp request |
// arp request |
121 |
p.arp.arp_hrd = htons(ARPHRD_ETHER); |
p.arp.arp_hrd = htons(ARPHRD_ETHER); |
123 |
p.arp.arp_hln = ETH_ALEN; |
p.arp.arp_hln = ETH_ALEN; |
124 |
p.arp.arp_pln = 4; |
p.arp.arp_pln = 4; |
125 |
p.arp.arp_op = htons(op); |
p.arp.arp_op = htons(op); |
126 |
memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN); |
memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN); |
127 |
memcpy(&p.arp.arp_spa, &source_ip, sizeof (p.arp.arp_spa)); |
memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa)); |
128 |
memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN); |
memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN); |
129 |
memcpy(&p.arp.arp_tpa, &target_ip, sizeof (p.arp.arp_tpa)); |
memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa)); |
130 |
|
|
131 |
// send it |
// send it |
132 |
if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) { |
// Even though sock_fd is already bound to saddr, just send() |
133 |
bb_perror_msg("sendto"); |
// won't work, because "socket is not connected" |
134 |
//return -errno; |
// (and connect() won't fix that, "operation not supported"). |
135 |
} |
// Thus we sendto() to saddr. I wonder which sockaddr |
136 |
// Currently all callers ignore errors, that's why returns are |
// (from bind() or from sendto()?) kernel actually uses |
137 |
// commented out... |
// to determine iface to emit the packet from... |
138 |
//return 0; |
xsendto(sock_fd, &p, sizeof(p), &saddr, sizeof(saddr)); |
139 |
|
#undef source_eth |
140 |
} |
} |
141 |
|
|
142 |
/** |
/** |
143 |
* Run a script. |
* Run a script. |
144 |
|
* argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL |
145 |
*/ |
*/ |
146 |
static int run(char *script, char *arg, char *intf, struct in_addr *ip) |
static int run(char *argv[3], const char *param, struct in_addr *ip) |
147 |
{ |
{ |
148 |
int pid, status; |
int status; |
149 |
char *why; |
char *addr = addr; /* for gcc */ |
150 |
|
const char *fmt = "%s %s %s" + 3; |
151 |
if(1) { //always true: if (script != NULL) |
|
152 |
VDBG("%s run %s %s\n", intf, script, arg); |
argv[2] = (char*)param; |
153 |
if (ip != NULL) { |
|
154 |
char *addr = inet_ntoa(*ip); |
VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]); |
155 |
setenv("ip", addr, 1); |
|
156 |
bb_info_msg("%s %s %s", arg, intf, addr); |
if (ip) { |
157 |
} |
addr = inet_ntoa(*ip); |
158 |
|
xsetenv("ip", addr); |
159 |
pid = vfork(); |
fmt -= 3; |
160 |
if (pid < 0) { // error |
} |
161 |
why = "vfork"; |
bb_info_msg(fmt, argv[2], argv[0], addr); |
|
goto bad; |
|
|
} else if (pid == 0) { // child |
|
|
execl(script, script, arg, NULL); |
|
|
bb_perror_msg("execl"); |
|
|
_exit(EXIT_FAILURE); |
|
|
} |
|
162 |
|
|
163 |
if (waitpid(pid, &status, 0) <= 0) { |
status = wait4pid(spawn(argv + 1)); |
164 |
why = "waitpid"; |
if (status < 0) { |
165 |
goto bad; |
bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]); |
166 |
} |
return -errno; |
|
if (WEXITSTATUS(status) != 0) { |
|
|
bb_error_msg("script %s failed, exit=%d", |
|
|
script, WEXITSTATUS(status)); |
|
|
return -errno; |
|
|
} |
|
167 |
} |
} |
168 |
return 0; |
if (status != 0) |
169 |
bad: |
bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status); |
|
status = -errno; |
|
|
bb_perror_msg("%s %s, %s", arg, intf, why); |
|
170 |
return status; |
return status; |
171 |
} |
} |
172 |
|
|
|
|
|
173 |
/** |
/** |
174 |
* Return milliseconds of random delay, up to "secs" seconds. |
* Return milliseconds of random delay, up to "secs" seconds. |
175 |
*/ |
*/ |
176 |
static unsigned ATTRIBUTE_ALWAYS_INLINE ms_rdelay(unsigned secs) |
static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs) |
177 |
{ |
{ |
178 |
return lrand48() % (secs * 1000); |
return rand() % (secs * 1000); |
179 |
} |
} |
180 |
|
|
181 |
/** |
/** |
182 |
* main program |
* main program |
183 |
*/ |
*/ |
184 |
|
int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
185 |
|
int zcip_main(int argc, char **argv) |
186 |
|
{ |
187 |
|
int state; |
188 |
|
char *r_opt; |
189 |
|
unsigned opts; |
190 |
|
|
191 |
/* Used to be auto variables on main() stack, but |
// ugly trick, but I want these zeroed in one go |
192 |
* most of them were zero-inited. Moving them to bss |
struct { |
193 |
* is more space-efficient. |
const struct in_addr null_ip; |
194 |
*/ |
const struct ether_addr null_addr; |
195 |
static const struct in_addr null_ip; // = { 0 }; |
struct in_addr ip; |
196 |
static const struct ether_addr null_addr; // = { {0, 0, 0, 0, 0, 0} }; |
struct ifreq ifr; |
197 |
|
int timeout_ms; /* must be signed */ |
198 |
|
unsigned conflicts; |
199 |
|
unsigned nprobes; |
200 |
|
unsigned nclaims; |
201 |
|
int ready; |
202 |
|
int verbose; |
203 |
|
} L; |
204 |
|
#define null_ip (L.null_ip ) |
205 |
|
#define null_addr (L.null_addr ) |
206 |
|
#define ip (L.ip ) |
207 |
|
#define ifr (L.ifr ) |
208 |
|
#define timeout_ms (L.timeout_ms) |
209 |
|
#define conflicts (L.conflicts ) |
210 |
|
#define nprobes (L.nprobes ) |
211 |
|
#define nclaims (L.nclaims ) |
212 |
|
#define ready (L.ready ) |
213 |
|
#define verbose (L.verbose ) |
214 |
|
|
215 |
static struct sockaddr saddr; // memset(0); |
memset(&L, 0, sizeof(L)); |
|
static struct in_addr ip; // = { 0 }; |
|
|
static struct ifreq ifr; //memset(0); |
|
|
|
|
|
static char *intf; // = NULL; |
|
|
static char *script; // = NULL; |
|
|
static suseconds_t timeout; // = 0; // milliseconds |
|
|
static unsigned conflicts; // = 0; |
|
|
static unsigned nprobes; // = 0; |
|
|
static unsigned nclaims; // = 0; |
|
|
static int ready; // = 0; |
|
|
static int verbose; // = 0; |
|
|
static int state = PROBE; |
|
|
|
|
|
int zcip_main(int argc, char *argv[]) |
|
|
{ |
|
|
struct ether_addr eth_addr; |
|
|
char *why; |
|
|
int fd; |
|
216 |
|
|
217 |
|
#define FOREGROUND (opts & 1) |
218 |
|
#define QUIT (opts & 2) |
219 |
// parse commandline: prog [options] ifname script |
// parse commandline: prog [options] ifname script |
220 |
char *r_opt; |
// exactly 2 args; -v accumulates and implies -f |
221 |
opt_complementary = "vv:vf"; // -v accumulates and implies -f |
opt_complementary = "=2:vv:vf"; |
222 |
opts = getopt32(argc, argv, "fqr:v", &r_opt, &verbose); |
opts = getopt32(argv, "fqr:v", &r_opt, &verbose); |
223 |
|
#if !BB_MMU |
224 |
|
// on NOMMU reexec early (or else we will rerun things twice) |
225 |
|
if (!FOREGROUND) |
226 |
|
bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv); |
227 |
|
#endif |
228 |
|
// open an ARP socket |
229 |
|
// (need to do it before openlog to prevent openlog from taking |
230 |
|
// fd 3 (sock_fd==3)) |
231 |
|
xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); |
232 |
if (!FOREGROUND) { |
if (!FOREGROUND) { |
233 |
/* Do it early, before all bb_xx_msg calls */ |
// do it before all bb_xx_msg calls |
|
logmode = LOGMODE_SYSLOG; |
|
234 |
openlog(applet_name, 0, LOG_DAEMON); |
openlog(applet_name, 0, LOG_DAEMON); |
235 |
|
logmode |= LOGMODE_SYSLOG; |
236 |
} |
} |
237 |
if (opts & 4) { // -r n.n.n.n |
if (opts & 4) { // -r n.n.n.n |
238 |
if (inet_aton(r_opt, &ip) == 0 |
if (inet_aton(r_opt, &ip) == 0 |
242 |
} |
} |
243 |
} |
} |
244 |
argc -= optind; |
argc -= optind; |
245 |
argv += optind; |
argv += optind - 1; |
246 |
if (argc != 2) |
|
247 |
bb_show_usage(); |
/* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */ |
248 |
intf = argv[0]; |
/* We need to make space for script argument: */ |
249 |
script = argv[1]; |
argv[0] = argv[1]; |
250 |
setenv("interface", intf, 1); |
argv[1] = argv[2]; |
251 |
|
/* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */ |
252 |
|
#define argv_intf (argv[0]) |
253 |
|
|
254 |
|
xsetenv("interface", argv_intf); |
255 |
|
|
256 |
// initialize the interface (modprobe, ifup, etc) |
// initialize the interface (modprobe, ifup, etc) |
257 |
if (run(script, "init", intf, NULL) < 0) |
if (run(argv, "init", NULL)) |
258 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
259 |
|
|
260 |
// initialize saddr |
// initialize saddr |
261 |
//memset(&saddr, 0, sizeof (saddr)); |
// saddr is: { u16 sa_family; u8 sa_data[14]; } |
262 |
safe_strncpy(saddr.sa_data, intf, sizeof (saddr.sa_data)); |
//memset(&saddr, 0, sizeof(saddr)); |
263 |
|
//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?! |
264 |
|
safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data)); |
265 |
|
|
|
// open an ARP socket |
|
|
fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); |
|
266 |
// bind to the interface's ARP socket |
// bind to the interface's ARP socket |
267 |
xbind(fd, &saddr, sizeof (saddr)); |
xbind(sock_fd, &saddr, sizeof(saddr)); |
268 |
|
|
269 |
// get the interface's ethernet address |
// get the interface's ethernet address |
270 |
//memset(&ifr, 0, sizeof (ifr)); |
//memset(&ifr, 0, sizeof(ifr)); |
271 |
strncpy(ifr.ifr_name, intf, sizeof (ifr.ifr_name)); |
strncpy(ifr.ifr_name, argv_intf, sizeof(ifr.ifr_name)); |
272 |
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { |
xioctl(sock_fd, SIOCGIFHWADDR, &ifr); |
|
bb_perror_msg_and_die("get ethernet address"); |
|
|
} |
|
273 |
memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); |
memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); |
274 |
|
|
275 |
// start with some stable ip address, either a function of |
// start with some stable ip address, either a function of |
276 |
// the hardware address or else the last address we used. |
// the hardware address or else the last address we used. |
277 |
|
// we are taking low-order four bytes, as top-order ones |
278 |
|
// aren't random enough. |
279 |
// NOTE: the sequence of addresses we try changes only |
// NOTE: the sequence of addresses we try changes only |
280 |
// depending on when we detect conflicts. |
// depending on when we detect conflicts. |
281 |
// (SVID 3 bogon: who says that "short" is always 16 bits?) |
{ |
282 |
seed48( (unsigned short*)&ifr.ifr_hwaddr.sa_data ); |
uint32_t t = get_unaligned_u32p((uint32_t *) ((char *)ð_addr + 2)); |
283 |
|
srand(t); |
284 |
|
} |
285 |
if (ip.s_addr == 0) |
if (ip.s_addr == 0) |
286 |
pick(&ip); |
ip.s_addr = pick(); |
287 |
|
|
288 |
// FIXME cases to handle: |
// FIXME cases to handle: |
289 |
// - zcip already running! |
// - zcip already running! |
291 |
|
|
292 |
// daemonize now; don't delay system startup |
// daemonize now; don't delay system startup |
293 |
if (!FOREGROUND) { |
if (!FOREGROUND) { |
294 |
setsid(); |
#if BB_MMU |
295 |
bb_daemonize(); |
bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/); |
296 |
bb_info_msg("start, interface %s", intf); |
#endif |
297 |
|
bb_info_msg("start, interface %s", argv_intf); |
298 |
} |
} |
299 |
|
|
300 |
// run the dynamic address negotiation protocol, |
// run the dynamic address negotiation protocol, |
301 |
// restarting after address conflicts: |
// restarting after address conflicts: |
302 |
// - start with some address we want to try |
// - start with some address we want to try |
303 |
// - short random delay |
// - short random delay |
304 |
// - arp probes to see if another host else uses it |
// - arp probes to see if another host uses it |
305 |
// - arp announcements that we're claiming it |
// - arp announcements that we're claiming it |
306 |
// - use it |
// - use it |
307 |
// - defend it, within limits |
// - defend it, within limits |
308 |
|
// exit if: |
309 |
|
// - address is successfully obtained and -q was given: |
310 |
|
// run "<script> config", then exit with exitcode 0 |
311 |
|
// - poll error (when does this happen?) |
312 |
|
// - read error (when does this happen?) |
313 |
|
// - sendto error (in arp()) (when does this happen?) |
314 |
|
// - revents & POLLERR (link down). run "<script> deconfig" first |
315 |
|
state = PROBE; |
316 |
while (1) { |
while (1) { |
317 |
struct pollfd fds[1]; |
struct pollfd fds[1]; |
318 |
struct timeval tv1; |
unsigned deadline_us; |
319 |
struct arp_packet p; |
struct arp_packet p; |
320 |
|
int source_ip_conflict; |
321 |
|
int target_ip_conflict; |
322 |
|
|
323 |
int source_ip_conflict = 0; |
fds[0].fd = sock_fd; |
|
int target_ip_conflict = 0; |
|
|
|
|
|
fds[0].fd = fd; |
|
324 |
fds[0].events = POLLIN; |
fds[0].events = POLLIN; |
325 |
fds[0].revents = 0; |
fds[0].revents = 0; |
326 |
|
|
327 |
// poll, being ready to adjust current timeout |
// poll, being ready to adjust current timeout |
328 |
if (!timeout) { |
if (!timeout_ms) { |
329 |
timeout = ms_rdelay(PROBE_WAIT); |
timeout_ms = random_delay_ms(PROBE_WAIT); |
330 |
// FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to |
// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to |
331 |
// make the kernel filter out all packets except |
// make the kernel filter out all packets except |
332 |
// ones we'd care about. |
// ones we'd care about. |
333 |
} |
} |
334 |
// set tv1 to the point in time when we timeout |
// set deadline_us to the point in time when we timeout |
335 |
gettimeofday(&tv1, NULL); |
deadline_us = MONOTONIC_US() + timeout_ms * 1000; |
336 |
tv1.tv_usec += (timeout % 1000) * 1000; |
|
337 |
while (tv1.tv_usec > 1000000) { |
VDBG("...wait %d %s nprobes=%u, nclaims=%u\n", |
338 |
tv1.tv_usec -= 1000000; |
timeout_ms, argv_intf, nprobes, nclaims); |
|
tv1.tv_sec++; |
|
|
} |
|
|
tv1.tv_sec += timeout / 1000; |
|
339 |
|
|
340 |
VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n", |
switch (safe_poll(fds, 1, timeout_ms)) { |
341 |
timeout, intf, nprobes, nclaims); |
|
342 |
switch (poll(fds, 1, timeout)) { |
default: |
343 |
|
//bb_perror_msg("poll"); - done in safe_poll |
344 |
|
return EXIT_FAILURE; |
345 |
|
|
346 |
// timeout |
// timeout |
347 |
case 0: |
case 0: |
352 |
// have been received, so we can progress through the states |
// have been received, so we can progress through the states |
353 |
if (nprobes < PROBE_NUM) { |
if (nprobes < PROBE_NUM) { |
354 |
nprobes++; |
nprobes++; |
355 |
VDBG("probe/%d %s@%s\n", |
VDBG("probe/%u %s@%s\n", |
356 |
nprobes, intf, inet_ntoa(ip)); |
nprobes, argv_intf, inet_ntoa(ip)); |
357 |
arp(fd, &saddr, ARPOP_REQUEST, |
arp(/* ARPOP_REQUEST, */ |
358 |
ð_addr, null_ip, |
/* ð_addr, */ null_ip, |
359 |
&null_addr, ip); |
&null_addr, ip); |
360 |
timeout = PROBE_MIN * 1000; |
timeout_ms = PROBE_MIN * 1000; |
361 |
timeout += ms_rdelay(PROBE_MAX |
timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN); |
|
- PROBE_MIN); |
|
362 |
} |
} |
363 |
else { |
else { |
364 |
// Switch to announce state. |
// Switch to announce state. |
365 |
state = ANNOUNCE; |
state = ANNOUNCE; |
366 |
nclaims = 0; |
nclaims = 0; |
367 |
VDBG("announce/%d %s@%s\n", |
VDBG("announce/%u %s@%s\n", |
368 |
nclaims, intf, inet_ntoa(ip)); |
nclaims, argv_intf, inet_ntoa(ip)); |
369 |
arp(fd, &saddr, ARPOP_REQUEST, |
arp(/* ARPOP_REQUEST, */ |
370 |
ð_addr, ip, |
/* ð_addr, */ ip, |
371 |
ð_addr, ip); |
ð_addr, ip); |
372 |
timeout = ANNOUNCE_INTERVAL * 1000; |
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
373 |
} |
} |
374 |
break; |
break; |
375 |
case RATE_LIMIT_PROBE: |
case RATE_LIMIT_PROBE: |
377 |
// have been received, so we can move immediately to the announce state |
// have been received, so we can move immediately to the announce state |
378 |
state = ANNOUNCE; |
state = ANNOUNCE; |
379 |
nclaims = 0; |
nclaims = 0; |
380 |
VDBG("announce/%d %s@%s\n", |
VDBG("announce/%u %s@%s\n", |
381 |
nclaims, intf, inet_ntoa(ip)); |
nclaims, argv_intf, inet_ntoa(ip)); |
382 |
arp(fd, &saddr, ARPOP_REQUEST, |
arp(/* ARPOP_REQUEST, */ |
383 |
ð_addr, ip, |
/* ð_addr, */ ip, |
384 |
ð_addr, ip); |
ð_addr, ip); |
385 |
timeout = ANNOUNCE_INTERVAL * 1000; |
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
386 |
break; |
break; |
387 |
case ANNOUNCE: |
case ANNOUNCE: |
388 |
// timeouts in the ANNOUNCE state mean no conflicting ARP packets |
// timeouts in the ANNOUNCE state mean no conflicting ARP packets |
389 |
// have been received, so we can progress through the states |
// have been received, so we can progress through the states |
390 |
if (nclaims < ANNOUNCE_NUM) { |
if (nclaims < ANNOUNCE_NUM) { |
391 |
nclaims++; |
nclaims++; |
392 |
VDBG("announce/%d %s@%s\n", |
VDBG("announce/%u %s@%s\n", |
393 |
nclaims, intf, inet_ntoa(ip)); |
nclaims, argv_intf, inet_ntoa(ip)); |
394 |
arp(fd, &saddr, ARPOP_REQUEST, |
arp(/* ARPOP_REQUEST, */ |
395 |
ð_addr, ip, |
/* ð_addr, */ ip, |
396 |
ð_addr, ip); |
ð_addr, ip); |
397 |
timeout = ANNOUNCE_INTERVAL * 1000; |
timeout_ms = ANNOUNCE_INTERVAL * 1000; |
398 |
} |
} |
399 |
else { |
else { |
400 |
// Switch to monitor state. |
// Switch to monitor state. |
401 |
state = MONITOR; |
state = MONITOR; |
402 |
// link is ok to use earlier |
// link is ok to use earlier |
403 |
// FIXME update filters |
// FIXME update filters |
404 |
run(script, "config", intf, &ip); |
run(argv, "config", &ip); |
405 |
ready = 1; |
ready = 1; |
406 |
conflicts = 0; |
conflicts = 0; |
407 |
timeout = -1; // Never timeout in the monitor state. |
timeout_ms = -1; // Never timeout in the monitor state. |
408 |
|
|
409 |
// NOTE: all other exit paths |
// NOTE: all other exit paths |
410 |
// should deconfig ... |
// should deconfig ... |
415 |
case DEFEND: |
case DEFEND: |
416 |
// We won! No ARP replies, so just go back to monitor. |
// We won! No ARP replies, so just go back to monitor. |
417 |
state = MONITOR; |
state = MONITOR; |
418 |
timeout = -1; |
timeout_ms = -1; |
419 |
conflicts = 0; |
conflicts = 0; |
420 |
break; |
break; |
421 |
default: |
default: |
422 |
// Invalid, should never happen. Restart the whole protocol. |
// Invalid, should never happen. Restart the whole protocol. |
423 |
state = PROBE; |
state = PROBE; |
424 |
pick(&ip); |
ip.s_addr = pick(); |
425 |
timeout = 0; |
timeout_ms = 0; |
426 |
nprobes = 0; |
nprobes = 0; |
427 |
nclaims = 0; |
nclaims = 0; |
428 |
break; |
break; |
429 |
} // switch (state) |
} // switch (state) |
430 |
break; // case 0 (timeout) |
break; // case 0 (timeout) |
431 |
// packets arriving |
|
432 |
|
// packets arriving, or link went down |
433 |
case 1: |
case 1: |
434 |
// We need to adjust the timeout in case we didn't receive |
// We need to adjust the timeout in case we didn't receive |
435 |
// a conflicting packet. |
// a conflicting packet. |
436 |
if (timeout > 0) { |
if (timeout_ms > 0) { |
437 |
struct timeval tv2; |
unsigned diff = deadline_us - MONOTONIC_US(); |
438 |
|
if ((int)(diff) < 0) { |
|
gettimeofday(&tv2, NULL); |
|
|
if (timercmp(&tv1, &tv2, <)) { |
|
439 |
// Current time is greater than the expected timeout time. |
// Current time is greater than the expected timeout time. |
440 |
// Should never happen. |
// Should never happen. |
441 |
VDBG("missed an expected timeout\n"); |
VDBG("missed an expected timeout\n"); |
442 |
timeout = 0; |
timeout_ms = 0; |
443 |
} else { |
} else { |
444 |
VDBG("adjusting timeout\n"); |
VDBG("adjusting timeout\n"); |
445 |
timersub(&tv1, &tv2, &tv1); |
timeout_ms = (diff / 1000) | 1; /* never 0 */ |
|
timeout = 1000 * tv1.tv_sec |
|
|
+ tv1.tv_usec / 1000; |
|
446 |
} |
} |
447 |
} |
} |
448 |
|
|
450 |
if (fds[0].revents & POLLERR) { |
if (fds[0].revents & POLLERR) { |
451 |
// FIXME: links routinely go down; |
// FIXME: links routinely go down; |
452 |
// this shouldn't necessarily exit. |
// this shouldn't necessarily exit. |
453 |
bb_error_msg("%s: poll error", intf); |
bb_error_msg("iface %s is down", argv_intf); |
454 |
if (ready) { |
if (ready) { |
455 |
run(script, "deconfig", |
run(argv, "deconfig", &ip); |
|
intf, &ip); |
|
456 |
} |
} |
457 |
return EXIT_FAILURE; |
return EXIT_FAILURE; |
458 |
} |
} |
460 |
} |
} |
461 |
|
|
462 |
// read ARP packet |
// read ARP packet |
463 |
if (recv(fd, &p, sizeof (p), 0) < 0) { |
if (safe_read(sock_fd, &p, sizeof(p)) < 0) { |
464 |
why = "recv"; |
bb_perror_msg_and_die(bb_msg_read_error); |
|
goto bad; |
|
465 |
} |
} |
466 |
if (p.hdr.ether_type != htons(ETHERTYPE_ARP)) |
if (p.eth.ether_type != htons(ETHERTYPE_ARP)) |
467 |
continue; |
continue; |
|
|
|
468 |
#ifdef DEBUG |
#ifdef DEBUG |
469 |
{ |
{ |
470 |
struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha; |
struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha; |
471 |
struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha; |
struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha; |
472 |
struct in_addr * spa = (struct in_addr *) p.arp.arp_spa; |
struct in_addr *spa = (struct in_addr *) p.arp.arp_spa; |
473 |
struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa; |
struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa; |
474 |
VDBG("%s recv arp type=%d, op=%d,\n", |
VDBG("%s recv arp type=%d, op=%d,\n", |
475 |
intf, ntohs(p.hdr.ether_type), |
argv_intf, ntohs(p.eth.ether_type), |
476 |
ntohs(p.arp.arp_op)); |
ntohs(p.arp.arp_op)); |
477 |
VDBG("\tsource=%s %s\n", |
VDBG("\tsource=%s %s\n", |
478 |
ether_ntoa(sha), |
ether_ntoa(sha), |
483 |
} |
} |
484 |
#endif |
#endif |
485 |
if (p.arp.arp_op != htons(ARPOP_REQUEST) |
if (p.arp.arp_op != htons(ARPOP_REQUEST) |
486 |
&& p.arp.arp_op != htons(ARPOP_REPLY)) |
&& p.arp.arp_op != htons(ARPOP_REPLY)) |
487 |
continue; |
continue; |
488 |
|
|
489 |
if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && |
source_ip_conflict = 0; |
490 |
memcmp(ð_addr, &p.arp.arp_sha, ETH_ALEN) != 0) { |
target_ip_conflict = 0; |
491 |
|
|
492 |
|
if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 |
493 |
|
&& memcmp(&p.arp.arp_sha, ð_addr, ETH_ALEN) != 0 |
494 |
|
) { |
495 |
source_ip_conflict = 1; |
source_ip_conflict = 1; |
496 |
} |
} |
497 |
if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && |
if (p.arp.arp_op == htons(ARPOP_REQUEST) |
498 |
p.arp.arp_op == htons(ARPOP_REQUEST) && |
&& memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 |
499 |
memcmp(ð_addr, &p.arp.arp_tha, ETH_ALEN) != 0) { |
&& memcmp(&p.arp.arp_tha, ð_addr, ETH_ALEN) != 0 |
500 |
|
) { |
501 |
target_ip_conflict = 1; |
target_ip_conflict = 1; |
502 |
} |
} |
503 |
|
|
511 |
if (source_ip_conflict || target_ip_conflict) { |
if (source_ip_conflict || target_ip_conflict) { |
512 |
conflicts++; |
conflicts++; |
513 |
if (conflicts >= MAX_CONFLICTS) { |
if (conflicts >= MAX_CONFLICTS) { |
514 |
VDBG("%s ratelimit\n", intf); |
VDBG("%s ratelimit\n", argv_intf); |
515 |
timeout = RATE_LIMIT_INTERVAL * 1000; |
timeout_ms = RATE_LIMIT_INTERVAL * 1000; |
516 |
state = RATE_LIMIT_PROBE; |
state = RATE_LIMIT_PROBE; |
517 |
} |
} |
518 |
|
|
519 |
// restart the whole protocol |
// restart the whole protocol |
520 |
pick(&ip); |
ip.s_addr = pick(); |
521 |
timeout = 0; |
timeout_ms = 0; |
522 |
nprobes = 0; |
nprobes = 0; |
523 |
nclaims = 0; |
nclaims = 0; |
524 |
} |
} |
528 |
if (source_ip_conflict) { |
if (source_ip_conflict) { |
529 |
VDBG("monitor conflict -- defending\n"); |
VDBG("monitor conflict -- defending\n"); |
530 |
state = DEFEND; |
state = DEFEND; |
531 |
timeout = DEFEND_INTERVAL * 1000; |
timeout_ms = DEFEND_INTERVAL * 1000; |
532 |
arp(fd, &saddr, |
arp(/* ARPOP_REQUEST, */ |
533 |
ARPOP_REQUEST, |
/* ð_addr, */ ip, |
534 |
ð_addr, ip, |
ð_addr, ip); |
|
ð_addr, ip); |
|
535 |
} |
} |
536 |
break; |
break; |
537 |
case DEFEND: |
case DEFEND: |
540 |
state = PROBE; |
state = PROBE; |
541 |
VDBG("defend conflict -- starting over\n"); |
VDBG("defend conflict -- starting over\n"); |
542 |
ready = 0; |
ready = 0; |
543 |
run(script, "deconfig", intf, &ip); |
run(argv, "deconfig", &ip); |
544 |
|
|
545 |
// restart the whole protocol |
// restart the whole protocol |
546 |
pick(&ip); |
ip.s_addr = pick(); |
547 |
timeout = 0; |
timeout_ms = 0; |
548 |
nprobes = 0; |
nprobes = 0; |
549 |
nclaims = 0; |
nclaims = 0; |
550 |
} |
} |
553 |
// Invalid, should never happen. Restart the whole protocol. |
// Invalid, should never happen. Restart the whole protocol. |
554 |
VDBG("invalid state -- starting over\n"); |
VDBG("invalid state -- starting over\n"); |
555 |
state = PROBE; |
state = PROBE; |
556 |
pick(&ip); |
ip.s_addr = pick(); |
557 |
timeout = 0; |
timeout_ms = 0; |
558 |
nprobes = 0; |
nprobes = 0; |
559 |
nclaims = 0; |
nclaims = 0; |
560 |
break; |
break; |
561 |
} // switch state |
} // switch state |
|
|
|
562 |
break; // case 1 (packets arriving) |
break; // case 1 (packets arriving) |
|
default: |
|
|
why = "poll"; |
|
|
goto bad; |
|
563 |
} // switch poll |
} // switch poll |
564 |
} |
} // while (1) |
565 |
bad: |
#undef argv_intf |
|
bb_perror_msg("%s, %s", intf, why); |
|
|
return EXIT_FAILURE; |
|
566 |
} |
} |