Transfer data

This commit is contained in:
Florian Hoss 2022-07-29 10:27:39 +02:00
parent cac6d98908
commit c43dd6121a
27 changed files with 3237 additions and 0 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
.gitattributes export-ignore
.gitignore export-ignore

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Florian Hoss
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

4
esp32example/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.pio
.vscode
include/theSecrets.h
components

View file

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ESP32Firewall)

View file

@ -0,0 +1,16 @@
#ifndef _LWIP_HOOKS_H_
#define _LWIP_HOOKS_H_
#ifdef __cplusplus
extern "C"
{
#endif
int lwip_hook_ip4_input(struct pbuf *pbuf, struct netif *input_netif);
#define LWIP_HOOK_IP4_INPUT lwip_hook_ip4_input
#ifdef __cplusplus
}
#endif
#endif /* _LWIP_HOOKS_H_ */

View file

@ -0,0 +1,9 @@
#ifndef _THE_SECRETS_H_
#define _THE_SECRETS_H_
const char *ssid = "Wifi";
const char *psk = "password";
const char *username = "username";
const char *password = "password";
#endif

View file

@ -0,0 +1,23 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = esp32
[env:esp32]
platform = espressif32
board = az-delivery-devkit-v4
framework = espidf
monitor_speed = 115200
lib_compat_mode = off
build_flags =
'-Iinclude'
'-DESP_IDF_LWIP_HOOK_FILENAME="lwip_hooks.h"'
lib_deps = https://gitlab.hs-esslingen.de/toheer/iot-security-tools.git

1439
esp32example/sdkconfig.esp32 Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})

72
esp32example/src/main.cpp Normal file
View file

@ -0,0 +1,72 @@
#include "Arduino.h"
#include "theSecrets.h"
#include "WiFi.h"
#include "lwip_hooks.h"
#include "esp32/Firewall.hpp"
#include "esp32/API.hpp"
fw::Firewall *firewall;
fw::API *firewallApi;
int lwip_hook_ip4_input(struct pbuf *pbuf, struct netif *input_netif)
{
// Firewall is not setup yet
if (firewall != NULL)
{
if (firewall->is_packet_allowed(pbuf))
return 0;
else
{
pbuf_free(pbuf);
return 1;
}
}
return 0;
}
void initFirewall(const String ip)
{
firewall = new fw::Firewall();
firewallApi = new fw::API(firewall, username, password, ip);
}
void handle_wifi_events(WiFiEvent_t event, WiFiEventInfo_t info)
{
switch (event)
{
case ARDUINO_EVENT_WIFI_STA_START:
Serial.println("[WiFi] connecting...");
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
Serial.println("[WiFi] connected");
break;
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
Serial.println("[WiFi] disconnected");
WiFi.reconnect();
break;
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
initFirewall(WiFi.localIP().toString());
break;
default:
Serial.print("[WiFi] other event: ");
Serial.println(event);
}
}
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.onEvent(handle_wifi_events, ARDUINO_EVENT_MAX);
WiFi.begin(ssid, psk);
// fix for https://github.com/espressif/arduino-esp32/issues/4732
WiFi.config(((u32_t)0x0UL), ((u32_t)0x0UL), ((u32_t)0x0UL));
}
void loop()
{
// https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html
sleep(1);
}

20
library.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "esp_firewall_api",
"authors": [
{
"name": "Florian Hoss",
"email": "flhoit00@hs-esslingen.de",
"maintainer": true
}
],
"keywords": "communication, esp32, esp8266, firewall",
"description": "An Arduino library for editing and creating firewall rules via REST API on an ESP32/ESP8266",
"repository": {
"type": "git",
"url": "https://gitlab.hs-esslingen.de/toheer/iot-security-tools.git"
},
"license": "MIT",
"version": "1.0.0",
"frameworks": "arduino",
"platforms": ["espressif32", "espressif8266"]
}

285
src/esp32/API.cpp Normal file
View file

@ -0,0 +1,285 @@
#include "API.hpp"
namespace fw
{
API::API(fw::Firewall *firewall, const char *username, const char *password, const String ip, const uint16_t port)
{
this->firewall = firewall;
this->api_ip = ip;
this->api_port = port;
if (this->setup_auth(username, password) == ERROR)
endless_loop();
this->server = new WebServer(port);
this->setup_routing();
this->server->begin();
Serial.printf("%s endpoints -> %s/api\n", TAG, this->get_url_base().c_str());
while (true)
{
this->server->handleClient();
// https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html
sleep(1);
}
}
API::~API()
{
this->server->stop();
}
String API::get_url_base()
{
return "http://" + this->api_ip + ":" + this->api_port;
}
ok_t API::setup_auth(const char *username, const char *password)
{
if (!username || *username == 0x00 || strlen(username) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Username too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.username, username, CREDENTIALS_LENGTH);
if (!password || *password == 0x00 || strlen(password) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Password too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.password, password, CREDENTIALS_LENGTH);
return SUCCESS;
}
auth_t API::check_auth()
{
if (server->authenticate(this->credentials.username, this->credentials.password))
{
return AUTHENTICATED;
}
this->json_message_response("unauthorized", 403);
return DENIED;
}
void API::setup_routing()
{
this->server->on("/api/firewall/rules", HTTP_GET, std::bind(&API::get_firewall_rules_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_GET, std::bind(&API::get_firewall_rule_handler, this));
this->server->on("/api/firewall/rules", HTTP_POST, std::bind(&API::post_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_PUT, std::bind(&API::put_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_DELETE, std::bind(&API::delete_firewall_handler, this));
this->server->on("/api", HTTP_GET, std::bind(&API::get_endpoint_list_handler, this));
this->server->onNotFound(std::bind(&API::not_found_handler, this));
add_endpoint_to_list("/api/firewall/rules", "GET", "Get all Firewall Rules");
add_endpoint_to_list("/api/firewall/rules/<key>", "GET", "Get Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules", "POST", "Create Firewall Rule");
add_endpoint_to_list("/api/firewall/rules/<key>", "PUT", "Update Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules/<key>", "DELETE", "Delete Firewall Rule by key");
}
void API::add_endpoint_to_list(const String uri, const char *method, const char *description)
{
api_endpoint_t *temp;
const String url = get_url_base() + uri;
api_endpoint_t *api_ptr = (api_endpoint_t *)malloc(sizeof(api_endpoint_t));
strncpy(api_ptr->uri, url.c_str(), sizeof(api_ptr->uri));
strncpy(api_ptr->method, method, sizeof(api_ptr->method));
strncpy(api_ptr->description, description, sizeof(api_ptr->description));
if (this->endpoint_head == NULL)
{
this->endpoint_head = api_ptr;
api_ptr->next = NULL;
return;
}
temp = this->endpoint_head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = api_ptr;
api_ptr->next = NULL;
return;
}
void API::not_found_handler()
{
this->json_message_response("see " + get_url_base() + "/api for available routes", 404);
}
void API::get_endpoint_list_handler()
{
this->json_array_response(this->construct_json_api(), 200);
}
void API::get_firewall_rule_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
firewall_rule_t *rule_ptr = firewall->get_rule_from_firewall(rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
void API::get_firewall_rules_handler()
{
if (this->check_auth() == DENIED)
return;
this->json_array_response(this->construct_json_firewall(), 200);
}
void API::post_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->add_rule_to_firewall(args);
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 201);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::put_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->update_rule_of_firewall(args, rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::delete_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (firewall->delete_rule_from_firewall(rule_number) == SUCCESS)
this->json_message_response("firewall rule deleted", 200);
else
this->json_message_response("cannot delete firewall rule", 500);
}
bool API::request_has_all_firewall_parameter()
{
if (!this->server->args())
return false;
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
if (i != KEY && !this->server->hasArg(firewall_fields[i]))
return false;
}
return true;
}
String API::json_new_attribute(String key, String value, bool last)
{
String json_string;
json_string += "\"" + key + "\": \"" + value + "\"";
if (!last)
json_string += ",";
return json_string;
}
String API::json_new_attribute(String key, uint32_t value, bool last)
{
return json_new_attribute(key, String(value), last);
}
void API::json_generic_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, serialized_string);
}
void API::json_array_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, "[" + serialized_string + "]");
}
void API::json_message_response(String message, const uint16_t response_code)
{
String serialized_string = "{";
serialized_string += json_new_attribute("message", message, true);
serialized_string += "}";
this->server->send(response_code, json_response_type, serialized_string);
}
String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute(firewall_fields[KEY], rule_ptr->key);
serialized_string += json_new_attribute(firewall_fields[IP], rule_ptr->ip);
serialized_string += json_new_attribute(firewall_fields[PORT_FROM], rule_ptr->port_from);
serialized_string += json_new_attribute(firewall_fields[PORT_TO], rule_ptr->port_to);
serialized_string += json_new_attribute(firewall_fields[PROTOCOL], protocol_to_string(rule_ptr->protocol));
serialized_string += json_new_attribute(firewall_fields[TARGET], target_to_string(rule_ptr->target), true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_firewall()
{
firewall_rule_t *rule_ptr = firewall->get_rule_head();
String serialized_string;
while (rule_ptr != NULL)
{
serialized_string += construct_json_firewall_rule(rule_ptr);
rule_ptr = rule_ptr->next;
if (rule_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
String API::construct_json_api_endpoint(api_endpoint_t *api_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute("endpoint", api_ptr->uri);
serialized_string += json_new_attribute("description", api_ptr->description);
serialized_string += json_new_attribute("method", api_ptr->method, true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_api()
{
api_endpoint_t *api_ptr = this->endpoint_head;
String serialized_string;
while (api_ptr != NULL)
{
serialized_string += construct_json_api_endpoint(api_ptr);
api_ptr = api_ptr->next;
if (api_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
}

55
src/esp32/API.hpp Normal file
View file

@ -0,0 +1,55 @@
#ifndef ESP32_API_HPP
#define ESP32_API_HPP
#include "WebServer.h"
#include "uri/UriBraces.h"
#include "Firewall.hpp"
#include "Utils.hpp"
namespace fw
{
class API
{
public:
API(Firewall *firewall, const char *username, const char *password, const String ip, const uint16_t port = 8080);
~API();
private:
WebServer *server;
Firewall *firewall;
credential_t credentials;
api_endpoint_t *endpoint_head = NULL;
String api_ip = "0.0.0.0";
uint16_t api_port;
String json_response_type = "application/json; charset=utf-8";
const char *TAG = "[API]";
String get_url_base();
ok_t setup_auth(const char *username, const char *password);
auth_t check_auth();
void setup_routing();
void add_endpoint_to_list(const String uri, const char *method, const char *description);
void not_found_handler();
void get_endpoint_list_handler();
void get_firewall_rule_handler();
void get_firewall_rules_handler();
void post_firewall_handler();
void put_firewall_handler();
void delete_firewall_handler();
bool request_has_all_firewall_parameter();
String json_new_attribute(String key, String value, bool last = false);
String json_new_attribute(String key, uint32_t value, bool last = false);
void json_generic_response(String serialized_string, const uint16_t response_code);
void json_array_response(String serialized_string, const uint16_t response_code);
void json_message_response(String message, const uint16_t response_code);
String construct_json_firewall_rule(firewall_rule_t *rule_ptr);
String construct_json_firewall();
String construct_json_api_endpoint(api_endpoint_t *api_ptr);
String construct_json_api();
};
}
#endif

181
src/esp32/Firewall.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "Firewall.hpp"
namespace fw
{
Firewall::Firewall()
{
this->amount_of_rules = retrieve_amount_of_rules();
for (uint8_t i = 1; i <= this->amount_of_rules; i++)
{
firewall_rule_t *rule_ptr = retrieve_firewall_rule(i);
this->add_rule_to_firewall(rule_ptr, false);
}
}
Firewall::~Firewall()
{
}
firewall_rule_t *Firewall::get_rule_head()
{
return this->rule_head;
}
void Firewall::add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom)
{
store_amount_of_rules(this->amount_of_rules);
if (save_in_eeprom)
Storage::store_firewall_rule(rule_ptr);
if (this->rule_head == NULL)
{
this->rule_head = rule_ptr;
rule_ptr->next = NULL;
return;
}
firewall_rule_t *current_rule;
current_rule = this->rule_head;
while (current_rule->next != NULL)
current_rule = current_rule->next;
current_rule->next = rule_ptr;
rule_ptr->next = NULL;
}
firewall_rule_t *Firewall::add_rule_to_firewall(String *args)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = ++this->amount_of_rules;
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
add_rule_to_firewall(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::update_rule_of_firewall(String *args, const uint8_t key)
{
firewall_rule_t *rule_ptr = get_rule_from_firewall(key);
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
Storage::store_firewall_rule(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::get_rule_from_firewall(const uint8_t key)
{
firewall_rule_t *rule_ptr = this->rule_head;
if (this->rule_head == NULL)
return NULL;
while (rule_ptr->key != key)
{
if (rule_ptr->next == NULL)
return NULL;
else
rule_ptr = rule_ptr->next;
}
return rule_ptr;
}
ok_t Firewall::delete_rule_from_firewall(const uint8_t key)
{
if (this->rule_head == NULL)
return NO_ACTION;
firewall_rule_t *current_rule = this->rule_head;
firewall_rule_t *previous_rule = NULL;
firewall_rule_t *temp = NULL;
while (current_rule->key != key)
{
if (current_rule->next == NULL)
return NO_ACTION;
else
{
previous_rule = current_rule;
current_rule = current_rule->next;
}
}
if (current_rule == this->rule_head)
{
this->rule_head = rule_head->next;
temp = this->rule_head;
}
else
{
previous_rule->next = current_rule->next;
temp = previous_rule->next;
}
while (temp != NULL)
{
temp->key--;
temp = temp->next;
}
free(current_rule);
this->amount_of_rules--;
Storage::store_amount_of_rules(this->amount_of_rules);
if (this->amount_of_rules != 0)
Storage::store_all_firewall_rules(rule_head);
return SUCCESS;
}
my_packet_t *Firewall::get_packet_information(struct pbuf *pbuf)
{
my_packet_t *packet = (my_packet_t *)malloc(sizeof(my_packet_t));
const struct ip_hdr *iphdr = (struct ip_hdr *)pbuf->payload;
u16_t iphdr_hlen = IPH_HL_BYTES(iphdr);
packet->protocol = (firewall_protocol_t)IPH_PROTO(iphdr);
sprintf(packet->ip, "%d.%d.%d.%d", ip4_addr1_16_val(iphdr->src), ip4_addr2_16_val(iphdr->src), ip4_addr3_16_val(iphdr->src), ip4_addr4_16_val(iphdr->src));
if (packet->protocol == PROTOCOL_UDP)
{
const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(udphdr->dest);
}
else if (packet->protocol == PROTOCOL_TCP)
{
const struct tcp_hdr *tcphdr = (const struct tcp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(tcphdr->dest);
}
return packet;
}
bool Firewall::rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet)
{
if (strncmp(rule_ptr->ip, packet->ip, IPV4ADDRESS_LENGTH) == 0)
{
if ((rule_ptr->protocol == PROTOCOL_ALL || packet->protocol == rule_ptr->protocol) &&
is_in_range(packet->port, rule_ptr->port_from, rule_ptr->port_to) &&
rule_ptr->target == TARGET_ACCEPT)
{
free(packet);
return true;
}
}
return false;
}
bool Firewall::is_packet_allowed(struct pbuf *pbuf)
{
// no rules -> no action
if (this->amount_of_rules == 0)
return true;
my_packet_t *packet = get_packet_information(pbuf);
firewall_rule_t *rule_ptr = this->rule_head;
while (rule_ptr != NULL)
{
if (rule_allows_packet(rule_ptr, packet))
return true;
rule_ptr = rule_ptr->next;
}
free(packet);
return false;
}
}

39
src/esp32/Firewall.hpp Normal file
View file

@ -0,0 +1,39 @@
#ifndef ESP32_FIREWALL_HPP
#define ESP32_FIREWALL_HPP
#include "Utils.hpp"
#include "Storage.hpp"
#include "WiFiClient.h"
#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "lwip/ip4.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
#include "lwip/prot/tcp.h"
namespace fw
{
class Firewall : public Storage
{
public:
Firewall();
~Firewall();
firewall_rule_t *get_rule_head();
void add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom = true);
firewall_rule_t *add_rule_to_firewall(String *args);
firewall_rule_t *update_rule_of_firewall(String *args, const uint8_t key);
firewall_rule_t *get_rule_from_firewall(const uint8_t key);
ok_t delete_rule_from_firewall(const uint8_t key);
bool is_packet_allowed(struct pbuf *pbuf);
protected:
bool rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet);
my_packet_t *get_packet_information(struct pbuf *pbuf);
uint8_t amount_of_rules = 0;
firewall_rule_t *rule_head = NULL;
};
}
#endif

70
src/esp32/Storage.cpp Normal file
View file

@ -0,0 +1,70 @@
#include "Storage.hpp"
namespace fw
{
Storage::Storage()
{
}
Storage::~Storage()
{
}
uint8_t Storage::retrieve_amount_of_rules()
{
this->memory.begin("settings", true);
const uint8_t amount_of_rules = memory.getUChar("amount_of_rules", 0);
this->memory.end();
return amount_of_rules;
}
void Storage::store_amount_of_rules(const uint8_t new_amount)
{
this->memory.begin("settings", false);
this->memory.putUChar("amount_of_rules", new_amount);
this->memory.end();
}
firewall_rule_t *Storage::retrieve_firewall_rule(const uint8_t key)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = key;
char rulename[10]; // fwRule99\n
sprintf(rulename, "fwRule%i", key);
this->memory.begin(rulename, true);
strncpy(rule_ptr->ip, this->memory.getString(firewall_fields[IP], "0.0.0.0").c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = this->memory.getUShort(firewall_fields[PORT_FROM], 0);
rule_ptr->port_to = this->memory.getUShort(firewall_fields[PORT_TO], 0);
rule_ptr->protocol = static_cast<firewall_protocol_t>(this->memory.getUChar(firewall_fields[PROTOCOL], PROTOCOL_ALL));
rule_ptr->target = static_cast<firewall_target_t>(this->memory.getUChar(firewall_fields[TARGET], TARGET_ACCEPT));
this->memory.end();
return rule_ptr;
}
void Storage::store_all_firewall_rules(firewall_rule_t *rule_head)
{
firewall_rule_t *temp = rule_head;
while (temp != NULL)
{
store_firewall_rule(temp);
temp = temp->next;
}
}
void Storage::store_firewall_rule(firewall_rule_t *rule_ptr)
{
char rulename[10]; // fwRule99\n
sprintf(rulename, "fwRule%i", rule_ptr->key);
this->memory.begin(rulename, false);
this->memory.putString(firewall_fields[IP], rule_ptr->ip);
this->memory.putUShort(firewall_fields[PORT_FROM], rule_ptr->port_from);
this->memory.putUShort(firewall_fields[PORT_TO], rule_ptr->port_to);
this->memory.putUChar(firewall_fields[PROTOCOL], rule_ptr->protocol);
this->memory.putUChar(firewall_fields[TARGET], rule_ptr->target);
this->memory.end();
}
}

27
src/esp32/Storage.hpp Normal file
View file

@ -0,0 +1,27 @@
#ifndef ESP32_STORAGE_HPP
#define ESP32_STORAGE_HPP
#include "Preferences.h"
#include "Utils.hpp"
namespace fw
{
class Storage
{
public:
Storage();
~Storage();
private:
Preferences memory;
protected:
uint8_t retrieve_amount_of_rules();
void store_amount_of_rules(const uint8_t new_amount);
firewall_rule_t *retrieve_firewall_rule(const uint8_t key);
void store_all_firewall_rules(firewall_rule_t *rule_head);
void store_firewall_rule(firewall_rule_t *rule_ptr);
};
}
#endif

58
src/esp32/Utils.cpp Normal file
View file

@ -0,0 +1,58 @@
#include "Utils.hpp"
namespace fw
{
String protocol_to_string(firewall_protocol_t &protocol)
{
switch (protocol)
{
case PROTOCOL_TCP:
return "TCP";
case PROTOCOL_UDP:
return "UDP";
default:
return "ALL";
}
}
firewall_protocol_t string_to_protocol(String &protocol)
{
if (protocol.equals("TCP"))
return PROTOCOL_TCP;
else if (protocol.equals("UDP"))
return PROTOCOL_UDP;
else
return PROTOCOL_ALL;
}
String target_to_string(firewall_target_t &target)
{
switch (target)
{
case TARGET_DROP:
return "DROP";
default:
return "ACCEPT";
}
}
firewall_target_t string_to_target(String &target)
{
if (target.equals("DROP"))
return TARGET_DROP;
else
return TARGET_ACCEPT;
}
void endless_loop()
{
Serial.printf("Something went wrong. Running endless loop until fixed...");
while (true)
delay(500);
}
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper)
{
return lower <= number && number <= upper;
}
}

90
src/esp32/Utils.hpp Normal file
View file

@ -0,0 +1,90 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include "Arduino.h"
#include "WString.h"
namespace fw
{
typedef enum firewall_targets : uint8_t
{
TARGET_DROP = 1,
TARGET_ACCEPT = 2,
} firewall_target_t;
typedef enum firewall_protocols : uint8_t
{
PROTOCOL_TCP = 6,
PROTOCOL_UDP = 17,
PROTOCOL_ALL = 255,
} firewall_protocol_t;
typedef enum ok : uint8_t
{
SUCCESS = 0,
ERROR = 1,
NO_ACTION = 2,
} ok_t;
typedef enum auth : uint8_t
{
AUTHENTICATED = 0,
DENIED = 1,
} auth_t;
static const uint8_t IPV4ADDRESS_LENGTH = 16;
typedef struct firewall_rules
{
uint8_t key;
char ip[IPV4ADDRESS_LENGTH];
uint16_t port_from;
uint16_t port_to;
firewall_protocol_t protocol;
firewall_target_t target;
struct firewall_rules *next;
} firewall_rule_t;
typedef struct my_packet
{
char ip[IPV4ADDRESS_LENGTH];
firewall_protocol_t protocol;
uint16_t port;
} my_packet_t;
static const uint8_t firewall_fields_amount = 6;
const char firewall_fields[firewall_fields_amount][10] = {"key", "ip", "port_from", "port_to", "protocol", "target"};
typedef enum firewall_fields : uint8_t
{
KEY,
IP,
PORT_FROM,
PORT_TO,
PROTOCOL,
TARGET,
} firewall_fields_t;
static const uint8_t CREDENTIALS_LENGTH = 32;
typedef struct credentials
{
char password[CREDENTIALS_LENGTH];
char username[CREDENTIALS_LENGTH];
} credential_t;
typedef struct api_endpoints
{
char uri[60];
char method[7];
char description[30];
struct api_endpoints *next;
} api_endpoint_t;
String protocol_to_string(firewall_protocol_t &protocol);
firewall_protocol_t string_to_protocol(String &protocol);
String target_to_string(firewall_target_t &target);
firewall_target_t string_to_target(String &target);
String response_code_to_string(const uint16_t response_code);
void endless_loop();
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper);
}
#endif

287
src/esp8266/API.cpp Normal file
View file

@ -0,0 +1,287 @@
#include "API.hpp"
namespace fw
{
API::API(fw::Firewall *firewall, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port)
{
this->firewall = firewall;
this->api_ip = ip;
this->api_port = port;
if (this->setup_auth(username, password) == ERROR)
endless_loop();
this->server = new ESP8266WebServerSecure(port);
this->serverCache = new ServerSessions(5);
this->setup_routing(cert, key);
this->server->begin();
Serial.printf("%s endpoints -> %s/api\n", TAG, this->get_url_base().c_str());
}
API::~API()
{
this->server->stop();
}
void API::handle_client()
{
this->server->handleClient();
}
String API::get_url_base()
{
return "https://" + this->api_ip + ":" + this->api_port;
}
ok_t API::setup_auth(const char *username, const char *password)
{
if (!username || *username == 0x00 || strlen(username) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Username too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.username, username, CREDENTIALS_LENGTH);
if (!password || *password == 0x00 || strlen(password) > CREDENTIALS_LENGTH)
{
Serial.printf("%s Password too long or missing!\n", TAG);
return ERROR;
}
strncpy(credentials.password, password, CREDENTIALS_LENGTH);
return SUCCESS;
}
auth_t API::check_auth()
{
if (server->authenticate(this->credentials.username, this->credentials.password))
{
return AUTHENTICATED;
}
this->json_message_response("unauthorised", 403);
return DENIED;
}
void API::setup_routing(const char *cert, const char *key)
{
this->server->getServer().setRSACert(new BearSSL::X509List(cert), new BearSSL::PrivateKey(key));
this->server->getServer().setCache(serverCache);
this->server->on("/api/firewall/rules", HTTP_GET, std::bind(&API::get_firewall_rules_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_GET, std::bind(&API::get_firewall_rule_handler, this));
this->server->on("/api/firewall/rules", HTTP_POST, std::bind(&API::post_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_PUT, std::bind(&API::put_firewall_handler, this));
this->server->on(UriBraces("/api/firewall/rules/{}"), HTTP_DELETE, std::bind(&API::delete_firewall_handler, this));
this->server->on("/api", HTTP_GET, std::bind(&API::get_endpoint_list_handler, this));
this->server->onNotFound(std::bind(&API::not_found_handler, this));
add_endpoint_to_list("/api/firewall/rules", "GET", "Get all Firewall Rules");
add_endpoint_to_list("/api/firewall/rules/<key>", "GET", "Get Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules", "POST", "Create Firewall Rule");
add_endpoint_to_list("/api/firewall/rules/<key>", "PUT", "Update Firewall Rule by key");
add_endpoint_to_list("/api/firewall/rules/<key>", "DELETE", "Delete Firewall Rule by key");
}
void API::add_endpoint_to_list(const String uri, const char *method, const char *description)
{
api_endpoint_t *temp;
const String url = get_url_base() + uri;
api_endpoint_t *api_ptr = (api_endpoint_t *)malloc(sizeof(api_endpoint_t));
strncpy(api_ptr->uri, url.c_str(), sizeof(api_ptr->uri));
strncpy(api_ptr->method, method, sizeof(api_ptr->method));
strncpy(api_ptr->description, description, sizeof(api_ptr->description));
if (this->endpoint_head == NULL)
{
this->endpoint_head = api_ptr;
api_ptr->next = NULL;
return;
}
temp = this->endpoint_head;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = api_ptr;
api_ptr->next = NULL;
return;
}
void API::not_found_handler()
{
this->json_message_response("see " + get_url_base() + "/api for available routes", 404);
}
void API::get_endpoint_list_handler()
{
this->json_array_response(this->construct_json_api(), 200);
}
void API::get_firewall_rule_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
firewall_rule_t *rule_ptr = firewall->get_rule_from_firewall(rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
void API::get_firewall_rules_handler()
{
if (this->check_auth() == DENIED)
return;
this->json_array_response(this->construct_json_firewall(), 200);
}
void API::post_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->add_rule_to_firewall(args);
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 201);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::put_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (request_has_all_firewall_parameter())
{
String args[IPV4ADDRESS_LENGTH] = {};
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
args[i] = this->server->arg(firewall_fields[i]);
}
firewall_rule_t *rule_ptr = firewall->update_rule_of_firewall(args, rule_number);
if (rule_ptr == NULL)
this->json_message_response("rule does not exist", 404);
else
this->json_generic_response(this->construct_json_firewall_rule(rule_ptr), 200);
}
else
{
this->json_message_response("not enough parameter provided", 400);
}
}
void API::delete_firewall_handler()
{
if (this->check_auth() == DENIED)
return;
String param = this->server->pathArg(0);
int rule_number = atoi(param.c_str());
if (firewall->delete_rule_from_firewall(rule_number) == SUCCESS)
this->json_message_response("firewall rule deleted", 200);
else
this->json_message_response("cannot delete firewall rule", 500);
}
bool API::request_has_all_firewall_parameter()
{
if (!this->server->args())
return false;
for (uint8_t i = 0; i < firewall_fields_amount; i++)
{
if (i != KEY && !this->server->hasArg(firewall_fields[i]))
return false;
}
return true;
}
String API::json_new_attribute(String key, String value, bool last)
{
String json_string;
json_string += "\"" + key + "\": \"" + value + "\"";
if (!last)
json_string += ",";
return json_string;
}
String API::json_new_attribute(String key, uint32_t value, bool last)
{
return json_new_attribute(key, String(value), last);
}
void API::json_generic_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, serialized_string);
}
void API::json_array_response(String serialized_string, const uint16_t response_code)
{
this->server->send(response_code, json_response_type, "[" + serialized_string + "]");
}
void API::json_message_response(String message, const uint16_t response_code)
{
String serialized_string = "{";
serialized_string += json_new_attribute("message", message, true);
serialized_string += "}";
this->server->send(response_code, json_response_type, serialized_string);
}
String API::construct_json_firewall_rule(firewall_rule_t *rule_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute(firewall_fields[KEY], rule_ptr->key);
serialized_string += json_new_attribute(firewall_fields[IP], rule_ptr->ip);
serialized_string += json_new_attribute(firewall_fields[PORT_FROM], rule_ptr->port_from);
serialized_string += json_new_attribute(firewall_fields[PORT_TO], rule_ptr->port_to);
serialized_string += json_new_attribute(firewall_fields[PROTOCOL], protocol_to_string(rule_ptr->protocol));
serialized_string += json_new_attribute(firewall_fields[TARGET], target_to_string(rule_ptr->target), true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_firewall()
{
firewall_rule_t *rule_ptr = firewall->get_rule_head();
String serialized_string;
while (rule_ptr != NULL)
{
serialized_string += construct_json_firewall_rule(rule_ptr);
rule_ptr = rule_ptr->next;
if (rule_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
String API::construct_json_api_endpoint(api_endpoint_t *api_ptr)
{
String serialized_string = "{";
serialized_string += json_new_attribute("endpoint", api_ptr->uri);
serialized_string += json_new_attribute("description", api_ptr->description);
serialized_string += json_new_attribute("method", api_ptr->method, true);
serialized_string += "}";
return serialized_string;
}
String API::construct_json_api()
{
api_endpoint_t *api_ptr = this->endpoint_head;
String serialized_string;
while (api_ptr != NULL)
{
serialized_string += construct_json_api_endpoint(api_ptr);
api_ptr = api_ptr->next;
if (api_ptr != NULL)
serialized_string += ",";
}
return serialized_string;
}
}

57
src/esp8266/API.hpp Normal file
View file

@ -0,0 +1,57 @@
#ifndef ESP8266_API_HPP
#define ESP8266_API_HPP
#include "ESP8266WebServerSecure.h"
#include "uri/UriBraces.h"
#include "Firewall.hpp"
#include "Utils.hpp"
namespace fw
{
class API
{
public:
API(Firewall *, const char *cert, const char *key, const char *username, const char *password, const String ip, const uint16_t port = 8080);
~API();
void handle_client();
private:
BearSSL::ESP8266WebServerSecure *server;
BearSSL::ServerSessions *serverCache;
Firewall *firewall;
credential_t credentials;
api_endpoint_t *endpoint_head = NULL;
String api_ip = "0.0.0.0";
uint16_t api_port;
String json_response_type = "application/json; charset=utf-8";
const char *TAG = "[API]";
String get_url_base();
ok_t setup_auth(const char *username, const char *password);
auth_t check_auth();
void setup_routing(const char *cert, const char *key);
void add_endpoint_to_list(const String uri, const char *method, const char *description);
void not_found_handler();
void get_endpoint_list_handler();
void get_firewall_rule_handler();
void get_firewall_rules_handler();
void post_firewall_handler();
void put_firewall_handler();
void delete_firewall_handler();
bool request_has_all_firewall_parameter();
String json_new_attribute(String key, String value, bool last = false);
String json_new_attribute(String key, uint32_t value, bool last = false);
void json_generic_response(String serialized_string, const uint16_t response_code);
void json_array_response(String serialized_string, const uint16_t response_code);
void json_message_response(String message, const uint16_t response_code);
String construct_json_firewall_rule(firewall_rule_t *rule_ptr);
String construct_json_firewall();
String construct_json_api_endpoint(api_endpoint_t *api_ptr);
String construct_json_api();
};
}
#endif

181
src/esp8266/Firewall.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "Firewall.hpp"
namespace fw
{
Firewall::Firewall()
{
this->amount_of_rules = retrieve_amount_of_rules();
for (uint8_t i = 1; i <= this->amount_of_rules; i++)
{
firewall_rule_t *rule_ptr = retrieve_firewall_rule(i);
this->add_rule_to_firewall(rule_ptr, false);
}
}
Firewall::~Firewall()
{
}
firewall_rule_t *Firewall::get_rule_head()
{
return this->rule_head;
}
void Firewall::add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom)
{
store_amount_of_rules(this->amount_of_rules);
if (save_in_eeprom)
Storage::store_firewall_rule(rule_ptr);
if (this->rule_head == NULL)
{
this->rule_head = rule_ptr;
rule_ptr->next = NULL;
return;
}
firewall_rule_t *current_rule;
current_rule = this->rule_head;
while (current_rule->next != NULL)
current_rule = current_rule->next;
current_rule->next = rule_ptr;
rule_ptr->next = NULL;
}
firewall_rule_t *Firewall::add_rule_to_firewall(String *args)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = ++this->amount_of_rules;
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
add_rule_to_firewall(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::update_rule_of_firewall(String *args, const uint8_t key)
{
firewall_rule_t *rule_ptr = get_rule_from_firewall(key);
strncpy(rule_ptr->ip, args[IP].c_str(), sizeof(rule_ptr->ip));
rule_ptr->port_from = args[PORT_FROM].toInt();
rule_ptr->port_to = args[PORT_TO].toInt();
rule_ptr->protocol = string_to_protocol(args[PROTOCOL]);
rule_ptr->target = string_to_target(args[TARGET]);
Storage::store_firewall_rule(rule_ptr);
return rule_ptr;
}
firewall_rule_t *Firewall::get_rule_from_firewall(const uint8_t key)
{
firewall_rule_t *rule_ptr = this->rule_head;
if (this->rule_head == NULL)
return NULL;
while (rule_ptr->key != key)
{
if (rule_ptr->next == NULL)
return NULL;
else
rule_ptr = rule_ptr->next;
}
return rule_ptr;
}
ok_t Firewall::delete_rule_from_firewall(const uint8_t key)
{
if (this->rule_head == NULL)
return NO_ACTION;
firewall_rule_t *current_rule = this->rule_head;
firewall_rule_t *previous_rule = NULL;
firewall_rule_t *temp = NULL;
while (current_rule->key != key)
{
if (current_rule->next == NULL)
return NO_ACTION;
else
{
previous_rule = current_rule;
current_rule = current_rule->next;
}
}
if (current_rule == this->rule_head)
{
this->rule_head = rule_head->next;
temp = this->rule_head;
}
else
{
previous_rule->next = current_rule->next;
temp = previous_rule->next;
}
while (temp != NULL)
{
temp->key--;
temp = temp->next;
}
free(current_rule);
this->amount_of_rules--;
Storage::store_amount_of_rules(this->amount_of_rules);
if (this->amount_of_rules != 0)
Storage::store_all_firewall_rules(rule_head);
return SUCCESS;
}
my_packet_t *Firewall::get_packet_information(struct pbuf *pbuf)
{
my_packet_t *packet = (my_packet_t *)malloc(sizeof(my_packet_t));
const struct ip_hdr *iphdr = (struct ip_hdr *)pbuf->payload;
u16_t iphdr_hlen = IPH_HL_BYTES(iphdr);
packet->protocol = (firewall_protocol_t)IPH_PROTO(iphdr);
sprintf(packet->ip, "%d.%d.%d.%d", ip4_addr1_16_val(iphdr->src), ip4_addr2_16_val(iphdr->src), ip4_addr3_16_val(iphdr->src), ip4_addr4_16_val(iphdr->src));
if (packet->protocol == PROTOCOL_UDP)
{
const struct udp_hdr *udphdr = (const struct udp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(udphdr->dest);
}
else if (packet->protocol == PROTOCOL_TCP)
{
const struct tcp_hdr *tcphdr = (const struct tcp_hdr *)((const u8_t *)iphdr + iphdr_hlen);
packet->port = lwip_ntohs(tcphdr->dest);
}
return packet;
}
bool Firewall::rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet)
{
if (strncmp(rule_ptr->ip, packet->ip, IPV4ADDRESS_LENGTH) == 0)
{
if ((rule_ptr->protocol == PROTOCOL_ALL || packet->protocol == rule_ptr->protocol) &&
is_in_range(packet->port, rule_ptr->port_from, rule_ptr->port_to) &&
rule_ptr->target == TARGET_ACCEPT)
{
free(packet);
return true;
}
}
return false;
}
bool Firewall::is_packet_allowed(struct pbuf *pbuf)
{
// no rules -> no action
if (this->amount_of_rules == 0)
return true;
my_packet_t *packet = get_packet_information(pbuf);
firewall_rule_t *rule_ptr = this->rule_head;
while (rule_ptr != NULL)
{
if (rule_allows_packet(rule_ptr, packet))
return true;
rule_ptr = rule_ptr->next;
}
free(packet);
return false;
}
}

38
src/esp8266/Firewall.hpp Normal file
View file

@ -0,0 +1,38 @@
#ifndef ESP8266_FIREWALL_HPP
#define ESP8266_FIREWALL_HPP
#include "Utils.hpp"
#include "Storage.hpp"
#include "WiFiClient.h"
#include "lwip/netif.h"
#include "lwip/pbuf.h"
#include "lwip/ip4.h"
#include "lwip/udp.h"
#include "lwip/prot/tcp.h"
namespace fw
{
class Firewall : public Storage
{
public:
Firewall();
~Firewall();
firewall_rule_t *get_rule_head();
void add_rule_to_firewall(firewall_rule_t *rule_ptr, const bool save_in_eeprom = true);
firewall_rule_t *add_rule_to_firewall(String *args);
firewall_rule_t *update_rule_of_firewall(String *args, const uint8_t key);
firewall_rule_t *get_rule_from_firewall(const uint8_t key);
ok_t delete_rule_from_firewall(const uint8_t key);
bool is_packet_allowed(struct pbuf *pbuf);
protected:
bool rule_allows_packet(firewall_rule_t *rule_ptr, my_packet_t *packet);
my_packet_t *get_packet_information(struct pbuf *pbuf);
uint8_t amount_of_rules = 0;
firewall_rule_t *rule_head = NULL;
};
}
#endif

74
src/esp8266/Storage.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "Storage.hpp"
namespace fw
{
Storage::Storage()
{
this->max_rules = 15;
this->eeprom_amount_of_rules = 0;
this->eeprom_rules_head = 1;
this->eeprom_size = this->max_rules * sizeof(firewall_rule_t) + eeprom_rules_head;
EEPROM.begin(this->eeprom_size);
}
Storage::~Storage()
{
}
uint16_t Storage::eeprom_rule_position(uint8_t key)
{
return eeprom_rules_head + (key - 1) * sizeof(firewall_rule_t);
}
uint8_t Storage::retrieve_amount_of_rules()
{
uint8_t amount_of_rules = EEPROM.read(this->eeprom_amount_of_rules);
if (amount_of_rules > this->max_rules)
return 0;
return amount_of_rules;
}
void Storage::store_amount_of_rules(const uint8_t new_amount)
{
EEPROM.put(this->eeprom_amount_of_rules, new_amount);
EEPROM.commit();
}
firewall_rule_t *Storage::retrieve_firewall_rule(const uint8_t key)
{
firewall_rule_t *rule_ptr = (firewall_rule_t *)malloc(sizeof(firewall_rule_t));
rule_ptr->key = key;
uint16_t eespom_position = eeprom_rule_position(key);
EEPROM.get(eespom_position, rule_ptr->ip);
EEPROM.get(eespom_position += sizeof(rule_ptr->ip), rule_ptr->port_from);
EEPROM.get(eespom_position += sizeof(rule_ptr->port_from), rule_ptr->port_to);
EEPROM.get(eespom_position += sizeof(rule_ptr->port_to), rule_ptr->protocol);
EEPROM.get(eespom_position += sizeof(rule_ptr->protocol), rule_ptr->target);
return rule_ptr;
}
void Storage::store_all_firewall_rules(firewall_rule_t *rule_head)
{
firewall_rule_t *temp = rule_head;
while (temp != NULL)
{
store_firewall_rule(temp);
temp = temp->next;
}
}
void Storage::store_firewall_rule(firewall_rule_t *rule_ptr)
{
uint16_t eespom_position = eeprom_rule_position(rule_ptr->key);
EEPROM.put(eespom_position, rule_ptr->ip);
EEPROM.put(eespom_position += sizeof(rule_ptr->ip), rule_ptr->port_from);
EEPROM.put(eespom_position += sizeof(rule_ptr->port_from), rule_ptr->port_to);
EEPROM.put(eespom_position += sizeof(rule_ptr->port_to), rule_ptr->protocol);
EEPROM.put(eespom_position += sizeof(rule_ptr->protocol), rule_ptr->target);
EEPROM.commit();
}
}

32
src/esp8266/Storage.hpp Normal file
View file

@ -0,0 +1,32 @@
#ifndef ESP8266_STORAGE_HPP
#define ESP8266_STORAGE_HPP
#include "EEPROM.h"
#include "Utils.hpp"
namespace fw
{
class Storage
{
public:
Storage();
~Storage();
private:
uint8_t max_rules;
uint16_t eeprom_size;
uint16_t eeprom_amount_of_rules;
uint16_t eeprom_rules_head;
uint16_t eeprom_rule_position(uint8_t key);
protected:
uint8_t retrieve_amount_of_rules();
void store_amount_of_rules(const uint8_t new_amount);
firewall_rule_t *retrieve_firewall_rule(const uint8_t key);
void store_all_firewall_rules(firewall_rule_t *rule_head);
void store_firewall_rule(firewall_rule_t *rule_ptr);
};
}
#endif

58
src/esp8266/Utils.cpp Normal file
View file

@ -0,0 +1,58 @@
#include "Utils.hpp"
namespace fw
{
String protocol_to_string(firewall_protocol_t &protocol)
{
switch (protocol)
{
case PROTOCOL_TCP:
return "TCP";
case PROTOCOL_UDP:
return "UDP";
default:
return "ALL";
}
}
firewall_protocol_t string_to_protocol(String &protocol)
{
if (protocol.equals("TCP"))
return PROTOCOL_TCP;
else if (protocol.equals("UDP"))
return PROTOCOL_UDP;
else
return PROTOCOL_ALL;
}
String target_to_string(firewall_target_t &target)
{
switch (target)
{
case TARGET_DROP:
return "DROP";
default:
return "ACCEPT";
}
}
firewall_target_t string_to_target(String &target)
{
if (target.equals("DROP"))
return TARGET_DROP;
else
return TARGET_ACCEPT;
}
void endless_loop()
{
Serial.printf("Something went wrong. Running endless loop until fixed...");
while (true)
delay(500);
}
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper)
{
return lower <= number && number <= upper;
}
}

90
src/esp8266/Utils.hpp Normal file
View file

@ -0,0 +1,90 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include "Arduino.h"
#include "WString.h"
namespace fw
{
typedef enum firewall_targets : uint8_t
{
TARGET_DROP = 1,
TARGET_ACCEPT = 2,
} firewall_target_t;
typedef enum firewall_protocols : uint8_t
{
PROTOCOL_TCP = 6,
PROTOCOL_UDP = 17,
PROTOCOL_ALL = 255,
} firewall_protocol_t;
typedef enum ok : uint8_t
{
SUCCESS = 0,
ERROR = 1,
NO_ACTION = 2,
} ok_t;
typedef enum auth : uint8_t
{
AUTHENTICATED = 0,
DENIED = 1,
} auth_t;
static const uint8_t IPV4ADDRESS_LENGTH = 16;
typedef struct firewall_rules
{
uint8_t key;
char ip[IPV4ADDRESS_LENGTH];
uint16_t port_from;
uint16_t port_to;
firewall_protocol_t protocol;
firewall_target_t target;
struct firewall_rules *next;
} firewall_rule_t;
typedef struct my_packet
{
char ip[IPV4ADDRESS_LENGTH];
firewall_protocol_t protocol;
uint16_t port;
} my_packet_t;
static const uint8_t firewall_fields_amount = 6;
const char firewall_fields[firewall_fields_amount][10] = {"key", "ip", "port_from", "port_to", "protocol", "target"};
typedef enum firewall_fields : uint8_t
{
KEY,
IP,
PORT_FROM,
PORT_TO,
PROTOCOL,
TARGET,
} firewall_fields_t;
static const uint8_t CREDENTIALS_LENGTH = 32;
typedef struct credentials
{
char password[CREDENTIALS_LENGTH];
char username[CREDENTIALS_LENGTH];
} credential_t;
typedef struct api_endpoints
{
char uri[60];
char method[7];
char description[30];
struct api_endpoints *next;
} api_endpoint_t;
String protocol_to_string(firewall_protocol_t &protocol);
firewall_protocol_t string_to_protocol(String &protocol);
String target_to_string(firewall_target_t &target);
firewall_target_t string_to_target(String &target);
String response_code_to_string(const uint16_t response_code);
void endless_loop();
bool is_in_range(const uint16_t number, const uint16_t lower, const uint16_t upper);
}
#endif