内核实现PPTP数据通道

2023-12-19 10:48

本文主要是介绍内核实现PPTP数据通道,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

开源的PPTP程序pptp-1.4.0未使用内核的新版PPTP驱动程序,如文章所述。结果是,所有的GRE报文都要上送到应用层pptpd处理,处理完GRE报头之后,还需要通过ppp的tty驱动送入内核的PPP系统处理内层的ppp数据部分。内核新增加的PPTP模块把GRE的处理放到了内核驱动中,不再上送PPTPD应用。并且,内核PPTP驱动提供了一个新的协议族套接口AF_PPPOX,可以为上层应用使用提供接收GRE报文,另外也可以对接内核PPP系统,作为内核PPP的通道使用。


PPTP-GRE套接口的建立

根据PPTP协议在PPTP的协商完成之后,开始交互PPTP-GRE的报文。所以在此之前,pptpd服务端程序需要创建一个PPPOX的套接口,以便接收PPTP-GRE报文,同时需要绑定本地的call id和地址,用于区分不同的pptp连接。pptp_bind函数处理套接口绑定请求,套接口地址需要指定为sockaddr_pppox类型,每个call id对应一个内核中的通道channel,故绑定函数主要是添加一个channel,如果用户未指定call id,内核将自动选择一个未使用的id,最大支持65535个id值。参见函数add_chan,内核将call id与套接口的对应关系保存在全局变量callid_sock中。之后将此套接口的状态更改为已绑定PPPOX_BOUND。

static int add_chan(struct pppox_sock *sock, struct pptp_addr *sa)
{if (!sa->call_id)   {call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, call_id + 1);if (call_id == MAX_CALLID) {call_id = find_next_zero_bit(callid_bitmap, MAX_CALLID, 1);}sa->call_id = call_id;} else if (test_bit(sa->call_id, callid_bitmap)) {goto out_err;}sock->proto.pptp.src_addr = *sa;set_bit(sa->call_id, callid_bitmap);rcu_assign_pointer(callid_sock[sa->call_id], sock);
}

另外此套接口还需要绑定一个远端通信的PPPOX套接口地址,以及作为一个通道channel注册自身到内核的PPP系统中。具体由pptp_connect函数实现。用户层需要在connect函数中提供远端的地址(sockaddr_pppox),并且初始化通道参数,比如操作集ops、头部长度hdrlen等,调用ppp_register_channel函数进行注册。

static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags)
{po->chan.private = sk;po->chan.ops = &pptp_chan_ops;po->chan.hdrlen = 2 + sizeof(struct pptp_gre_header);error = ppp_register_channel(&po->chan);opt->dst_addr = sp->sa_addr.pptp;sk->sk_state |= PPPOX_CONNECTED;
}

PPTP-GRE通道提供给内核PPP系统发送和ioctl控制的函数接口,注册完成之后ppp报文即可由此通道发送。至此,PPTP-GRE套接口的初始化工作完成。

static const struct ppp_channel_ops pptp_chan_ops = {.start_xmit = pptp_xmit,.ioctl      = pptp_ppp_ioctl,
};

GRE-PPP报文接收

内核使用gre_add_protocol注册了PPTP GRE的接收函数pptp_rcv,注意第二个参数GREPROTO_PPTP,其值为1,即为GRE协议的版本1。版本0为通用的CISCO定义的GRE标准。这样在数据包由IP层向四层协议提交时,根据协议号47(GRE)和版本1,将会进入到pptp_rcv函数处理。

static const struct gre_protocol gre_pptp_protocol = {.handler = pptp_rcv,
};
static int __init pptp_init_module(void)
{err = gre_add_protocol(&gre_pptp_protocol, GREPROTO_PPTP);
}

pptp_rcv函数查找是否有AF_PPPOX套接口在监听此数据包中指定的call_id,判断数据包的源地址是否与套接口中指定目的地址相同,此两个条件已经在套接口初始化中分别使用bind和connect系统调用完成,找到之后,将数据包送往此套接口,通过sk_receive_skb调用接收。条件判断在函数lookup_chan中实现。

static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr)
{struct pppox_sock *sock;struct pptp_opt *opt;sock = rcu_dereference(callid_sock[call_id]);if (sock) {opt = &sock->proto.pptp;if (opt->dst_addr.sin_addr.s_addr != s_addr)sock = NULL;elsesock_hold(sk_pppox(sock));}return sock;
}

在套接口创建(pptp_create)时,内核注册了套接口接收函数(pptp_rcv_core),此函数负责处理GRE头部信息字段,之后将数据包送往内核ppp系统进行处理,即ppp_input函数。

static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
{ppp_input(&po->chan, skb);return NET_RX_SUCCESS;
}

对于PPP协商报文,由内核PPP系统与用户层PPPD交互处理,对于PPP数据可直接在内核的PPP系统中处理完成,最终通过PPTP通道注册的start_xmit回调函数(pptp_xmit)发出,ppto_xmit对数据包封装GRE头部信息和IP头部信息,调用ip_local_out发往对端。
以上可见,PPTP-GRE数据包的处理不再需要到用户层pptpd进行处理,通过PPTP套接口驱动在内核中就可处理,以及与内核PPP系统进行直接的交互。PPTP-GRE数据流程如下:

                                      +--------------+|              ||  USER PPPD   ||              |+--------------+|  ||  |+----------------+      +--------------+IN ---->|                |----->|              ||   KERNEL PPTP  |      |  KERNEL PPP  |OUT <----|                |<-----|              |+----------------+      +--------------+

 

内核版本

Linux-4.15

 

这篇关于内核实现PPTP数据通道的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/511890

相关文章

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

Python中bisect_left 函数实现高效插入与有序列表管理

《Python中bisect_left函数实现高效插入与有序列表管理》Python的bisect_left函数通过二分查找高效定位有序列表插入位置,与bisect_right的区别在于处理重复元素时... 目录一、bisect_left 基本介绍1.1 函数定义1.2 核心功能二、bisect_left 与

VSCode设置python SDK路径的实现步骤

《VSCode设置pythonSDK路径的实现步骤》本文主要介绍了VSCode设置pythonSDK路径的实现步骤,包括命令面板切换、settings.json配置、环境变量及虚拟环境处理,具有一定... 目录一、通过命令面板快速切换(推荐方法)二、通过 settings.json 配置(项目级/全局)三、

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

C/C++中OpenCV 矩阵运算的实现

《C/C++中OpenCV矩阵运算的实现》本文主要介绍了C/C++中OpenCV矩阵运算的实现,包括基本算术运算(标量与矩阵)、矩阵乘法、转置、逆矩阵、行列式、迹、范数等操作,感兴趣的可以了解一下... 目录矩阵的创建与初始化创建矩阵访问矩阵元素基本的算术运算 ➕➖✖️➗矩阵与标量运算矩阵与矩阵运算 (逐元

C/C++的OpenCV 进行图像梯度提取的几种实现

《C/C++的OpenCV进行图像梯度提取的几种实现》本文主要介绍了C/C++的OpenCV进行图像梯度提取的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录预www.chinasem.cn备知识1. 图像加载与预处理2. Sobel 算子计算 X 和 Y

C/C++和OpenCV实现调用摄像头

《C/C++和OpenCV实现调用摄像头》本文主要介绍了C/C++和OpenCV实现调用摄像头,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录准备工作1. 打开摄像头2. 读取视频帧3. 显示视频帧4. 释放资源5. 获取和设置摄像头属性