Skip to content

Commit

Permalink
Added Makefile and -add subnet support
Browse files Browse the repository at this point in the history
  • Loading branch information
rdmcguire committed Sep 9, 2022
1 parent 86eb353 commit 9ef0261
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 29 deletions.
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Simple makefile to build and install gateway-mon
#
BIN_NAME=gateway-mon

all: build

build: $(BIN_NAME)

$(BIN_NAME):
go mod tidy -v
go get .
go build -v -x -o $(BIN_NAME) .

install: $(BIN_NAME)
install -vm0544 $(BIN_NAME) /usr/local/bin/
install ./gateway-mon.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable gateway-mon
systemctl restart gateway-mon

clean:
rm $(BIN_NAME)

uninstall:
systemctl stop gateway-mon
systemctl disable gateway-mon
rm /etc/systemd/system/gateway-mon.service
systemctl daemon-reload
rm /usr/local/bin/gateway-mon
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ go build .
Just edit the command in gateway-mon.service. No need to get fancy.
```
Usage of ./gateway-mon:
-add value
Routed networks to create (can be used more than once)
-del value
Extra destination net to delete
Extra destination net to delete (can be used more than once)
-delDefaultGw
Delete Default Gateway
-linkName string
Expand Down
Binary file modified gateway-mon
Binary file not shown.
2 changes: 1 addition & 1 deletion gateway-mon.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/gateway-mon -linkName gpd0 -delDefaultGw -del 192.168.0.0/16
ExecStart=/usr/local/bin/gateway-mon -linkName gpd0 -delDefaultGw -del 192.168.0.0/16 -add 192.168.114.0/24 -add 192.168.115.0/24
StandardOutput=journal
Restart=on-failure

Expand Down
161 changes: 134 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ import (
"net"
"os"
"os/signal"
"strings"
"syscall"

"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
)

// List of destination networks to delete
type netList []string
// List of networks to be deleted or
// added to the interface
type netList struct {
items []string
nets []*net.IPNet
}

var (
logLevel string = "info"
matchRoute *netlink.Route
deleteRoutes []*netlink.Route
log *logrus.Logger
extraNets netList // Routed networks to delete (in addition to default)
matchRoute *netlink.Route
delNets netList // Routed networks to delete (in addition to default)
addNets netList // Routes to add to interface if missing (or deleted by a -del)
linkName string = "gpd0"
delDefaultGw bool
)
Expand All @@ -30,7 +35,8 @@ func init() {
flag.StringVar(&logLevel, "logLevel", logLevel, "Default Log Level")
flag.StringVar(&linkName, "linkName", linkName, "Name of interface to monitor routes for")
flag.BoolVar(&delDefaultGw, "delDefaultGw", delDefaultGw, "Delete Default Gateway")
flag.Var(&extraNets, "del", "Extra destination net to delete")
flag.Var(&delNets, "del", "Extra destination net to delete (can be used more than once)")
flag.Var(&addNets, "add", "Routed networks to create (can be used more than once)")
flag.Parse()

// Logging
Expand All @@ -41,42 +47,39 @@ func init() {
}
log.SetLevel(level)

if len(extraNets) > 0 {
log.Info(extraNets.String())
if len(delNets.items) > 0 {
log.Infof("Deleting Net Routes: %s", delNets.String())
}
if len(addNets.items) > 0 {
log.Infof("Adding Net Routes: %s", addNets.String())
}

if delDefaultGw {
log.Info("Deleting default gateways")
}

if len(extraNets) == 0 && !delDefaultGw {
if len(delNets.items) == 0 && !delDefaultGw {
log.Fatalf("I'm useless, nothing to delete, set one or more -del subnets or -delDefaultGw")
os.Exit(1)
}

log.Infof("Receiving Route Updates from Netlink...")

}

// Slice of networks
// Stringer returns list of parsed CIDRs
func (l *netList) String() string {
str := "Deleting Extra Networks:"
for _, n := range *l {
str += " " + n
}
return str
return strings.Join(l.items, ", ")
}

// Addes item to the list and also parses
// the subnet, returning err if invalid
func (l *netList) Set(val string) error {
_, net, err := net.ParseCIDR(val)
if err != nil {
return err
}
// Add value to list
*l = append(*l, val)
// Create Route
deleteRoutes = append(deleteRoutes, &netlink.Route{
Dst: net,
})
// Add value and network
l.items = append(l.items, val)
l.nets = append(l.nets, net)
return nil
}

Expand All @@ -92,15 +95,21 @@ func main() {
log.Fatalf("Failed to subscribe to netlink route monitor")
}

log.Infof("Receiving Route Updates from Netlink...")

// Handle signals
signal.Notify(killed, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGABRT)

// First-time run
addRoutesIfMissing()
delUnwantedRoutes()

for {
select {
case update := <-routeUpdates:
route := update.Route
// If it's a new route, check it
if update.Type == unix.RTM_NEWROUTE {
// If it's a new route, check it
log.Debugf("Route Added: %+v", route)

// Check interface
Expand All @@ -114,8 +123,11 @@ func main() {
log.Debugf("Route: %+v", route)

delIfDefault(&route) // Delete this route if it's a default route
delIfExtraRoute(&route) // Deletet this route if it's unwanted
delIfExtraRoute(&route) // Delete this route if it's unwanted
}
} else if update.Type == unix.RTM_DELROUTE {
// If a route was removed, ensure desired routes
addRoutesIfMissing()
}

// Handle signal
Expand All @@ -127,10 +139,105 @@ func main() {

}

// Scans through the link's routes, deleting any route that
// is set to delete in -del or -delDefaultGw. Typically only
// run on startup
func delUnwantedRoutes() {
// Retrieve the link
link, err := netlink.LinkByName(linkName)
if err != nil {
log.Errorf("Can't delete routes, link %s not found yet: %+v", linkName, err)
return
}

// Retrieve its routes
linkRoutes, err := netlink.RouteList(link, 4)
if err != nil || len(linkRoutes) == 0 {
log.Errorf("Can't delete routes, no routes found on %s: %+v", linkName, err)
return
}

// Delete unwanted routes
for _, r := range linkRoutes {
delIfExtraRoute(&r)
delIfDefault(&r)
}
}

// Scans through the link's routes, and adds in any requested
// routes that are missing. Chooses the most common next-hop, and aborts
// if there are no routes or if the link doesn't exist
func addRoutesIfMissing() {
if len(addNets.nets) == 0 {
log.Debugf("No routes to add, skipping...")
return
}

// Retrieve the link
link, err := netlink.LinkByName(linkName)
if err != nil {
log.Errorf("Can't add routes, link %s not found yet: %+v", linkName, err)
return
}

// Retrieve its routes
linkRoutes, err := netlink.RouteList(link, 4)
if err != nil || len(linkRoutes) == 0 {
log.Errorf("Can't add routes, no routes found on %s: %+v", linkName, err)
return
}

// Find next-hop
nextHop := getLinkNextHop(linkRoutes)

// Add routes
if nextHop != nil {
for _, net := range addNets.nets {
newRoute := &netlink.Route{
LinkIndex: link.Attrs().Index,
Dst: net,
Gw: *nextHop,
}
if err := netlink.RouteAdd(newRoute); err != nil {
log.WithFields(logrus.Fields{
"link": linkName,
"route": newRoute,
"error": err,
})
} else {
log.WithFields(logrus.Fields{
"link": linkName,
"to": newRoute.Dst.String(),
"via": nextHop.String(),
}).Infof("Added route to link")
}
}
}
}

// Given a list of routes, returns the most commonly used Gw address
func getLinkNextHop(routes []netlink.Route) *net.IP {
var nextHop *net.IP
// Record gateway occurrences
gateways := make(map[*net.IP]int)
for _, r := range routes {
gateways[&r.Gw] += 1
}
// Return most common
var max int
for gw, i := range gateways {
if i > max {
nextHop = gw
}
}
log.Debugf("Found most common gateway (%s) in %d routes", nextHop.String(), len(routes))
return nextHop
}

// If this route is listed in our extraNets slice, delete it
func delIfExtraRoute(r *netlink.Route) {
for _, route := range deleteRoutes {
if r.Dst != nil && r.Dst.IP.Equal(route.Dst.IP) {
for _, route := range delNets.nets {
if r.Dst != nil && r.Dst.IP.Equal(route.IP) {
log.Infof("Found extra route to delete: %+v", r)
delRoute(r)
}
Expand Down

0 comments on commit 9ef0261

Please sign in to comment.