内核实现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

相关文章

C#实现将Excel表格转换为图片(JPG/ PNG)

《C#实现将Excel表格转换为图片(JPG/PNG)》Excel表格可能会因为不同设备或字体缺失等问题,导致格式错乱或数据显示异常,转换为图片后,能确保数据的排版等保持一致,下面我们看看如何使用C... 目录通过C# 转换Excel工作表到图片通过C# 转换指定单元格区域到图片知识扩展C# 将 Excel

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

Qt中QGroupBox控件的实现

《Qt中QGroupBox控件的实现》QGroupBox是Qt框架中一个非常有用的控件,它主要用于组织和管理一组相关的控件,本文主要介绍了Qt中QGroupBox控件的实现,具有一定的参考价值,感兴趣... 目录引言一、基本属性二、常用方法2.1 构造函数 2.2 设置标题2.3 设置复选框模式2.4 是否

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法

《springboot整合阿里云百炼DeepSeek实现sse流式打印的操作方法》:本文主要介绍springboot整合阿里云百炼DeepSeek实现sse流式打印,本文给大家介绍的非常详细,对大... 目录1.开通阿里云百炼,获取到key2.新建SpringBoot项目3.工具类4.启动类5.测试类6.测

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

SpringBoot集成Milvus实现数据增删改查功能

《SpringBoot集成Milvus实现数据增删改查功能》milvus支持的语言比较多,支持python,Java,Go,node等开发语言,本文主要介绍如何使用Java语言,采用springboo... 目录1、Milvus基本概念2、添加maven依赖3、配置yml文件4、创建MilvusClient

JS+HTML实现在线图片水印添加工具

《JS+HTML实现在线图片水印添加工具》在社交媒体和内容创作日益频繁的今天,如何保护原创内容、展示品牌身份成了一个不得不面对的问题,本文将实现一个完全基于HTML+CSS构建的现代化图片水印在线工具... 目录概述功能亮点使用方法技术解析延伸思考运行效果项目源码下载总结概述在社交媒体和内容创作日益频繁的