+ // Else discard.
+}
+
+//
+// Maximum number of actions to complete.
+// This is to avoid sending out too many packets
+// at once.
+#define MAX_ACTIONS 500
+
+int regular_cleanups(void)
+{
+ static sessionidt s = 0; // Next session to check for actions on.
+ tunnelidt t;
+ int count=0,i;
+ u16 r;
+ static clockt next_acct = 0;
+ int a;
+
+ log(3, 0, 0, 0, "Begin regular cleanup\n");
+
+ for (r = 1; r < MAXRADIUS; r++)
+ {
+ if (!radius[r].state)
+ continue;
+ if (radius[r].retry)
+ {
+ if (radius[r].retry <= TIME)
+ radiusretry(r);
+ } else
+ radius[r].retry = backoff(radius[r].try+1); // Is this really needed? --mo
+ }
+ for (t = 1; t <= config->cluster_highest_tunnelid; t++)
+ {
+ // check for expired tunnels
+ if (tunnel[t].die && tunnel[t].die <= TIME)
+ {
+ STAT(tunnel_timeout);
+ tunnelkill(t, "Expired");
+ continue;
+ }
+ // check for message resend
+ if (tunnel[t].retry && tunnel[t].controlc)
+ {
+ // resend pending messages as timeout on reply
+ if (tunnel[t].retry <= TIME)
+ {
+ controlt *c = tunnel[t].controls;
+ u8 w = tunnel[t].window;
+ tunnel[t].try++; // another try
+ if (tunnel[t].try > 5)
+ tunnelkill(t, "Timeout on control message"); // game over
+ else
+ while (c && w--)
+ {
+ tunnelsend(c->buf, c->length, t);
+ c = c->next;
+ }
+ }
+ }
+ // Send hello
+ if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < TIME + 600)
+ {
+ controlt *c = controlnew(6); // sending HELLO
+ controladd(c, t, 0); // send the message
+ log(3, tunnel[t].ip, 0, t, "Sending HELLO message\n");
+ }
+
+ // Check for tunnel changes requested from the CLI
+ if ((a = cli_tunnel_actions[t].action))
+ {
+ cli_tunnel_actions[t].action = 0;
+ if (a & CLI_TUN_KILL)
+ {
+ log(2, tunnel[t].ip, 0, t, "Dropping tunnel by CLI\n");
+ tunnelshutdown(t, "Requested by administrator");
+ }
+ }
+
+ }
+
+ count = 0;
+ for (i = 1; i <= config->cluster_highest_sessionid; i++)
+ {
+ s++;
+ if (s > config->cluster_highest_sessionid)
+ s = 1;
+
+ if (!session[s].tunnel) // Session isn't in use
+ continue;
+
+ if (!session[s].die && session[s].ip && !(session[s].flags & SF_IPCP_ACKED))
+ {
+ // IPCP has not completed yet. Resend
+ log(3, session[s].ip, s, session[s].tunnel, "No ACK for initial IPCP ConfigReq... resending\n");
+ sendipcp(session[s].tunnel, s);
+ }
+
+ // check for expired sessions
+ if (session[s].die && session[s].die <= TIME)
+ {
+ sessionkill(s, "Expired");
+ if (++count >= MAX_ACTIONS) break;
+ continue;
+ }
+
+ // Drop sessions who have not responded within IDLE_TIMEOUT seconds
+ if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
+ {
+ sessionkill(s, "No response to LCP ECHO requests");
+ STAT(session_timeout);
+ if (++count >= MAX_ACTIONS) break;
+ continue;
+ }
+
+ // No data in IDLE_TIMEOUT seconds, send LCP ECHO
+ if (session[s].user[0] && (time_now - session[s].last_packet >= ECHO_TIMEOUT))
+ {
+ u8 b[MAXCONTROL] = {0};
+
+ u8 *q = makeppp(b, sizeof(b), 0, 0, session[s].tunnel, s, PPPLCP);
+ if (!q)
+ {
+ log(3, session[s].ip, s, t, "failed to send ECHO packet.\n");
+ continue;
+ }
+
+ *q = EchoReq;
+ *(u8 *)(q + 1) = (time_now % 255); // ID
+ *(u16 *)(q + 2) = htons(8); // Length
+ *(u32 *)(q + 4) = 0; // Magic Number (not supported)
+
+ log(4, session[s].ip, s, session[s].tunnel, "No data in %d seconds, sending LCP ECHO\n",
+ (int)(time_now - session[s].last_packet));
+ tunnelsend(b, 24, session[s].tunnel); // send it
+ if (++count >= MAX_ACTIONS) break;
+ }
+
+ // Check for actions requested from the CLI
+ if ((a = cli_session_actions[s].action))
+ {
+ int send = 0;
+
+ cli_session_actions[s].action = 0;
+ if (a & CLI_SESS_KILL)
+ {
+ log(2, 0, s, session[s].tunnel, "Dropping session by CLI\n");
+ sessionshutdown(s, "Requested by administrator");
+ a = 0; // dead, no need to check for other actions
+ }
+
+ if (a & CLI_SESS_SNOOP)
+ {
+ log(2, 0, s, session[s].tunnel, "Snooping session by CLI (to %s:%d)\n",
+ inet_toa(cli_session_actions[s].snoop_ip), cli_session_actions[s].snoop_port);
+
+ session[s].snoop_ip = cli_session_actions[s].snoop_ip;
+ session[s].snoop_port = cli_session_actions[s].snoop_port;
+ send++;
+ }
+
+ if (a & CLI_SESS_NOSNOOP)
+ {
+ log(2, 0, s, session[s].tunnel, "Unsnooping session by CLI\n");
+ session[s].snoop_ip = 0;
+ session[s].snoop_port = 0;
+ send++;
+ }
+
+ if (a & CLI_SESS_THROTTLE)
+ {
+ log(2, 0, s, session[s].tunnel, "Throttling session by CLI (to %dkb/s up and %dkb/s down)\n",
+ cli_session_actions[s].throttle & 0xFFFF,
+ cli_session_actions[s].throttle >> 16);
+
+ throttle_session(s, cli_session_actions[s].throttle);
+ }
+
+ if (a & CLI_SESS_NOTHROTTLE)
+ {
+ log(2, 0, s, session[s].tunnel, "Un-throttling session by CLI\n");
+ throttle_session(s, 0);
+ }
+
+ if (send)
+ cluster_send_session(s);
+
+ if (++count >= MAX_ACTIONS) break;
+ }
+ }
+
+ if (*config->accounting_dir && next_acct <= TIME)
+ {
+ // Dump accounting data
+ next_acct = TIME + ACCT_TIME;
+ dump_acct_info();
+ }
+
+ if (count >= MAX_ACTIONS)
+ return 1; // Didn't finish!
+
+ log(3, 0, 0, 0, "End regular cleanup (%d actions), next in %d seconds\n", count, config->cleanup_interval);
+ return 0;
+}
+
+
+//
+// Are we in the middle of a tunnel update, or radius
+// requests??
+//
+int still_busy(void)
+{
+ int i;
+ static clockt last_talked = 0;
+ static clockt start_busy_wait = 0;
+ if (start_busy_wait == 0)
+ start_busy_wait = TIME;
+
+ for (i = config->cluster_highest_tunnelid ; i > 0 ; --i)
+ {
+ if (!tunnel[i].controlc)
+ continue;
+
+ if (last_talked != TIME)
+ {
+ log(2,0,0,0, "Tunnel %d still has un-acked control messages.\n", i);
+ last_talked = TIME;
+ }
+ return 1;
+ }
+
+ // We stop waiting for radius after BUSY_WAIT_TIME 1/10th seconds
+ if (abs(TIME - start_busy_wait) > BUSY_WAIT_TIME)
+ {
+ log(1, 0, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n");
+ return 0;
+ }
+
+ for (i = 1; i < MAXRADIUS; i++)
+ {
+ if (radius[i].state == RADIUSNULL)
+ continue;
+ if (radius[i].state == RADIUSWAIT)
+ continue;
+
+ if (last_talked != TIME)
+ {
+ log(2,0,0,0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session);
+ last_talked = TIME;
+ }
+ return 1;
+ }
+
+ return 0;