⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcap-linux.c

📁 用来监视网络通信数据的源代码和应用程序,方便网络程序底层开发.
💻 C
📖 第 1 页 / 共 5 页
字号:
	 */
	return pcap_read_packet(handle, callback, user);
}

/*
 *  Read a packet from the socket calling the handler provided by
 *  the user. Returns the number of packets received or -1 if an
 *  error occured.
 */
static int
pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
{
	u_char			*bp;
	int			offset;
#ifdef HAVE_PF_PACKET_SOCKETS
	struct sockaddr_ll	from;
	struct sll_header	*hdrp;
#else
	struct sockaddr		from;
#endif
	socklen_t		fromlen;
	int			packet_len, caplen;
	struct pcap_pkthdr	pcap_header;

#ifdef HAVE_PF_PACKET_SOCKETS
	/*
	 * If this is a cooked device, leave extra room for a
	 * fake packet header.
	 */
	if (handle->md.cooked)
		offset = SLL_HDR_LEN;
	else
		offset = 0;
#else
	/*
	 * This system doesn't have PF_PACKET sockets, so it doesn't
	 * support cooked devices.
	 */
	offset = 0;
#endif

	/* Receive a single packet from the kernel */

	bp = handle->buffer + handle->offset;
	do {
		fromlen = sizeof(from);
		packet_len = recvfrom(
			handle->fd, bp + offset,
			handle->bufsize - offset, MSG_TRUNC,
			(struct sockaddr *) &from, &fromlen);
	} while (packet_len == -1 && errno == EINTR);

	/* Check if an error occured */

	if (packet_len == -1) {
		if (errno == EAGAIN)
			return 0;	/* no packet there */
		else {
			snprintf(handle->errbuf, sizeof(handle->errbuf),
				 "recvfrom: %s", pcap_strerror(errno));
			return -1;
		}
	}

#ifdef HAVE_PF_PACKET_SOCKETS
	/*
	 * If this is from the loopback device, reject outgoing packets;
	 * we'll see the packet as an incoming packet as well, and
	 * we don't want to see it twice.
	 *
	 * We can only do this if we're using PF_PACKET; the address
	 * returned for SOCK_PACKET is a "sockaddr_pkt" which lacks
	 * the relevant packet type information.
	 */
	if (!handle->md.sock_packet &&
	    from.sll_ifindex == handle->md.lo_ifindex &&
	    from.sll_pkttype == PACKET_OUTGOING)
		return 0;
#endif

#ifdef HAVE_PF_PACKET_SOCKETS
	/*
	 * If this is a cooked device, fill in the fake packet header.
	 */
	if (handle->md.cooked) {
		/*
		 * Add the length of the fake header to the length
		 * of packet data we read.
		 */
		packet_len += SLL_HDR_LEN;

		hdrp = (struct sll_header *)bp;

		/*
		 * Map the PACKET_ value to a LINUX_SLL_ value; we
		 * want the same numerical value to be used in
		 * the link-layer header even if the numerical values
		 * for the PACKET_ #defines change, so that programs
		 * that look at the packet type field will always be
		 * able to handle DLT_LINUX_SLL captures.
		 */
		switch (from.sll_pkttype) {

		case PACKET_HOST:
			hdrp->sll_pkttype = htons(LINUX_SLL_HOST);
			break;

		case PACKET_BROADCAST:
			hdrp->sll_pkttype = htons(LINUX_SLL_BROADCAST);
			break;

		case PACKET_MULTICAST:
			hdrp->sll_pkttype = htons(LINUX_SLL_MULTICAST);
			break;

		case PACKET_OTHERHOST:
			hdrp->sll_pkttype = htons(LINUX_SLL_OTHERHOST);
			break;

		case PACKET_OUTGOING:
			hdrp->sll_pkttype = htons(LINUX_SLL_OUTGOING);
			break;

		default:
			hdrp->sll_pkttype = -1;
			break;
		}

		hdrp->sll_hatype = htons(from.sll_hatype);
		hdrp->sll_halen = htons(from.sll_halen);
		memcpy(hdrp->sll_addr, from.sll_addr,
		    (from.sll_halen > SLL_ADDRLEN) ?
		      SLL_ADDRLEN :
		      from.sll_halen);
		hdrp->sll_protocol = from.sll_protocol;
	}
#endif

	/*
	 * XXX: According to the kernel source we should get the real
	 * packet len if calling recvfrom with MSG_TRUNC set. It does
	 * not seem to work here :(, but it is supported by this code
	 * anyway.
	 * To be honest the code RELIES on that feature so this is really
	 * broken with 2.2.x kernels.
	 * I spend a day to figure out what's going on and I found out
	 * that the following is happening:
	 *
	 * The packet comes from a random interface and the packet_rcv
	 * hook is called with a clone of the packet. That code inserts
	 * the packet into the receive queue of the packet socket.
	 * If a filter is attached to that socket that filter is run
	 * first - and there lies the problem. The default filter always
	 * cuts the packet at the snaplen:
	 *
	 * # tcpdump -d
	 * (000) ret      #68
	 *
	 * So the packet filter cuts down the packet. The recvfrom call
	 * says "hey, it's only 68 bytes, it fits into the buffer" with
	 * the result that we don't get the real packet length. This
	 * is valid at least until kernel 2.2.17pre6.
	 *
	 * We currently handle this by making a copy of the filter
	 * program, fixing all "ret" instructions with non-zero
	 * operands to have an operand of 65535 so that the filter
	 * doesn't truncate the packet, and supplying that modified
	 * filter to the kernel.
	 */

	caplen = packet_len;
	if (caplen > handle->snapshot)
		caplen = handle->snapshot;

	/* Run the packet filter if not using kernel filter */
	if (!handle->md.use_bpf && handle->fcode.bf_insns) {
		if (bpf_filter(handle->fcode.bf_insns, bp,
		                packet_len, caplen) == 0)
		{
			/* rejected by filter */
			return 0;
		}
	}

	/* Fill in our own header data */

	if (ioctl(handle->fd, SIOCGSTAMP, &pcap_header.ts) == -1) {
		snprintf(handle->errbuf, sizeof(handle->errbuf),
			 "ioctl: %s", pcap_strerror(errno));
		return -1;
	}
	pcap_header.caplen	= caplen;
	pcap_header.len		= packet_len;

	/*
	 * Count the packet.
	 *
	 * Arguably, we should count them before we check the filter,
	 * as on many other platforms "ps_recv" counts packets
	 * handed to the filter rather than packets that passed
	 * the filter, but if filtering is done in the kernel, we
	 * can't get a count of packets that passed the filter,
	 * and that would mean the meaning of "ps_recv" wouldn't
	 * be the same on all Linux systems.
	 *
	 * XXX - it's not the same on all systems in any case;
	 * ideally, we should have a "get the statistics" call
	 * that supplies more counts and indicates which of them
	 * it supplies, so that we supply a count of packets
	 * handed to the filter only on platforms where that
	 * information is available.
	 *
	 * We count them here even if we can get the packet count
	 * from the kernel, as we can only determine at run time
	 * whether we'll be able to get it from the kernel (if
	 * HAVE_TPACKET_STATS isn't defined, we can't get it from
	 * the kernel, but if it is defined, the library might
	 * have been built with a 2.4 or later kernel, but we
	 * might be running on a 2.2[.x] kernel without Alexey
	 * Kuznetzov's turbopacket patches, and thus the kernel
	 * might not be able to supply those statistics).  We
	 * could, I guess, try, when opening the socket, to get
	 * the statistics, and if we can not increment the count
	 * here, but it's not clear that always incrementing
	 * the count is more expensive than always testing a flag
	 * in memory.
	 */
	handle->md.stat.ps_recv++;

	/* Call the user supplied callback function */
	callback(userdata, &pcap_header, bp);

	return 1;
}



#ifdef HAVE_PCAPREADEX
int 
pcap_read_ex(pcap_t *handle, struct pcap_pkthdr **pkt_header, u_char **pkt_data)
{
	/* Check the capture type */
#ifdef REMOTE
	if (handle->rmt_clientside)
	{
		/* We are on an remote capture */
		if (!handle->rmt_capstarted)
		{
			// if the capture has not started yet, please start it
			if (pcap_startcapture_remote(handle) )
				return -1;
			handle->rmt_capstarted= 1;
		}
		return pcap_read_ex_remote(handle, pkt_header, pkt_data);
	}
#endif

	if (handle->sf.rfile==NULL)
	{
		u_char			*bp;
		int			offset;
#ifdef HAVE_PF_PACKET_SOCKETS
		struct sockaddr_ll	from;
		struct sll_header	*hdrp;
#else
		struct sockaddr		from;
#endif
		socklen_t		fromlen;
		int			packet_len, caplen;

#ifdef HAVE_PF_PACKET_SOCKETS
		/*
		 * If this is a cooked device, leave extra room for a
		 * fake packet header.
		 */
		if (handle->md.cooked)
			offset = SLL_HDR_LEN;
		else
			offset = 0;
#else
		/*
		 * This system doesn't have PF_PACKET sockets, so it doesn't
		 * support cooked devices.
		 */
		offset = 0;
#endif

		/* Receive a single packet from the kernel */

		bp = handle->buffer + handle->offset;
		do {
			fromlen = sizeof(from);
			packet_len = recvfrom(
				handle->fd, bp + offset,
				handle->bufsize - offset, MSG_TRUNC,
				(struct sockaddr *) &from, &fromlen);
		} while (packet_len == -1 && errno == EINTR);

		/* Check if an error occured */

		if (packet_len == -1) {
			if (errno == EAGAIN)
				return 0;	/* no packet there */
			else {
				snprintf(handle->errbuf, sizeof(handle->errbuf),
					 "recvfrom: %s", pcap_strerror(errno));
				return -1;
			}
		}

#ifdef HAVE_PF_PACKET_SOCKETS
		/*
		 * If this is from the loopback device, reject outgoing packets;
		 * we'll see the packet as an incoming packet as well, and
		 * we don't want to see it twice.
		 *
		 * We can only do this if we're using PF_PACKET; the address
		 * returned for SOCK_PACKET is a "sockaddr_pkt" which lacks
		 * the relevant packet type information.
		 */
		if (!handle->md.sock_packet &&
			from.sll_ifindex == handle->md.lo_ifindex &&
			from.sll_pkttype == PACKET_OUTGOING)
			return 0;
#endif

#ifdef HAVE_PF_PACKET_SOCKETS
		/*
		 * If this is a cooked device, fill in the fake packet header.
		 */
		if (handle->md.cooked) {
			/*
			 * Add the length of the fake header to the length
			 * of packet data we read.
			 */
			packet_len += SLL_HDR_LEN;

			hdrp = (struct sll_header *)bp;

			/*
			 * Map the PACKET_ value to a LINUX_SLL_ value; we
			 * want the same numerical value to be used in
			 * the link-layer header even if the numerical values
			 * for the PACKET_ #defines change, so that programs
			 * that look at the packet type field will always be
			 * able to handle DLT_LINUX_SLL captures.
			 */
			switch (from.sll_pkttype) {

			case PACKET_HOST:
				hdrp->sll_pkttype = htons(LINUX_SLL_HOST);
				break;

			case PACKET_BROADCAST:
				hdrp->sll_pkttype = htons(LINUX_SLL_BROADCAST);
				break;

			case PACKET_MULTICAST:
				hdrp->sll_pkttype = htons(LINUX_SLL_MULTICAST);
				break;

			case PACKET_OTHERHOST:
				hdrp->sll_pkttype = htons(LINUX_SLL_OTHERHOST);
				break;

			case PACKET_OUTGOING:
				hdrp->sll_pkttype = htons(LINUX_SLL_OUTGOING);
				break;

			default:
				hdrp->sll_pkttype = -1;
				break;
			}

			hdrp->sll_hatype = htons(from.sll_hatype);
			hdrp->sll_halen = htons(from.sll_halen);
			memcpy(hdrp->sll_addr, from.sll_addr,
				(from.sll_halen > SLL_ADDRLEN) ?
				  SLL_ADDRLEN :
				  from.sll_halen);
			hdrp->sll_protocol = from.sll_protocol;
		}
#endif

		/*
		 * XXX: According to the kernel source we should get the real
		 * packet len if calling recvfrom with MSG_TRUNC set. It does
		 * not seem to work here :(, but it is supported by this code
		 * anyway.
		 * To be honest the code RELIES on that feature so this is really
		 * broken with 2.2.x kernels.
		 * I spend a day to figure out what's going on and I found out
		 * that the following is happening:
		 *
		 * The packet comes from a random interface and the packet_rcv
		 * hook is called with a clone of the packet. That code inserts
		 * the packet into the receive queue of the packet socket.
		 * If a filter is attached to that socket that filter is run
		 * first - and there lies the problem. The default filter always
		 * cuts the packet at the snaplen:
		 *
		 * # tcpdump -d
		 * (000) ret      #68
		 *
		 * So the packet filter cuts down the packet. The recvfrom call
		 * says "hey, it's only 68 bytes, it fits into the buffer" with
		 * the result that we don't get the real packet length. This
		 * is valid at least until kernel 2.2.17pre6.
		 *
		 * We currently handle this by making a copy of the filter
		 * program, fixing all "ret" instructions with non-zero
		 * operands to have an operand of 65535 so that the filter
		 * doesn't truncate the packet, and supplying that modified
		 * filter to the kernel.
		 */

		caplen = packet_len;
		if (caplen > handle->snapshot)
			caplen = handle->snapshot;

		/* Run the packet filter if not using kernel filter */
		if (!handle->md.use_bpf && handle->fcode.bf_insns) {
			if (bpf_filter(handle->fcode.bf_insns, bp,
							packet_len, caplen) == 0)
			{
				/* rejected by filter */
				return 0;
			}
		}

		/* Fill in our own header data */

		if (ioctl(handle->fd, SIOCGSTAMP, &(handle->pcap_header.ts)) == -1) {
			snprintf(handle->errbuf, sizeof(handle->errbuf),
				 "ioctl: %s", pcap_strerror(errno));
			return -1;
		}
		handle->pcap_header.caplen	= caplen;
		handle->pcap_header.len		= packet_len;

		/*
		 * Count the packet.
		 *
		 * Arguably, we should count them before we check the filter,
		 * as on many other platforms "ps_recv" counts packets
		 * handed to the filter rather than packets that passed
		 * the filter, but if filtering is done in the kernel, we
		 * can't get a count of packets that passed the filter,
		 * and that would mean the meaning of "ps_recv" wouldn't
		 * be the same on all Linux systems.
		 *
		 * XXX - it's not the same on all systems in any case;
		 * ideally, we should have a "get the statistics" call
		 * that supplies more counts and indicates which of them
		 * it supplies, so that we supply a count of packets
		 * handed to the filter only on platforms where that
		 * information is available.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -