Commit 80486dfe authored by Sascha Steinbiss's avatar Sascha Steinbiss

New upstream version 0.0~git20180801.7e2d884

parent 4277d565
......@@ -15,14 +15,14 @@ where `InterfaceStatus` can be:
```Go
const (
// InterfaceUnknown represents an interface with no assigned state.
InterfaceUnknown InterfaceStatus = iota
// InterfaceUp represents an interface with a cable connected.
InterfaceUp
// InterfaceDown represents an interface with no cable connected.
InterfaceDown
// InterfaceErr represents an interface with errors querying its status.
InterfaceErr
// InterfaceUnknown represents an interface with no assigned state.
InterfaceUnknown InterfaceStatus = iota
// InterfaceUp represents an interface with a cable connected.
InterfaceUp
// InterfaceDown represents an interface with no cable connected.
InterfaceDown
// InterfaceErr represents an interface with errors querying its status.
InterfaceErr
)
```
......@@ -41,6 +41,14 @@ go func() {
mon.Run()
```
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:
```Go
mon.CheckIncomingDelta(true, 1000)
```
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.
## Prerequisites
To build ifplugo, one needs [libdaemon](http://0pointer.de/lennart/projects/libdaemon/) in addition to Go and C compilers.
......@@ -50,7 +58,7 @@ Also, obviously, this is Linux-only.
See the source code of the simple command line tools in `cmd/*` for more simple examples of how to use ifplugo.
```
```Text
$ ifplugo-watch eth0,eth1,eth2,eth3
eth0: link
eth1: link
......@@ -62,11 +70,9 @@ $
## Authors
This source code includes parts of ifplugd, written by Lennart Poettering
<mzvscyhtq (at) 0pointer (dot) de>.
This source code includes parts of ifplugd, written by Lennart Poettering <mzvscyhtq (at) 0pointer (dot) de>.
The Go component of the code was written by Sascha Steinbiss
<sascha (at) steinbiss (dot) name>.
The Go component of the code was written by Sascha Steinbiss <sascha (at) steinbiss (dot) name>.
## License
......
......@@ -17,23 +17,37 @@ package main
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import (
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/satta/ifplugo"
log "github.com/sirupsen/logrus"
)
func main() {
if len(os.Args) < 2 {
deltaValPtr := flag.Int("delta", 0, "number of bytes transferred per polling period to mark interface as seeing traffic")
pollingPtr := flag.Duration("poll", 2*time.Second, "polling period")
verbPtr := flag.Bool("verbose", false, "verbose logging")
flag.Parse()
if len(flag.Args()) < 1 {
fmt.Println("Usage: ifplugo-status <iface1,iface2,...>")
os.Exit(1)
}
ifaces := strings.Split(os.Args[1], ",")
ifaces := strings.Split(flag.Args()[0], ",")
if *verbPtr {
log.SetLevel(log.DebugLevel)
}
outchan := make(chan ifplugo.LinkStatusSample)
mon := ifplugo.MakeLinkStatusMonitor(2*time.Second, ifaces, outchan)
mon := ifplugo.MakeLinkStatusMonitor(*pollingPtr, ifaces, outchan)
if *deltaValPtr > 0 {
mon.CheckIncomingDelta(true, uint64(*deltaValPtr))
}
go func() {
for v := range outchan {
for k, v := range v.Ifaces {
......
......@@ -27,6 +27,9 @@ import (
import (
"syscall"
"time"
"github.com/shirou/gopsutil/net"
log "github.com/sirupsen/logrus"
)
// InterfaceStatus represents the link status of an interface.
......@@ -91,12 +94,15 @@ func GetLinkStatus(iface string) (InterfaceStatus, error) {
// periodically checks a list of given interfaces and returns their link status
// via a specified channel.
type LinkStatusMonitor struct {
PollPeriod time.Duration
LastStatus map[string]InterfaceStatus
OutChan chan LinkStatusSample
CloseChan chan bool
ClosedChan chan bool
Ifaces []string
PollPeriod time.Duration
LastStatus map[string]InterfaceStatus
LastStats map[string]net.IOCountersStat
checkIncomingDelta bool
checkIncomingThreshold uint64
OutChan chan LinkStatusSample
CloseChan chan bool
ClosedChan chan bool
Ifaces []string
}
// LinkStatusSample is a single description of the link status at a given time.
......@@ -119,10 +125,27 @@ func MakeLinkStatusMonitor(pollPeriod time.Duration, ifaces []string,
ClosedChan: make(chan bool),
Ifaces: ifaces,
LastStatus: make(map[string]InterfaceStatus),
LastStats: make(map[string]net.IOCountersStat),
}
return a
}
// CheckIncomingDelta allows to enable the optional behaviour to also consider
// an interface as 'up' if traffic is received on it. This is, for example,
// necessary in passive monitoring setups where there is no physical link
// detected (e.g. using taps that only provide RX lines).
func (a *LinkStatusMonitor) CheckIncomingDelta(val bool, threshold uint64) {
a.checkIncomingDelta = val
a.checkIncomingThreshold = threshold
}
func myDiffAbs(new, old uint64) uint64 {
if new > old {
return new - old
}
return 0
}
func (a *LinkStatusMonitor) flush() error {
out := LinkStatusSample{
Ifaces: make(map[string]InterfaceStatus),
......@@ -142,6 +165,42 @@ func (a *LinkStatusMonitor) flush() error {
}
}
ifstats, err := net.IOCounters(true)
if err != nil {
return err
}
if a.checkIncomingDelta {
for _, stat := range ifstats {
for _, iface := range a.Ifaces {
if stat.Name == iface {
log.Debugf("%s, %d/%d -> %d", a.LastStatus[iface], stat.BytesRecv, a.LastStats[iface].BytesRecv, myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv))
if a.LastStatus[iface] != InterfaceUp {
if myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv) > a.checkIncomingThreshold {
out.Ifaces[iface] = InterfaceUp
log.Debugf("changed %s to up", iface)
} else {
out.Ifaces[iface] = a.LastStatus[iface]
}
} else {
if myDiffAbs(stat.BytesRecv, a.LastStats[iface].BytesRecv) <= a.checkIncomingThreshold {
out.Ifaces[iface] = InterfaceDown
log.Debugf("changed %s to down", iface)
} else {
out.Ifaces[iface] = a.LastStatus[iface]
}
}
if a.LastStatus[iface] != out.Ifaces[iface] {
log.Debugf("%s <-> %s", a.LastStatus[iface], out.Ifaces[iface])
changed = true
a.LastStatus[iface] = out.Ifaces[iface]
}
}
}
a.LastStats[stat.Name] = stat
}
}
if changed {
a.OutChan <- out
}
......@@ -151,19 +210,14 @@ func (a *LinkStatusMonitor) flush() error {
// Run starts watching interfaces in the background.
func (a *LinkStatusMonitor) Run() {
go func() {
i := 0 * time.Second
a.flush()
for {
select {
case <-a.CloseChan:
close(a.ClosedChan)
return
default:
if i >= a.PollPeriod {
a.flush()
i = 0 * time.Second
}
time.Sleep(1 * time.Second)
i += 1 * time.Second
case <-time.After(a.PollPeriod):
a.flush()
}
}
}()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment