一、 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;
else
return &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);
}