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. ...
随机推荐
- Anaconda安装和使用 akshare获取股票数据
介绍 Anaconda是开源的Python包管理器.既是Python各种库的大礼包集合,特别是数据分析和科学计算方面的库都预装了,也是一个能创建虚拟机环境的工具. 我为什么安装 我安装它的原因不是科学 ...
- 远程IO
远程io 远程io ZLAN6842,ZLAN6844是8路远程O控制器.含有8路DI.8路DO,8路AI输入.其中DI支持干节点和湿节点,带光耦隔离:DO为继电器输出,具有5A 250VAC或5A ...
- python 字典使用——增删改查
创建字典 dict= {key1 : value1, key2 : value2 } key : value 为键值对 增: dict[key] = value 删: del dict[key] 改: ...
- 0基础如何更快速入门Linux系统?学完Linux有哪些就业方向?
Linux系统是使用Linux内核及开源自由软件组成的一套操作系统,是一种类UNIX系统,其内核在1991年10月5日由林纳斯·托瓦兹首次发布. 它的主要特性:Linux文件一切皆文件.完全开源免费. ...
- Socket编程,C语言版!
socket编程--send函数&recv函数详解 一.send函数 ✍ 函数原型: int send( SOCKET s,char *buf,int len,int flags ); ✍ 功 ...
- 第二章 rsync服务原理
一.备份 1.什么是备份? 1)把重要的数据或者文件再次复制一份并保存下来 2.为什么要做备份? 1)数据的重要性 2)为了出现故障,恢复数据 3.能不能不备份? 1)重要的数据一定要备份 2)不重要 ...
- Linux用户和组管理命令-切换用户su
切换用户或以其他用户身份执行命令 su: 即 switch user,命令可以切换用户身份,并且以指定用户的身份执行命令 格式: su [options...] [-] [user [args...] ...
- bootstrapvalidator常用验证解析和使用
学这个博主的:https://www.cnblogs.com/wang-kai-xuan/p/11031733.html BootStrapValidator表单验证插件的学习和使用 引入标签 ...
- ssm整合所用全部依赖pom.xml(idea版)
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven ...
- Phoenix创建索引源码过程
date: 2020-09-27 13:50:00 updated: 2020-09-28 16:30:00 Phoenix创建索引源码过程 org.apache.phoenix.index.Inde ...