I'm having a problem while using the SNMP4J library. My code works great when the SNMP server I'm sending the request to is running, but when it isn't running, I want the SNMP request to eventually time-out. However, it never times out. For example, the code below, even when dcadb2 does not exist (i.e. can not ping this host name), the program will never print "Timed out". It will attempt to reach the device for infinity.
private ResponseEvent getSnmpResponse() {
PDU pdu = createPdu();
Target target = getTarget();
try {
ResponseEvent event = snmp.send(pdu, target, null);
return event;
}
catch (IOException e ){
System.out.println("Timed out");
}
}
protected PDU createPdu() {
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(DISK_TOTAL_OID)));
pdu.add(new VariableBinding(new OID(DISK_AVAIL_OID)));
pdu.add(new VariableBinding(new OID(DISK_USED_OID)));
pdu.add(new VariableBinding(new OID(DISK_PERCENT_USED_OID)));
pdu.setType( PDU.GET );
return pdu;
}
private Target getTarget() {
CommunityTarget target = new CommunityTarget();
target.setCommunity( new OctetString("public") );
target.setAddress( GenericAddress.parse("dcadb2/161") );
target.setRetries( 1 );
target.setTimeout( 1000L );
target.setVersion( SnmpConstants.version2c );
return target;
}
I found the solution. It turns out SNMP4J (or at least the version I'm using, 1.11.2) will time out if you give it an IP that doesn't exist, but not if you give it a hostname that doesn't exist. In the latter case, the thread will continue for infinity. So the solution is to use IP addresses.
Related
I want to implement basic network check functionality to test if the provided url is responding or not (eg. ping www.google.com).It must provide operational information indicating, for example, that a requested service is not available or that a host could not be reached. I am able to achive it using icmp4j library. But i want to achieve the same using pcap4j library. I want to put the url in text box and click on connect button which will call pcap4j api to check whether the host is responding or not.
You can create ICMPv4 Echo (ping) on Ethernet packets using the Builders of IcmpV4EchoPacket, IcmpV4CommonPacket, IpV4Packet, and EthernetPacket and send them by PcapHandle.sendPacket(). Please refer to org.pcap4j.sample.SendFragmentedEcho in pcap4j-sample project.
You will need to implement ARP to resolve IP addresses to MAC addresses like org.pcap4j.sample.SendArpRequest in pcap4j-sample project.
And you will also need to implement a feature to find the next hop (default gateway or so) from the given IP address somehow. Pcap4J doesn't provide API to support this implementation. (Java doesn't provide API to get routing table...)
You'd maybe better use java.net.InetAddress#isReachable() instead.
It took me more than a year to figure this out, as I wanted to make a traceroute with pcap4j, so here is what I did:
Get your IPv4 Address and Mac Address, this can be easily achieved by querying the PcapNetworkInterface
Get target IP Address, if you have a DNS Name you need to resolve it beforehand.
Get target Mac Address.
Target is in the same subnet: send an ARP Request to resolve the mac (alternatively, a mac broadcast will likely work fine too).
Target is in different subnet: you need to get the mac of your gateway server then, this is not as easy. Assuming you have other network traffic going on, you could listen for incoming packets and get the source mac, where the source IP address is from a different subnet, this is likely the mac address of your gateway server.
Create IcmpV4EchoPacket and sent it
Listen for incoming ICMP traffic, you will get one of these three:
A IcmpV4EchoReplyPacket which is likely to be an answer to your request (check identifier and sequence number to be sure)
A IcmpV4TimeExceededPacket if the target could not be reached with the time-to-live you specified
Nothing, routers and pinged targets are free to ignore and not answer your request
Variables that need to be filled:
short IDENTIFIER; // identifer may be any 16 bit interger
short SEQUENCE; // sequence may be any 16 bit integer
byte TTL; // time to live (1-255)
Inet4Address IP_TARGET; // ip address of your ping target
Inet4Address IP_ORIGIN; // your own ip address
MacAddress MAC_TARGET; // target or gateway mac address
MacAddress MAC_SOURCE; // your own mac address
PcapNetworkInterface PCAP4J_NETWORK_INTERFACE; // network interface used to execute the ping
How to make a ICMP Echo Request Packet (as payload of IcmpV4CommonPacket of IpV4Packet of EthernetPacket):
public Packet buildPacket() {
IcmpV4EchoPacket.Builder icmpV4Echo = new IcmpV4EchoPacket.Builder()
.identifier(IDENTIFIER) // optional, default zero
.sequenceNumber(SEQUENCE); // optional, default zero
IcmpV4CommonPacket.Builder icmpV4Common = new IcmpV4CommonPacket.Builder()
.type(IcmpV4Type.ECHO) // type is echo
.code(IcmpV4Code.NO_CODE) // echo request doesn't need this
.payloadBuilder(icmpV4Echo)
.correctChecksumAtBuild(true);
IpV4Packet.Builder ipv4Builder = new IpV4Packet.Builder()
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.dstAddr(IP_TARGET) // IPv4 Address where tp send the request
.payloadBuilder(icmpV4Common)
.protocol(IpNumber.ICMPV4) // payload is ICMPV4
.srcAddr(IP_ORIGIN) // Your own IPv4 Address
.tos(IpV4Rfc1349Tos.newInstance((byte) 0))
.ttl(TTL) // time to live (1-255)
.version(IpVersion.IPV4); // IP Version is IPv4
EthernetPacket.Builder etherBuilder = new EthernetPacket.Builder()
.dstAddr(MAC_TARGET) // the targets mac address
.srcAddr(MAC_SOURCE) // your own mac address
.type(EtherType.IPV4) // payload protocl is IPv4
.payloadBuilder(ipv4Builder)
.paddingAtBuild(true);
return etherBuilder.build(); // build your packet
}
Listener for ICMP Echo Answers or timeouts:
public PacketListener buildListener() {
return new PacketListener() {
#Override
public void gotPacket(Packet packet) {
if (!(packet instanceof EthernetPacket))
return;
EthernetPacket ethernetPacket = (EthernetPacket) packet;
packet = ethernetPacket.getPayload();
if (!(packet instanceof IpV4Packet))
return;
IpV4Packet ipV4Packet = (IpV4Packet) packet;
IpV4Header ipV4Header = ipV4Packet.getHeader();
packet = ipV4Packet.getPayload();
if (!(packet instanceof IcmpV4CommonPacket))
return;
IcmpV4CommonPacket icmpPacket = (IcmpV4CommonPacket) packet;
packet = icmpPacket.getPayload();
// successful reply just measure time and done
if (packet instanceof IcmpV4EchoReplyPacket) {
IcmpV4EchoReplyPacket icmpV4EchoReplyPacket = (IcmpV4EchoReplyPacket) packet;
IcmpV4EchoReplyHeader icmpV4EchoReplyHeader = icmpV4EchoReplyPacket.getHeader();
if (icmpV4EchoReplyHeader.getIdentifier() != identifier)
return;
if (icmpV4EchoReplyHeader.getSequenceNumber() != sequence)
return;
// here you got an echo reply
System.out.println(packet);
return;
}
// try handle time to live exceeded messages
if (packet instanceof IcmpV4TimeExceededPacket) {
packet = packet.getPayload(); // original IPv4
if (!(packet instanceof IpV4Packet))
return;
packet = packet.getPayload(); // original ICMP common
if (!(packet instanceof IcmpV4CommonPacket))
return;
packet = packet.getPayload(); // original ICMP echo
if (!(packet instanceof IcmpV4EchoPacket))
return;
IcmpV4EchoHeader icmpV4EchoHeader = ((IcmpV4EchoPacket)packet).getHeader();
if (icmpV4EchoHeader.getIdentifier() != IDENTIFIER)
return;
if(icmpV4EchoHeader.getSequenceNumber() != SEQUENCE)
return;
// HERE you got an answer, that the time to live has been used up
System.out.println(packet);
return;
}
};
}
Combining it togther:
public static void main(String[] args) throws IOException, PcapNativeException, NotOpenException, InterruptedException {
try (PcapHandle handle = PCAP4J_NETWORK_INTERFACE.openLive(1024, PromiscuousMode.PROMISCUOUS, 1000)) {
// set filter to only get incoming ICMP traffic
handle.setFilter("icmp and dst host " + Pcaps.toBpfString(IP_ORIGIN), BpfCompileMode.OPTIMIZE);
// send ARP request
Packet p = buildPacket();
handle.sendPacket(p);
// wait (forever) for ARP answer
PacketListener listener = buildListener();
handle.loop(-1, listener);
}
}
I've hit kind of a brick wall. I got a small system that communicate with DSLAM's by SNMP.
Everything has worked fine for a couple of months, but when I recently added a new DSLAM to the system, I couldn't get an answer from it. Tried the other IP's and didn't have a problem.
After an hour or so, suddenly on of the other DSLAM's stopped answering me too. So now I've got two units without any communication, which kinda sucks. So of course I checked the units, and didn't find a problem. By my MIB-browser, I can reach all of the units... But not via my software. So the error lies somewhere in my software. So I checked Wireshark, and see that the getNext requests are going out, but I don't seem to get an answer. When I do it via the MIB browser, there comes an answer. But the funny thing is: the two requests are identical. So I must not be listening - and yes, it is listening.
Why in the world is this specific to some IP's, and dear Lord why do they contaminate eachother?
Let's look at some code:
public String GetNextValue(String OID, Dslam dslam) throws IOException {
Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
snmp.listen();
CommunityTarget target = initializeTarget(dslam);
PDU request = new PDU();
request.setType(PDU.GETNEXT);
OID oid= new OID(OID);
request.add(new VariableBinding(oid));
PDU responsePDU=null;
ResponseEvent responseEvent;
responseEvent = snmp.send(request, target);
if (responseEvent != null){
System.out.println("resonse event not null..");
responsePDU = responseEvent.getResponse();
if ( responsePDU != null){
System.out.println("pdu not null..");
#SuppressWarnings("unchecked")
Vector <VariableBinding> tmpv = (Vector<VariableBinding>) responsePDU.getVariableBindings();
if(tmpv != null){
System.out.println("tmpv not null..");
VariableBinding vb = (VariableBinding) tmpv.get(0);
if(!vb.isException()){
return vb.getVariable().toString()
}
}
}
}
_errorHandler.criticalError("Response error in DSLAM communication");
return null;
}
And the initializer:
private CommunityTarget initializeTarget(Dslam dslam){
Address addr = new UdpAddress(dslam.getAddress() + "/" + dslam.getManagementPort() );
System.out.println("IP: " + dslam.getAddress() + " port: " + dslam.getManagementPort());
CommunityTarget target = new CommunityTarget(addr, new OctetString("public"));
target.setVersion(SnmpConstants.version2c);
target.setTimeout(3000);
target.setRetries(3);
return target;
}
And if we run a test upon a working DSLAM:
#Test
public void Lowtest() throws IOException{
SnmpController snmpController = SnmpController.GetInstance();
DslamGrabber dslamGrabber = new DslamGrabber();
Dslam dslam = dslamGrabber.getByDslamId("test5xda5");
String result = snmpController.GetNextValue(".1.3.6.1.4.1.637.61.1.39.3.3.1.1.2", dslam);
System.out.println(result);
}
Result:
IP: 195.215.96.135 port: 161
resonse event not null..
pdu not null..
tmpv not null..
OID: 1.3.6.1.4.1.637.61.1.39.3.3.1.1.2.1
BF512_2048
The we try against test5xda9 (the second one to succumb to this hideous disease-like error)
We get 3 retries in Wireshark, and the following output:
IP: 192.215.96.139 port: 161
resonse event not null..
Response error in DSLAM communication
null
I really hope somebody here can help me. I'm a few hours away to either break down in tears or break a DSLAM..
Best regards
Ben
Well, as a friend pointed out 192 does not equal 195.
I am trying to get switches device and model name through snmp. When I try to get Nortell or Juniper switch, it works fine but Cisco switches cause a problem. I use this oid value: ".1.3.6.1.2.1.1.1.0" , but I tried "1.3.6.1.2.1.1.1" also. And return value is null.
Here is my code:
package list;
public class DeviceInfo {
private static String ipAddress = "10.20.X.XX";
private static String port = "161";
private static String oidValue = ".1.3.6.1.2.1.1.1";
private static int snmpVersion = SnmpConstants.version1; // or version2c
private static String community = "myreadcommunity";
public static void main(String[] args) throws Exception {
TransportMapping transport = new DefaultUdpTransportMapping();
transport.listen();
CommunityTarget comtarget = new CommunityTarget();
comtarget.setCommunity(new OctetString(community));
comtarget.setVersion(snmpVersion);
comtarget.setAddress(new UdpAddress(ipAddress + "/" + port));
comtarget.setRetries(2);
comtarget.setTimeout(1000);
PDU pdu = new PDU();
pdu.add(new VariableBinding(new OID(oidValue)));
pdu.setType(PDU.GET);
pdu.setRequestID(new Integer32(1));
Snmp snmp = new Snmp(transport);
System.out.println("Sending request.");
ResponseEvent response = snmp.get(pdu, comtarget);
if (response != null) {
System.out.println("Got results.");
PDU responsePDU = response.getResponse();
if (responsePDU != null) {
int errorStatus = responsePDU.getErrorStatus();
int errorIndex = responsePDU.getErrorIndex();
String errorStatusText = responsePDU.getErrorStatusText();
if (errorStatus == PDU.noError) {
System.out.println("Switch Name: = " + responsePDU.getVariableBindings());
System.out.println(responsePDU.size());
} else {
System.out.println("Error");
System.out.println("Error code: " + errorStatus);
System.out.println("Error Name: " + errorStatusText);
}
} else {
System.out.println("NULL");
}
} else {
System.out.println("Error: Timeout ");
}
snmp.close();
}
}
I would suggest to start with making sure that you really get snmp response from a switch. I suspect that snmp is not fully configured on a switch and your code gets timeout instead of snmp response.
Example:
$ tcpdump udp and port 161
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
10:36:26.980138 IP host.example.com.41226 > rtr.example.com.snmp: GetRequest(28) system.sysName.0
10:36:26.983971 IP rtr.example.com.snmp > host.example.com.41226: GetResponse(43) system.sysName.0="rtr.example.com"
Since you are doing snmp GET requests your oids must end with ".0".
Device name is returned in response to oid sysName.0
$ snmptranslate -IR -On sysName.0
.1.3.6.1.2.1.1.5.0
Example:
$ snmpget -v1 -c public rtr sysName.0
SNMPv2-MIB::sysName.0 = STRING: rtr.example.com
The oid you are using:
$ snmptranslate -IR -On sysDescr.0
.1.3.6.1.2.1.1.1.0
is unlikely to give device name or even exact model.
$ snmpget -v1 -c public rtr sysDescr.0
SNMPv2-MIB::sysDescr.0 = STRING: Cisco Internetwork Operating System Software
IOS (tm) C2600 Software (C2600-IPBASE-M), Version 12.3(6c), RELEASE SOFTWARE (fc1)
Copyright (c) 1986-2004 by cisco Systems, Inc.
Compiled Tue 20-Jul-04 05:24 by kellythw
Device model can be requested with sysObjectID:
$ snmptranslate -IR -On sysObjectID.0
.1.3.6.1.2.1.1.2.0
$ snmpget -v1 -c public rtr sysObjectID.0
SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.9.1.469
You can decode the response with lookup in CISCO-PRODUCTS-MIB
There is no guarantee that this .1.3.6.1.2.1.1.1.0 will give you the name you want. Please check Cisco manual for this model to see if there is a way to configure that before you make the queries.
The given oid has not to do what you expect. You have to learn the correct oid from manual
As you are getting problem with oid and want to get device name and model via snmp, then I will prefer you to check which are available in your network. Then you can choose.
You can use Nmap's snmp-brute command, like below
nmap -sU -p161 --script snmp-brute --script-args snmplist=community.lst 10.20.X.XX/24
On the other hand,
you can use this script, which generates an XML file containing snmp-enabled devices and their respective communities.
This script also accepts IP addresses and multiple community names as input files.
Resource Link:
How to find all the snmp enabled devices in my network?
1.3.6.1.2.1.1 - SNMP MIB-2 System
#PeerNet I executed your code, the only change I made was adding a '0' to the OID i.e '.1.3.6.1.2.1.1.1.0'.
Try Paessler SNMP Tester, and it will give you all the OIDs that are on the switch, and accordingly you can use those in your code.
https://www.paessler.com/tools/snmptester
You can try this code I found on http://www.jitendrazaa.com/blog/java/snmp/create-snmp-client-in-java-using-snmp4j/
public class SNMPManager
{
Snmp snmp = null;
String address = null;
/**
* Constructor
* #param add
*/
public SNMPManager(String add)
{
address = add;
}
public static void main(String[] args) throws IOException
{
/**
* Port 161 is used for Read and Other operations
* Port 162 is used for the trap generation
*/
SNMPManager client = new SNMPManager("udp:127.0.0.1/161");
//System.out.println(client);
client.start();
/**
* OID - .1.3.6.1.2.1.1.1.0 => SysDec
* OID - .1.3.6.1.2.1.1.5.0 => SysName
* => MIB explorer will be useful here, as discussed in previous article
*/
String sysDescr = client.getAsString(new OID("1.3.6.1.2.1.1.1.0"));
System.out.println(sysDescr);
}
/**
* Start the Snmp session. If you forget the listen() method you will not
* get any answers because the communication is asynchronous
* and the listen() method listens for answers.
* #throws IOException
*/
void start() throws IOException
{
TransportMapping transport = new DefaultUdpTransportMapping();
snmp = new Snmp(transport);
// Do not forget this line!
transport.listen();
}
/**
* Method which takes a single OID and returns the response from the agent as a String.
* #param oid
* #return
* #throws IOException
*/
public String getAsString(OID oid) throws IOException
{
ResponseEvent event = get(new OID[] { oid });
//System.out.println(oid);
return event.getResponse().get(0).getVariable().toString();
}
/**
* This method is capable of handling multiple OIDs
* #param oids
* #return
* #throws IOException
*/
public ResponseEvent get(OID oids[]) throws IOException
{
PDU pdu = new PDU();
for (OID oid : oids)
{
pdu.add(new VariableBinding(oid));
}
pdu.setType(PDU.GET);
ResponseEvent event = snmp.send(pdu, getTarget(), null);
if(event != null)
{
return event;
}
throw new RuntimeException("GET timed out");
}
/**
* This method returns a Target, which contains information about
* where the data should be fetched and how.
* #return
*/
private Target getTarget()
{
Address targetAddress = GenericAddress.parse(address);
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
target.setAddress(targetAddress);
target.setRetries(2);
target.setTimeout(1500);
target.setVersion(SnmpConstants.version2c);
return target;
}
}
If you run a query using OID .1.3.6.1.2.1.1.5.0 (sysName) against a Cisco router or switch running a version of IOS you will get the values for these two configuration statements concatenated together:
router#hostname "hostname"
router#ip domain-name "domain_name"
Thus, querying using the above OID will yield "hostname"."domain_name". If "ip domain-name" is not set with a value you'll simply get "hostname".
If all you want is the hostname you need to query .1.3.6.1.4.1.9.2.1.3.0., which is in CISCO-MIB.mib. Querying .1.3.6.1.4.1.9.2.1.4.0 will return "domain_name".
If the device is running NX-OS then querying those two OIDs won't work and I don't know what will (although I currently have a query open to Cisco on that).
Priority & Dependency:
Here I made I simple test. But the result seems not so good.
I tried to make 100 request in a for loop in the same connection(the request url is the same, I am wondering whether this part influence the results).
If the index is i, then my request stream_id is i while the dependent stream_id is 100+i. If our assumption is right, the request can never get response because there is no stream_id from 101 to 200.
But the results shows there is no difference for setting the dependency and not. I got the response data frame one by one without timeout or waiting.
And also some other related test, the start point is to let the stream which depends on other stream to be sent first and the stream dependent later. But the result is same.
I am still thinking the reason of the results. Can anyone help me? Many thanks.
Code here:
public void run() throws Exception
{
host = "google.com";
port = 443;
//client init
HTTP2Client client = new HTTP2Client();
SslContextFactory sslContextFactory = new SslContextFactory(true);
client.addBean(sslContextFactory);
client.start();
//connect init
FuturePromise<Session> sessionPromise = new FuturePromise<>();
client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
Session session = sessionPromise.get(10, TimeUnit.SECONDS);
//headers init
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
final Phaser phaser = new Phaser(2);
//multiple request in one connection
for(int i=0;i<100;i++)
{
MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
PriorityFrame testPriorityFrame = new PriorityFrame(i, 100+i, 4, true);
HeadersFrame headersFrame = new HeadersFrame(0, metaData, testPriorityFrame, true);
//listen header/data/push frame
session.newStream(headersFrame, new Promise.Adapter<Stream>(), new Stream.Listener.Adapter()
{
#Override
public void onHeaders(Stream stream, HeadersFrame frame)
{
System.err.println(frame+"headId:"+frame.getStreamId());
if (frame.isEndStream())
phaser.arrive();
}
#Override
public void onData(Stream stream, DataFrame frame, Callback callback)
{
System.err.println(frame +"streamid:"+ frame.getStreamId());
callback.succeeded();
if (frame.isEndStream())
phaser.arrive();
}
#Override
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
{
System.err.println(frame+"pushid:"+frame.getStreamId());
phaser.register();
return this;
}
});
}
phaser.awaitAdvanceInterruptibly(phaser.arrive(), 5, TimeUnit.SECONDS);
client.stop();
}
The Jetty project did not implement (yet) HTTP/2 request prioritization.
We are discussing whether this is any useful for a server, whose concern is to write back the responses as quick as it can.
Having one client changing its mind on the priority of the requests, or making a request knowing that in reality it first wanted another request served, it's a lot of work for the server that in the meantime has to serve the other 10,000 clients connected to it.
By the time we the server has recomputed the priority tree for the dependent requests, it could have probably have served the requests already.
By the time the client realizes that it has to change the priority of a request, the whole response for it could already be in flight.
Having said that, we are certainly interested in real world use cases where request prioritization performed by the server yields a real performance improvement. We just have not seen it yet.
I would love to hear why you are interested in request prioritization and how you are leveraging it. Your answer could be a drive for the Jetty project to implement HTTP/2 priorities.
I'm using this SNMP4J code to do some SNMP walks. But, when I run it on say, 1.3.6.1.2.1.31.1.1.1.1 which is ifName, it gets all the interfaces which are represented by 1.3.6.1.2.1.31.1.1.1.1.x, but then it also grabs 1.3.6.1.2.1.31.1.1.1.2 which are ifInMulticastPkts and then sometimes 1.3.6.1.2.1.31.1.1.1.3 which are ifInBroadcastPkts. I'm only interested in ifName.
How do keep the GETBULK from incrementing the last digit before traversing the MIB?
public ArrayList<String> walk(String oid) throws IOException, InterruptedException {
Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
snmp.listen();
Address targetAddress = GenericAddress.parse(address);
CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString(community));
target.setVersion(SnmpConstants.version2c);
target.setAddress(targetAddress);
target.setTimeout(3000); //3s
target.setRetries(1);
PDU pdu = new PDU();
pdu.setType(PDU.GETBULK);
pdu.setMaxRepetitions(200);
pdu.setNonRepeaters(0);
pdu.add(new VariableBinding(new OID(oid)));
ResponseEvent responseEvent = snmp.send(pdu, target);
PDU response = responseEvent.getResponse();
ArrayList<String> responsePieces = new ArrayList<String>();
if (response == null) {
System.out.println("TimeOut...");
}
else
{
if (response.getErrorStatus() == PDU.noError)
{
Vector<? extends VariableBinding> vbs = response.getVariableBindings();
for (VariableBinding vb : vbs) {
responsePieces.add(vb.toString());
}
}
else
{
System.out.println("Error:" + response.getErrorStatusText());
}
}
return responsePieces;
}
You should probably be using the getTable method of the TableUtils class to fetch the column(s) you need. That way, you don't have to worry about the nitty-gritty details of how the protocol behaves when walking a table.
That said, if you want to do it yourself... What you've discovered is the normal behavior of GetBulk. The Agent is only responsible for returning as many rows as you specified (200), if it has them. The response will also indicate what OID you should request if you want more rows.
It is only you as a manager who can tell when you've got all the data you want, and stop sending new GetBulk requests. You should simply discard the unwanted data in the last response. In your case, detect whether an OID is no longer a child of the column you requested.
You can read more about how SNMP walking and GetBulk work, in RFC 1905, particularly sections 4.2.3 and 4.2.3.1.
But do use the method provided by the API, it will save you some gray hairs, and is guaranteed to be correct.