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' },

Reply via email to