1 /*-
2  * Copyright (c) 2001, 2003, 2004, 2007 Lev Walkin <vlm@lionet.info>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $Id: process.c,v 1.24 2007/04/22 07:53:12 vlm Exp $
27  */
28 #define _BSD_SOURCE	/* for th_flags in tcp.h */
29 #include "ipcad.h"
30 #include "rw.h"
31 #include "opt.h"
32 #include "storage.h"
33 #include "cfgvar.h"
34 
35 /*
36  * Get a pointer to the IPv4 header given the type of the input source,
37  * start of the link-level packet and captured size.
38  * IP header is guaranteed to be fully contained within the captured data,
39  * except for options, which may be missing.
40  */
41 static struct ip *
find_ipv4_level(packet_source_t * ps,char * packet,int caplen)42 find_ipv4_level(packet_source_t *ps, char *packet, int caplen) {
43 	struct ip *ipv4h = NULL;
44 	struct ether_header *eth;
45 
46 
47 	switch(ps->dlt) {
48 	case DLT_EN10MB:
49 		eth = (struct ether_header *)packet;
50 
51 		if((size_t)caplen < sizeof(*eth))
52 			return NULL;
53 
54 		switch(ntohs(eth->ether_type)) {
55 		case 0x0800:	/* ETHERTYPE_IP */
56 			ipv4h = (struct ip *)(packet + ETHER_HDR_LEN);
57 			break;
58 		case 0x8100:	/* ETHERTYPE_VLAN */
59 			ipv4h = (struct ip *)(packet + ETHER_HDR_LEN + 4);
60 			break;
61 		case 0x8864:	/* ETHERTYPE_PPPOE */
62 			ipv4h = (struct ip *)(packet + ETHER_HDR_LEN + 8);
63 			break;
64 		default:
65 			return NULL;
66 		}
67 
68 		break;
69 	case DLT_PPP:
70 #ifdef	DLT_C_HDLC
71 	case DLT_C_HDLC:
72 #endif
73 		ipv4h = (struct ip *)(packet + 4);
74 		break;
75 	case DLT_RAW:	/* Some PPP implementations */
76 		/*
77 		 * Packets are always IP.
78 		 */
79 		ipv4h = (struct ip *)packet;
80 		break;
81 #ifdef	DLT_LOOP
82 	case DLT_LOOP:
83 #endif	/* DLT_LOOP */
84 	case DLT_NULL:
85 		ipv4h = (struct ip *)(packet + 4);
86 		break;
87 #ifdef	DLT_LINUX_SLL
88 	case DLT_LINUX_SLL:	/* Linux cooked socket */
89 		ipv4h = (struct ip *)(packet + 16);
90 		break;
91 #endif
92 	case DLT_IEEE802:	/* Token Ring */
93 		ipv4h = (struct ip *)(packet + 22);
94 		break;
95 	default:
96 		return NULL;
97 	}
98 
99 	if(caplen - ((char *)ipv4h - (char *)packet)
100 		< (int)sizeof(struct ip)) {
101 		/* It is not complete IP header */
102 		return NULL;
103 	}
104 
105 	if(ipv4h->ip_v != IPVERSION)
106 		return NULL;
107 
108 	return ipv4h;
109 }
110 
111 /*
112  * Fill-in the ports of the flow structure, if it is a UDP or TCP packet.
113  * Also find ICMP type and code.
114  */
115 static void
find_ipv4_ports(struct ip * ipv4h,int caplen,flow_t * flow)116 find_ipv4_ports(struct ip *ipv4h, int caplen, flow_t *flow) {
117 	unsigned l4_offset;
118 
119 	switch((flow->ip_p = ipv4h->ip_p)) {
120 	case IPPROTO_ICMP:
121 		l4_offset = ipv4h->ip_hl << 2;
122 		if(l4_offset >= sizeof(struct ip)
123 		&& (int)(l4_offset + 2) <= caplen) {
124 			uint8_t *typecode_ptr;	/* two 1-byte values */
125 			typecode_ptr = (void *)((void *)ipv4h + l4_offset);
126 			flow->src_port = typecode_ptr[0];
127 			flow->dst_port = typecode_ptr[1];
128 			return;
129 		}
130 		break;
131 	case IPPROTO_UDP:
132 	case IPPROTO_TCP:
133 	case IPPROTO_SCTP:
134 		l4_offset = ipv4h->ip_hl << 2;
135 		if(l4_offset >= sizeof(struct ip)
136 		&& (int)(l4_offset + 4) <= caplen) {
137 			uint8_t *ports_ptr;	/* two 2-byte values */
138 			ports_ptr = (void *)((void *)ipv4h + l4_offset);
139 			flow->src_port = (ports_ptr[0] << 8) | ports_ptr[1];
140 			flow->dst_port = (ports_ptr[2] << 8) | ports_ptr[3];
141 			return;
142 		}
143 		break;
144 	}
145 
146 	flow->src_port = -1;
147 	flow->dst_port = -1;
148 }
149 
150 /*
151  * Fill-in the ports of the flow structure for NetFlow.
152  */
153 static void
find_netflow_stuff(struct ip * ipv4h,int caplen,flow_t * flow)154 find_netflow_stuff(struct ip *ipv4h, int caplen, flow_t *flow) {
155 	unsigned l4_offset;
156 
157 	/*
158 	 * The ToS is required to be an identifier of a flow uniqueness,
159 	 * according to Cisco. However, many have complained that this
160 	 * creates unanticipated duplicate flows. Although this behavior
161 	 * is strictly expected, you may comment out this line to disable
162 	 * differentiating between ToS-marked flows, if it bothers you.
163 	 */
164 	flow->ip_tos = ipv4h->ip_tos;
165 
166 	switch((flow->ip_p = ipv4h->ip_p)) {
167 	case IPPROTO_ICMP:
168 		/* Do not capture ICMP T/C as ports for NetFlow */
169 		break;
170 	case IPPROTO_UDP:
171 	case IPPROTO_TCP:
172 	case IPPROTO_SCTP:
173 		l4_offset = ipv4h->ip_hl << 2;
174 		if(l4_offset >= sizeof(struct ip)
175 		&& (int)(l4_offset + 4) <= caplen) {
176 			uint8_t *ports_ptr;	/* two 2-byte values */
177 			ports_ptr = (void *)((void *)ipv4h + l4_offset);
178 			flow->src_port = (ports_ptr[0] << 8) | ports_ptr[1];
179 			flow->dst_port = (ports_ptr[2] << 8) | ports_ptr[3];
180 
181 			if(flow->ip_p == IPPROTO_TCP
182 			&& (int)(l4_offset + offsetof(struct tcphdr, th_flags))
183 				< caplen) {
184 				struct tcphdr *th = (void *)ipv4h + l4_offset;
185 				flow->tcp_flags = th->th_flags;
186 			}
187 			return;
188 		}
189 		break;
190 	}
191 
192 	flow->src_port = -1;
193 	flow->dst_port = -1;
194 }
195 
196 void
process_packet_data(packet_source_t * ps,const unsigned char * packet,int caplen,const char * iface_in,const char * iface_out)197 process_packet_data(packet_source_t *ps,
198 		const unsigned char *packet, int caplen,
199 		const char *iface_in, const char *iface_out) {
200 	flow_t flow;
201 	struct ip *ipv4h;
202 	int iplen;
203 	agg_e nf_agg = AGG_NONE;	/* What to aggregate in NetFlow */
204 
205 	ipv4h = find_ipv4_level(ps, (char *)packet, caplen);
206 	if(!ipv4h)
207 		return;
208 
209 	/*
210 	 * Fetch some IP-level properties from the found header.
211 	 */
212 	iplen = ntohs(ipv4h->ip_len);
213 
214 	flow.src = ipv4h->ip_src;
215 	flow.dst = ipv4h->ip_dst;
216 	flow.packets = 1;	/* Single packet being added */
217 	flow.bytes = iplen;	/* Bytes in packet */
218 	flow.ip_tos = 0;	/* IP type of service */
219 	flow.tcp_flags = 0;	/* TCP flags */
220 	flow.ifSource = 0;	/* Interface */
221 	if(conf->capture_ports) {
222 		if(iface_in) {
223 			memcpy(flow.ifInName, iface_in, IFNAMSIZ);
224 			flow.ifInName[IFNAMSIZ-1] = 0;
225 			flow.ifInIndex = iface_to_snmp_id(flow.ifInName);
226 		} else {
227 			flow.ifInName[0] = '\0';
228 			flow.ifInIndex = ps->ifIndex;
229 		}
230 		if(iface_out) {
231 			memcpy(flow.ifOutName, iface_out, IFNAMSIZ);
232 			flow.ifOutName[IFNAMSIZ-1] = 0;
233 			flow.ifOutIndex = iface_to_snmp_id(flow.ifOutName);
234 		} else {
235 			flow.ifOutName[0] = '\0';
236 			flow.ifOutIndex = -1;
237 		}
238 	} else {
239 		flow.ifInName[0] = '\0';
240 		flow.ifOutName[0] = '\0';
241 		flow.ifOutIndex = -1;
242 		flow.ifInIndex = -1;
243 	}
244 
245 	/*
246 	 * Find ports, if instructed to account for them.
247 	 */
248 	if((ps->iflags & IFLAG_RSH_EXTRA)) {
249 		flow.ifSource = ps;
250 		find_ipv4_ports(ipv4h,
251 			caplen - ((void *)ipv4h - (void *)packet),
252 			&flow);
253 	} else {
254 		flow.ip_p = 0;		/* IP protocol */
255 		flow.src_port = -1;
256 		flow.dst_port = -1;
257 		nf_agg = AGG_PORTS;
258 	}
259 
260 	/*
261 	 * This update goes into the main storage for RSH export.
262 	 */
263 	flow_update(&active_storage, &flow, AGG_ALL);
264 
265 	/*
266 	 * This update goes into the NetFlow cache.
267 	 */
268 	if(conf->netflow_enabled && !(ps->iflags & IFLAG_NF_DISABLE)) {
269 		if((ps->iflags & IFLAG_NF_SAMPLED)
270 		&& ((ps->sample_count++) % conf->netflow_packet_interval)) {
271 			/*
272 			 * NetFlow sampling-mode packet-interval feature.
273 			 */
274 		} else {
275 			find_netflow_stuff(ipv4h,
276 				caplen - ((void *)ipv4h - (void *)packet),
277 				&flow);
278 			flow.ifSource = ps;
279 			flow_update(&netflow_storage, &flow, nf_agg);
280 		}
281 	}
282 
283 	/*
284 	 * Exit critical section: flow updated or created.
285 	 */
286 
287 	/* Perform statistics counting */
288 	ps->bytes_cur += iplen;
289 	ps->packets_cur += 1;
290 }
291 
292