Line data Source code
1 : /*
2 : * Copyright (c) 2015 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 : /*
16 : * pcap.c: libpcap packet capture format
17 : *
18 : * Copyright (c) 2008 Eliot Dresselhaus
19 : *
20 : * Permission is hereby granted, free of charge, to any person obtaining
21 : * a copy of this software and associated documentation files (the
22 : * "Software"), to deal in the Software without restriction, including
23 : * without limitation the rights to use, copy, modify, merge, publish,
24 : * distribute, sublicense, and/or sell copies of the Software, and to
25 : * permit persons to whom the Software is furnished to do so, subject to
26 : * the following conditions:
27 : *
28 : * The above copyright notice and this permission notice shall be
29 : * included in all copies or substantial portions of the Software.
30 : *
31 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 : * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 : * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 : * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 : */
39 :
40 : #include <fcntl.h>
41 : #include <vppinfra/pcap.h>
42 :
43 : /**
44 : * @file
45 : * @brief PCAP function.
46 : *
47 : * Usage:
48 : *
49 : * <code><pre>
50 : * \#include <vppinfra/pcap.h>
51 : *
52 : * static pcap_main_t pcap = {
53 : * .file_name = "/tmp/ip4",
54 : * .n_packets_to_capture = 2,
55 : * .packet_type = PCAP_PACKET_TYPE_ip,
56 : * };
57 : * </pre></code>
58 : *
59 : * To add a buffer:
60 : *
61 : * <code><pre>pcap_add_buffer (&pcap, vm, pi0, 128);</pre></code>
62 : *
63 : * File will be written after @c n_packets_to_capture or call to pcap_write (&pcap).
64 : *
65 : */
66 :
67 : /**
68 : * @brief Close PCAP file
69 : *
70 : * @return rc - clib_error_t
71 : *
72 : */
73 : __clib_export clib_error_t *
74 13322 : pcap_close (pcap_main_t * pm)
75 : {
76 13322 : close (pm->file_descriptor);
77 13322 : pm->flags &= ~PCAP_MAIN_INIT_DONE;
78 13322 : pm->file_descriptor = -1;
79 13322 : return 0;
80 : }
81 :
82 : /**
83 : * @brief Write PCAP file
84 : *
85 : * @return rc - clib_error_t
86 : *
87 : */
88 : __clib_export clib_error_t *
89 16817 : pcap_write (pcap_main_t * pm)
90 : {
91 16817 : clib_error_t *error = 0;
92 :
93 16817 : if (!(pm->flags & PCAP_MAIN_INIT_DONE))
94 : {
95 : pcap_file_header_t fh;
96 : int n;
97 :
98 13955 : if (!pm->file_name)
99 0 : pm->file_name = "/tmp/vnet.pcap";
100 :
101 13955 : pm->file_descriptor =
102 13955 : open (pm->file_name, O_CREAT | O_TRUNC | O_WRONLY, 0664);
103 13955 : if (pm->file_descriptor < 0)
104 : {
105 : error =
106 0 : clib_error_return_unix (0, "failed to open `%s'", pm->file_name);
107 0 : goto done;
108 : }
109 :
110 13955 : pm->flags |= PCAP_MAIN_INIT_DONE;
111 13955 : pm->n_packets_captured = 0;
112 13955 : pm->n_pcap_data_written = 0;
113 13955 : clib_spinlock_init (&pm->lock);
114 :
115 : /* Write file header. */
116 13955 : clib_memset (&fh, 0, sizeof (fh));
117 13955 : fh.magic = 0xa1b2c3d4;
118 13955 : fh.major_version = 2;
119 13955 : fh.minor_version = 4;
120 13955 : fh.time_zone = 0;
121 13955 : fh.max_packet_size_in_bytes = 1 << 16;
122 13955 : fh.packet_type = pm->packet_type;
123 13955 : n = write (pm->file_descriptor, &fh, sizeof (fh));
124 13955 : if (n != sizeof (fh))
125 : {
126 0 : if (n < 0)
127 : error =
128 0 : clib_error_return_unix (0, "write file header `%s'",
129 : pm->file_name);
130 : else
131 : error =
132 0 : clib_error_return (0, "short write of file header `%s'",
133 : pm->file_name);
134 0 : goto done;
135 : }
136 : }
137 :
138 33634 : while (vec_len (pm->pcap_data) > pm->n_pcap_data_written)
139 : {
140 16817 : i64 n = vec_len (pm->pcap_data) - pm->n_pcap_data_written;
141 :
142 33634 : n = write (pm->file_descriptor,
143 16817 : vec_elt_at_index (pm->pcap_data, pm->n_pcap_data_written),
144 : n);
145 :
146 16817 : if (n < 0 && unix_error_is_fatal (errno))
147 : {
148 0 : error = clib_error_return_unix (0, "write `%s'", pm->file_name);
149 0 : goto done;
150 : }
151 16817 : pm->n_pcap_data_written += n;
152 : }
153 :
154 16817 : if (pm->n_pcap_data_written >= vec_len (pm->pcap_data))
155 : {
156 16817 : vec_reset_length (pm->pcap_data);
157 16817 : pm->n_pcap_data_written = 0;
158 : }
159 :
160 0 : done:
161 16817 : if (error)
162 : {
163 0 : if (pm->file_descriptor >= 0)
164 0 : close (pm->file_descriptor);
165 : }
166 16817 : return error;
167 : }
168 :
169 : /**
170 : * @brief Read PCAP file
171 : *
172 : * @return rc - clib_error_t
173 : *
174 : */
175 : __clib_export clib_error_t *
176 15047 : pcap_read (pcap_main_t * pm)
177 : {
178 15047 : clib_error_t *error = 0;
179 : int fd, need_swap, n;
180 : pcap_file_header_t fh;
181 : pcap_packet_header_t ph;
182 :
183 15047 : fd = open (pm->file_name, O_RDONLY);
184 15047 : if (fd < 0)
185 : {
186 0 : error = clib_error_return_unix (0, "open `%s'", pm->file_name);
187 0 : goto done;
188 : }
189 :
190 15047 : if (read (fd, &fh, sizeof (fh)) != sizeof (fh))
191 : {
192 : error =
193 0 : clib_error_return_unix (0, "read file header `%s'", pm->file_name);
194 0 : goto done;
195 : }
196 :
197 15047 : need_swap = 0;
198 15047 : if (fh.magic == 0xd4c3b2a1)
199 : {
200 0 : need_swap = 1;
201 : #define _(t,f) fh.f = clib_byte_swap_##t (fh.f);
202 0 : foreach_pcap_file_header;
203 : #undef _
204 : }
205 :
206 15047 : if (fh.magic != 0xa1b2c3d4)
207 : {
208 0 : error = clib_error_return (0, "bad magic `%s'", pm->file_name);
209 0 : goto done;
210 : }
211 :
212 15047 : pm->min_packet_bytes = 0;
213 15047 : pm->max_packet_bytes = 0;
214 543597 : while ((n = read (fd, &ph, sizeof (ph))) != 0)
215 : {
216 : u8 *data;
217 : u64 timestamp;
218 : u32 timestamp_sec;
219 : u32 timestamp_usec;
220 :
221 528550 : if (need_swap)
222 : {
223 : #define _(t,f) ph.f = clib_byte_swap_##t (ph.f);
224 0 : foreach_pcap_packet_header;
225 : #undef _
226 : }
227 :
228 528550 : data = vec_new (u8, ph.n_bytes_in_packet);
229 528550 : if (read (fd, data, ph.n_packet_bytes_stored_in_file) !=
230 528550 : ph.n_packet_bytes_stored_in_file)
231 : {
232 0 : error = clib_error_return (0, "short read `%s'", pm->file_name);
233 0 : goto done;
234 : }
235 :
236 528550 : if (vec_len (pm->packets_read) == 0)
237 15047 : pm->min_packet_bytes = pm->max_packet_bytes = ph.n_bytes_in_packet;
238 : else
239 : {
240 513503 : pm->min_packet_bytes =
241 513503 : clib_min (pm->min_packet_bytes, ph.n_bytes_in_packet);
242 513503 : pm->max_packet_bytes =
243 513503 : clib_max (pm->max_packet_bytes, ph.n_bytes_in_packet);
244 : }
245 :
246 528550 : timestamp_sec = ph.time_in_sec;
247 528550 : timestamp_usec = ph.time_in_usec;
248 528550 : timestamp = ((u64) timestamp_sec) * 1000000 + (u64) timestamp_usec;
249 528550 : vec_add1 (pm->packets_read, data);
250 528550 : vec_add1 (pm->timestamps, timestamp);
251 : }
252 :
253 15047 : done:
254 15047 : if (fd >= 0)
255 15047 : close (fd);
256 15047 : return error;
257 :
258 : }
259 :
260 : /*
261 : * fd.io coding-style-patch-verification: ON
262 : *
263 : * Local Variables:
264 : * eval: (c-set-style "gnu")
265 : * End:
266 : */
|