├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── mfw.c ├── mfw.h └── mfw_module.c /.gitignore: -------------------------------------------------------------------------------- 1 | mfw 2 | *~ 3 | *.o* 4 | *.ko* 5 | *.mod* 6 | modules.order 7 | Module.symvers -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sucha Supittayapornpong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | obj-m += mfw_module.o 2 | 3 | oall: mfw mfwmod 4 | 5 | mfw: mfw.c mfw.h 6 | gcc -Wall -o mfw mfw.c 7 | 8 | mfwmod: 9 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 10 | 11 | clean: 12 | rm -f mfw mfw_file 13 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniFirewall 2 | ## Introduction 3 | MiniFirewall is a simple Firewall system for Linux operating system. It employs an exact-match algorithm to filter TCP and UDP packets according to user-specified rules. 4 | 5 | MiniFirewall is composed of a user-space program `mfw` and a kernel-space module `mfw_module`. Communications between user space and kernel space go through a (charactor) device file `mfw_file`. Using `mfw` command, a user can specify filtering rules, which consist of some of the following fields: 6 | * Direction: inbound, outbound 7 | * Source: IP address, subnet mask, port number 8 | * Destination: IP address, subnet mask, port number 9 | * Protocol number: TCP(6), UDP(17) 10 | 11 | Each created rule is sent to and stored in `mfw_module` module. The module utilizes 'netfilter' to compare every packets with user-specified rules. When packet's fields matches one of the rules, the packat is dropped (filtered). 12 | 13 | ## Installation 14 | The user-space program `mfw` and the kernel-space module `mfw_module` can be compiled by executing: 15 | ``` 16 | $ make 17 | ``` 18 | A (charactor) device file must be created as an interface between the user-space program and the kernel module. A default device number is `100`, so a device file `mfw_file` can be created as follows: 19 | ``` 20 | $ mknod mfw_file c 100 0 21 | ``` 22 | 23 | ## Usage 24 | ### Insert kernel module 25 | The kernel module `mfw_module` must be inserted into the kernel of a Linux operating system before running the user-space program `mfw`. This can be done by the insert module command: 26 | ``` 27 | $ insmod ./mfw_module.ko 28 | ``` 29 | ### Add / Remove / View rules 30 | A user can add, remove, and view rules by executing `mfw` command as the following examples. 31 | 32 | To add a rule that blocks all inbound TCP and UDP packets with port number 55555: 33 | ``` 34 | $ ./mfw --add --in --d_port 55555 35 | ``` 36 | 37 | To view all configured rules: 38 | ``` 39 | $ ./mfw --view 40 | ``` 41 | 42 | To remove the above rule: 43 | ``` 44 | $ ./mfw --remove --in --d_port 55555 45 | ``` 46 | 47 | Additional usage information can be shown by executing: 48 | ``` 49 | $ ./mfw --help 50 | ``` 51 | 52 | ## References 53 | I personally implemented this project to learn Linux kernel programming, device files, kernel interfaces, and netfilter. The project is inspired by several online material relating to Linux kernel programming, ioctl, netfilter, firewall projects as listed below. 54 | * [The Linux Kernel Module Programming Guide](http://tldp.org/LDP/lkmpg/2.6/html/) 55 | * [Sysfs, Procfs, Sysctl, Debugfs And Other Similar Kernel Interfaces](https://johnsofteng.wordpress.com/2013/11/20/sysfs-procfs-sysctl-debugfs-and-other-similar-kernel-interfaces/) 56 | * [The netfilter.org project](https://www.netfilter.org/) 57 | * [A Deep Dive into Iptables and Netfilter Architecture](https://www.digitalocean.com/community/tutorials/a-deep-dive-into-iptables-and-netfilter-architecture) 58 | * [How to Write a Linux Firewall in Less than 1000 Lines of Code](http://www.roman10.net/2011/07/23/a-linux-firewall-using-netfilter-part-1overview/) 59 | * [Linux Firewall Lab](http://www.cis.syr.edu/~wedu/seed/Labs/Firewall_Linux/) 60 | 61 | -------------------------------------------------------------------------------- /mfw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "mfw.h" 10 | 11 | #define PORT_NUM_MAX USHRT_MAX 12 | 13 | 14 | /* 15 | * The function prints usage and parameters. 16 | */ 17 | static void 18 | print_usage(void) 19 | { 20 | printf("Usage: mf RULE_OPTIONS..\n" 21 | "MiniFirewall implements an exact match algorithm, where " 22 | "unspecified options are ignored.\n" 23 | "-i --in input\n" 24 | "-o --out output\n" 25 | "-s --s_ip IPADDR source ip address\n" 26 | "-m --s_mask MASK source mask\n" 27 | "-p --s_port PORT source port\n" 28 | "-d --d_ip IPADDR destination ip address\n" 29 | "-n --d_mask MASK destination mask\n" 30 | "-q --d_port PORT destination port\n" 31 | "-c --proto PROTO protocol\n" 32 | "-a --add add a rule\n" 33 | "-r --remove remove a rule\n" 34 | "-v --view view rules\n" 35 | "-h --help this usage\n"); 36 | } 37 | 38 | 39 | /* 40 | * The function sends a command to a MiniFirewall module via a device file. 41 | */ 42 | static void 43 | send_instruction(struct mfw_ctl *ctl) 44 | { 45 | FILE *fp; 46 | int byte_count; 47 | 48 | fp = fopen(DEVICE_INTF_NAME, "w"); 49 | if(fp == NULL) { 50 | printf("An device file (%s) cannot be opened.\n", 51 | DEVICE_INTF_NAME); 52 | return; 53 | } 54 | byte_count = fwrite(ctl, 1, sizeof(*ctl), fp); 55 | if(byte_count != sizeof(*ctl)) 56 | printf("Write process is incomplete. Please try again.\n"); 57 | 58 | fclose(fp); 59 | } 60 | 61 | 62 | /* 63 | * The function prints all existing rules, installed in the kernel module. 64 | */ 65 | static void 66 | view_rules(void) 67 | { 68 | FILE *fp; 69 | char *buffer; 70 | int byte_count; 71 | struct in_addr addr; 72 | struct mfw_rule *rule; 73 | 74 | fp = fopen(DEVICE_INTF_NAME, "r"); 75 | if(fp == NULL) { 76 | printf("An device file (%s) cannot be opened.\n", 77 | DEVICE_INTF_NAME); 78 | return; 79 | } 80 | 81 | buffer = (char *)malloc(sizeof(*rule)); 82 | if(buffer == NULL) { 83 | printf("Rule cannot be printed duel to insufficient memory\n"); 84 | return; 85 | } 86 | 87 | /* Each rule is printed line-by-line. */ 88 | printf("I/O " 89 | "S_Addr S_Mask S_Port " 90 | "D_Addr D_Mask D_Port Proto\n"); 91 | while((byte_count = fread(buffer, 1, sizeof(struct mfw_rule), fp)) > 0) { 92 | rule = (struct mfw_rule *)buffer; 93 | printf("%-3s ", rule->in ? "In" : "Out"); 94 | addr.s_addr = rule->s_ip; 95 | printf("%-15s ", inet_ntoa(addr)); 96 | addr.s_addr = rule->s_mask; 97 | printf("%-15s ", inet_ntoa(addr)); 98 | printf("%-5d ", ntohs(rule->s_port)); 99 | addr.s_addr = rule->d_ip; 100 | printf("%-15s ", inet_ntoa(addr)); 101 | addr.s_addr = rule->d_mask; 102 | printf("%-15s ", inet_ntoa(addr)); 103 | printf("%-5d ", ntohs(rule->d_port)); 104 | printf("%-3d\n", rule->proto); 105 | } 106 | free(buffer); 107 | fclose(fp); 108 | } 109 | 110 | 111 | /* 112 | * The function parses a string and checks its range. 113 | */ 114 | static int64_t 115 | parse_number(const char *str, uint32_t min_val, uint32_t max_val) 116 | { 117 | uint32_t num; 118 | char *end; 119 | 120 | num = strtol(str, &end, 10); 121 | if(end == str || (num > max_val) || (num < min_val)) 122 | return -1; 123 | 124 | return num; 125 | } 126 | 127 | 128 | /* 129 | * The function parses arguments (argv) to form a control instruction. 130 | */ 131 | static int 132 | parse_arguments(int argc, char **argv, struct mfw_ctl *ret_ctl) 133 | { 134 | int opt; 135 | int64_t lnum; 136 | int opt_index; 137 | struct mfw_ctl ctl = {}; 138 | struct in_addr addr; 139 | 140 | /* Long option configuration */ 141 | static struct option long_options[] = { 142 | {"in", no_argument, 0, 'i'}, 143 | {"out", no_argument, 0, 'o'}, 144 | {"s_ip", required_argument, 0, 's'}, 145 | {"s_mask", required_argument, 0, 'm'}, 146 | {"s_port", required_argument, 0, 'p'}, 147 | {"d_ip", required_argument, 0, 'd'}, 148 | {"d_mask", required_argument, 0, 'n'}, 149 | {"d_port", required_argument, 0, 'q'}, 150 | {"proto", required_argument, 0, 'c'}, 151 | {"add", no_argument, 0, 'a'}, 152 | {"remove", no_argument, 0, 'r'}, 153 | {"view", no_argument, 0, 'v'}, 154 | {"help", no_argument, 0, 'h'}, 155 | {0, 0, 0, 0} 156 | }; 157 | 158 | if(argc == 1) { 159 | print_usage(); 160 | return 0; 161 | } 162 | 163 | ctl.mode = MFW_NONE; 164 | ctl.rule.in = -1; 165 | while(1) { 166 | opt_index = 0; 167 | opt = getopt_long(argc, argv, "ios:m:p:d:n:q:c:arvh", 168 | long_options, &opt_index); 169 | if(opt == -1) { 170 | break; 171 | } 172 | 173 | switch(opt) { 174 | case 'i': /* Inbound rule */ 175 | if(ctl.rule.in == 0) { 176 | printf("Please select either In or Out\n"); 177 | return -1; 178 | } 179 | ctl.rule.in = 1; 180 | break; 181 | case 'o': /* Outbound rule */ 182 | if(ctl.rule.in == 1) { 183 | printf("Please select either In or Out\n"); 184 | return -1; 185 | } 186 | ctl.rule.in = 0; 187 | break; 188 | case 's': /* Source ip address */ 189 | if(inet_aton(optarg, &addr) == 0) { 190 | printf("Invalid source ip address\n"); 191 | return -1; 192 | } 193 | ctl.rule.s_ip = addr.s_addr; 194 | break; 195 | case 'm': /* Source subnet mask */ 196 | if(inet_aton(optarg, &addr) == 0) { 197 | printf("Invalid source subnet mask\n"); 198 | return -1; 199 | } 200 | ctl.rule.s_mask = addr.s_addr; 201 | break; 202 | case 'p': /* Source port number */ 203 | lnum = parse_number(optarg, 0, USHRT_MAX); 204 | if(lnum < 0) { 205 | printf("Invalid source port number\n"); 206 | return -1; 207 | } 208 | ctl.rule.s_port = htons((uint16_t)lnum); 209 | break; 210 | case 'd': /* Destination ip address */ 211 | if(inet_aton(optarg, &addr) == 0) { 212 | printf("Invalid destination ip address\n"); 213 | return -1; 214 | } 215 | ctl.rule.d_ip = addr.s_addr; 216 | break; 217 | case 'n': /* Destination subnet mask */ 218 | if(inet_aton(optarg, &addr) == 0) { 219 | printf("Invalid destination subnet mask\n"); 220 | return -1; 221 | } 222 | ctl.rule.d_mask = addr.s_addr; 223 | break; 224 | case 'q': /* Destination port number */ 225 | lnum = parse_number(optarg, 0, USHRT_MAX); 226 | if(lnum < 0) { 227 | printf("Invalid destination port number\n"); 228 | return -1; 229 | } 230 | ctl.rule.d_port = htons((uint16_t)lnum); 231 | break; 232 | case 'c': /* Protocol number */ 233 | lnum = parse_number(optarg, 0, UCHAR_MAX); 234 | if(lnum < 0 || 235 | !(lnum == 0 || 236 | lnum == IPPROTO_TCP || 237 | lnum == IPPROTO_UDP)) { 238 | printf("Invalid protocol number\n"); 239 | return -1; 240 | } 241 | ctl.rule.proto = (uint8_t)lnum; 242 | break; 243 | case 'a': /* Add rule */ 244 | if(ctl.mode != MFW_NONE) { 245 | printf("Only one mode can be selected.\n"); 246 | return -1; 247 | } 248 | ctl.mode = MFW_ADD; 249 | break; 250 | case 'r': /* Remove rule */ 251 | if(ctl.mode != MFW_NONE) { 252 | printf("Only one mode can be selected.\n"); 253 | return -1; 254 | } 255 | ctl.mode = MFW_REMOVE; 256 | break; 257 | case 'v': /* View rules */ 258 | if(ctl.mode != MFW_NONE) { 259 | printf("Only one mode can be selected.\n"); 260 | return -1; 261 | } 262 | ctl.mode = MFW_VIEW; 263 | break; 264 | case 'h': 265 | case '?': 266 | default: 267 | print_usage(); 268 | return -1; 269 | } 270 | } 271 | if(ctl.mode == MFW_NONE) { 272 | printf("Please specify mode --(add|remove|view)\n"); 273 | return -1; 274 | } 275 | if(ctl.mode != MFW_VIEW && ctl.rule.in == -1) { 276 | printf("Please specify either In or Out\n"); 277 | return -1; 278 | } 279 | 280 | *ret_ctl = ctl; 281 | return 0; 282 | } 283 | 284 | 285 | int 286 | main(int argc, char *argv[]) 287 | { 288 | struct mfw_ctl ctl = {}; 289 | int ret; 290 | 291 | ret = parse_arguments(argc, argv, &ctl); 292 | if(ret < 0) 293 | return ret; 294 | 295 | switch(ctl.mode) { 296 | case MFW_ADD: 297 | case MFW_REMOVE: 298 | send_instruction(&ctl); 299 | break; 300 | case MFW_VIEW: 301 | view_rules(); 302 | break; 303 | default: 304 | return 0; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /mfw.h: -------------------------------------------------------------------------------- 1 | #ifndef _MFW_H_ 2 | #define _MFW_H_ 3 | 4 | #include 5 | 6 | #define DEVICE_INTF_NAME "mfw_file" 7 | #define DEVICE_MAJOR_NUM 100 8 | 9 | 10 | /* Mode of an instruction */ 11 | enum mfw_mode { 12 | MFW_NONE = 0, 13 | MFW_ADD = 1, 14 | MFW_REMOVE = 2, 15 | MFW_VIEW = 3 16 | }; 17 | 18 | 19 | /* Filter rule of MiniFirewall */ 20 | struct mfw_rule { 21 | uint32_t in; 22 | uint32_t s_ip; 23 | uint32_t s_mask; 24 | uint16_t s_port; 25 | uint32_t d_ip; 26 | uint32_t d_mask; 27 | uint16_t d_port; 28 | uint8_t proto; 29 | }; 30 | 31 | 32 | /* Control instruction */ 33 | struct mfw_ctl { 34 | enum mfw_mode mode; 35 | struct mfw_rule rule; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /mfw_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "mfw.h" 16 | 17 | #define EQUAL_NET_ADDR(ip1, ip2, mask) (((ip1 ^ ip2) & mask) == 0) 18 | #define IGNORE(x) (x == 0) 19 | #define IP_POS(ip, i) (ip >> ((8*(3-i))) & 0xFF) 20 | 21 | MODULE_LICENSE("GPL"); 22 | MODULE_AUTHOR("Sucha Supittayapornpong "); 23 | 24 | 25 | /* List node containing a filter rule */ 26 | struct rule_node { 27 | struct mfw_rule rule; 28 | struct list_head list; 29 | }; 30 | 31 | struct list_head In_lhead; /* Head of inbound-rule list */ 32 | struct list_head Out_lhead; /* Head of outbound-rule list */ 33 | 34 | static int Device_open; /* Opening counter of a device file */ 35 | static char *Buffer; /* A buffer for receving data from a user space */ 36 | 37 | 38 | /* 39 | * General filter uses exact match algorithm based on the given rule list. 40 | */ 41 | static unsigned int 42 | mfw_general_filter(void *priv, struct sk_buff *skb, 43 | const struct nf_hook_state *state, 44 | struct list_head *rule_list_head) 45 | { 46 | struct list_head *listh; 47 | struct rule_node *node; 48 | struct mfw_rule *r; 49 | struct iphdr *iph; 50 | struct tcphdr *tcph; 51 | struct udphdr *udph; 52 | 53 | uint32_t s_ip; 54 | uint32_t d_ip; 55 | uint16_t s_port; 56 | uint16_t d_port; 57 | unsigned char proto; 58 | 59 | if(!skb || rule_list_head->next == rule_list_head) 60 | return NF_ACCEPT; 61 | 62 | /* Get IP header and extract information */ 63 | iph = (struct iphdr *)skb_network_header(skb); 64 | if(iph == NULL) 65 | return NF_ACCEPT; 66 | 67 | proto = iph->protocol; 68 | s_ip = iph->saddr; 69 | d_ip = iph->daddr; 70 | if(proto == IPPROTO_UDP) { 71 | udph = (struct udphdr *)(skb_transport_header(skb)); 72 | s_port = udph->source; 73 | d_port = udph->dest; 74 | } 75 | else if(proto == IPPROTO_TCP) { 76 | tcph = (struct tcphdr *)(skb_transport_header(skb)); 77 | s_port = tcph->source; 78 | d_port = tcph->dest; 79 | } 80 | else 81 | return NF_ACCEPT; 82 | 83 | /* Loop through the rule list and perform exact match */ 84 | listh = rule_list_head; 85 | list_for_each_entry(node, listh, list) { 86 | r = &node->rule; 87 | 88 | if(!IGNORE(r->proto) && (r->proto != iph->protocol)) 89 | continue; 90 | 91 | if(!IGNORE(r->s_ip) && !EQUAL_NET_ADDR(r->s_ip, s_ip, r->s_mask)) 92 | continue; 93 | 94 | if(!IGNORE(r->s_port) && (r->s_port != s_port)) 95 | continue; 96 | 97 | if(!IGNORE(r->d_ip) && !EQUAL_NET_ADDR(r->d_ip, d_ip, r->s_mask)) 98 | continue; 99 | 100 | if(!IGNORE(r->d_port) && (r->d_port != d_port)) 101 | continue; 102 | 103 | printk(KERN_INFO "MiniFirewall: Drop packet " 104 | "src %d.%d.%d.%d : %d dst %d.%d.%d.%d : %d proto %d\n", 105 | IP_POS(s_ip, 3), IP_POS(s_ip, 2), 106 | IP_POS(s_ip, 1), IP_POS(s_ip, 0), s_port, 107 | IP_POS(d_ip, 3), IP_POS(d_ip, 2), 108 | IP_POS(d_ip, 1), IP_POS(d_ip, 0), d_port, 109 | iph->protocol); 110 | 111 | return NF_DROP; 112 | } 113 | return NF_ACCEPT; 114 | } 115 | 116 | 117 | /* 118 | * Inbound filter is applied to all inbound packets. 119 | */ 120 | static unsigned int 121 | mfw_in_filter(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) 122 | { 123 | return mfw_general_filter(priv, skb, state, &In_lhead); 124 | } 125 | 126 | 127 | /* 128 | * Outbound filter is applied to all outbound packets. 129 | */ 130 | static unsigned int 131 | mfw_out_filter(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) 132 | { 133 | return mfw_general_filter(priv, skb, state, &Out_lhead); 134 | } 135 | 136 | 137 | /* 138 | * The function handles an open operation of a device file. 139 | */ 140 | static int 141 | mfw_dev_open(struct inode *inode, struct file *file) 142 | { 143 | if(Device_open) 144 | return -EBUSY; 145 | 146 | /* Increase value to enforce a signal access policy */ 147 | Device_open++; 148 | 149 | if(!try_module_get(THIS_MODULE)) { 150 | printk(KERN_ALERT "MiniFirewall: Module is not available\n"); 151 | return -ESRCH; 152 | } 153 | return 0; 154 | } 155 | 156 | 157 | /* 158 | * The function handles a release operation of a device file. 159 | */ 160 | static int 161 | mfw_dev_release(struct inode *inode, struct file *file) 162 | { 163 | module_put(THIS_MODULE); 164 | Device_open--; 165 | return 0; 166 | } 167 | 168 | 169 | /* 170 | * The function handles user-space view operation, which reads inbound and 171 | * outbound rules stored in the module. The function is called iteratively 172 | * until it returns 0. 173 | */ 174 | static ssize_t 175 | mfw_dev_read(struct file *file, char *buffer, size_t length, loff_t *offset) 176 | { 177 | int byte_read = 0; 178 | static struct list_head *inlp = &In_lhead; 179 | static struct list_head *outlp = &Out_lhead; 180 | struct rule_node *node; 181 | char *readptr; 182 | 183 | /* Read a rule if it is not the last one in the inbound list */ 184 | if(inlp->next != &In_lhead) { 185 | node = list_entry(inlp->next, struct rule_node, list); 186 | readptr = (char*)&node->rule; 187 | inlp = inlp->next; 188 | } 189 | /* Read a rule if it is not the last one in the outbound list */ 190 | else if(outlp->next != &Out_lhead) { 191 | node = list_entry(outlp->next, struct rule_node, list); 192 | readptr = (char*)&node->rule; 193 | outlp = outlp->next; 194 | } 195 | /* Reset reading pointers to heads of inbound and outbound lists */ 196 | else { 197 | inlp = &In_lhead; 198 | outlp = &Out_lhead; 199 | return 0; 200 | } 201 | 202 | /* Write to a user-space buffer */ 203 | while(length && (byte_read < sizeof(struct mfw_rule))) { 204 | put_user(readptr[byte_read], &(buffer[byte_read])); 205 | byte_read++; 206 | length--; 207 | } 208 | return byte_read; 209 | } 210 | 211 | 212 | /* 213 | * The function adds a rule to either an inbound list or an outbound list. 214 | */ 215 | static void 216 | mfw_rule_add(struct mfw_rule *rule) 217 | { 218 | struct rule_node *nodep; 219 | nodep = (struct rule_node *)kmalloc(sizeof(struct rule_node), GFP_KERNEL); 220 | if(nodep == NULL) { 221 | printk(KERN_ALERT "MiniFirewall: Cannot add a new rule due to " 222 | "insufficient memory\n"); 223 | return; 224 | } 225 | nodep->rule = *rule; 226 | 227 | if(nodep->rule.in == 1) { 228 | list_add_tail(&nodep->list, &In_lhead); 229 | printk(KERN_INFO "MiniFirewall: Add rule to the inbound list "); 230 | } 231 | else { 232 | list_add_tail(&nodep->list, &Out_lhead); 233 | printk(KERN_INFO "MiniFirewall: Add rule to the outbound list "); 234 | } 235 | printk(KERN_INFO 236 | "src %d.%d.%d.%d : %d dst %d.%d.%d.%d : %d proto %d\n", 237 | IP_POS(rule->s_ip, 3), IP_POS(rule->s_ip, 2), 238 | IP_POS(rule->s_ip, 1), IP_POS(rule->s_ip, 0), rule->s_port, 239 | IP_POS(rule->d_ip, 3), IP_POS(rule->d_ip, 2), 240 | IP_POS(rule->d_ip, 1), IP_POS(rule->d_ip, 0), rule->d_port, 241 | rule->proto); 242 | } 243 | 244 | 245 | /* 246 | * The function deletes a rule from inbound and outbound lists. 247 | */ 248 | static void 249 | mfw_rule_del(struct mfw_rule *rule) 250 | { 251 | struct rule_node *node; 252 | struct list_head *lheadp; 253 | struct list_head *lp; 254 | 255 | if(rule->in == 1) 256 | lheadp = &In_lhead; 257 | else 258 | lheadp = &Out_lhead; 259 | 260 | for(lp = lheadp; lp->next != lheadp; lp = lp->next) { 261 | node = list_entry(lp->next, struct rule_node, list); 262 | if(node->rule.in == rule->in && 263 | node->rule.s_ip == rule->s_ip && 264 | node->rule.s_mask == rule->s_mask && 265 | node->rule.s_port == rule->s_port && 266 | node->rule.d_ip == rule->d_ip && 267 | node->rule.d_mask == rule->d_mask && 268 | node->rule.d_port == rule->d_port && 269 | node->rule.proto == rule->proto) { 270 | list_del(lp->next); 271 | kfree(node); 272 | printk(KERN_INFO "MiniFirewall: Remove rule " 273 | "src %d.%d.%d.%d : %d dst %d.%d.%d.%d : %d " 274 | "proto %d\n", 275 | IP_POS(rule->s_ip, 3), IP_POS(rule->s_ip, 2), 276 | IP_POS(rule->s_ip, 1), IP_POS(rule->s_ip, 0), 277 | rule->s_port, 278 | IP_POS(rule->d_ip, 3), IP_POS(rule->d_ip, 2), 279 | IP_POS(rule->d_ip, 1), IP_POS(rule->d_ip, 0), 280 | rule->d_port, rule->proto); 281 | break; 282 | } 283 | } 284 | } 285 | 286 | 287 | /* 288 | * The function handles user-space write operation, which sends add and remove 289 | * instruction to the MiniFirewall module 290 | */ 291 | static ssize_t 292 | mfw_dev_write(struct file *file, const char *buffer, size_t length, 293 | loff_t *offset) 294 | { 295 | struct mfw_ctl *ctlp; 296 | int byte_write = 0; 297 | 298 | if(length < sizeof(*ctlp)) { 299 | printk(KERN_ALERT 300 | "MiniFirewall: Receives incomplete instruction\n"); 301 | return byte_write; 302 | } 303 | 304 | /* Transfer user-space data to kernel-space buffer */ 305 | while(length && (byte_write < sizeof(*ctlp))) { 306 | get_user(Buffer[byte_write], buffer + byte_write); 307 | byte_write++; 308 | length--; 309 | } 310 | 311 | ctlp = (struct mfw_ctl *)Buffer; 312 | switch(ctlp->mode) { 313 | case MFW_ADD: 314 | mfw_rule_add(&ctlp->rule); 315 | break; 316 | case MFW_REMOVE: 317 | mfw_rule_del(&ctlp->rule); 318 | break; 319 | default: 320 | printk(KERN_ALERT 321 | "MiniFirewall: Received an unknown command\n"); 322 | } 323 | 324 | return byte_write; 325 | } 326 | 327 | 328 | /* Inbound hook configuration for netfilter */ 329 | struct nf_hook_ops mfw_in_hook_ops = { 330 | .hook = mfw_in_filter, 331 | .pf = PF_INET, 332 | .hooknum = NF_INET_PRE_ROUTING, 333 | .priority = NF_IP_PRI_FIRST 334 | }; 335 | 336 | 337 | /* Outbound hook configuration for netfilter */ 338 | struct nf_hook_ops mfw_out_hook_ops = { 339 | .hook = mfw_out_filter, 340 | .pf = PF_INET, 341 | .hooknum = NF_INET_LOCAL_OUT, 342 | .priority = NF_IP_PRI_FIRST 343 | }; 344 | 345 | 346 | /* File operation configuration for a device file */ 347 | struct file_operations mfw_dev_fops = { 348 | .read = mfw_dev_read, 349 | .write = mfw_dev_write, 350 | .open = mfw_dev_open, 351 | .release = mfw_dev_release 352 | }; 353 | 354 | 355 | /* 356 | * The MiniFirewall kernel module is initialized by this function. 357 | */ 358 | static int __init mfw_mod_init(void) 359 | { 360 | int ret; 361 | 362 | /* Initialize static global variables */ 363 | Device_open = 0; 364 | Buffer = (char *)kmalloc(sizeof(struct mfw_ctl *), GFP_KERNEL); 365 | if(Buffer == NULL) { 366 | printk(KERN_ALERT 367 | "MiniFirewall: Fails to start due to out of memory\n"); 368 | return -1; 369 | } 370 | INIT_LIST_HEAD(&In_lhead); 371 | INIT_LIST_HEAD(&Out_lhead); 372 | 373 | /* Register character device */ 374 | ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_INTF_NAME, &mfw_dev_fops); 375 | if(ret < 0) { 376 | printk(KERN_ALERT 377 | "MiniFirewall: Fails to start due to device register\n"); 378 | return ret; 379 | } 380 | printk(KERN_INFO "MiniFirewall: " 381 | "Char device %s is registered with major number %d\n", 382 | DEVICE_INTF_NAME, DEVICE_MAJOR_NUM); 383 | printk(KERN_INFO "MiniFirewall: " 384 | "To communicate to the device, use: mknod %s c %d 0\n", 385 | DEVICE_INTF_NAME, DEVICE_MAJOR_NUM); 386 | 387 | /* Register netfilter inbound and outbound hooks */ 388 | nf_register_hook(&mfw_in_hook_ops); 389 | nf_register_hook(&mfw_out_hook_ops); 390 | return 0; 391 | } 392 | /* Add the (above) initialize function to the module */ 393 | module_init(mfw_mod_init); 394 | 395 | 396 | /* 397 | * The MiniFirewall module is cleaned up by this function. 398 | */ 399 | static void __exit mfw_mod_cleanup(void) 400 | { 401 | struct rule_node *nodep; 402 | struct rule_node *ntmp; 403 | 404 | kfree(Buffer); 405 | 406 | list_for_each_entry_safe(nodep, ntmp, &In_lhead, list) { 407 | list_del(&nodep->list); 408 | kfree(nodep); 409 | printk(KERN_INFO "MiniFirewall: Deleted inbound rule %p\n", 410 | nodep); 411 | } 412 | 413 | list_for_each_entry_safe(nodep, ntmp, &Out_lhead, list) { 414 | list_del(&nodep->list); 415 | kfree(nodep); 416 | printk(KERN_INFO "MiniFirewall: Deleted outbound rule %p\n", 417 | nodep); 418 | } 419 | 420 | unregister_chrdev(DEVICE_MAJOR_NUM, DEVICE_INTF_NAME); 421 | printk(KERN_INFO "MiniFirewall: Device %s is unregistered\n", 422 | DEVICE_INTF_NAME); 423 | 424 | nf_unregister_hook(&mfw_in_hook_ops); 425 | nf_unregister_hook(&mfw_out_hook_ops); 426 | } 427 | /* Add the (above) cleanup function to the module */ 428 | module_exit(mfw_mod_cleanup); 429 | --------------------------------------------------------------------------------