Firewall: Schedule - add missing schedules support in "Firewall: Rules [new]" and refactor existing usage to avoid duplication of logic

PR: https://github.com/opnsense/core/issues/9690

(cherry picked from commit a5fed616a5)
(cherry picked from commit ce432fa769)
This commit is contained in:
Ad Schellevis 2026-02-01 13:21:26 +01:00 committed by Franco Fichtner
parent e23898d791
commit 714fc1f3d7
7 changed files with 78 additions and 39 deletions

View file

@ -187,8 +187,8 @@ function filter_configure_sync($verbose = false, $load_aliases = true)
filter_core_bootstrap($fw);
$cnfint = iterator_to_array($fw->getInterfaceMapping());
plugins_firewall($fw);
// register user rules, returns kill states for schedules
$sched_kill_states = filter_core_rules_user($fw);
// register legacy user rules
filter_core_rules_user($fw);
// manual outbound nat rules
if (
@ -256,6 +256,33 @@ function filter_configure_sync($verbose = false, $load_aliases = true)
foreach ((new OPNsense\Firewall\DNat())->rule->sortedBy(['sequence']) as $key => $rule) {
$fw->registerForwardRule(600, $rule->getNodeContent());
}
/**
* XXX: Apply schedules on all installed rules so we can use both legacy and MVC ones.
* Eventually this needs to be replaced, but as this requires legacy code, we cannot do that easily now
* without increasing impact.
*/
$sched_kill_states = [];
foreach ($fw->iterateFilterRules() as $rule) {
$rrule = $rule->getRawRule();
if (!empty($rrule['sched'])) {
$descr = $rrule['descr'] ?? '';
$descr .= " ({$rrule['sched']})";
$rule->updateDescription($descr);
foreach ($config['schedules']['schedule'] as $sched) {
if ($sched['name'] == $rrule['sched']) {
if (!filter_get_time_based_rule_status($sched)) {
if (!isset($config['system']['schedule_states'])) {
$sched_kill_states[] = $rrule['label'];
}
/* disable rule, suffix label to mark end of schedule */
$rule->disable();
$rule->updateDescription("[FIN] " . $descr);
}
break;
}
}
}
}
openlog("firewall", LOG_DAEMON, LOG_LOCAL4);

View file

@ -667,14 +667,12 @@ function filter_core_rules_system($fw, $defaults)
}
/**
* register user rules, returns kill states for schedules
* register user rules
*/
function filter_core_rules_user($fw)
{
global $config;
$sched_kill_states = [];
foreach ((new \OPNsense\Firewall\Group())->ifgroupentry->iterateItems() as $node) {
$ifgroups[(string)$node->ifname] = !empty((string)$node->sequence) ? (string)$node->sequence : 0;
}
@ -696,9 +694,6 @@ function filter_core_rules_user($fw)
if (empty($rule['descr'])) {
$rule['descr'] = '';
}
if (!empty($rule['sched'])) {
$rule['descr'] .= " ({$rule['sched']})";
}
if (isset($rule['floating'])) {
$prio = 200000;
@ -707,25 +702,7 @@ function filter_core_rules_user($fw)
} else {
$prio = 400000;
}
/* is a time based rule schedule attached? */
if (!empty($rule['sched']) && !empty($config['schedules'])) {
foreach ($config['schedules']['schedule'] as $sched) {
if ($sched['name'] == $rule['sched']) {
if (!filter_get_time_based_rule_status($sched)) {
if (!isset($config['system']['schedule_states'])) {
$sched_kill_states[] = $rule['label'];
}
/* disable rule, suffix label to mark end of schedule */
$rule['disabled'] = true;
$rule['descr'] = "[FIN] " . $rule['descr'];
}
break;
}
}
}
$fw->registerFilterRule($prio, $rule);
}
}
return $sched_kill_states;
}

View file

@ -58,36 +58,31 @@ function pf_cron()
{
global $config;
$jobs = array();
$jobs = [];
if (isset($config['filter']['rule'])) {
foreach ($config['filter']['rule'] as $rule) {
if (empty($rule['disabled']) && !empty($rule['sched'])) {
$jobs[]['autocron'] = array('/usr/bin/logger "reload filter for configured schedules" ; /usr/local/etc/rc.filter_configure', '1,16,31,46');
break;
}
}
if ((new OPNsense\Firewall\Filter(true))->hasSchedule()) {
$jobs[]['autocron'] = ['/usr/bin/logger "reload filter for configured schedules" ; /usr/local/etc/rc.filter_configure', '1,16,31,46'];
}
/* bogons fetch always set in default config.xml */
switch ($config['system']['bogons']['interval']) {
case 'daily':
$jobs[]['autocron'] = array('/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '*', '*', '*');
$jobs[]['autocron'] = ['/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '*', '*', '*'];
break;
case 'weekly':
$jobs[]['autocron'] = array('/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '*', '*', '0');
$jobs[]['autocron'] = ['/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '*', '*', '0'];
break;
case 'monthly':
default:
$jobs[]['autocron'] = array('/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '1', '*', '*');
$jobs[]['autocron'] = ['/usr/local/sbin/configctl -d filter schedule bogons', '1', '3', '1', '*', '*'];
break;
}
$jobs[]['autocron'] = array(
$jobs[]['autocron'] = [
'/usr/local/bin/flock -n -E 0 -o /tmp/filter_update_tables.lock ' .
'/usr/local/opnsense/scripts/filter/update_tables.py --quick',
'*'
);
];
return $jobs;
}

View file

@ -286,6 +286,7 @@
<advanced>true</advanced>
<grid_view>
<visible>false</visible>
<formatter>sched</formatter>
</grid_view>
</field>
<!-- XXX: start as advanced, promote to non-advanced feature in a later version -->

View file

@ -476,6 +476,16 @@ abstract class Rule
return 'internal2'; // late
}
public function updateDescription($descr)
{
$this->rule['descr'] = $descr;
}
public function disable()
{
$this->rule['disabled'] = 1;
}
/**
* return raw rule
*/

View file

@ -366,4 +366,22 @@ class Filter extends BaseModel
}
return false;
}
public function hasSchedule()
{
foreach ($this->rules->rule->iterateItems() as $rule) {
if (!$rule->sched->isEmpty() && !$rule->enabled->isEmpty()) {
return true;
}
}
$cfg = (\OPNsense\Core\Config::getInstance())->object();
if (isset($cfg->filter->rule)) {
foreach ($cfg->filter->children() as $tag => $rule) {
if ($tag === 'rule' && !empty($rule->sched) && empty((string)$rule->disabled)) {
return true;
}
}
}
return false;
}
}

View file

@ -561,6 +561,17 @@
</div>
`;
},
sched: function(column, row) {
if (row.isGroup || typeof row[column.id] !== "string" || row[column.id] === "") {
return "";
}
return `
${row[column.id]} &nbsp;
<a href="/firewall_schedule_edit.php?name=${row[column.id]}" data-toggle="tooltip" title="{{ lang._('Edit') }}">
<i class="fa fa-calendar text-muted"></i>
</a>
`;
},
},
},
commands: {