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

📄 softflowd.c

📁 根据网络原始数据生成NetFlow记录的软件。NetFlow是Cisco提出的流技术
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Copyright 2002 Damien Miller <djm@mindrot.org> All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *//* $Id: softflowd.c,v 1.93 2006/11/02 06:23:29 djm Exp $ *//* * This is software implementation of Cisco's NetFlow(tm) traffic  * reporting system. It operates by listening (via libpcap) on a  * promiscuous interface and tracking traffic flows.  * * Traffic flows are recorded by source/destination/protocol IP address or, in the * case of TCP and UDP, by src_addr:src_port/dest_addr:dest_port/protocol * * Flows expire automatically after a period of inactivity (default: 1 hour) * They may also be evicted (in order of age) in situations where there are  * more flows than slots available. * * Netflow version 1 compatible packets are sent to a specified target  * host upon flow expiry. * * As this implementation watches traffic promiscuously, it is likely to  * place significant load on hosts or gateways on which it is installed. */#include "common.h"#include "sys-tree.h"#include "convtime.h"#include "softflowd.h"#include "treetype.h"#include "log.h"#include <pcap.h>RCSID("$Id: softflowd.c,v 1.93 2006/11/02 06:23:29 djm Exp $");/* Global variables */static int verbose_flag = 0;		/* Debugging flag *//* Signal handler flags */static int graceful_shutdown_request = 0;	/* Context for libpcap callback functions */struct CB_CTXT {	struct FLOWTRACK *ft;	int linktype;	int fatal;	int want_v6;};/* Describes a datalink header and how to extract v4/v6 frames from it */struct DATALINK {	int dlt;		/* BPF datalink type */	int skiplen;		/* Number of bytes to skip datalink header */	int ft_off;		/* Datalink frametype offset */	int ft_len;		/* Datalink frametype length */	int ft_is_be;		/* Set if frametype is big-endian */	u_int32_t ft_mask;	/* Mask applied to frametype */	u_int32_t ft_v4;	/* IPv4 frametype */	u_int32_t ft_v6;	/* IPv6 frametype */};/* Datalink types that we know about */static const struct DATALINK lt[] = {	{ DLT_EN10MB,	14, 12,  2,  1, 0xffffffff,  0x0800,   0x86dd },	{ DLT_PPP,	 5,  3,  2,  1, 0xffffffff,  0x0021,   0x0057 },#ifdef DLT_LINUX_SLL	{ DLT_LINUX_SLL,16, 14,  2,  1, 0xffffffff,  0x0800,   0x86dd },#endif	{ DLT_RAW,	 0,  0,  1,  1, 0x000000f0,  0x0040,   0x0060 },	{ DLT_NULL,	 4,  0,  4,  0, 0xffffffff, AF_INET, AF_INET6 },#ifdef DLT_LOOP	{ DLT_LOOP,	 4,  0,  4,  1, 0xffffffff, AF_INET, AF_INET6 },#endif	{ -1,		-1, -1, -1, -1, 0x00000000,  0xffff,   0xffff },};/* Netflow send functions */typedef int (netflow_send_func_t)(struct FLOW **, int, int, u_int64_t *,    struct timeval *, int);struct NETFLOW_SENDER {	int version;	netflow_send_func_t *func;	int v6_capable;};/* Array of NetFlow export function that we know of. NB. nf[0] is default */static const struct NETFLOW_SENDER nf[] = {	{ 5, send_netflow_v5, 0 },	{ 1, send_netflow_v1, 0 },	{ 9, send_netflow_v9, 1 },	{ -1, NULL, 0 },};/* Describes a location where we send NetFlow packets to */struct NETFLOW_TARGET {	int fd;	const struct NETFLOW_SENDER *dialect;};/* Signal handlers */static void sighand_graceful_shutdown(int signum){	graceful_shutdown_request = signum;}static void sighand_other(int signum){	/* XXX: this may not be completely safe */	logit(LOG_WARNING, "Exiting immediately on unexpected signal %d", signum);	_exit(0);}/* * This is the flow comparison function. */static intflow_compare(struct FLOW *a, struct FLOW *b){	/* Be careful to avoid signed vs unsigned issues here */	int r;	if (a->af != b->af)		return (a->af > b->af ? 1 : -1);	if ((r = memcmp(&a->addr[0], &b->addr[0], sizeof(a->addr[0]))) != 0)		return (r > 0 ? 1 : -1);	if ((r = memcmp(&a->addr[1], &b->addr[1], sizeof(a->addr[1]))) != 0)		return (r > 0 ? 1 : -1);#ifdef notyet	if (a->ip6_flowlabel[0] != 0 && b->ip6_flowlabel[0] != 0 && 	    a->ip6_flowlabel[0] != b->ip6_flowlabel[0])		return (a->ip6_flowlabel[0] > b->ip6_flowlabel[0] ? 1 : -1);	if (a->ip6_flowlabel[1] != 0 && b->ip6_flowlabel[1] != 0 && 	    a->ip6_flowlabel[1] != b->ip6_flowlabel[1])		return (a->ip6_flowlabel[1] > b->ip6_flowlabel[1] ? 1 : -1);#endif	if (a->protocol != b->protocol)		return (a->protocol > b->protocol ? 1 : -1);	if (a->port[0] != b->port[0])		return (ntohs(a->port[0]) > ntohs(b->port[0]) ? 1 : -1);	if (a->port[1] != b->port[1])		return (ntohs(a->port[1]) > ntohs(b->port[1]) ? 1 : -1);	return (0);}/* Generate functions for flow tree */FLOW_PROTOTYPE(FLOWS, FLOW, trp, flow_compare);FLOW_GENERATE(FLOWS, FLOW, trp, flow_compare);/* * This is the expiry comparison function. */static intexpiry_compare(struct EXPIRY *a, struct EXPIRY *b){	if (a->expires_at != b->expires_at)		return (a->expires_at > b->expires_at ? 1 : -1);	/* Make expiry entries unique by comparing flow sequence */	if (a->flow->flow_seq != b->flow->flow_seq)		return (a->flow->flow_seq > b->flow->flow_seq ? 1 : -1);	return (0);}/* Generate functions for flow tree */EXPIRY_PROTOTYPE(EXPIRIES, EXPIRY, trp, expiry_compare);EXPIRY_GENERATE(EXPIRIES, EXPIRY, trp, expiry_compare);#if 0/* Dump a packet */static voiddump_packet(const u_int8_t *p, int len){	char buf[1024], tmp[3];	int i;	for (*buf = '\0', i = 0; i < len; i++) {		snprintf(tmp, sizeof(tmp), "%02x%s", p[i], i % 2 ? " " : "");		if (strlcat(buf, tmp, sizeof(buf) - 4) >= sizeof(buf) - 4) {			strlcat(buf, "...", sizeof(buf));			break;		}	}	logit(LOG_INFO, "packet len %d: %s", len, buf);}#endif/* Format a time in an ISOish format */static const char *format_time(time_t t){	struct tm *tm;	static char buf[20];	tm = localtime(&t);	strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", tm);	return (buf);}/* Format a flow in a verbose and ugly way */static const char *format_flow(struct FLOW *flow){	char addr1[64], addr2[64], stime[20], ftime[20];	static char buf[1024];	inet_ntop(flow->af, &flow->addr[0], addr1, sizeof(addr1));	inet_ntop(flow->af, &flow->addr[1], addr2, sizeof(addr2));	snprintf(stime, sizeof(ftime), "%s", 	    format_time(flow->flow_start.tv_sec));	snprintf(ftime, sizeof(ftime), "%s", 	    format_time(flow->flow_last.tv_sec));	snprintf(buf, sizeof(buf),  "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u "	    "octets>:%u packets>:%u octets<:%u packets<:%u "	    "start:%s.%03ld finish:%s.%03ld tcp>:%02x tcp<:%02x "	    "flowlabel>:%08x flowlabel<:%08x ",	    flow->flow_seq,	    addr1, ntohs(flow->port[0]), addr2, ntohs(flow->port[1]),	    (int)flow->protocol, 	    flow->octets[0], flow->packets[0], 	    flow->octets[1], flow->packets[1], 	    stime, (flow->flow_start.tv_usec + 500) / 1000, 	    ftime, (flow->flow_last.tv_usec + 500) / 1000,	    flow->tcp_flags[0], flow->tcp_flags[1],	    flow->ip6_flowlabel[0], flow->ip6_flowlabel[1]);	return (buf);}/* Format a flow in a brief way */static const char *format_flow_brief(struct FLOW *flow){	char addr1[64], addr2[64];	static char buf[1024];	inet_ntop(flow->af, &flow->addr[0], addr1, sizeof(addr1));	inet_ntop(flow->af, &flow->addr[1], addr2, sizeof(addr2));	snprintf(buf, sizeof(buf), 	    "seq:%llu [%s]:%hu <> [%s]:%hu proto:%u",	    flow->flow_seq,	    addr1, ntohs(flow->port[0]), addr2, ntohs(flow->port[1]),	    (int)flow->protocol);	return (buf);}/* Fill in transport-layer (tcp/udp) portions of flow record */static inttransport_to_flowrec(struct FLOW *flow, const u_int8_t *pkt,     const size_t caplen, int isfrag, int protocol, int ndx){	const struct tcphdr *tcp = (const struct tcphdr *)pkt;	const struct udphdr *udp = (const struct udphdr *)pkt;	const struct icmp *icmp = (const struct icmp *)pkt;	/*	 * XXX to keep flow in proper canonical format, it may be necessary	 * to swap the array slots based on the order of the port numbers	 * does this matter in practice??? I don't think so - return flows will	 * always match, because of their symmetrical addr/ports	 */	switch (protocol) {	case IPPROTO_TCP:		/* Check for runt packet, but don't error out on short frags */		if (caplen < sizeof(*tcp))			return (isfrag ? 0 : 1);		flow->port[ndx] = tcp->th_sport;		flow->port[ndx ^ 1] = tcp->th_dport;		flow->tcp_flags[ndx] |= tcp->th_flags;		break;	case IPPROTO_UDP:		/* Check for runt packet, but don't error out on short frags */		if (caplen < sizeof(*udp))			return (isfrag ? 0 : 1);		flow->port[ndx] = udp->uh_sport;		flow->port[ndx ^ 1] = udp->uh_dport;		break;	case IPPROTO_ICMP:		/*		 * Encode ICMP type * 256 + code into dest port like		 * Cisco routers		 */		flow->port[ndx] = 0;		flow->port[ndx ^ 1] = htons(icmp->icmp_type * 256 +		    icmp->icmp_code);		break;	}	return (0);}/* Convert a IPv4 packet to a partial flow record (used for comparison) */static intipv4_to_flowrec(struct FLOW *flow, const u_int8_t *pkt, size_t caplen,     size_t len, int *isfrag, int af){	const struct ip *ip = (const struct ip *)pkt;	int ndx;	if (caplen < 20 || caplen < ip->ip_hl * 4)		return (-1);	/* Runt packet */	if (ip->ip_v != 4)		return (-1);	/* Unsupported IP version */		/* Prepare to store flow in canonical format */	ndx = memcmp(&ip->ip_src, &ip->ip_dst, sizeof(ip->ip_src)) > 0 ? 1 : 0;		flow->af = af;	flow->addr[ndx].v4 = ip->ip_src;	flow->addr[ndx ^ 1].v4 = ip->ip_dst;	flow->protocol = ip->ip_p;	flow->octets[ndx] = len;	flow->packets[ndx] = 1;	*isfrag = (ntohs(ip->ip_off) & (IP_OFFMASK|IP_MF)) ? 1 : 0;	/* Don't try to examine higher level headers if not first fragment */	if (*isfrag && (ntohs(ip->ip_off) & IP_OFFMASK) != 0)		return (0);	return (transport_to_flowrec(flow, pkt + (ip->ip_hl * 4), 	    caplen - (ip->ip_hl * 4), *isfrag, ip->ip_p, ndx));}/* Convert a IPv6 packet to a partial flow record (used for comparison) */static intipv6_to_flowrec(struct FLOW *flow, const u_int8_t *pkt, size_t caplen,     size_t len, int *isfrag, int af){	const struct ip6_hdr *ip6 = (const struct ip6_hdr *)pkt;	const struct ip6_ext *eh6;	const struct ip6_frag *fh6;	int ndx, nxt;	if (caplen < sizeof(*ip6))		return (-1);	/* Runt packet */	if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION)		return (-1);	/* Unsupported IPv6 version */	/* Prepare to store flow in canonical format */	ndx = memcmp(&ip6->ip6_src, &ip6->ip6_dst,	    sizeof(ip6->ip6_src)) > 0 ? 1 : 0;		flow->af = af;	flow->ip6_flowlabel[ndx] = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;	flow->addr[ndx].v6 = ip6->ip6_src;	flow->addr[ndx ^ 1].v6 = ip6->ip6_dst;	flow->octets[ndx] = len;	flow->packets[ndx] = 1;	*isfrag = 0;	nxt = ip6->ip6_nxt;	pkt += sizeof(*ip6);	caplen -= sizeof(*ip6);	/* Now loop through headers, looking for transport header */	for (;;) {		eh6 = (const struct ip6_ext *)pkt;		if (nxt == IPPROTO_HOPOPTS || 		    nxt == IPPROTO_ROUTING || 		    nxt == IPPROTO_DSTOPTS) {			if (caplen < sizeof(*eh6) ||			    caplen < (eh6->ip6e_len + 1) << 3)				return (1); /* Runt */			nxt = eh6->ip6e_nxt;			pkt += (eh6->ip6e_len + 1) << 3;			caplen -= (eh6->ip6e_len + 1) << 3;		} else if (nxt == IPPROTO_FRAGMENT) {			*isfrag = 1;			fh6 = (const struct ip6_frag *)eh6;			if (caplen < sizeof(*fh6))				return (1); /* Runt */			/*			 * Don't try to examine higher level headers if 			 * not first fragment			 */			if ((fh6->ip6f_offlg & IP6F_OFF_MASK) != 0)				return (0);			nxt = fh6->ip6f_nxt;			pkt += sizeof(*fh6);			caplen -= sizeof(*fh6);		} else 			break;	}	flow->protocol = nxt;	return (transport_to_flowrec(flow, pkt, caplen, *isfrag, nxt, ndx));}static voidflow_update_expiry(struct FLOWTRACK *ft, struct FLOW *flow){	EXPIRY_REMOVE(EXPIRIES, &ft->expiries, flow->expiry);	/* Flows over 2Gb traffic */	if (flow->octets[0] > (1U << 31) || flow->octets[1] > (1U << 31)) {		flow->expiry->expires_at = 0;		flow->expiry->reason = R_OVERBYTES;		goto out;	}		/* Flows over maximum life seconds */	if (ft->maximum_lifetime != 0 && 	    flow->flow_last.tv_sec - flow->flow_start.tv_sec > 	    ft->maximum_lifetime) {		flow->expiry->expires_at = 0;		flow->expiry->reason = R_MAXLIFE;		goto out;	}		if (flow->protocol == IPPROTO_TCP) {		/* Reset TCP flows */		if (ft->tcp_rst_timeout != 0 &&		    ((flow->tcp_flags[0] & TH_RST) ||		    (flow->tcp_flags[1] & TH_RST))) {			flow->expiry->expires_at = flow->flow_last.tv_sec + 			    ft->tcp_rst_timeout;			flow->expiry->reason = R_TCP_RST;			goto out;		}		/* Finished TCP flows */		if (ft->tcp_fin_timeout != 0 &&		    ((flow->tcp_flags[0] & TH_FIN) &&		    (flow->tcp_flags[1] & TH_FIN))) {			flow->expiry->expires_at = flow->flow_last.tv_sec + 			    ft->tcp_fin_timeout;			flow->expiry->reason = R_TCP_FIN;			goto out;		}		/* TCP flows */		if (ft->tcp_timeout != 0) {			flow->expiry->expires_at = flow->flow_last.tv_sec + 			    ft->tcp_timeout;			flow->expiry->reason = R_TCP;			goto out;		}	}	if (ft->udp_timeout != 0 && flow->protocol == IPPROTO_UDP) {		/* UDP flows */		flow->expiry->expires_at = flow->flow_last.tv_sec + 		    ft->udp_timeout;		flow->expiry->reason = R_UDP;		goto out;	}	if (ft->icmp_timeout != 0 &&	    ((flow->af == AF_INET && flow->protocol == IPPROTO_ICMP) || 

⌨️ 快捷键说明

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