# # Copyright (C) 2010 Alexandros Stergiakis <alsterg@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # #//# # The "lldp" module enable layer2 network discovery via the [https://trac.luffy.cx/lldpd/ lldpd] package. # # Supported protocols: # * LLDP (Link Layer Discovery Protocol) # * CDP (Cisco Discovery Protocol) # * FDP (Foundry Discovery Protocol) # * SONMP (Nortel Discovery Protocol) # * EDP (Extreme Discovery Protocol) # # @assume lldpd is compiled with: --with-privsep-user=_drop --with-privsep-group=_drop --with-privsep-chroot=/var/log/lldpd #//# module require base 1.0 module provide lldp 1.0 namespace eval ::module::lldp { namespace import ::helper::* ::module::api::* proc description {} { return "Layer 2 Network Discovery" } proc version {} { return "lldpd Version 0.4.0. Supported protocols: LLDP CDP FDP EDP SONMP" } proc check {} { if {[lempty [auto_execok lldpd]]} { error "lldpd is not installed" } if {[lempty [auto_execok lldpctl]]} { error "lldpctl is not installed" } } proc update_lldpd {} { variable lldpd_run variable lldpd_protocols variable lldpd_vlan variable lldpd_class killall lldpd if {$lldpd_run} { set params {} foreach p $lldpd_protocols { switch -exact -- $p { "cdp" { append params "-c "} "fdp" { append params "-f "} "edp" { append params "-e "} "sonmp" { append params "-s "} default { error "Unrecognized network discovery protocol: $p" } } } if {$lldpd_vlan} { append params "-v " } if {$lldpd_class} { append params "-M $lldpd_class " } exec lldpd {*}$params } } proc reset {} { variable lldpd_run 0 variable lldpd_protocols {} variable lldpd_vlan 0 variable lldpd_class 0 update_lldpd } proc constructor {} { log::Info "Loading \"lldp\" module: [description]" check Global USER USER_GROUP # The following paths are embedded in lldpd, that is why we don't use VAR_DIR. file mkdir [file join / var log lldpd] file attributes [file join / var log lldpd] -owner $USER -group $USER_GROUP file delete -force -- [file join / var run lldpd.socket] variable Classes [list endpoint1 endpoint2 endpoint3 network] reset # Finally load Command Specs sysconf loadspecs "modules/lldp/lldp.specs" } proc destructor {} { # First unload Command Specs sysconf remove "lldp" reset ;# will terminate daemon. } ################ # Handlers ################ command LldpRun {cmdline argstart sid out no arguments args} { variable lldpd_run OnError Critical if {$no} { set lldpd_run 0 } else { set lldpd_run 1 } update_lldpd } proc print_LldpRun {} { variable lldpd_run if {$lldpd_run} { return "lldp run\n" } return } command LldpProtocols {cmdline argstart sid out no arguments args} { variable lldpd_protocols set protos [getall PROTOCOL] if {$no} { foreach p $protos { if {$p in $lldpd_protocols} { lremove lldpd_protocols $p } } } else { set lldpd_protocols $protos } update_lldpd } proc print_LldpProtocols {} { variable lldpd_protocols if {! [lempty $lldpd_protocols]} { return "lldp protocols $lldpd_protocols\n" } return } command LldpVlan {cmdline argstart sid out no arguments args} { variable lldpd_vlan if {$no} { set lldpd_vlan 0 } else { set lldpd_vlan 1 } update_lldpd } proc print_LldpVlan {} { variable lldpd_vlan if {$lldpd_vlan} { return "lldp vlan\n" } return } proc class2num {class} { variable Classes set result [lsearch -exact $Classes $class] if {$result == -1} { error "unrecognized severity level \"$class\"" } incr result return $result } proc num2class {num} { variable Classes if {! (1 <= $num <= 4)} { error "class number out of bounds" } return [lindex $Classes $num-1] } command LldpClass {cmdline argstart sid out no arguments args} { variable lldpd_class if {$no} { set lldpd_class 0 } else { set class [dict get $arguments CLASS] set lldpd_class [class2num $class] } update_lldpd } proc print_LldpClass {} { variable lldpd_class if {$lldpd_class} { return "lldp class [num2class $lldpd_class]\n" } return } command ShowNeighbors {cmdline argstart sid out no arguments args} { variable lldpd_run if {! $lldpd_run} { return "Layer 2 Network Discovery is not enabled" } if {[dict exists $arguments detailed]} { exec lldpctl -d } else { exec lldpctl } } } ;# End of Namespace