利用tcpprobe思想和tracepoint利器可以做一个检测丢包工具
来源:互联网 发布:西安理工大学知行 编辑:程序博客网 时间:2024/05/17 08:11
如果从app的应用的角度来说,如果固定了端口和ip的话,就确定一条链路,那么可以通过tcpprobe分析数据的流向,也可以根据修改成自己的代码。当然看了tracepoint之后,我们知道可以做更多的事情了,完全可以定制自己的丢包检查工具,
假设我们实战在传屏中,那么可以做到,搞一个工具来给自己使用。这里需要了解网络基本知识,skb,struct udphdr之间的转换关系,同时需要了解在C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。接下里直接给源码,稍微分析下,这里考虑四种情况,丢包的情况,缓冲区满的情况,以及从udp入栈到qdsc失败的情况,以及打印缓冲满的时候的rx,tx mem的情况。
第一步:需要注册和释放的入口函数,如下,
static int __init trace_init(void){ int ret = 0; printk("\nMAJOR:%s port = %d\n",major,port); WARN_ON(register_trace_netif_rx(my_netif_rx,NULL)); ret = register_trace_sock_rcvqueue_full(my_sock_rcvqueue_full,NULL); WARN_ON(ret); ret = register_trace_skb_copy_datagram_iovec(my_skb_copy_datagram_iovec,NULL); WARN_ON(ret); ret = register_trace_udp_fail_queue_rcv_skb(my_udp_fail_queue_rcv_skb,NULL); WARN_ON(ret); ret = register_trace_sock_exceed_buf_limit(my_sock_exceed_buf_limit,NULL); WARN_ON(ret); return 0;}static void __exit trace_exit(void){ unregister_trace_netif_rx(my_netif_rx,NULL); unregister_trace_sock_rcvqueue_full(my_sock_rcvqueue_full,NULL); unregister_trace_skb_copy_datagram_iovec(my_skb_copy_datagram_iovec,NULL); unregister_trace_udp_fail_queue_rcv_skb(my_udp_fail_queue_rcv_skb,NULL); unregister_trace_sock_exceed_buf_limit(my_sock_exceed_buf_limit,NULL);}
接下来,需要结合kernel的代码来操作,因此
首先最简单的!从kernel的代码可以看出,在net/ipv4/udp.c找到trace_udp_fail_queue_rcv_skb,然后可以看出,
rc = ip_queue_rcv_skb(sk, skb);继续跟rc的返回值意义即可,
于是,也可以直接看kernel出错信息!
static void my_udp_fail_queue_rcv_skb(int rc,struct sock *sk){ if (rc == -ENOMEM) { printk("func[%s] rc = %d port = %d ENOMEM\n",__func__,rc,ntohs(inet_sk(sk)->inet_num)); } else if (rc == -ENOBUFS) { printk("func[%s] rc = %d port = %d ENOBUFS\n",__func__,rc,ntohs(inet_sk(sk)->inet_num)); }else printk("func[%s] rc = %d port = %d unkown reason\n",__func__,rc,ntohs(inet_sk(sk)->inet_num));}
第二:这里需要看下struct proto* prot;定义了很多tcp数据的参数。后面慢慢来分析!
static void my_sock_exceed_buf_limit(struct sock *sk,struct proto* prot,long allocated){ printk("func[%s] proto:%s sysctl_mem=%ld,%ld,%ld allocated=%ld sysctl_rmem=%d rmem_alloc=%d", __func__, prot->name, prot->sysctl_mem[0], prot->sysctl_mem[1], prot->sysctl_mem[2], allocated, prot->sysctl_rmem[0], atomic_read(&sk->sk_rmem_alloc));}
第三:和第二是一样的!
static void my_sock_rcvqueue_full(void struct sock *sk,struct sk_buff *skb){ printk("func[%s]rmem_alloc=%d size=%u rcvbuf=%d\n",__func__,atomic_read(&sk->sk_rmem_alloc),skb->truesize, sk->sk_rcvbuf);}
最后来分析,发包的核心处理函数,丢包是怎么样抓取和分析的!首先从驱动buffer里面拿数据,然后通过tcp协议栈给到用户空间。这里先看netif_rx吧,另外一个可以仿照写出来的。
根据上图rtp header的分布图,可以得到具体的信息,
static void my_netif_rx(void * ignore,struct sk_buff *skb){ unsigned char *sockBuf; unsigned short int port; static unsigned int rx_seqNo = 0,rx_oldSeqNo = 0; sockBuf = skb->data;//指向保存数据内容的首地址 //0x16 = 22 = ipheader (20) + udpheader->source_port len ===>udpheader->dest_port port = htons(*((unsigned short int *)(sockBuf + 0x16)));//偏移地址或者也可以直接先拿udp头部,然后拿dest_port也是一样的。 if (port == rtp_port) { //0x1e= 30 = ipheader(20) + udphead (8) + RTP header(2) ==>the RTP seqnum is 2BYTE offset //这里的偏移是根据头指针 rx_seqNo = ntohs(*((unsigned short int *)(sockBuf + 0x1E))); if (rx_seqNo != (rx_oldSeqNo + 1) && rx_seqNo != 0) { printk("func[%s] pre seqNo: %u current seqNo: %u lost num of seq: %u\n", __func__,rx_oldSeqNo, rx_seqNo, rx_seqNo-rx_oldSeqNo-1); } rx_oldSeqNo = rx_seqNo; }}
接下来,那就看看从协议栈到用户空间,就是一个拷贝而已!如下所示:
static void my_skb_copy_datagram_iovec(const struct sk_buff *skb ,int len) { struct udphdr *uh; char *sockBuf; static unsigned int seqNo = 0,oldSeqNo = 0; uh = udp_hdr(skb);//这里直接用函数来拿udp header if (ntohs(uh->dest) == rtp_port) { sockBuf = skb->data + sizeof (struct udphdr);//拿rtp header的指针 seqNo = (unsigned int)(sockBuf[2]<<8|sockBuf[3]);//这里有大小端的问题,需要看清楚 if (seqNo != (oldSeqNo + 1) && seqNo != 0) { printk("func[%s] pre seqNo: %u current seqNo: %u lost num of seq: %u\n", __func__,oldSeqNo, seqNo, seqNo-oldSeqNo-1); } oldSeqNo = seqNo; }}
这样就整体的架构完成了,
至于怎么样编译,还有如何串起来就不多说了!
最后一个有意思的问题就是,sockBuf是怎么样算出seqNo的,
稍微下了一个demo,如下:
#include<stdio.h>#include <malloc.h>#include <netinet/in.h>void main(){ struct rtp_hdr {/**//* byte 0 */ unsigned char csrc_len : 4; /**//* expect 0 */ unsigned char extension : 1; /**//* expect 1, see RTP_OP below */ unsigned char padding : 1; /**//* expect 0 */ unsigned char version : 2; /**//* expect 2 */ /**//* byte 1 */ unsigned char payload : 7; /**//* RTP_PAYLOAD_RTSP */ unsigned char marker : 1; /**//* expect 1 */ /**//* bytes 2, 3 */ unsigned short seq_no; /**//* bytes 4-7 */ unsigned long timestamp; /**//* bytes 8-11 */ unsigned long ssrc; /**//* stream number is used here. */ } *rtp_hdr_t; rtp_hdr_t = (struct rtp_hdr*)malloc(sizeof(struct rtp_hdr)); rtp_hdr_t->seq_no=htons(3000); //rtp_hdr_t->version = 2; printf("p->seq = %u\n",rtp_hdr_t->seq_no); char *data = rtp_hdr_t; printf("p->seq = %u\n",ntohs(rtp_hdr_t->seq_no)); printf("data[2]<<8|data[3]) = %u\n",ntohs(data[2]<<8|data[3])); printf("data[2]<<8) = %u\n",data[2]<<8); printf("data[3]) = %u\n",data[3]); printf("data address = %u\n",&data); printf("rtp_hdr_t address = %u\n",&rtp_hdr_t); printf("unsigned int = %u\n",sizeof(unsigned int)); printf("char = %u\n",sizeof(char)); int i = 1; i = i<<2; printf("i=%d\n",i);}发现一个问题,当seq当一定值的时候,会出现打印不对的情况
这是在用户空间的结果,进一步验证需要到内核空间实施!
靠,终于抓包看懂了网上的这个分析手法,差点绕进去了,我晕!
像上面的udp rtp 包所示,根据rtp header的分析
可以知道,18 9f是SequenceNum,
肯定按照平常的算法不就是18是高位,9f是低位,然后18左移8位加上9f不就是
序列号吗?
因此写成了:SockData[2]<<8|SockData[3]
so easy!
- 利用tcpprobe思想和tracepoint利器可以做一个检测丢包工具
- 利器 -- 抓包工具总结
- 丢包工具
- 网络丢包工具network emulator和tc使用
- virtualPT, 一个IEC61850抓包工具
- 抓包工具:tcpdump和wireshark
- iphone真机上抓包和抓包工具汇总
- 抓包工具之:Wireshark 和 RawCap
- App和Web抓包工具
- 抓包工具fiddler安装和使用
- 抓包工具原理和使用
- 抓包工具Fiddler 检测后台接口数据
- 如何利用抓包工具Fiddler抓取手机的包?
- 利用tcpdump抓包工具监控TCP连接的三次握手和断开连接的四次挥手
- 近来发现一个很好用的抓包工具,HttpWatch
- Wireshark 网络抓包工具介绍、应用及一个案例
- 手写一个Pcap捕包工具及性能优化
- 弃掉Fidder换一个抓包工具
- 剑指offer-矩阵中的路径
- 第一个jquery插件(jquery插件)
- selenium自动化测试
- openjudge1807正方形题解
- 【SignalR学习系列】4. SignalR广播程序
- 利用tcpprobe思想和tracepoint利器可以做一个检测丢包工具
- 中点的规律
- AVL_WORKSPACE_SUITE_
- Access denied for user 'root'@'localhost' (using password:YES) 解决方法
- 卡片游戏
- js模块化进程
- 最长公共子序列Lcs (dp)
- 将虚拟机ubuntu里的代码共享到window端进行编辑采用samba服务
- leetcode 205. Isomorphic Strings | str中字母计数(dict)与定位