一、 lipcap回调注册
在libpcap中,最重要的就是打开接口,其中关键函数为pcap_activate
。这里只关注Linux平台。
只分析通用平台。
pcap_t *
pcap_create(const char *device, char *errbuf)
{
...
p = pcap_create_interface(device_str, errbuf);
...
}
// pcap-linux.c
pcap_t *
pcap_create_interface(const char *device, char *ebuf)
{pcap_t *handle;handle = pcap_create_common(ebuf, sizeof (struct pcap_linux));if (handle == NULL)return NULL;handle->activate_op = pcap_activate_linux;...
二、libpcap激活接口 - step1
int
pcap_activate(pcap_t *p)
{
...
status = p->activate_op(p);
...
}
static int
pcap_activate_linux(pcap_t *handle)
{...handle->inject_op = pcap_inject_linux;handle->setfilter_op = pcap_setfilter_linux;handle->setdirection_op = pcap_setdirection_linux;handle->set_datalink_op = pcap_set_datalink_linux;handle->getnonblock_op = pcap_getnonblock_fd;handle->setnonblock_op = pcap_setnonblock_fd;handle->cleanup_op = pcap_cleanup_linux;handle->read_op = pcap_read_linux;handle->stats_op = pcap_stats_linux;...ret = activate_new(handle);
static int
activate_new(pcap_t *handle)
{
.../** Open a socket with protocol family packet. If the* "any" device was specified, we open a SOCK_DGRAM* socket for the cooked interface, otherwise we first* try a SOCK_RAW socket for the raw interface.*/sock_fd = is_any_device ?socket(PF_PACKET, SOCK_DGRAM, protocol) :socket(PF_PACKET, SOCK_RAW, protocol);
只考虑抓取某个特定网卡的流量,因此 socket_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
三、进入kernel 创建socket
int sock_create(int family, int type, int protocol, struct socket **res)
{return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}int __sock_create(struct net *net, int family, int type, int protocol,struct socket **res, int kern)
{int err;struct socket *sock;const struct net_proto_family *pf;.../** Allocate the socket and allow the family to set things up. if* the protocol is 0, the family is instructed to select an appropriate* default.*/sock = sock_alloc();...sock->type = type;...pf = rcu_dereference(net_families[family]);
...err = pf->create(net, sock, protocol, kern);
...*res = sock;return 0;
...
}
首先根据family获取对应的协议族处理函数,然后通过回调create函数进行实际的创建
这里create的回调为packet_create
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;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;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);}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;
}
这里关键是设置了po->prot_hook.func = packet_rcv;
, 后续处理数据就是这个入口
以及register_prot_hook
将回调注册到ptype_all链表中
static void register_prot_hook(struct sock *sk)
{struct packet_sock *po = pkt_sk(sk);if (!po->running) {...dev_add_pack(&po->prot_hook);...po->running = 1;}
}
// net/core/dev.c
static inline struct list_head *ptype_head(const struct packet_type *pt)
{if (pt->type == htons(ETH_P_ALL))return &ptype_all;elsereturn &ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
}
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);
}