Commit 4277d565 authored by Sascha Steinbiss's avatar Sascha Steinbiss

New upstream version 0.0~git20180516.4249335

parents
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
\ No newline at end of file
language: go
addons:
apt:
packages:
- libdaemon-dev
go:
- 1.9
- master
matrix:
# It's ok if our code fails on unstable development versions of Go.
allow_failures:
- go: master
# Don't wait for tip tests to finish. Mark the test run green if the
# tests pass on the stable versions of Go.
fast_finish: true
# Anything in before_script that returns a nonzero exit code will
# flunk the build and immediately stop. It's sorta like having
# set -e enabled in bash.
before_script:
- GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/) # All the .go files, excluding vendor/
- go get github.com/golang/lint/golint # Linter
- go get honnef.co/go/tools/cmd/megacheck # Badass static analyzer/linter
- go get github.com/fzipp/gocyclo
# script always run to completion (set +e). All of these code checks are must haves
# in a modern Go project.
script:
- test -z $(gofmt -s -l $GO_FILES) # Fail if a .go file hasn't been formatted with gofmt
- go test -v -race ./... # Run all the tests with the race detector enabled
- go vet ./... # go vet is the official Go static analyzer
- megacheck ./... # "go vet on steroids" + linter
- gocyclo -over 19 $GO_FILES # forbid code with huge functions
- golint -set_exit_status $(go list ./...) # one last linter
This source code includes parts of ifplugd, written by Lennart Poettering
<mzvscyhtq (at) 0pointer (dot) de> (Copyright 2002-2005).
The Go component of the code was written by Sascha Steinbiss
<sascha@steinbiss.name>.
\ No newline at end of file
This diff is collapsed.
# ifplugo
[![GoDoc](https://godoc.org/github.com/satta/ifplugo?status.svg)](http://godoc.org/github.com/satta/ifplugo)
[![Build Status](https://travis-ci.org/satta/ifplugo.svg?branch=master)](https://travis-ci.org/satta/ifplugo)
ifplugo delivers network interface link information and link changes. It does this (on Linux) by using code from [ifplugd](http://0pointer.de/lennart/projects/ifplugd/) to gather the necessary status information, then emits a status summary on a given channel. This summary (`LinkStatusSample`) is emitted on the first invocation and each time the state changes for at least one monitored interface.
```Go
type LinkStatusSample struct {
Ifaces map[string]InterfaceStatus
}
```
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
)
```
These summaries can then easily be consumed (example):
```Go
outchan := make(chan ifplugo.LinkStatusSample)
mon := ifplugo.MakeLinkStatusMonitor(2 * time.Second, []string{"eth0"}, outchan)
go func() {
for v := range outchan {
for k, v := range v.Ifaces {
fmt.Printf("%s: %s\n", k, v)
}
}
}()
mon.Run()
```
## Prerequisites
To build ifplugo, one needs [libdaemon](http://0pointer.de/lennart/projects/libdaemon/) in addition to Go and C compilers.
Also, obviously, this is Linux-only.
## Example
See the source code of the simple command line tools in `cmd/*` for more simple examples of how to use ifplugo.
```
$ ifplugo-watch eth0,eth1,eth2,eth3
eth0: link
eth1: link
eth2: link
eth3: no link
^C
$
```
## Authors
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>.
## License
GPL2
package main
// This file is part of ifplugo.
//
// ifplugo is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// ifplugo 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ifplugo; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import (
"fmt"
"os"
"github.com/satta/ifplugo"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: ifplugo-status <iface>")
os.Exit(1)
}
fmt.Println(ifplugo.GetLinkStatus(os.Args[1]))
}
package main
// This file is part of ifplugo.
//
// ifplugo is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// ifplugo 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ifplugo; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import (
"fmt"
"os"
"strings"
"time"
"github.com/satta/ifplugo"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: ifplugo-status <iface1,iface2,...>")
os.Exit(1)
}
ifaces := strings.Split(os.Args[1], ",")
outchan := make(chan ifplugo.LinkStatusSample)
mon := ifplugo.MakeLinkStatusMonitor(2*time.Second, ifaces, outchan)
go func() {
for v := range outchan {
for k, v := range v.Ifaces {
fmt.Printf("%s: %s\n", k, v)
}
}
}()
mon.Run()
select {}
}
This diff is collapsed.
#ifndef fooethtoollocalhfoo
#define fooethtoollocalhfoo
/* $Id: ethtool-local.h 43 2003-09-13 11:25:11Z lennart $ */
/*
* This file is part of ifplugd.
*
* ifplugd is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ifplugd 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ifplugd; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
typedef unsigned long long u64;
typedef __uint32_t u32;
typedef __uint16_t u16;
typedef __uint8_t u8;
#include "ethtool-kernel.h"
#endif
package ifplugo
// This file is part of ifplugo.
//
// ifplugo is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// ifplugo 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ifplugo; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
/*
#cgo LDFLAGS: -ldaemon
#include <interface.h>
*/
import (
"C"
)
import (
"syscall"
"time"
)
// InterfaceStatus represents the link status of an interface.
type InterfaceStatus int
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
)
var statusLookup = map[C.interface_status_t]InterfaceStatus{
C.IFSTATUS_UP: InterfaceUp,
C.IFSTATUS_DOWN: InterfaceDown,
C.IFSTATUS_ERR: InterfaceErr,
}
func (s InterfaceStatus) String() string {
switch s {
case InterfaceUp:
return "link"
case InterfaceDown:
return "no link"
case InterfaceErr:
return "error"
default:
return "unknown"
}
}
// GetLinkStatus returns, for a given interface, the corresponding status code
// at the time of the call. If any error was encountered (e.g. invalid
// interface, etc.) we simply return ifplugo.InterfaceErr.
func GetLinkStatus(iface string) (InterfaceStatus, error) {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM,
syscall.IPPROTO_IP)
if err != nil {
return InterfaceErr, err
}
defer syscall.Close(fd)
e := C.interface_detect_beat_ethtool(C.int(fd), C.CString(iface))
if e == C.IFSTATUS_ERR {
e = C.interface_detect_beat_mii(C.int(fd), C.CString(iface))
if e == C.IFSTATUS_ERR {
e = C.interface_detect_beat_wlan(C.int(fd), C.CString(iface))
if e == C.IFSTATUS_ERR {
e = C.interface_detect_beat_iff(C.int(fd), C.CString(iface))
}
}
}
return statusLookup[e], nil
}
// LinkStatusMonitor represents a concurrent software component that
// 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
}
// LinkStatusSample is a single description of the link status at a given time.
// Changed is set to true if the state is different than the previously emitted
// one.
type LinkStatusSample struct {
Ifaces map[string]InterfaceStatus
}
// MakeLinkStatusMonitor creates a new LinkStatusMonitor, polling each interval
// given in pollPeriod for the status information of the interfaces given in
// ifaces and outputting results as a map of interface->status pairs in the
// channel outChan.
func MakeLinkStatusMonitor(pollPeriod time.Duration, ifaces []string,
outChan chan LinkStatusSample) *LinkStatusMonitor {
a := &LinkStatusMonitor{
PollPeriod: pollPeriod,
OutChan: outChan,
CloseChan: make(chan bool),
ClosedChan: make(chan bool),
Ifaces: ifaces,
LastStatus: make(map[string]InterfaceStatus),
}
return a
}
func (a *LinkStatusMonitor) flush() error {
out := LinkStatusSample{
Ifaces: make(map[string]InterfaceStatus),
}
changed := false
for _, iface := range a.Ifaces {
v, err := GetLinkStatus(iface)
if err != nil {
out.Ifaces[iface] = InterfaceUnknown
}
out.Ifaces[iface] = v
if a.LastStatus[iface] != out.Ifaces[iface] {
changed = true
a.LastStatus[iface] = out.Ifaces[iface]
}
}
if changed {
a.OutChan <- out
}
return nil
}
// Run starts watching interfaces in the background.
func (a *LinkStatusMonitor) Run() {
go func() {
i := 0 * time.Second
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
}
}
}()
}
// Stop causes the monitor to cease monitoring interfaces.
func (a *LinkStatusMonitor) Stop() {
close(a.CloseChan)
<-a.ClosedChan
}
package ifplugo
// This file is part of ifplugo.
//
// ifplugo is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// ifplugo 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with ifplugo; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
import (
"log"
"net"
"sync"
"testing"
"time"
)
func TestIface(t *testing.T) {
intfs, err := net.Interfaces()
if err != nil {
t.Fatal(err)
}
if len(intfs) == 0 {
log.Println("No interfaces present, skipping...")
t.SkipNow()
}
empty := true
for _, intf := range intfs {
stats, err := GetLinkStatus(intf.Name)
if err != nil {
continue
}
log.Println("Got status for ", intf.Name)
if stats != InterfaceErr {
empty = false
}
}
if empty {
t.Fatal("Unable to retrieve status from any interface of this system.")
}
}
func TestMonitor(t *testing.T) {
intfs, err := net.Interfaces()
if err != nil {
t.Fatal(err)
}
if len(intfs) == 0 {
log.Println("No interfaces present, skipping...")
t.SkipNow()
}
ifaces := make([]string, 0)
for _, intf := range intfs {
ifaces = append(ifaces, intf.Name)
}
waitChan := make(chan bool)
outChan := make(chan LinkStatusSample)
mon := MakeLinkStatusMonitor(2*time.Second, ifaces, outChan)
var resMutex sync.Mutex
cnt := 0
results := make(map[string]int)
go func(c *int) {
for o := range outChan {
resMutex.Lock()
for k, v := range o.Ifaces {
results[k]++
log.Printf("got status for %s: %s", k, v)
}
(*c)++
resMutex.Unlock()
}
close(waitChan)
}(&cnt)
mon.Run()
time.Sleep(5 * time.Second)
mon.Stop()
resMutex.Lock()
if cnt != 1 {
t.Fatalf("expected 1 output, got %d", cnt)
}
for _, v := range ifaces {
if results[v] == 0 {
t.Fatalf("unseen interface %s", v)
}
}
for k := range results {
found := false
for _, i := range ifaces {
if i == k {
found = true
break
}
}
if !found {
t.Fatalf("unknown result interface %s", k)
}
}
resMutex.Unlock()
close(outChan)
<-waitChan
}
/* $Id: interface.c 114 2004-12-19 00:08:01Z lennart $ */
/*
* This file is part of ifplugd.
*
* ifplugd is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ifplugd 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ifplugd; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <linux/sockios.h>
#include <linux/if_ether.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include "ethtool-local.h"
#include "interface.h"
#include "wireless.h"
#include <libdaemon/dlog.h>
void interface_up(int fd, char *iface) {
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: Could not get interface flags.");
return;
}
if ((ifr.ifr_flags & IFF_UP) == IFF_UP)
return;
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: Could not get interface address.");
} else if (ifr.ifr_addr.sa_family != AF_INET) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: The interface is not IP-based.");
} else {
((struct sockaddr_in *)(&ifr.ifr_addr))->sin_addr.s_addr = INADDR_ANY;
if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: Could not set interface address.");
}
}
if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: Could not get interface flags.");
return;
}
ifr.ifr_flags |= IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
if (interface_do_message)
daemon_log(LOG_WARNING, "Warning: Could not set interface flags.");
}
interface_status_t interface_detect_beat_mii(int fd, char *iface) {
struct ifreq ifr;
if (interface_auto_up)
interface_up(fd, iface);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
if (ioctl(fd, SIOCGMIIPHY, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "SIOCGMIIPHY failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
((unsigned short*) &ifr.ifr_data)[1] = 1;
if (ioctl(fd, SIOCGMIIREG, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "SIOCGMIIREG failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
return (((unsigned short*) &ifr.ifr_data)[3] & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
}
interface_status_t interface_detect_beat_priv(int fd, char *iface) {
struct ifreq ifr;
if (interface_auto_up)
interface_up(fd, iface);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "SIOCDEVPRIVATE failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
((unsigned short*) &ifr.ifr_data)[1] = 1;
if (ioctl(fd, SIOCDEVPRIVATE+1, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "SIOCDEVPRIVATE+1 failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
return (((unsigned short*) &ifr.ifr_data)[3] & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
}
interface_status_t interface_detect_beat_ethtool(int fd, char *iface) {
struct ifreq ifr;
struct ethtool_value edata;
if (interface_auto_up)
interface_up(fd, iface);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
edata.cmd = ETHTOOL_GLINK;
ifr.ifr_data = (caddr_t) &edata;
if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "ETHTOOL_GLINK failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
}
interface_status_t interface_detect_beat_iff(int fd, char *iface) {
struct ifreq ifr;
if (interface_auto_up)
interface_up(fd, iface);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {
if (interface_do_message)
daemon_log(LOG_ERR, "SIOCGIFFLAGS failed: %s", strerror(errno));
return IFSTATUS_ERR;
}
return ifr.ifr_flags & IFF_RUNNING ? IFSTATUS_UP : IFSTATUS_DOWN;
}
static int get_wlan_qual_old(char *iface) {
FILE *f;
char buf[256];
char *bp;
int l, q = -1;
l = strlen(iface);
if (!(f = fopen("/proc/net/wireless", "r"))) {
if (interface_do_message)
daemon_log(LOG_WARNING, "Failed to open /proc/net/wireless: %s",strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf)-1, f)) {
bp = buf;
while (*bp && isspace(*bp))
bp++;
if(!strncmp(bp, iface, l) && bp[l]==':') {