Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support filtering using BPF byte code

Add support for loading filters into the BPF device.

Filters are set by calling:

    bpf:ctl(Socket, setf, Filter).

Where filter is a list of binaries. For example, the rules to filter
IP addresses used in the bpf man page would look like:

-define(ETHERTYPE_IP, 16#0800).
ip() ->
    [
        ?BPF_STMT(?BPF_LD+?BPF_H+?BPF_ABS, 12),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, ?ETHERTYPE_IP, 0, 8),
        ?BPF_STMT(?BPF_LD+?BPF_W+?BPF_ABS, 26),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, 16#8003700f, 0, 2),
        ?BPF_STMT(?BPF_LD+?BPF_W+?BPF_ABS, 30),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, 16#80037023, 3, 4),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, 16#80037023, 0, 3),
        ?BPF_STMT(?BPF_LD+?BPF_W+?BPF_ABS, 30),
        ?BPF_JUMP(?BPF_JMP+?BPF_JEQ+?BPF_K, 16#8003700f, 0, 1),
        ?BPF_STMT(?BPF_RET+?BPF_K, ?SIZEOF_U_INT-1),
        ?BPF_STMT(?BPF_RET+?BPF_K, 0)
    ].

The ?BPF_STMT and ?BPF_JUMP macros are a just convenience when
re-using existing examples. They simply call bpf:stmt/2 and bpf:jump/4.
  • Loading branch information...
commit e8008bda05d47ef5b6d567e04daf01865dc4d0af 1 parent 0a45094
@msantos authored
Showing with 328 additions and 82 deletions.
  1. +180 −0 include/bpf.hrl
  2. +148 −82 src/bpf.erl
View
180 include/bpf.hrl
@@ -0,0 +1,180 @@
+%% Copyright (c) 2011, Michael Santos <michael.santos@gmail.com>
+%% All rights reserved.
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions
+%% are met:
+%%
+%% Redistributions of source code must retain the above copyright
+%% notice, this list of conditions and the following disclaimer.
+%%
+%% Redistributions in binary form must reproduce the above copyright
+%% notice, this list of conditions and the following disclaimer in the
+%% documentation and/or other materials provided with the distribution.
+%%
+%% Neither the name of the author nor the names of its contributors
+%% may be used to endorse or promote products derived from this software
+%% without specific prior written permission.
+%%
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+%% POSSIBILITY OF SUCH DAMAGE.
+
+%%-------------------------------------------------------------------------
+%%% BPF ioctl structures and commands
+%%-------------------------------------------------------------------------
+
+-define(SIZEOF_STRUCT_IFREQ, 32).
+-define(SIZEOF_INT32_T, 4).
+-define(SIZEOF_U_INT, ?SIZEOF_INT32_T).
+
+%% struct bpf_program {
+%% u_int bf_len;
+%% struct bpf_insn *bf_insns;
+%% };
+
+%% struct bpf_insn {
+%% u_short code;
+%% u_char jt;
+%% u_char jf;
+%% bpf_u_int32 k;
+%% };
+-define(SIZEOF_STRUCT_BPF_PROGRAM, 2 + 1 + 1 + ?SIZEOF_U_INT).
+
+%% struct bpf_stat {
+%% u_int bs_recv;
+%% u_int bs_drop;
+%% };
+-define(SIZEOF_STRUCT_BPF_STAT,
+ ?SIZEOF_U_INT + ?SIZEOF_U_INT).
+
+%% struct bpf_version {
+%% u_short bv_major;
+%% u_short bv_minor;
+%% };
+-define(SIZEOF_STRUCT_BPF_VERSION, 2 + 2).
+
+%% struct bpf_dltlist {
+%% u_int32_t bfl_len;
+%% union {
+%% u_int32_t *bflu_list;
+%% u_int64_t bflu_pad;
+%% } bfl_u;
+%% };
+-define(SIZEOF_STRUCT_BPF_DLTLIST,
+ ?SIZEOF_U_INT + ?SIZEOF_U_INT + 8).
+
+-define(BPF_ALIGNMENT, ?SIZEOF_INT32_T).
+
+-define(IOC_IN, 16#80000000).
+-define(IOC_OUT, 16#40000000).
+-define(IOC_INOUT, ?IOC_IN bor ?IOC_OUT).
+-define(IOC_VOID, 16#20000000).
+-define(IOCPARM_MASK, 16#1fff).
+
+-define(BIOCGBLEN, bpf:ior($B, 102, ?SIZEOF_U_INT)).
+-define(BIOCSBLEN, bpf:iowr($B, 102, ?SIZEOF_U_INT)).
+-define(BIOCSETF, bpf:iow($B, 103, ?SIZEOF_STRUCT_BPF_PROGRAM)).
+-define(BIOCFLUSH, bpf:io($B, 104)).
+-define(BIOCPROMISC, bpf:io($B, 105)).
+-define(BIOCGDLT, bpf:ior($B,106, ?SIZEOF_U_INT)).
+-define(BIOCGETIF, bpf:ior($B,107, ?SIZEOF_STRUCT_IFREQ)).
+-define(BIOCSETIF, bpf:iow($B, 108, ?SIZEOF_STRUCT_IFREQ)).
+-define(BIOCSRTIMEOUT, bpf:iow($B, 109, bpf:sizeof(timeval)).
+-define(BIOCGRTIMEOUT, bpf:ior($B, 110, bpf:sizeof(timeval)).
+-define(BIOCGSTATS, bpf:ior($B, 111, ?SIZEOF_STRUCT_BPF_STAT)).
+-define(BIOCIMMEDIATE, bpf:iow($B, 112, ?SIZEOF_U_INT)).
+-define(BIOCVERSION, bpf:ior($B, 113, ?SIZEOF_STRUCT_BPF_VERSION)).
+-define(BIOCGRSIG, bpf:ior($B, 114, ?SIZEOF_U_INT)).
+-define(BIOCSRSIG, bpf:iow($B, 115, ?SIZEOF_U_INT)).
+-define(BIOCGHDRCMPLT, bpf:ior($B, 116, ?SIZEOF_U_INT)).
+-define(BIOCSHDRCMPLT, bpf:iow($B, 117, ?SIZEOF_U_INT)).
+-define(BIOCGSEESENT, bpf:ior($B, 118, ?SIZEOF_U_INT)).
+-define(BIOCSSEESENT, bpf:iow($B, 119, ?SIZEOF_U_INT)).
+-define(BIOCSDLT, bpf:iow($B, 120, ?SIZEOF_U_INT)).
+-define(BIOCGDLTLIST, bpf:iowr($B, 121, ?SIZEOF_STRUCT_BPF_DLTLIST)).
+
+
+%%-------------------------------------------------------------------------
+%%% BPF Filter
+%%-------------------------------------------------------------------------
+
+%% instruction classes
+-define(BPF_CLASS(Code), Code band 16#07).
+-define(BPF_LD, 16#00).
+-define(BPF_LDX, 16#01).
+-define(BPF_ST, 16#02).
+-define(BPF_STX, 16#03).
+-define(BPF_ALU, 16#04).
+-define(BPF_JMP, 16#05).
+-define(BPF_RET, 16#06).
+-define(BPF_MISC, 16#07).
+
+%% ld/ldx fields
+-define(BPF_SIZE(Code), Code band 16#18).
+-define(BPF_W, 16#00).
+-define(BPF_H, 16#08).
+-define(BPF_B, 16#10).
+-define(BPF_MODE(Code), Code band 16#e0).
+-define(BPF_IMM, 16#00).
+-define(BPF_ABS, 16#20).
+-define(BPF_IND, 16#40).
+-define(BPF_MEM, 16#60).
+-define(BPF_LEN, 16#80).
+-define(BPF_MSH, 16#a0).
+
+%% alu/jmp fields
+-define(BPF_OP(Code), Code band 16#f0).
+-define(BPF_ADD, 16#00).
+-define(BPF_SUB, 16#10).
+-define(BPF_MUL, 16#20).
+-define(BPF_DIV, 16#30).
+-define(BPF_OR, 16#40).
+-define(BPF_AND, 16#50).
+-define(BPF_LSH, 16#60).
+-define(BPF_RSH, 16#70).
+-define(BPF_NEG, 16#80).
+-define(BPF_JA, 16#00).
+-define(BPF_JEQ, 16#10).
+-define(BPF_JGT, 16#20).
+-define(BPF_JGE, 16#30).
+-define(BPF_JSET, 16#40).
+-define(BPF_SRC(Code), Code band 16#08).
+-define(BPF_K, 16#00).
+-define(BPF_X, 16#08).
+
+%% ret - BPF_K and BPF_X also apply
+-define(BPF_RVAL(Code), Code band 16#18).
+-define(BPF_A, 16#10).
+
+%% misc
+-define(BPF_MISCOP(Code), Code band 16#f8).
+-define(BPF_TAX, 16#00).
+-define(BPF_TXA, 16#80).
+
+%% struct bpf_insn {
+%% u_short code;
+%% u_char jt;
+%% u_char jf;
+%% bpf_u_int32 k;
+%% };
+%% NOTE: the man page says k is a u_long,
+%% the header says u_int32_t.
+-record(insn, {
+ code = 0,
+ jt = 0,
+ jf = 0,
+ k = 0
+ }).
+
+-define(BPF_STMT(Code, K), bpf:stmt(Code, K)).
+-define(BPF_JUMP(Code, K, JT, JF), bpf:jump(Code, K, JT, JF)).
View
230 src/bpf.erl
@@ -30,83 +30,27 @@
%% POSSIBILITY OF SUCH DAMAGE.
-module(bpf).
+-include("bpf.hrl").
+
+% BPF ioctl
-export([
open/1,
data/1,
promiscuous/1,
- attr/2, attr/3
+ ctl/2, ctl/3
+ ]).
+% BPF filters
+-export([
+ insn/1,
+ stmt/2, jump/4,
+ offset/1
+ ]).
+% Utility functions
+-export([
+ pad/1, align/1,
+ io/2, iow/3, ior/3, iowr/3, ioc/4,
+ sizeof/1
]).
--export([pad/1, align/1]).
-
--define(SIZEOF_STRUCT_IFREQ, 32).
--define(SIZEOF_INT32_T, 4).
--define(SIZEOF_U_INT, ?SIZEOF_INT32_T).
-
-%% struct bpf_program {
-%% u_int bf_len;
-%% struct bpf_insn *bf_insns;
-%% };
-
-%% struct bpf_insn {
-%% u_short code;
-%% u_char jt;
-%% u_char jf;
-%% bpf_u_int32 k;
-%% };
--define(SIZEOF_STRUCT_BPF_PROGRAM,
- ?SIZEOF_U_INT + 2 + 1 + 1 + ?SIZEOF_U_INT).
-
-%% struct bpf_stat {
-%% u_int bs_recv;
-%% u_int bs_drop;
-%% };
--define(SIZEOF_STRUCT_BPF_STAT,
- ?SIZEOF_U_INT + ?SIZEOF_U_INT)
-
-%% struct bpf_version {
-%% u_short bv_major;
-%% u_short bv_minor;
-%% };
--define(SIZEOF_STRUCT_BPF_VERSION, 2 + 2).
-
-%% struct bpf_dltlist {
-%% u_int32_t bfl_len;
-%% union {
-%% u_int32_t *bflu_list;
-%% u_int64_t bflu_pad;
-%% } bfl_u;
-%% };
--define(SIZEOF_STRUCT_BPF_DLTLIST,
- ?SIZEOF_U_INT + ?SIZEOF_U_INT + 8).
-
--define(BPF_ALIGNMENT, ?SIZEOF_INT32_T).
-
--define(IOC_IN, 16#80000000).
--define(IOC_OUT, 16#40000000).
--define(IOC_VOID, 16#20000000).
--define(IOCPARM_MASK, 16#1fff).
-
--define(BIOCGBLEN, ior($B, 102, ?SIZEOF_U_INT)).
--define(BIOCSBLEN, iowr($B, 102, ?SIZEOF_U_INT)).
--define(BIOCSETF, iow($B, 103, ?SIZEOF_STRUCT_BPF_PROGRAM)).
--define(BIOCFLUSH, io($B, 104)).
--define(BIOCPROMISC, io($B, 105)).
--define(BIOCGDLT, ior($B,106, ?SIZEOF_U_INT)).
--define(BIOCGETIF, ior($B,107, ?SIZEOF_STRUCT_IFREQ)).
--define(BIOCSETIF, iow($B, 108, ?SIZEOF_STRUCT_IFREQ)).
--define(BIOCSRTIMEOUT, iow($B, 109, sizeof(timeval)).
--define(BIOCGRTIMEOUT, ior($B, 110, sizeof(timeval)).
--define(BIOCGSTATS, ior($B, 111, ?SIZEOF_STRUCT_BPF_STAT)).
--define(BIOCIMMEDIATE, iow($B, 112, ?SIZEOF_U_INT)).
--define(BIOCVERSION, ior($B, 113, SIZEOF_STRUCT_BPF_VERSION)).
--define(BIOCGRSIG, ior($B, 114, ?SIZEOF_U_INT)).
--define(BIOCSRSIG, iow($B, 115, ?SIZEOF_U_INT)).
--define(BIOCGHDRCMPLT, ior($B, 116, ?SIZEOF_U_INT)).
--define(BIOCSHDRCMPLT, iow($B, 117, ?SIZEOF_U_INT)).
--define(BIOCGSEESENT, ior($B, 118, ?SIZEOF_U_INT)).
--define(BIOCSSEESENT, iow($B, 119, ?SIZEOF_U_INT)).
--define(BIOCSDLT, iow($B, 120, ?SIZEOF_U_INT)).
--define(BIOCGDLTLIST, iowr($B, 121, ?SIZEOF_STRUCT_BPF_DLTLIST)).
open(Dev) ->
@@ -122,13 +66,66 @@ open(Dev) ->
end.
-attr(Socket, blen) ->
+%%
+%% Get bpf attributes
+%%
+ctl(Socket, blen) ->
case procket:ioctl(Socket, ?BIOCGBLEN, <<1:32/native>>) of
{ok, Len} -> {ok, procket:ntohl(Len)};
Error -> Error
+ end;
+
+ctl(Socket, dlt) ->
+ case procket:ioctl(Socket, ?BIOCGDLT, <<1:32/native>>) of
+ {ok, DLT} -> {ok, procket:ntohl(DLT)};
+ Error -> Error
+ end;
+
+% struct bpf_dtlist
+%ctl(Socket, dltlist) ->
+% procket:ioctl(Socket, ?BIOCGDLTLIST, <<1:32/native>>);
+
+ctl(Socket, flush) ->
+ procket:ioctl(Socket, ?BIOCFLUSH, <<0:32/native>>);
+
+% struct ifreq
+ctl(Socket, getif) ->
+ case procket:ioctl(Socket, ?BIOCGETIF, <<0:32/integer-unit:8>>) of
+ {ok, Ifname} ->
+ {ok, binary_to_list(hd(binary:split(Ifname, <<0>>)))};
+ Error ->
+ Error
+ end;
+
+%ctl(Socket, timeout) ->
+% Size = sizeof(timeval),
+% procket:ioctl(Socket, ?BIOCGRTIMEOUT, <<0:Size/bytes>>);
+
+ctl(Socket, version) ->
+ case procket:ioctl(Socket, ?BIOCVERSION, <<0:32>>) of
+ {ok, <<Major:2/native-unsigned-integer-unit:8,
+ Minor:2/native-unsigned-integer-unit:8>>} ->
+ {Major, Minor};
+ Error ->
+ Error
end.
-attr(Socket, setif, Ifname) ->
+%%
+%% Set bpf attributes
+%%
+ctl(Socket, blen, Len) ->
+ case procket:ioctl(Socket, ?BIOCSBLEN, <<Len:32/native>>) of
+ {ok, Len} -> {ok, procket:ntohl(Len)};
+ Error -> Error
+ end;
+
+ctl(Socket, dlt, DLT) ->
+ case procket:ioctl(Socket, ?BIOCSDLT, <<DLT:32/native>>) of
+ {ok, N} -> {ok, procket:ntohl(N)};
+ Error -> Error
+ end;
+
+ctl(Socket, setif, Ifname) ->
% struct ifreq
Ifreq = list_to_binary([
Ifname, <<0:((15*8) - (length(Ifname)*8)), 0:8>>,
@@ -136,14 +133,33 @@ attr(Socket, setif, Ifname) ->
]),
procket:ioctl(Socket, ?BIOCSETIF, Ifreq);
-attr(Socket, immediate, Bool) when Bool == true; Bool == false ->
+ctl(Socket, immediate, Bool) when Bool == true; Bool == false ->
procket:ioctl(Socket, ?BIOCIMMEDIATE, bool(Bool));
-attr(Socket, hdrcmplt, Bool) when Bool == true; Bool == false ->
+ctl(Socket, hdrcmplt, Bool) when Bool == true; Bool == false ->
procket:ioctl(Socket, ?BIOCSHDRCMPLT, bool(Bool));
-attr(Socket, seesent, Bool) when Bool == true; Bool == false ->
- procket:ioctl(Socket, ?BIOCSSEESENT, bool(Bool)).
+ctl(Socket, seesent, Bool) when Bool == true; Bool == false ->
+ procket:ioctl(Socket, ?BIOCSSEESENT, bool(Bool));
+
+ctl(Socket, setf, []) ->
+ procket:ioctl(Socket, ?BIOCSETF, <<0:((?SIZEOF_STRUCT_BPF_PROGRAM)*8)>>);
+ctl(Socket, setf, Insn) when is_list(Insn) ->
+ % struct bpf_program
+ {ok, Code, [Res]} = procket:alloc([
+ <<(length(Insn)):4/native-unsigned-integer-unit:8>>,
+ {ptr, list_to_binary(Insn)}
+ ]),
+ case procket:ioctl(Socket, ?BIOCSETF, Code) of
+ {ok, _} ->
+ procket:buf(Res);
+ Error ->
+ Error
+ end.
+
+% struct timeval
+%ctl(Socket, timeout, Timeout) ->
+% procket:ioctl(Socket, ?BIOCSRTIMEOUT, <<0:(sizeof(timeval))/bytes>>).
bool(true) -> <<1:32/native>>;
@@ -198,6 +214,53 @@ promiscuous(FD) ->
%%-------------------------------------------------------------------------
+%%% BPF filtering
+%%-------------------------------------------------------------------------
+insn(#insn{
+ code = Code,
+ jt = JT,
+ jf = JF,
+ k = K
+ }) ->
+ <<Code:2/native-unsigned-integer-unit:8,
+ JT:8, JF:8,
+ K:4/native-unsigned-integer-unit:8>>;
+insn(<<Code:2/native-unsigned-integer-unit:8,
+ JT:8, JF:8,
+ K:4/native-unsigned-integer-unit:8>>) ->
+ #insn{
+ code = Code,
+ jt = JT,
+ jf = JF,
+ k = K
+ }.
+
+stmt(Code, K) when is_integer(Code), is_integer(K) ->
+ insn(#insn{
+ code = Code,
+ k = K
+ }).
+
+jump(Code, K, JT, JF) when is_integer(Code), is_integer(K),
+ is_integer(JT), is_integer(JF) ->
+ insn(#insn{
+ code = Code,
+ jt = JT,
+ jf = JF,
+ k = K
+ }).
+
+
+offset(word) -> ?BPF_W;
+offset(halfword) -> ?BPF_H;
+offset(byte) -> ?BPF_B;
+
+offset(?BPF_W) -> word;
+offset(?BPF_H) -> halfword;
+offset(?BPF_B) -> byte.
+
+
+%%-------------------------------------------------------------------------
%%% Internal functions
%%-------------------------------------------------------------------------
%% BSD ioctl request calculation (taken from ioccom.h)
@@ -213,18 +276,21 @@ iow(G,N,T) ->
ior(G,N,T) ->
ioc(?IOC_OUT, G, N, T).
+iowr(G,N,T) ->
+ ioc(?IOC_INOUT, G, N, T).
+
sizeof(timeval) ->
erlang:system_info({wordsize, external}) + ?SIZEOF_U_INT.
init(Socket, Dev) ->
% Set the interface for the bpf
- {ok, _} = attr(Socket, setif, Dev),
+ {ok, _} = ctl(Socket, setif, Dev),
% Allow caller to provide packet header (header complete)
- {ok, _} = attr(Socket, hdrcmplt, true),
+ {ok, _} = ctl(Socket, hdrcmplt, true),
% Return packets sent from the interface
- {ok, _} = attr(Socket, seesent, true),
+ {ok, _} = ctl(Socket, seesent, true),
% Get bpf buf len
- attr(Socket, blen).
+ ctl(Socket, blen).
Please sign in to comment.
Something went wrong with that request. Please try again.