Package: ucarp Version: 1.1-2 Tags: patch Kernel 2.6.13-rc6-git8 Libc 2.3.5-8
The patch below addresses the following issues in ucarp-1.1-2. 1. In src/carp.c/docarp(), the "now" static global variable was not initialized before carp_setrun() was called. The result is that sc.sc_md_tmo was always less than "now" the first time through the loop, and ucarp always became a master. It recovered at the next advertisement from the real master, but the damage was already done. If the upscript set the IP address on an interface and also performed gratuitous arps, that corrupted arp tables and blocked access to the real master until the arp tables timed out. Instead of providing quick failover, this resulted in denial of service. "now" is now initialized before carp_setrun() is called. 2. In src/carp.c/carp_setrun(), the INIT state caused ucarp to become a master immediately, if the preempt switch had been specified, even if the base and skew values resulted in ucarp not being the preferred master. It recovered at the next advertisement from the preferred master, but the damage was already done. See the description in 1 above. The INIT state now always sets ucarp to the BACKUP state so that ucarp can listen on the network first to determine if it should become the preferred master. 3. If --shutdown (-z) was specified, the downscript would be run at exit, even if ucarp was already in the BACKUP state (src/carp.c/sighandler_exit()). This could result in the downscript being run twice in a row. In most scenarios, that was probably not an issue. In some cases, however, it is not desireable to run the downscript if the service was not up. In any case, it does not make logical sense to do so. Ucarp now does not run the downscript at exit if it is already in the BACKUP state (i.e., the service is not up). 4. In src/carp.c/carp_set_state(), changing to the BACKUP state always ran the downscript. Because ucarp changes from the INIT state to the BACKUP state, that meant that ucarp would always run the downscript when it started, even though the service presumeably was not up (or at least not started by this ucarp). In some cases, it may be beneficial to first run the downscript in order to perform cleanup and provide a consistent state. In other cases, it is undesireable to run the downscript if the service is not up. Changes have been made to globals.h, ucarp_p.h and ucarp.c to provide the --neutral (-n) switch, which causes ucarp to not run the downscript when it changes from the INIT state to the BACKUP state (i.e., it remains neutral when it starts). By default, then, the old behavior is retained. 5. The README has been modified to reflect the change in the --shutdown (-z) behavior and the addition of the --neutral (-n) switch. Russell Mosemann, Ph.D. Associate Profesor of Computer Science Concordia University, Nebraska diff -Naur ucarp-1.1/README ucarp-1.1-3/README --- ucarp-1.1/README 2004-09-02 17:07:42.000000000 -0500 +++ ucarp-1.1-3/README 2006-01-13 20:21:02.000000000 -0600 @@ -126,10 +126,23 @@ Please note that by default, and if everything's ok, a master will stay a master as long as possible. If you want a "preferred" master to immediately -become a master even if the other host is: +become a master even if another host is already the master, - add the --preempt (or -P) switch to *all* hosts - use a lower skew or a lower base for the "preferred" one. +When ucarp first runs, it starts as a backup and listens to the network to +determine if it should become the master. When it enters the backup state, +it normally runs the downscript. That can be useful to remove old +temporary files or clean up an interface that is in an unknown state. In +some circumstances, however, it is undesirable to run the downscript if +the service was not already up. In that case, use the --neutral (-n) switch +for ucarp to not run the downscript when it enters the backup state the +first time. All changes from the master state to the backup state after +that will run the downscript. + +--shutdown (-z) will run the downscript at exit, unless ucarp is already +in the backup state. + The "dead ratio" (--deadratio=...) knob basically changes how long a backup server will wait for an unresponsive master before considering it as dead, and becoming the new master. In the original protocol, the ratio is 3. This is diff -Naur ucarp-1.1/src/carp.c ucarp-1.1-3/src/carp.c --- ucarp-1.1/src/carp.c 2004-08-28 11:30:02.000000000 -0500 +++ ucarp-1.1-3/src/carp.c 2006-01-13 20:27:03.000000000 -0600 @@ -59,7 +59,8 @@ break; case BACKUP: logfile(LOG_WARNING, _("Switching to state: BACKUP")); - (void) spawn_handler(dev_desc_fd, downscript); + if ((sc->sc_state != INIT) || (neutral != 1)) + (void) spawn_handler(dev_desc_fd, downscript); gratuitous_arp(dev_desc_fd, 0); break; case MASTER: @@ -313,17 +314,8 @@ #endif switch (sc->sc_state) { case INIT: - if (preempt != 0 && carp_suppress_preempt == 0) { - carp_send_ad(sc); - gratuitous_arp(dev_desc_fd, 1); -#ifdef INET6 - carp_send_na(sc); -#endif /* INET6 */ - carp_set_state(sc, MASTER); - } else { - carp_set_state(sc, BACKUP); - carp_setrun(sc, 0); - } + carp_set_state(sc, BACKUP); + carp_setrun(sc, 0); break; case BACKUP: sc->sc_ad_tmo.tv_sec = 0; @@ -607,7 +599,8 @@ logfile(LOG_DEBUG, "sighandler_exit(): Calling [%s] and exiting", downscript); #endif - (void) spawn_handler(dev_desc_fd, downscript); + if (sc->sc_state != BACKUP) + (void) spawn_handler(dev_desc_fd, downscript); _exit(EXIT_SUCCESS); } @@ -687,14 +680,18 @@ pfds[0].fd = dev_desc_fd; pfds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; - carp_setrun(&sc, 0); - if (shutdown_at_exit != 0) { (void) signal(SIGINT, sighandler_exit); (void) signal(SIGQUIT, sighandler_exit); (void) signal(SIGTERM, sighandler_exit); } + if (gettimeofday(&now, NULL) != 0) { + logfile(LOG_WARNING, "initializing now to gettimeofday() failed: %s", + strerror(errno)); + } + carp_setrun(&sc, 0); + for (;;) { nfds = poll(pfds, (nfds_t) 1, sc.sc_advbase * 1000); if (nfds == -1 || diff -Naur ucarp-1.1/src/globals.h ucarp-1.1-3/src/globals.h --- ucarp-1.1/src/globals.h 2004-06-20 14:35:22.000000000 -0500 +++ ucarp-1.1-3/src/globals.h 2006-01-13 19:05:29.000000000 -0600 @@ -20,6 +20,7 @@ GLOBAL0(char *upscript); GLOBAL0(char *downscript); GLOBAL0(signed char preempt); +GLOBAL0(signed char neutral); GLOBAL0(signed char shutdown_at_exit); GLOBAL0(unsigned char hwaddr[6]); GLOBAL0(signed char no_syslog); diff -Naur ucarp-1.1/src/ucarp.c ucarp-1.1-3/src/ucarp.c --- ucarp-1.1/src/ucarp.c 2004-08-28 11:04:05.000000000 -0500 +++ ucarp-1.1-3/src/ucarp.c 2006-01-13 19:03:06.000000000 -0600 @@ -28,6 +28,7 @@ "--vhid=<id> (-v <id>): virtual IP identifier (1-255)\n" "--pass=<pass> (-p <pass>): password\n" "--preempt (-P): becomes a master as soon as possible\n" + "--neutral (-n): do not run downscript at start if backup\n" "--addr=<ip> (-a <ip>): virtual shared IP address\n" "--help (-h): summary of command-line options\n" "--advbase=<seconds> (-b <seconds>): advertisement frequency\n" @@ -127,6 +128,10 @@ preempt = 1; break; } + case 'n': { + neutral = 1; + break; + } case 'a': { if (inet_pton(AF_INET, optarg, &vaddr) == 0) { logfile(LOG_ERR, _("Invalid address: [%s]"), optarg); diff -Naur ucarp-1.1/src/ucarp_p.h ucarp-1.1-3/src/ucarp_p.h --- ucarp-1.1/src/ucarp_p.h 2004-08-28 11:02:51.000000000 -0500 +++ ucarp-1.1-3/src/ucarp_p.h 2006-01-13 19:03:33.000000000 -0600 @@ -1,7 +1,7 @@ #ifndef __CARP_P_H__ #define __CARP_P_H__ 1 -static const char *GETOPT_OPTIONS = "i:s:v:p:Pa:hb:k:u:d:r:zf:B"; +static const char *GETOPT_OPTIONS = "i:s:v:p:Pna:hb:k:u:d:r:zf:B"; static struct option long_options[] = { { "interface", 1, NULL, 'i' }, @@ -9,6 +9,7 @@ { "vhid", 1, NULL, 'v' }, { "pass", 1, NULL, 'p' }, { "preempt", 0, NULL, 'P' }, + { "neutral", 0, NULL, 'n' }, { "addr", 1, NULL, 'a' }, { "help", 0, NULL, 'h' }, { "advbase", 1, NULL, 'b' },