netcrawl 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/Gemfile +3 -0
- data/README.md +86 -0
- data/Rakefile +47 -0
- data/bin/netcrawl +9 -0
- data/lib/netcrawl.rb +63 -0
- data/lib/netcrawl/.cli.rb.swp +0 -0
- data/lib/netcrawl/cli.rb +59 -0
- data/lib/netcrawl/config.rb +37 -0
- data/lib/netcrawl/dns.rb +53 -0
- data/lib/netcrawl/method/cdp.rb +33 -0
- data/lib/netcrawl/method/lldp.rb +35 -0
- data/lib/netcrawl/namemap.rb +12 -0
- data/lib/netcrawl/output.rb +72 -0
- data/lib/netcrawl/output/dot.rb +53 -0
- data/lib/netcrawl/pollmap.rb +13 -0
- data/lib/netcrawl/snmp.rb +68 -0
- data/netcrawl.gemspec +18 -0
- metadata +119 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# Netcrawl
|
2
|
+
Given snmp community and single device, crawls the network via discovering
|
3
|
+
LLDP/CDP neighbours, while producing list or dot file (for graphviz digraphs)
|
4
|
+
|
5
|
+
## Install
|
6
|
+
% gem install netcrawl
|
7
|
+
|
8
|
+
## Use
|
9
|
+
% netcrawl --graphiz router.example.com
|
10
|
+
|
11
|
+
## Command line
|
12
|
+
```
|
13
|
+
Usage: netcrawl [options] hostname
|
14
|
+
-g, --graphviz dot output use 'dot -Tpng -o map.png map.dot'
|
15
|
+
-l, --list list nodes
|
16
|
+
-j, --json json output
|
17
|
+
-y, --yaml yaml output
|
18
|
+
-a, --hash hash/associative array output
|
19
|
+
-r, --resolve resolve addresses to names
|
20
|
+
-p, --purge remove peers not in configured CIDR
|
21
|
+
-c, --community SNMP community to use
|
22
|
+
-d, --debug turn debugging on
|
23
|
+
-h, --help Display this help message.
|
24
|
+
|
25
|
+
```
|
26
|
+
* graphiz - graphis (dot) output
|
27
|
+
* list - list nodes found
|
28
|
+
* json - json output
|
29
|
+
* yaml - yaml output
|
30
|
+
* hash - ruby hash output
|
31
|
+
* resolve - resolve IP addresses
|
32
|
+
* purge - remove non-cidr matching peers from output
|
33
|
+
* community - sets snmp community
|
34
|
+
* debug - turn on debugging
|
35
|
+
|
36
|
+
## Config
|
37
|
+
```
|
38
|
+
---
|
39
|
+
use:
|
40
|
+
- LLDP
|
41
|
+
- CDP
|
42
|
+
poll:
|
43
|
+
- 192.0.2.0/24
|
44
|
+
snmp:
|
45
|
+
community: public
|
46
|
+
timeout: 1
|
47
|
+
retries: 2
|
48
|
+
bulkrows: 35
|
49
|
+
dot:
|
50
|
+
bothlinks: true
|
51
|
+
color:
|
52
|
+
- - cpe
|
53
|
+
- gold
|
54
|
+
- - -sw
|
55
|
+
- blue
|
56
|
+
- - -pe
|
57
|
+
- red
|
58
|
+
- - ' -p'
|
59
|
+
- yellow
|
60
|
+
dns:
|
61
|
+
afi:
|
62
|
+
log: STDERR
|
63
|
+
debug: false
|
64
|
+
namemap:
|
65
|
+
- - -re\d+
|
66
|
+
- ''
|
67
|
+
- - (.*(?<!as23456.net)$)
|
68
|
+
- \1.as23456.net
|
69
|
+
```
|
70
|
+
|
71
|
+
* use - methods to use for crawling
|
72
|
+
* poll - cidrs to allow snmp for
|
73
|
+
* snmp community - snmp community to use
|
74
|
+
* snmp timout - snmp timout in seconds
|
75
|
+
* snmp retries - snmp retries count
|
76
|
+
* snmp bulkrows - snmp row count for bulkget
|
77
|
+
* dot bothlinks - show a-b and b-a link
|
78
|
+
* dot color - regexp to color, first hit used
|
79
|
+
* dns afi - ipv4/ipv6 or nil
|
80
|
+
* log - STDERR/STDOUT or file
|
81
|
+
* debug - debugging
|
82
|
+
* namemap - map (LLDP) name to FQDN (JunOS does not give domain)
|
83
|
+
|
84
|
+
## Library use
|
85
|
+
require 'netcrawl'
|
86
|
+
output = NetCrawl.new.crawl('192.0.2.1').to_hash
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
rescue LoadError
|
6
|
+
warn 'bunler missing'
|
7
|
+
exit 42
|
8
|
+
end
|
9
|
+
|
10
|
+
gemspec = eval(File.read(Dir['*.gemspec'].first))
|
11
|
+
file = [gemspec.name, gemspec.version].join('-') + '.gem'
|
12
|
+
|
13
|
+
desc 'Validate gemspec'
|
14
|
+
task :gemspec do
|
15
|
+
gemspec.validate
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Run minitest'
|
19
|
+
task :test do
|
20
|
+
Rake::TestTask.new do |t|
|
21
|
+
t.libs.push "lib"
|
22
|
+
t.test_files = FileList['spec/*_spec.rb']
|
23
|
+
t.verbose = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Build gem'
|
28
|
+
task :build do
|
29
|
+
system "gem build #{gemspec.name}.gemspec"
|
30
|
+
FileUtils.mkdir_p 'gems'
|
31
|
+
FileUtils.mv file, 'gems'
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Install gem'
|
35
|
+
task :install => :build do
|
36
|
+
system "sudo -E sh -c \'umask 022; gem install gems/#{file}\'"
|
37
|
+
end
|
38
|
+
|
39
|
+
desc 'Remove gems'
|
40
|
+
task :clean do
|
41
|
+
FileUtils.rm_rf 'gems'
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Push to rubygems'
|
45
|
+
task :push do
|
46
|
+
system "gem push gems/#{file}"
|
47
|
+
end
|
data/bin/netcrawl
ADDED
data/lib/netcrawl.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'netcrawl/config'
|
2
|
+
class NetCrawl
|
3
|
+
class NetCrawlError < StandardError; end
|
4
|
+
class MethodNotFound < NetCrawlError; end
|
5
|
+
attr_reader :hosts
|
6
|
+
|
7
|
+
# @param [String] host host to start crawl from
|
8
|
+
# @return [NetCrawl::Output]
|
9
|
+
def crawl host
|
10
|
+
recurse host
|
11
|
+
Output.new @hosts, @poll
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [String] host host to get list of peers from
|
15
|
+
# @return [Array] list of peers seen connected to host
|
16
|
+
def get host
|
17
|
+
peers = []
|
18
|
+
@methods.each do |method|
|
19
|
+
peers += method.send(:get, host)
|
20
|
+
end
|
21
|
+
peers.uniq
|
22
|
+
end
|
23
|
+
|
24
|
+
# Given string of IP address, recurses through peers seen and populates @hosts hash
|
25
|
+
# @param [String] host host to start recurse from
|
26
|
+
# @return [void]
|
27
|
+
def recurse host
|
28
|
+
peers = get host
|
29
|
+
@hosts[host] = peers
|
30
|
+
peers.each do |peer|
|
31
|
+
next if @hosts.has_key? peer
|
32
|
+
next unless @poll.include? peer
|
33
|
+
crawl peer
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@methods = []
|
41
|
+
@hosts = {}
|
42
|
+
@poll = PollMap.new
|
43
|
+
CFG.use.each do |method|
|
44
|
+
begin
|
45
|
+
method = File.basename method.to_s
|
46
|
+
file = 'netcrawl/method/' + method.downcase
|
47
|
+
require_relative file
|
48
|
+
@methods.push NetCrawl.const_get(method)
|
49
|
+
rescue NameError, LoadError => error
|
50
|
+
raise MethodNotFound, "unable to find method '#{method}'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
57
|
+
require_relative 'netcrawl/pollmap'
|
58
|
+
require_relative 'netcrawl/namemap'
|
59
|
+
require_relative 'netcrawl/pollmap'
|
60
|
+
require_relative 'netcrawl/method/cdp'
|
61
|
+
require_relative 'netcrawl/method/lldp'
|
62
|
+
require_relative 'netcrawl/dns'
|
63
|
+
require_relative 'netcrawl/output'
|
Binary file
|
data/lib/netcrawl/cli.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative '../netcrawl'
|
2
|
+
require 'slop'
|
3
|
+
|
4
|
+
class NetCrawl
|
5
|
+
class CLI
|
6
|
+
class MissingHost < NetCrawlError; end
|
7
|
+
class NoConfig < NetCrawlError; end
|
8
|
+
|
9
|
+
def run
|
10
|
+
output = NetCrawl.new.crawl @host
|
11
|
+
output.clean if @opts[:purge]
|
12
|
+
output.resolve if @opts[:resolve]
|
13
|
+
if @opts[:graphviz]
|
14
|
+
output.to_dot
|
15
|
+
elsif @opts[:list]
|
16
|
+
output.to_list
|
17
|
+
elsif @opts[:json]
|
18
|
+
output.to_json
|
19
|
+
elsif @opts[:yaml]
|
20
|
+
output.to_yaml
|
21
|
+
else
|
22
|
+
output.to_hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
if Config.system.empty? and Config.user.empty?
|
30
|
+
Config.user = Config.default
|
31
|
+
Config.save :user
|
32
|
+
raise NoConfig, 'edit ~/.config/netcrawl/config'
|
33
|
+
end
|
34
|
+
@opts = opt_parse
|
35
|
+
args = @opts.parse
|
36
|
+
@host = DNS.getip args.shift
|
37
|
+
CFG.snmp.community = @opts[:community] if @opts[:community]
|
38
|
+
CFG.debug = true if @opts[:debug]
|
39
|
+
raise MissingHost, 'no hostname given as argument' unless @host
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def opt_parse
|
44
|
+
opts = Slop.parse(:help=>true) do
|
45
|
+
banner 'Usage: netcrawl [options] hostname'
|
46
|
+
on 'g', 'graphviz', 'dot output use \'dot -Tpng -o map.png map.dot\''
|
47
|
+
on 'l', 'list', 'list nodes'
|
48
|
+
on 'j', 'json', 'json output'
|
49
|
+
on 'y', 'yaml', 'yaml output'
|
50
|
+
on 'a', 'hash', 'hash/associative array output'
|
51
|
+
on 'r', 'resolve', 'resolve addresses to names'
|
52
|
+
on 'p', 'purge', 'remove peers not in configured CIDR'
|
53
|
+
on 'c=', 'community', 'SNMP community to use'
|
54
|
+
on 'd', 'debug', 'turn debugging on'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'asetus'
|
2
|
+
require 'logger'
|
3
|
+
class NetCrawl
|
4
|
+
Config = Asetus.new :name=>'netcrawl', :load=>false
|
5
|
+
Config.default.use = %w(LLDP CDP)
|
6
|
+
Config.default.poll = [ # addresses accepted for polling
|
7
|
+
'192.0.2.0/24',
|
8
|
+
'198.51.100.0/24',
|
9
|
+
'0.0.0.0/0',
|
10
|
+
]
|
11
|
+
Config.default.snmp.community = 'public'
|
12
|
+
Config.default.snmp.timeout = 1
|
13
|
+
Config.default.snmp.retries = 2
|
14
|
+
Config.default.snmp.bulkrows = 35 # 1500B packet should fit about 50 :cdpCacheAddress rows
|
15
|
+
Config.default.dot.bothlinks = false # keep both a-b and b-a links
|
16
|
+
Config.default.dot.color = [ # regexp of host => color
|
17
|
+
[ 'cpe', 'gold' ],
|
18
|
+
[ '-sw', 'blue' ],
|
19
|
+
[ '-pe', 'red' ],
|
20
|
+
[' -p', 'yellow' ],
|
21
|
+
]
|
22
|
+
Config.default.dns.afi = nil # could be 'ipv4' or 'ipv6'
|
23
|
+
Config.default.log = 'STDERR'
|
24
|
+
Config.default.debug = false
|
25
|
+
Config.default.namemap = [ # regexp match+sub of hostname (needed for LLDP)
|
26
|
+
['-re\d+', ''],
|
27
|
+
['^KILLME(.*(?<!my.domain.com)$)', '\1.my.domain.com'], #adds missing domain name
|
28
|
+
]
|
29
|
+
Config.load
|
30
|
+
CFG = Config.cfg
|
31
|
+
log = CFG.log
|
32
|
+
log = STDERR if log == 'STDERR'
|
33
|
+
log = STDOUT if log == 'STDOUT'
|
34
|
+
Log = Logger.new log
|
35
|
+
Log.level = Logger::INFO
|
36
|
+
Log.level = Logger::DEBUG if CFG.debug
|
37
|
+
end
|
data/lib/netcrawl/dns.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
class NetCrawl
|
3
|
+
class Resolve
|
4
|
+
|
5
|
+
# @param [String] name DNS name which we try to resolve to IP
|
6
|
+
# @return [String, nil] string if name resolves to IP, otherwise nil
|
7
|
+
def getip name
|
8
|
+
if @cacheip.has_key? name
|
9
|
+
@cacheip[name]
|
10
|
+
else
|
11
|
+
ip = nil
|
12
|
+
begin
|
13
|
+
if CFG.dns.afi == 'ipv4'
|
14
|
+
ip = Resolv::DNS.new.getresource(name, Resolv::DNS::Resource::IN::A).address
|
15
|
+
elsif CFG.dns.afi == 'ipv6'
|
16
|
+
ip = Resolv::DNS.new.getresource(name, Resolv::DNS::Resource::IN::AAAA).address
|
17
|
+
else
|
18
|
+
ip = Resolv.getaddress name
|
19
|
+
end
|
20
|
+
rescue => error
|
21
|
+
Log.debug "DNS resolution for '#{name}' raised error '#{error.class}' with message '#{error.message}'"
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
@cacheip[name] = ip
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [String] ip DNS IP which we try to resolve to name
|
29
|
+
# @return [String] name if it resolves, ip otherwise
|
30
|
+
def getname ip
|
31
|
+
if @cachename.has_key? ip
|
32
|
+
@cachename[ip]
|
33
|
+
else
|
34
|
+
name = nil
|
35
|
+
begin
|
36
|
+
name = Resolv.getname ip
|
37
|
+
rescue => error
|
38
|
+
Log.debug "DNS resolution for '#{ip}' raised error '#{error.class}' with message '#{error.message}'"
|
39
|
+
name = ip
|
40
|
+
end
|
41
|
+
@cachename[ip] = name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@cacheip = {}
|
49
|
+
@cachename = {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
DNS = Resolve.new
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative '../snmp'
|
2
|
+
class NetCrawl
|
3
|
+
class CDP
|
4
|
+
OID = {
|
5
|
+
# http://tools.cisco.com/Support/SNMP/do/BrowseOID.do?local=en&translate=Translate&objectInput=1.3.6.1.4.1.9.9.23.1.2.1.1
|
6
|
+
:cdpCacheAddress => '1.3.6.1.4.1.9.9.23.1.2.1.1.4',
|
7
|
+
:cdpCacheDeviceId => '1.3.6.1.4.1.9.9.23.1.2.1.1.6',
|
8
|
+
}
|
9
|
+
|
10
|
+
# @param [String] host host to query
|
11
|
+
# @return [Hash] neighbor information
|
12
|
+
def self.get host
|
13
|
+
cdp = new(host)
|
14
|
+
cdp.peers
|
15
|
+
end
|
16
|
+
|
17
|
+
def peers
|
18
|
+
@snmp.bulkwalk(OID[:cdpCacheAddress]).map do |vb|
|
19
|
+
vb.as_ip
|
20
|
+
end
|
21
|
+
rescue SNMP::NoResponse
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def initialize host
|
28
|
+
@snmp = SNMP.new host
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative '../snmp'
|
2
|
+
class NetCrawl
|
3
|
+
class LLDP
|
4
|
+
include NameMap
|
5
|
+
OID = {
|
6
|
+
# http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf
|
7
|
+
:lldpRemChassisIdSubtype => '1.0.8802.1.1.2.1.4.1.1.4', # CSCO and JNPR use 4 (MAC address) rendering ChassisID useless
|
8
|
+
:lldpRemChassisId => '1.0.8802.1.1.2.1.4.1.1.5',
|
9
|
+
:lldpRemSysName => '1.0.8802.1.1.2.1.4.1.1.9',
|
10
|
+
}
|
11
|
+
|
12
|
+
# @param [String] host host to query
|
13
|
+
# @return [Hash] neighbor information
|
14
|
+
def self.get host
|
15
|
+
lldp = new(host)
|
16
|
+
lldp.peers
|
17
|
+
end
|
18
|
+
|
19
|
+
def peers
|
20
|
+
@snmp.bulkwalk(OID[:lldpRemSysName]).map do |vb|
|
21
|
+
DNS.getip namemap(vb.value)
|
22
|
+
end.compact
|
23
|
+
rescue SNMP::NoResponse
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize host
|
30
|
+
@snmp = SNMP.new host
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class NetCrawl
|
2
|
+
class Output
|
3
|
+
attr_reader :iphash, :namehash, :hash
|
4
|
+
|
5
|
+
# @return [String] pretty print of hash
|
6
|
+
def to_hash
|
7
|
+
require 'pp'
|
8
|
+
out = ''
|
9
|
+
PP.pp @hash, out
|
10
|
+
out
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [String] yaml of hosts and peers found
|
14
|
+
def to_yaml
|
15
|
+
require 'yaml'
|
16
|
+
YAML.dump @hash
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [String] json of hosts and peers found
|
20
|
+
def to_json
|
21
|
+
require 'json'
|
22
|
+
JSON.pretty_generate @hash
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Array] of nodes found
|
26
|
+
def to_list
|
27
|
+
nodes = []
|
28
|
+
@hash.each do |host, peers|
|
29
|
+
nodes << host
|
30
|
+
nodes << peers
|
31
|
+
end
|
32
|
+
nodes.flatten.uniq.sort
|
33
|
+
end
|
34
|
+
|
35
|
+
# resolves ip addresses and changes @hash to point to the resolved hash
|
36
|
+
# @return [void]
|
37
|
+
def resolve
|
38
|
+
@namehash = {}
|
39
|
+
@iphash.each do |host, peers|
|
40
|
+
host = DNS.getname host
|
41
|
+
@namehash[host] = []
|
42
|
+
peers.each do |peer|
|
43
|
+
@namehash[host].push DNS.getname(peer)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
@hash = @namehash
|
47
|
+
end
|
48
|
+
|
49
|
+
# remove peers not matchin configured CIDR
|
50
|
+
def clean
|
51
|
+
@hash.each do |host, peers|
|
52
|
+
peers = peers.delete_if{|peer|not @pollmap.include? peer}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize hash, pollmap
|
59
|
+
@iphash = hash
|
60
|
+
@hash = @iphash
|
61
|
+
@pollmap = pollmap
|
62
|
+
end
|
63
|
+
|
64
|
+
def method_missing name, *args
|
65
|
+
raise NoMethodError, "invalid method #{name} for #{inspect}:#{self.class}" unless name.match(/to_.*/)
|
66
|
+
output = File.basename name[3..-1]
|
67
|
+
require_relative 'output/' + output
|
68
|
+
output = NetCrawl::Output.const_get output.capitalize
|
69
|
+
output.send :output, self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class NetCrawl
|
2
|
+
class Output
|
3
|
+
class Dot
|
4
|
+
INDENT = ' ' * 2
|
5
|
+
DEFAULT_COLOR = 'black'
|
6
|
+
def self.output output
|
7
|
+
new(output).to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
str = "graph NetCrawl {\n"
|
12
|
+
@output.to_list.each do |host|
|
13
|
+
str << INDENT + id(host) + "[color=\"#{color(host)}\"]\n"
|
14
|
+
str << INDENT + id(host) + "[label=\"#{host}\"]\n"
|
15
|
+
if @hash.has_key? host
|
16
|
+
@hash[host].each do |peer|
|
17
|
+
next if not CFG.dot.bothlinks and @connections.include?([peer, host].sort)
|
18
|
+
@connections << [peer, host].sort
|
19
|
+
str << INDENT + INDENT + id(host) + ' -- ' + id(peer) + "\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
str << "}\n"
|
24
|
+
str
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize output
|
30
|
+
@output = output
|
31
|
+
@connections = []
|
32
|
+
@hash = @output.hash
|
33
|
+
end
|
34
|
+
|
35
|
+
def id host
|
36
|
+
host = host.gsub(/[-.]/, '_')
|
37
|
+
'_' + host
|
38
|
+
end
|
39
|
+
|
40
|
+
def color host
|
41
|
+
color = nil
|
42
|
+
CFG.dot.color.each do |re, clr|
|
43
|
+
if host.match re
|
44
|
+
color = clr
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
color or DEFAULT_COLOR
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'snmp'
|
2
|
+
class NetCrawl
|
3
|
+
class SNMP
|
4
|
+
class NoResponse < NetCrawlError; end
|
5
|
+
# Closes the SNMP connection
|
6
|
+
# @return [void]
|
7
|
+
def close
|
8
|
+
@snmp.close
|
9
|
+
end
|
10
|
+
|
11
|
+
# Gets one oid, return value
|
12
|
+
# @param [String] oid to get
|
13
|
+
# @return [SNMP::VarBind]
|
14
|
+
def get oid
|
15
|
+
mget([oid]).first
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get multiple oids, return array of values
|
19
|
+
# @param [Array(String)] oids to get
|
20
|
+
# @return [SNMP::VarBindList]
|
21
|
+
def mget oids
|
22
|
+
snmp :get, oids
|
23
|
+
end
|
24
|
+
|
25
|
+
# Bulkwalk everything below root oid
|
26
|
+
# @param [String] root oid to start from
|
27
|
+
# @return [Array(SNMP::VarBind)]
|
28
|
+
def bulkwalk root
|
29
|
+
last, oid, results = false, root.dup, []
|
30
|
+
root = root.split('.').map{|chr|chr.to_i}
|
31
|
+
while not last
|
32
|
+
vbs = snmp(:get_bulk, 0, CFG.snmp.bulkrows, oid).varbind_list
|
33
|
+
vbs.each do |vb|
|
34
|
+
oid = vb.oid
|
35
|
+
(last = true; break) if not oid[0..root.size-1] == root
|
36
|
+
results.push vb
|
37
|
+
end
|
38
|
+
end
|
39
|
+
results
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def initialize host, community=CFG.snmp.community, timeout=CFG.snmp.timeout, retries=CFG.snmp.retries
|
45
|
+
@host = host
|
46
|
+
@snmp = ::SNMP::Manager.new :Host=>@host, :Community=>community,
|
47
|
+
:Timeout=>timeout, :Retries=>retries,
|
48
|
+
:MibModules=>[]
|
49
|
+
end
|
50
|
+
|
51
|
+
def snmp cmd, *args
|
52
|
+
@snmp.send cmd, *args
|
53
|
+
rescue ::SNMP::RequestTimeout, Errno::EACCES => error
|
54
|
+
msg = "host '#{@host}' raised '#{error.class}' with message '#{error.message}' for method '#{cmd}' with args '#{args}'"
|
55
|
+
Log.warn msg
|
56
|
+
raise NoResponse, msg
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module SNMP
|
63
|
+
class VarBind
|
64
|
+
def as_ip
|
65
|
+
SNMP::IpAddress.new(value).to_s
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/netcrawl.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'netcrawl'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = [ 'Saku Ytti' ]
|
6
|
+
s.email = %w( saku@ytti.fi )
|
7
|
+
s.homepage = 'http://github.com/ytti/netcrawl'
|
8
|
+
s.summary = 'lldp/cdp crawler'
|
9
|
+
s.description = 'given snmp community and one node crawls through the network to produce list/dot file'
|
10
|
+
s.rubyforge_project = s.name
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.executables = %w( netcrawl )
|
13
|
+
s.require_path = 'lib'
|
14
|
+
|
15
|
+
s.add_dependency 'snmp'
|
16
|
+
s.add_dependency 'slop'
|
17
|
+
s.add_dependency 'asetus'
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netcrawl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Saku Ytti
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: snmp
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: slop
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: asetus
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: given snmp community and one node crawls through the network to produce
|
63
|
+
list/dot file
|
64
|
+
email:
|
65
|
+
- saku@ytti.fi
|
66
|
+
executables:
|
67
|
+
- netcrawl
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- .gitignore
|
72
|
+
- Gemfile
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- bin/netcrawl
|
76
|
+
- lib/netcrawl.rb
|
77
|
+
- lib/netcrawl/.cli.rb.swp
|
78
|
+
- lib/netcrawl/cli.rb
|
79
|
+
- lib/netcrawl/config.rb
|
80
|
+
- lib/netcrawl/dns.rb
|
81
|
+
- lib/netcrawl/method/cdp.rb
|
82
|
+
- lib/netcrawl/method/lldp.rb
|
83
|
+
- lib/netcrawl/namemap.rb
|
84
|
+
- lib/netcrawl/output.rb
|
85
|
+
- lib/netcrawl/output/dot.rb
|
86
|
+
- lib/netcrawl/pollmap.rb
|
87
|
+
- lib/netcrawl/snmp.rb
|
88
|
+
- netcrawl.gemspec
|
89
|
+
homepage: http://github.com/ytti/netcrawl
|
90
|
+
licenses: []
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
hash: -152408900674429923
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
hash: -152408900674429923
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project: netcrawl
|
115
|
+
rubygems_version: 1.8.25
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: lldp/cdp crawler
|
119
|
+
test_files: []
|