libpcap之socket创建

一、 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值