From 1d7746b2895e83d83ed9726c5eccc9d6129499c7 Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 12:53:12 -0700 Subject: [PATCH 1/7] Refactor: Comments for vlan.c Refactor comments and add comments for each function in vlan.c file such that they adhere to the Linux Kernely style comment block Signed-off-by: Korel --- src/vlan.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 5 deletions(-) diff --git a/src/vlan.c b/src/vlan.c index 53dbf7d..e185e4c 100644 --- a/src/vlan.c +++ b/src/vlan.c @@ -12,11 +12,21 @@ DEFINE_SPINLOCK(vlan_lock); static struct hlist_head vlan_hash_table[VLAN_HASH_SIZE]; int currentNumberOfVLANs; -// Hash function +/** + * vlan_hash - Compute hash index for a VLAN ID + * @vlan_id: VLAN identifier + * + * Return: A hash bucket index for the given VLAN ID using hash_32(). + */ static inline unsigned int vlan_hash(u16 vlan_id) { return hash_32(vlan_id, VLAN_HASH_BITS); } +/** + * init_vlan_hash_table - Initialize VLAN hash table + * + * Sets up each bucket in the vlan_hash_table as an empty hlist head. + */ void init_vlan_hash_table(void) { int i; for (i = 0; i < VLAN_HASH_SIZE; i++) { @@ -24,7 +34,18 @@ void init_vlan_hash_table(void) { } } -// Add VLAN to be inspected +/** + * add_vlan_to_inspect - Add a VLAN ID to the inspection hash table + * @vlan_id: VLAN identifier to add + * + * This function adds a VLAN ID to the vlan_hash_table for inspection. + * It first validates that the VLAN ID is within the valid range (1-4094). + * If the VLAN is already present in the hash table, it does nothing. + * Otherwise, it allocates memory for a new hash entry, initializes it, + * and inserts it at the head of the appropriate hash bucket. + * The insertion and lookup are protected by a spinlock. + * + */ void add_vlan_to_inspect(u16 vlan_id) { unsigned int hash; struct vlan_hash_entry *entry; @@ -61,7 +82,13 @@ void add_vlan_to_inspect(u16 vlan_id) { spin_unlock_irqrestore(&vlan_lock, flags); } -// Check if VLAN should be inspected +/** + * vlan_should_be_inspected - Check if a VLAN ID is in the inspection list. + * @vlan_id: The VLAN ID to check. + * + * Return: true if the VLAN ID is present in the hash table of VLANs to be inspected + * false if the VLAN ID is NOT present in the hash table of VLANs to be inspected + */ bool vlan_should_be_inspected(u16 vlan_id) { unsigned int hash = vlan_hash(vlan_id); struct vlan_hash_entry *entry; @@ -77,7 +104,13 @@ bool vlan_should_be_inspected(u16 vlan_id) { return false; } -// To remove a VLAN +/** + * remove_vlan_from_inspect - Remove a VLAN ID from the inspection list. + * @vlan_id: The VLAN ID to remove. + * + * Searches the VLAN hash table for the given VLAN ID and removes it if found, + * freeing the associated memory and decrementing the count of VLANs. + */ void remove_vlan_from_inspect(u16 vlan_id) { unsigned int hash = vlan_hash(vlan_id); struct vlan_hash_entry *entry; @@ -95,10 +128,24 @@ void remove_vlan_from_inspect(u16 vlan_id) { spin_unlock_irqrestore(&vlan_lock, flags); } +/** + * compare_u16 - Compare two u16 values for sorting. + * @a: Pointer to the first u16 value. + * @b: Pointer to the second u16 value. + * + * Return: Negative if *a < *b, zero if equal, positive if *a > *b. + */ static int compare_u16(const void * a, const void * b){ return *(u16 *)a - *(u16 *)b; } +/** + * print_all_vlans_in_hash - Print all VLAN IDs currently in the VLAN hash table. + * + * This function collects all VLAN IDs from the VLAN hash table into a dynamically + * allocated array, sorts them in ascending order, and prints the list to the kernel log. + * The function acquires a spinlock to protect the hash table during collection. + */ void print_all_vlans_in_hash(void) { int i; struct vlan_hash_entry *entry; @@ -145,7 +192,17 @@ void print_all_vlans_in_hash(void) { // Free the dynamically allocated memory kfree(vlan_ids); } -//Taken a string of comma seperated vlans and add those vlans to the inspection list + +/** + * parse_vlans - Parse a comma-separated list of VLAN IDs and add them to the inspection list. + * @vlans: A string containing comma-separated VLAN IDs (e.g., "100,200,300"). + * + * This function duplicates the input string to safely modify it, then splits it by commas + * to extract individual VLAN ID tokens. Each token is converted to a 16-bit unsigned integer. + * If conversion succeeds, the VLAN ID is added to the inspection list via add_vlan_to_inspect(). + * Invalid VLAN IDs are logged as informational messages. + * + */ void parse_vlans(char * vlans) { char * token; char * str; @@ -175,6 +232,16 @@ void parse_vlans(char * vlans) { } +/** + * free_all_vlan_entries - Free all entries in the VLAN hash table. + * + * This function iterates over all buckets in the VLAN hash table, + * safely removes each VLAN entry from the hash lists, and frees + * the associated memory. It also resets the global count of VLANs + * to zero. The function acquires the vlan_lock spinlock to protect + * concurrent access during modification of the hash table. + * + */ void free_all_vlan_entries(void) { //loop counter for hashtable buckets int i; From 0f6fd2aa33eff618afd9b4252b504a22445362ec Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 12:54:14 -0700 Subject: [PATCH 2/7] Refactor: Comments for trusted_interfaces.c Refactor comments and add comments for each function in trusted_interfaces.c file such that they adhere to the Linux Kernely style comment block Signed-off-by: Korel --- src/trusted_interfaces.c | 65 +++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/trusted_interfaces.c b/src/trusted_interfaces.c index 190e221..a712eac 100644 --- a/src/trusted_interfaces.c +++ b/src/trusted_interfaces.c @@ -6,11 +6,10 @@ DEFINE_SPINLOCK(interface_lock); static int trusted_list_size = 0; /** - * populate_trusted_interface_list - Populate the trusted interface list with all network interfaces.asm - * - * This function iterates over all netowrk interfaces in the system and inserts each one into the + * populate_trusted_interface_list - Populate the trusted interface list with all network interfaces + * + * This function iterates over all network interfaces in the system and inserts each one into * the trusted interface list. - * */ void populate_trusted_interface_list(void) { struct net_device *dev; @@ -32,8 +31,11 @@ void populate_trusted_interface_list(void) { * the end fo the lis tusing list_add_tail. The trusted_list_size if then incremented, and * a message is printed to indicate the enw addition.asm * - * Return: This fucntion returns 1 if the interface name was added, 0 if it already exists, - * and -1 if mmory allocaiton failed, -2 if interface was not found, -3 if vlan id was invalid + * Return: 1 if the interface name was added, + * 0 if it already exists, + * -1 if memory allocation failed, + * -2 if the interface was not found, + * -3 if the VLAN ID was invalid. */ int insert_trusted_interface(const char *device_name, u16 vlan_id) { struct net_device *dev; @@ -89,13 +91,13 @@ int insert_trusted_interface(const char *device_name, u16 vlan_id) { /** * find_trusted_interface - Find an interface in the trusted list. - * @interface_name: The name of the interaface to find - * @vlan_id: The vlan associated for DAI - * - * This function searches the trusted interface list for an entry that matches the given interface name. - * If a matching entry is found, the function returns the name of th einterface. If no match is found the - * function returns NULL. - * + * @interface_name: The name of the interface to find. + * @vlan_id: The VLAN ID associated with the interface. + * + * This function searches the trusted interface list for an entry that matches + * the given interface name and VLAN ID. If a matching entry is found, the function + * returns the name of the interface. If no match is found, it returns NULL. + * * Return: The name of the trusted interface if found, or NULL if not found. */ const char* find_trusted_interface(const char *interface_name, u16 vlan_id) { @@ -115,12 +117,12 @@ const char* find_trusted_interface(const char *interface_name, u16 vlan_id) { } /** - * print_trusted_interface_list - Print all interfaces in the trusted list - * - * This function prints the names of all network interfaces in the trusted interface list. - * If the list is empty, it prints a message indicaitng that all interfaces are assumed to be Untrusted. - * - * Return: This function does not return a value + * print_trusted_interface_list - Print all interfaces in the trusted list. + * + * This function logs the names and VLAN IDs of all network interfaces currently + * stored in the trusted interface list. If the list is empty, it prints a message + * indicating that no interfaces are trusted and all are treated as untrusted. + * */ void print_trusted_interface_list(void) { struct interface_entry *entry; @@ -148,15 +150,13 @@ void print_trusted_interface_list(void) { } /** - * free_trusted_interface_list - Free all entries in the trusted interface list - * + * free_trusted_interface_list - Free all entries in the trusted interface list. + * * This function iterates through the trusted interface list and frees each entry. - * It uses list_for_each_entry_safe to traverse the list while deleting entries. - * This means it uses an additional temporary pointer to store the next entry before - * deleting the currnet one Each entry is removed from the list using list_del and - * then freed using kfree. - * - * Return: this function does not return anything + * It uses list_for_each_entry_safe to safely traverse the list while deleting entries. + * This allows safe deletion by storing the next pointer before removing the current one. + * Each entry is removed using list_del and its memory is freed with kfree. + * */ void free_trusted_interface_list(void) { struct interface_entry *entry, *tmp; @@ -172,7 +172,16 @@ void free_trusted_interface_list(void) { } -//Taken a string of comma seperated vlans and add those vlans to the inspection list +/** + * parse_interfaces_and_vlan - parse a comma-separated list of interfaces with VLANs and add them to the trusted list + * @interfaces_and_vlan: string of comma-separated interface:vlan pairs, e.g. "eth0:1,eth1:2" + * + * Parses the string containing interface and VLAN pairs separated by commas. + * Each pair is separated by ':' into interface name and VLAN ID. + * Calls insert_trusted_interface() to add each valid pair to the trusted list. + * Invalid entries (missing colon or invalid VLAN) are ignored with an info message. + * +*/ void parse_interfaces_and_vlan(char * interfaces_and_vlan) { char * token; char * vlan_id_str; From d5eec83d24903365fdc82f0b2e129227f81694d0 Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 12:55:41 -0700 Subject: [PATCH 3/7] Refactor: Comments for rate_limit.c Refactor comments and add comments for each function in rate_limitc file such that they adhere to the Linux Kernely style comment block Signed-off-by: Korel --- src/rate_limit.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rate_limit.c b/src/rate_limit.c index c447217..a421744 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -152,7 +152,6 @@ bool rate_limit_reached(const char *interface_name, u16 vlan_id){ * each entry from the list, and frees the allocated memory. * The operation is done under a spinlock to ensure thread safety. * - * Return: void */ void clean_rate_limit_table(void){ struct list_head* curr, *next; From 577f7c8c4649f8b9b0b32de17d3d08c6c596801c Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 12:59:28 -0700 Subject: [PATCH 4/7] Refactor: Comments for dhcp.c Refactor comments and add comments for each function in dhcp.c file such that they adhere to the Linux Kernel style comment block Signed-off-by: Korel --- src/dhcp.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/src/dhcp.c b/src/dhcp.c index 0ccc50d..4b75664 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -7,6 +7,21 @@ DEFINE_SPINLOCK(slock); struct task_struct* dhcp_thread = NULL; +/** + * insert_dhcp_snooping_entry - Insert a DHCP snooping entry into the list. + * @mac: Pointer to the MAC address. + * @ip: The IP address associated with the DHCP lease. + * @lease_time: Duration of the lease. + * @expire_time: Expiration timestamp of the lease. + * @vlan_id: VLAN ID associated with the entry. + * + * This function creates a new DHCP snooping entry with the given parameters and + * adds it to the global dhcp_snooping_list. The function checks that the VLAN ID + * is within the valid range (1-4094) before insertion. The list is protected with + * a spinlock to ensure thread safety. + * + * If memory allocation fails, or VLAN ID is invalid, the function returns early. + */ void insert_dhcp_snooping_entry(u8 *mac, u32 ip, u32 lease_time, u32 expire_time, u16 vlan_id) { struct dhcp_snooping_entry* entry; unsigned long flags; @@ -34,6 +49,16 @@ void insert_dhcp_snooping_entry(u8 *mac, u32 ip, u32 lease_time, u32 expire_time } +/** + * find_dhcp_snooping_entry - Find a DHCP snooping entry by IP and VLAN ID. + * @ip: The IP address to search for. + * @vlan_id: The VLAN ID associated with the DHCP entry. + * + * Searches the global dhcp_snooping_list for an entry matching the given IP and VLAN ID. + * The list is locked during the search to ensure thread safety. + * + * Returns a pointer to the matching dhcp_snooping_entry if found, or NULL if not found. + */ struct dhcp_snooping_entry* find_dhcp_snooping_entry(u32 ip, u16 vlan_id) { struct list_head* curr, *next; struct dhcp_snooping_entry* entry; @@ -51,7 +76,15 @@ struct dhcp_snooping_entry* find_dhcp_snooping_entry(u32 ip, u16 vlan_id) { return NULL; } - +/** + * delete_dhcp_snooping_entry - Delete a DHCP snooping entry by IP and VLAN ID. + * @ip: The IP address of the entry to delete. + * @vlan_id: The VLAN ID associated with the entry to delete. + * + * Finds and removes the DHCP snooping entry matching the given IP and VLAN ID + * from the dhcp_snooping_list. The list is locked during removal to ensure + * thread safety. Frees the memory allocated for the entry after deletion. + */ void delete_dhcp_snooping_entry(u32 ip, u16 vlan_id) { unsigned long flags; struct dhcp_snooping_entry* entry = find_dhcp_snooping_entry(ip, vlan_id); @@ -64,7 +97,13 @@ void delete_dhcp_snooping_entry(u32 ip, u16 vlan_id) { } } - +/** + * clean_dhcp_snooping_table - Remove and free all entries in the DHCP snooping list. + * + * Iterates over the dhcp_snooping_list, removing each entry from the list and + * freeing its associated memory. The list is locked during this operation to + * ensure thread safety. + */ void clean_dhcp_snooping_table(void) { struct list_head* curr, *next; struct dhcp_snooping_entry* entry; @@ -79,8 +118,18 @@ void clean_dhcp_snooping_table(void) { spin_unlock_irqrestore(&slock, flags); } -//Continuously check the list of DHCP snooping entries and remove any entries that ave expired based on their -//expiration time +/** + * dhcp_thread_handler - Kernel thread function to clean expired DHCP snooping entries. + * @arg: Unused parameter. + * + * This function runs in a loop until the kernel thread is requested to stop. + * It obtains the current time and scans through the dhcp_snooping_list, + * removing and freeing any entries whose expiration time has passed. + * The list is locked during the traversal to ensure thread safety. + * The thread sleeps for 1 second between iterations to reduce CPU usage. + * + * Return: Always returns 0. + */ int dhcp_thread_handler(void *arg) { struct list_head* curr, *next; struct dhcp_snooping_entry* entry; @@ -116,7 +165,19 @@ int dhcp_thread_handler(void *arg) { return 0; } - +/** + * dhcp_is_valid - Validate DHCP packet consistency. + * @skb: Pointer to the socket buffer containing the DHCP packet. + * + * This function checks if the DHCP packet meets certain validity criteria: + * - For DHCP Discover and Request messages, it verifies that the client hardware address (chaddr) + * in the DHCP payload matches the source MAC address in the Ethernet header. + * - It also checks that the gateway IP address (giaddr) is zero. + * + * Returns 0 (SUCCESS) if valid, or a negative error code if validation fails: + * - -EHWADDR if MAC addresses do not match. + * - -EIPADDR if giaddr is non-zero. + */ int dhcp_is_valid(struct sk_buff* skb) { int status = SUCCESS; struct udphdr* udp; From 257b0df62e9e285ab074d08d4b95f06f1c8f6208 Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 14:46:50 -0700 Subject: [PATCH 5/7] Refactor and add Comments for Module Parameters Seperate the module parameters into a sperate file to help wiht readability. Comments have also been added to each module parameter funciton to help wit understanding its functionality Signed-off-by: Korel --- include/common.h | 1 + include/module_params.h | 12 +++ src/Makefile | 2 +- src/main.c | 152 +---------------------------- src/module_params.c | 207 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 222 insertions(+), 152 deletions(-) create mode 100644 include/module_params.h create mode 100644 src/module_params.c diff --git a/include/common.h b/include/common.h index 78dafe2..6102cd5 100644 --- a/include/common.h +++ b/include/common.h @@ -21,6 +21,7 @@ #include #include #include +#include #endif diff --git a/include/module_params.h b/include/module_params.h new file mode 100644 index 0000000..2549b46 --- /dev/null +++ b/include/module_params.h @@ -0,0 +1,12 @@ +#ifndef MODULE_PARAMS_H +#define MODULE_PARAMS_H + +#include "common.h" + +// Module parameter variables +extern bool globally_enabled_DAI; // Enable DAI inspection for all packets under a single VLAN +extern bool static_ACL_Enabled; // DAI inspection uses static ACLs only +extern char *vlans_to_inspect; // Comma-separated VLAN list for DAI inspection +extern char *trusted_interfaces; // Comma-separated trusted Interface:VLAN_ID pairs + +#endif \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index bcba7a9..a2731f2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ obj-m := ${MODULE}.o -${MODULE}-objs := main.o dhcp.o trusted_interfaces.o rate_limit.o vlan.o +${MODULE}-objs := main.o dhcp.o trusted_interfaces.o rate_limit.o vlan.o module_params.o ccflags-y := -I$(PWD)/include -Wall -Werror diff --git a/src/main.c b/src/main.c index 66ea72d..4ff9327 100644 --- a/src/main.c +++ b/src/main.c @@ -4,159 +4,9 @@ #include "rate_limit.h" #include "vlan.h" #include "errno.h" +#include "module_params.h" #include "common.h" - -bool globally_enabled_DAI = false; //Default is false -//module_param(globally_enabled_DAI, bool, 0644); -MODULE_PARM_DESC(globally_enabled_DAI, "Enable or disable DAI Inspection for all Packets. All packets will be assumed to be in the same VLAN."); -static int set_globally_enabled_DAI(const char *val, const struct kernel_param *kp) -{ - bool tmp; - // Convert the input string (e.g., "1" or "true") to a boolean (0 or 1) - int ret = kstrtobool(val, &tmp); - if (ret < 0) { - printk(KERN_INFO "kdai: globally_enabled_DAI was NOT updated, input was invalid\n\n"); - return ret; - } - - // Log the old value before updating and the new value after - printk(KERN_INFO "kdai: globally_enabled_DAI updated from %d to %d\n\n", globally_enabled_DAI, tmp); - - globally_enabled_DAI = tmp; - - return 0; -} -static const struct kernel_param_ops globally_enabled_DAI_ops = { - .set = set_globally_enabled_DAI, //This funciton will be called whenever the static_ACL_Enabled variable is written too - .get = param_get_bool, //This function will be called whenever the static_ACL_Enabled variable is read -}; -module_param_cb(globally_enabled_DAI, &globally_enabled_DAI_ops, &globally_enabled_DAI, 0644); - - -bool static_ACL_Enabled = false; //Default is false -//module_param(static_ACL_Enabled, bool, 0644); -MODULE_PARM_DESC(static_ACL_Enabled, "Enable or disable DAI Inspection using static ACLs ONLY. Static Entries for packets not found in the ARP table will be dropped."); -static int set_static_acl(const char *val, const struct kernel_param *kp) -{ - bool tmp; - // Convert the input string (e.g., "1" or "true") to a boolean (0 or 1) - int ret = kstrtobool(val, &tmp); - if (ret < 0) { - printk(KERN_INFO "kdai: static_ACL_Enabled was NOT updated, input was invalid\n\n"); - return ret; - } - - // Log a message to the kernel log to confirm the update - printk(KERN_INFO "kdai: static_ACL_Enabled updated from %d to %d\n\n", static_ACL_Enabled, tmp); - - static_ACL_Enabled = tmp; - return 0; -} -static const struct kernel_param_ops static_acl_ops = { - .set = set_static_acl, //This funciton will be called whenever the static_ACL_Enabled variable is written too - .get = param_get_bool, //This function will be called whenever the static_ACL_Enabled variable is read -}; -module_param_cb(static_ACL_Enabled, &static_acl_ops, &static_ACL_Enabled, 0644); - - -char * vlans_to_inspect = NULL; //Default is None -//module_param(vlans_to_inspect, charp, 0644); -MODULE_PARM_DESC(vlans_to_inspect, "Comma-separated list of VLANs DAI should inspect"); -static int set_vlans_to_inspect(const char *val, const struct kernel_param *kp){ - char *to_free; // Declare to_free for duplicating the string - char *str; - - // If the input string is empty, just return - if (val == NULL) { - printk(KERN_INFO "kdai: No VLANs to inspect (empty input).\n\n"); - return 0; - } - if (strcmp(val,"clear") == 0) { - printk(KERN_INFO "kdai: Clearing VLANs To Inspect list\n\n"); - free_all_vlan_entries(); - print_all_vlans_in_hash(); - return 0; - } - - // Parse the incoming string of VLANs - to_free = kstrdup(val, GFP_KERNEL); - if (!to_free) { - printk(KERN_INFO "kdai: Could not dup\n\n"); - return -ENOMEM; // Memory allocation failed - } - str = to_free; - - //Remove all VLAN_ID entries from the list - printk(KERN_INFO "kdai: Clearing VLANs To Inspect list\n\n"); - free_all_vlan_entries(); - - //Add all entries that are specified in new val - printk(KERN_INFO "kdai: Parsing VLANs To Inspect\n\n"); - parse_vlans(to_free); - - //Free allocate dmmemory - kfree(to_free); - - printk(KERN_INFO "kdai: VLANs to inspect updated.\n\n"); - print_all_vlans_in_hash(); - return 0; -} -static const struct kernel_param_ops vlans_to_inspect_ops = { - .set = set_vlans_to_inspect, //This funciton will be called whenever the static_ACL_Enabled variable is written too - .get = param_get_charp, //This function will be called whenever the static_ACL_Enabled variable is read -}; -module_param_cb(vlans_to_inspect, &vlans_to_inspect_ops, &vlans_to_inspect, 0644); - - -char * trusted_interfaces; //Default is None -//module_param(trusted_interfaces, charp, 0644); -MODULE_PARM_DESC(trusted_interfaces, "Comma-separated list of Interfaces:VLAN_ID that are considered to be trusted"); -static int set_trusted_interfaces(const char *val, const struct kernel_param *kp){ - char *to_free; // Declare to_free for duplicating the string - char *str; - - printk(KERN_INFO "kdai: Changed Trusted Interface List\n"); - // If the input string is empty, just return - if (val == NULL) { - printk(KERN_INFO "kdai: Empty input for Trusted Interfaces.\n\n"); - return 0; - } - if(strcmp(val,"clear") == 0) { - printk(KERN_INFO "kdai: Clearing Trusted list\n\n"); - free_trusted_interface_list(); - print_trusted_interface_list(); - return 0; - } - - // Parse the incoming string of VLANs - to_free = kstrdup(val, GFP_KERNEL); - if (!to_free) { - printk(KERN_INFO "kdai: Could not dup\n\n"); - return -ENOMEM; // Memory allocation failed - } - str = to_free; - - //Remove all trusted entries from the list - free_trusted_interface_list(); - - //Add all entries that are specified in new val - parse_interfaces_and_vlan(to_free); - - //Free allocate dmmemory - kfree(to_free); - - printk(KERN_INFO "kdai: Trusted Interfaces Updated.\n\n"); - print_trusted_interface_list(); - return 0; -} -static const struct kernel_param_ops trusted_interfaces_ops = { - .set = set_trusted_interfaces, //This funciton will be called whenever the static_ACL_Enabled variable is written too - .get = param_get_charp, //This function will be called whenever the static_ACL_Enabled variable is read -}; -module_param_cb(trusted_interfaces, &trusted_interfaces_ops, &trusted_interfaces, 0644); - - MODULE_LICENSE("GPL"); MODULE_AUTHOR("M. Sami GURPINAR . Edited by Korel Ucpinar "); MODULE_DESCRIPTION("kdai(Kernel Dynamic ARP Inspection) is a linux kernel module to defend against arp spoofing"); diff --git a/src/module_params.c b/src/module_params.c new file mode 100644 index 0000000..8b6c376 --- /dev/null +++ b/src/module_params.c @@ -0,0 +1,207 @@ +#include "dhcp.h" +#include "trusted_interfaces.h" +#include "rate_limit.h" +#include "vlan.h" +#include "errno.h" +#include "module_params.h" + +//The following are the avilable Kernel Module Parameters + +bool globally_enabled_DAI = false; //Default is false; When true Enable DAI inspection for all packets under a single VLAN +bool static_ACL_Enabled = false; //Default is false; When true DAI inspection uses static ACLs only, DHCP snooping is nNOT considered +char * vlans_to_inspect = NULL; //Default is None; Comma-separated list of VLANs for DAI inspection +char * trusted_interfaces = NULL; //Default is None; Comma-separated list of trusted Interfaces:VLAN_ID pairs + +/** + * set_globally_enabled_DAI - Set global DAI inspection enable flag + * @val: Input string representing a boolean value ("0", "1", "true", or "false") + * @kp: Pointer to the kernel_param structure (unused) + * + * Updates the global flag globally_enabled_DAI based on the input value. + * This flag enables or disables DAI inspection for all packets, assuming + * all packets belong to the same VLAN. + * + * If the input is invalid, logs an error and returns a negative error code. + * + * Return: 0 on success, negative error code on failure. + */ +static int set_globally_enabled_DAI(const char *val, const struct kernel_param *kp) +{ + bool tmp; + // Convert the input string (e.g., "1" or "true") to a boolean (0 or 1) + int ret = kstrtobool(val, &tmp); + if (ret < 0) { + printk(KERN_INFO "kdai: globally_enabled_DAI was NOT updated, input was invalid\n\n"); + return ret; + } + + // Log the old value before updating and the new value after + printk(KERN_INFO "kdai: globally_enabled_DAI updated from %d to %d\n\n", globally_enabled_DAI, tmp); + + globally_enabled_DAI = tmp; + + return 0; +} +static const struct kernel_param_ops globally_enabled_DAI_ops = { + .set = set_globally_enabled_DAI, //This funciton will be called whenever the static_ACL_Enabled variable is written too + .get = param_get_bool, //This function will be called whenever the static_ACL_Enabled variable is read +}; +module_param_cb(globally_enabled_DAI, &globally_enabled_DAI_ops, &globally_enabled_DAI, 0644); +MODULE_PARM_DESC(globally_enabled_DAI, "Enable or disable DAI Inspection for all Packets. All packets will be assumed to be in the same VLAN."); + +/** + * set_static_acl - Set the static ACL inspection mode + * @val: Input string representing a boolean value ("0", "1", "true", or "false") + * @kp: Pointer to the kernel_param structure (unused) + * + * Updates the global static_ACL_Enabled flag based on the input value. + * If the input is invalid (i.e., not a valid boolean), the function logs + * an error and returns an appropriate error code. + * + * This controls whether DAI (Dynamic ARP Inspection) falls back to static + * ACLs for packets not found in the ARP table. + * + * Return: 0 on success, negative error code on failure (e.g. -EINVAL). + */ +static int set_static_acl(const char *val, const struct kernel_param *kp) +{ + bool tmp; + // Convert the input string (e.g., "1" or "true") to a boolean (0 or 1) + int ret = kstrtobool(val, &tmp); + if (ret < 0) { + printk(KERN_INFO "kdai: static_ACL_Enabled was NOT updated, input was invalid\n\n"); + return ret; + } + + // Log a message to the kernel log to confirm the update + printk(KERN_INFO "kdai: static_ACL_Enabled updated from %d to %d\n\n", static_ACL_Enabled, tmp); + + static_ACL_Enabled = tmp; + return 0; +} +static const struct kernel_param_ops static_acl_ops = { + .set = set_static_acl, //This funciton will be called whenever the static_ACL_Enabled variable is written too + .get = param_get_bool, //This function will be called whenever the static_ACL_Enabled variable is read +}; +module_param_cb(static_ACL_Enabled, &static_acl_ops, &static_ACL_Enabled, 0644); +MODULE_PARM_DESC(static_ACL_Enabled, "Enable or disable DAI Inspection using static ACLs ONLY. Static Entries for packets not found in the ARP table will be dropped."); + +/** + * set_vlans_to_inspect - Set the VLANs to inspect via module parameter + * @val: Input string with comma-separated VLAN IDs or special "clear" string + * @kp: Pointer to kernel_param structure (unused) + * + * Called when the `vlans_to_inspect` module parameter is written. Accepts a + * comma-separated list of VLAN IDs (e.g., "100,200,300") or the special string + * "clear", which removes all entries from the inspection list. + * + * On valid input, clears the current list and adds the new VLANs by calling + * parse_vlans(). Memory allocation is performed for parsing, and a failure to + * allocate memory will prevent changes. + * + * Return: 0 on success, -ENOMEM if memory allocation fails. + */ +static int set_vlans_to_inspect(const char *val, const struct kernel_param *kp){ + char *to_free; // Declare to_free for duplicating the string + char *str; + + // If the input string is empty, just return + if (val == NULL) { + printk(KERN_INFO "kdai: No VLANs to inspect (empty input).\n\n"); + return 0; + } + if (strcmp(val,"clear") == 0) { + printk(KERN_INFO "kdai: Clearing VLANs To Inspect list\n\n"); + free_all_vlan_entries(); + print_all_vlans_in_hash(); + return 0; + } + + // Parse the incoming string of VLANs + to_free = kstrdup(val, GFP_KERNEL); + if (!to_free) { + printk(KERN_INFO "kdai: Could not dup\n\n"); + return -ENOMEM; // Memory allocation failed + } + str = to_free; + + //Remove all VLAN_ID entries from the list + printk(KERN_INFO "kdai: Clearing VLANs To Inspect list\n\n"); + free_all_vlan_entries(); + + //Add all entries that are specified in new val + printk(KERN_INFO "kdai: Parsing VLANs To Inspect\n\n"); + parse_vlans(to_free); + + //Free allocate dmmemory + kfree(to_free); + + printk(KERN_INFO "kdai: VLANs to inspect updated.\n\n"); + print_all_vlans_in_hash(); + return 0; +} +static const struct kernel_param_ops vlans_to_inspect_ops = { + .set = set_vlans_to_inspect, //This funciton will be called whenever the static_ACL_Enabled variable is written too + .get = param_get_charp, //This function will be called whenever the static_ACL_Enabled variable is read +}; +module_param_cb(vlans_to_inspect, &vlans_to_inspect_ops, &vlans_to_inspect, 0644); +MODULE_PARM_DESC(vlans_to_inspect, "Comma-separated list of VLANs DAI should inspect"); + +/** + * set_trusted_interfaces - Update the list of trusted interfaces from a module parameter + * @val: Input string containing comma-separated interface:VLAN_ID pairs + * @kp: Pointer to kernel_param structure (unused) + * + * This function is called when the `trusted_interfaces` module parameter is written. + * It parses a comma-separated string of interface:VLAN_ID entries, such as: + * "eth0:10,eth1:20", and updates the trusted interface list accordingly. + * + * If the special string "clear" is passed, the trusted list is cleared. + * If the string is NULL or empty, the function exits with no changes. + * + * Return: 0 on success, -ENOMEM if memory allocation fails. + */ +static int set_trusted_interfaces(const char *val, const struct kernel_param *kp){ + char *to_free; // Declare to_free for duplicating the string + char *str; + + printk(KERN_INFO "kdai: Changed Trusted Interface List\n"); + // If the input string is empty, just return + if (val == NULL) { + printk(KERN_INFO "kdai: Empty input for Trusted Interfaces.\n\n"); + return 0; + } + if(strcmp(val,"clear") == 0) { + printk(KERN_INFO "kdai: Clearing Trusted list\n\n"); + free_trusted_interface_list(); + print_trusted_interface_list(); + return 0; + } + + // Parse the incoming string of VLANs + to_free = kstrdup(val, GFP_KERNEL); + if (!to_free) { + printk(KERN_INFO "kdai: Could not dup\n\n"); + return -ENOMEM; // Memory allocation failed + } + str = to_free; + + //Remove all trusted entries from the list + free_trusted_interface_list(); + + //Add all entries that are specified in new val + parse_interfaces_and_vlan(to_free); + + //Free allocate dmmemory + kfree(to_free); + + printk(KERN_INFO "kdai: Trusted Interfaces Updated.\n\n"); + print_trusted_interface_list(); + return 0; +} +static const struct kernel_param_ops trusted_interfaces_ops = { + .set = set_trusted_interfaces, //This funciton will be called whenever the static_ACL_Enabled variable is written too + .get = param_get_charp, //This function will be called whenever the static_ACL_Enabled variable is read +}; +module_param_cb(trusted_interfaces, &trusted_interfaces_ops, &trusted_interfaces, 0644); +MODULE_PARM_DESC(trusted_interfaces, "Comma-separated list of Interfaces:VLAN_ID that are considered to be trusted"); \ No newline at end of file From 6ca970620a6b8f64f5fad74c25789c349aefa578 Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 15:08:40 -0700 Subject: [PATCH 6/7] Refactor: Update hook Name Update the hook and associated function name to more accurately represent their funciton. Signed-off-by: Korel --- src/main.c | 76 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/main.c b/src/main.c index 4ff9327..c7dac91 100644 --- a/src/main.c +++ b/src/main.c @@ -12,10 +12,12 @@ MODULE_AUTHOR("M. Sami GURPINAR . Edited by Korel Ucpin MODULE_DESCRIPTION("kdai(Kernel Dynamic ARP Inspection) is a linux kernel module to defend against arp spoofing"); MODULE_VERSION("0.1"); +//A Macro used to check if an Ethernet address (addr) is a broadcast address #define eth_is_bcast(addr) (((addr)[0] & 0xffff) && ((addr)[2] & 0xffff) && ((addr)[4] & 0xffff)) -static struct nf_hook_ops* ipho = NULL; -static struct nf_hook_ops* brho = NULL; +//Two Netfilter Hooks used to capture incoming packets and check if they are either a dhcp or arp packet +static struct nf_hook_ops* bridge_dhcp_hook = NULL; +static struct nf_hook_ops* bridge_arp_hook = NULL; static int arp_is_valid(struct sk_buff* skb, u16 ar_op, unsigned char* sha, u32 sip, unsigned char* tha, u32 tip) { @@ -200,7 +202,7 @@ static bool is_trusted(const char *interface_name, u16 vlan_id) { } } -static unsigned int bridge_hook(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { +static unsigned int arp_hook_handler(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { struct net_device *dev; struct ethhdr * eth; @@ -302,7 +304,7 @@ static unsigned int bridge_hook(void* priv, struct sk_buff* skb, const struct nf } } -static unsigned int ip_hook(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { +static unsigned int dhcp_hook_handler(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { struct udphdr* udp; struct dhcp* payload; unsigned char* opt; @@ -453,35 +455,19 @@ static int __init kdai_init(void) { printk(KERN_INFO "kdai: static_ACL_Enabled=%d\n\n", static_ACL_Enabled); print_trusted_interface_list(); print_all_vlans_in_hash(); - - /*Initialize Generic Hook for rate limiting all Bridged Traffic*/ - brho = (struct nf_hook_ops *) kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL); - if (unlikely(!brho)) - goto err; - - brho->hook = (nf_hookfn *) bridge_hook; - brho->hooknum = NF_BR_PRE_ROUTING; - brho->pf = NFPROTO_BRIDGE; - brho->priority = NF_BR_PRI_FIRST; - #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - nf_register_net_hook(&init_net, brho); - #else - nf_register_hook(brho); - #endif - - /* Initialize ip netfilter hook */ - ipho = (struct nf_hook_ops *) kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL); - if (unlikely(!ipho)) + + bridge_dhcp_hook = (struct nf_hook_ops *) kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL); + if (unlikely(!bridge_dhcp_hook)) goto err; - ipho->hook = (nf_hookfn *) ip_hook; /* hook function */ - ipho->hooknum = NF_BR_PRE_ROUTING; /* received packets */ - ipho->pf = NFPROTO_BRIDGE; /* IP */ - ipho->priority = NF_BR_PRI_FIRST; + bridge_dhcp_hook->hook = (nf_hookfn *) dhcp_hook_handler; + bridge_dhcp_hook->hooknum = NF_BR_PRE_ROUTING; + bridge_dhcp_hook->pf = NFPROTO_BRIDGE; + bridge_dhcp_hook->priority = NF_BR_PRI_FIRST; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - nf_register_net_hook(&init_net, ipho); + nf_register_net_hook(&init_net, bridge_dhcp_hook); #else - nf_register_hook(ipho); + nf_register_hook(bridge_dhcp_hook); #endif dhcp_thread = kthread_run(dhcp_thread_handler, NULL, "DHCP Thread"); @@ -491,10 +477,26 @@ static int __init kdai_init(void) { printk(KERN_INFO"kdai: Cannot create kthread\n"); goto err; } + + bridge_arp_hook = (struct nf_hook_ops *) kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL); + if (unlikely(!bridge_arp_hook)) + goto err; + + bridge_arp_hook->hook = (nf_hookfn *) arp_hook_handler; + bridge_arp_hook->hooknum = NF_BR_PRE_ROUTING; + bridge_arp_hook->pf = NFPROTO_BRIDGE; + bridge_arp_hook->priority = NF_BR_PRI_FIRST; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) + nf_register_net_hook(&init_net, bridge_arp_hook); + #else + nf_register_hook(bridge_arp_hook); + #endif + return 0; /* success */ + err: - if (ipho) kfree(ipho); - if(brho) kfree(brho); + if (bridge_dhcp_hook) kfree(bridge_dhcp_hook); + if(bridge_arp_hook) kfree(bridge_arp_hook); return -ENOMEM; } @@ -502,18 +504,18 @@ static int __init kdai_init(void) { static void __exit kdai_exit(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - nf_unregister_net_hook(&init_net, brho); + nf_unregister_net_hook(&init_net, bridge_arp_hook); #else - nf_unregister_hook(brho); + nf_unregister_hook(bridge_arp_hook); #endif - kfree(brho); + kfree(bridge_arp_hook); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) - nf_unregister_net_hook(&init_net, ipho); + nf_unregister_net_hook(&init_net, bridge_dhcp_hook); #else - nf_unregister_hook(ipho); + nf_unregister_hook(bridge_dhcp_hook); #endif - kfree(ipho); + kfree(bridge_dhcp_hook); clean_dhcp_snooping_table(); kthread_stop(dhcp_thread); From f154de02729b615787192c004a41828a36a29c2d Mon Sep 17 00:00:00 2001 From: Korel Date: Sun, 15 Jun 2025 15:19:14 -0700 Subject: [PATCH 7/7] Refactor: Comments for main.c Refactor comments and add comments for each function in main.c file such that they adhere to the Linux Kernel style comment block Signed-off-by: Korel --- src/main.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index c7dac91..0e57803 100644 --- a/src/main.c +++ b/src/main.c @@ -10,7 +10,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("M. Sami GURPINAR . Edited by Korel Ucpinar "); MODULE_DESCRIPTION("kdai(Kernel Dynamic ARP Inspection) is a linux kernel module to defend against arp spoofing"); -MODULE_VERSION("0.1"); +MODULE_VERSION("1.0"); //A Macro used to check if an Ethernet address (addr) is a broadcast address #define eth_is_bcast(addr) (((addr)[0] & 0xffff) && ((addr)[2] & 0xffff) && ((addr)[4] & 0xffff)) @@ -19,6 +19,24 @@ MODULE_VERSION("0.1"); static struct nf_hook_ops* bridge_dhcp_hook = NULL; static struct nf_hook_ops* bridge_arp_hook = NULL; +/** + * arp_is_valid - Validate ARP packet fields for consistency and correctness + * @skb: Pointer to the socket buffer containing the packet + * @ar_op: ARP operation code (e.g., ARP request or reply) + * @sha: Sender hardware (MAC) address from the ARP message body + * @sip: Sender IP address from the ARP message body + * @tha: Target hardware (MAC) address from the ARP message body + * @tip: Target IP address from the ARP message body + * + * This function performs sanity checks on the ARP packet fields to ensure + * the packet is well-formed and not malformed. It can validate: + * - That the sender MAC address in the ARP message matches the Ethernet header source MAC + * - That sender and target IP addresses are not multicast, loopback, or zero network addresses + * - That for ARP replies, the target MAC address in the ARP message matches the Ethernet destination MAC + * + * Return: SUCCESS (0) if all validations pass, + * or negative error codes indicating specific validation failures. + */ static int arp_is_valid(struct sk_buff* skb, u16 ar_op, unsigned char* sha, u32 sip, unsigned char* tha, u32 tip) { int status = SUCCESS; @@ -76,6 +94,28 @@ static int arp_is_valid(struct sk_buff* skb, u16 ar_op, unsigned char* sha, return status; } +/** + * validate_arp_request - Validate incoming ARP requests for security + * @priv: Private data pointer + * @skb: Pointer to the socket buffer containing the packet + * @state: Netfilter hook state info + * @vlan_id: VLAN ID on which the packet was received + * + * This function processes ARP packets hooked at the bridge pre-routing stage. + * It validates the ARP message by checking: + * - ARP header fields (using arp_is_valid) + * - Whether the ARP source IP and MAC match known entries in the ARP table + * - If static ACL mode is enabled, drops packets not matching static entries + * - Otherwise, validates the ARP source against the DHCP snooping table + * + * If validation fails at any stage, the packet is dropped. + * Packets matching all validation steps are accepted. + * Special debug bypass for interface "enp0s7". + * + * Return: + * NF_ACCEPT to allow packet processing to continue + * NF_DROP to drop the packet due to validation failure + */ static unsigned int validate_arp_request(void* priv, struct sk_buff* skb, const struct nf_hook_state* state, u16 vlan_id) { //Refrence Structure to Standard ARP header used in the linux Kernel @@ -191,6 +231,14 @@ static unsigned int validate_arp_request(void* priv, struct sk_buff* skb, const } +/** + * is_trusted - check if interface and VLAN combination is trusted + * @interface_name: interface name string + * @vlan_id: VLAN ID + * + * Returns true if the given interface and VLAN ID are in the trusted list, + * false otherwise. + */ static bool is_trusted(const char *interface_name, u16 vlan_id) { // Check if the device is trusted using the find_trusted_interface function if (find_trusted_interface(interface_name, vlan_id)) { @@ -202,6 +250,19 @@ static bool is_trusted(const char *interface_name, u16 vlan_id) { } } +/** + * arp_hook_handler - netfilter hook for processing ARP packets + * @priv: private data pointer + * @skb: socket buffer containing the packet + * @state: netfilter hook state information + * + * This function inspects incoming ARP packets on network interfaces, + * applying Dynamic ARP Inspection (DAI) rules depending on VLAN, + * global enablement, interface trust, and rate limits. This funciton relies on + * validate_arp_request. + * + * Returns NF_ACCEPT to accept the packet or NF_DROP to drop it. + */ static unsigned int arp_hook_handler(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { struct net_device *dev; @@ -304,6 +365,18 @@ static unsigned int arp_hook_handler(void* priv, struct sk_buff* skb, const stru } } +/** + * dhcp_hook_handler - netfilter hook for processing DHCP packets + * @priv: private data pointer + * @skb: socket buffer containing the packet + * @state: netfilter hook state information + * + * Processes DHCP packets on the network, updating the DHCP snooping + * table for lease times, IP-MAC bindings, and handling DHCPACK, DHCPNAK, + * DHCPRELEASE, and DHCPDECLINE messages. Drops invalid DHCP packets. + * + * Returns NF_ACCEPT to accept the packet or NF_DROP to drop it. + */ static unsigned int dhcp_hook_handler(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { struct udphdr* udp; struct dhcp* payload; @@ -443,7 +516,15 @@ static unsigned int dhcp_hook_handler(void* priv, struct sk_buff* skb, const str } - +/** + * kdai_init - Module initialization function + * + * Initializes data structures, parses configuration parameters, + * registers netfilter hooks for DHCP and ARP, and starts the DHCP + * cleanup kernel thread. + * + * Returns 0 on success or -ENOMEM on failure. + */ static int __init kdai_init(void) { init_vlan_hash_table(); @@ -500,7 +581,12 @@ static int __init kdai_init(void) { return -ENOMEM; } - +/** + * kdai_exit - Module cleanup function + * + * Unregisters netfilter hooks, stops DHCP cleanup thread, + * and frees all allocated resources. + */ static void __exit kdai_exit(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)