Added rule expire and purge for ipfw. Almost there...

git-svn-id: file:///home/mbr/svn/fwknop/trunk@283 510a4753-2344-4c79-9c09-4d669213fbeb
This commit is contained in:
Damien Stuart
2010-08-24 03:09:35 +00:00
parent 51c21b318f
commit cdff077bb6
5 changed files with 389 additions and 30 deletions

View File

@@ -395,9 +395,9 @@ validate_options(fko_srv_options_t *opts)
/* Set IPFW Dynamic rule expiry interval.
*/
if(opts->config[CONF_IPFW_DYNAMIC_INTERVAL] == NULL)
set_config_entry(opts, CONF_IPFW_DYNAMIC_INTERVAL,
DEF_IPFW_DYNAMIC_INTERVAL);
if(opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL] == NULL)
set_config_entry(opts, CONF_IPFW_EXPIRE_PURGE_INTERVAL,
DEF_IPFW_EXPIRE_PURGE_INTERVAL);
/* Set IPFW Dynamic rule expiry interval.
*/

View File

@@ -46,7 +46,7 @@ get_next_rule_num(void)
for(i=0; i < fwc.max_rules; i++)
{
if(fwc.rule_map[i] == 0)
if(fwc.rule_map[i] == RULE_FREE)
return(fwc.start_rule_num + i);
}
@@ -124,6 +124,7 @@ fw_config_init(fko_srv_options_t *opts)
fwc.max_rules = atoi(opts->config[CONF_IPFW_MAX_RULES]);
fwc.active_set_num = atoi(opts->config[CONF_IPFW_ACTIVE_SET_NUM]);
fwc.expire_set_num = atoi(opts->config[CONF_IPFW_EXPIRE_SET_NUM]);
fwc.purge_interval = atoi(opts->config[CONF_IPFW_EXPIRE_PURGE_INTERVAL]);
/* Let us find it via our opts struct as well.
*/
@@ -178,11 +179,28 @@ fw_initialize(fko_srv_options_t *opts)
fwc.active_set_num
);
(fwc.rule_map)[0] = 1;
fwc.rule_map[0] = RULE_ACTIVE;
}
else
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
}
/* Make sure our expire set is disabled.
*/
zero_cmd_buffers();
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_DISABLE_SET_ARGS,
fwc.fw_command,
fwc.expire_set_num
);
res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
if(EXTCMD_IS_SUCCESS(res))
log_msg(LOG_INFO, "Set ipfw set %u to disabled.",
fwc.expire_set_num);
else
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
}
int
@@ -246,9 +264,6 @@ fw_cleanup(void)
int
process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
{
/* TODO: Implement me */
char nat_ip[16] = {0};
char *ndx;
unsigned short rule_num;
@@ -288,8 +303,19 @@ process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
if(spadat->message_type == FKO_ACCESS_MSG
|| spadat->message_type == FKO_CLIENT_TIMEOUT_ACCESS_MSG)
{
/* Pull the next available rule number.
*/
rule_num = get_next_rule_num();
/* If rule_num comes back as 0, we aready have the maximum number
* of active rules allowed so we reject and bail here.
*/
if(rule_num == 0)
{
log_msg(LOG_WARNING, "Access request rejected: Maximum allowed number of rules has been reached.");
return(-1);
}
/* Create an access command for each proto/port for the source ip.
*/
while(ple != NULL)
@@ -317,10 +343,13 @@ process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
spadat->spa_message_remain, exp_ts
);
(fwc.rule_map)[fwc.start_rule_num + rule_num] = 1;
fwc.rule_map[rule_num - fwc.start_rule_num] = RULE_ACTIVE;
fwc.active_rules++;
fwc.total_rules++;
/* Reset the next expected expire time for this chain if it
* is warranted.
* is warranted.
*/
if(fwc.next_expire < now || exp_ts < fwc.next_expire)
fwc.next_expire = exp_ts;
@@ -334,8 +363,19 @@ process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
}
else
{
/* No other modes supported yet.
/* No other SPA request modes are supported yet.
*/
if(spadat->message_type == FKO_LOCAL_NAT_ACCESS_MSG
|| spadat->message_type == FKO_CLIENT_TIMEOUT_LOCAL_NAT_ACCESS_MSG)
{
log_msg(LOG_WARNING, "Local NAT requests are not currently supported.");
}
else if(spadat->message_type == FKO_NAT_ACCESS_MSG
|| spadat->message_type == FKO_CLIENT_TIMEOUT_NAT_ACCESS_MSG)
{
log_msg(LOG_WARNING, "Forwarding/NAT requests are not currently supported.");
}
return(-1);
}
@@ -348,17 +388,305 @@ process_spa_request(fko_srv_options_t *opts, spa_data_t *spadat)
void
check_firewall_rules(fko_srv_options_t *opts)
{
char exp_str[12];
char rule_num_str[6];
char *ndx, *rn_start, *rn_end, *tmp_mark;
char exp_str[12];
char rule_num_str[6];
char *ndx, *rn_start, *rn_end, *tmp_mark;
int i, res, rn_offset;
int i, res;
time_t now, rule_exp, min_exp = 0;
unsigned short curr_rule;
time(&now);
/* Just in case we somehow lose track and fall out-of-whack.
*/
if(fwc.active_rules > fwc.max_rules)
fwc.active_rules = 0;
/* If there are no active rules or we have not yet
* reached our expected next expire time, continue.
*/
if(fwc.active_rules == 0 || fwc.next_expire > now)
return;
zero_cmd_buffers();
/* There should be a rule to delete. Get the current list of
* rules for this chain and delete the ones that are expired.
*/
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_LIST_SET_RULES_ARGS,
opts->fw_config->fw_command,
fwc.active_set_num
);
res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0);
if(!EXTCMD_IS_SUCCESS(res))
{
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
return;
}
if(opts->verbose > 2)
log_msg(LOG_INFO, "RES=%i, CMD_BUF: %s\nRULES LIST: %s", res, cmd_buf, cmd_out);
/* Find the first _exp_ string (if any).
*/
ndx = strstr(cmd_out, "_exp_");
if(ndx == NULL)
{
/* we did not find an expected rule.
*/
log_msg(LOG_ERR,
"Did not find expire comment in rules list %i.\n", i);
fwc.active_rules--;
return;
}
/* Walk the list and process rules as needed.
*/
while (ndx != NULL) {
/* Jump forward and extract the timestamp
*/
ndx +=5;
/* remember this spot for when we look for the next
* rule.
*/
tmp_mark = ndx;
strlcpy(exp_str, ndx, 11);
rule_exp = (time_t)atoll(exp_str);
//fprintf(stderr, "RULE_EXP=%u, NOW=%u\n", rule_exp, now);
if(rule_exp <= now)
{
/* Backtrack and get the rule number and delete it.
*/
rn_start = ndx;
while(--rn_start > cmd_out)
{
if(*rn_start == '\n')
break;
}
if(*rn_start == '\n')
{
rn_start++;
}
else if(rn_start > cmd_out)
{
/* This should not happen. But if it does, complain,
* decrement the active rule value, and go on.
*/
log_msg(LOG_ERR,
"Rule parse error while finding rule line start.");
fwc.active_rules--;
break;
}
rn_end = strchr(rn_start, ' ');
if(rn_end == NULL)
{
/* This should not happen. But if it does, complain,
* decrement the active rule value, and go on.
*/
log_msg(LOG_ERR,
"Rule parse error while finding rule number.");
fwc.active_rules--;
break;
}
strlcpy(rule_num_str, rn_start, (rn_end - rn_start)+1);
curr_rule = atoi(rule_num_str);
zero_cmd_buffers();
/* Move the rule to the expired rules set.
*/
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_MOVE_RULE_ARGS,
opts->fw_config->fw_command,
curr_rule,
fwc.expire_set_num
);
//fprintf(stderr, "MOVE RULE CMD: %s\n", cmd_buf);
res = run_extcmd(cmd_buf, err_buf, CMD_BUFSIZE, 0);
if(EXTCMD_IS_SUCCESS(res))
{
log_msg(LOG_INFO, "Moved rule %s with expire time of %u to set %u.",
rule_num_str, rule_exp, fwc.expire_set_num
);
fwc.active_rules--;
fwc.rule_map[curr_rule - fwc.start_rule_num] = RULE_EXPIRED;
}
else
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, err_buf);
}
else
{
/* Track the minimum future rule expire time.
*/
if(rule_exp > now)
min_exp = (min_exp < rule_exp) ? min_exp : rule_exp;
}
/* Push our tracking index forward beyond (just processed) _exp_
* string so we can continue to the next rule in the list.
*/
ndx = strstr(tmp_mark, "_exp_");
}
/* Set the next pending expire time accordingly. 0 if there are no
* more rules, or whatever the next expected (min_exp) time will be.
*/
if(fwc.active_rules < 1)
fwc.next_expire = 0;
else if(min_exp)
fwc.next_expire = min_exp;
}
/* Iterate over the expired rule set and purge those that no longer have
* corresponding dynamic rules.
*/
void
purge_expired_rules(fko_srv_options_t *opts)
{
char exp_str[12];
char rule_num_str[6];
char *ndx, *next_nl, *co_end;
int i, res;
unsigned short curr_rule;
/* First, we get the current active dynamic rules for the expired rule
* set. Then we compare it to the expired rules in the rule_map. Any
* rules in the map that do not have a dynamic rule, can be deleted.
*/
zero_cmd_buffers();
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_LIST_SET_DYN_RULES_ARGS,
opts->fw_config->fw_command,
fwc.expire_set_num
);
res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0);
if(!EXTCMD_IS_SUCCESS(res))
{
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
return;
}
co_end = cmd_out + strlen(cmd_out);
if(opts->verbose > 2)
log_msg(LOG_INFO, "RES=%i, CMD_BUF: %s\nEXP RULES LIST: %s", res, cmd_buf, cmd_out);
/* Find the "## Dynamic rules" string.
*/
ndx = strcasestr(cmd_out, "## Dynamic rules");
if(ndx == NULL)
{
log_msg(LOG_ERR,
"Unexpected error: did not find 'Dynamic rules' string in list output."
);
return;
}
/* Jump to the next newline char.
*/
ndx = strchr(ndx, '\n');
if(ndx == NULL)
{
log_msg(LOG_ERR,
"Unexpected error: did not find 'Dynamic rules' line terminating newline."
);
return;
}
/* Walk the list of dynamic rules (if any).
*/
while(ndx != NULL)
{
ndx++;
while(!isdigit(*ndx) && ndx < co_end)
ndx++;
if(ndx >= co_end)
break;
/* If we are at a digit, assume it is a rule number, extract it,
* and if it falls in the correct range, mark it (so it is not
* removed in the next step.
*/
if(isdigit(*ndx))
{
curr_rule = atoi(ndx);
if(curr_rule >= fwc.start_rule_num
&& curr_rule < fwc.start_rule_num + fwc.max_rules)
fwc.rule_map[curr_rule - fwc.start_rule_num] = RULE_TMP_MARKED;
}
ndx = strchr(ndx, '\n');
}
/* Now, walk the rule map an remove any still marked as expired.
*/
for(i=0; i<fwc.max_rules; i++)
{
/* If it is TMP_MARKED, set it back to EXPIRED and move on.
*/
if(fwc.rule_map[i] == RULE_TMP_MARKED)
{
fwc.rule_map[i] = RULE_EXPIRED;
continue;
}
/* If it is not expired, move on.
*/
if(fwc.rule_map[i] != RULE_EXPIRED)
continue;
/* This rule is ready to go away.
*/
zero_cmd_buffers();
curr_rule = fwc.start_rule_num + i;
snprintf(cmd_buf, CMD_BUFSIZE-1, "%s " IPFW_DEL_RULE_ARGS,
opts->fw_config->fw_command,
fwc.expire_set_num,
curr_rule
);
res = run_extcmd(cmd_buf, cmd_out, STANDARD_CMD_OUT_BUFSIZE, 0);
if(!EXTCMD_IS_SUCCESS(res))
{
log_msg(LOG_ERR, "Error %i from cmd:'%s': %s", res, cmd_buf, cmd_out);
continue;
}
log_msg(LOG_INFO, "Purged rule %u from set %u", curr_rule, fwc.expire_set_num);
fwc.rule_map[curr_rule - fwc.start_rule_num] = RULE_FREE;
fwc.total_rules--;
}
}
#endif /* FIREWALL_IPFW */

View File

@@ -26,15 +26,27 @@
#ifndef FW_UTIL_IPFW_H
#define FW_UTIL_IPFW_H
enum {
RULE_FREE = 0,
RULE_ACTIVE,
RULE_EXPIRED,
RULE_TMP_MARKED
};
/* ipfw command args
*/
#define IPFW_ADD_RULE_ARGS "add %u set %u pass %u from %s to me dst-port %u setup keep-state // _exp_%u"
#define IPFW_ADD_CHECK_STATE_ARGS "add %u set %u check-state"
#define IPFW_MOVE_RULE_ARGS "set move rule %u to %u"
#define IPFW_MOVE_SET_ARGS "set move %u to %u"
#define IPFW_DEL_RULE_ARGS "set %u delete %u"
#define IPFW_DEL_RULE_SET_ARGS "delete set %u"
#define IPFW_LIST_RULES_ARGS "-d -S -T set %u list"
#define IPFW_ADD_RULE_ARGS "add %u set %u pass %u from %s to me dst-port %u setup keep-state // _exp_%u"
#define IPFW_ADD_CHECK_STATE_ARGS "add %u set %u check-state"
#define IPFW_MOVE_RULE_ARGS "set move rule %u to %u"
#define IPFW_MOVE_SET_ARGS "set move %u to %u"
#define IPFW_DISABLE_SET_ARGS "set disable %u"
#define IPFW_DEL_RULE_ARGS "set %u delete %u"
#define IPFW_DEL_RULE_SET_ARGS "delete set %u"
#define IPFW_LIST_RULES_ARGS "-d -S -T set %u list"
#define IPFW_LIST_SET_RULES_ARGS "set %u list"
#define IPFW_LIST_SET_DYN_RULES_ARGS "-d set %u list"
void purge_expired_rules(fko_srv_options_t *opts);
#endif /* FW_UTIL_IPFW_H */

View File

@@ -106,12 +106,12 @@
*/
#elif FIREWALL_IPFW
#define DEF_IPFW_START_RULE_NUM "10000"
#define DEF_IPFW_MAX_RULES "1000"
#define DEF_IPFW_ACTIVE_SET_NUM "1"
#define DEF_IPFW_EXPIRE_SET_NUM "2"
#define DEF_IPFW_DYNAMIC_INTERVAL "60"
#define DEF_IPFW_ADD_CHECK_STATE "N"
#define DEF_IPFW_START_RULE_NUM "10000"
#define DEF_IPFW_MAX_RULES "1000"
#define DEF_IPFW_ACTIVE_SET_NUM "1"
#define DEF_IPFW_EXPIRE_SET_NUM "2"
#define DEF_IPFW_EXPIRE_PURGE_INTERVAL "30"
#define DEF_IPFW_ADD_CHECK_STATE "N"
#elif FIREWALL_IPF
@@ -183,7 +183,7 @@ enum {
CONF_IPFW_MAX_RULES,
CONF_IPFW_ACTIVE_SET_NUM,
CONF_IPFW_EXPIRE_SET_NUM,
CONF_IPFW_DYNAMIC_INTERVAL,
CONF_IPFW_EXPIRE_PURGE_INTERVAL,
CONF_IPFW_ADD_CHECK_STATE,
#elif FIREWALL_IPF
/* --DSS Place-holder */
@@ -251,7 +251,7 @@ static char *config_map[NUMBER_OF_CONFIG_ENTRIES] = {
"IPFW_MAX_RULES",
"IPFW_ACTIVE_SET_NUM",
"IPFW_EXPIRE_SET_NUM",
"IPFW_DYNAMIC_INTERVAL",
"IPFW_EXPIRE_PURGE_INTERVAL",
"IPFW_ADD_CHECK_STATE",
#elif FIREWALL_IPF
/* --DSS Place-holder */
@@ -374,10 +374,14 @@ typedef struct acc_stanza
struct fw_config {
unsigned short start_rule_num;
unsigned short max_rules;
unsigned short active_rules;
unsigned short total_rules;
unsigned short active_set_num;
unsigned short expire_set_num;
unsigned short purge_interval;
unsigned char *rule_map;
time_t next_expire;
time_t last_purge;
char fw_command[MAX_PATH_LEN];
};

View File

@@ -55,6 +55,7 @@ pcap_capture(fko_srv_options_t *opts)
int promisc = 0;
int status;
pid_t child_pid;
time_t now;
/* Set promiscuous mode if ENABLE_PCAP_PROMISC is set to 'Y'.
*/
@@ -264,6 +265,20 @@ pcap_capture(fko_srv_options_t *opts)
*/
check_firewall_rules(opts);
#if FIREWALL_IPFW
/* Purge expired rules that no longer have any corresponding
* dynamic rules.
*/
if(opts->fw_config->total_rule > 0)
{
time(&now);
if(opts->fw_config->last_purge < (now - opts->fw_config->purge_interval))
{
purge_expired_rules(opts);
opts->fw_config->last_purge = now;
}
}
#endif
}
pcap_close(pcap);