3
0

zcip.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * RFC3927 ZeroConf IPv4 Link-Local addressing
  3. * (see <http://www.zeroconf.org/>)
  4. *
  5. * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
  6. * Copyright (C) 2004 by David Brownell
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  21. * 02111-1307 USA
  22. */
  23. /*
  24. * This can build as part of BusyBox or by itself:
  25. *
  26. * $(CROSS_COMPILE)cc -Os -Wall -DNO_BUSYBOX -DDEBUG -o zcip zcip.c
  27. *
  28. * ZCIP just manages the 169.254.*.* addresses. That network is not
  29. * routed at the IP level, though various proxies or bridges can
  30. * certainly be used. Its naming is built over multicast DNS.
  31. */
  32. // #define DEBUG
  33. // TODO:
  34. // - more real-world usage/testing, especially daemon mode
  35. // - kernel packet filters to reduce scheduling noise
  36. // - avoid silent script failures, especially under load...
  37. // - link status monitoring (restart on link-up; stop on link-down)
  38. #include <errno.h>
  39. #include <stdlib.h>
  40. #include <stdio.h>
  41. #include <string.h>
  42. #include <syslog.h>
  43. #include <poll.h>
  44. #include <time.h>
  45. #include <unistd.h>
  46. #include <sys/ioctl.h>
  47. #include <sys/types.h>
  48. #include <sys/wait.h>
  49. #include <sys/time.h>
  50. #include <sys/socket.h>
  51. #include <arpa/inet.h>
  52. #include <netinet/in.h>
  53. #include <netinet/ether.h>
  54. #include <net/ethernet.h>
  55. #include <net/if.h>
  56. #include <net/if_arp.h>
  57. #include <linux/if_packet.h>
  58. #include <linux/sockios.h>
  59. struct arp_packet {
  60. struct ether_header hdr;
  61. // FIXME this part is netinet/if_ether.h "struct ether_arp"
  62. struct arphdr arp;
  63. struct ether_addr source_addr;
  64. struct in_addr source_ip;
  65. struct ether_addr target_addr;
  66. struct in_addr target_ip;
  67. } __attribute__ ((__packed__));
  68. /* 169.254.0.0 */
  69. static const uint32_t LINKLOCAL_ADDR = 0xa9fe0000;
  70. /* protocol timeout parameters, specified in seconds */
  71. static const unsigned PROBE_WAIT = 1;
  72. static const unsigned PROBE_MIN = 1;
  73. static const unsigned PROBE_MAX = 2;
  74. static const unsigned PROBE_NUM = 3;
  75. static const unsigned MAX_CONFLICTS = 10;
  76. static const unsigned RATE_LIMIT_INTERVAL = 60;
  77. static const unsigned ANNOUNCE_WAIT = 2;
  78. static const unsigned ANNOUNCE_NUM = 2;
  79. static const unsigned ANNOUNCE_INTERVAL = 2;
  80. static const time_t DEFEND_INTERVAL = 10;
  81. static const unsigned char ZCIP_VERSION[] = "0.75 (18 April 2005)";
  82. static char *prog;
  83. static const struct in_addr null_ip = { 0 };
  84. static const struct ether_addr null_addr = { {0, 0, 0, 0, 0, 0} };
  85. static int verbose = 0;
  86. #ifdef DEBUG
  87. #define DBG(fmt,args...) \
  88. fprintf(stderr, "%s: " fmt , prog , ## args)
  89. #define VDBG(fmt,args...) do { \
  90. if (verbose) fprintf(stderr, "%s: " fmt , prog ,## args); \
  91. } while (0)
  92. #else
  93. #define DBG(fmt,args...) \
  94. do { } while (0)
  95. #define VDBG DBG
  96. #endif /* DEBUG */
  97. /**
  98. * Pick a random link local IP address on 169.254/16, except that
  99. * the first and last 256 addresses are reserved.
  100. */
  101. static void
  102. pick(struct in_addr *ip)
  103. {
  104. unsigned tmp;
  105. /* use cheaper math than lrand48() mod N */
  106. do {
  107. tmp = (lrand48() >> 16) & IN_CLASSB_HOST;
  108. } while (tmp > (IN_CLASSB_HOST - 0x0200));
  109. ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
  110. }
  111. /**
  112. * Broadcast an ARP packet.
  113. */
  114. static int
  115. arp(int fd, struct sockaddr *saddr, int op,
  116. const struct ether_addr *source_addr, struct in_addr source_ip,
  117. const struct ether_addr *target_addr, struct in_addr target_ip)
  118. {
  119. struct arp_packet p;
  120. // ether header
  121. p.hdr.ether_type = htons(ETHERTYPE_ARP);
  122. memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN);
  123. memset(p.hdr.ether_dhost, 0xff, ETH_ALEN);
  124. // arp request
  125. p.arp.ar_hrd = htons(ARPHRD_ETHER);
  126. p.arp.ar_pro = htons(ETHERTYPE_IP);
  127. p.arp.ar_hln = ETH_ALEN;
  128. p.arp.ar_pln = 4;
  129. p.arp.ar_op = htons(op);
  130. memcpy(&p.source_addr, source_addr, ETH_ALEN);
  131. memcpy(&p.source_ip, &source_ip, sizeof (p.source_ip));
  132. memcpy(&p.target_addr, target_addr, ETH_ALEN);
  133. memcpy(&p.target_ip, &target_ip, sizeof (p.target_ip));
  134. // send it
  135. if (sendto(fd, &p, sizeof (p), 0, saddr, sizeof (*saddr)) < 0) {
  136. perror("sendto");
  137. return -errno;
  138. }
  139. return 0;
  140. }
  141. /**
  142. * Run a script.
  143. */
  144. static int
  145. run(char *script, char *arg, char *intf, struct in_addr *ip)
  146. {
  147. int pid, status;
  148. char *why;
  149. if (script != NULL) {
  150. VDBG("%s run %s %s\n", intf, script, arg);
  151. if (ip != NULL) {
  152. char *addr = inet_ntoa(*ip);
  153. setenv("ip", addr, 1);
  154. syslog(LOG_INFO, "%s %s %s", arg, intf, addr);
  155. }
  156. pid = vfork();
  157. if (pid < 0) { // error
  158. why = "vfork";
  159. goto bad;
  160. } else if (pid == 0) { // child
  161. execl(script, script, arg, NULL);
  162. perror("execl");
  163. _exit(EXIT_FAILURE);
  164. }
  165. if (waitpid(pid, &status, 0) <= 0) {
  166. why = "waitpid";
  167. goto bad;
  168. }
  169. if (WEXITSTATUS(status) != 0) {
  170. fprintf(stderr, "%s: script %s failed, exit=%d\n",
  171. prog, script, WEXITSTATUS(status));
  172. return -errno;
  173. }
  174. }
  175. return 0;
  176. bad:
  177. status = -errno;
  178. syslog(LOG_ERR, "%s %s, %s error: %s",
  179. arg, intf, why, strerror(errno));
  180. return status;
  181. }
  182. #ifndef NO_BUSYBOX
  183. #include "busybox.h"
  184. #endif
  185. /**
  186. * Print usage information.
  187. */
  188. static void __attribute__ ((noreturn))
  189. usage(const char *msg)
  190. {
  191. fprintf(stderr, "%s: %s\n", prog, msg);
  192. #ifdef NO_BUSYBOX
  193. fprintf(stderr, "Usage: %s [OPTIONS] ifname script\n"
  194. "\t-f foreground mode (implied by -v)\n"
  195. "\t-q quit after address (no daemon)\n"
  196. "\t-r 169.254.x.x request this address first\n"
  197. "\t-v verbose; show version\n",
  198. prog);
  199. exit(0);
  200. #else
  201. bb_show_usage();
  202. #endif
  203. }
  204. /**
  205. * Return milliseconds of random delay, up to "secs" seconds.
  206. */
  207. static inline unsigned
  208. ms_rdelay(unsigned secs)
  209. {
  210. return lrand48() % (secs * 1000);
  211. }
  212. /**
  213. * main program
  214. */
  215. int
  216. main(int argc, char *argv[])
  217. __attribute__ ((weak, alias ("zcip_main")));
  218. int zcip_main(int argc, char *argv[])
  219. {
  220. char *intf = NULL;
  221. char *script = NULL;
  222. int quit = 0;
  223. int foreground = 0;
  224. char *why;
  225. struct sockaddr saddr;
  226. struct ether_addr addr;
  227. struct in_addr ip = { 0 };
  228. int fd;
  229. int ready = 0;
  230. suseconds_t timeout = 0; // milliseconds
  231. time_t defend = 0;
  232. unsigned conflicts = 0;
  233. unsigned nprobes = 0;
  234. unsigned nclaims = 0;
  235. int t;
  236. // parse commandline: prog [options] ifname script
  237. prog = argv[0];
  238. while ((t = getopt(argc, argv, "fqr:v")) != EOF) {
  239. switch (t) {
  240. case 'f':
  241. foreground = 1;
  242. continue;
  243. case 'q':
  244. quit = 1;
  245. continue;
  246. case 'r':
  247. if (inet_aton(optarg, &ip) == 0
  248. || (ntohl(ip.s_addr) & IN_CLASSB_NET)
  249. != LINKLOCAL_ADDR) {
  250. usage("invalid link address");
  251. }
  252. continue;
  253. case 'v':
  254. if (!verbose)
  255. printf("%s: version %s\n", prog, ZCIP_VERSION);
  256. verbose++;
  257. foreground = 1;
  258. continue;
  259. default:
  260. usage("bad option");
  261. }
  262. }
  263. if (optind < argc - 1) {
  264. intf = argv[optind++];
  265. setenv("interface", intf, 1);
  266. script = argv[optind++];
  267. }
  268. if (optind != argc || !intf)
  269. usage("wrong number of arguments");
  270. openlog(prog, 0, LOG_DAEMON);
  271. // initialize the interface (modprobe, ifup, etc)
  272. if (run(script, "init", intf, NULL) < 0)
  273. return EXIT_FAILURE;
  274. // initialize saddr
  275. memset(&saddr, 0, sizeof (saddr));
  276. strncpy(saddr.sa_data, intf, sizeof (saddr.sa_data));
  277. // open an ARP socket
  278. if ((fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP))) < 0) {
  279. why = "open";
  280. fail:
  281. foreground = 1;
  282. goto bad;
  283. }
  284. // bind to the interface's ARP socket
  285. if (bind(fd, &saddr, sizeof (saddr)) < 0) {
  286. why = "bind";
  287. goto fail;
  288. } else {
  289. struct ifreq ifr;
  290. short seed[3];
  291. // get the interface's ethernet address
  292. memset(&ifr, 0, sizeof (ifr));
  293. strncpy(ifr.ifr_name, intf, sizeof (ifr.ifr_name));
  294. if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
  295. why = "get ethernet address";
  296. goto fail;
  297. }
  298. memcpy(&addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
  299. // start with some stable ip address, either a function of
  300. // the hardware address or else the last address we used.
  301. // NOTE: the sequence of addresses we try changes only
  302. // depending on when we detect conflicts.
  303. memcpy(seed, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
  304. seed48(seed);
  305. if (ip.s_addr == 0)
  306. pick(&ip);
  307. }
  308. // FIXME cases to handle:
  309. // - zcip already running!
  310. // - link already has local address... just defend/update
  311. // daemonize now; don't delay system startup
  312. if (!foreground) {
  313. if (daemon(0, verbose) < 0) {
  314. why = "daemon";
  315. goto bad;
  316. }
  317. syslog(LOG_INFO, "start, interface %s", intf);
  318. }
  319. // run the dynamic address negotiation protocol,
  320. // restarting after address conflicts:
  321. // - start with some address we want to try
  322. // - short random delay
  323. // - arp probes to see if another host else uses it
  324. // - arp announcements that we're claiming it
  325. // - use it
  326. // - defend it, within limits
  327. while (1) {
  328. struct pollfd fds[1];
  329. struct timeval tv1;
  330. struct arp_packet p;
  331. fds[0].fd = fd;
  332. fds[0].events = POLLIN;
  333. fds[0].revents = 0;
  334. // poll, being ready to adjust current timeout
  335. if (timeout > 0) {
  336. gettimeofday(&tv1, NULL);
  337. tv1.tv_usec += (timeout % 1000) * 1000;
  338. while (tv1.tv_usec > 1000000) {
  339. tv1.tv_usec -= 1000000;
  340. tv1.tv_sec++;
  341. }
  342. tv1.tv_sec += timeout / 1000;
  343. } else if (timeout == 0) {
  344. timeout = ms_rdelay(PROBE_WAIT);
  345. // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to
  346. // make the kernel filter out all packets except
  347. // ones we'd care about.
  348. }
  349. VDBG("...wait %ld %s nprobes=%d, nclaims=%d\n",
  350. timeout, intf, nprobes, nclaims);
  351. switch (poll(fds, 1, timeout)) {
  352. // timeouts trigger protocol transitions
  353. case 0:
  354. // probes
  355. if (nprobes < PROBE_NUM) {
  356. nprobes++;
  357. VDBG("probe/%d %s@%s\n",
  358. nprobes, intf, inet_ntoa(ip));
  359. (void)arp(fd, &saddr, ARPOP_REQUEST,
  360. &addr, null_ip,
  361. &null_addr, ip);
  362. if (nprobes < PROBE_NUM) {
  363. timeout = PROBE_MIN * 1000;
  364. timeout += ms_rdelay(PROBE_MAX
  365. - PROBE_MIN);
  366. } else
  367. timeout = ANNOUNCE_WAIT * 1000;
  368. }
  369. // then announcements
  370. else if (nclaims < ANNOUNCE_NUM) {
  371. nclaims++;
  372. VDBG("announce/%d %s@%s\n",
  373. nclaims, intf, inet_ntoa(ip));
  374. (void)arp(fd, &saddr, ARPOP_REQUEST,
  375. &addr, ip,
  376. &addr, ip);
  377. if (nclaims < ANNOUNCE_NUM) {
  378. timeout = ANNOUNCE_INTERVAL * 1000;
  379. } else {
  380. // link is ok to use earlier
  381. run(script, "config", intf, &ip);
  382. ready = 1;
  383. conflicts = 0;
  384. timeout = -1;
  385. // NOTE: all other exit paths
  386. // should deconfig ...
  387. if (quit)
  388. return EXIT_SUCCESS;
  389. // FIXME update filters
  390. }
  391. }
  392. break;
  393. // packets arriving
  394. case 1:
  395. // maybe adjust timeout
  396. if (timeout > 0) {
  397. struct timeval tv2;
  398. gettimeofday(&tv2, NULL);
  399. if (timercmp(&tv1, &tv2, <)) {
  400. timeout = -1;
  401. } else {
  402. timersub(&tv1, &tv2, &tv1);
  403. timeout = 1000 * tv1.tv_sec
  404. + tv1.tv_usec / 1000;
  405. }
  406. }
  407. if ((fds[0].revents & POLLIN) == 0) {
  408. if (fds[0].revents & POLLERR) {
  409. // FIXME: links routinely go down;
  410. // this shouldn't necessarily exit.
  411. fprintf(stderr, "%s %s: poll error\n",
  412. prog, intf);
  413. if (ready) {
  414. run(script, "deconfig",
  415. intf, &ip);
  416. }
  417. return EXIT_FAILURE;
  418. }
  419. continue;
  420. }
  421. // read ARP packet
  422. if (recv(fd, &p, sizeof (p), 0) < 0) {
  423. why = "recv";
  424. goto bad;
  425. }
  426. if (p.hdr.ether_type != htons(ETHERTYPE_ARP))
  427. continue;
  428. VDBG("%s recv arp type=%d, op=%d,\n",
  429. intf, ntohs(p.hdr.ether_type),
  430. ntohs(p.arp.ar_op));
  431. VDBG("\tsource=%s %s\n",
  432. ether_ntoa(&p.source_addr),
  433. inet_ntoa(p.source_ip));
  434. VDBG("\ttarget=%s %s\n",
  435. ether_ntoa(&p.target_addr),
  436. inet_ntoa(p.target_ip));
  437. if (p.arp.ar_op != htons(ARPOP_REQUEST)
  438. && p.arp.ar_op != htons(ARPOP_REPLY))
  439. continue;
  440. // some cases are always conflicts
  441. if ((p.source_ip.s_addr == ip.s_addr)
  442. && (memcmp(&addr, &p.source_addr,
  443. ETH_ALEN) != 0)) {
  444. collision:
  445. VDBG("%s ARP conflict from %s\n", intf,
  446. ether_ntoa(&p.source_addr));
  447. if (ready) {
  448. time_t now = time(0);
  449. if ((defend + DEFEND_INTERVAL)
  450. < now) {
  451. defend = now;
  452. (void)arp(fd, &saddr,
  453. ARPOP_REQUEST,
  454. &addr, ip,
  455. &addr, ip);
  456. VDBG("%s defend\n", intf);
  457. timeout = -1;
  458. continue;
  459. }
  460. defend = now;
  461. ready = 0;
  462. run(script, "deconfig", intf, &ip);
  463. // FIXME rm filters: setsockopt(fd,
  464. // SO_DETACH_FILTER, ...)
  465. }
  466. conflicts++;
  467. if (conflicts >= MAX_CONFLICTS) {
  468. VDBG("%s ratelimit\n", intf);
  469. sleep(RATE_LIMIT_INTERVAL);
  470. }
  471. // restart the whole protocol
  472. pick(&ip);
  473. timeout = 0;
  474. nprobes = 0;
  475. nclaims = 0;
  476. }
  477. // two hosts probing one address is a collision too
  478. else if (p.target_ip.s_addr == ip.s_addr
  479. && nclaims == 0
  480. && p.arp.ar_op == htons(ARPOP_REQUEST)
  481. && memcmp(&addr, &p.target_addr,
  482. ETH_ALEN) != 0) {
  483. goto collision;
  484. }
  485. break;
  486. default:
  487. why = "poll";
  488. goto bad;
  489. }
  490. }
  491. bad:
  492. if (foreground)
  493. perror(why);
  494. else
  495. syslog(LOG_ERR, "%s %s, %s error: %s",
  496. prog, intf, why, strerror(errno));
  497. return EXIT_FAILURE;
  498. }