New upstream version 0.0~git20180801.7e2d884
Sascha Steinbiss
5 years ago
14 | 14 | |
15 | 15 | ```Go |
16 | 16 | const ( |
17 | // InterfaceUnknown represents an interface with no assigned state. | |
18 | InterfaceUnknown InterfaceStatus = iota | |
19 | // InterfaceUp represents an interface with a cable connected. | |
20 | InterfaceUp | |
21 | // InterfaceDown represents an interface with no cable connected. | |
22 | InterfaceDown | |
23 | // InterfaceErr represents an interface with errors querying its status. | |
24 | InterfaceErr | |
17 | // InterfaceUnknown represents an interface with no assigned state. | |
18 | InterfaceUnknown InterfaceStatus = iota | |
19 | // InterfaceUp represents an interface with a cable connected. | |
20 | InterfaceUp | |
21 | // InterfaceDown represents an interface with no cable connected. | |
22 | InterfaceDown | |
23 | // InterfaceErr represents an interface with errors querying its status. | |
24 | InterfaceErr | |
25 | 25 | ) |
26 | 26 | ``` |
27 | 27 | |
40 | 40 | mon.Run() |
41 | 41 | ``` |
42 | 42 | |
43 | It is also possible to determine the status of an interface from whether any data is flowing or not. This can be useful if, for example, the interesting interface is only connected to one way of the physical connection (RX or TX) or for other reasons can not complete autonegotiation. Use `CheckIncomingDelta()` in this case, it allows to also mark an interface as 'up' and seeing traffic if a certain threshold of received bytes is exceeded during one polling period. Example: | |
44 | ||
45 | ```Go | |
46 | mon.CheckIncomingDelta(true, 1000) | |
47 | ``` | |
48 | ||
49 | This would, for example, also mark an interface as up if more than 1000 bytes are received during the polling period, and mark the interface as down if there are ever less than 1000 bytes received in a polling period. | |
50 | ||
43 | 51 | ## Prerequisites |
44 | 52 | |
45 | 53 | To build ifplugo, one needs [libdaemon](http://0pointer.de/lennart/projects/libdaemon/) in addition to Go and C compilers. |
49 | 57 | |
50 | 58 | See the source code of the simple command line tools in `cmd/*` for more simple examples of how to use ifplugo. |
51 | 59 | |
52 | ``` | |
60 | ```Text | |
53 | 61 | $ ifplugo-watch eth0,eth1,eth2,eth3 |
54 | 62 | eth0: link |
55 | 63 | eth1: link |
61 | 69 | |
62 | 70 | ## Authors |
63 | 71 | |
64 | This source code includes parts of ifplugd, written by Lennart Poettering | |
65 | <mzvscyhtq (at) 0pointer (dot) de>. | |
72 | This source code includes parts of ifplugd, written by Lennart Poettering <mzvscyhtq (at) 0pointer (dot) de>. | |
66 | 73 | |
67 | The Go component of the code was written by Sascha Steinbiss | |
68 | <sascha (at) steinbiss (dot) name>. | |
74 | The Go component of the code was written by Sascha Steinbiss <sascha (at) steinbiss (dot) name>. | |
69 | 75 | |
70 | 76 | ## License |
71 | 77 |
16 | 16 | // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
17 | 17 | |
18 | 18 | import ( |
19 | "flag" | |
19 | 20 | "fmt" |
20 | 21 | "os" |
21 | 22 | "strings" |
22 | 23 | "time" |
23 | 24 | |
24 | 25 | "github.com/satta/ifplugo" |
26 | log "github.com/sirupsen/logrus" | |
25 | 27 | ) |
26 | 28 | |
27 | 29 | func main() { |
28 | if len(os.Args) < 2 { | |
30 | deltaValPtr := flag.Int("delta", 0, "number of bytes transferred per polling period to mark interface as seeing traffic") | |
31 | pollingPtr := flag.Duration("poll", 2*time.Second, "polling period") | |
32 | verbPtr := flag.Bool("verbose", false, "verbose logging") | |
33 | flag.Parse() | |
34 | ||
35 | if len(flag.Args()) < 1 { | |
29 | 36 | fmt.Println("Usage: ifplugo-status <iface1,iface2,...>") |
30 | 37 | os.Exit(1) |
31 | 38 | } |
32 | ifaces := strings.Split(os.Args[1], ",") | |
39 | ifaces := strings.Split(flag.Args()[0], ",") | |
40 | ||
41 | if *verbPtr { | |
42 | log.SetLevel(log.DebugLevel) | |
43 | } | |
33 | 44 | |
34 | 45 | outchan := make(chan ifplugo.LinkStatusSample) |
35 | mon := ifplugo.MakeLinkStatusMonitor(2*time.Second, ifaces, outchan) | |
46 | mon := ifplugo.MakeLinkStatusMonitor(*pollingPtr, ifaces, outchan) | |
47 | if *deltaValPtr > 0 { | |
48 | mon.CheckIncomingDelta(true, uint64(*deltaValPtr)) | |
49 | } | |
36 | 50 | go func() { |
37 | 51 | for v := range outchan { |
38 | 52 | for k, v := range v.Ifaces { |
26 | 26 | import ( |
27 | 27 | "syscall" |
28 | 28 | "time" |
29 | ||
30 | "github.com/shirou/gopsutil/net" | |
31 | log "github.com/sirupsen/logrus" | |
29 | 32 | ) |
30 | 33 | |
31 | 34 | // InterfaceStatus represents the link status of an interface. |
90 | 93 | // periodically checks a list of given interfaces and returns their link status |
91 | 94 | // via a specified channel. |
92 | 95 | type LinkStatusMonitor struct { |
93 | PollPeriod time.Duration | |
94 | LastStatus map[string]InterfaceStatus | |
95 | OutChan chan LinkStatusSample | |
96 | CloseChan chan bool | |
97 | ClosedChan chan bool | |
98 | Ifaces []string | |
96 | PollPeriod time.Duration | |
97 | LastStatus map[string]InterfaceStatus | |
98 | LastStats map[string]net.IOCountersStat | |
99 | checkIncomingDelta bool | |
100 | checkIncomingThreshold uint64 | |
101 | OutChan chan LinkStatusSample | |
102 | CloseChan chan bool | |
103 | ClosedChan chan bool | |
104 | Ifaces []string | |
99 | 105 | } |
100 | 106 | |
101 | 107 | // LinkStatusSample is a single description of the link status at a given time. |
118 | 124 | ClosedChan: make(chan bool), |
119 | 125 | Ifaces: ifaces, |
120 | 126 | LastStatus: make(map[string]InterfaceStatus), |
127 | LastStats: make(map[string]net.IOCountersStat), | |
121 | 128 | } |
122 | 129 | return a |
130 | } | |
131 | ||
132 | // CheckIncomingDelta allows to enable the optional behaviour to also consider | |
133 | // an interface as 'up' if traffic is received on it. This is, for example, | |
134 | // necessary in passive monitoring setups where there is no physical link | |
135 | // detected (e.g. using taps that only provide RX lines). | |
136 | func (a *LinkStatusMonitor) CheckIncomingDelta(val bool, threshold uint64) { | |
137 | a.checkIncomingDelta = val | |
138 | a.checkIncomingThreshold = threshold | |
139 | } | |
140 | ||
141 | func myDiffAbs(new, old uint64) uint64 { | |
142 | if new > old { | |
143 | return new - old | |
144 | } | |
145 | return 0 | |
123 | 146 | } |
124 | 147 | |
125 | 148 | func (a *LinkStatusMonitor) flush() error { |
141 | 164 | } |
142 | 165 | } |
143 | 166 | |
167 | ifstats, err := net.IOCounters(true) | |
168 | if err != nil { | |
169 | return err | |
170 | } | |
171 | ||
172 | if a.checkIncomingDelta { | |
173 | for _, stat := range ifstats { | |
174 | for _, iface := range a.Ifaces { | |
175 | if stat.Name == iface { | |
176 | log.Debugf("%s, %d/%d -> %d", a.LastStatus[iface], stat.BytesRecv, a.LastStats[iface].BytesRecv, myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv)) | |
177 | if a.LastStatus[iface] != InterfaceUp { | |
178 | if myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv) > a.checkIncomingThreshold { | |
179 | out.Ifaces[iface] = InterfaceUp | |
180 | log.Debugf("changed %s to up", iface) | |
181 | } else { | |
182 | out.Ifaces[iface] = a.LastStatus[iface] | |
183 | } | |
184 | } else { | |
185 | if myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv) <= a.checkIncomingThreshold { | |
186 | out.Ifaces[iface] = InterfaceDown | |
187 | log.Debugf("changed %s to down", iface) | |
188 | } else { | |
189 | out.Ifaces[iface] = a.LastStatus[iface] | |
190 | } | |
191 | } | |
192 | if a.LastStatus[iface] != out.Ifaces[iface] { | |
193 | log.Debugf("%s <-> %s", a.LastStatus[iface], out.Ifaces[iface]) | |
194 | changed = true | |
195 | a.LastStatus[iface] = out.Ifaces[iface] | |
196 | } | |
197 | } | |
198 | } | |
199 | a.LastStats[stat.Name] = stat | |
200 | } | |
201 | } | |
202 | ||
144 | 203 | if changed { |
145 | 204 | a.OutChan <- out |
146 | 205 | } |
150 | 209 | // Run starts watching interfaces in the background. |
151 | 210 | func (a *LinkStatusMonitor) Run() { |
152 | 211 | go func() { |
153 | i := 0 * time.Second | |
212 | a.flush() | |
154 | 213 | for { |
155 | 214 | select { |
156 | 215 | case <-a.CloseChan: |
157 | 216 | close(a.ClosedChan) |
158 | 217 | return |
159 | default: | |
160 | if i >= a.PollPeriod { | |
161 | a.flush() | |
162 | i = 0 * time.Second | |
163 | } | |
164 | time.Sleep(1 * time.Second) | |
165 | i += 1 * time.Second | |
218 | case <-time.After(a.PollPeriod): | |
219 | a.flush() | |
166 | 220 | } |
167 | 221 | } |
168 | 222 | }() |