Source file netsnmp_monad.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
open Netsnmp_raw_monad

let option_default ~default v = match v with Some v -> v | None -> default

module type IO = Io_intf.S

module Netsnmp(IO : IO) : Netsnmp_intf.S with module IO := IO = struct
  open IO

  module ASN1_value = ASN1_value
  include Netsnmp_types
  module Mib = Mib_monad.Mib(IO)
  module Session = Session_monad.Session(IO)
  module Pdu = Pdu_monad.Pdu(IO)

  let list_iter ~f l =
    let rec loop = function
    | [] -> return ()
    | hd::tl -> f hd >>= fun () -> loop tl
    in
    loop l

  let list_fold ~init ~f l =
    let rec loop a = function
    | [] -> return a
    | hd::tl -> f a hd >>= fun a -> loop a tl
    in
    loop init l

  module Oid = struct
    include Oid

    (** Track the mibs that have been loaded *)
    let loaded_mibs = ref []
    let initialised = ref false

    let check_init () =
      match !initialised with
      | true -> return ()
      | false ->
        Mib.netsnmp_init_mib ()
        >>= fun () ->
        initialised := true;
        return ()

    let oid_module =
      let re = Re.(compile (rep1 (char ':'))) in
      fun oidstr ->
        match Re.split re oidstr with
        | oidm::_::_ -> Some oidm
        | _ -> None
    ;;

    let of_string oidstr =
      check_init ()
      >>= fun () ->
      match oid_module oidstr with
      | Some oidm -> begin
          if not (List.exists (String.equal oidm) !loaded_mibs) then begin
            Mib.netsnmp_read_module oidm
            >>= fun () ->
            loaded_mibs := oidm::!loaded_mibs;
            return ()
          end
          else
            return ()
        end
        >>= fun () ->
        Mib.get_node oidstr
      | None ->
        Mib.get_node oidstr
    ;;

    let to_string oidstr = check_init () >>= fun () -> Mib.snprint_objid oidstr
  end

  module Connection = struct
    type t = {
      session : Netsnmp_raw.Session.t
    ; mutable closed : bool
    }

    let version_auth_to_version = function
      | Snmp_version_auth.Version_1 _  -> Session.Snmp_version.Version_1
      | Version_2c _                   -> Version_2c
      | Version_3 _                    -> Version_3

    let close sess =
      begin
        if not sess.closed then Session.snmp_sess_close sess.session
        else return ()
      end
      >>= fun () ->
      sess.closed <- true;
      return ()

    let connect (cinfo:Connection_info.t) =
      Oid.check_init ()
      >>= fun () ->
      let version = version_auth_to_version cinfo.version_auth in
      let retries = option_default ~default:3 cinfo.retries in
      let timeout = option_default ~default:3_000_000 cinfo.timeout in
      let peername = cinfo.peername in
      let localname = option_default ~default:"" cinfo.localname in
      let local_port = option_default ~default:0 cinfo.local_port in
      let (community, securityName, securityAuthProto, securityAuthPassword) =
        match cinfo.version_auth with
        | Snmp_version_auth.Version_1 auth | Version_2c auth ->
          (auth.community, "", Session.Snmp_sec_auth_proto.Ignore, "")
        | Version_3 auth  ->
          ("", auth.securityName, auth.securityAuthProto, auth.securityAuthPassword)
      in
      Session.snmp_sess_init ()
      >>= fun netsnmp_session ->
      Session.snmp_sess_open
        ~netsnmp_session ~version ~retries ~timeout ~peername ~localname ~local_port
        ~community ~securityName ~securityAuthProto ~securityAuthPassword ()
      >>= fun session ->
      let t = {
        session
      ; closed = false
      }
      in
      let () = IO.gc_finalise (fun t -> close t) t in
      return t

    let with_connection cinfo ~f =
      connect cinfo
      >>= fun sess ->
      match f sess with
      | exception e -> close sess >>= fun () -> raise e
      | _ as res -> close sess >>= fun () -> return res

  end

  let add_mibdir p =
    Oid.check_init ()
    >>= fun () ->
    Mib.add_mibdir p
    >>= function
    | i when i < 0 -> raise (Failure ("add_mibdir failed: " ^ p))
    | _ -> return ()
  ;;

  let add_mib_paths paths = list_iter ~f:add_mibdir paths

  let check_sess (sess:Connection.t) =
    match sess.closed with
    | true -> raise (Failure "session has been closed")
    | _ -> ()
  ;;

  let get_s (sess:Connection.t) oids =
    let () = check_sess sess in
    let add_oid oid pdu = Oid.of_string oid >>= Pdu.snmp_add_null_var pdu in
    Pdu.snmp_pdu_create Pdu.Pdu_type.Get
    >>= fun pdu ->
    list_fold ~init:pdu ~f:(fun pdu oid -> add_oid oid pdu) oids
    >>= fun pdu ->
    Session.snmp_sess_synch_response sess.session pdu
  ;;

  let get (sess:Connection.t) oids =
    let () = check_sess sess in
    let add_oid oid pdu = Pdu.snmp_add_null_var pdu oid in
    Pdu.snmp_pdu_create Pdu.Pdu_type.Get
    >>= fun pdu ->
    list_fold ~init:pdu ~f:(fun pdu oid -> add_oid oid pdu) oids
    >>= fun pdu ->
    Session.snmp_sess_synch_response sess.session pdu
  ;;

  let get_next (sess:Connection.t) oid =
    let () = check_sess sess in
    Pdu.snmp_pdu_create Pdu.Pdu_type.Getnext
    >>= fun pdu ->
    Pdu.snmp_add_null_var pdu oid
    >>= fun pdu ->
    Session.snmp_sess_synch_response sess.session pdu
  ;;

  module Raw = struct
    module Oid = Oid
    module Pdu = Pdu_monad.Pdu(IO)
    module Mib = Mib_monad.Mib(IO)
    module Session = Session_monad.Session(IO)
  end
end