lextudio / sharpsnmplib

Sharp SNMP Library- Open Source SNMP for .NET
https://sharpsnmp.com
MIT License
356 stars 152 forks source link

SNMP Broadcast? #368

Closed lextudio-support closed 2 hours ago

lextudio-support commented 2 hours ago

Hi,
I've just started playing with SharpSNMPLib, and so far, I think it's a great project. I'm building a "wrapper" around it to ease management of devices that I work with (for example, a simple "GetDeviceStatus()" method which queries a particularly status table in our devices' own private MIB, and returns the values as an easy to work with format that I've defined)

However, I haven't found a way to do a "broadcast" - technically, I can actually broadcast just by specifying a broadcast address, but I only get the result from the first reponder of course, not all of them.
For example, if I do something like:
Manager snmpMgr = new Manager(); IPAddress ipAddy = IPAddress.Parse("192.168.0.255"); Variable var = snmpMgr.Get(ipAddy, "public", new Variable(OIDStringToUInt("1.3.6.1.2.1.1.1.0")));
Then (of course) var contains only the response from the first device on the network that replies. (just to note, yes, it definitely does do that - I tested)
What I'd really like is to be able to get the responses from ALL devices on the network that reply. So, it would be very nice if there were something like:
Variable[] var = snmpMgr.GetBroadcast(ipBroadcastAddress, "public", new Variable(OIDStringToUInt("1.3.6.1.2.1.1.1.0")));
If there actually already is an easy way to do this, please let me know! I definitely can't find any such function...

(by the way: "OIDStringToUInt" is my own little method - the name makes it obvious what it does!)

Original Reported Date: 2008-08-05T02:30:36.42-07:00 Original CodePlex Discussion Thread ID: 32902

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Yep, I am searching for that possibility of broadcasting, too. But I do not think it is reasonable to place the broadcasting logic inside #SNMP library (such as Manager component).

My understanding is, there should be a way to broadcast in LAN and receive general responses from all network elements inside. Then when you have a list of IP addresses generated from the responses, you can start a loop and use SNMP to contact each of them. This can be considered as a new feature, Agent Auto Discovery. However, I am not sure when I have time to work on it.

Original Posted Date: 2008-08-05T21:13:39.647-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi Lextm,
Thanks for the answer.
However, I'd think that inside the Manager would be a sensible place to put the logic for it - SNMP is often used for these kinds of broadcasts, not just "one to one". So, using some kind of "FindDevices()" and then looping through the results to actually get the variables you want just seems a little messy and would be a lot more network traffic than is required (which is pretty much the point of using a broadcast!).
I'm not saying it really has to be like my example of "Manager.GetBroadcast" above - there's quite a few ways it could be implemented, but it really SHOULD be inside the Manager (or something very much like the Manager).
Perhaps an alternative you might prefer would be something like "Manager.StartBroadcast" and then have a "ResultReceived" event being triggered for each reply that comes in within the timeout? That would also be okay from my point of view (but I actually still prefer my original idea since it's less work!)
I'll look in to implementing something myself (to be honest, I haven't even looked at the source code yet, I've just been using the assembly based on your examples) and if I come up with a nice solution that feels "comfortable", I'll let you know and you're free to do what you like with it.

Original Posted Date: 2008-08-05T23:51:50.497-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, nice to hear details about your idea. Yes, it sounds reasonable, but I do not find a way inside SNMP standard that can broadcast GET messages. Therefore I posted my understanding above. Maybe I was wrong.

Please feel free to play with #SNMP, even with the source code (notice it is covered by LGPL or BSD). It is an open source project, so you can modify it to meet your needs.

-Lex

Original Posted Date: 2008-08-06T01:01:57.58-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

I'll defnitely look in to doing it myself and let you know the results. Unfortunately, another large project just got lumped on me at work, so I probably won't have the opportunity to do it for a week or so now.

Just for your information though - you can already broadcast "Get" messages with the current implementation you have. SNMP broadcast is as simple as specifying a broadcast address (e.g. "192.168.0.255") rather than the IP address of an individual agent - if you do that even with your current implementation, it works perfectly, and every agent that gets the broadcast will respond (the issue is that currently, #SNMP only gets the first response)

Original Posted Date: 2008-08-06T02:19:22.993-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, I have checked #SNMP Library source code. Because GetRequestMessage.Send returns immediately after receiving the first GetResponseMessage, all later responses are ignored. So IMHO a fix can be added like this,

Create a GetRequestMessage.SendAsync which does not wait any response and return (therefore, a new Manager.GetAsync should be added, too). Then let Manager monitors responses asynchronously. However, this confuzes me because I don't know how to tell Manager where to monitor those responses.

We may discuss about the technical details later once you have time. Thank you for sharing those nice ideas, and I have learned a lot.

-Lex

Original Posted Date: 2008-08-06T19:32:30.357-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, I have just find some clues from Wikipedia,

Typically, SNMP uses UDP ports 161 for the agent and 162 for the manager. The Manager may send Requests from any available ports (source port) to port 161 in the agent (destination port). The agent response will be given back to the source port. [http://en.wikipedia.org/wiki/Simple_Network_Management_Protocol]

Therefore, it is possible to implement asynchronous GET, SET, GETNEXT, GETBULK and so on. Responses can be captured by monitoring the source port. Now the question is how to save the source port to Manager from GetRequestMessage.Send (or AsyncSend) and monitor responses. And another question is how to make the API easy to understand and use. :-)

-Lex

Original Posted Date: 2008-08-06T20:05:59.407-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

I have a network here at work with around 150 SNMP agents on it (mostly devices made by us, but also quite a few others (routers, PCs, etc)). I just checked with Wireshark, and every single one of them responds on the same port that the request came from, so I can confirm what Wikipedia is saying.

I actually think it would be best to implement it in two different ways in the Manager - one synchronous and one asynchronous.
The synchronous method would be almost identical to the current method, but instead of returning immediately upon getting a response, it waits for the specified timeout and then returns all responses as an array. That would be, I think, very easy to implement and also the most transparent to the person using the API (especially people who are already familiar with the current methods that are exposed).
The asynchronous method should basically be a kind of "listening" thread that starts when the request is issued and stops after the timeout. During that time, events are triggered when a response comes in. This seems "nicer" from a technical perspective, but I do actually wonder about its use, since in general people set SNMP timeouts to be pretty short (even on my work network, which is constantly pretty choked, I get all responses within about 1.5 seconds). Despite it being more technically elegant, I also can't imagine myself using it - if I'm broadcasting, I don't want to do anything with the responses until I have all of them...
Of course, thinking about it this way, one way to do the synchronous method would actually be to create the asynchronous method first and then just "wrap" it for the synchronous one.

Actually, that reminds me of another topic I wanted to ask you about - I'll post a new thread for that though to avoid cluttering this one...

Original Posted Date: 2008-08-07T01:36:36.963-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, I have done an experimental synchronous implementation. And I just want it to be used for agent discovery. Therefore, this is simply a new method in Manager called Discover. TestDiscovery demo is added in source code package, too.

Because I don't have such a nice test environment like you, I cannot test if my experiment works fine. May you check out the latest source code and have a test? Thank you.

-Lex

Original Posted Date: 2008-08-10T22:44:57.19-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Sorry for the delay in replying - busy at work, and I just had a couple of days off, where I decided I wouldn't go NEAR a piece of code... it's needed once in a while!

I've just given it a quick test, and unfortunately, I only get one response (only one item in the Dictionary).
Checking with Wireshark shows it's the FIRST response that appears in the byte array, all other responses are ignored.
I added a breakpoint after you create the byte array to check what's in it, and there's definitely only the one response in there (170ish bytes instead of the several KB I'd expect), so the problem is before it gets to the parsing (MessageFactory.ParseMessages)
Looking at it, you're calling result.AsyncWaitHandle.WaitOne, then immediately (well, after a check for the timeout) calling udp.EndReceive. This means that as soon as it gets the first response, it stops listening, and only gives the first result.
Because you don't know how many results there should be, really you should be waiting for the timeout to pass (and not throwing an exception at that point) rather than just calling WaitOne.
Unfortuantely, it's not just as simple as that, even replacing the WaitOne with a simple Thread.Sleep(timeout), it still only returns one result. A quick check of the documentation for UdpClient seems to suggest to me that this is what we should expect from BeginReceive... calling BeginReceive does NOT behave as you'd really expect from the name - it receives a single datagram only, not "continuously receive until EndReceive is called".

Just to test it quickly, I created a fixed loop (20 times) and called BeginReceive/EndReceive each time within this loop. It successfully received the first 20 responses and put it in the byte array (note that EndReceive actually just waits forever if there's no data, so an infinite loop is a bad idea!). Since you don't actually know when it's finished (no way to tell if the "last" response has come through or not) you basically need to wait for the timeout, and simply have collected all responses during that time.
What I did is obviously not a solution (you might have less than 20 items, or you might have more, and I effectively ignored the timeout as well), but it gives an idea of how to go forward and make a real solution to it. As I mentioned earlier though, I'm a bit short on time, so don't really have the chance right now to fix it myself... have I given you enough info to go on?

The next problem is smaller, but still something I hit - your IPEndPoint called "from" doesn't get changed during the foreach loop on the messages, so on the second iteration through, it gives an exception that an item with that key has already been added to the dictionary. You're updating "from" at udp.EndReceive, so it ends up being the last one that you collected data from (this would be true even without the loop I added, except it'd be the first and only item instead). I think what you'll need to do is either store the address along with the message somehow (maybe a new simple structure for it?) or just create a second list that'll "match up" with the first one as you iterate through the foreach loop. However you want to do it, this should be pretty easy to fix.

Hope that's helpful :-)

Original Posted Date: 2008-08-13T04:44:39.13-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, wonderful analysis. I finally know why I successfully discovered a new SNMP agent in my network (it must be a public network device), but could not discover my own PC. Therefore, I will continue my investigation according to your suggestions. I post any progress here tomorrow night.

-Lex

Original Posted Date: 2008-08-13T06:30:23.107-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

And may you test the latest source code again? This time I use a TrapListener to monitor responses from agents.

Original Posted Date: 2008-08-14T20:54:58.757-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi Lex,
I just did a "1 minute test" (compiled and ran the discovery test, looked at the results) - it looks good! It found all the devices that I was expecting it to.
It should now be trivial to modify this to broadcast for any variables I want to get. Thanks a lot for your efforts on this.

Original Posted Date: 2008-08-15T00:19:36.57-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Aha, happy to see it works as expected. (But it also means inside my network there is only one SNMP agent alive.)

Yes, you can modify this to broadcast any variables, but why you'd like to broadcast other variables?

I think the broadcasting is only useful when you need to find what agents are alive there. After discovering them, management may be done one by one. :-)

-Lex

Original Posted Date: 2008-08-15T02:23:32.807-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

The devices (agents) I deal with at work are MFPs (printer/scanner/copier/fax/things). They have a large and "complex" private MIB, with a LOT of information. Doing a broadcast for other variables could be useful for things like: "Find all devices on the network that can do colour", or "Find all devices on the network that have fax capabilities" etc etc.
I certainly COULD just "discover" them and then go through each individually, but that's more network traffic, and more code. It's quicker to just broadcast for the specific OID and see what responses I get.
I think there are probably quite a few other uses for other kinds of devices, so it's not just a specific case for me, but it's up to you whether you implement this or not. I'll definitely be putting it in for my own use, and then it's up to you whether you merge that back in to the main, or whether my version is a fork of yours. Once I've done it, I'll send the code your way and you can decide what you want to do with it.
Either way, thanks again for all the help!

Original Posted Date: 2008-08-15T04:10:35.677-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

No devices were found in my network with the latest version. Machine 192.168.4.178 should be SNMP-capable device, my IP is 192.168.4.192.

Wireshark log:

Time Source Destination Protocol Info

35 8.615122 192.168.4.162 255.255.255.255 SNMP get-request SNMPv2-MIB::sysDescr.0
36 0.000728 192.168.4.178 192.168.4.162 SNMP get-response SNMPv2-MIB::sysDescr.0
37 0.000055 192.168.4.162 192.168.4.178 ICMP Destination unreachable (Port unreachable)

Original Posted Date: 2008-08-15T06:05:30.67-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi, I do not want to change Manager.Discover any more because it already meets my design intent. But what YttriumOx wants can be done in GetRequestMessage.Broadcast. If I add a new argument in it so the broadcasting variable can be passed in, most of us may be happy.

Dear k59319, is it only 192.168.4.178 missing while the other two found? And I never use Wireshark so I do not know why #SNMP fails in this case. How to understand the line "36 0.000728 192.168.4.178 192.168.4.162 SNMP get-response SNMPv2-MIB::sysDescr.0"? It is puzzling the fourth place is 192.168.4.162 instead of 255.255.255.255. :-)

-Lex

Original Posted Date: 2008-08-15T07:16:32.07-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi k59319, I don't know if I have just reproduced your case or not. I have just found the result list returned by broadcasting is not stable. In my network, sometimes it returns four agents while other times it returns three or even none. I have no idea how to overcome this instability at this moment.

-Lex

Original Posted Date: 2008-08-18T00:13:43.99-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Finally I knew that Wireshark is Ethereal. Therefore I did a test and reproduced the issue. Yes, the first few responses are not caught by #SNMP. Actually the TrapListener starts to monitor responses after the broadcast, so there may be a significant delay that probably causes this issue. I will try to locate the bug at first. I will keep you updated.

-Lex

Original Posted Date: 2008-08-19T01:44:03.793-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi guys, if you follow this post, I'd like to announce a new implementation of Manager.Broadcast in Change Set 15225. I think this time I have fixed the bug reported by k59319. I am going to do more testing next week.

-Lex

Original Posted Date: 2008-08-22T07:25:34.033-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

A test in my office environment illustrated that this bug had been fixed. k59319, may you test the latest Change Set in your environment?

Original Posted Date: 2008-08-24T21:09:25.063-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

Hi Lex,
I did some more tests, and was able to confirm the bug that k59319 found. I think I just didn't notice it at first due to the number of devices on my network (it's hard to notice when just one or two agents are missing!). I compared the output of a Wireshark (yes, Ethereal) trace to the output of the program, and saw the missing devices.
I just tested again with the new version, and everything looks better now.

Just for reference, I actually tested two scenarios - one is a "complete" broadcast (255.255.255.255) and the other is a broadcast of just my local subnet (192.168.0.255) - both appear to work well now.

- YttriumOx

Original Posted Date: 2008-08-25T01:37:18.467-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

YttriumOx and k59319, thank you so much. Now I can move on to other features on the library.

- Lex

Original Posted Date: 2008-08-25T21:41:16.277-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

is this what now the Discoverer is doing?

What is the criteria that an device is responding?

With the discoverer i get two devices in my network, but at least one printer is there too which i can access manually via snmp..

ip from my machine is 192.168.99.6. I did an complete broadcast, 255.255.255.255.

The responding devices are 192.168.99.227 and 192.168.99.251

The printer which i dont get an response of has the IP 192.168.99.224

any ideas?

Original Posted Date: 2011-05-18T00:21:29.847-07:00

lextudio-support commented 2 hours ago

Copied from CodePlex without authors:

There are a few things that can prevent Discoverer from discovering a certain device,

The device manual may contain more information on how to troubleshoot, and network packet captures may provide some hints.

Regards,

Lex

Original Posted Date: 2011-05-21T01:43:25.247-07:00

lextudio-support commented 2 hours ago

Marked as Answer Date: 2013-10-06T04:44:54.08-07:00