写这篇文章,我只是为了嘲笑我自己。我其实什么都不懂。
我总是觉得sk_buff的API太难用了,但也可能是我不会用才会这么认为…无论如何,我一直不喜欢这个东西。
为了证明TCP/UDP的校验和的脆弱性,我想演示一个效果,即交换一下TCP的payload的两个uint16值,它的校验和保持不变,所以我们必须为每一个文件单独再生成一个MD5文件。
我的代码是这么写的:
unsigned short *pstart, tmp;
...
tcph = (struct tcphdr *)((char *)iph + iph->ihl*4);
pstart = (unsigned short *)((char *)tcph + tcph->doff*4);
tmp = *pstart;
*pstart = *(pstart + 1);
*(pstart + 1) = tmp;
如果这个代码部署在中间节点,那OK没问题,但是如果是部署在数据的发送节点,那么pstart并没有指向payload的开始处。发生了什么?
我在玩TCP换头操作的时候,曾经天真的用memmove来操作skb(一下一个panic),我当然懂memmove的细节,知道它如何处理overlap,但我不知道skb的data可能并不连续。
难道一个skb表示的数据包还能不连续?数据上看,协议头和payload显然是分离的。
如果不了解skb的运作方式,还真的想不通,但事实上这就是不用API,非要裸操作skb的后果。
下面的代码无论部署在哪里都是对的:
pstart = (unsigned short *)((char *)tcph + tcph->doff*4);
if (skb_shinfo(skb)->nr_frags) {
skb_frag_t *frag;
frag = &skb_shinfo(skb)->frags[0];
buf = kmap_atomic(skb_frag_page(frag));
} else {
buf = (char *)((char *)tcph + tcph->doff*4);
}
pstart = (unsigned short *)buf;
tmp = *pstart;
*pstart = *(pstart + 1);
*(pstart + 1) = tmp;
if (skb_shinfo(skb)->nr_frags) {
kunmap_atomic(buf);
}
很简单是吧, 要用skb的frag而不是直接假设skb的data是连续的!
这里面的理由很容易理解。
当我们调用send的时候,其参数是一个用户态的buffer,当这个buffer需要被封装到skb中被发出的时候,一般的理解是协议栈会将这个buffer拷贝到内核态为skb分配的data中,这明显是低效的。显然正确的做法是直接将skb的payload指针指向这块用户态的buffer,这就是本地始发的数据包为什么使用frag的原因。
sk_buff非常复杂,你会用下面这些API吗:
- skb_reset_network_header
- skb_set_transport_header
- pskb_expand_head
- …
反正我一直都不会用,所以我一般裸操作skb的各个字段,我自诩太了解skb了,所以当我要修改数据报文payload的时候,我会选择手工设置skb的各个字段,直到我碰到了frag,frag_list,gro,gso…
涉及到GSO/GRO就更加复杂了。
我承认我最害怕天黑,我承认我错了,我会努力学习API的用法而不是炫技秀花活儿,这些花活儿显然没有任何意义。
幸运的是,经过两周的历练,我已经会用这些API了。
浙江温州皮鞋湿,下雨进水不会胖。
原文链接: https://blog.csdn.net/dog250/article/details/113717771
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
![请不要裸操作sk_buff,请使用sk_buff的API_怎么看buff上有没有设置api](https://www.ccppcoding.com/wp-content/themes/justnews/themer/assets/images/lazy.png)
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/405777
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!