Skip to content
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

Merged
merged 10 commits into from
Dec 23, 2016
Merged

Feature/rate limit dhcp #1845

merged 10 commits into from
Dec 23, 2016

Conversation

julsemaan
Copy link
Collaborator

@julsemaan julsemaan commented Nov 17, 2016

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

  • Rate limited the DHCP listener processes to prevent specific device(s) from performing a denial of service on the DHCP listening processes (pfdhcplistener rate-limiting #1722)

@julsemaan
Copy link
Collaborator Author

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 );
Copy link
Member

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 ...

Copy link
Collaborator Author

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

Copy link
Collaborator Author

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

Copy link
Member

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
Copy link
Member

@fdurand fdurand Nov 29, 2016

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)

Copy link
Collaborator Author

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");
Copy link
Member

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");

Copy link
Member

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};
Copy link
Member

@fdurand fdurand Dec 15, 2016

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

Copy link
Collaborator Author

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

Copy link
Member

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?

Copy link
Collaborator Author

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 ?

Copy link
Member

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

Copy link
Collaborator Author

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.

Copy link
Member

@jrouzierinverse jrouzierinverse left a 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 ) );
Copy link
Member

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;
}
Copy link
Member

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};
Copy link
Member

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");
Copy link
Member

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.

@julsemaan
Copy link
Collaborator Author

Adjustments completed for @fdurand and @jrouzierinverse reviews

$dhcp->{'chaddr'} = $dhcp_mac;

# grab DHCP information
if ( !defined($dhcp_mac) ) {
Copy link
Member

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);
Copy link
Member

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

@julsemaan
Copy link
Collaborator Author

changes pushaide

}

# grab DHCP information
if ($dhcp_mac) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!defined $dhcp_mac) {

Copy link
Collaborator Author

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then
if (!$dhcp_mac) {

Copy link
Collaborator Author

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

@fdurand fdurand merged commit 23b79cd into devel Dec 23, 2016
@jrouzierinverse jrouzierinverse deleted the feature/rate-limit-dhcp branch January 31, 2017 19:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

pfdhcplistener rate-limiting
4 participants