信息安全实践2.5(防火墙设计实现)

前言

        这个实验其实就是之前网络编程一个实验,不过因为代码是拿老师的代码进行修改的,所以就没有写对应的博客。

实验要求

        基于操作系统内核驱动,设计实现一个简单防火墙系统

实验思路

        可以基于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就可以查看日志了。

测试效果

        运行防火墙后,删除不存在规则:

3c08c75546064dd8ad9152299d2af18c.png

        可以看到,规则不存在会进行提示

        连续添加两个相同的规则:

4c8e7defb9da484eb4c14271b7ccc28f.png        也会进行提示,并且每次添加或删除成功后,会输出当前的规则集

        通过ifconfig命令获取虚拟机的ip地址后用主机ping

268a160ac36c4b4bb2397d787c99af95.png

79c9db5bacc5419895dc62db90e11173.png

        可以看到,此时防火墙允许主机ping

        查看主机ip并添加规则禁止主机ping虚拟机:

        我的主机ip为:192.168.110.1

44dcb9dfc76c4a0989278abd67b10206.png

3172853859a74afb98ba71b9feff92ea.png

        说明防火墙成功的将主机发送的数据包丢弃。

        最后查看输出的日志:

226c490abe6d42c18613c494e8cf01ac.png

实验源代码

        该防火墙代码主要有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

// fire.cpp : Defines the class behaviors for the application. // #include "StarWarsCtrl.h" // Added by ClassView #include "stdafx.h" #include "fire.h" #include "MainFrm.h" #include "fireDoc.h" #include "fireView.h" #include <afxsock.h> #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CFireApp BEGIN_MESSAGE_MAP(CFireApp, CWinApp) //{{AFX_MSG_MAP(CFireApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) // NOTE - the ClassWizard will add and remove mapping macros here. // DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CFireApp construction CFireApp::CFireApp() { // TODO: add construction code here, // Place all significant initialization in InitInstance } ///////////////////////////////////////////////////////////////////////////// // The one and only CFireApp object CFireApp theApp; ///////////////////////////////////////////////////////////////////////////// // CFireApp initialization BOOL CFireApp::InitInstance() { // CG: The following block was added by the Windows Sockets component. { if (!AfxSocketInit()) { AfxMessageBox(CG_IDS_SOCKETS_INIT_FAILED); return FALSE; } } AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls(); // Call this when using MFC in a shared DLL #else Enable3dControlsStatic(); // Call this when linking to MFC statically #endif // Change the registry key under which our settings are stored. // TODO: You should modify this string to be something appropriate // such as the name of your company or organization. SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views. CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CFireDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CFireView)); AddDocTemplate(pDocTemplate); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE; // The one and only window has been initialized, so show and update it. HICON hIcon = ::LoadIcon (AfxGetResourceHandle (), MAKEINTRESOURCE(IDI_MAINFRAME)); m_pMainWnd->SetIcon(hIcon,FALSE); m_pMainWnd->SetWindowText("NetDefender"); m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; CStarWarsCtrl m_StarWarsCtrl; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) // No message handlers //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) // DDX_Control(pDX, IDC_STARWARS, m_StarWarsCtrl); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() // App command to run the dialog void CFireApp::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); } ///////////////////////////////////////////////////////////////////////////// // CFireApp message handlers
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoyuer2815

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值