Linux内核网络编程

article/2025/9/24 6:43:54

netfilter

内核网络编程

网络协议数据结构inet_protosw

Linux-2.6.26.3/net/ipv4/af_inet.c文件中有一个名为inet_init()的函数对协议进行了初始化。inet_init()函数使用proto_register()函数来注册每个内嵌协议。

软中断CPU报文队列及其处理

  • Linux内核网络协议层的层间传递手段——软中断

软中断机制的核心元素:

  • 软中断状态:是否有触发的软中断未处理
  • 软中断向量表:包含两个成员变量,一个是处理此软中断的回调函数,另一个是处理时所需的参数。
  • 软中断守护内核线程:内核建立一个内核线程ksoftirqd来轮询软中断状态,调用软中断向量表中的软中断回调函数处理中断

中断事件处理过程:

  1. 中断事件发生
  2. 调用raise_softirq()函数设置对应的中断标记位,触发中断事务
  3. 检测中断状态寄存器的状态
  4. ksoftirqd通过查询发现某一软中断事务发生之后,通过软中断向量表调用软中断程序action

软中断使用方法

Linux系统最用同时注册32个软中断,目前系统使用了6个软中断。其中一个为taskle机制,该机制用来实现下半部,描述软中断的核心数据结构为中断向量表,其定义如下:

struct softirq_action 
{ void (*action)(struct softirq_action *); void *data; 
};
  • action:软中断服务程序
  • data:服务程序输入参数

sk_buff结构

因为内核层和用户层在网络方面的差别很大,在内核的网络层的sk_buff结构占有重要的地位,几乎所有的处理与此结构有关系。

sk_buff主要成员:

image-20220405143913438

image-20220405143934422

socket数据在内核中接收和发送

socket数据在内核中的流程主要包括初始化、销毁、接收和发送网络数据源。其过程设计网卡驱动、网络协议栈和应用层的接口函数。

  • socket()初始化:创建socket()需要传递family、type、protocol这三个参数。
  • 创建socket()其实就是创建一个socket实例,然后创建一个文件描述符结构。创建套接字文件描述符会互相建立关联,即建立相互连接的的指针,并且初始化这些文件的读写操作映射到read()、write()函数上来。

  • 在初始化套接字的时候,同时初始化socket的操作函数(proto_ops结构)

  • 创建socket的同时还创建sock结构的数据空间。还会初始化三个队列:receive_queue,send_queue,backlog_queue。

  • 接收网络数据recv():

网络数据接收依次经过网卡驱动和协议栈程序。

image-20220325144139835

image-20220325144502467

  • 发送网络数据send():

linux对网络数据的发送过程的处理与接收过程相反。在一端对socket进行write()的过程中。首先会把write的字符串缓冲区整理成msghdr的数据结构形式,然后调用sock_sendmsg()把msghdr的数据传送至inet层。

image-20220325144513325

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命令能得到

image-20220325152108271

以上这些文件,使用sudo insmod Hello.ko来加载内核:

但是能发现并没有任何消息提示,这是因为printk函数并不是在命令行将信息打印而是在系统日志里面,需要使用指令dmesg进入系统日志:

image-20220325152255344

也可以使用lsmod查看内核依赖关系:

image-20220325152321560

最后,输入rmmod指令卸载内核

image-20220325152411520

内核模块的基本架构

image-20220325153548250

  1. 模块初始化

自动调用模块的的初始化函数,进行模块的初始化,主要是资源申请。

  1. 模块清楚函数

使用命令rmmod卸载内核模块,模块清除函数自动调用。主要状态重置和资源释放。

  1. 模块许可声明、作者、模块描述等信息

并不是强制要求必须声明。内核可以识别以下四种许可方式:GPLa Dual BSD/GPLa Dual MPL/GPLa Proprietary。如果没有采用以上许可证方式的声明则假定为私有的,内核加载这种模块会被“污染”。

  • MODULE_AUTHOR:作者描述模块
  • MODULE_DESCRIPTION:模块用途的简短描述
  • MODULE_VERSION:模块版本号
  • MODULE_AVIAS:模块别名

内核加载模块过程

image-20220325155012425


内核卸载模块过程

image-20220325155120104


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; 	   		 //钩子的优先级,默认情况为继承优先级
};

image-20220325164013026

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");
}

http://chatgpt.dhexx.cn/article/5SwqnqhW.shtml

相关文章

Linux学习之内核模块编程

前言 之前成功编译了内核&#xff0c;这次学习如何修改增加删除内核模块&#xff0c;为了保证内核的纯净&#xff0c;我特意重新编译安装了一个新的5.11.8的内核&#xff0c;其他内核同理。 本文原创&#xff0c;创作不易&#xff0c;转载请注明&#xff01;&#xff01;&…

MATLAB脚本调用simulink仿真文件及simulink模块参数修改-load_system函数-sim函数-set_param函数

文章目录 1. load_system2. sim3. set_param4. get_param5. 应用 1. load_system 加载系统&#xff0c;添加所需要加载的simulink仿真模型所在的路径和名称。 load_system(pathname\filename);必须加载系统之后才可以完成后续的运行simulink仿真模型和获取仿真模型参数和设置…

Matlab学习笔记3——str2num

Matlab学习笔记3——str2num Convert character array or string to numeric array 将字符数组或字符串转换为数字数组 语法 X str2num(chr) [X,tf] str2num(chr) 输出 X — 输出数组 数字矩阵 输出数组&#xff0c;以数字矩阵形式返回。 tf — 真或假 1 |0 真或假结果…

strlen,strcpy,strcat,strcmp函数

1.strlen函数 strlen函数的作用是计算给定字符串的长度&#xff0c;从内存的某个位置开始&#xff0c;遇到第一个\0结束。 使用样例&#xff1a; int main() {const char *ar "abcdef";printf("%d\n", strlen(ar)); } 返回字符串"abcdef"的…

MATLAB str char cell num格式互相转换

简介 关于如何str char cell num格式互相转换的例子很多&#xff0c;但是都很单一&#xff0c;有时候使用的时候需要查找很久才可以解决问题。这里就对这几种方法进行一个汇总。 之所以会涉及使用cell&#xff0c;是因为涉及字符串、数字在同一个“矩阵”中&#xff0c;这时候…

C++ std::string::substr()

substr()函数返回一个新建的 初始化为string对象的子串的拷贝string对象。 子串是&#xff0c;在字符位置_Off开始&#xff0c;跨越_Count个字符&#xff08;或直到字符串的结尾&#xff09;对象的部分 void main() {//std::string::substr(_Off 0, _Count 4294967295U)&am…

matlab中 str2num 函数与 str2double 函数的区别

str2num 函数与 str2double 函数的相同点与不同点 1. 相同点&#xff1a; 当str为一个含数字的字符串时&#xff0c; str2num 函数与 str2double 函数一样。 如&#xff1a; 2. 不同点&#xff1a;当str为多个字符串构成的数组时&#xff0c; str2num 函数与 str2double 函数有…

可逆计算:下一代软件构造理论

可逆计算&#xff1a;下一代软件构造理论 众所周知&#xff0c;计算机科学得以存在的基石是两个基本理论&#xff1a;图灵于1936年提出的图灵机理论和丘奇同年早期发表的Lambda演算理论。这两个理论奠定了所谓通用计算&#xff08;Universal Computation&#xff09;的概念基础…

android 微积分计算器,不到1M的良心之作 连微积分都能算的计算器APP

计算器可谓是被手机取代的一大电子产品了&#xff0c;不过手机上的APP是否真的有传统的计算器好用&#xff1f;也并不一定。 一来&#xff0c;手机上的计算器APP功能普遍偏弱&#xff0c;特别是手机ROM自带的计算器&#xff1b;二来&#xff0c;计算器APP也算得上是流氓软件的重…

matlab对信号积分,对信号求积分 - Simulink - MathWorks 中国

说明 Integrator 模块输出其输入信号相对于时间的积分值。 Simulink 将 Integrator 模块作为具有一种状态的动态系统进行处理。模块动态由以下方程指定: {x˙(t)=u(t)y(t)=x(t)x(t0)=x0 ,其中: u 是模块输入。 y 是模块输出。 x 是模块状态。 x0 是 x 的初始条件。 虽然这些…

清华大学计算机学复变函数吗,清华大学计算机系课程 - osc_vq6lx46c的个人空间 - OSCHINA - 中文开源技术交流社区...

这么NB的大学&#xff0c;这么NB的课程&#xff0c;我们还有何理由不努力&#xff0c;同是大学生&#xff0c;人家在学校学的本身就比我们多&#xff0c;还需要更加努力才能跟上步伐&#xff0c;加油。 补充&#xff1a; 清华大学计算机系的课程分为六类课程&#xff1a; (一)公…

Chapter2.4:复数和复变函数运算

该系列博客主要讲述Matlab软件在自动控制方面的应用&#xff0c;如无自动控制理论基础&#xff0c;请先学习自动控制系列博文&#xff0c;该系列博客不再详细讲解自动控制理论知识。 自动控制理论基础相关链接&#xff1a;https://blog.csdn.net/qq_39032096/category_10287468…

信号与系统分析中的复变函数

动态图片来自于&#xff1a; ShutterStock 网站 . 01 教程规划 1.1 背景介绍 针对于信号与系统分析 课程学习的同学&#xff0c;由于之前没有先修过课程复变函数 &#xff0c;则会在后面信号与系统理论学习中缺少复变函数相关理论支持。为了帮助这部分同学及时补充上复变函数相…

matlab复变函数应用,matlab在复变函数中的一些应用修改后的.doc

matlab在复变函数中的一些应用修改后的.doc MATLAB语言课程论文MATLAB在复变函数中的一些应用姓名刘乐学号12013241953专业通信工程班级2013级通信2班指导老师朱瑜红学院物理电气性息学院完成日期2013年11月9日MATLAB在复变函数中的一些应用刘乐120132419532013级通信2班【摘要…

复变函数与积分变换matlab,matlab在复变函数与积分变换的应用

matlab在复变函数与积分变换的应用 本科毕业论文题目&#xff1a; MATLAB在复变函数与积分变换的应用 学院&#xff1a; 数学与计算机科学学院 班级&#xff1a; 数学与应用数学2009级班 姓名&#xff1a; 指导教师&#xff1a; 职称&#xff1a; 副教授 完成日期&#xff1a;…

复变函数与积分变换

复变函数与积分变换 一、拉普拉斯变换1.拉氏变换的性质a.线性性质b.相似性质c.微分性质例子例子 拉式变换 象函数的微分性质例子例子 积分性质象函数的积分性质例子例子 延迟性质位移性质拉氏变换的应用 一、拉普拉斯变换 1.拉氏变换的性质 a.线性性质 b.相似性质 pygame ursi…

unigui美化界面源码框架

对于delphier来说&#xff0c;顺应互联网时代&#xff0c;用delphi开发web程序&#xff0c;一直是一个很头痛的问题&#xff0c;以往开发delphi程序往往不需要前端和美工参与。但在ui界面上要想漂亮&#xff0c;需要配合和学习css、JS和美工知识&#xff0c;所以很多人会放弃。…

【一起学UniGUI】--UniGUI的界面与程序架构(4)

1、【统一的界面】 uniGUI是统一的图形用户界面&#xff0c;简称统一GUI。之所以称为统一的是因为它在所有带有Web浏览器的设备中提供了相同的UI体验。无论设备、操作系统、CPU和显示器是什么&#xff0c;在所有具有兼容Web浏览器的设备上&#xff0c;用户体验都是相同…

【一起学UniGUI】--创建新的uniGUI应用程序(11)

打开Delphi 10.3.1&#xff0c;在Delphi IDE中可以通过uniGUI应用程序向导轻松创建一个新的uniGUI应用程序(必须通过此方式来创建一个新的uniGUI应用程序)&#xff0c;并按向导界面提示操作。&#xff08;一&#xff09;、使用uniGUI应用程序向导创建一个新的uniGUI应用程序&am…

(1)uniGUI for C++ builder网站开发之uniGUI控件安装和你好世界

uniGUI for CBuilder网站开发之uniGUI控件安装和你好世界 By runsky中行雷威 2018.2.5 &#xff08;同一个世界&#xff0c;同一个梦想&#xff0c;交流学习CBuilder XE10&#xff0c;传承cbuilder的魅力&#xff01;欢迎各地朋友加入我的QQ群299497712,860634510、484979943…