3709192002-03-20Martin Nilsson #pike __REAL_VERSION__
6790812000-12-05Honza Petrous //: //: Honza Petrous, 2000-10-07 (on the 'coding party' after user conference :)
dc93732015-08-22Martin Nilsson  //! SNMP protocol implementation for Pike, implementing @rfc{1155@} //! and @rfc{1901@}.
6790812000-12-05Honza Petrous  #include "snmp_globals.h" #include "snmp_errors.h"
a1933e2014-06-20Martin Nilsson inherit Stdio.UDP : snmp;
6790812000-12-05Honza Petrous 
a1933e2014-06-20Martin Nilsson import Standards.ASN1.Types;
6790812000-12-05Honza Petrous 
d15deb2014-11-12Martin Karlgren #define U(C,T) make_combined_tag(C,T)
f75fc42014-11-11Martin Karlgren 
9eaf1d2008-06-28Martin Nilsson protected mapping snmp_type_proc =
a1933e2014-06-20Martin Nilsson  ([ // from RFC-1065 :
d15deb2014-11-12Martin Karlgren  U(1,0) : OctetString, // ipaddress U(1,1) : Integer, // counter U(1,2) : Integer, // gauge U(1,3) : Integer, // timeticks U(1,4) : OctetString, // opaque
a1933e2014-06-20Martin Nilsson  // v2
d15deb2014-11-12Martin Karlgren  U(1,6) : Integer, // counter64
a1933e2014-06-20Martin Nilsson  // context PDU
d15deb2014-11-12Martin Karlgren  U(2,0) : Sequence, U(2,1) : Sequence, U(2,2) : Sequence, U(2,3) : Sequence,
a1933e2014-06-20Martin Nilsson  ]);
6790812000-12-05Honza Petrous  //: //: public vars //: //: //: private variables //:
a99b3f2002-12-03Honza Petrous private int remote_port; // = SNMP_DEFAULT_PORT; private string local_host = SNMP_DEFAULT_HOST; private string remote_host; private int request_id = 1; private int next_id = 1;
d3b0cb2002-11-30H. William Welliver III  //! SNMP version
3524712015-05-26Martin Nilsson //!
d3b0cb2002-11-30H. William Welliver III //! currently version 1 and 2 are supported.
6790812000-12-05Honza Petrous int snmp_version = SNMP_DEFAULT_VERSION;
d3b0cb2002-11-30H. William Welliver III  //! SNMP community string //! //! should be set to the appropriate SNMP community before sending a request.
80b7ca2002-12-03Honza Petrous //! //! @note //! Set to "public" by default.
6790812000-12-05Honza Petrous string snmp_community = SNMP_DEFAULT_COMMUNITY;
d3b0cb2002-11-30H. William Welliver III 
6790812000-12-05Honza Petrous int snmp_errno = SNMP_SUCCESS; int ok;
3524712015-05-26Martin Nilsson //:
6790812000-12-05Honza Petrous //: msg pool //:
a99b3f2002-12-03Honza Petrous private mapping msgpool = ([]);
6790812000-12-05Honza Petrous  //: //: callback support //:
4b516a2008-01-04Martin Nilsson private function con_fail;
a99b3f2002-12-03Honza Petrous private array extra_args;
6790812000-12-05Honza Petrous 
553ed32002-11-30H. William Welliver III //! create a new SNMP protocol object //! //! @param rem_port //! @param rem_addr //! remote address and UDP port (optional) //! @param loc_port //! @param loc_addr
80b7ca2002-12-03Honza Petrous //! local address and UDP port (optional)
553ed32002-11-30H. William Welliver III //!
0af55a2005-11-26Martin Nilsson void create(int|void rem_port, string|void rem_addr, int|void loc_port, string|void loc_addr) {
2f806e2005-11-26Martin Nilsson 
6790812000-12-05Honza Petrous  int lport = loc_port; local_host = (!loc_addr || !sizeof(loc_addr)) ? SNMP_DEFAULT_LOCHOST : loc_addr; if(stringp(rem_addr) && sizeof(rem_addr)) remote_host = rem_addr;
70b4b72002-12-01Martin Stjernholm  if(rem_port) remote_port = rem_port;
6790812000-12-05Honza Petrous 
f5dc042013-09-03Tobias S. Josefowitz  if (!snmp::bind(lport, local_host, 1)) {
6790812000-12-05Honza Petrous  //# error ... DWRITE("protocol.create: can't bind to the socket.\n"); ok = 0; if(con_fail)
563bd72004-01-11Martin Nilsson  con_fail(this, @extra_args);
6790812000-12-05Honza Petrous  } if(snmp_errno)
2f806e2005-11-26Martin Nilsson  ERROR("Failed to bind to SNMP port.\n");
6790812000-12-05Honza Petrous  DWRITE("protocol.bind: success!\n");
2f806e2005-11-26Martin Nilsson  DWRITE("protocol.create: local adress:port bound: [%s:%d].\n", local_host, lport);
6790812000-12-05Honza Petrous  }
553ed32002-11-30H. William Welliver III //! return the whole SNMP message in raw format
2f806e2005-11-26Martin Nilsson mapping readmsg(int|float|void timeout) {
6790812000-12-05Honza Petrous  mapping rv;
29bb4d2006-01-20Martin Nilsson  if(timeout && !wait(timeout)) return 0;
6790812000-12-05Honza Petrous  rv = read();
6659452003-09-01Martin Nilsson  return rv;
6790812000-12-05Honza Petrous }
553ed32002-11-30H. William Welliver III //! decode ASN1 data, if garbaged ignore it
b399dd2001-03-30Honza Petrous mapping decode_asn1_msg(mapping rawd) {
6790812000-12-05Honza Petrous 
25415e2014-06-21Martin Nilsson  Object xdec = Standards.ASN1.Decode.simple_der_decode(rawd->data, snmp_type_proc);
6790812000-12-05Honza Petrous  string msgid = (string)xdec->elements[2]->elements[0]->value; int errno = xdec->elements[2]->elements[1]->value; mapping msg = ([ "ip":rawd->ip, "port":rawd->port, "error-status":errno, "error-string":snmp_errlist[errno], "error-index":xdec->elements[2]->elements[2]->value,
b399dd2001-03-30Honza Petrous  "version":xdec->elements[0]->value, "community":xdec->elements[1]->value, "op":xdec->elements[2]->get_tag(),
2f806e2005-11-26Martin Nilsson  "attribute":map(xdec->elements[2]->elements[3]->elements, lambda(object duo) { return ([ (array(string))(duo->elements[0]->id)*".":duo->elements[1]->value ]); } )
6790812000-12-05Honza Petrous  ]);
6659452003-09-01Martin Nilsson  return ([msgid:msg]);
b399dd2001-03-30Honza Petrous }
553ed32002-11-30H. William Welliver III //! decode raw pdu message and place in message pool
b399dd2001-03-30Honza Petrous void to_pool(mapping rawd) { //: put decoded msg to the pool msgpool += decode_asn1_msg(rawd);
6790812000-12-05Honza Petrous } mapping|int from_pool(string msgid) { //: get data from poll
65340d2014-08-15Martin Nilsson  return m_delete(msgpool, msgid);
6790812000-12-05Honza Petrous }
553ed32002-11-30H. William Welliver III //! read decoded message from pool
a99b3f2002-12-03Honza Petrous private mapping readmsg_from_pool(int msgid) {
6790812000-12-05Honza Petrous  //: read SNMP response PDU from PDU pool mapping rv = from_pool((string)msgid); if(rv)
6659452003-09-01Martin Nilsson  return rv;
6790812000-12-05Honza Petrous  #define HACK 1 #ifdef HACK mapping tmpm = readmsg(); to_pool(tmpm); #endif
6659452003-09-01Martin Nilsson  return from_pool((string)msgid);
6790812000-12-05Honza Petrous }
a99b3f2002-12-03Honza Petrous private int writemsg(string rem_addr, int rem_port, object pdu) {
6790812000-12-05Honza Petrous  //: send SNMP encoded message and return status //: OK, in most cases :) object msg; string rawd; int msize;
c42e812014-06-20Martin Nilsson  msg = Sequence(({ Integer(snmp_version-1), OctetString(snmp_community),
6790812000-12-05Honza Petrous  pdu})); rawd = msg->get_der();
2f806e2005-11-26Martin Nilsson  DWRITE("protocol.writemsg: %O\n", rawd);
6790812000-12-05Honza Petrous  msize = send(rem_addr, rem_port, rawd);
6659452003-09-01Martin Nilsson  return (msize = sizeof(rawd) ? SNMP_SUCCESS : SNMP_SEND_ERROR);
6790812000-12-05Honza Petrous }
a99b3f2002-12-03Honza Petrous private int get_req_id() {
6790812000-12-05Honza Petrous  //: returns unicate id //LOCK int rv = request_id++; //UNLOCK
6659452003-09-01Martin Nilsson  return rv;
6790812000-12-05Honza Petrous } //:
b399dd2001-03-30Honza Petrous //: read_response
6790812000-12-05Honza Petrous //:
b399dd2001-03-30Honza Petrous array(mapping)|int read_response(int msgid) {
6790812000-12-05Honza Petrous  mapping rawpdu = readmsg_from_pool(msgid);
6659452003-09-01Martin Nilsson  return ({rawpdu});
6790812000-12-05Honza Petrous }
553ed32002-11-30H. William Welliver III //! //! GetRequest-PDU call //! //! @param varlist //! an array of OIDs to GET //! @param rem_addr //! @param rem_port //! remote address an UDP port to send request to (optional) //! @returns //! request ID
6790812000-12-05Honza Petrous int get_request(array(string) varlist, string|void rem_addr, int|void rem_port) { object pdu; int id = get_req_id(), flg; array vararr = ({}); foreach(varlist, string varname)
c42e812014-06-20Martin Nilsson  vararr += ({Sequence( ({Identifier(@(array(int))(varname/".")), Null()})
6790812000-12-05Honza Petrous  )});
299ee32014-06-25Martin Nilsson  pdu = ASN1_CONTEXT_SEQUENCE(0,
c42e812014-06-20Martin Nilsson  ({Integer(id), // request-id Integer(0), // error-status Integer(0), // error-index Sequence(vararr)})
6658ef2001-09-11Honza Petrous  );
6790812000-12-05Honza Petrous  // now we have PDU ...
2f806e2005-11-26Martin Nilsson  flg = writemsg(rem_addr||remote_host, rem_port || remote_port || SNMP_DEFAULT_PORT, pdu);
6790812000-12-05Honza Petrous  return id; }
c42e812014-06-20Martin Nilsson Object mk_asn1_val(string type, int|string val) {
b399dd2001-03-30Honza Petrous // returns appropriate ASN.1 value
c42e812014-06-20Martin Nilsson  Object rv;
b399dd2001-03-30Honza Petrous  switch(type) { case "oid": // OID
c42e812014-06-20Martin Nilsson  rv = Identifier( @(array(int))(val/".") );
b399dd2001-03-30Honza Petrous  break; case "int": // INTEGER
c42e812014-06-20Martin Nilsson  rv = Integer(val);
b399dd2001-03-30Honza Petrous  break; case "str": // STRING
c42e812014-06-20Martin Nilsson  rv = OctetString(val);
b399dd2001-03-30Honza Petrous  break; case "ipaddr": // ipAddress
5bdaf32014-06-20Martin Nilsson  rv = OctetString(val[..3]); rv->cls = 1; rv->tag = 0;
b399dd2001-03-30Honza Petrous  break; case "count": // COUNTER
5bdaf32014-06-20Martin Nilsson  rv = Integer(val); rv->cls = 1; rv->tag = 1;
b399dd2001-03-30Honza Petrous  break; case "gauge": // GAUGE
5bdaf32014-06-20Martin Nilsson  rv = Integer(val); rv->cls = 1; rv->tag = 2;
b399dd2001-03-30Honza Petrous  break; case "tick": // TICK
5bdaf32014-06-20Martin Nilsson  rv = Integer(val); rv->cls = 1; rv->tag = 3;
b399dd2001-03-30Honza Petrous  break; case "opaque": // OPAQUE
5bdaf32014-06-20Martin Nilsson  rv = OctetString(val); rv->cls = 1; rv->tag = 4;
b399dd2001-03-30Honza Petrous  break; case "count64": // COUNTER64 - v2 object
5bdaf32014-06-20Martin Nilsson  rv = Integer(val); rv->cls = 1; rv->tag = 6;
b399dd2001-03-30Honza Petrous  break; default: // bad type!
2f806e2005-11-26Martin Nilsson  error("Unknown SNMP data type %O.", type);
b399dd2001-03-30Honza Petrous  } return rv; }
553ed32002-11-30H. William Welliver III //! //! GetResponse-PDU call //! //! @param varlist //! a mapping containing data to return //! @mapping //! @member array oid1 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid1 //! @endarray //! @member array oid2 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid2 //! @endarray //! @member array oidn //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oidn //! @endarray //! @endmapping //! @param origdata //! original received decoded pdu that this response corresponds to //! @param errcode //! error code //! @param erridx //! error index //! @returns //! request ID
2f806e2005-11-26Martin Nilsson int get_response(mapping varlist, mapping origdata, int|void errcode, int|void erridx) {
b399dd2001-03-30Honza Petrous  //: GetResponse-PDU low call object pdu; int id = indices(origdata)[0], flg; array vararr = ({}); foreach(indices(varlist), string varname) if(arrayp(varlist[varname]) || sizeof(varlist[varname]) > 1) {
c42e812014-06-20Martin Nilsson  vararr += ({Sequence( ({Identifier(@(array(int))(varname/".")),
b399dd2001-03-30Honza Petrous  mk_asn1_val(varlist[varname][0], varlist[varname][1])}) )}); }
299ee32014-06-25Martin Nilsson  pdu = ASN1_CONTEXT_SEQUENCE(2,
c42e812014-06-20Martin Nilsson  ({Integer(id), // request-id Integer(errcode), // error-status Integer(erridx), // error-index Sequence(vararr)})
6658ef2001-09-11Honza Petrous  );
b399dd2001-03-30Honza Petrous  // now we have PDU ...
2f806e2005-11-26Martin Nilsson  flg = writemsg(origdata[id]->ip||remote_host, origdata[id]->port || remote_port || SNMP_DEFAULT_PORT, pdu);
b399dd2001-03-30Honza Petrous  return id; }
6790812000-12-05Honza Petrous 
553ed32002-11-30H. William Welliver III //! //! GetNextRequest-PDU call //! //! @param varlist //! an array of OIDs to GET //! @param rem_addr //! @param rem_port //! remote address an UDP port to send request to (optional) //! @returns //! request ID
6790812000-12-05Honza Petrous int get_nextrequest(array(string) varlist, string|void rem_addr, int|void rem_port) { //: GetNextRequest-PDU low call object pdu; int id = get_req_id(), flg; array vararr = ({}); foreach(varlist, string varname)
c42e812014-06-20Martin Nilsson  vararr += ({Sequence( ({Identifier(@(array(int))(varname/".")), Null()})
6790812000-12-05Honza Petrous  )});
299ee32014-06-25Martin Nilsson  pdu = ASN1_CONTEXT_SEQUENCE(1,
c42e812014-06-20Martin Nilsson  ({Integer(id), // request-id Integer(0), // error-status Integer(0), // error-index Sequence(vararr)})
6658ef2001-09-11Honza Petrous  );
6790812000-12-05Honza Petrous  // now we have PDU ...
2f806e2005-11-26Martin Nilsson  flg = writemsg(rem_addr||remote_host, rem_port || remote_port || SNMP_DEFAULT_PORT, pdu);
6790812000-12-05Honza Petrous  return id; }
553ed32002-11-30H. William Welliver III //! //! SetRequest-PDU call //! //! @param varlist //! a mapping of OIDs to SET //! @mapping //! @member array oid1 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid1 //! @endarray //! @member array oid2 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid2 //! @endarray //! @member array oidn //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oidn //! @endarray //! @endmapping //! @param rem_addr //! @param rem_port //! remote address an UDP port to send request to (optional) //! @returns //! request ID
d3b0cb2002-11-30H. William Welliver III //! //! @example //! // set the value of 1.3.6.1.4.1.1882.2.1 to "blah". //! object s=Protocols.SNMP.protocol(); //! s->snmp_community="mysetcommunity";
700e272002-12-04H. William Welliver III //! mapping req=(["1.3.6.1.4.1.1882.2.1": ({"str", "blah"})]);
d3b0cb2002-11-30H. William Welliver III //! int id=s->set_request(req, "172.21.124.32");
3524712015-05-26Martin Nilsson //!
b399dd2001-03-30Honza Petrous int set_request(mapping varlist, string|void rem_addr, int|void rem_port) { //: SetRequest-PDU low call object pdu; int id = get_req_id(), flg; array vararr = ({});
6790812000-12-05Honza Petrous 
b399dd2001-03-30Honza Petrous  foreach(indices(varlist), string varname)
c42e812014-06-20Martin Nilsson  vararr += ({Sequence( ({Identifier(@(array(int))(varname/".")),
b399dd2001-03-30Honza Petrous  mk_asn1_val(varlist[varname][0], varlist[varname][1])}) )});
6790812000-12-05Honza Petrous 
299ee32014-06-25Martin Nilsson  pdu = ASN1_CONTEXT_SEQUENCE(3,
c42e812014-06-20Martin Nilsson  ({Integer(id), // request-id Integer(0), // error-status Integer(0), // error-index Sequence(vararr)})
6658ef2001-09-11Honza Petrous  );
6790812000-12-05Honza Petrous 
b399dd2001-03-30Honza Petrous  // now we have PDU ...
2f806e2005-11-26Martin Nilsson  flg = writemsg(rem_addr||remote_host, rem_port || remote_port || SNMP_DEFAULT_PORT, pdu);
6790812000-12-05Honza Petrous 
b399dd2001-03-30Honza Petrous  return id;
6790812000-12-05Honza Petrous }
553ed32002-11-30H. William Welliver III  //! send an SNMP-v1 trap //! //! @param varlist //! a mapping of OIDs to include in trap //! @mapping //! @member array oid1 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid1 //! @endarray //! @member array oid2 //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oid2 //! @endarray //! @member array oidn //! @array //! @elem string type //! data type such as tick, oid, gauge, etc //! @elem mixed data //! data to return for oidn //! @endarray //! @endmapping //! @param oid //! @param type //! generic trap-type //! @param spectype //! specific trap-type //! @param ticks //! uptime //! @param locip //! originating ip address of the trap //! @param remaddr //! @param remport //! address and UDP to send trap to
3524712015-05-26Martin Nilsson //! @returns
553ed32002-11-30H. William Welliver III //! request id
6658ef2001-09-11Honza Petrous int trap(mapping varlist, string oid, int type, int spectype, int ticks, string|void locip, string|void remaddr, int|void remport) {
b399dd2001-03-30Honza Petrous  //: Trap-PDU low call
6790812000-12-05Honza Petrous  object pdu; int id = get_req_id(), flg; array vararr = ({});
6658ef2001-09-11Honza Petrous  string lip = "1234"; locip = locip || "0.0.0.0";
51dfdd2003-04-23Martin Nilsson  if (has_value(locip,":")) // FIXME: Can't handle IPv6 locip = "0.0.0.0";
6658ef2001-09-11Honza Petrous  if (sizeof(locip/".") != 4) locip = "0.0.0.0"; //FIXME: what for hell I want to do with such ugly value? sscanf(locip, "%d.%d.%d.%d", lip[0], lip[1], lip[2], lip[3]);
6790812000-12-05Honza Petrous  foreach(indices(varlist), string varname)
c42e812014-06-20Martin Nilsson  vararr += ({Sequence( ({Identifier(@(array(int))(varname/".")),
b399dd2001-03-30Honza Petrous  mk_asn1_val(varlist[varname][0], varlist[varname][1])})
6790812000-12-05Honza Petrous  )});
299ee32014-06-25Martin Nilsson  pdu = ASN1_CONTEXT_SEQUENCE(4,
6658ef2001-09-11Honza Petrous  ({
2f806e2005-11-26Martin Nilsson  // enterprise OID mk_asn1_val("oid", oid), // ip address (UGLY!) mk_asn1_val("ipaddr", lip), // type (0 = coldstart, ...) mk_asn1_val("int", type), // enterprise type mk_asn1_val("int", spectype), // uptime mk_asn1_val("tick", ticks), // optional vars
c42e812014-06-20Martin Nilsson  Sequence(vararr)
2f806e2005-11-26Martin Nilsson  })
3524712015-05-26Martin Nilsson 
6658ef2001-09-11Honza Petrous  );
6790812000-12-05Honza Petrous  // now we have PDU ...
2f806e2005-11-26Martin Nilsson  flg = writemsg(remaddr||remote_host, remport || remote_port || SNMP_DEFAULT_TRAPPORT, pdu);
6790812000-12-05Honza Petrous  return id; }