]> The Tcpdump Group git mirrors - libpcap/blob - pcap-hurd.c
CI: Call print_so_deps() on rpcapd in remote enabled build
[libpcap] / pcap-hurd.c
1 #define _GNU_SOURCE
2
3 /* XXX Hack not to include the Mach BPF interface */
4 #define _DEVICE_BPF_H_
5
6 #include <config.h>
7
8 #include <fcntl.h>
9 #include <hurd.h>
10 #include <mach.h>
11 #include <time.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <device/device.h>
18 #include <device/device_types.h>
19 #include <device/net_status.h>
20 #include <net/if_ether.h>
21
22 #include "pcap-int.h"
23
24 struct pcap_hurd {
25 struct pcap_stat stat;
26 device_t mach_dev;
27 mach_port_t rcv_port;
28 int filtering_in_kernel;
29 };
30
31 /* Accept all packets. */
32 static struct bpf_insn filter[] = {
33 { NETF_IN | NETF_OUT | NETF_BPF, 0, 0, 0 },
34 { BPF_RET | BPF_K, 0, 0, MAXIMUM_SNAPLEN },
35 };
36
37 /* device_set_filter calls net_set_filter which uses CSPF_BYTES which counts in
38 * shorts, not elements, so using extra parenthesis to silence compilers which
39 * believe we are computing wrong here. */
40 #define FILTER_COUNT (sizeof(filter) / (sizeof(short)))
41
42 /*
43 * strerror() on GNU/Hurd maps Mach error messages to strings,
44 * so we can use pcapint_fmt_errmsg_for_errno() to format
45 * messages for them.
46 */
47 #define pcapint_fmt_errmsg_for_kern_return_t pcapint_fmt_errmsg_for_errno
48
49 static int
50 PCAP_WARN_UNUSED_RESULT
51 pcap_device_set_filter(pcap_t *p, filter_array_t filter_array,
52 const mach_msg_type_number_t filter_count)
53 {
54 kern_return_t kr;
55 struct pcap_hurd *ph = p->priv;
56 kr = device_set_filter(ph->mach_dev, ph->rcv_port,
57 MACH_MSG_TYPE_MAKE_SEND, 0,
58 filter_array, filter_count);
59 if (! kr)
60 return 0;
61 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr,
62 "device_set_filter");
63 return PCAP_ERROR;
64 }
65
66 static int
67 pcap_setfilter_hurd(pcap_t *p, struct bpf_program *program)
68 {
69 if (! program || pcapint_install_bpf_program(p, program) < 0) {
70 pcapint_strlcpy(p->errbuf, "setfilter: invalid program",
71 sizeof(p->errbuf));
72 return PCAP_ERROR;
73 }
74
75 /*
76 * The bytecode is valid and the copy in p->fcode can be used for
77 * userland filtering if kernel filtering does not work out.
78 *
79 * The kernel BPF implementation supports neither BPF_MOD nor BPF_XOR,
80 * it also fails to reject unsupported bytecode properly, so the check
81 * must be done here.
82 */
83 struct pcap_hurd *ph = p->priv;
84 for (u_int i = 0; i < program->bf_len; i++) {
85 u_short c = program->bf_insns[i].code;
86 if (BPF_CLASS(c) == BPF_ALU &&
87 (BPF_OP(c) == BPF_MOD || BPF_OP(c) == BPF_XOR))
88 goto userland;
89 }
90
91 /*
92 * The kernel takes an array of 16-bit Hurd network filter commands, no
93 * more than NET_MAX_FILTER elements. The first four commands form a
94 * header that says "BPF bytecode follows", the rest is a binary copy
95 * of 64-bit instructions of the required BPF bytecode.
96 */
97 mach_msg_type_number_t cmdcount = 4 + 4 * program->bf_len;
98 if (cmdcount > NET_MAX_FILTER)
99 goto userland;
100
101 filter_t cmdbuffer[NET_MAX_FILTER];
102 memcpy(cmdbuffer, filter, sizeof(struct bpf_insn));
103 memcpy(cmdbuffer + 4, program->bf_insns,
104 program->bf_len * sizeof(struct bpf_insn));
105 if (pcap_device_set_filter(p, cmdbuffer, cmdcount))
106 goto userland;
107 ph->filtering_in_kernel = 1;
108 return 0;
109
110 userland:
111 /*
112 * Could not install a new kernel filter for a reason, so replace any
113 * previous kernel filter with one that accepts all packets and lets
114 * userland filtering do the job. If that fails too, something is
115 * badly broken and even userland filtering would not work correctly,
116 * so expose the failure.
117 */
118 ph->filtering_in_kernel = 0;
119 return pcap_device_set_filter(p, (filter_array_t)filter, FILTER_COUNT);
120 }
121
122 static int
123 pcap_read_hurd(pcap_t *p, int cnt _U_, pcap_handler callback, u_char *user)
124 {
125 struct net_rcv_msg *msg;
126 struct pcap_hurd *ph;
127 struct pcap_pkthdr h;
128 struct timespec ts;
129 int wirelen, caplen;
130 u_char *pkt;
131 kern_return_t kr;
132
133 ph = p->priv;
134 msg = (struct net_rcv_msg *)p->buffer;
135
136 retry:
137 if (p->break_loop) {
138 p->break_loop = 0;
139 return PCAP_ERROR_BREAK;
140 }
141
142 kr = mach_msg(&msg->msg_hdr, MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0,
143 p->bufsize, ph->rcv_port, MACH_MSG_TIMEOUT_NONE,
144 MACH_PORT_NULL);
145 clock_gettime(CLOCK_REALTIME, &ts);
146
147 if (kr) {
148 if (kr == MACH_RCV_INTERRUPTED)
149 goto retry;
150
151 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr,
152 "mach_msg");
153 return PCAP_ERROR;
154 }
155
156 ph->stat.ps_recv++;
157
158 /* XXX Ethernet support only */
159 /*
160 * wirelen calculation assumes the following:
161 * msg->packet_type.msgt_name == MACH_MSG_TYPE_BYTE
162 * msg->packet_type.msgt_size == 8
163 * msg->packet_type.msgt_number is a size in bytes
164 */
165 wirelen = ETH_HLEN + msg->net_rcv_msg_packet_count
166 - sizeof(struct packet_header);
167 pkt = p->buffer + offsetof(struct net_rcv_msg, packet)
168 + sizeof(struct packet_header) - ETH_HLEN;
169 memmove(pkt, p->buffer + offsetof(struct net_rcv_msg, header),
170 ETH_HLEN);
171
172 /*
173 * It seems, kernel device filters treat the K in BPF_RET as a Boolean:
174 * so long as it is positive, the Mach message will contain the entire
175 * packet and wirelen will be set accordingly. Thus the caplen value
176 * for the callback needs to be calculated for every packet no matter
177 * which type of filtering is in effect.
178 *
179 * For the userland filtering this calculated value is not an input:
180 * buflen always equals wirelen and a userland program can examine the
181 * entire packet, same way as a kernel program. It is not an output
182 * either: pcapint_filter() returns either zero or MAXIMUM_SNAPLEN.
183 * The same principle applies to kernel filtering.
184 */
185 caplen = (wirelen > p->snapshot) ? p->snapshot : wirelen;
186
187 if (! ph->filtering_in_kernel &&
188 ! pcapint_filter(p->fcode.bf_insns, pkt, wirelen, wirelen)) {
189 ph->stat.ps_drop++;
190 return 0;
191 }
192
193 h.ts.tv_sec = ts.tv_sec;
194 h.ts.tv_usec = ts.tv_nsec / 1000;
195 h.len = wirelen;
196 h.caplen = caplen;
197 callback(user, &h, pkt);
198 return 1;
199 }
200
201 static int
202 pcap_inject_hurd(pcap_t *p, const void *buf, int size)
203 {
204 struct pcap_hurd *ph;
205 kern_return_t kr;
206 int count;
207
208 ph = p->priv;
209 kr = device_write(ph->mach_dev, D_NOWAIT, 0,
210 (io_buf_ptr_t)buf, size, &count);
211
212 if (kr) {
213 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr,
214 "device_write");
215 return -1;
216 }
217
218 return count;
219 }
220
221 static int
222 pcap_stats_hurd(pcap_t *p, struct pcap_stat *ps)
223 {
224 struct pcap_hurd *ph;
225
226 ph = p->priv;
227 *ps = ph->stat;
228 return 0;
229 }
230
231 static void
232 pcap_cleanup_hurd(pcap_t *p)
233 {
234 struct pcap_hurd *ph;
235
236 ph = p->priv;
237
238 if (ph->rcv_port != MACH_PORT_NULL) {
239 mach_port_deallocate(mach_task_self(), ph->rcv_port);
240 ph->rcv_port = MACH_PORT_NULL;
241 }
242
243 if (ph->mach_dev != MACH_PORT_NULL) {
244 device_close(ph->mach_dev);
245 ph->mach_dev = MACH_PORT_NULL;
246 }
247
248 pcapint_cleanup_live_common(p);
249 }
250
251 static int
252 pcap_activate_hurd(pcap_t *p)
253 {
254 struct pcap_hurd *ph;
255 mach_port_t master;
256 kern_return_t kr;
257 int ret = PCAP_ERROR;
258
259 ph = p->priv;
260
261 if (p->snapshot <= 0 || p->snapshot > MAXIMUM_SNAPLEN)
262 p->snapshot = MAXIMUM_SNAPLEN;
263
264 /* Try devnode first */
265 master = file_name_lookup(p->opt.device, O_READ | O_WRITE, 0);
266
267 if (master != MACH_PORT_NULL)
268 kr = device_open(master, D_WRITE | D_READ, "eth", &ph->mach_dev);
269 else {
270 /* If unsuccessful, try Mach device */
271 kr = get_privileged_ports(NULL, &master);
272
273 if (kr) {
274 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf,
275 PCAP_ERRBUF_SIZE, kr, "get_privileged_ports");
276 if (kr == EPERM)
277 ret = PCAP_ERROR_PERM_DENIED;
278 goto error;
279 }
280
281 kr = device_open(master, D_READ | D_WRITE, p->opt.device,
282 &ph->mach_dev);
283 }
284
285 mach_port_deallocate(mach_task_self(), master);
286
287 if (kr) {
288 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr,
289 "device_open");
290 if (kr == ED_NO_SUCH_DEVICE) /* not ENODEV */
291 ret = PCAP_ERROR_NO_SUCH_DEVICE;
292 goto error;
293 }
294
295 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
296 &ph->rcv_port);
297
298 if (kr) {
299 pcapint_fmt_errmsg_for_kern_return_t(p->errbuf, PCAP_ERRBUF_SIZE, kr,
300 "mach_port_allocate");
301 goto error;
302 }
303
304 p->bufsize = sizeof(struct net_rcv_msg);
305 p->buffer = malloc(p->bufsize);
306
307 if (p->buffer == NULL) {
308 pcapint_fmt_errmsg_for_errno(p->errbuf, PCAP_ERRBUF_SIZE,
309 errno, "malloc");
310 goto error;
311 }
312
313 /*
314 * XXX Ethernet only currently
315 *
316 * XXX - does "Ethernet only currently" mean "the only devices
317 * on which the Hurd supports packet capture are Ethernet
318 * devices", or "it supports other devices but makes them
319 * all provide Ethernet headers"?
320 *
321 * If the latter, is there a way to determine whether the
322 * device is a real Ethernet, so that we could offer DLT_DOCSIS,
323 * in case you're capturing DOCSIS traffic that a Cisco Cable
324 * Modem Termination System is putting out onto an Ethernet
325 * (it doesn't put an Ethernet header onto the wire, it puts
326 * raw DOCSIS frames out on the wire inside the low-level
327 * Ethernet framing)?
328 */
329 p->linktype = DLT_EN10MB;
330
331 p->read_op = pcap_read_hurd;
332 p->inject_op = pcap_inject_hurd;
333 p->setfilter_op = pcap_setfilter_hurd;
334 p->stats_op = pcap_stats_hurd;
335
336 return 0;
337
338 error:
339 pcap_cleanup_hurd(p);
340 return ret;
341 }
342
343 pcap_t *
344 pcapint_create_interface(const char *device _U_, char *ebuf)
345 {
346 struct pcap_hurd *ph;
347 pcap_t *p;
348
349 p = PCAP_CREATE_COMMON(ebuf, struct pcap_hurd);
350 if (p == NULL)
351 return NULL;
352
353 ph = p->priv;
354 ph->mach_dev = MACH_PORT_NULL;
355 ph->rcv_port = MACH_PORT_NULL;
356 p->activate_op = pcap_activate_hurd;
357 return p;
358 }
359
360 static int
361 can_be_bound(const char *name)
362 {
363 /*
364 * On Hurd lo appears in the list of interfaces, but the call to
365 * device_open() fails with: "(os/device) no such device".
366 */
367 if (! strcmp(name, "lo"))
368 return 0;
369 return 1;
370 }
371
372 static int
373 get_if_flags(const char *name _U_, bpf_u_int32 *flags, char *errbuf _U_)
374 {
375 /*
376 * This would apply to the loopback interface if it worked. Ethernet
377 * interfaces appear up and running regardless of the link status.
378 */
379 *flags |= PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
380 return 0;
381 }
382
383 int
384 pcapint_platform_finddevs(pcap_if_list_t *devlistp, char *errbuf)
385 {
386 return pcapint_findalldevs_interfaces(devlistp, errbuf, can_be_bound,
387 get_if_flags);
388 }
389
390 /*
391 * Libpcap version string.
392 */
393 const char *
394 pcap_lib_version(void)
395 {
396 return PCAP_VERSION_STRING;
397 }