Skip to content

Commit b7284a9

Browse files
committed
Implement promiscuous option for open_link.
This was prompted by issue #1 from @gabrik. This allows one to put the device in promiscuous mode when opening the link. On linux (af_packet) we can use PACKET_MR_PROMISC which is nicer than SIOCSIFFLAGS, that is because the kernel keeps state of the process that joined the membership and clears it when the process dies, that's also what libpcap(3) does. I struggled a bit in the beginning since I was doing SIOCIFFLAGS and had to keep the state in userland which is almost impossible to do it right if we get a SIGKILL. BPF always did this, so it's also straightforward.
1 parent 6585864 commit b7284a9

File tree

7 files changed

+136
-23
lines changed

7 files changed

+136
-23
lines changed

bin/dune

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(executable
2+
(name rawcap)
3+
(public_name rawcap)
4+
(libraries rawlink))

bin/rawcap.ml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
(*
2+
* Copyright (c) 2015-2022 Christiano F. Haesbaert <haesbaert@haesbaert.org>
3+
*
4+
* Permission to use, copy, modify, and distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15+
*)
16+
17+
(* Useful for livetesting *)
18+
19+
let () =
20+
if Array.length Sys.argv <> 2 then invalid_arg "usage: rawcap ifname";
21+
let ifname = Sys.argv.(1) in
22+
let link = Rawlink.open_link ~promisc:true ifname in
23+
let rec loop () =
24+
let pkt = Rawlink.read_packet link in
25+
Printf.printf "got packet with %d bytes\n%!" (Cstruct.length pkt);
26+
loop ()
27+
in
28+
loop ()

lib/lwt_rawlink.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(*
2-
* Copyright (c) 2015 Christiano F. Haesbaert <haesbaert@haesbaert.org>
2+
* Copyright (c) 2015-2022 Christiano F. Haesbaert <haesbaert@haesbaert.org>
33
*
44
* Permission to use, copy, modify, and distribute this software for any
55
* purpose with or without fee is hereby granted, provided that the above
@@ -28,14 +28,14 @@ type driver =
2828
| AF_PACKET
2929
| BPF
3030

31-
external opensock: ?filter:string -> string -> Unix.file_descr = "caml_rawlink_open"
31+
external opensock: ?filter:string -> ?promisc:bool -> string -> Unix.file_descr = "caml_rawlink_open"
3232
external dhcp_server_filter: unit -> string = "caml_dhcp_server_filter"
3333
external dhcp_client_filter: unit -> string = "caml_dhcp_client_filter"
3434
external driver: unit -> driver = "caml_driver"
3535
external bpf_align: int -> int -> int = "caml_bpf_align"
3636

37-
let open_link ?filter ifname =
38-
let fd = Lwt_unix.of_unix_file_descr (opensock ?filter:filter ifname) in
37+
let open_link ?filter ?(promisc=false) ifname =
38+
let fd = Lwt_unix.of_unix_file_descr (opensock ?filter:filter ~promisc ifname) in
3939
let () = Lwt_unix.set_blocking fd false in
4040
{ fd; packets = ref []; buffer = (Cstruct.create 65536) }
4141

lib/lwt_rawlink.mli

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(*
2-
* Copyright (c) 2015 Christiano F. Haesbaert <haesbaert@haesbaert.org>
2+
* Copyright (c) 2015-2022 Christiano F. Haesbaert <haesbaert@haesbaert.org>
33
*
44
* Permission to use, copy, modify, and distribute this software for any
55
* purpose with or without fee is hereby granted, provided that the above
@@ -25,10 +25,11 @@
2525

2626
type t
2727

28-
val open_link : ?filter:string -> string -> t
29-
(** [open_link ~filter interface]. Creates a rawlink on the specified
30-
[interface], a BPF program [filter] can be passed to filter out incoming
31-
packets. *)
28+
val open_link : ?filter:string -> ?promisc:bool -> string -> t
29+
(** [open_link ~filter ~promisc interface]. Creates a rawlink on the
30+
specified [interface], a BPF program [filter] can be passed to
31+
filter out incoming packets. If [promisc] is true, sets [interface]
32+
to promiscuous mode, defaults to false. *)
3233

3334
val close_link : t -> unit Lwt.t
3435
(** [close_link]. Closes a rawlink. *)

lib/rawlink.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(*
2-
* Copyright (c) 2015 Christiano F. Haesbaert <haesbaert@haesbaert.org>
2+
* Copyright (c) 2015-2022 Christiano F. Haesbaert <haesbaert@haesbaert.org>
33
*
44
* Permission to use, copy, modify, and distribute this software for any
55
* purpose with or without fee is hereby granted, provided that the above
@@ -35,16 +35,16 @@ type driver =
3535
| AF_PACKET
3636
| BPF
3737

38-
external opensock: ?filter:string -> string -> Unix.file_descr = "caml_rawlink_open"
38+
external opensock: ?filter:string -> ?promisc:bool -> string -> Unix.file_descr = "caml_rawlink_open"
3939
external dhcp_server_filter: unit -> string = "caml_dhcp_server_filter"
4040
external dhcp_client_filter: unit -> string = "caml_dhcp_client_filter"
4141
external driver: unit -> driver = "caml_driver"
4242
external unix_bytes_read: Unix.file_descr -> Cstruct.buffer -> int -> int -> int =
4343
"lwt_unix_bytes_read"
4444
external bpf_align: int -> int -> int = "caml_bpf_align"
4545

46-
let open_link ?filter ifname =
47-
{ fd = opensock ?filter:filter ifname;
46+
let open_link ?filter ?(promisc=false) ifname =
47+
{ fd = opensock ?filter ~promisc ifname;
4848
packets = ref [];
4949
buffer = Cstruct.create 65536 }
5050

lib/rawlink.mli

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(*
2-
* Copyright (c) 2015 Christiano F. Haesbaert <haesbaert@haesbaert.org>
2+
* Copyright (c) 2015-2022 Christiano F. Haesbaert <haesbaert@haesbaert.org>
33
*
44
* Permission to use, copy, modify, and distribute this software for any
55
* purpose with or without fee is hereby granted, provided that the above
@@ -26,10 +26,11 @@
2626
type t
2727
(** Type of a rawlink. *)
2828

29-
val open_link : ?filter:string -> string -> t
30-
(** [open_link ~filter interface]. Creates a rawlink on the specified
31-
[interface], a BPF program [filter] can be passed to filter out incoming
32-
packets. *)
29+
val open_link : ?filter:string -> ?promisc:bool -> string -> t
30+
(** [open_link ~filter ~promisc interface]. Creates a rawlink on the
31+
specified [interface], a BPF program [filter] can be passed to
32+
filter out incoming packets. If [promisc] is true, sets [interface]
33+
to promiscuous mode, defaults to false.*)
3334

3435
val close_link : t -> unit
3536
(** [close_link]. Closes a rawlink. *)

lib/rawlink_stubs.c

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,27 @@ bpf_setfilter(int fd, value vfilter)
163163
return (r);
164164
}
165165

166+
int
167+
bpf_setpromisc(int fd, int promisc)
168+
{
169+
int r = 0;
170+
171+
if (promisc) {
172+
caml_enter_blocking_section();
173+
r = ioctl(fd, BIOCPROMISC, NULL);
174+
caml_leave_blocking_section();
175+
}
176+
177+
if (r == -1)
178+
uerror("bpf_setpromisc", Nothing);
179+
180+
return (0);
181+
}
182+
166183
CAMLprim value
167-
caml_rawlink_open(value vfilter, value vifname)
184+
caml_rawlink_open(value vfilter, value vpromisc, value vifname)
168185
{
169-
CAMLparam2(vfilter, vifname);
186+
CAMLparam3(vfilter, vpromisc, vifname);
170187
int fd;
171188

172189
if ((fd = bpf_open()) == -1)
@@ -181,6 +198,8 @@ caml_rawlink_open(value vfilter, value vifname)
181198
CAMLreturn(Val_unit);
182199
if (bpf_setimmediate(fd, 1) == -1)
183200
CAMLreturn(Val_unit);
201+
if (bpf_setpromisc(fd, Bool_val(vpromisc)) == -1)
202+
CAMLreturn(Val_unit);
184203

185204
CAMLreturn (Val_int(fd));
186205
}
@@ -204,6 +223,34 @@ caml_bpf_align(value va, value vb)
204223

205224
#ifdef USE_AF_PACKET
206225

226+
/*
227+
* Welcome to linux where glibc insists in not providing strlcpy.
228+
*/
229+
size_t
230+
strlcpy(char *dst, const char *src, size_t dsize)
231+
{
232+
const char *osrc = src;
233+
size_t nleft = dsize;
234+
235+
/* Copy as many bytes as will fit. */
236+
if (nleft != 0) {
237+
while (--nleft != 0) {
238+
if ((*dst++ = *src++) == '\0')
239+
break;
240+
}
241+
}
242+
243+
/* Not enough room in dst, add NUL and traverse rest of src. */
244+
if (nleft == 0) {
245+
if (dsize != 0)
246+
*dst = '\0'; /* NUL-terminate dst */
247+
while (*src++)
248+
;
249+
}
250+
251+
return(src - osrc - 1); /* count does not include NUL */
252+
}
253+
207254
#define FILTER sock_filter
208255

209256
int
@@ -271,18 +318,48 @@ af_packet_setfilter(int fd, value vfilter)
271318
return (r);
272319
}
273320

321+
int
322+
af_packet_setpromisc(int fd, int promisc, const char *ifname)
323+
{
324+
int r = 0;
325+
struct packet_mreq mr;
326+
int ifidx;
327+
328+
ifidx = if_nametoindex(ifname);
329+
if (ifidx == 0)
330+
uerror("af_set_promisc: if_nametoindex", Nothing);
331+
332+
if (promisc) {
333+
bzero(&mr, sizeof(mr));
334+
mr.mr_ifindex = ifidx;
335+
mr.mr_type = PACKET_MR_PROMISC;
336+
caml_enter_blocking_section();
337+
r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
338+
&mr, sizeof(mr));
339+
caml_leave_blocking_section();
340+
if (r == -1)
341+
uerror("af_set_promisc: PACKET_ADD_MEMBERSHIP",
342+
Nothing);
343+
}
344+
345+
return (r);
346+
}
347+
274348
CAMLprim value
275-
caml_rawlink_open(value vfilter, value vifname)
349+
caml_rawlink_open(value vfilter, value vpromisc, value vifname)
276350
{
277-
CAMLparam2(vfilter, vifname);
351+
CAMLparam3(vfilter, vpromisc, vifname);
278352
int fd;
279353

280354
if ((fd = af_packet_open()) == -1)
281355
CAMLreturn (Val_unit);
282356
if (af_packet_setfilter(fd, vfilter) == -1)
283-
CAMLreturn(Val_unit);
357+
CAMLreturn (Val_unit);
284358
if (af_packet_setif(fd, String_val(vifname)) == -1)
285359
CAMLreturn (Val_unit);
360+
if (af_packet_setpromisc(fd, Bool_val(vpromisc),
361+
String_val(vifname)) == -1)
362+
CAMLreturn (Val_unit);
286363

287364
CAMLreturn (Val_int(fd));
288365
}
@@ -307,6 +384,7 @@ caml_driver(value vunit)
307384
CAMLreturn (Val_int(1));
308385
#endif
309386
}
387+
310388
/* Filters */
311389
CAMLprim value
312390
caml_dhcp_server_filter(value vunit)
@@ -345,6 +423,7 @@ caml_dhcp_server_filter(value vunit)
345423

346424
CAMLreturn (vfilter);
347425
}
426+
348427
CAMLprim value
349428
caml_dhcp_client_filter(value vunit)
350429
{

0 commit comments

Comments
 (0)