Tim
Tim

Reputation: 375

How to use TIdSNMP to receive trap notifications from multiple sources

We are developing a program in Delphi XE5 to monitor MFPs in a local network. It is important that for each incoming SNMP trap notification we are able to determine the MFP that sent it. It seems that TIdSNMP.ReceiveTrap places each notification that was received since the last call into an element of the TIdSNMP.Trap.Value array property; TIdSNMP.Trap.Host then contains the IP address of the MFP which sent the latest trap notification. Can somebody confirm this? Is there some way to obtain the IP addresses corresponding to the other elements of TIdSNMP.Trap.Value?

For testing purposes we are using the following code to receive and display SNMP trap messages:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdSNMP;

type
  TForm1 = class(TForm)
    Button2: TButton;
    IdSNMP1: TIdSNMP;
    Memo1: TMemo;
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);

  procedure FormatTrap(ASnmpInfo : TSnmpInfo);
  var
    i : integer;
  begin
    Memo1.Lines.Add('{');
    with ASnmpInfo do begin
      Memo1.Lines.Add(Format('Host=%s, ', [Host]));
      Memo1.Lines.Add(Format('Port=%d, ', [Port]));
      Memo1.Lines.Add(Format('Enterprise=%s, ', [Enterprise]));
      Memo1.Lines.Add(Format('GenTrap=%d, ', [GenTrap]));
      Memo1.Lines.Add(Format('SpecTrap=%d, ', [SpecTrap]));
      Memo1.Lines.Add(Format('Version=%d, ', [Version]));
      Memo1.Lines.Add(Format('PDUType=%d, ', [PDUType]));
      Memo1.Lines.Add(Format('TimeTicks=%d, ', [TimeTicks]));
      Memo1.Lines.Add(Format('ID=%d, ', [ID]));
      Memo1.Lines.Add(Format('ErrorStatus=%d, ', [ErrorStatus]));
      Memo1.Lines.Add(Format('ErrorIndex=%d, ', [ErrorIndex]));
      Memo1.Lines.Add(Format('Community=%s, ', [Community]));
      Memo1.Lines.Add(Format('ValueCount=%d, ', [ValueCount]));
      for i := 0 to ValueCount-1 do begin
        Memo1.Lines.Add( Format('Value[%d]=%s, ', [i,Value[i]]));
        Memo1.Lines.Add( Format('ValueOID[%d]=%s, ', [i,ValueOID[i]]));
        Memo1.Lines.Add( Format('ValueType[%d]=%d, ', [i,ValueType[i]]));
      end;
      Memo1.Lines.Add('}');
    end;
  end;

begin
  while not Application.Terminated do begin
    if IdSNMP1.ReceiveTrap() then begin
      FormatTrap(IdSNMP1.Trap);
    end;
    Sleep(100);
    Application.ProcessMessages();
  end;
end;

end.

Two MFPs -- a Konica Minolta bizhub C364e under 192.168.197.159 and a Konica Minolta bizhub C364 under 192.168.197.19 -- have been configured to send SNMP trap notifications to the computer this program is running on. Here are some sample results:

{
Host=192.168.197.159, 
Port=32884, 
Enterprise=1.3.6.1.4.1.18334, 
GenTrap=6, 
SpecTrap=10, 
Version=0, 
 PDUType=164, 
TimeTicks=7792839, 
ID=0, 
ErrorStatus=0, 
ErrorIndex=0, 
Community=public, 
ValueCount=5, 
Value[0]=No Paper, 
ValueOID[0]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[0]=4, 
 Value[1]=No Paper, 
ValueOID[1]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[1]=4, 
Value[2]=No Paper, 
ValueOID[2]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[2]=4, 
Value[3]=No Paper, 
ValueOID[3]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
 ValueType[3]=4, 
Value[4]=Job End, 
ValueOID[4]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[4]=4, 
}

{
Host=192.168.197.19, 
Port=53365, 
Enterprise=1.3.6.1.4.1.18334, 
GenTrap=6, 
SpecTrap=10, 
Version=0, 
 PDUType=164, 
TimeTicks=12469234, 
ID=0, 
ErrorStatus=0, 
ErrorIndex=0, 
Community=public, 
ValueCount=6, 
Value[0]=No Paper, 
ValueOID[0]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[0]=4, 
 Value[1]=No Paper, 
ValueOID[1]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[1]=4, 
Value[2]=No Paper, 
ValueOID[2]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[2]=4, 
Value[3]=No Paper, 
ValueOID[3]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
 ValueType[3]=4, 
Value[4]=Job End, 
ValueOID[4]=1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2, 
ValueType[4]=4, 
Value[5]=Job End, 
ValueOID[5]=1.3.6.1.4.1.18334.1.1.1.2.1.83.3.1, 
ValueType[5]=4, 
}

1.3.6.1.4.1.18334.1.1.1.2.1.105.2.2 is the SysObjectID for C364e and 1.3.6.1.4.1.18334.1.1.1.2.1.83.3.1 the SysObjectID for C364. So it seems that all but the very last notification comes from the MFP under 192.168.197.159, although for the last 6 notifications TIdSNMP.Trap.Host contains the value 192.168.197.19.

Upvotes: 0

Views: 3797

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 597550

What you describe looks like a bug in TIdSNMP.ReceiveTrap(). It is not calling TIdSNMP.Trap.Clear() each time. It is not supposed to keep a history of received traps over time. I have just checked in a fix into Indy's SVN. If you do not want to, or cannot, upgrade your Indy install, a simple workaround would be to call IdSNMP1.Trap.Clear() before calling IdSNMP1.ReceiveTrap():

while not Application.Terminated do begin
  IdSNMP1.Trap.Clear(); // <-- add this
  if IdSNMP1.ReceiveTrap() then begin
    FormatTrap(IdSNMP1.Trap);
  end;
  Sleep(100);
  Application.ProcessMessages();
end;

With that said, using a busy loop inside a UI click handler is not a good idea. I would suggest you use a TTimer instead:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages,
  System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  Vcl.ExtCtrls, IdBaseComponent, IdComponent, IdUDPBase, IdUDPClient, IdSNMP;

type
  TForm1 = class(TForm)
    Button2: TButton;
    IdSNMP1: TIdSNMP;
    Memo1: TMemo;
    Timer1: TTimer;
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
begin
  Timer1.Interval := 100;
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);

  procedure FormatTrap(ASnmpInfo : TSnmpInfo);
  var
    i : integer;
  begin
    Memo1.Lines.BeginUpdate;
    try
      Memo1.Lines.Add('{');
      Memo1.Lines.Add(Format('Host=%s, ', [ASnmpInfo.Host]));
      Memo1.Lines.Add(Format('Port=%d, ', [ASnmpInfo.Port]));
      Memo1.Lines.Add(Format('Enterprise=%s, ', [ASnmpInfo.Enterprise]));
      Memo1.Lines.Add(Format('GenTrap=%d, ', [ASnmpInfo.GenTrap]));
      Memo1.Lines.Add(Format('SpecTrap=%d, ', [ASnmpInfo.SpecTrap]));
      Memo1.Lines.Add(Format('Version=%d, ', [ASnmpInfo.Version]));
      Memo1.Lines.Add(Format('PDUType=%d, ', [ASnmpInfo.PDUType]));
      Memo1.Lines.Add(Format('TimeTicks=%d, ', [ASnmpInfo.TimeTicks]));
      Memo1.Lines.Add(Format('ID=%d, ', [ASnmpInfo.ID]));
      Memo1.Lines.Add(Format('ErrorStatus=%d, ', [ASnmpInfo.ErrorStatus]));
      Memo1.Lines.Add(Format('ErrorIndex=%d, ', [ASnmpInfo.ErrorIndex]));
      Memo1.Lines.Add(Format('Community=%s, ', [ASnmpInfo.Community]));
      Memo1.Lines.Add(Format('ValueCount=%d, ', [ASnmpInfo.ValueCount]));
      for i := 0 to ASnmpInfo.ValueCount-1 do begin
        Memo1.Lines.Add( Format('Value[%d]=%s, ', [i,ASnmpInfo.Value[i]]));
        Memo1.Lines.Add( Format('ValueOID[%d]=%s, ', [i,ASnmpInfo.ValueOID[i]]));
        Memo1.Lines.Add( Format('ValueType[%d]=%d, ', [i,ASnmpInfo.ValueType[i]]));
      end;
      Memo1.Lines.Add('}');
    finally
      Memo1.Lines.EndUpdate;
    end;
  end;

begin
  IdSNMP1.Trap.Clear();
  if IdSNMP1.ReceiveTrap() then begin
    FormatTrap(IdSNMP1.Trap);
  end;
end;

end.

Upvotes: 2

Related Questions