PF_PACKET&&tcpdump
linux下抓包原理
linux下的抓包是通过注册一种虚拟的底层网络协议来完成对网络设备消息的处理权。当网卡接收到一个网络报文之后,它会遍历系统中所有已经注册的网络协议,当抓包模块把自己伪装成一个网络协议的时候,系统在收到报文的时候就会给这个伪协议一次机会,让它来对网卡收到的报文进行一次处理,此时该模块就会趁机对报文进行窥探,也就是把这个报文完完整整的复制一份,假装是自己接收到的报文,汇报给抓包模块
在驱动收到报文送往内核协议栈时,会经过pty_all协议钩子 处理分析报文,
static int __netif_receive_skb(struct sk_buff *skb)
{
---------------------------------------------------------
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
----------------------------------------------
}
对应的注册为:
/*******************************************************************************
Protocol management and registration routines
*******************************************************************************/
/*
* Add a protocol ID to the list. Now that the input handler is
* smarter we can dispense with all the messy stuff that used to be
* here.
*
* BEWARE!!! Protocol handlers, mangling input packets,
* MUST BE last in hash buckets and checking protocol handlers
* MUST start from promiscuous ptype_all chain in net_bh.
* It is true now, do not change it.
* Explanation follows: if protocol handler, mangling packet, will
* be the first on list, it is not able to sense, that packet
* is cloned and should be copied-on-write, so that it will
* change it and subsequent readers will get broken packet.
* --ANK (980803)
*/
static inline struct list_head *ptype_head(const struct packet_type *pt)
{
if (pt->type == htons(ETH_P_ALL))
return &ptype_all;
else
return &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
}
/**
* dev_add_pack - add packet handler
* @pt: packet type declaration
*
* Add a protocol handler to the networking stack. The passed &packet_type
* is linked into kernel lists and may not be freed until it has been
* removed from the kernel lists.
*
* This call does not sleep therefore it can not
* guarantee all CPU's that are in middle of receiving packets
* will see the new packet type (until the next received packet).
*/
void dev_add_pack(struct packet_type *pt)
{
struct list_head *head = ptype_head(pt);
spin_lock(&ptype_lock);
list_add_rcu(&pt->list, head);
spin_unlock(&ptype_lock);
}
可知tcpdump 收报就是只注册一个pty_all的伪协议钩子处理数据报文;
1.先创建socket,内核dev_add_packet()挂上自己的钩子函数
2.然后在钩子函数中,把skb放到自己的接收队列中,
3.接着系统调用recv取出skb来,把数据包skb->data拷贝到用户空间
4.最后关闭socket,内核dev_remove_packet()删除自己的钩子函数
static int __init packet_init(void)
{
int rc = proto_register(&packet_proto, 0); if (rc != 0)
goto out; sock_register(&packet_family_ops);//注册pf_packet 类型create socket回调钩子
register_pernet_subsys(&packet_net_ops);
register_netdevice_notifier(&packet_netdev_notifier);
out:
return rc;
} static const struct proto_ops packet_ops = {
.family = PF_PACKET,
.owner = THIS_MODULE,
.release = packet_release,
.bind = packet_bind,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = packet_getname,
.poll = packet_poll,
.ioctl = packet_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = packet_setsockopt,
.getsockopt = packet_getsockopt,
.sendmsg = packet_sendmsg,
.recvmsg = packet_recvmsg,
.mmap = packet_mmap,
.sendpage = sock_no_sendpage,
}; static const struct net_proto_family packet_family_ops = {
.family = PF_PACKET,
.create = packet_create,
.owner = THIS_MODULE,
};
根据socket 系统调用可知:
scoket(pf_packet,.......)会调用packet_create,Create a packet of type SOCK_PACKET.
socket PF_PACKET目前有两种工作模式,以(SOCK_PACKET)类别运行的模式;和以(SOCK_DGRAM/SOCK_RAW)类别运行的模式。
前者为传统的方式,在内核和用户层拷贝数据包,并且兼容老内核的数据包抓取接口(参考以下介绍);后者为前者的替代类型,正常情况下 通过packet_rcv 处理伪二层报文,
而且可以通过设置共享内存的方式,在内核与用户层交换数据,节省内存拷贝的消耗
/*
* Create a packet of type SOCK_PACKET.
*/ static int packet_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
struct packet_sock *po;
__be16 proto = (__force __be16)protocol; /* weird, but documented */
int err; if (!capable(CAP_NET_RAW))
return -EPERM;
// type 必须是使用 raw/dgam or packet(比较老的版本 一般不使用)
if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW &&
sock->type != SOCK_PACKET)
return -ESOCKTNOSUPPORT; sock->state = SS_UNCONNECTED; err = -ENOBUFS;
sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto);
if (sk == NULL)
goto out; sock->ops = &packet_ops;
if (sock->type == SOCK_PACKET)//一般不使用
sock->ops = &packet_ops_spkt; sock_init_data(sock, sk); po = pkt_sk(sk);
sk->sk_family = PF_PACKET;
po->num = proto; sk->sk_destruct = packet_sock_destruct;
sk_refcnt_debug_inc(sk); /*
* Attach a protocol block
*/ spin_lock_init(&po->bind_lock);
mutex_init(&po->pg_vec_lock);
po->prot_hook.func = packet_rcv;// 在__netif_receive_skb 处理报文的时候, 会调用prot_hook.func 也就是 packet_rcv 处理 if (sock->type == SOCK_PACKET)
po->prot_hook.func = packet_rcv_spkt; po->prot_hook.af_packet_priv = sk; if (proto) {
po->prot_hook.type = proto;
register_prot_hook(sk);//挂载对应的proto 链表上 在netif_recvive在中遍历处理
} spin_lock_bh(&net->packet.sklist_lock);
sk_add_node_rcu(sk, &net->packet.sklist);
sock_prot_inuse_add(net, &packet_proto, 1);
spin_unlock_bh(&net->packet.sklist_lock); return 0;
out:
return err;
可以知道packet_rcv 只是简单的处理二层报文,并且挂载到socket的收包队列上,然后唤醒对应的等待进程
/*
* This function makes lazy skb cloning in hope that most of packets
* are discarded by BPF.
*
* Note tricky part: we DO mangle shared skb! skb->data, skb->len
* and skb->cb are mangled. It works because (and until) packets
* falling here are owned by current CPU. Output packets are cloned
* by dev_queue_xmit_nit(), input packets are processed by net_bh
* sequencially, so that if we return skb to original state on exit,
* we will not harm anyone.
*/ static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
struct sock *sk;
struct sockaddr_ll *sll;
struct packet_sock *po;
u8 *skb_head = skb->data;
int skb_len = skb->len;
unsigned int snaplen, res; if (skb->pkt_type == PACKET_LOOPBACK)
goto drop; sk = pt->af_packet_priv;
po = pkt_sk(sk);
printk("skb dev name:%s dev_name:%s ptype:%x\n", skb->dev->name, dev->name, pt->type);
if (!net_eq(dev_net(dev), sock_net(sk)))
goto drop; skb->dev = dev; if (dev->header_ops) {
/* The device has an explicit notion of ll header,
* exported to higher levels.
*
* Otherwise, the device hides details of its frame
* structure, so that corresponding packet head is
* never delivered to user.
*/
if (sk->sk_type != SOCK_DGRAM)
skb_push(skb, skb->data - skb_mac_header(skb));
else if (skb->pkt_type == PACKET_OUTGOING) {
/* Special case: outgoing packets have ll header at head */
skb_pull(skb, skb_network_offset(skb));
}
} snaplen = skb->len; res = run_filter(skb, sk, snaplen);//执行filter
if (!res)
goto drop_n_restore;
if (snaplen > res)
snaplen = res; if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
goto drop_n_acct; if (skb_shared(skb)) {
struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);
if (nskb == NULL)
goto drop_n_acct; if (skb_head != skb->data) {
skb->data = skb_head;
skb->len = skb_len;
}
consume_skb(skb);
skb = nskb;
} BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 >
sizeof(skb->cb));
//读取控制信息
sll = &PACKET_SKB_CB(skb)->sa.ll;
sll->sll_family = AF_PACKET;
sll->sll_hatype = dev->type;
sll->sll_protocol = skb->protocol;
sll->sll_pkttype = skb->pkt_type;
if (unlikely(po->origdev))
sll->sll_ifindex = orig_dev->ifindex;
else
sll->sll_ifindex = dev->ifindex; sll->sll_halen = dev_parse_header(skb, sll->sll_addr); PACKET_SKB_CB(skb)->origlen = skb->len; if (pskb_trim(skb, snaplen))
goto drop_n_acct; skb_set_owner_r(skb, sk);
skb->dev = NULL;
skb_dst_drop(skb); /* drop conntrack reference */
nf_reset(skb); spin_lock(&sk->sk_receive_queue.lock);
po->stats.tp_packets++;
skb->dropcount = atomic_read(&sk->sk_drops);
__skb_queue_tail(&sk->sk_receive_queue, skb); //放在收报队列
spin_unlock(&sk->sk_receive_queue.lock);
sk->sk_data_ready(sk, skb->len);//唤醒等待进程
return 0; drop_n_acct:
spin_lock(&sk->sk_receive_queue.lock);
po->stats.tp_drops++;
atomic_inc(&sk->sk_drops);
spin_unlock(&sk->sk_receive_queue.lock); drop_n_restore:
if (skb_head != skb->data && skb_shared(skb)) {
skb->data = skb_head;
skb->len = skb_len;
}
drop:
consume_skb(skb);
return 0;
}
PF_PACKET&&tcpdump的更多相关文章
- tcpdump 实现原理【整理】
参考:http://blog.sina.com.cn/s/blog_523491650101au7f.html 一.tcpdump 对于本机中进程的系统行为调用跟踪,strace是一个很好的工具,而在 ...
- 如何利用tcpdump对mysql进行抓包操作
命令如下: tcpdump -s -l -w - dst -i eno16777736 |strings 其中-i指定监听的网络接口,在RHEL 7下,网络接口名不再是之前的eth0,而是 eno16 ...
- 运维之网络安全抓包—— WireShark 和 tcpdump
------------------------------------------------本文章只解释抓包工具的捕获器和过滤器的说明,以及简单使用,应付日常而已----------------- ...
- tcpdump、nc网络工具使用
tcpdump: 网络嗅探器 nc: nmap: 端口扫描 混杂模式(promisc) C设置为监控,当A和B通信,C是无法探测到数据的,除非有交换机的权限,将全网端口的数据通信都发送副本到C的端口上 ...
- 【Network】TCPDUMP 详解
参考资料: https://www.baidu.com/s?ie=UTF-8&wd=tcpdump%20%E6%8C%87%E5%AE%9Aip tcpdump非常实用的抓包实例: http ...
- tcpdump抓取HTTP包
tcpdump抓取HTTP包 tcpdump -XvvennSs 0 -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x4854 0x4745为"GET&quo ...
- 在php中使用strace、gdb、tcpdump调试工具
[转] http://www.syyong.com/php/Using-strace-GDB-and-tcpdump-debugging-tools-in-PHP.html 在php中我们最常使用调试 ...
- tcpdump
tcpdump tcp -i eth1 -t -s -c and dst port ! and src net -w ./target.cap (1)tcp: ip icmp arp rarp 和 t ...
- tcpdump的简单使用
tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析 1.tcpdump host 192.168.8.49 获取主机192.168.8.49接收到和发出的所有分组 2. ...
随机推荐
- 详解工程师不可不会的LRU缓存淘汰算法
大家好,欢迎大家来到算法数据结构专题,今天我们和大家聊一个非常常用的算法,叫做LRU. LRU的英文全称是Least Recently Used,也即最不经常使用.我们看着好像挺迷糊的,其实这个含义要 ...
- 多测师讲解接口测试 _HTTP常见的状态码归纳_高级讲师肖sir
100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分 101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议 200 OK 一切正常,对 ...
- JVM 第六篇:极致优化 IDEA 启动速度
本文内容过于硬核,建议有 Java 相关经验人士阅读. 1. 引言 相信做 Java 开发的同学,对 IDEA 这个工具应该都不陌生,即使不使用 IDEA 做开发,那么对 Eclipse 这个工具应该 ...
- 阿里百秀后台管理项目笔记 ---- Day01
摘要 在此记录一下阿里百秀项目的教学视频的学习笔记,部分页面被我修改了,某些页面效果会不一样,基本操作是一致的,好记性不如烂笔头,加油叭!!! step 1 : 整合全部静态页面 将静态页面全部拷贝到 ...
- day25 Pyhton学习 MD5加密.日志
一.MD5加密 MD5是一种不可逆的加密算法. 它是可靠的. 并且安全的. 在python中我们不需要手写这一套算法. 只需要引入一个叫hashlib的模块就能搞定MD5的加密工作 import ha ...
- Golang 随机生成中国人姓名
package main import ( "fmt" "math/rand" "time" ) var lastName = []stri ...
- java内存模型深入理解
作为一个java程序员 jvm 虚拟机应该是最先接触的了,但是当初由于理解能力有限一直没搞明白是怎么回事,而是将他理解为运行java程序的环境,不过这也没错.但是随着工作时间的增加开始思考jvm里面工 ...
- JAVA递归实现线索化二叉树
JAVA递归实现线索化二叉树 基础理论 首先,二叉树递归遍历分为先序遍历.中序遍历和后序遍历. 先序遍历为:根节点+左子树+右子树 中序遍历为:左子树+根节点+右子树 后序遍历为:左子树+右子树+根节 ...
- proto文件生成对应的js和ts文件
安装protobuf 先要安装node.js,然后用npm安装protobuf npm install -g protobufjs 生成js文件 单个文件 pbjs -t static-module ...
- Sec-Fetch-*请求头,了解下?
如果你使用76+版本的chrome浏览器,通过开发者面板查看每个网络请求,会发现都有几个Sec-Fetch开头的请求头,例如访问百度首页https://www.baidu.com/的请求: Sec-F ...