fix: refactored routing logic (fixes #701)

This commit is contained in:
Simone Margaritelli 2021-04-10 21:55:00 +02:00
parent 88a83192ef
commit 43a93fd866
11 changed files with 202 additions and 80 deletions

View file

@ -305,7 +305,6 @@ func SetInterfaceTxPower(name string, txpower int) error {
} }
func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) { func GatewayProvidedByUser(iface *Endpoint, gateway string) (*Endpoint, error) {
Debug("GatewayProvidedByUser(%s) [cmd=%v opts=%v parser=%v]", gateway, IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser)
if IPv4Validator.MatchString(gateway) { if IPv4Validator.MatchString(gateway) {
Debug("valid gateway ip %s", gateway) Debug("valid gateway ip %s", gateway)
// we have the address, now we need its mac // we have the address, now we need its mac

View file

@ -13,23 +13,8 @@ import (
const airPortPath = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport" const airPortPath = "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport"
var IPv4RouteParser = regexp.MustCompile(`([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+).*`)
var IPv4RouteTokens = 5
var IPv4RouteCmd = "netstat"
var IPv4RouteCmdOpts = []string{"-n", "-r"}
var WiFiChannelParser = regexp.MustCompile(`(?m)^.*Supported Channels: (.*)$`) var WiFiChannelParser = regexp.MustCompile(`(?m)^.*Supported Channels: (.*)$`)
func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) {
flags := tokens[3]
ifname2 := tokens[4]
if ifname == ifname2 && flags == "UGSc" {
gateway := tokens[2]
return f(gateway)
}
return nil, nil
}
// see Windows version to understand why .... // see Windows version to understand why ....
func getInterfaceName(iface net.Interface) string { func getInterfaceName(iface net.Interface) string {
return iface.Name return iface.Name

View file

@ -3,46 +3,26 @@
package network package network
import ( import (
"strings" "github.com/bettercap/bettercap/routing"
"github.com/bettercap/bettercap/core"
"github.com/evilsocket/islazy/str"
) )
func FindGateway(iface *Endpoint) (*Endpoint, error) { func FindGateway(iface *Endpoint) (*Endpoint, error) {
Debug("FindGateway(%s) [cmd=%v opts=%v parser=%v]", iface.Name(), IPv4RouteCmd, IPv4RouteCmdOpts, IPv4RouteParser) gateway, err := routing.Gateway(routing.IPv4, iface.Name())
output, err := core.Exec(IPv4RouteCmd, IPv4RouteCmdOpts)
if err != nil { if err != nil {
Debug("FindGateway(%s): core.Exec failed with %s", err)
return nil, err return nil, err
} }
Debug("FindGateway(%s) output:\n%s", iface.Name(), output) if gateway == iface.IpAddress {
Debug("gateway is the interface")
ifName := iface.Name() return iface, nil
for _, line := range strings.Split(output, "\n") { } else {
if line = str.Trim(line); strings.Contains(line, ifName) { // we have the address, now we need its mac
m := IPv4RouteParser.FindStringSubmatch(line) mac, err := ArpLookup(iface.Name(), gateway, false)
if len(m) >= IPv4RouteTokens { if err != nil {
Debug("FindGateway(%s) line '%s' matched with %v", iface.Name(), line, m) return nil, err
return IPv4RouteIsGateway(ifName, m, func(gateway string) (*Endpoint, error) {
if gateway == iface.IpAddress {
Debug("gateway is the interface")
return iface, nil
} else {
// we have the address, now we need its mac
mac, err := ArpLookup(ifName, gateway, false)
if err != nil {
return nil, err
}
Debug("gateway is %s[%s]", gateway, mac)
return NewEndpoint(gateway, mac), nil
}
})
}
} }
Debug("gateway is %s[%s]", gateway, mac)
return NewEndpoint(gateway, mac), nil
} }
Debug("FindGateway(%s): nothing found :/", iface.Name()) Debug("FindGateway(%s): nothing found :/", iface.Name())

View file

@ -24,4 +24,4 @@ func FindGateway(iface *Endpoint) (*Endpoint, error) {
} }
return nil, ErrNoGateway return nil, ErrNoGateway
} }

View file

@ -11,23 +11,6 @@ import (
"github.com/bettercap/bettercap/core" "github.com/bettercap/bettercap/core"
) )
// only matches gateway lines
var IPv4RouteParser = regexp.MustCompile(`^(default|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\svia\s([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\sdev\s(\S+).*$`)
var IPv4RouteTokens = 4
var IPv4RouteCmd = "ip"
var IPv4RouteCmdOpts = []string{"route"}
func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) {
ifname2 := tokens[3]
if ifname == ifname2 {
gateway := tokens[2]
return f(gateway)
}
return nil, nil
}
// see Windows version to understand why .... // see Windows version to understand why ....
func getInterfaceName(iface net.Interface) string { func getInterfaceName(iface net.Interface) string {
return iface.Name return iface.Name

View file

@ -3,25 +3,11 @@ package network
import ( import (
"fmt" "fmt"
"net" "net"
"regexp"
"strings" "strings"
"github.com/google/gopacket/pcap" "github.com/google/gopacket/pcap"
) )
// only matches gateway lines
var IPv4RouteParser = regexp.MustCompile("^.+\\s+.+\\s+\\d+\\s+([0-9\\.]+/\\d+)\\s+\\d+\\s+([0-9\\.]+).*$")
var IPv4RouteTokens = 3
var IPv4RouteCmd = "netsh"
var IPv4RouteCmdOpts = []string{"interface", "ipv4", "show", "route"}
func IPv4RouteIsGateway(ifname string, tokens []string, f func(gateway string) (*Endpoint, error)) (*Endpoint, error) {
// TODO check if the subnet is the same as iface ?
// subnet := tokens[1]
gateway := tokens[2]
return f(gateway)
}
/* /*
* net.Interface does not have the correct name on Windows and pcap.Interface * net.Interface does not have the correct name on Windows and pcap.Interface
* does not have the hardware address for some reason ... so this is what I * does not have the hardware address for some reason ... so this is what I

17
routing/route.go Normal file
View file

@ -0,0 +1,17 @@
package routing
type RouteType int
const (
IPv4 RouteType = 0
IPv6 RouteType = 1
)
type Route struct {
Type RouteType
Default bool
Device string
Destination string
Gateway string
Flags string
}

39
routing/tables.go Normal file
View file

@ -0,0 +1,39 @@
package routing
import "sync"
var (
lock = sync.RWMutex{}
table = make([]Route, 0)
)
func Table() []Route {
lock.RLock()
defer lock.RUnlock()
return table
}
func Update() ([]Route, error) {
lock.Lock()
defer lock.Unlock()
return update()
}
func Gateway(ip RouteType, device string) (string, error) {
Update()
lock.RLock()
defer lock.RUnlock()
for _, r := range table {
if r.Type == ip {
if device == "" || r.Device == device || r.Device == "" /* windows case */ {
if r.Default {
return r.Gateway, nil
}
}
}
}
return "", nil
}

44
routing/update_darwin.go Normal file
View file

@ -0,0 +1,44 @@
package routing
import (
"github.com/bettercap/bettercap/core"
"github.com/evilsocket/islazy/str"
"regexp"
"strings"
)
var parser = regexp.MustCompile(`^([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+).*$`)
func update() ([]Route, error) {
table = make([]Route, 0)
output, err := core.Exec("netstat", []string{"-n", "-r"})
if err != nil {
return nil, err
}
for _, line := range strings.Split(output, "\n") {
if line = str.Trim(line); len(line) > 0 {
matches := parser.FindStringSubmatch(line)
if num := len(matches); num == 5 && matches[1] != "Destination" {
route := Route{
Destination: matches[1],
Gateway: matches[2],
Flags: matches[3],
Device: matches[4],
Default: matches[1] == "default",
}
if strings.ContainsRune(route.Destination, '.') || strings.ContainsRune(route.Gateway, '.') {
route.Type = IPv4
} else {
route.Type = IPv6
}
table = append(table, route)
}
}
}
return table, nil
}

45
routing/update_linux.go Normal file
View file

@ -0,0 +1,45 @@
package routing
import (
"github.com/bettercap/bettercap/core"
"github.com/evilsocket/islazy/str"
"regexp"
"strings"
)
var parser = regexp.MustCompile(`^(.+)\sdev\s([^\s]+)\s(.+)$`)
func update() ([]Route, error) {
table = make([]Route, 0)
for ip, inet := range map[RouteType]string{IPv4: "inet", IPv6: "inet6"} {
output, err := core.Exec("ip", []string{"-f", inet, "route"})
if err != nil {
return nil, err
}
for _, line := range strings.Split(output, "\n") {
if line = str.Trim(line); len(line) > 0 {
matches := parser.FindStringSubmatch(line)
if num := len(matches); num == 4 {
route := Route{
Type: ip,
Destination: matches[1],
Device: matches[2],
Flags: matches[3],
Default: strings.Index(matches[1], "default ") == 0,
}
if idx := strings.Index(route.Destination, " via "); idx >= 0 {
route.Gateway = route.Destination[idx + len(" via "):]
route.Destination = route.Destination[:idx]
}
table = append(table, route)
}
}
}
}
return table, nil
}

44
routing/update_windows.go Normal file
View file

@ -0,0 +1,44 @@
package routing
import (
"github.com/bettercap/bettercap/core"
"github.com/evilsocket/islazy/str"
"regexp"
"strings"
)
var parser = regexp.MustCompile(`^.+\d+\s+([^\s]+)\s+\d+\s+(.+)$`)
func update() ([]Route, error) {
table = make([]Route, 0)
for ip, inet := range map[RouteType]string{IPv4: "ipv4", IPv6: "ipv6"} {
output, err := core.Exec("netsh", []string{"interface", inet, "show", "route"})
if err != nil {
return nil, err
}
for _, line := range strings.Split(output, "\n") {
if line = str.Trim(line); len(line) > 0 {
matches := parser.FindStringSubmatch(line)
if num := len(matches); num == 3 {
route := Route{
Type: ip,
Destination: matches[1],
Device: matches[2],
}
if route.Destination == "0.0.0.0/0" || route.Destination == "::/0" {
route.Default = true
route.Gateway = route.Device
route.Device = ""
}
table = append(table, route)
}
}
}
}
return table, nil
}