前言
这个实验其实就是之前网络编程一个实验,不过因为代码是拿老师的代码进行修改的,所以就没有写对应的博客。
实验要求
基于操作系统内核驱动,设计实现一个简单防火墙系统
实验思路
可以基于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