Linux内核网络编程

netfilter框架概述 1 Netfilter 1.1 netfilter概述 Netfilter是linux 2.4.x引入的一个子系统,它是一个通用的、抽象的框架。其不依赖于具体的协议,而是为每种网络协议定义一套HOOK函数。这些HOOK函数在数据报经过协议栈的几个关键点被调用。 内核的任何模块可以对每种协议的一个或多个HOOK进行注册,实现挂接。这样当某个数据报被传递给Netfilter框架时,内核能检测到是否有任何模块对该协议和HOOK函数进行了注册。若注册了,则调用该模块的注册时使用的回调函数,这样这些模块就有机会检查、 阅读详情

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");
}
16.1.5 网络协议数据结构inet_protosw 16.1.5  网络协议数据结构inet_protosw第5章中对TCP/IP的网络协议族进行了介绍(IP、TCP、UDP等)。其中协议TCP、UDP、RAW在文件Linux-2.6.26.3/net/ipv4/af_inet.c中一个名为inet_init()的函数中进行了初始化(因为TCP和UDP都是inet簇协议的一部分)。inet_init()函数使用proto_register()函 阅读详情

相关推荐

Linux内核网络协议注册与初始化:从proto_register到tcp_v4_init_sock的深度解析

通过和统一管理:全局协议列表整合所有网络协议按需初始化实现协议专属初始化安全隔离:错误处理机制确保资源安全释放高效扩展:函数指针+操作表支持灵活扩展这种设计完美体现了Unix的"机制与策略分离"哲学——内核提供协议注册框架(机制),各协议实现自己的操作函数(策略),共同构建了Linux强大的网络子系统。NULL);return 0;

数字人生 584

Linux内核--网络协议栈深入分析(四)--套接字内核初始化和创建过程

本文分析基于Linux Kernel 3.2.1 作者:闫明https://blog.youkuaiyun.com/geekcome/article/details/7984238 更多请查看专栏http://blog.youkuaiyun.com/column/details/linux-kernel-net.html 1、系统初始化过程中会调用sock_init函数进行套接字的初始化,主要是进行缓存的初始化 static int __init sock_init(void) { int err; ...

weixin_39094034的博客 673

Linux网络编程与内核数据包处理精要

本文深入探讨了Linux网络编程的核心概念和实践,包括创建IPv6 TCP套接字、使用epoll进行事件驱动、网络接口和IP配置、容器网络中的虚拟网络接口、桥接接口和VETH设备,以及内核中的数据包处理和Netfilter框架。文章通过实际代码示例和系统调用,向读者展示了网络编程和网络通信的复杂性,并探讨了在Linux环境下进行网络调试时可能遇到的问题和解决方案。

weixin_42452924的博客 422

应用防火墙原理

1. Netfilter Linux内核在Netfilter(下文简称NF)框架的基础上提供了IP Queue机制,使得基于用户态(User Mode)的防火墙开发成为可能。   内核中NF对网络报文的处理这里不做详细描述。假设读者已经熟悉NF的工作原理和工作流程。但这里还是要简单介绍一下NF中各个钩子(hook)函数对数据包处理的返回值,即该函数告诉内核对该数据包的处理意见。所有的返回值如

程序狗的成长之路 807

Python网络编程与Linux内核Crypto API实践

本文深入探讨了使用Python进行网络编程的高级技巧,特别是通过Linux内核Crypto API实现AES加密与解密的过程。文章首先介绍了使用sendfile进行文件传输的高效方法,随后详细讲解了如何利用AF_ALG进行AES-CBC和AES-GCM模式的加密与解密。此外,还涉及了性能比较,展示了AF_ALG与cryptography库在处理加密任务时的效率差异。

weixin_36204513的博客 339

Claude Code 接入 DeepSeek-V4 教程

本文介绍Claude Code的安装配置流程:1) 安装Node.js 18+和Git(Windows);2) 通过npm全局安装@anthropic-ai/claude-code;3) 配置DeepSeek API环境变量(需替换API Key);4) 启动Claude Code或配置VSCode插件。重点包括Linux/macOS和Windows的环境变量设置方法,以及VSCode插件的settings.json配置项。完成配置后即可在终端或VSCode中使用Claude Code功能。

培根芝士的专栏 2007

【回眸】Linux 内核 (十七) 之 网络编程

努力赶紧把Linux内核的内容更新完。

啤酒鸭的物联网开发记录博客 1201

四旋翼无人机控制算法实战:从PID到MPC的路径跟踪

本文深入探讨了四旋翼无人机控制算法的演进路径,从基础的PID控制到高级的MPC算法,详细解析了各类算法的原理、实现技巧及适用场景。通过实战案例对比分析,为开发者提供从算法选型到参数调优的全方位指导,助力提升无人机飞行控制的精度与稳定性。

weixin_29090917的博客 240

(持续更新)linux网络编程中需要注意的内核参数与网络机制

本篇博客会持续更新网络编程中经常遇到的内核参数调整和相关网络功能优化机制,一切以提高服务器性能为目的。具体参数调整,根据业务需求来。

我要出家当道士 1019

Ubuntu 24.04.3 LTS 安装 OpenClaw 图文版

OpenClow安装指南

qq_17204557的博客 2551

东方通TongWeb虚拟主机配置全流程:从创建到应用部署的保姆级教程

本文提供东方通TongWeb虚拟主机从创建、HTTP通道配置到应用部署的完整实战指南。重点解析虚拟主机的逻辑隔离价值、通道参数调优原理及企业级多应用部署架构,并涵盖常见报错排查与性能优化建议,旨在帮助运维和开发人员高效、稳定地完成应用系统部署。

oil88的博客 530

Linux内核开发五:Socket网络编程

1、Linux网络编程/套接字编程 现在大多数计算机网络都是根据TCP/IP协议栈搭建起来的,为了具体实现TCP/IP协议栈,Linux操作系统就对外提供了一套可供程序员做网络开发所用的操作接口,这就是Socket编程接口,这就是用来进行Linux网络编程项目开发的一些最基本的接口函数,比如socket、listen、connect、accept、send、read和write等等。总而言之,S

七色米的足迹 1262

LPDDR5 学习笔记01:功能概述

LPDDR5/LPDDR5X SDRAM设备采用8/16DQ通道架构,支持2Gb-32Gb容量。通过CK命令时钟和更快的WCK数据时钟协同工作,7个CA引脚传输控制信息,支持2:1或4:1的WCK:CK比例。设备提供BG/8B/16B三种存储模式选择,分别支持不同数据速率(最高8.533Gbps)和突发长度。采用0.5V VDDQ电源电压,实现高速双倍数据速率传输,并通过差分WCK时钟完成数据采样。读写采用突发访问机制,需先激活命令再执行读写操作。新型LPDDR5X仅支持BG和16B模式,优化了高速性能。

一只豌豆象的博客 2200

linux内核中使用inet_ntop,linux 网络编程 inet_pton & inet_ntop函数

#include int inet_pton(int family,const char * strptr,void * addrptr);返回:--成功, --输入不是有效的表达格式 , ---出错const char * inet_ntop(int family,const void * addrptr,char * strptr,size_t len);其中len =sizeof(* str...

weixin_42485891的博客 1172

Autosar诊断实战系列05-Dcm之0x27加解密服务配置开发详解

UDS 0x27服务实战开发介绍,对解锁尝试次数及delay时间进一步解释。

initiallizer的博客 1992

黑猫带你学eMMC协议第26篇:eMMC的硬件复位操作(H/W reset)

本文依据eMMC JEDEC5.1及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,禁止私自转载。 文章所在专栏:《黑猫带你学:eMMC协议详解》 1 概述 什么是硬件复位? 硬件复位就是使用一个特殊硬件信号(这个信号在RST_n这根实实在在的物理线路上传输)将emmc复位到pre-idle state,而不需要使用cmd0或者power cycle讲emmc复位到pre-idle state。 如何进行硬件复位? 1、设置RST_n_FUNCTION [162]为1,使emm.

黑猫奥利奥的博客 6316

linux内核构造skb发包-----raw、tcp网络编程

1. 内核raw发包 #include <linux/init.h>#include <linux/module.h> #include <linux/kernel.h>#include <linux/moduleparam.h>#include <linux/ip.h>#include <linux/version.h>#...

weixin_30802171的博客 573

uni-app开发-第6篇:uni-app 样式与主题

在 uni-app 开发中,样式与主题的管理是关键部分。样式分为全局样式和局部样式,全局样式在 App.vue 中定义,适用于整个应用,而局部样式在单个 .vue 文件中定义,仅对当前页面生效。局部样式优先级高于全局样式,内联样式优先级最高。样式属性如 font-family 和 color 可被子元素继承。要覆盖全局样式,可在局部样式中重新定义相同规则。主题切换通过 CSS 变量和类名实现,动态切换主题可通过修改元素的类名来完成。例如,定义浅色和深色主题,并通过按钮点击切换主题样式。这些方法帮助开发者灵活

无 369

Linux2.6内核epoll 网络编程

http://www.yuanma.org/data/2007/0321/article_2432.htm(1)导言:首 先,我强烈建议大家阅读Richard Stevens著作《TCP/IP Illustracted Volume 1,2,3》和《UNIX Network Programming Volume 1,2》。虽然他离开我们大家已经5年多了,但是他的书依然是进入网络编程的最直接的道

Jerry's Tech Notes Home 928

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【网络编程】Linux网络内核结构以及分布剖析

在Linux内核中,软中断(Soft Interrupt)是一种非硬件中断,它允许内核代码在不需要CPU硬件中断的情况下执行中断处理代码。软中断通常用于执行一些需要立即执行的任务,但不需要立即响应的场合,例如网络数据包的处理、磁盘I/O操作等。

weixin_73951752的博客 2362

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

linux tcp 截获 rawsocket,linux内核构造skb发包-----raw、tcp网络编程

1. 内核raw发包#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #...

weixin_39788451的博客 287

Linux网络编程 第16章 Linux内核中网络部分 结构

Linux网络编程 第16章 Linux内核中网络部分 结构 ...

ciq61956的博客 156

探秘Linux内核套接字,解锁网络编程技能

1.1套接字定义套接字最早是由BSD(伯克利软件套件)在1982年引入的通信机制,目前已被广泛移植到主流的操作系统中。对于应用开发人员来说,套接字(socket)是一个抽象层,是一种特殊的I/O接口,独立于具体协议的网络编程接口,也是一种文件描述符。应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。

深度Linux 1362
上一篇: Nessus漏洞扫描工具的使用
下一篇: Libpcap的介绍与简单使用
SouthAir_nx
博客等级 码龄8年 32粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值