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