[radvd-devel-l] Linux infinite loop if interface goes down
Pekka Savola
pekkas at netcore.fi
Fri Aug 28 06:05:26 EDT 2009
On Thu, 27 Aug 2009, Teemu Torma wrote:
> The issue is that when interface comes back up, send_ra will call
> reload_config which will reset timers for all interfaces. However, the
> current timer event will continue processing as usual and at the end will
> reset the current timer. Since reload_config already did that, timer list
> pointers will be screwed and at least in my case cause an infinite loop.
>
> I haven't tried this, but it might suffice to ignore already existing timer
> entry in set_timer (i.e, tm->next && tm->prev).
>
> I don't know the code well enough to say if this is a good way to deal with
> the issue.
I like this approach. I tried to figure out a way to avoid timer list
corruption otherwise, but couldn't come up with anything that'd work
well.
I added a 'return 0' also to the branch where interface first failed
and added some comments. Reuben, could you test whether this works
for you too?
--
Pekka Savola "You each name yourselves king, yet the
Netcore Oy kingdom bleeds."
Systems. Networks. Security. -- George R.R. Martin: A Clash of Kings
-------------- next part --------------
diff -ur radvd-1.4/process.c radvd-1.4.reload/process.c
--- radvd-1.4/process.c 2009-08-03 16:00:51.000000000 +0300
+++ radvd-1.4.reload/process.c 2009-08-28 12:19:53.000000000 +0300
@@ -205,10 +205,10 @@
else {
/* no RA sent in a while, send an immediate multicast reply */
clear_timer(&iface->tm);
- send_ra_forall(sock, iface, NULL);
-
- next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
- set_timer(&iface->tm, next);
+ if (send_ra_forall(sock, iface, NULL) == 0) {
+ next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
+ set_timer(&iface->tm, next);
+ }
}
}
diff -ur radvd-1.4/radvd.c radvd-1.4.reload/radvd.c
--- radvd-1.4/radvd.c 2009-06-19 10:37:11.000000000 +0300
+++ radvd-1.4.reload/radvd.c 2009-08-28 12:19:53.000000000 +0300
@@ -339,7 +339,8 @@
dlog(LOG_DEBUG, 4, "timer_handler called for %s", iface->Name);
- send_ra_forall(sock, iface, NULL);
+ if (send_ra_forall(sock, iface, NULL) != 0)
+ return;
next = rand_between(iface->MinRtrAdvInterval, iface->MaxRtrAdvInterval);
@@ -389,13 +390,14 @@
break;
/* send an initial advertisement */
- send_ra_forall(sock, iface, NULL);
+ if (send_ra_forall(sock, iface, NULL) == 0) {
- iface->init_racount++;
+ iface->init_racount++;
- set_timer(&iface->tm,
- min(MAX_INITIAL_RTR_ADVERT_INTERVAL,
- iface->MaxRtrAdvInterval));
+ set_timer(&iface->tm,
+ min(MAX_INITIAL_RTR_ADVERT_INTERVAL,
+ iface->MaxRtrAdvInterval));
+ }
}
}
diff -ur radvd-1.4/radvd.h radvd-1.4.reload/radvd.h
--- radvd-1.4/radvd.h 2009-06-19 10:34:07.000000000 +0300
+++ radvd-1.4.reload/radvd.h 2009-08-28 12:19:53.000000000 +0300
@@ -217,8 +217,8 @@
int open_icmpv6_socket(void);
/* send.c */
-void send_ra(int, struct Interface *iface, struct in6_addr *dest);
-void send_ra_forall(int, struct Interface *iface, struct in6_addr *dest);
+int send_ra(int, struct Interface *iface, struct in6_addr *dest);
+int send_ra_forall(int, struct Interface *iface, struct in6_addr *dest);
/* process.c */
void process(int sock, struct Interface *, unsigned char *, int,
diff -ur radvd-1.4/send.c radvd-1.4.reload/send.c
--- radvd-1.4/send.c 2009-06-24 12:27:49.000000000 +0300
+++ radvd-1.4.reload/send.c 2009-08-28 12:35:07.000000000 +0300
@@ -26,16 +26,14 @@
* address only, but only if it was configured.
*
*/
-void
+int
send_ra_forall(int sock, struct Interface *iface, struct in6_addr *dest)
{
struct Clients *current;
/* If no list of clients was specified for this interface, we broadcast */
- if (iface->ClientList == NULL) {
- send_ra(sock, iface, dest);
- return;
- }
+ if (iface->ClientList == NULL)
+ return send_ra(sock, iface, dest);
/* If clients are configured, send the advertisement to all of them via unicast */
for (current = iface->ClientList; current; current = current->next)
@@ -53,10 +51,10 @@
/* If we should only send the RA to a specific address, we are done */
if (dest != NULL)
- return;
+ return 0;
}
if (dest == NULL)
- return;
+ return 0;
/* If we refused a client's solicitation, log it if debugging is high enough */
char address_text[INET6_ADDRSTRLEN];
@@ -65,9 +63,10 @@
inet_ntop(AF_INET6, dest, address_text, INET6_ADDRSTRLEN);
dlog(LOG_DEBUG, 5, "Not answering request from %s, not configured", address_text);
+ return 0;
}
-void
+int
send_ra(int sock, struct Interface *iface, struct in6_addr *dest)
{
uint8_t all_hosts_addr[] = {0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
@@ -96,13 +95,20 @@
flog(LOG_WARNING, "interface %s does not exist, ignoring the interface", iface->Name);
}
iface->HasFailed = 1;
+ /* not really a 'success', but we need to schedule new timers.. */
+ return 0;
} else {
/* check_device was successful, act if it has failed previously */
if (iface->HasFailed == 1) {
flog(LOG_WARNING, "interface %s seems to have come back up, trying to reinitialize", iface->Name);
iface->HasFailed = 0;
- /* XXX: reinitializes 'iface', so this probably isn't going to work until next send_ra().. */
- reload_config();
+ /*
+ * return -1 so timer_handler() doesn't schedule new timers,
+ * reload_config() will kick off new timers anyway. This avoids
+ * timer list corruption.
+ */
+ reload_config();
+ return -1;
}
}
@@ -375,4 +381,6 @@
else
dlog(LOG_DEBUG, 3, "sendmsg: %s", strerror(errno));
}
+
+ return 0;
}
More information about the radvd-devel-l
mailing list