-
Notifications
You must be signed in to change notification settings - Fork 276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/rate limit dhcp #1845
Feature/rate limit dhcp #1845
Conversation
This is ready for review. Opinions wanted on whether or not this should be on by default (right now it rate limits the packets for 5 seconds by default) |
@@ -105,6 +107,9 @@ my $pcap; | |||
my $net_type; | |||
my $process; | |||
|
|||
my $rate_limit_hash = {}; | |||
my $rate_limit_cache = CHI->new( driver => 'Memory', datastore => $rate_limit_hash ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to see what is in the cache with pfcmd cache ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it to use a pf::CHI namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just looked into it and if we want to have this available through pfcmd then we can't use the memory storage for obvious reasons..
Now the question is whether we prefer using Redis or the memory storage. I put it in memory for faster access and reduced load on Redis so we'll need to discard this advantage if we want access through pfcmd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO keep it faster as possible
# For example, a DHCPREQUEST for the same MAC/IP will only be processed once in the timeframe configured below. | ||
# This is independant of the DHCP server/relay handling the packet and is only based on the IP, MAC Address and DHCP type inside the packet. | ||
# A value of 0 will disable the rate limitation. | ||
dhcp_rate_limiting=5s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe something lower than the maximum we set for dhcp lease time (29s)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is we could miss some renewals and if someone sets the DHCP lease to 15 seconds, it won't adjust this automatically, so for the default value I wanted to keep something that would work for all the cases and wouldn't create a hard to debug issue.
} | ||
} | ||
else { | ||
$logger->info("Ignoring packet due to rate-limiting"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$logger->info("[mac:$dhcp_mac] Ignoring packet due to rate-limiting");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could create a lot of noise in the log files can you change to debug or trace.
|
||
my $dhcp_mac = $dhcp->{'chaddr'}; | ||
# If its a DHCPREQUEST, we take the IP address from the options | ||
my $dhcp_ip = ( $dhcp->{'options'}{'53'} == 3 ) ? $dhcp->{'options'}{'50'} : $dhcp->{yiaddr}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that sometimes $dhcp_ip is empty, we probably have to add a condition if define($dhcp_ip) ...after in the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, I will need to find why its empty sometimes though as it shouldn't be
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question is the rate limiting key too specific?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would it be too specific ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was curious why use the ip in calculating the key and not just $op . $options_53 . $mac
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well since the iplog is mac<->ip based if a device is assigned a different address for whatever reasons, then it won't be ignored by rate limiting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should move any logic that is used for deciding to send or not send early in the flow to avoid unnecessary work.
return; | ||
} | ||
|
||
$dhcp->{'chaddr'} = clean_mac( substr( $dhcp->{'chaddr'}, 0, 12 ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create a temporary variable to hold $dhcp->{'chaddr'} to avoid accessing the hash multiple times for the same variable.
"invalid CHADDR value ($dhcp->{'chaddr'}) in DHCP packet from $dhcp->{src_mac} ($dhcp->{src_ip})" | ||
}); | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move any logic that would exit early before creating the %args hash and doing the base64 of the packet to avoid extra work.
|
||
my $dhcp_mac = $dhcp->{'chaddr'}; | ||
# If its a DHCPREQUEST, we take the IP address from the options | ||
my $dhcp_ip = ( $dhcp->{'options'}{'53'} == 3 ) ? $dhcp->{'options'}{'50'} : $dhcp->{yiaddr}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question is the rate limiting key too specific?
} | ||
} | ||
else { | ||
$logger->info("Ignoring packet due to rate-limiting"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could create a lot of noise in the log files can you change to debug or trace.
Adjustments completed for @fdurand and @jrouzierinverse reviews |
$dhcp->{'chaddr'} = $dhcp_mac; | ||
|
||
# grab DHCP information | ||
if ( !defined($dhcp_mac) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check would be better after the clean_mac of $dhcp->{'chaddr'}
# - there is no cache entry (packet wasn't seen in the last $rate_limiting seconds) | ||
if(!defined($rate_limit_key) || $rate_limiting == 0 || !$rate_limit_cache->get($rate_limit_key)) { | ||
my $apiclient = pf::api::queue->new(queue => 'pfdhcplistener'); | ||
$apiclient->notify('process_dhcp', %args, udp_payload_b64 => $udp_payload_b64); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not need to send the udp_payload_b64 anymore for dhcpv4
So to avoid additional processing we should only base64 it when sending it process_dhcpv6.
Also remove the decoding of the udp_payload_b64 in pf::api::process_dhcp
changes pushaide |
} | ||
|
||
# grab DHCP information | ||
if ($dhcp_mac) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (!defined $dhcp_mac) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clean_mac returns "0" when it can't clean the mac
Since you made me move the clean_mac at the top, now the defined($clean_mac) doesn't apply anymore which I've fixed at the same time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then
if (!$dhcp_mac) {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, let me fit it
Description
Rate limit the pfdhcplistener
Move the decoding logic back in the pfdhcplistener (since it has to take decision on the L4 payload)
Impacts
pfdhcplistener
Issue
fixes #1722
Delete branch after merge
NO (to backport on setup)
NEWS file entries
Enhancements