一个FreeBSD下的通信协议监控程序

栏目: 服务器 · 发布时间: 6年前

内容简介:最近想要在一个内核里的小聊天服务器练手用,现在写完的内容,可以作为一个通讯协议的监控程序使用。

本文作者:rochek,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。

这是一个简易的通信协议的hook,可以在FreeBSD11上使用。

简介

最近想要在一个内核里的小聊天服务器练手用,现在写完的内容,可以作为一个通讯协议的监控程序使用。

这里是我的BLOG,如果这个项目有更新,应该会发在这里。

BLOG

使用Hux的模板做的Github pages,轻拍(捂脸)

使用的原理很简单,替换掉原有的协议处理函数,把协议载荷内的信息打印到dmesg内。

现在仅仅处理了UDP的内容。TCP,ICMP稍作改动应该也可以监控。

实现

1. 替换通信协议的处理函数

在FreeBSD内核内,通信协议的protosw是这样定义的。

ICMP

{
    .pr_type =		SOCK_RAW,
    .pr_domain =		&inetdomain,
    .pr_protocol =		IPPROTO_ICMP,
    .pr_flags =		PR_ATOMIC|PR_ADDR|PR_LASTHDR,
    .pr_input =		icmp_input,
    .pr_ctloutput =		rip_ctloutput,
    .pr_usrreqs =		&rip_usrreqs
},

TCP

{
    .pr_type =		SOCK_STREAM,
    .pr_domain =		&inetdomain,
    .pr_protocol =		IPPROTO_TCP,
    .pr_flags =		PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD,
    .pr_input =		tcp_input,
    .pr_ctlinput =		tcp_ctlinput,
    .pr_ctloutput =		tcp_ctloutput,
    .pr_init =		tcp_init,
    .pr_slowtimo =		tcp_slowtimo,
    .pr_drain =		tcp_drain,
    .pr_usrreqs =		&tcp_usrreqs
},

UDP

{
    .pr_type = 		SOCK_DGRAM,
    .pr_domain = 		&inetdomain,
    .pr_protocol = 		IPPROTO_UDP,
    .pr_flags = 		PR_ATOMIC|PR_ADDR,
    .pr_input = 		udp_input,
    .pr_ctlinput = 		udp_ctlinput,
    .pr_ctloutput = 	ip_ctloutput,
    .pr_init = 		udp_init,
    .pr_usrreqs = 		&udp_usrreqs
},

想要获取通信协议mbuf内的信息,替换掉pr_input即可。

一般的做法是在内核组件载入时,把通信协议对应的pr_input替换,在卸载时还原。

case MOD_LOAD:
        /* Replace udp_input with udp_input_hook. */
        inetsw[ip_protox[IPPROTO_UDP]].pr_input = udp_input_hook;
        break;

当然也可以写一个应用层控制程序,向这个内核模块发送某段数据时,内核模块才替换pr_input

2. 设计hook函数

作为hook时使用的函数,需要和原有函数相同的定义。

例如这样:

int
udp_input_hook(struct mbuf **mp, int *offp, int proto);

经过以上这两段,UDP报文的处理函数就直接进入了我们自定义的函数udp_input_hook()内了。

作为一个通信协议的处理函数,首先需要处理mbuf内UDP的信息确实存在。

if (iphlen > sizeof (struct ip)) {
	ip_stripoptions(m);
	iphlen = sizeof(struct ip);
}

为了保证之后有UDP信息,UDP头和IP头的长度一定要保证下来。

剩余长度不满足UDP头的话,接下来就没办法处理了,所以这里把数据返回。

ip = mtod(m, struct ip *);
if (m->m_len < iphlen + sizeof(struct udphdr)) {
	if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == NULL) {

		UDPSTAT_INC(udps_hdrops);
		return (IPPROTO_DONE);
	}
	ip = mtod(m, struct ip *);
}

然后,获取UDP头

uh = (struct udphdr *)((caddr_t)ip + iphlen);

可以从UDP头内获取一些所需要的信息,结合在上一步获取的ip头,就可以获取到这个包的具体信息了。

例如发送地址,源端口号,目的端口号等等。

可以把这些都记录下来,以后扩展功能用。

再剥离头

m->m_len -= iphlen + sizeof(struct udphdr);
m->m_data += iphlen + sizeof(struct udphdr);

这样,当前m_data的位置就是UDP数据区的位置了。

这个位置就是UDP载荷的信息。

之前说的,直接打印到dmesg内。

printf("load=%s \n", m->m_data);

为了让hook后的通讯协议正常工作,需要把mbuf返回给原来的处理函数。否则这个机器的UDP协议就没法正常运行了。

首先,需要恢复mbuf。

m->m_len += iphlen + sizeof(struct udphdr);
m->m_data -= iphlen + sizeof(struct udphdr);

再返回给原处理函数

return udp_input(mp, offp, proto);

后记

这样,就完成了把每次pr_input都打印出的一个内核协议的hook。

因为最后返回给了原有的协议栈,所以不影响原有处理功能。

这个hook,我原本的计划是作为一个非阻塞的recive_from(),稍作改动,可以做其它事情。

比如,接着写下去,做一个基于UDP的特殊协议等等。

本文作者:rochek,本文属 FreeBuf 原创奖励计划,未经许可禁止转载。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Twenty Lectures on Algorithmic Game Theory

Twenty Lectures on Algorithmic Game Theory

Tim Roughgarden / Cambridge University Press / 2016-8-31 / USD 34.99

Computer science and economics have engaged in a lively interaction over the past fifteen years, resulting in the new field of algorithmic game theory. Many problems that are central to modern compute......一起来看看 《Twenty Lectures on Algorithmic Game Theory》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具