From 5d40cde0c8ef10b63741701cf6dbb7861b9bcb25 Mon Sep 17 00:00:00 2001 From: Ruakij Date: Tue, 15 Nov 2022 18:12:16 +0100 Subject: [PATCH] Add project --- app.go | 9 - cmd/app/filter.go | 18 ++ cmd/app/logger.go | 17 ++ cmd/app/main.go | 214 +++++++++++++++++++++ go.mod | 22 ++- go.sum | 70 +++++++ lib/environmentchecks/environmentchecks.go | 27 +++ lib/iproute2mapping/mapping.go | 102 ++++++++++ lib/iproute2mapping/mapping_test.go | 2 + lib/netchecks/netchecks.go | 25 +++ lib/wgchecks/wgchecks.go | 42 ++++ 11 files changed, 538 insertions(+), 10 deletions(-) delete mode 100644 app.go create mode 100644 cmd/app/filter.go create mode 100644 cmd/app/logger.go create mode 100644 cmd/app/main.go create mode 100644 go.sum create mode 100644 lib/environmentchecks/environmentchecks.go create mode 100644 lib/iproute2mapping/mapping.go create mode 100644 lib/iproute2mapping/mapping_test.go create mode 100644 lib/netchecks/netchecks.go create mode 100644 lib/wgchecks/wgchecks.go diff --git a/app.go b/app.go deleted file mode 100644 index 20f0d0f..0000000 --- a/app.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "fmt" -) - -func main() { - fmt.Println("Hello World!") -} diff --git a/cmd/app/filter.go b/cmd/app/filter.go new file mode 100644 index 0000000..9e1c9e2 --- /dev/null +++ b/cmd/app/filter.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/vishvananda/netlink" +) + +type FilterOptions struct { + Table int + Protocol int +} + +func CheckFilter(options FilterOptions, route netlink.Route) bool { + if (options.Table != -1 && options.Table != route.Table) || + (options.Protocol != -1 && options.Protocol != route.Protocol) { + return false + } + return true +} diff --git a/cmd/app/logger.go b/cmd/app/logger.go new file mode 100644 index 0000000..2bf05c4 --- /dev/null +++ b/cmd/app/logger.go @@ -0,0 +1,17 @@ +package main + +import ( + "log" + "os" +) + +type Log struct { + Info log.Logger + Warn log.Logger + Error log.Logger +} +var logger Log = Log{ + Info: *log.New(os.Stdout, "[INFO]\t", log.Ltime|log.Lshortfile), + Warn: *log.New(os.Stderr, "[WARN]\t", log.Ltime|log.Lshortfile), + Error: *log.New(os.Stderr, "[ERROR]\t", log.Ltime|log.Lshortfile), +} diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 0000000..e75798b --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,214 @@ +package main + +import ( + "net" + "os" + + envChecks "git.ruekov.eu/ruakij/routingtabletowg/lib/environmentchecks" + ip2Map "git.ruekov.eu/ruakij/routingtabletowg/lib/iproute2mapping" + "git.ruekov.eu/ruakij/routingtabletowg/lib/wgchecks" + + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +var envRequired = []string{ + "INTERFACE", +} +var envDefaults = map[string]string{ + "INTERFACE": "wg0", + //"MANAGE_ALL": "true", + + "FILTER_PROTOCOL": "-1", + "FILTER_TABLE": "-1", +} + +func main() { + // Environment-vars + err := envChecks.HandleRequired(envRequired) + if(err != nil){ + logger.Error.Fatal(err) + } + envChecks.HandleDefaults(envDefaults) + + iface := os.Getenv("INTERFACE") + //MANAGE_ALL = os.Getenv("MANAGE_ALL") + + // Parse filter-env-vars + filterProtocolStr := os.Getenv("FILTER_PROTOCOL") + filterProtocol, err := ip2Map.TryGetId(ip2Map.PROTOCOL, filterProtocolStr) + if err != nil { + logger.Error.Fatalf("Couldn't read FILTER_PROTOCOL '%s': %s", filterProtocolStr, err) + } + + filterTableStr := os.Getenv("FILTER_TABLE") + filterTable, err := ip2Map.TryGetId(ip2Map.TABLE, filterTableStr) + if err != nil { + logger.Error.Fatalf("Couldn't read FILTER_TABLE '%s': %s", filterTableStr, err) + } + + // Create filter + filterOptions := FilterOptions{ + Table: filterTable, + Protocol: filterProtocol, + } + + // Get Link-Device + link, err := netlink.LinkByName(iface) + if err != nil { + logger.Error.Fatalf("Couldn't get interface '%s': %s", iface, err) + } + + // Test getting wg-client + client, err := wgctrl.New() + if err != nil { + logger.Error.Fatalf("Couldn't create wgctl-client: %s", err) + } + // Test getting wg-device + _, err = client.Device(iface) + if err != nil { + logger.Error.Fatalf("Couldn't get wg-interface '%s': %s", iface, err) + } + + + // Subscribe to route-change events + routeSubChan, routeSubDoneChan := make(chan netlink.RouteUpdate), make(chan struct{}) + + netlink.RouteSubscribe(routeSubChan, routeSubDoneChan) + go handleRouteEvents(routeSubChan, filterOptions, iface) + + //# Initial Route-setup + // Get routing-table entries from device + routeList, err := netlink.RouteList(link, netlink.FAMILY_ALL) + if err != nil { + logger.Error.Fatalf("Couldn't get route-entries: %s", err) + } + + logger.Info.Printf("Initially setting all current routes") + for _, route := range routeList { + // Ignore routes with empty gateway + if(route.Gw == nil){ + continue + } + + // Send current routes to handler + routeSubChan <- netlink.RouteUpdate{ + Type: unix.RTM_NEWROUTE, + Route: route, + } + } + + select {} +} + +var routeUpdateTypeMapFromId = map[uint16]string{ + unix.RTM_NEWROUTE: "+", + unix.RTM_DELROUTE: "-", +} +// TODO: Add proxy to apply filter in channels rather than.. this mess +func handleRouteEvents(routeSubChan <-chan netlink.RouteUpdate, filterOptions FilterOptions, iface string) { + // Create wg-client + client, err := wgctrl.New() + if err != nil { + logger.Error.Fatalf("Couldn't create wgctl-client: %s", err) + } + + for { + // Receive Route-Updates + routeUpdate := <-routeSubChan + route := routeUpdate.Route + + // Check filter + if(!CheckFilter(filterOptions, routeUpdate.Route)){ + continue + } + + // Special case for default-route + if route.Dst == nil{ + if route.Gw.To4() != nil { // IPv4 + route.Dst = &net.IPNet{ + IP: net.IPv4zero, + Mask: net.CIDRMask(0, 32), + } + } else { // IPv6 + route.Dst = &net.IPNet{ + IP: net.IPv6zero, + Mask: net.CIDRMask(0, 128), + } + } + } + + logger.Info.Printf("Route-Update: [%s] %s via %s", routeUpdateTypeMapFromId[routeUpdate.Type], route.Dst, route.Gw) + + // Get wgDevice + wgDevice, err := client.Device(iface) + if err != nil { + logger.Error.Fatalf("Couldn't get wg-interface '%s' while running: %s", iface, err) + } + + // Empty config for filling in switch + var wgConfig wgtypes.Config + + switch routeUpdate.Type{ + case unix.RTM_NEWROUTE: + // Check if gateway is set + if route.Gw == nil{ + logger.Warn.Printf("Gateway unset, ignoring") + continue + } + + // Check if other peer already has exact same dst + if peer, err := wgChecks.PeerByIPNet(wgDevice.Peers, *route.Dst); err == nil { + logger.Warn.Printf("dst-IPNet already set for Peer '%s', ignoring", peer.PublicKey) + continue + } + + // Get peer containing gateway-addr + peer, err := wgChecks.PeerByIP(wgDevice.Peers, route.Gw) + if(err != nil){ + logger.Warn.Printf("No peer found containing gw-IP '%s', ignoring", route.Gw) + continue + } + + // Set peerConfig, this will override set values for that peer + wgConfig.Peers = []wgtypes.PeerConfig{ + { + PublicKey: peer.PublicKey, + AllowedIPs: append(peer.AllowedIPs, *route.Dst), + }, + } + + case unix.RTM_DELROUTE: + // Get peer containing dst-NetIP + peerIndex, ipNetIndex, err := wgChecks.PeerIndexByIPNet(wgDevice.Peers, *route.Dst) + if(err != nil){ + logger.Warn.Printf("No peer found having dst-IPNet '%s', ignoring", route.Dst) + continue + } + peer := wgDevice.Peers[peerIndex] + + // Delete dstNet from allowedIPs + peer.AllowedIPs[ipNetIndex] = peer.AllowedIPs[len(peer.AllowedIPs)-1] + peer.AllowedIPs = peer.AllowedIPs[:len(peer.AllowedIPs)-1] + + // Set peerConfig, this will override set values for that peer + wgConfig.Peers = []wgtypes.PeerConfig{ + { + PublicKey: peer.PublicKey, + UpdateOnly: true, + + ReplaceAllowedIPs: true, + AllowedIPs: peer.AllowedIPs, + }, + } + } + + err = client.ConfigureDevice(iface, wgConfig) + if(err != nil){ + logger.Error.Fatalf("Error configuring wg-device '%s': %s", iface, err) + } + } +} diff --git a/go.mod b/go.mod index 284ac4a..fd10532 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,23 @@ -module routingtabletowg +module git.ruekov.eu/ruakij/routingtabletowg go 1.19 + +require ( + github.com/jsimonetti/rtnetlink v1.2.3 + github.com/vishvananda/netlink v1.1.0 + golang.org/x/sys v0.1.0 + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb +) + +require ( + github.com/google/go-cmp v0.5.9 // indirect + github.com/josharian/native v1.0.0 // indirect + github.com/mdlayher/genetlink v1.2.0 // indirect + github.com/mdlayher/netlink v1.6.2 // indirect + github.com/mdlayher/socket v0.2.3 // indirect + github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect + golang.org/x/crypto v0.1.0 // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..52f109c --- /dev/null +++ b/go.sum @@ -0,0 +1,70 @@ +github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= +github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jsimonetti/rtnetlink v1.2.3 h1:JntWIxmljlDswWwebzpZCz2Aa3t2kThJ79f658zgsPU= +github.com/jsimonetti/rtnetlink v1.2.3/go.mod h1:5r5Rj9WEseVOUzDk5RN9v0gVXbkBz9XtENwhC6PwvtU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU= +github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ= +github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= +github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= +github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= +github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= +github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= +github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c h1:Okh6a1xpnJslG9Mn84pId1Mn+Q8cvpo4HCeeFWHo0cA= +golang.zx2c4.com/wireguard v0.0.0-20220920152132-bb719d3a6e2c/go.mod h1:enML0deDxY1ux+B6ANGiwtg0yAJi1rctkTpcHNAVPyg= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/lib/environmentchecks/environmentchecks.go b/lib/environmentchecks/environmentchecks.go new file mode 100644 index 0000000..5626c5d --- /dev/null +++ b/lib/environmentchecks/environmentchecks.go @@ -0,0 +1,27 @@ +package environmentchecks + +import ( + "os" + "fmt" +) + +func HandleDefaults(envDefaults map[string]string){ + for env, defaultValue := range envDefaults{ + // Check if env is set + _, isSet := os.LookupEnv(env) + if(!isSet){ + os.Setenv(env, defaultValue) + } + } +} + +func HandleRequired(envRequired []string) error { + for _, env := range envRequired{ + // Check if env is set + _, isSet := os.LookupEnv(env) + if(!isSet){ + return fmt.Errorf("env '%s' required, but not set", env) + } + } + return nil +} diff --git a/lib/iproute2mapping/mapping.go b/lib/iproute2mapping/mapping.go new file mode 100644 index 0000000..5b2e6c0 --- /dev/null +++ b/lib/iproute2mapping/mapping.go @@ -0,0 +1,102 @@ +package iproute2mapping + +import ( + "os" + "bufio" + "strings" + "strconv" + "fmt" +) + +// Storage for mapping +var ByName = make(map[int]map[string]int) +var ById = make(map[int]map[int]string) + +// Mapping types +const ( + PROTOCOL = iota + TABLE +) + +// Paths +var filePaths = map[int]string{ + PROTOCOL: "/etc/iproute2/rt_protos", + TABLE: "/etc/iproute2/rt_tables", +} + +// Export error-check +var Errors []error; + +func init() { + var err error + for mapType, filePath := range filePaths{ + ByName[mapType], ById[mapType], err = readFromFile(filePath) + if(err != nil){ + Errors = []error{ + fmt.Errorf("failed reading iproute2 mapping-file '%s': %s", filePath, err), + }; + } + } +} + +func readFromFile(filePath string) (mapByName map[string]int, mapById map[int]string, err error){ + file, err := os.Open(filePath) + if(err != nil){ + return nil, nil, err; + } + defer file.Close() + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + + mapByName = make(map[string]int) + mapById = make(map[int]string) + + // Go through file line-by-line + for scanner.Scan() { + text := scanner.Text() + if(strings.HasPrefix(text, "#") || text == ""){ + continue + } + + id, name, err := getMappingFromText(text) + if(err != nil){ + // Only warn as we can continue processing the file + Errors = append(Errors, + fmt.Errorf("mappig-data invalid '%s': %s", text, err), + ) + continue + } + + mapByName[name] = id + mapById[id] = name + } + + return +} + +func getMappingFromText(text string) (int, string, error) { + // Split and read/convert data + data := strings.Split(text, "\t") + + id, err := strconv.Atoi(data[0]) + if(err != nil){ + return 0, "", err + } + + return id, data[1], nil +} + +// Try getting an id from a name (or a string containing an id) with a specified type +func TryGetId(mappingType int, name string) (int, error){ + // Try to convert name to id + id, err := strconv.Atoi(name) + if(err != nil){ // name given -> Convert to id + var found bool + id, found = ByName[mappingType][name] + if(!found){ + return 0, fmt.Errorf("no id found from name") + } + } + return id, nil +} diff --git a/lib/iproute2mapping/mapping_test.go b/lib/iproute2mapping/mapping_test.go new file mode 100644 index 0000000..21c2d7d --- /dev/null +++ b/lib/iproute2mapping/mapping_test.go @@ -0,0 +1,2 @@ +package iproute2mapping + diff --git a/lib/netchecks/netchecks.go b/lib/netchecks/netchecks.go new file mode 100644 index 0000000..6e747cd --- /dev/null +++ b/lib/netchecks/netchecks.go @@ -0,0 +1,25 @@ +package netchecks + +import ( + "fmt" + "net" + "reflect" +) + +func IPNetIndexByIP(list []net.IPNet, ip net.IP) (int, error) { + for index, ipNetEntry := range list { + if ipNetEntry.Contains(ip) { + return index, nil + } + } + return -1, fmt.Errorf("ip not in ipNet-list") +} + +func IPNetIndexByIPNet(list []net.IPNet, ipNet net.IPNet) (int, error) { + for index, ipNetEntry := range list { + if reflect.DeepEqual(ipNetEntry, ipNet) { + return index, nil + } + } + return -1, fmt.Errorf("ipNet not in ipNet-list") +} diff --git a/lib/wgchecks/wgchecks.go b/lib/wgchecks/wgchecks.go new file mode 100644 index 0000000..5a7ea41 --- /dev/null +++ b/lib/wgchecks/wgchecks.go @@ -0,0 +1,42 @@ +package wgChecks + +import ( + "fmt" + "net" + + "git.ruekov.eu/ruakij/routingtabletowg/lib/netchecks" + + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +func PeerIndexByIP(peers []wgtypes.Peer, ip net.IP) (int, int, error) { + for index, peer := range peers { + if ipIndex, err := netchecks.IPNetIndexByIP(peer.AllowedIPs, ip); err == nil { + return index, ipIndex, nil + } + } + return -1, -1, fmt.Errorf("no peer by ip in list") +} +func PeerByIP(peers []wgtypes.Peer, ip net.IP) (*wgtypes.Peer, error) { + index, _, err := PeerIndexByIP(peers, ip) + if(err != nil) { + return nil, err + } + return &peers[index], nil +} + +func PeerIndexByIPNet(peers []wgtypes.Peer, ipNet net.IPNet) (int, int, error) { + for index, peer := range peers { + if ipNetIndex, err := netchecks.IPNetIndexByIPNet(peer.AllowedIPs, ipNet); err == nil { + return index, ipNetIndex, nil + } + } + return -1, -1, fmt.Errorf("no peer by ipNet in list") +} +func PeerByIPNet(peers []wgtypes.Peer, ipNet net.IPNet) (*wgtypes.Peer, error) { + index, _, err := PeerIndexByIPNet(peers, ipNet) + if(err != nil) { + return nil, err + } + return &peers[index], nil +}