bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/snmp_bridge.go (about) 1 package collectors 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "fmt" 7 "sort" 8 "strings" 9 "time" 10 11 "bosun.org/cmd/scollector/conf" 12 "bosun.org/metadata" 13 "bosun.org/opentsdb" 14 "bosun.org/slog" 15 ) 16 17 const ( 18 vtpVlanState = "1.3.6.1.4.1.9.9.46.1.3.1.1.2.1" 19 dot1dTpFdbAddress = "1.3.6.1.2.1.17.4.3.1.1" 20 dot1dTpFdbPort = "1.3.6.1.2.1.17.4.3.1.2" 21 dot1dBasePortIfIndex = "1.3.6.1.2.1.17.1.4.1.2" 22 ) 23 24 // SNMP Bridge registers 25 func SNMPBridge(cfg conf.SNMP) { 26 collectors = append(collectors, &IntervalCollector{ 27 F: func() (opentsdb.MultiDataPoint, error) { 28 return c_snmp_bridge(cfg.Community, cfg.Host) 29 }, 30 Interval: time.Minute * 5, 31 name: fmt.Sprintf("snmp-bridge-%s", cfg.Host), 32 }) 33 collectors = append(collectors, &IntervalCollector{ 34 F: func() (opentsdb.MultiDataPoint, error) { 35 return c_snmp_cdp(cfg.Community, cfg.Host) 36 }, 37 Interval: time.Minute * 5, 38 name: fmt.Sprintf("snmp-cdp-%s", cfg.Host), 39 }) 40 } 41 42 func c_snmp_bridge(community, host string) (opentsdb.MultiDataPoint, error) { 43 var md opentsdb.MultiDataPoint 44 vlanRaw, err := snmp_subtree(host, community, vtpVlanState) 45 if err != nil { 46 return md, err 47 } 48 vlans := []string{} 49 for vlan, state := range vlanRaw { 50 Add(&md, "cisco.net.vlan_state", state, opentsdb.TagSet{"host": host, "vlan": vlan}, metadata.Gauge, metadata.StatusCode, "") 51 vlans = append(vlans, vlan) 52 } 53 ifMacs := make(map[string][]string) 54 for _, vlan := range vlans { 55 // community string indexing: http://www.cisco.com/c/en/us/support/docs/ip/simple-network-management-protocol-snmp/40367-camsnmp40367.html 56 macRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dTpFdbAddress) 57 if err != nil { 58 slog.Infoln(err) 59 // continue since it might just be the one vlan 60 continue 61 } 62 remoteMacAddresses := make(map[string]string) 63 for k, v := range macRaw { 64 if ba, ok := v.([]byte); ok { 65 remoteMacAddresses[k] = strings.ToUpper(hex.EncodeToString(ba)) 66 } 67 } 68 toPort := make(map[string]string) 69 toPortRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dTpFdbPort) 70 if err != nil { 71 slog.Infoln(err) 72 } 73 for k, v := range toPortRaw { 74 toPort[k] = fmt.Sprintf("%v", v) 75 } 76 portToIfIndex := make(map[string]string) 77 portToIfIndexRaw, err := snmp_subtree(host, community+"@"+vlan, dot1dBasePortIfIndex) 78 for k, v := range portToIfIndexRaw { 79 portToIfIndex[k] = fmt.Sprintf("%v", v) 80 } 81 if err != nil { 82 slog.Infoln(err) 83 } 84 for port, mac := range remoteMacAddresses { 85 if port, ok := toPort[port]; ok { 86 if ifIndex, ok := portToIfIndex[port]; ok { 87 if _, ok := ifMacs[ifIndex]; ok { 88 ifMacs[ifIndex] = append(ifMacs[ifIndex], mac) 89 } else { 90 ifMacs[ifIndex] = []string{mac} 91 } 92 } 93 } 94 } 95 } 96 for iface, macs := range ifMacs { 97 sort.Strings(macs) 98 j, err := json.Marshal(macs) 99 if err != nil { 100 return md, nil 101 } 102 metadata.AddMeta("", opentsdb.TagSet{"host": host, "iface": iface}, "remoteMacs", string(j), false) 103 } 104 return md, nil 105 } 106 107 const ( 108 cdpCacheDeviceId = "1.3.6.1.4.1.9.9.23.1.2.1.1.6" 109 cdpCacheDevicePort = "1.3.6.1.4.1.9.9.23.1.2.1.1.7" 110 ) 111 112 type cdpCacheEntry struct { 113 InterfaceId string `json:"-"` 114 DeviceId string 115 DevicePort string 116 } 117 118 func c_snmp_cdp(community, host string) (opentsdb.MultiDataPoint, error) { 119 var md opentsdb.MultiDataPoint 120 cdpEntries := make(map[string]*cdpCacheEntry) 121 deviceIdRaw, err := snmp_subtree(host, community, cdpCacheDeviceId) 122 if err != nil { 123 return md, err 124 } 125 for k, v := range deviceIdRaw { 126 ids := strings.Split(k, ".") 127 if len(ids) != 2 { 128 slog.Error("unexpected snmp cdpCacheEntry id") 129 continue 130 } 131 cdpEntries[ids[0]] = &cdpCacheEntry{} 132 cdpEntries[ids[0]].DeviceId = fmt.Sprintf("%s", v) 133 cdpEntries[ids[0]].InterfaceId = ids[1] 134 } 135 devicePortRaw, err := snmp_subtree(host, community, cdpCacheDevicePort) 136 for k, v := range devicePortRaw { 137 ids := strings.Split(k, ".") 138 if len(ids) != 2 { 139 slog.Error("unexpected snmp cdpCacheEntry id") 140 continue 141 } 142 if entry, ok := cdpEntries[ids[0]]; ok { 143 entry.DevicePort = fmt.Sprintf("%s", v) 144 } 145 } 146 byInterface := make(map[string][]*cdpCacheEntry) 147 for _, entry := range cdpEntries { 148 if _, ok := byInterface[entry.InterfaceId]; ok { 149 byInterface[entry.InterfaceId] = append(byInterface[entry.InterfaceId], entry) 150 } else { 151 byInterface[entry.InterfaceId] = []*cdpCacheEntry{entry} 152 } 153 } 154 for iface, entry := range byInterface { 155 j, err := json.Marshal(entry) 156 if err != nil { 157 return md, err 158 } 159 metadata.AddMeta("", opentsdb.TagSet{"host": host, "iface": iface}, "cdpCacheEntries", string(j), false) 160 } 161 if err != nil { 162 return md, nil 163 } 164 return md, nil 165 }