这个实验其实就是之前网络编程一个实验,不过因为代码是拿老师的代码进行修改的,所以就没有写对应的博客。
实验要求基于操作系统内核驱动,设计实现一个简单防火墙系统
实验思路可以基于netfilter框架,编写内核防火墙模块(驱动)和用户态防火墙控制程序,实现服务协议、IP地址、端口等的控制和过滤。
实验环境使用的虚拟机:VMware16中的Ubuntu2020
使用的操作系统:Linux操作系统
使用的语言:C语言[gcc (Ubuntu 11.1.0-1uUbuntu1-20.04) 11.1.0]
使用的内核版本:Linux ubuntu2020 5.15.0-69-generic
实验中遇到的问题第一个问题是内核版本的问题,在将使用make进行编译内核代码的时候,报错显示nfhoSockopt.set = hookSockoptSet; 这里赋值的变量类型不匹配,但是我百度查书都显示这是可以赋值的,所以估计是内核版本的问题。再看报错有提示说将一些警告视为报错了,经百度发现可以通过在makefile文件中添加KBUILD_CFLAGS += -w 可以解决这个问题。当然,也可以将对应函数的代码版本改成当前的代码版本。
第二个问题是,在删除规则的时候,规则不存在,但是防火墙没有进行提示且显示setsockopt()success,用户层的代码确实和内核层完成了信息交互,但是如果提前进行判断,就可以不用与内核进行信息交互了。这个问题可以通过在用户层将数据交给内核层的时候,先获取内核层中的规则集,然后比较要删除的规则是否在规则集中,如果在那么可以进行删除,否则输出提示要删除的规则不存在。
第三个问题是,在添加了一个规则后,再次添加完全相同的规则时,仍能添加成功,显然代码在添加规则时没有进行查重判断。这一个问题可以和上一个问题一起解决,在添加规则之前先获取内核层的规则集,然后进行遍历,看是否有完全一样的规则。当然,解决这个问题还需要获取当前输入的规则的信息,所以我创建了一个全局变量来保存输入时候的规则,所以可以进行比较。如果遍历规则集时发现有一样的规则,那么不进行添加并且输出该规则已存在的信息。
第四个问题是规则优先级的问题(规则匹配的问题),在输入新规则进行查重之后,如果有两个规则除了最后一个参数其他都一样,也就是一个是allow另一个是reject,那么是允许还是拒绝?正常来说,后面输入的优先级高,所以如果添加规则的时候是类似与头插法,那么只需要找到第一个符合的规则就退出规则匹配,如果是尾插法,也就是将新规则放在最后,那么要匹配到最后一个符合的规则才行。从节省时间的角度出发,当然是将新规则放在首位更加节省时间,所以匹配到第一个符合的规则即可退出匹配。
2024年6月20日晚添加:使用到的测试命令发现很多同学看了不知道怎么运行,我这里来说明一下。
最好虚拟机的版本和内核是和我差不多的,不然很有可能会有未知的错误(函数过期啊啥的,当然不一样的能运行也可以)
首先将我下面的三个代码复制到同一个目录下面(这个目录最好只有这个三个文件),然后右键在这个目录下打开终端。下面就是具体的命令了。
第一条命令是:make。(make正常的话会生成一个myfw.ko文件,这个就是生成的内核的模块了。如果报错的话原因就有很多种了,建议是先自行百度看能解决吗,也可以问题但我不一定能给出好的解决方案。实在不行可以放弃看我这篇博客了。。。。)。强调一下,myfw.c这个代码是内核层的代码,不能在用户层编译,并且这个代码在make 的时候会自动编译。
第二条命令就是编译myfwctl.c文件:
gcc -o myfwctl myfwctl.c
第三条命令就是加载myfw.ko模块:
sudo insmod myfw.ko
提一嘴,移除模块的命令是:sudo rmmod myfw.ko
其他命令说明一下,sudo ./myfwctl rule 就是查看现有的规则。sudo ./myfwctl rule del n就是删除第n条规则(前提是规则存在)。sudo ./myfwctl rule add any any any any ICMP a/r,这条命令就是拿来添加规则的。这四个any按顺序分别代表:发送端端口 发送端ip 接收端端口 接收端ip,ICMP就是使用的协议了,可以改为其他协议但我没测试过(就用过ping来测试)。最后还有一个字母a或者r,分别是allow和reject的缩写,分别代表允许和拒绝。大概就这些了,有问题可以评论或者私信我,我看到会及时回复的。
还有就是查看日志的命令,sudo su输入ubuntu的密码进入管理员权限然后使用dmesg就可以查看日志了。
测试效果运行防火墙后,删除不存在规则:
可以看到,规则不存在会进行提示
连续添加两个相同的规则:
也会进行提示,并且每次添加或删除成功后,会输出当前的规则集通过ifconfig命令获取虚拟机的ip地址后用主机ping
可以看到,此时防火墙允许主机ping
查看主机ip并添加规则禁止主机ping虚拟机:
我的主机ip为:192.168.110.1
说明防火墙成功的将主机发送的数据包丢弃。
最后查看输出的日志:
实验源代码该防火墙代码主要有3部分,头文件代码(myfw.c)、内核层代码(myfw.c)和用户层代码(myfwctl.c),另外还有一个Makefile工程文件,用于将这3个代码编译并生成内核模块,之后加载该模块,就可以运行用户层代码,进行防火墙的测试了。
内核层代码(myfw.c):
#include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h>//包含sk_buff结构 #include <net/tcp.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netfilter_ipv4.h> #include "myfw.h" //内核层的代码 //使用静态变量更适合与模块化编程,使用static进行定义的结构或变量只能在本文件中使用 //定义detfilter的5个钩子点: static struct nf_hook_ops nfhoLocalIn; static struct nf_hook_ops nfhoLocalOut; static struct nf_hook_ops nfhoPreRouting; static struct nf_hook_ops nfhoForward; static struct nf_hook_ops nfhoPostRouting; //创建套接字选项,与用户层通信 static struct nf_sockopt_ops nfhoSockopt; static int debug_level = 0; static int nfcount = 0; static Rule* g_rules;//规则集,用指针形式来记录可以更省空间 static int g_rules_count = 0;//用来记录规则的个数 //------函数声明------ void addRule(Rule* rule);//增加规则的函数 void delRule(int rule_num);//删除规则,输入的参数表示要删除的规则的编号 int matchRule(void* skb);//进行规则比较的函数,判断是否能进行通信 void debugInfo(char* msg);//记录操作次数,将每次的操作信息输出到日志 //sk_buff就是传入的数据包,*skb unsigned int hookLocalIn(void* priv, struct sk_buff* skb, const struct nf_hook_state* state); unsigned int hookLocalOut(void* priv, struct sk_buff* skb, const struct nf_hook_state* state); unsigned int hookPreRouting(void* priv, struct sk_buff* skb, const struct nf_hook_state* state); unsigned int hookPostRouting(void* priv, struct sk_buff* skb, const struct nf_hook_state* state); unsigned int hookForward(void* priv, struct sk_buff* skb, const struct nf_hook_state* state); //用于接收用户端数据的函数 int hookSockoptSet(struct sock* sock, int cmd, void __user* user, unsigned int len); //用与将数据传到用户层的函数 int hookSockoptGet(struct sock* sock, int cmd, void __user* user, int* len); int init_module();//内核模块初始化,初始化五个钩子(进行钩子的注册) void cleanup_module();//将钩子注销 //------函数实现------ void addRule(Rule* rule)//增加规则的函数 { //这一个函数是先将要添加的规则放入新的规则集中,再把原来的规则集放到新的规则集中 //所以每次添加规则时,添加的规则都会放到第一位 int r_c = g_rules_count + 1;//将规则个数+1 Rule* g_r = (Rule*)vmalloc(r_c * sizeof(Rule));//开辟相应大小的空间 memcpy(g_r, rule, sizeof(Rule));//将要增加的规则放到新开辟的规则集中 if (g_rules_count > 0){//如果原规则集中有规则,则先将原来的规则集赋值给新规则集 memcpy(g_r + 1, g_rules, g_rules_count * sizeof(Rule)); vfree(g_rules);//回收之前的rule,释放空间 } g_rules = g_r;//将新的规则集赋值给全局规则集 g_rules_count = r_c;//更新规则集中规则的数量 } void delRule(int rule_num)//删除规则,输入的参数表示要删除的规则的编号 { int i; if (rule_num > 0 && rule_num <= g_rules_count){//如果输入的规则编号有效,则进行删除 for (i = rule_num; i < g_rules_count; i++){ //把要删除的规则的位置之后的规则都往前移一个位置,将要删除的规则覆盖掉 memcpy(g_rules + i - 1, g_rules + i, sizeof(Rule)); } g_rules_count--;//最后一个空间闲着,不用管 } } int matchRule(void* skb)//进行规则比较的函数,判断是否能进行通信 { //增加了端口控制的规则匹配 int sport = 0; int dport = 0; struct iphdr* iph = ip_hdr(skb); struct tcphdr* tcph; struct udphdr* udph; int act = 1, i; Rule* r; for (i = 0; i < g_rules_count; i++){//遍历规则集 r = g_rules + i;//用r来遍历 if ((!r->sip || r->sip == iph->saddr) && (!r->dip || r->dip == iph->daddr) && (!r->protocol || r->protocol == iph->protocol)){ switch (iph->protocol){//对协议类型进行判断进行判断 case MYFW_TCP: tcph = (struct tcphdr*)skb_transport_header(skb); sport = tcph->source; dport = tcph->dest; break; case MYFW_UDP: udph = (struct udphdr*)skb_transport_header(skb); sport = udph->source; dport = udph->dest; break; } if ((!r->sport || !sport || r->sport == sport) && (!r->dport || !dport || r->dport == dport)){ act = r->allow; } } } return act; } void debugInfo(char* msg)//记录操作次数,将每次的操作信息输出到日志 { if (debug_level){//如果等级符合要求,才进行+1和输出到日志 nfcount++; printk("%s, nfcount: %d\n", msg, nfcount); } } unsigned int hookLocalIn(void* priv, struct sk_buff* skb,//sk_buff就是传入的数据包,*skb const struct nf_hook_state* state) { unsigned rc = NF_ACCEPT;//默认继续传递,保持和原来输出的一致 if (matchRule(skb) <= 0)//查规则集,如果返回值<=0,那么不允许进行通信 rc = NF_DROP;//丢弃包,不再继续传递 debugInfo("hookLocalIn"); return rc;//返回是是否允许通信,是否丢包 } unsigned int hookLocalOut(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { debugInfo("hookLocalOut"); return NF_ACCEPT;//接收该数据 } unsigned int hookPreRouting(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { debugInfo("hookPreRouting"); return NF_ACCEPT;//接收该数据 } unsigned int hookPostRouting(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { debugInfo("hookPostRouting"); return NF_ACCEPT;//接收该数据 } unsigned int hookForward(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) { debugInfo("hookForwarding"); return NF_ACCEPT;//接收该数据 } int hookSockoptSet(struct sock* sock, int cmd, void __user* user, unsigned int len)//用于接收用户端数据的函数 { int ret = 0; Rule r; int r_num; debugInfo("hookSockoptSet"); switch (cmd){ case CMD_DEBUG: //copy_from_user函数的作用:用于将用户空间的数据拷贝到内核空间 ret = copy_from_user(&debug_level, user, sizeof(debug_level)); printk("set debug level to %d", debug_level);//设置debug等级 break; case CMD_RULE: ret = copy_from_user(&r, user, sizeof(Rule));//如果是添加规则 addRule(&r); printk("add rule");//输出到日志 break; case CMD_RULE_DEL: ret = copy_from_user(&r_num, user, sizeof(r_num));//删除规则 delRule(r_num); printk("del rule");//输出到日志 break; } if (ret != 0)//说明赋值失败,进行输出 { printk("copy_from_user error"); ret = -EINVAL; } return ret; } int hookSockoptGet(struct sock* sock, int cmd, void __user* user, int* len)//用与将数据传到用户层的函数 { int ret; debugInfo("hookSockoptGet"); switch (cmd){ case CMD_DEBUG: ret = copy_to_user(user, &debug_level, sizeof(debug_level)); break; case CMD_RULE: //copy_to_user函数的作用:将内核空间的数据拷贝到用户空间 //拷贝成功返回0 ret = copy_to_user(user, &g_rules_count, sizeof(g_rules_count)); ret = copy_to_user(user + sizeof(g_rules_count), g_rules, sizeof(Rule) * g_rules_count); break; } if (ret != 0){ ret = -EINVAL; debugInfo("copy_to_user error"); } return ret; } int init_module()//内核模块初始化,初始化五个钩子(进行钩子的注册) { nfhoLocalIn.hook = hookLocalIn;//设置一些参数 nfhoLocalIn.hooknum = NF_INET_LOCAL_IN;//注册回调函数 nfhoLocalIn.pf = PF_INET; nfhoLocalIn.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfhoLocalIn);//注册hook nfhoLocalOut.hook = hookLocalOut; nfhoLocalOut.hooknum = NF_INET_LOCAL_OUT; nfhoLocalOut.pf = PF_INET; nfhoLocalOut.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfhoLocalOut); nfhoPreRouting.hook = hookPreRouting; nfhoPreRouting.hooknum = NF_INET_PRE_ROUTING; nfhoPreRouting.pf = PF_INET; nfhoPreRouting.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfhoPreRouting); nfhoForward.hook = hookForward; nfhoForward.hooknum = NF_INET_FORWARD; nfhoForward.pf = PF_INET; nfhoForward.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfhoForward); nfhoPostRouting.hook = hookPostRouting; nfhoPostRouting.hooknum = NF_INET_POST_ROUTING; nfhoPostRouting.pf = PF_INET; nfhoPostRouting.priority = NF_IP_PRI_FIRST; nf_register_net_hook(&init_net, &nfhoPostRouting); nfhoSockopt.pf = PF_INET;//PF_INET表示协议族 nfhoSockopt.set_optmin = CMD_MIN; nfhoSockopt.set_optmax = CMD_MAX; nfhoSockopt.set = hookSockoptSet; nfhoSockopt.get_optmin = CMD_MIN; nfhoSockopt.get_optmax = CMD_MAX; nfhoSockopt.get = hookSockoptGet; nf_register_sockopt(&nfhoSockopt);//注册扩展套接字选项 printk("myfirewall started\n");//将信息输出到日志中 return 0; } void cleanup_module()//将钩子注销 { nf_unregister_net_hook(&init_net, &nfhoLocalIn);//将5个hook注销 nf_unregister_net_hook(&init_net, &nfhoLocalOut); nf_unregister_net_hook(&init_net, &nfhoPreRouting); nf_unregister_net_hook(&init_net, &nfhoForward); nf_unregister_net_hook(&init_net, &nfhoPostRouting); nf_unregister_sockopt(&nfhoSockopt); printk("myfirewall stopped\n");//输出相应的信息 } MODULE_LICENSE("GPL");//模块的许可证声明,防止收到内核被污染的警告用户层代码(myfwctl.c):
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <pthread.h> #include <signal.h> #include <errno.h> #include "myfw.h" //这一个是用户层代码 //------全局变量------ //创建一个全局变量,将要添加规则时的信息进行保留 Rule *pd_temp; //------函数声明------ void printError(char* msg);//输出错误信息的函数 void printSuccess(char* msg);//输出成功信息的函数 void usage(char* program);//输出一些信息 unsigned int str2Ip(char* ipstr);//字符串类型的ip转换为整型 char* ip2Str(unsigned int ip, char buf[32]);//将整型的ip转为字符型的ip unsigned short str2Port(char* portstr);//将端口转为整型 char* port2Str(unsigned short port, char buf[16]);//将整型端口构造为字符 char* protocol2Str(unsigned short protocol, char buf[16]);//将整型协议进行转为字符串 unsigned short str2Protocol(char* protstr);//将字符串类型的协议转为短整型的协议 void printRuleTable(RuleTable* rtb);//将规则集中的规则输出 int set(int cmd, void* val, int val_len, int sockfd);//将信息传到内核对规则进行修改 int get(int cmd, int sockfd);//将规则集进行输出或输出debug_level int parseArgs(int argc, char* argv[], int* cmd, void* val, int* val_len);//获取用户端输入的信息并进行处理 //------主函数------ int main(int argc, char *argv[]) { int ret = -1; int cmd; char val[sizeof(Rule)]; int val_len; int get_set = parseArgs(argc, argv, &cmd, val, &val_len);//将输入的参数进行处理 if (get_set)//如果函数返回值不为零 { int sockfd; //创建套接字,SOCK_RAW是RAW类型,提供原始网络协议访问 //AF_INET设置地址族,IPPRPTP_RAW:原始IP数据包 if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) printError("socket()");//创建套接字失败输出错误信息 else { if (get_set > 0) ret = set(cmd, val, val_len, sockfd); //将信息传到内核层,对规则集进行修改 else ret = get(cmd, sockfd); close(sockfd);//关闭套接字 } } else usage(argv[0]);//到这里说明是输入不对,对输入的格式进行提示 return ret;//结束用户端程序 } //------函数实现------ void printError(char* msg)//输出错误信息的函数 { printf("%s error %d: %s\n", msg, errno, strerror(errno)); } void printSuccess(char* msg)//输出成功信息的函数 { printf("%s success\n", msg); } void usage(char* program)//输出一些输入格式信息 { printf("please check your input,the correct input format is:\n"); printf("%s debug\n", program); printf("%s debug debug_level\n", program); printf("%s rule add sip sport dip dport protocol a|r\n", program); printf("%s rule del rule_number\n", program); printf("%s rule\n", program); } unsigned int str2Ip(char* ipstr)//字符串类型的ip转换为整型 { unsigned int ip; if (!strcmp(ipstr, "any"))ip = 0;//如果是any,表示任何任何端口或ip都可以,用0表示 else inet_pton(AF_INET, ipstr, &ip);//将ip地址转换为用于网络传输的数值格式 return ip; } char* ip2Str(unsigned int ip, char buf[32])//将整型的ip转为字符型的ip { if (ip){ unsigned char* c = (unsigned char*)&ip; sprintf(buf, "%d.%d.%d.%d", *c, *(c + 1), *(c + 2), *(c + 3)); } else sprintf(buf, "any"); return buf; } unsigned short str2Port(char* portstr)//将端口转为整型 { unsigned short port; if (!strcmp(portstr, "any"))port = 0; else port = atoi(portstr); return port; } char* port2Str(unsigned short port, char buf[16])//将整型端口构造为字符 { if (port)sprintf(buf, "%d", port); else sprintf(buf, "any"); return buf; } char* protocol2Str(unsigned short protocol, char buf[16])//将整型协议进行转为字符串 { switch (protocol){ case 0: strcpy(buf, "any"); break; case MYFW_ICMP: strcpy(buf, "ICMP"); break; case MYFW_TCP: strcpy(buf, "TCP"); break; case MYFW_UDP: strcpy(buf, "UDP"); break; default: strcpy(buf, "Unknown"); } return buf; } unsigned short str2Protocol(char* protstr)//将字符串类型的协议转为短整型的协议 { unsigned short protocol = 0; if (!strcmp(protstr, "any")) protocol = 0;//0表示任何端口 else if (!strcmp(protstr, "ICMP"))protocol = MYFW_ICMP; else if (!strcmp(protstr, "TCP"))protocol = MYFW_TCP; else if (!strcmp(protstr, "UDP"))protocol = MYFW_UDP; return protocol; } void printRuleTable(RuleTable* rtb)//将规则集中的规则输出 { char sip[32], dip[32], sport[16], dport[16], protocol[16]; Rule* r = &(rtb->rule); printf("Rules count: %d\n", rtb->count); for (int i = 0; i < rtb->count; i++){//遍历规则集 ip2Str(r->sip, sip);//将源ip进行转换 ip2Str(r->dip, dip);//将目的地址进行转换 port2Str(r->sport, sport);//将源端口进行转换 port2Str(r->dport, dport);//将目的端口进行转换 protocol2Str(r->protocol, protocol);//将协议进行转换 //进行输出 printf("%d\t%s:%s -> %s:%s, %s is %s\n", i + 1, sip, sport, dip, dport, protocol, r->allow ? "allow" : "reject"); r = r + 1;//移到下一个规则 } } int set(int cmd, void* val, int val_len, int sockfd)//将信息传到内核对规则进行修改 { //val_len表示的是数据类型的长度,不是规则集的个数 int ret = -1; //判断要添加的规则或要删除的规则是否在规则集中 int new_val_len = 1024 * 1024; void* new_val = malloc(new_val_len); //从内核层获取规则表 if (getsockopt(sockfd, IPPROTO_IP, CMD_RULE, new_val, &new_val_len)) printError("getsockopt");//输出错误信息,说明从内核层获取信息失败 else { RuleTable* rules = (RuleTable*)new_val; Rule* r = &(rules->rule); if (cmd == CMD_RULE_DEL) {//要进行删除操作 RuleTable* rules2 = (RuleTable*)val; if (rules2->count > rules->count) {//说明规则集中不存在该规则 printf("failed to delete:the rule to delete does not exist\n"); return 0;//不存在不需要删除 } } else if (cmd == CMD_RULE) { for (int i = 0; i < rules->count; i++) {//遍历规则集,判断是否已经存在要添加的规则。 if (pd_temp->allow == r->allow && pd_temp->dip == r->dip && pd_temp->dport == r->dport && pd_temp->protocol == r->protocol && pd_temp->sip == r->sip && pd_temp->sport == r->sport) { printf("failed to add:rule to add already exists\n"); return 0;//已存在不需要添加 } r = r + 1;//移到下一个规则 } } } //若无错误发生,setsockopt返回0,否则返回socket_error错误 if (setsockopt(sockfd, IPPROTO_IP, cmd, val, val_len)) printError("setsockopt()");//输出错误信息,说明消息传到内核失败或创建套接字失败 else{ printf("setsockopt() success\n");//前后的长度不相同说明添加或删除成功 //获取当前的规则集并进行输出 //从内核层获取规则表 int new_val_len1 = 1024 * 1024; void* new_val1 = malloc(new_val_len1); if (getsockopt(sockfd, IPPROTO_IP, CMD_RULE, new_val1, &new_val_len1)) printError("getsockopt");//输出错误信息,说明从内核层获取信息失败 else { printf("Current rule set:\n"); printRuleTable(new_val1);//将规则集进行输出 } ret = 0; } return ret; } int get(int cmd, int sockfd)//将规则集进行输出或输出debug_level { int ret = -1; int val_len = 1024 * 1024; void* val = malloc(val_len); if (getsockopt(sockfd, IPPROTO_IP, cmd, val, &val_len))printError("getsockopt");//输出错误信息 else{ switch (cmd){ case CMD_DEBUG://如果输入的是debug,那么输出debug等级 printf("debug level=%d\n", *(int*)val); break; case CMD_RULE://输入的是rule,则将规则集中的数据进行输出 printRuleTable((RuleTable*)val); break; } } return ret; } int parseArgs(int argc, char* argv[], int* cmd, void* val, int* val_len)//获取用户端输入的信息并进行处理 { int ret = 0;//初始将ret设为0 //argc是输入的参数个数 //argv数组是存放输入的参数 if (argc == 2){//说明不需要添加或者删除规则 if (!strcmp(argv[1], "debug")){ *cmd = CMD_DEBUG; ret = -1; } else if (!strcmp(argv[1], "rule")){ *cmd = CMD_RULE; ret = -1; } } else if (argc > 2){ if (!strcmp(argv[1], "debug") && argc == 3){ *cmd = CMD_DEBUG; *(int*)val = atoi(argv[2]); *val_len = sizeof(int); ret = 1; } else if (!strcmp(argv[1], "rule")){//说明要对规则进行操作 if (argc == 4){ if (!strcmp(argv[2], "del")){//删除规则 *cmd = CMD_RULE_DEL; *(int*)val = atoi(argv[3]); *val_len = sizeof(int); ret = 1; } } else if (argc == 9){ if (!strcmp(argv[2], "add")){//添加规则 *cmd = CMD_RULE; Rule* r = (Rule*)val; *val_len = sizeof(Rule); r->sip = str2Ip(argv[3]);//下面都是对输入的参数进行处理,将字符串转为数字或其他类型 r->sport = str2Port(argv[4]); r->dip = str2Ip(argv[5]); r->dport = str2Port(argv[6]); r->protocol = str2Protocol(argv[7]); r->allow = strcmp(argv[8], "a") ? 0 : 1;//如果是a,说明是允许通信,赋值为1,否则赋值为0 pd_temp = r;//将变量保存,用于后面添加规则时进行判断 ret = 1; } } } } return ret; }头文件代码(myfw.h):
#define CMD_MIN 0x6000//十进制:24576 #define CMD_DEBUG CMD_MIN+1 #define CMD_RULE CMD_MIN+2 #define CMD_RULE_DEL CMD_MIN+3 #define CMD_MAX 0x6100//十进制:24832 #define MYFW_ICMP 1 //IPPROTO_ICMP #define MYFW_TCP 6 //IPPROTO_TCP #define MYFW_UDP 17 //IPPROTO_UDP typedef struct{ unsigned int sip;//信息来源ip地址 unsigned int dip;//信息目的ip地址 unsigned short sport;//信息的发出端口 unsigned short dport;//发送信息的目的端口 unsigned short protocol;//使用的协议 unsigned short allow;//是否接收信息 }Rule; typedef struct{ unsigned int count;//表示规则的个数 Rule rule;//保存规则 }RuleTable;Makefile文件代码:
# Makefile 4.0 obj-m := myfw.o CURRENT_PATH := $(shell pwd) LINUX_KERNEL := $(shell uname -r) LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL) KBUILD_CFLAGS += -w all: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules clean: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean