1
0
mirror of https://github.com/osmarks/ngircd.git synced 2024-12-12 09:50:29 +00:00

Handle "throttling" in a single function

ngIRCd uses "command throttling" and "bps throttling" (bytes per second).
The states are detected in different functions, Conn_Handler() and
Read_Request(), but handle the actual "throttling" in a common function:
this enables us to guarantee consistent behavior and to disable throttling
for special connections in only one place, eventually.
This commit is contained in:
Alexander Barton 2014-03-18 14:48:52 +01:00
parent 4c2acd55c6
commit 35f1db5f28

View File

@ -74,6 +74,9 @@
#define SD_LISTEN_FDS_START 3 /** systemd(8) socket activation offset */ #define SD_LISTEN_FDS_START 3 /** systemd(8) socket activation offset */
#define THROTTLE_CMDS 1 /** Throttling: max commands reached */
#define THROTTLE_BPS 2 /** Throttling: max bps reached */
static bool Handle_Write PARAMS(( CONN_ID Idx )); static bool Handle_Write PARAMS(( CONN_ID Idx ));
static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len )); static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
static int New_Connection PARAMS(( int Sock, bool IsSSL )); static int New_Connection PARAMS(( int Sock, bool IsSSL ));
@ -88,6 +91,8 @@ static void New_Server PARAMS(( int Server, ng_ipaddr_t *dest ));
static void Simple_Message PARAMS(( int Sock, const char *Msg )); static void Simple_Message PARAMS(( int Sock, const char *Msg ));
static int NewListener PARAMS(( const char *listen_addr, UINT16 Port )); static int NewListener PARAMS(( const char *listen_addr, UINT16 Port ));
static void Account_Connection PARAMS((void)); static void Account_Connection PARAMS((void));
static void Throttle_Connection PARAMS((const CONN_ID Idx, CLIENT *Client,
const int Reason, unsigned int Value));
static array My_Listeners; static array My_Listeners;
static array My_ConnArray; static array My_ConnArray;
@ -665,7 +670,7 @@ GLOBAL void
Conn_Handler(void) Conn_Handler(void)
{ {
int i; int i;
size_t wdatalen, bytes_processed; size_t wdatalen;
struct timeval tv; struct timeval tv;
time_t t; time_t t;
@ -684,17 +689,7 @@ Conn_Handler(void)
if ((My_Connections[i].sock > NONE) if ((My_Connections[i].sock > NONE)
&& (array_bytes(&My_Connections[i].rbuf) > 0)) { && (array_bytes(&My_Connections[i].rbuf) > 0)) {
/* ... and try to handle the received data */ /* ... and try to handle the received data */
bytes_processed = Handle_Buffer(i); Handle_Buffer(i);
/* if we processed data, and there might be
* more commands in the input buffer, do not
* try to read any more data now */
if (bytes_processed &&
array_bytes(&My_Connections[i].rbuf) > 2) {
LogDebug
("Throttling connection %d: command limit reached!",
i);
Conn_SetPenalty(i, 1);
}
} }
} }
@ -1573,8 +1568,8 @@ Read_Request( CONN_ID Idx )
{ {
/* Read buffer is full */ /* Read buffer is full */
Log(LOG_ERR, Log(LOG_ERR,
"Receive buffer space exhausted (connection %d): %d bytes", "Receive buffer space exhausted (connection %d): %d/%d bytes",
Idx, array_bytes(&My_Connections[Idx].rbuf)); Idx, array_bytes(&My_Connections[Idx].rbuf), READBUFFER_LEN);
Conn_Close(Idx, "Receive buffer space exhausted", NULL, false); Conn_Close(Idx, "Receive buffer space exhausted", NULL, false);
return; return;
} }
@ -1626,6 +1621,8 @@ Read_Request( CONN_ID Idx )
/* Update connection statistics */ /* Update connection statistics */
My_Connections[Idx].bytes_in += len; My_Connections[Idx].bytes_in += len;
/* Handle read buffer */
My_Connections[Idx].bps += Handle_Buffer(Idx); My_Connections[Idx].bps += Handle_Buffer(Idx);
/* Make sure that there is still a valid client registered */ /* Make sure that there is still a valid client registered */
@ -1651,14 +1648,8 @@ Read_Request( CONN_ID Idx )
} }
/* Look at the data in the (read-) buffer of this connection */ /* Look at the data in the (read-) buffer of this connection */
if (Client_Type(c) != CLIENT_SERVER if (My_Connections[Idx].bps >= maxbps)
&& Client_Type(c) != CLIENT_UNKNOWNSERVER Throttle_Connection(Idx, c, THROTTLE_BPS, maxbps);
&& Client_Type(c) != CLIENT_SERVICE
&& My_Connections[Idx].bps >= maxbps) {
LogDebug("Throttling connection %d: BPS exceeded! (%u >= %u)",
Idx, My_Connections[Idx].bps, maxbps);
Conn_SetPenalty(Idx, 1);
}
} /* Read_Request */ } /* Read_Request */
/** /**
@ -1827,6 +1818,11 @@ Handle_Buffer(CONN_ID Idx)
array_bytes(&My_Connections[Idx].rbuf)); array_bytes(&My_Connections[Idx].rbuf));
#endif #endif
/* If data has been processed but there is still data in the read
* buffer, the command limit triggered. Enforce the penalty time: */
if (len_processed && array_bytes(&My_Connections[Idx].rbuf) > 2)
Throttle_Connection(Idx, c, THROTTLE_CMDS, maxcmd);
return len_processed; return len_processed;
} /* Handle_Buffer */ } /* Handle_Buffer */
@ -2410,6 +2406,31 @@ Conn_GetFromProc(int fd)
return NONE; return NONE;
} /* Conn_GetFromProc */ } /* Conn_GetFromProc */
/**
* Throttle a connection because of excessive usage.
*
* @param Reason The reason, see THROTTLE_xxx constants.
* @param Idx The connection index.
* @param Client The client of this connection.
* @param Seconds The time to delay this connection.
*/
static void
Throttle_Connection(const CONN_ID Idx, CLIENT *Client, const int Reason,
unsigned int Value)
{
assert(Idx > NONE);
assert(Client != NULL);
/* Never throttle servers or services, only interrupt processing */
if (Client_Type(Client) == CLIENT_SERVER
|| Client_Type(Client) == CLIENT_UNKNOWNSERVER
|| Client_Type(Client) == CLIENT_SERVICE)
return;
LogDebug("Throttling connection %d: code %d, value %d!", Idx,
Reason, Value);
Conn_SetPenalty(Idx, 1);
}
#ifndef STRICT_RFC #ifndef STRICT_RFC