non-GPL/HostIsolation/Lib/UpdateMaps.c (198 lines of code) (raw):
// SPDX-License-Identifier: Elastic-2.0
/*
* Copyright 2021 Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under
* one or more contributor license agreements. Licensed under the Elastic
* License 2.0; you may not use this file except in compliance with the Elastic
* License 2.0.
*/
//
// Host Isolation - tool for updating maps of allowed IPs, subnets and pids
//
#include "UpdateMaps.h"
#include "Common.h"
#include <argp.h>
#include <arpa/inet.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
static int ebpf_update_map(const char *map_path,
enum ebpf_hostisolation_map map_id,
const void *key,
const void *val);
static int ebpf_create_map(enum ebpf_hostisolation_map map_id, int *map_fd);
static int ebpf_clear_map(const char *map_path, enum ebpf_hostisolation_map map_id);
static int ebpf_map_delete_key(const char *map_path, const void *key);
int ebpf_map_allowed_IPs_add(uint32_t IPaddr)
{
uint32_t key = IPaddr;
uint32_t val = 1; // values are not used in the hash map
return ebpf_update_map(EBPF_ALLOWED_IPS_MAP_PATH, EBPF_MAP_ALLOWED_IPS, &key, &val);
}
int ebpf_map_allowed_IPs_delete(uint32_t IPaddr)
{
uint32_t key = IPaddr;
return ebpf_map_delete_key(EBPF_ALLOWED_IPS_MAP_PATH, &key);
}
int ebpf_map_allowed_subnets_add(uint32_t IPaddr, uint32_t netmask)
{
struct lpm_key {
uint32_t prefix;
uint32_t IP;
} key = {
.prefix = netmask,
.IP = IPaddr,
};
uint32_t val = 1; // values are not used in the lpm trie map
return ebpf_update_map(EBPF_ALLOWED_SUBNETS_MAP_PATH, EBPF_MAP_ALLOWED_SUBNETS, &key, &val);
}
int ebpf_map_allowed_subnets_delete(uint32_t IPaddr, uint32_t netmask)
{
struct lpm_key {
uint32_t prefix;
uint32_t IP;
} key = {
.prefix = netmask,
.IP = IPaddr,
};
return ebpf_map_delete_key(EBPF_ALLOWED_SUBNETS_MAP_PATH, &key);
}
int ebpf_map_allowed_pids_add(uint32_t pid)
{
uint32_t key = pid;
uint32_t val = 1; // values are not used in the hash map
return ebpf_update_map(EBPF_ALLOWED_PIDS_MAP_PATH, EBPF_MAP_ALLOWED_PIDS, &key, &val);
}
int ebpf_map_allowed_pids_delete(uint32_t pid)
{
uint32_t key = pid;
return ebpf_map_delete_key(EBPF_ALLOWED_PIDS_MAP_PATH, &key);
}
int ebpf_map_allowed_IPs_clear(void)
{
return ebpf_clear_map(EBPF_ALLOWED_IPS_MAP_PATH, EBPF_MAP_ALLOWED_IPS);
}
int ebpf_map_allowed_subnets_clear(void)
{
return ebpf_clear_map(EBPF_ALLOWED_SUBNETS_MAP_PATH, EBPF_MAP_ALLOWED_SUBNETS);
}
int ebpf_map_allowed_pids_clear(void)
{
return ebpf_clear_map(EBPF_ALLOWED_PIDS_MAP_PATH, EBPF_MAP_ALLOWED_PIDS);
}
static int ebpf_create_map(enum ebpf_hostisolation_map map_id, int *map_fd)
{
int rv = 0;
int fd = -1;
if (map_id >= EBPF_MAP_NUM) {
ebpf_log("Error: invalid map ID\n");
rv = -1;
goto cleanup;
}
struct bpf_map_create_opts opts = {};
opts.map_flags = ebpf_maps[map_id].map_flags;
opts.sz = sizeof(opts);
fd = bpf_map_create(ebpf_maps[map_id].type, NULL, ebpf_maps[map_id].key_size,
ebpf_maps[map_id].value_size, ebpf_maps[map_id].max_entries, &opts);
if (fd < 0) {
ebpf_log("Error creating map\n");
rv = -1;
goto cleanup;
}
*map_fd = fd;
cleanup:
return rv;
}
static int ebpf_map_delete_key(const char *map_path, const void *key)
{
int rv = 0;
int map_fd = -1;
if (map_path == NULL) {
ebpf_log("Error: map_path is NULL\n");
rv = -1;
goto cleanup;
}
map_fd = bpf_obj_get(map_path);
if (map_fd < 0) {
ebpf_log("Error: map not found\n");
rv = -1;
goto cleanup;
}
rv = bpf_map_delete_elem(map_fd, key);
if (rv) {
ebpf_log("Error: failed to delete key in map: %s, errno=%d\n", map_path, errno);
goto cleanup;
}
cleanup:
if (map_fd >= 0) {
close(map_fd);
}
return rv;
}
static int ebpf_update_map(const char *map_path,
enum ebpf_hostisolation_map map_id,
const void *key,
const void *val)
{
int rv = 0;
int map_fd = -1;
if (map_path == NULL) {
ebpf_log("Error: map_path is NULL\n");
rv = -1;
goto cleanup;
}
map_fd = bpf_obj_get(map_path);
if (map_fd < 0) {
// perhaps the map does not exist, try to create it and pin to bpf fs
rv = ebpf_create_map(map_id, &map_fd);
if (rv) {
ebpf_log("Error updating map, make sure to run with sudo. Errno=%d\n", errno);
goto cleanup;
}
rv = bpf_obj_pin(map_fd, map_path);
if (rv) {
ebpf_log("Error pinning map, make sure to run with sudo. Errno=%d\n", errno);
goto cleanup;
}
}
rv = bpf_map_update_elem(map_fd, key, val, 0);
if (rv) {
ebpf_log("Error: failed to add entry to map: %s, errno=%d\n", map_path, errno);
goto cleanup;
}
cleanup:
if (map_fd >= 0) {
close(map_fd);
}
return rv;
}
static int ebpf_clear_map(const char *map_path, enum ebpf_hostisolation_map map_id)
{
int rv = 0;
int map_fd = -1;
uint8_t key_buf[64] = {0};
uint8_t next_key_buf[64] = {0};
if (map_path == NULL) {
ebpf_log("Error: map_path is NULL\n");
rv = -1;
goto cleanup;
}
map_fd = bpf_obj_get(map_path);
if (map_fd < 0) {
// perhaps the map does not exist, try to create it
rv = ebpf_create_map(map_id, &map_fd);
if (rv) {
ebpf_log("Error clearing map, make sure to run with sudo. Errno=%d\n", errno);
goto cleanup;
}
}
// get the first key
if (bpf_map_get_next_key(map_fd, NULL, key_buf) < 0) {
if (errno == ENOENT) {
// map is already empty
rv = 0;
goto cleanup;
} else {
// failure (perhaps not supported)
rv = -1;
ebpf_log("Error getting next key while clearing map, errno=%d\n", errno);
goto cleanup;
}
}
// iterate over map
while (0 == bpf_map_get_next_key(map_fd, key_buf, next_key_buf)) {
// return value 0 means 'key' exists and 'next_key' has been set
(void)bpf_map_delete_elem(map_fd, key_buf);
memcpy(key_buf, next_key_buf, sizeof(key_buf));
}
// -1 was returned so 'key' is the last element - delete it
(void)bpf_map_delete_elem(map_fd, key_buf);
cleanup:
if (map_fd >= 0) {
close(map_fd);
}
return rv;
}