Linux内核网络编程
netfilter
内核网络编程
网络协议数据结构inet_protosw
在Linux-2.6.26.3/net/ipv4/af_inet.c
文件中有一个名为inet_init()的函数对协议进行了初始化。inet_init()函数使用proto_register()函数来注册每个内嵌协议。
软中断CPU报文队列及其处理
- Linux内核网络协议层的层间传递手段——软中断。
软中断机制的核心元素:
- 软中断状态:是否有触发的软中断未处理
- 软中断向量表:包含两个成员变量,一个是处理此软中断的回调函数,另一个是处理时所需的参数。
- 软中断守护内核线程:内核建立一个内核线程ksoftirqd来轮询软中断状态,调用软中断向量表中的软中断回调函数处理中断。
中断事件处理过程:
- 中断事件发生
- 调用raise_softirq()函数设置对应的中断标记位,触发中断事务
- 检测中断状态寄存器的状态
- ksoftirqd通过查询发现某一软中断事务发生之后,通过软中断向量表调用软中断程序action
软中断使用方法
Linux系统最用同时注册32个软中断,目前系统使用了6个软中断。其中一个为taskle机制,该机制用来实现下半部,描述软中断的核心数据结构为中断向量表,其定义如下:
struct softirq_action
{ void (*action)(struct softirq_action *); void *data;
};
- action:软中断服务程序
- data:服务程序输入参数
sk_buff结构
因为内核层和用户层在网络方面的差别很大,在内核的网络层的sk_buff结构占有重要的地位,几乎所有的处理与此结构有关系。
sk_buff主要成员:
socket数据在内核中接收和发送
socket数据在内核中的流程主要包括初始化、销毁、接收和发送网络数据源。其过程设计网卡驱动、网络协议栈和应用层的接口函数。
- socket()初始化:创建socket()需要传递family、type、protocol这三个参数。
创建socket()其实就是创建一个socket实例,然后创建一个文件描述符结构。创建套接字文件描述符会互相建立关联,即建立相互连接的的指针,并且初始化这些文件的读写操作映射到read()、write()函数上来。
在初始化套接字的时候,同时初始化socket的操作函数(proto_ops结构)
创建socket的同时还创建sock结构的数据空间。还会初始化三个队列:receive_queue,send_queue,backlog_queue。
- 接收网络数据recv():
网络数据接收依次经过网卡驱动和协议栈程序。
- 发送网络数据send():
linux对网络数据的发送过程的处理与接收过程相反。在一端对socket进行write()的过程中。首先会把write的字符串缓冲区整理成msghdr的数据结构形式,然后调用sock_sendmsg()把msghdr的数据传送至inet层。
msghdr结构中数据区的每个数据包,创建sk_buff结构,填充数据,挂至发送队列。一层层往下层协议传递。以下的每层协议将不再对数据进行复制,而是对sk_buff结构直接进行操作。
内核模块编程
内核模块编程和典型的应用程序的区别:
典型应用有一个main程序,而内核模块需要一个初始化函数和清理函数,在向内核中插入模块时调用初始化函数,卸载内核模块时调用清理函数。
加载内核相对于直接编写内核模块有很大方便性:
- 不用重新编译内核
- 可以动态加载和卸载,调试使用方便。
Hello,World
编写C语言程序:
//必要的头文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
//模块许可证声明(必须)
MODULE_LICENSE("Dual BSD/GPL");
//模块加载函数(必须)
static int hello_init(void)
{printk(KERN_ALERT "Hello World enter/n");return 0;
}
//模块卸载函数(必须)
static void hello_exit(void)
{printk(KERN_ALERT "Hello World exit/n");
}
//模块的注册
module_init(hello_init);
module_exit(hello_exit);
//声明模块的作者(可选)
MODULE_AUTHOR("XXX");
//声明模块的描述(可选)
MODULE_DESCRIPTION("This is a simple example!/n");
//声明模块的别名(可选)
MODULE_ALIAS("A simplest example");
编写makefile文件:
obj-m += hello.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
执行make命令能得到
以上这些文件,使用sudo insmod Hello.ko
来加载内核:
但是能发现并没有任何消息提示,这是因为printk函数并不是在命令行将信息打印而是在系统日志里面,需要使用指令dmesg
进入系统日志:
也可以使用lsmod查看内核依赖关系:
最后,输入rmmod
指令卸载内核
内核模块的基本架构
- 模块初始化
自动调用模块的的初始化函数,进行模块的初始化,主要是资源申请。
- 模块清楚函数
使用命令rmmod卸载内核模块,模块清除函数自动调用。主要状态重置和资源释放。
- 模块许可声明、作者、模块描述等信息
并不是强制要求必须声明。内核可以识别以下四种许可方式:GPLa Dual BSD/GPLa Dual MPL/GPLa Proprietary。如果没有采用以上许可证方式的声明则假定为私有的,内核加载这种模块会被“污染”。
- MODULE_AUTHOR:作者描述模块
- MODULE_DESCRIPTION:模块用途的简短描述
- MODULE_VERSION:模块版本号
- MODULE_AVIAS:模块别名
内核加载模块过程
内核卸载模块过程
netfilter的五个钩子函数
在IP包的IPv4协议栈上的传递过程中,有五个检查点,并且各引入了一对NF_HOOK()宏函数的一个响应的调用:
- PREROUTING:在报文做路由以前执行
- LOCAK-IN:在报文转向另一个NIC(网卡)以前执行
- FORWARD:在报文流出以前执行
- LOCAL-OUT:在流入本地的报文做路由以后执行
- POSTROUTING:在本地报文做流出路由之前执行
利用这5个参考点,查阅用户注册的回调函数,根据用户定义的回调函数来监视进出的网络数据包,是netfilter的基本实现框架。
netfilter定义了一个全局变量来存储用户的回调函数:
struct list_head nf_hooks[NPROTO][NF_MAX_HOOKS];
其中,NPROTO是协议类型,可以是TCP、UDP或者IP协议。NF_MAX_HOOKS是挂接的钩子最大数量。
以上介绍的五个检查点对应五个钩子函数。
- NF_IP_PRE_ROUTING:
- NF_IP_FORWARD:
- NF_IP_POST_ROUTING:
- NF_IP_LOCAL_IN:
- NF_IP_LOACL_OUT:
如果是要做包过滤,需要用到NF_IP_LOCAL_IN钩子函数。
NF_HOOK宏
netfilter的框架是在协议栈处理过程中调用NF_HOOK(),插入处理过程来实现的。
#ifdef CONFIG_NETFILTER
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) \
nf_hook_slow((pf), (hook), (skb), (indev), (outdev), (okfn)))
#else
#define NF_HOOK(pf, hook, skb, indev, outdev, okfn) (okfn)(skb)
#endif /*CONFIG_NETFILTER*/
以上为NF_HOOK()函数的定义。从该函数宏可知,当编译了netfilter时(#ifdef CONFIG_NETFILTER )就会调用nf_hook,slow()函数。如果没有的话则调用NF_HOOK宏中的参数okfn。
钩子的处理规则
netfilter的钩子函数的返回值可以为NF_ACCEPT、 NF_DROP、 NF_STOLEN、NF_QUERE、 NF_REPEAT
- nf_accept:继续传递,保持和原来传输的一致
- nf_drop:丢弃包,不再继续传递
- nf_stolen:接管包,不再继续传递
- nf_quere:队列化包(通常是为用户空间处理做准备)
- nf_repeat:再次调用这个钩子
注册/注销钩子
注册和注销钩子函数的接口主要有:nf_register_hook、nf_unregister_hook、nf_register_sockopt 、nf_unregister_sockopt
nf_hook_ops结构
struct nf_hook_ops
{ struct list_head list; //钩子链表nf_hookfn *hook; //钩子处理函数struct module //模块所有者 int pf; //钩子的协议族int hooknum; //钩子的位置值int priority; //钩子的优先级,默认情况为继承优先级
};
typedef unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *));
okfn()函数是当回调函数为空时,netfilter调用的处理函数。
- 注册钩子
void nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops);
- 注销钩子
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *ops);
钩子函数的Hello,World
#include <linux/init.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <net/tcp.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>static struct nf_hook_ops nfhoLocalIn; //定义实现钩子函数的结构体
MODULE_LICENSE("Dual BSD/GPL");unsigned int hello_hookfn(void* priv, struct sk_buff* skb, const struct nf_hook_state* state) //回调函数
{printk(KERN_ALERT "Hello World Hook\n");return NF_ACCEPT;
}
int init_module() //初始化模块
{nfhoLocalIn.hook = hello_hookfn; //构建结构体nfhoLocalIn.pf = PF_INET;nfhoLocalIn.priority = NF_IP_PRI_FIRST;nf_register_net_hook(&init_net, &nfhoLocalIn); //注册钩子printk("My nf register\n");return 0;
}
void cleanup_module()
{nf_unregister_net_hook(&init_net, &nfhoLocalIn); //注销钩子printk("My nf unregister\n");
}
Linux内核网络编程相关推荐
- 显示驱动包含在Linux内核层,驱动程序层(上) - Linux内核--网络栈实现分析_Linux编程_Linux公社-Linux系统门户网站...
经过前面两篇博文的分析,已经对Linux的内核网络栈的结构有了一个模糊的认识,这里我们开始从底层开始详细分析Linux内核网络栈的实现.由于这是早期版本,代码的层次隔离做的还不是很好,这里说是从底层分 ...
- Linux内核网络栈1.2.13-socket.c函数概述
参考资料 <<linux内核网络栈源代码情景分析>> socket常用函数概述 根据socket提供的常用的库函数,socket,read,write等函数, 执行的过程 in ...
- 深入Linux内核网络堆栈
前一段时间看到这篇帖子,确实很经典,于是翻出了英文原版再读,顺便再翻译出来供大家学习,这篇文章的中文版也早都有了,不过出于完全理解的目的,我还是将它翻译了出来,加进了自己的代码,虽然在上一周的翻译过程 ...
- Linux内核网络性能优化
Linux内核网络性能优化 1. 前言 2. Linux网络协议栈 3. DPDK 4. XDP 4.1 XDP主要的特性 4.2 XDP与DPDK的对比 4.3 应用场景 5. CPU负载均衡 5. ...
- 基于linux epoll网络编程细节处理丨epoll原理剖析
epoll原理剖析以及三握四挥的处理 1. epoll原理详解 2. 连接的创建与断开 3. epoll如何连接细节问题 视频讲解如下,点击观看: 基于linux epoll网络编程细节处理丨epol ...
- Linux多线程网络编程要义丨epoll与reactor原理
linux多线程网络编程要义 1. epoll原理剖析 2. 单reactor原理以及应用 3. 多reactor原理以及应用 [Linux服务器系列]Linux多线程网络编程要义丨epoll与rea ...
- Linux内核网络协议栈流程及架构
文章目录 Linux内核网络报文处理流程 Linux内核网络协议栈架构 Linux内核网络报文处理流程 linux网络协议栈是由若干个层组成的,网络数据的处理流程主要是指在协议栈的各个层之间的传递. ...
- 【Linux】网络编程三:TCP通信和UDP通信介绍及代码编写
参考连接:https://www.nowcoder.com/study/live/504/2/16. [Linux]网络编程一:网络结构模式.MAC/IP/端口.网络模型.协议及网络通信过程简单介绍 ...
- linux和网络编程笔记
第一部分.章节目录 3.4.1.程序的开始和结束 3.4.2.进程环境 3.4.3.进程的正式引入 3.4.4.fork创建子进程 3.4.5.父子进程对文件的操作 3.4.6.进程的诞生和消亡 3. ...
- 《Linux内核驱动模块编程指南》
Foreword Table of Contents 作者声明 版本和注意 感谢 译者注 作者声明 <Linux内核驱动模块编程指南>最初是由Ori Pomerantz为2.2版本的内核编 ...
最新文章
- JTable动态显示隐藏列
- android黑科技系列——静态分析技术来破解Apk
- 【杂谈】新手如何掌握深度学习模型?赠书2本,星球券10张
- Exchange Server2010系列之七:多邮箱搜索找出神秘邮件的幕后黑手
- 浏览器了解(二)HTML解析过程
- 20应用统计考研复试要点(part8)--应用多元分析
- linux的locate工具,linux文本查找工具之locate、find
- vue全家桶+Koa2开发笔记(3)--mongodb
- Visual Studio Code 编辑器使用
- MyBatis返回结果不稳定
- docker入门2---docker的初体验
- vi编辑器使用技巧篇1
- 计算机工程工艺,中国计算机学会第十届计算机工程与工艺学术年会.pdf
- Win10快捷键模式退出的方法
- PDFObject无法加载远程url和不支持IE浏览器解决方案
- 统信系统安装京瓷打印机驱动步骤 针对京瓷系列复合机的UOS操作系统用户使用说明
- 脚本文件BAT入门(1)
- DWG中注记平移问题
- 视频监控ai分析系统 yolo
- Ubuntu 10.04内核源码树的编译和安装
热门文章
- 【FPGA——协议篇】:I2C总线协议详解+verilog源码
- 递归算法经典实例python-python实现汉诺塔递归算法经典案例
- QQ登录超时,请检查您的网络或本机防火墙设置【00001】
- jieba java_【NLP】【一】中文分词之jieba
- GPU机器无法使用GPU
- mxf转换工具(Aiseesoft MXF Converter) v9.2.36
- /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o:在函数‘_start’中:(.text+0x20):对‘main’未
- LOIC低轨道离子拒绝服务攻击
- 微信小程序毕业设计和毕业论文怎么写,答辩流程是怎样的?
- Windows系统字体和系统应用字体