Added blocktime filter option and optimized code.

This commit is contained in:
Christian Deacon
2020-05-07 13:18:43 +00:00
parent f8730a511c
commit d17f5a4f54
5 changed files with 385 additions and 328 deletions

View File

@@ -22,14 +22,17 @@ Config option `filters` is an array. Each filter includes the following options:
* `enabled` => If true, this rule is enabled. * `enabled` => If true, this rule is enabled.
* `action` => What action to perform against the packet if matched. 0 = Block. 1 = Allow. * `action` => What action to perform against the packet if matched. 0 = Block. 1 = Allow.
* `srcip` => The source IP to match (e.g. 10.50.0.3). * `srcip` => The source IP the packet must have to match (e.g. 10.50.0.3).
* `dstip` => The destination IP to match (e.g. 10.50.0.4). * `dstip` => The destination IP the packet must have to match (e.g. 10.50.0.4).
* `min_ttl` => The minimum TTL (time to live) the packet has to match. * `min_ttl` => The minimum TTL (time to live) the packet must have to match.
* `max_ttl` => The maximum TTL (time to live) the packet has to match. * `max_ttl` => The maximum TTL (time to live) the packet must have to match.
* `max_len` => The maximum packet length the packet has to match. This includes the entire frame (ethernet header, IP header, L4 header, and data). * `max_len` => The maximum packet length the packet must have to match. This includes the entire frame (ethernet header, IP header, L4 header, and data).
* `min_len` => The minimum packet length the packet has to match. This includes the entire frame (ethernet header, IP header, L4 header, and data). * `min_len` => The minimum packet length the packet must have to match. This includes the entire frame (ethernet header, IP header, L4 header, and data).
* `tos` => The TOS (type of service) the packet has to match. * `tos` => The TOS (type of service) the packet must have to match.
* `payloadmatch` => The payload (L4 data) the packet has to match. The format is in hexadecimal and each byte is separated by a space. An example includes: `FF FF FF FF 59`. * `pps` => The maximum packets per second a source IP can send before matching.
* `bps` => The maximum amount of bytes per second a source IP can send before matching.
* `blocktime` => The maximum of time in seconds to block the source IP if the rule matches and the action is block (0). Default value is `1`.
* `payloadmatch` => The payload (L4 data) the packet must have to match. The format is in hexadecimal and each byte is separated by a space. An example includes: `FF FF FF FF 59`.
#### TCP Options #### TCP Options
The config option `tcpopts` within a filter is an array including TCP options. This should only be one array per filter. Options include: The config option `tcpopts` within a filter is an array including TCP options. This should only be one array per filter. Options include:

View File

@@ -278,6 +278,18 @@ int ReadConfig(struct config_map *cfg)
cfg->filters[i].do_bps = 1; cfg->filters[i].do_bps = 1;
} }
// Block time (default 1).
int blocktime;
if (config_setting_lookup_int(filter, "blocktime", &blocktime))
{
cfg->filters[i].blockTime = blocktime;
}
else
{
cfg->filters[i].blockTime = 1;
}
// Payload match. // Payload match.
const char *payload; const char *payload;

View File

@@ -91,6 +91,8 @@ struct filter
unsigned int do_bps : 1; unsigned int do_bps : 1;
uint64_t bps; uint64_t bps;
uint16_t blockTime;
uint8_t payloadMatch[MAX_PCKT_LENGTH]; uint8_t payloadMatch[MAX_PCKT_LENGTH];
uint16_t payloadLen; uint16_t payloadLen;

View File

@@ -66,6 +66,14 @@ struct bpf_map_def SEC("maps") ip_stats_map =
.max_entries = MAX_TRACK_IPS .max_entries = MAX_TRACK_IPS
}; };
struct bpf_map_def SEC("maps") ip_blacklist_map =
{
.type = BPF_MAP_TYPE_LRU_PERCPU_HASH,
.key_size = sizeof(uint32_t),
.value_size = sizeof(uint64_t),
.max_entries = MAX_TRACK_IPS
};
SEC("xdp_prog") SEC("xdp_prog")
int xdp_prog_main(struct xdp_md *ctx) int xdp_prog_main(struct xdp_md *ctx)
{ {
@@ -82,22 +90,16 @@ int xdp_prog_main(struct xdp_md *ctx)
return XDP_DROP; return XDP_DROP;
} }
// Let's get the filters we need. // Check Ethernet protocol.
struct filter *filter[MAX_FILTERS]; if (unlikely(ethhdr->h_proto != htons(ETH_P_IP)))
for (uint8_t i = 0; i < MAX_FILTERS; i++)
{ {
uint32_t key = i; return XDP_PASS;
filter[i] = bpf_map_lookup_elem(&filters_map, &key);
} }
uint8_t matched = 0; uint8_t matched = 0;
uint8_t action = 0; uint8_t action = 0;
uint64_t blocktime = 1;
// Check Ethernet protocol and ensure it's IP.
if (likely(ethhdr->h_proto == htons(ETH_P_IP)))
{
// Scan IP header. // Scan IP header.
struct iphdr *iph = data + sizeof(struct ethhdr); struct iphdr *iph = data + sizeof(struct ethhdr);
@@ -110,20 +112,42 @@ int xdp_prog_main(struct xdp_md *ctx)
// Check IP header protocols. // Check IP header protocols.
if (unlikely(iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_ICMP)) if (unlikely(iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_ICMP))
{ {
return XDP_PASS; return XDP_DROP;
}
uint64_t now = bpf_ktime_get_ns();
// Check blacklist map.
uint64_t *blocked = bpf_map_lookup_elem(&ip_blacklist_map, &iph->saddr);
if (blocked != NULL && *blocked > 0)
{
#ifdef DEBUG
bpf_printk("Checking for blocked packet... Block time %" PRIu64 "\n", *blocked);
#endif
if (now > *blocked)
{
// Remove element from map.
bpf_map_delete_elem(&ip_blacklist_map, &iph->saddr);
}
else
{
// They're still blocked. Drop the packet.
return XDP_DROP;
}
} }
// Update IP stats (PPS/BPS). // Update IP stats (PPS/BPS).
uint64_t pps = 0; uint64_t pps = 0;
uint64_t bps = 0; uint64_t bps = 0;
uint64_t now = bpf_ktime_get_ns();
struct xdpfw_ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &iph->saddr); struct xdpfw_ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &iph->saddr);
if (ip_stats) if (ip_stats)
{ {
// Check for reset. // Check for reset.
if ((now - ip_stats->tracking) > 1e9) if ((now - ip_stats->tracking) > 1000000000)
{ {
ip_stats->pps = 0; ip_stats->pps = 0;
ip_stats->bps = 0; ip_stats->bps = 0;
@@ -151,6 +175,16 @@ int xdp_prog_main(struct xdp_md *ctx)
bpf_map_update_elem(&ip_stats_map, &iph->saddr, &new, BPF_ANY); bpf_map_update_elem(&ip_stats_map, &iph->saddr, &new, BPF_ANY);
} }
// Let's get the filters we need.
struct filter *filter[MAX_FILTERS];
for (uint8_t i = 0; i < MAX_FILTERS; i++)
{
uint32_t key = i;
filter[i] = bpf_map_lookup_elem(&filters_map, &key);
}
struct tcphdr *tcph; struct tcphdr *tcph;
struct udphdr *udph; struct udphdr *udph;
struct icmphdr *icmph; struct icmphdr *icmph;
@@ -408,6 +442,7 @@ int xdp_prog_main(struct xdp_md *ctx)
matched = 1; matched = 1;
action = filter[i]->action; action = filter[i]->action;
blocktime = filter[i]->blockTime;
break; break;
} }
@@ -441,10 +476,14 @@ int xdp_prog_main(struct xdp_md *ctx)
//bpf_printk("Matched with protocol %" PRIu8 " and sAddr %" PRIu32 ".\n", iph->protocol, iph->saddr); //bpf_printk("Matched with protocol %" PRIu8 " and sAddr %" PRIu32 ".\n", iph->protocol, iph->saddr);
#endif #endif
} }
}
if (matched && action == 0) if (matched && action == 0)
{ {
// Before dropping, update the blacklist map.
uint64_t newTime = now + (blocktime * 1000000000);
bpf_map_update_elem(&ip_blacklist_map, &iph->saddr, &newTime, BPF_ANY);
return XDP_DROP; return XDP_DROP;
} }

View File

@@ -344,6 +344,7 @@ int main(int argc, char *argv[])
fprintf(stdout, "TOS => %" PRIu8 "\n", conf->filters[i].tos); fprintf(stdout, "TOS => %" PRIu8 "\n", conf->filters[i].tos);
fprintf(stdout, "PPS => %" PRIu64 "\n", conf->filters[i].pps); fprintf(stdout, "PPS => %" PRIu64 "\n", conf->filters[i].pps);
fprintf(stdout, "BPS => %" PRIu64 "\n\n", conf->filters[i].bps); fprintf(stdout, "BPS => %" PRIu64 "\n\n", conf->filters[i].bps);
fprintf(stdout, "Block Time => %" PRIu16 "\n\n", conf->filters[i].blockTime);
// TCP Options. // TCP Options.
fprintf(stdout, "TCP Enabled => %" PRIu8 "\n", conf->filters[i].tcpopts.enabled); fprintf(stdout, "TCP Enabled => %" PRIu8 "\n", conf->filters[i].tcpopts.enabled);