problem with user-defined function
HugoFlorentino opened this issue · 22 comments
I defined a function to humanize the hashlimit match:
@def &lim_src($name, $type, $rate, $burst, $expire, $destination) = {
mod hashlimit
hashlimit-mode srcip
@if @eq($type, 'upto') hashlimit-upto $rate
@if @eq($type, 'above') hashlimit-above $rate
hashlimit-burst $burst
hashlimit-htable-expire $expire
hashlimit-name $name
jump $destination;
}
I used it in the PREROUTING chain of the raw table, like this:
interface ! lo protocol tcp dport 443 mod tcp syn &lim_src(limit_https, above, 64kb/s, 1mb, 180000, MYCHAIN);
However when I tried to verify the resulting iptables rules with ferm -nl file.conf
, no rule whatsoever was generated regarding hashlimit, and ferm produced no error either.
Is there anything invalid in the function?
it has worked for me this way:
@def &L($name, $type, $rate, $burst, $expire, $destination) = {
jump $destination
mod hashlimit
hashlimit-mode srcip
hashlimit-burst $burst
hashlimit-htable-expire $expire
hashlimit-name $name
@if @eq($type, 'upto') {
hashlimit-upto $rate;
}
@else {
hashlimit-above $rate;
}
}
table raw chain PREROUTING interface ! lo protocol tcp dport 443 mod tcp syn &L('limit_https', 'above', '64kb/s', '1mb', 180000, MYCHAIN);
table raw chain PREROUTING interface ! lo protocol tcp dport 443 mod tcp syn &L('limit_https', 'upto', '64kb/s', '1mb', 180000, MYCHAIN);
it has worked for me this way:
@if @eq($type, 'upto') { hashlimit-upto $rate; } @else { hashlimit-above $rate; }
This is not exactly the same, someone might pass 'garbage' as a parameter and it would match 'hashlimit-above'. I tried with @else @if(
but for some reason it didn't work for me.
it has worked for me this way:
@def &L($name, $type, $rate, $burst, $expire, $destination) = { jump $destination mod hashlimit hashlimit-mode srcip hashlimit-burst $burst hashlimit-htable-expire $expire hashlimit-name $name @if @eq($type, 'upto') { hashlimit-upto $rate; } @else { hashlimit-above $rate; } } table raw chain PREROUTING interface ! lo protocol tcp dport 443 mod tcp syn &L('limit_https', 'above', '64kb/s', '1mb', 180000, MYCHAIN); table raw chain PREROUTING interface ! lo protocol tcp dport 443 mod tcp syn &L('limit_https', 'upto', '64kb/s', '1mb', 180000, MYCHAIN);
BTW, Why does ferm fail if you move the jump line towards the end of the function? (which is the usual way to declare it in iptables)
You have two conflicting conditionals in your expression:
@if @eq($type, 'upto') hashlimit-upto $rate
@if @eq($type, 'above') hashlimit-above $rate
$type
can't be both upto
and above
, so one of the two will always evaluate to false, making this rule always empty.
Note that @if
doesn't apply to a line of ferm code, but to the whole rule.
BTW, Why does ferm fail if you move the jump line towards the end of the function? (which is the usual way to declare it in iptables)
It doesn't, usually. But the way it's used here, the jump
cannot be at the end, because the }
closes the rule, just like a ;
does. This is a side effect of ferm's basic syntax when used with a curly braced block. Common stuff needs to be written before that block starts.
You can have the target at the end, but @endreszabo's rule has two ends: one in each block. Therefore, you'd need to copy the target into both blocks, duplicating code.
Further clarification: this syntax problem has nothing to do with targets; this applies to any part of a rule. You can't have additional rule pieces after the curly brace is closed, because the rule has ended already.
it
You have two conflicting conditionals in your expression:
@if @eq($type, 'upto') hashlimit-upto $rate @if @eq($type, 'above') hashlimit-above $rate
$type
can't be bothupto
andabove
, so one of the two will always evaluate to false, making this rule always empty.
Note that@if
doesn't apply to a line of ferm code, but to the whole rule.
I thought the function parser would simply skip the non-matching condition and continue evaluating the rest (which is the typical behavior of an if function).
I thought the function parser would simply skip the non-matching condition and continue evaluating the rest (which is the typical behavior of an if function).
True, it does! It skips the condition and thus the rest of the rule. That's how @if
works!
I was unaware that if had a final efect on the whole function and not just the local evaluation.
BTW, Why does ferm fail if you move the jump line towards the end of the function? (which is the usual way to declare it in iptables)
It doesn't, usually. But the way it's used here, the
jump
cannot be at the end, because the}
closes the rule, just like a;
does. This is a side effect of ferm's basic syntax when used with a curly braced block. Common stuff needs to be written before that block starts.
You can have the target at the end, but @endreszabo's rule has two ends: one in each block. Therefore, you'd need to copy the target into both blocks, duplicating code.
Further clarification: this syntax problem has nothing to do with targets; this applies to any part of a rule. You can't have additional rule pieces after the curly brace is closed, because the rule has ended already.
One thing puzzles me though: his example even having two ends, did produce both iptables rules (although with a jump in the middle, I don't even know if iptables will evaluate contidions after a jump instruction).
I was unaware that if had a final efect on the whole function and not just the local evaluation.
I have no idea what that means.
One thing puzzles me though: his example even having two ends, did produce both iptables rules
No, one function call only produced one rule. But there were two function calls.
No, one function call only produced one rule. But there were two function calls.
Yes, first call with type 'above' and it worked. If right curly braces marks the end of a user-defined function, it shouldn't have worked. Or could one do this: ?
[...]
@if @eq($type, 'upto') {
hashlimit-upto $rate;
}
@else @if @eq($type, 'above') {
hashlimit-above $rate;
}
[...]
If right curly braces marks the end of a user-defined function, it shouldn't have worked.
Again, I have no idea what you mean.
Or could one do this: ?
You can do that.
btw. you don't need the curly braces. You need them only if you want to generate more than one rule in that branch. But it's ok to have unnecessary curly braces.
If right curly braces marks the end of a user-defined function, it shouldn't have worked.
Again, I have no idea what you mean.
Or could one do this: ?
You can do that.
I tried with his example, it didn't work.
Didn't work is the worst possible problem description.
If you have a problem with @if
syntax and you believe it's a ferm bug, write a new bug report with all information on how to reproduce.
@def &L($name, $type, $rate, $burst, $expire, $destination) = {
jump $destination
mod hashlimit
hashlimit-mode srcip
hashlimit-burst $burst
hashlimit-htable-expire $expire
hashlimit-name $name
@if @eq($type, 'upto') {
hashlimit-upto $rate;
}
@else @if @eq($type, 'above') {
hashlimit-above $rate;
}
}
domain ip table raw {
chain LOG_DROP {
NFLOG nflog-prefix DROPPED;
RETURN;
}
chain PREROUTING {
policy ACCEPT;
interface ! lo proto tcp
mod tcp syn
mod multiport destination-ports (80 443)
&L(limit_nav, above, 128kb/minute, 1mb, 180000, LOG_DROP);
}
}
The output from ferm -nl
:
*raw
:LOG_DROP - [0:0]
:PREROUTING ACCEPT [0:0]
-A LOG_DROP --jump NFLOG --nflog-prefix DROPPED
-A LOG_DROP --jump RETURN
COMMIT
(It skips the hashlimit rule entirely)
You still didn't describe a problem.
sorry, a copy-paste problem:
*raw
:LOG_DROP - [0:0]
:PREROUTING ACCEPT [0:0]
-A LOG_DROP --jump NFLOG --nflog-prefix DROPPED
-A LOG_DROP --jump RETURN
-A PREROUTING ! --in-interface lo --protocol tcp --syn --match multiport --destination-ports 80,443 --jump LOG_DROP --match hashlimit --hashlimit-mode srcip --hashlimit-burst 1mb --hashlimit-htable-expire 180000 --hashlimit-name limit_nav --hashlimit-above 128kb/minute
COMMIT
And you still did not describe a problem.
And you still did not describe a problem.
Are hashlimit conditions even evaluated after a --jump
instruction?
Edit: well .. it seems they are accepted and iptables rearranges them to place the jump at the end. I didn't know it could do this.