Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/networking/zcip.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/networking/zcip.c
r902 r1770 1 /* vi: set sw=4 ts=4: */ 1 2 /* 2 3 * RFC3927 ZeroConf IPv4 Link-Local addressing … … 23 24 // - link status monitoring (restart on link-up; stop on link-down) 24 25 25 #include "busybox.h"26 #include <errno.h>27 #include <string.h>28 26 #include <syslog.h> 29 27 #include <poll.h> 30 #include <time.h>31 32 28 #include <sys/wait.h> 33 34 29 #include <netinet/ether.h> 35 30 #include <net/ethernet.h> 36 31 #include <net/if.h> 37 32 #include <net/if_arp.h> 38 39 33 #include <linux/if_packet.h> 40 34 #include <linux/sockios.h> 41 35 36 #include "libbb.h" 37 38 /* We don't need more than 32 bits of the counter */ 39 #define MONOTONIC_US() ((unsigned)monotonic_us()) 42 40 43 41 struct arp_packet { … … 72 70 }; 73 71 74 /* Implicitly zero-initialized */ 75 static const struct in_addr null_ip; 76 static const struct ether_addr null_addr; 77 static int verbose; 78 79 #define DBG(fmt,args...) \ 72 #define VDBG(fmt,args...) \ 80 73 do { } while (0) 81 #define VDBG DBG82 74 83 75 /** … … 87 79 static void pick(struct in_addr *ip) 88 80 { 89 unsigned tmp; 90 91 /* use cheaper math than lrand48() mod N */ 81 unsigned tmp; 82 92 83 do { 93 tmp = (lrand48() >> 16) & IN_CLASSB_HOST;84 tmp = rand() & IN_CLASSB_HOST; 94 85 } while (tmp > (IN_CLASSB_HOST - 0x0200)); 95 86 ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp); … … 99 90 * Broadcast an ARP packet. 100 91 */ 101 static intarp(int fd, struct sockaddr *saddr, int op,92 static void arp(int fd, struct sockaddr *saddr, int op, 102 93 const struct ether_addr *source_addr, struct in_addr source_ip, 103 94 const struct ether_addr *target_addr, struct in_addr target_ip) … … 118 109 p.arp.arp_op = htons(op); 119 110 memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN); 120 memcpy(&p.arp.arp_spa, &source_ip, sizeof 111 memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa)); 121 112 memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN); 122 memcpy(&p.arp.arp_tpa, &target_ip, sizeof 113 memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa)); 123 114 124 115 // send it 125 if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) { 126 perror("sendto"); 116 xsendto(fd, &p, sizeof(p), saddr, sizeof(*saddr)); 117 118 // Currently all callers ignore errors, that's why returns are 119 // commented out... 120 //return 0; 121 } 122 123 /** 124 * Run a script. argv[2] is already NULL. 125 */ 126 static int run(char *argv[3], const char *intf, struct in_addr *ip) 127 { 128 int status; 129 130 VDBG("%s run %s %s\n", intf, argv[0], argv[1]); 131 132 if (ip) { 133 char *addr = inet_ntoa(*ip); 134 setenv("ip", addr, 1); 135 bb_info_msg("%s %s %s", argv[1], intf, addr); 136 } 137 138 status = wait4pid(spawn(argv)); 139 if (status < 0) { 140 bb_perror_msg("%s %s", argv[1], intf); 127 141 return -errno; 128 142 } 129 return 0; 143 if (status != 0) 144 bb_error_msg("script %s %s failed, exitcode=%d", argv[0], argv[1], status); 145 return status; 130 146 } 131 147 132 148 /** 133 * R un a script.134 */ 135 static int run(char *script, char *arg, char *intf, struct in_addr *ip)149 * Return milliseconds of random delay, up to "secs" seconds. 150 */ 151 static unsigned ALWAYS_INLINE ms_rdelay(unsigned secs) 136 152 { 137 int pid, status; 138 char *why; 139 140 if (script != NULL) { 141 VDBG("%s run %s %s\n", intf, script, arg); 142 if (ip != NULL) { 143 char *addr = inet_ntoa(*ip); 144 setenv("ip", addr, 1); 145 syslog(LOG_INFO, "%s %s %s", arg, intf, addr); 146 } 147 148 pid = vfork(); 149 if (pid < 0) { // error 150 why = "vfork"; 151 goto bad; 152 } else if (pid == 0) { // child 153 execl(script, script, arg, NULL); 154 perror("execl"); 155 _exit(EXIT_FAILURE); 156 } 157 158 if (waitpid(pid, &status, 0) <= 0) { 159 why = "waitpid"; 160 goto bad; 161 } 162 if (WEXITSTATUS(status) != 0) { 163 bb_error_msg("script %s failed, exit=%d\n", 164 script, WEXITSTATUS(status)); 165 return -errno; 153 return rand() % (secs * 1000); 154 } 155 156 /** 157 * main program 158 */ 159 int zcip_main(int argc, char **argv); 160 int zcip_main(int argc, char **argv) 161 { 162 int state = PROBE; 163 struct ether_addr eth_addr; 164 const char *why; 165 int fd; 166 char *r_opt; 167 unsigned opts; 168 169 /* Ugly trick, but I want these zeroed in one go */ 170 struct { 171 const struct in_addr null_ip; 172 const struct ether_addr null_addr; 173 struct sockaddr saddr; 174 struct in_addr ip; 175 struct ifreq ifr; 176 char *intf; 177 char *script_av[3]; 178 int timeout_ms; /* must be signed */ 179 unsigned conflicts; 180 unsigned nprobes; 181 unsigned nclaims; 182 int ready; 183 int verbose; 184 } L; 185 #define null_ip (L.null_ip ) 186 #define null_addr (L.null_addr ) 187 #define saddr (L.saddr ) 188 #define ip (L.ip ) 189 #define ifr (L.ifr ) 190 #define intf (L.intf ) 191 #define script_av (L.script_av ) 192 #define timeout_ms (L.timeout_ms) 193 #define conflicts (L.conflicts ) 194 #define nprobes (L.nprobes ) 195 #define nclaims (L.nclaims ) 196 #define ready (L.ready ) 197 #define verbose (L.verbose ) 198 199 memset(&L, 0, sizeof(L)); 200 201 #define FOREGROUND (opts & 1) 202 #define QUIT (opts & 2) 203 // parse commandline: prog [options] ifname script 204 // exactly 2 args; -v accumulates and implies -f 205 opt_complementary = "=2:vv:vf"; 206 opts = getopt32(argv, "fqr:v", &r_opt, &verbose); 207 if (!FOREGROUND) { 208 /* Do it early, before all bb_xx_msg calls */ 209 openlog(applet_name, 0, LOG_DAEMON); 210 logmode |= LOGMODE_SYSLOG; 211 } 212 if (opts & 4) { // -r n.n.n.n 213 if (inet_aton(r_opt, &ip) == 0 214 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR 215 ) { 216 bb_error_msg_and_die("invalid link address"); 166 217 } 167 218 } 168 return 0; 169 bad: 170 status = -errno; 171 syslog(LOG_ERR, "%s %s, %s error: %s", 172 arg, intf, why, strerror(errno)); 173 return status; 174 } 175 176 177 /** 178 * Return milliseconds of random delay, up to "secs" seconds. 179 */ 180 static inline unsigned ms_rdelay(unsigned secs) 181 { 182 return lrand48() % (secs * 1000); 183 } 184 185 /** 186 * main program 187 */ 188 189 int zcip_main(int argc, char *argv[]) 190 { 191 char *intf = NULL; 192 char *script = NULL; 193 int quit = 0; 194 int foreground = 0; 195 196 char *why; 197 struct sockaddr saddr; 198 struct ether_addr addr; 199 struct in_addr ip = { 0 }; 200 int fd; 201 int ready = 0; 202 suseconds_t timeout = 0; // milliseconds 203 unsigned conflicts = 0; 204 unsigned nprobes = 0; 205 unsigned nclaims = 0; 206 int t; 207 int state = PROBE; 208 209 // parse commandline: prog [options] ifname script 210 while ((t = getopt(argc, argv, "fqr:v")) != EOF) { 211 switch (t) { 212 case 'f': 213 foreground = 1; 214 continue; 215 case 'q': 216 quit = 1; 217 continue; 218 case 'r': 219 if (inet_aton(optarg, &ip) == 0 220 || (ntohl(ip.s_addr) & IN_CLASSB_NET) 221 != LINKLOCAL_ADDR) { 222 bb_error_msg_and_die("invalid link address"); 223 } 224 continue; 225 case 'v': 226 verbose++; 227 foreground = 1; 228 continue; 229 default: 230 bb_error_msg_and_die("bad option"); 231 } 232 } 233 if (optind < argc - 1) { 234 intf = argv[optind++]; 235 setenv("interface", intf, 1); 236 script = argv[optind++]; 237 } 238 if (optind != argc || !intf) 239 bb_show_usage(); 240 openlog(bb_applet_name, 0, LOG_DAEMON); 219 // On NOMMU reexec early (or else we will rerun things twice) 220 #if !BB_MMU 221 if (!FOREGROUND) 222 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); 223 #endif 224 argc -= optind; 225 argv += optind; 226 227 intf = argv[0]; 228 script_av[0] = argv[1]; 229 setenv("interface", intf, 1); 241 230 242 231 // initialize the interface (modprobe, ifup, etc) 243 if (run(script, "init", intf, NULL) < 0) 232 script_av[1] = (char*)"init"; 233 if (run(script_av, intf, NULL)) 244 234 return EXIT_FAILURE; 245 235 246 236 // initialize saddr 247 memset(&saddr, 0, sizeof(saddr));248 safe_strncpy(saddr.sa_data, intf, sizeof 237 //memset(&saddr, 0, sizeof(saddr)); 238 safe_strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data)); 249 239 250 240 // open an ARP socket 251 if ((fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) < 0) { 252 why = "open"; 253 fail: 254 foreground = 1; 255 goto bad; 256 } 241 fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); 257 242 // bind to the interface's ARP socket 258 if (bind(fd, &saddr, sizeof (saddr)) < 0) { 259 why = "bind"; 260 goto fail; 261 } else { 262 struct ifreq ifr; 263 unsigned short seed[3]; 264 265 // get the interface's ethernet address 266 memset(&ifr, 0, sizeof (ifr)); 267 strncpy(ifr.ifr_name, intf, sizeof (ifr.ifr_name)); 268 if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 269 why = "get ethernet address"; 270 goto fail; 271 } 272 memcpy(&addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); 273 274 // start with some stable ip address, either a function of 275 // the hardware address or else the last address we used. 276 // NOTE: the sequence of addresses we try changes only 277 // depending on when we detect conflicts. 278 memcpy(seed, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); 279 seed48(seed); 280 if (ip.s_addr == 0) 281 pick(&ip); 282 } 243 xbind(fd, &saddr, sizeof(saddr)); 244 245 // get the interface's ethernet address 246 //memset(&ifr, 0, sizeof(ifr)); 247 strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name)); 248 xioctl(fd, SIOCGIFHWADDR, &ifr); 249 memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); 250 251 // start with some stable ip address, either a function of 252 // the hardware address or else the last address we used. 253 // NOTE: the sequence of addresses we try changes only 254 // depending on when we detect conflicts. 255 srand(*(unsigned*)&ifr.ifr_hwaddr.sa_data); 256 if (ip.s_addr == 0) 257 pick(&ip); 283 258 284 259 // FIXME cases to handle: … … 287 262 288 263 // daemonize now; don't delay system startup 289 if (!foreground) { 290 if (daemon(0, verbose) < 0) { 291 why = "daemon"; 292 goto bad; 293 } 294 syslog(LOG_INFO, "start, interface %s", intf); 264 if (!FOREGROUND) { 265 #if BB_MMU 266 bb_daemonize(DAEMON_CHDIR_ROOT); 267 #endif 268 bb_info_msg("start, interface %s", intf); 295 269 } 296 270 … … 305 279 while (1) { 306 280 struct pollfd fds[1]; 307 struct timeval tv1;281 unsigned deadline_us; 308 282 struct arp_packet p; 283 284 int source_ip_conflict = 0; 285 int target_ip_conflict = 0; 309 286 310 287 fds[0].fd = fd; … … 312 289 fds[0].revents = 0; 313 290 314 int source_ip_conflict = 0;315 int target_ip_conflict = 0;316 317 291 // poll, being ready to adjust current timeout 318 if (!timeout ) {319 timeout = ms_rdelay(PROBE_WAIT);292 if (!timeout_ms) { 293 timeout_ms = ms_rdelay(PROBE_WAIT); 320 294 // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to 321 295 // make the kernel filter out all packets except 322 296 // ones we'd care about. 323 297 } 324 // set tv1 to the point in time when we timeout 325 gettimeofday(&tv1, NULL); 326 tv1.tv_usec += (timeout % 1000) * 1000; 327 while (tv1.tv_usec > 1000000) { 328 tv1.tv_usec -= 1000000; 329 tv1.tv_sec++; 330 } 331 tv1.tv_sec += timeout / 1000; 332 333 VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n", 334 timeout, intf, nprobes, nclaims); 335 switch (poll(fds, 1, timeout)) { 298 // set deadline_us to the point in time when we timeout 299 deadline_us = MONOTONIC_US() + timeout_ms * 1000; 300 301 VDBG("...wait %d %s nprobes=%u, nclaims=%u\n", 302 timeout_ms, intf, nprobes, nclaims); 303 switch (poll(fds, 1, timeout_ms)) { 336 304 337 305 // timeout … … 340 308 switch (state) { 341 309 case PROBE: 342 // timeouts in the PROBE state mean sno conflicting ARP packets310 // timeouts in the PROBE state mean no conflicting ARP packets 343 311 // have been received, so we can progress through the states 344 312 if (nprobes < PROBE_NUM) { 345 313 nprobes++; 346 VDBG("probe/% d%s@%s\n",314 VDBG("probe/%u %s@%s\n", 347 315 nprobes, intf, inet_ntoa(ip)); 348 (void)arp(fd, &saddr, ARPOP_REQUEST,349 & addr, null_ip,316 arp(fd, &saddr, ARPOP_REQUEST, 317 ð_addr, null_ip, 350 318 &null_addr, ip); 351 timeout = PROBE_MIN * 1000; 352 timeout += ms_rdelay(PROBE_MAX 353 - PROBE_MIN); 319 timeout_ms = PROBE_MIN * 1000; 320 timeout_ms += ms_rdelay(PROBE_MAX - PROBE_MIN); 354 321 } 355 322 else { … … 357 324 state = ANNOUNCE; 358 325 nclaims = 0; 359 VDBG("announce/% d%s@%s\n",326 VDBG("announce/%u %s@%s\n", 360 327 nclaims, intf, inet_ntoa(ip)); 361 (void)arp(fd, &saddr, ARPOP_REQUEST,362 & addr, ip,363 & addr, ip);364 timeout = ANNOUNCE_INTERVAL * 1000;328 arp(fd, &saddr, ARPOP_REQUEST, 329 ð_addr, ip, 330 ð_addr, ip); 331 timeout_ms = ANNOUNCE_INTERVAL * 1000; 365 332 } 366 333 break; 367 334 case RATE_LIMIT_PROBE: 368 // timeouts in the RATE_LIMIT_PROBE state mean sno conflicting ARP packets335 // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets 369 336 // have been received, so we can move immediately to the announce state 370 337 state = ANNOUNCE; 371 338 nclaims = 0; 372 VDBG("announce/% d%s@%s\n",339 VDBG("announce/%u %s@%s\n", 373 340 nclaims, intf, inet_ntoa(ip)); 374 (void)arp(fd, &saddr, ARPOP_REQUEST,375 & addr, ip,376 & addr, ip);377 timeout = ANNOUNCE_INTERVAL * 1000;341 arp(fd, &saddr, ARPOP_REQUEST, 342 ð_addr, ip, 343 ð_addr, ip); 344 timeout_ms = ANNOUNCE_INTERVAL * 1000; 378 345 break; 379 346 case ANNOUNCE: 380 // timeouts in the ANNOUNCE state mean sno conflicting ARP packets347 // timeouts in the ANNOUNCE state mean no conflicting ARP packets 381 348 // have been received, so we can progress through the states 382 349 if (nclaims < ANNOUNCE_NUM) { 383 350 nclaims++; 384 VDBG("announce/% d%s@%s\n",351 VDBG("announce/%u %s@%s\n", 385 352 nclaims, intf, inet_ntoa(ip)); 386 (void)arp(fd, &saddr, ARPOP_REQUEST,387 & addr, ip,388 & addr, ip);389 timeout = ANNOUNCE_INTERVAL * 1000;353 arp(fd, &saddr, ARPOP_REQUEST, 354 ð_addr, ip, 355 ð_addr, ip); 356 timeout_ms = ANNOUNCE_INTERVAL * 1000; 390 357 } 391 358 else { … … 394 361 // link is ok to use earlier 395 362 // FIXME update filters 396 run(script, "config", intf, &ip); 363 script_av[1] = (char*)"config"; 364 run(script_av, intf, &ip); 397 365 ready = 1; 398 366 conflicts = 0; 399 timeout = -1; // Never timeout in the monitor state.400 401 // NOTE: 367 timeout_ms = -1; // Never timeout in the monitor state. 368 369 // NOTE: all other exit paths 402 370 // should deconfig ... 403 if ( quit)371 if (QUIT) 404 372 return EXIT_SUCCESS; 405 373 } … … 408 376 // We won! No ARP replies, so just go back to monitor. 409 377 state = MONITOR; 410 timeout = -1;378 timeout_ms = -1; 411 379 conflicts = 0; 412 380 break; … … 415 383 state = PROBE; 416 384 pick(&ip); 417 timeout = 0;385 timeout_ms = 0; 418 386 nprobes = 0; 419 387 nclaims = 0; … … 425 393 // We need to adjust the timeout in case we didn't receive 426 394 // a conflicting packet. 427 if (timeout > 0) { 428 struct timeval tv2; 429 430 gettimeofday(&tv2, NULL); 431 if (timercmp(&tv1, &tv2, <)) { 395 if (timeout_ms > 0) { 396 unsigned diff = deadline_us - MONOTONIC_US(); 397 if ((int)(diff) < 0) { 432 398 // Current time is greater than the expected timeout time. 433 399 // Should never happen. 434 400 VDBG("missed an expected timeout\n"); 435 timeout = 0;401 timeout_ms = 0; 436 402 } else { 437 403 VDBG("adjusting timeout\n"); 438 timersub(&tv1, &tv2, &tv1); 439 timeout = 1000 * tv1.tv_sec 440 + tv1.tv_usec / 1000; 404 timeout_ms = diff / 1000; 405 if (!timeout_ms) timeout_ms = 1; 441 406 } 442 407 } … … 446 411 // FIXME: links routinely go down; 447 412 // this shouldn't necessarily exit. 448 bb_error_msg("%s: poll error \n", intf);413 bb_error_msg("%s: poll error", intf); 449 414 if (ready) { 450 run(script, "deconfig",451 415 script_av[1] = (char*)"deconfig"; 416 run(script_av, intf, &ip); 452 417 } 453 418 return EXIT_FAILURE; … … 457 422 458 423 // read ARP packet 459 if (recv(fd, &p, sizeof 424 if (recv(fd, &p, sizeof(p), 0) < 0) { 460 425 why = "recv"; 461 426 goto bad; … … 486 451 487 452 if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 && 488 memcmp(& addr, &p.arp.arp_sha, ETH_ALEN) != 0) {453 memcmp(ð_addr, &p.arp.arp_sha, ETH_ALEN) != 0) { 489 454 source_ip_conflict = 1; 490 455 } 491 456 if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 && 492 457 p.arp.arp_op == htons(ARPOP_REQUEST) && 493 memcmp(& addr, &p.arp.arp_tha, ETH_ALEN) != 0) {458 memcmp(ð_addr, &p.arp.arp_tha, ETH_ALEN) != 0) { 494 459 target_ip_conflict = 1; 495 460 } 496 461 497 VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", 462 VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n", 498 463 state, source_ip_conflict, target_ip_conflict); 499 464 switch (state) { … … 506 471 if (conflicts >= MAX_CONFLICTS) { 507 472 VDBG("%s ratelimit\n", intf); 508 timeout = RATE_LIMIT_INTERVAL * 1000;473 timeout_ms = RATE_LIMIT_INTERVAL * 1000; 509 474 state = RATE_LIMIT_PROBE; 510 475 } … … 512 477 // restart the whole protocol 513 478 pick(&ip); 514 timeout = 0;479 timeout_ms = 0; 515 480 nprobes = 0; 516 481 nclaims = 0; … … 522 487 VDBG("monitor conflict -- defending\n"); 523 488 state = DEFEND; 524 timeout = DEFEND_INTERVAL * 1000;525 (void)arp(fd, &saddr,489 timeout_ms = DEFEND_INTERVAL * 1000; 490 arp(fd, &saddr, 526 491 ARPOP_REQUEST, 527 & addr, ip,528 & addr, ip);492 ð_addr, ip, 493 ð_addr, ip); 529 494 } 530 495 break; … … 535 500 VDBG("defend conflict -- starting over\n"); 536 501 ready = 0; 537 run(script, "deconfig", intf, &ip); 502 script_av[1] = (char*)"deconfig"; 503 run(script_av, intf, &ip); 538 504 539 505 // restart the whole protocol 540 506 pick(&ip); 541 timeout = 0;507 timeout_ms = 0; 542 508 nprobes = 0; 543 509 nclaims = 0; … … 549 515 state = PROBE; 550 516 pick(&ip); 551 timeout = 0;517 timeout_ms = 0; 552 518 nprobes = 0; 553 519 nclaims = 0; … … 561 527 } // switch poll 562 528 } 563 bad: 564 if (foreground) 565 perror(why); 566 else 567 syslog(LOG_ERR, "%s %s, %s error: %s", 568 bb_applet_name, intf, why, strerror(errno)); 529 bad: 530 bb_perror_msg("%s, %s", intf, why); 569 531 return EXIT_FAILURE; 570 532 }
Note:
See TracChangeset
for help on using the changeset viewer.