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

📄 packet.c

📁 开源的防火墙代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* Copyright (c) 2002-2005 Vladislav Goncharov.
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*/

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// $Id: packet.c,v 1.9 2003/09/12 07:58:36 dev Exp $

/*
* Stateful packet checking engine
*/

#ifdef USE_PACKET_ENGINE

#include <ntddk.h>
#include <ndis.h>
#include <tdikrnl.h>
#include "sock.h"

#if _WIN32_WINNT >= 0x0500
/* include pfhook.h */
typedef ULONG IPAddr, IPMask;
#include <pfhook.h>
#endif

#include "conn_state.h"
#include "filter.h"
#include "ipc.h"
#include "ndis_hk_ioctl.h"
#include "net.h"
#include "obj_tbl.h"
#include "packet.h"

/* prototypes */

static NTSTATUS	get_iface(void);

static BOOLEAN	filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self,
			      BOOLEAN packet_unchanged);

static void		process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo);

static BOOLEAN	process_transp(int direction, UCHAR proto,
			       struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len,
			       struct flt_request *log_nfo);

static void		process_tcp(int direction, struct tcp_hdr *tcp_hdr, struct flt_request *log_nfo);
static void		process_udp(int direction, struct udp_hdr *udp_hdr, struct flt_request *log_nfo);
static void		process_icmp(int direction, struct icmp_hdr *icmp_hdr, struct flt_request *log_nfo);

static void		init_tcp_states(void);

static void		check_packet(struct flt_request *log_nfo);

#if _WIN32_WINNT >= 0x0500

/* for "ipfilterdriver" */

static PF_FORWARD_ACTION	hook_proc(
				    unsigned char *PacketHeader, unsigned char *Packet, unsigned int PacketLength,
				    unsigned int RecvInterfaceIndex, unsigned int SendInterfaceIndex,
				    IPAddr RecvLinkNextHop, IPAddr SendLinkNextHop);

static NTSTATUS		set_hook(PacketFilterExtensionPtr hook_fn);

static BOOLEAN g_set_hook = FALSE;

#endif

/* globals */

static PDEVICE_OBJECT g_ndis_hk_devobj;
static PFILE_OBJECT g_ndis_hk_fileobj;

// interface of ndis_hk
struct ndis_hk_interface *g_ndis_hk;

static struct filter_nfo g_tdi_fw =
{
	sizeof(g_tdi_fw),
	filter_packet,
	NULL,
	NULL,
	NULL,
	NULL
};

// here's a table with all possible tcp flags (except RST) and connection states

#define MAX_FLAGS	3

static struct
{
	UCHAR	tcp_flags_set;
	UCHAR	tcp_flags_not_set;
} g_tcp_states[TCP_STATE_MAX][2][MAX_FLAGS];	// init this array in init_tcp_states


NTSTATUS	init_packet(void)
{
	NTSTATUS status;
	UNICODE_STRING devname;
	
	// connect with ndis_hk
	RtlInitUnicodeString(&devname, L"\\Device\\ndis_hk");
	
	status = IoGetDeviceObjectPointer(
					    &devname,
					    STANDARD_RIGHTS_ALL,
					    &g_ndis_hk_fileobj,
					    &g_ndis_hk_devobj);
	if (status == STATUS_SUCCESS)
		{
		
		/* using ndis_hk driver */
		
		status = get_iface();
		if (status != STATUS_SUCCESS)
			{
			KdPrint(("[tdi_fw] init_packet get_iface: 0x%x!\n", status));
			goto done;
			}
		
		// attach our filter!
		g_ndis_hk->attach_filter(&g_tdi_fw, TRUE, FALSE);	// to bottom of filter stack
		
		}
	else	{
#if _WIN32_WINNT >= 0x0500
		/* try to use "ipfilterdriver" */
		
		status = set_hook(hook_proc);
		if (status != STATUS_SUCCESS)
			{
			KdPrint(("[tdi_fw] init_packet set_hook: 0x%x!\n", status));
			goto done;
			}
		
		g_set_hook = TRUE;
#endif
		}
	
	init_tcp_states();
	
	done:
	if (status != STATUS_SUCCESS)
		{
		// cleanup
		free_packet();
		}
	
	return status;
}

void	free_packet(void)
{
	if (g_ndis_hk_fileobj != NULL)
		{
		// detach our filter!
		if (g_ndis_hk != NULL)
			{
			g_ndis_hk->attach_filter(&g_tdi_fw, FALSE, FALSE);
			g_ndis_hk = NULL;
			}
		
		ObDereferenceObject(g_ndis_hk_fileobj);
		g_ndis_hk_fileobj = NULL;
		}
	
#if _WIN32_WINNT >= 0x0500
	if (g_set_hook)
		{
		set_hook(NULL);
		g_set_hook = FALSE;
		}
#endif
}

NTSTATUS	get_iface(void)
{
	PIRP irp;
	IO_STATUS_BLOCK isb;
	
	irp = IoBuildDeviceIoControlRequest(IOCTL_CMD_GET_KM_IFACE,
					    g_ndis_hk_devobj,
					    NULL, 0,
					    &g_ndis_hk, sizeof(g_ndis_hk),
					    TRUE, NULL, &isb);
	if (irp == NULL)
		{
		KdPrint(("[tdi_fw] get_iface: IoBuildDeviceIoControlRequest!\n"));
		return STATUS_INSUFFICIENT_RESOURCES;
		}
	
	return IoCallDriver(g_ndis_hk_devobj, irp);
}

BOOLEAN	filter_packet(int direction, int iface, PNDIS_PACKET packet, struct filter_nfo *self,
		      BOOLEAN packet_unchanged)
{
	BOOLEAN result = FALSE, log = TRUE;
	struct filter_nfo *child;
	struct flt_request log_nfo;
	PNDIS_BUFFER buffer;
	UINT packet_len, buffer_len, buffer_offset, hdr_len;
	void *pointer;
	struct ether_hdr *ether_hdr;
	struct ip_hdr *ip_hdr;
	
	KdPrint(("[tdi_fw] filter_packet: direction = %s; iface = %d; packet = 0x%x\n",
		direction == DIRECTION_IN ? "in" : "out", iface, packet));
	
	// get child in filter stack
	if (direction == DIRECTION_IN)
		child = self->lower;
	else
		child = self->upper;
	
	memset(&log_nfo, 0, sizeof(log_nfo));
	log_nfo.struct_size = sizeof(log_nfo);
	log_nfo.direction = direction;
	
	// parse packet
	
	NdisQueryPacket(packet, NULL, NULL, &buffer, &packet_len);
	
	if (packet_len < sizeof(struct ether_hdr))
		{
		KdPrint(("[tdi_fw] filter_packet: too small packet for ether_hdr! (%u)\n", packet_len));
		goto done;
		}
	
	/* process ether_hdr */
	
	NdisQueryBuffer(buffer, &ether_hdr, &buffer_len);
	
	if (buffer_len < sizeof(struct ether_hdr))
		{
		KdPrint(("[tdi_fw] filter_packet: too small buffer for ether_hdr! (%u)\n", buffer_len));
		goto done;
		}
	buffer_offset = 0;
	
#define PRINT_ETH_ADDR(addr) \
		(addr)[0], (addr)[1], (addr)[2], (addr)[3], (addr)[4], (addr)[5]
	
	KdPrint(("[tdi_fw] filter_packet: eth %02x-%02x-%02x-%02x-%02x-%02x -> %02x-%02x-%02x-%02x-%02x-%02x (0x%x)\n",
		PRINT_ETH_ADDR(ether_hdr->ether_shost),
		PRINT_ETH_ADDR(ether_hdr->ether_dhost),
		ether_hdr->ether_type));
	
	// UGLY way to determine IP broadcasts
	if (memcmp(ether_hdr->ether_dhost, "\xff\xff\xff\xff\xff\xff", 6) == 0)
		log_nfo.packet.is_broadcast = 1;
	
	// go to the next header
	if (buffer_len > sizeof(struct ether_hdr))
		{
		
		pointer = (char *)ether_hdr + sizeof(struct ether_hdr);
		buffer_offset += sizeof(struct ether_hdr);
		
		buffer_len -= sizeof(struct ether_hdr);
		
		}
	else	{
		// use next buffer in chain
		
		do	{
			NdisGetNextBuffer(buffer, &buffer);
			NdisQueryBuffer(buffer, &pointer, &buffer_len);
			} while (buffer_len == 0);		// sometimes there're buffers with zero size in chain
		
		buffer_offset = 0;
		}
	
	if (ntohs(ether_hdr->ether_type) == ETHERTYPE_IP)
		{
		
		/* process ip_hdr */
		
		if (buffer_len < sizeof(struct ip_hdr))
			{
			KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u)\n",
				buffer_len));
			goto done;
			}
		
		ip_hdr = (struct ip_hdr *)pointer;
		hdr_len = ip_hdr->ip_hl * 4;
		
		if (buffer_len < hdr_len)
			{
			KdPrint(("[tdi_fw] filter_packet: too small buffer for ip_hdr! (%u vs. %u)\n",
				buffer_len, hdr_len));
			goto done;
			}
		
		// check we've got the first fragment (don't work with another!)
		if ((ntohs(ip_hdr->ip_off) & IP_OFFMASK) != 0 && (ip_hdr->ip_off & IP_DF) == 0)
			{
			
			KdPrint(("[tdi_fw] filter_packet: got not first fragment\n"));
			
			result = TRUE;
			log = FALSE;
			goto done;
			}
		
		process_ip(direction, ip_hdr, &log_nfo);
		
		// go to the next header
		if (buffer_len > hdr_len)
			{
			
			pointer = (char *)ip_hdr + hdr_len;
			buffer_offset += hdr_len;
			
			buffer_len -= hdr_len;
			
			}
		else	{
			// use next buffer in chain
			
			do	{
				NdisGetNextBuffer(buffer, &buffer);
				NdisQueryBuffer(buffer, &pointer, &buffer_len);
				} while (buffer_len == 0);		// sometimes there're buffers with zero size in chain
			
			buffer_offset = 0;
			}
		
		result = process_transp(direction, ip_hdr->ip_p, ip_hdr, pointer, buffer_len, &log_nfo);
		if (!result)
			goto done;
		
		// using log_nfo->pid as signal to log it
		log = (log_nfo.pid == 0);
		}
	else
		log = FALSE;		// don't log not IP packets
	
	result = TRUE;		// packet headers looks like valid
	
	done:
	if (log){
		log_nfo.result = result ? FILTER_PACKET_LOG : FILTER_PACKET_BAD;
		log_nfo.pid = (ULONG)-1;
		log_request(&log_nfo);
		}
	
	if (result)
		return child->process_packet(direction, iface, packet, child, packet_unchanged);
	else
		return FALSE;
}

/* IP header */
void	process_ip(int direction, struct ip_hdr *ip_hdr, struct flt_request *log_nfo)
{
#define PRINT_IP_ADDR(addr) \
		((UCHAR *)&(addr))[0], ((UCHAR *)&(addr))[1], ((UCHAR *)&(addr))[2], ((UCHAR *)&(addr))[3]
	
	KdPrint(("[tdi_fw] process_ip: ip %d.%d.%d.%d -> %d.%d.%d.%d (proto = %d, ipid = 0x%x)\n",
		PRINT_IP_ADDR(ip_hdr->ip_src),
		PRINT_IP_ADDR(ip_hdr->ip_dst),
		ip_hdr->ip_p,
		ip_hdr->ip_id));
	
	log_nfo->proto = ip_hdr->ip_p;
	
	if (direction == DIRECTION_IN)
		log_nfo->log_bytes_in = ntohs(ip_hdr->ip_len);
	else
		log_nfo->log_bytes_out = ntohs(ip_hdr->ip_len);
	
	log_nfo->addr.len = sizeof(struct sockaddr_in);
	
	((struct sockaddr_in *)(&log_nfo->addr.from))->sin_family = AF_INET;
	((struct sockaddr_in *)(&log_nfo->addr.from))->sin_addr.s_addr = ip_hdr->ip_src;
	
	((struct sockaddr_in *)(&log_nfo->addr.to))->sin_family = AF_INET;
	((struct sockaddr_in *)(&log_nfo->addr.to))->sin_addr.s_addr = ip_hdr->ip_dst;
}

/* process TCP, UDP or ICMP header */
BOOLEAN	process_transp(int direction, UCHAR proto,
		       struct ip_hdr *ip_hdr, char *pointer, UINT buffer_len,
		       struct flt_request *log_nfo)
{
	struct tcp_hdr *tcp_hdr;
	struct udp_hdr *udp_hdr;
	struct icmp_hdr *icmp_hdr;
	
	switch (proto)
		{
		case IPPROTO_TCP:
			
			/* process tcp_hdr */

⌨️ 快捷键说明

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