Skip to content

tunnel+conf+ui: periodic update of endpoint ip #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions conf/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,12 @@ type Peer struct {
AllowedIPs []netip.Prefix
Endpoint Endpoint
PersistentKeepalive uint16
UpdateEndpointIP uint16

RxBytes Bytes
TxBytes Bytes
LastHandshakeTime HandshakeTime
UnresolvedHost string
}

func (conf *Config) IntersectsWith(other *Config) bool {
Expand Down
7 changes: 5 additions & 2 deletions conf/dnsresolver_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"golang.zx2c4.com/wireguard/windows/services"
)

func resolveHostname(name string) (resolvedIPString string, err error) {
func ResolveHostname(name string) (resolvedIPString string, err error) {
maxTries := 10
if services.StartedAtBoot() {
maxTries *= 3
Expand Down Expand Up @@ -87,8 +87,11 @@ func (config *Config) ResolveEndpoints() error {
if config.Peers[i].Endpoint.IsEmpty() {
continue
}
if config.Peers[i].UpdateEndpointIP > 0 {
config.Peers[i].UnresolvedHost = config.Peers[i].Endpoint.Host
}
var err error
config.Peers[i].Endpoint.Host, err = resolveHostname(config.Peers[i].Endpoint.Host)
config.Peers[i].Endpoint.Host, err = ResolveHostname(config.Peers[i].Endpoint.Host)
if err != nil {
return err
}
Expand Down
26 changes: 26 additions & 0 deletions conf/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ func parsePersistentKeepalive(s string) (uint16, error) {
return uint16(m), nil
}

func parseUpdateEndpointIP(s string) (uint16, error) {
if s == "off" {
return 0, nil
}
m, err := strconv.Atoi(s)
if err != nil {
return 0, err
}
if m < 0 || m > 65535 {
return 0, &ParseError{l18n.Sprintf("Invalid update endpoint IP"), s}
}
return uint16(m), nil
}

func parseTableOff(s string) (bool, error) {
if s == "off" {
return true, nil
Expand Down Expand Up @@ -290,6 +304,12 @@ func FromWgQuick(s, name string) (*Config, error) {
return nil, err
}
peer.PersistentKeepalive = p
case "updateendpointip":
p, err := parseUpdateEndpointIP(val)
if err != nil {
return nil, err
}
peer.UpdateEndpointIP = p
case "endpoint":
e, err := parseEndpoint(val)
if err != nil {
Expand Down Expand Up @@ -374,6 +394,12 @@ func FromDriverConfiguration(interfaze *driver.Interface, existingConfig *Config
if p.Flags&driver.PeerHasPersistentKeepalive != 0 {
peer.PersistentKeepalive = p.PersistentKeepalive
}
for i := range existingConfig.Peers {
if existingConfig.Peers[i].PublicKey == peer.PublicKey && existingConfig.Peers[i].UpdateEndpointIP > 0 {
// Get UpdateEndpointIP option from config as it is cannot be retrieved from the driver
peer.UpdateEndpointIP = existingConfig.Peers[i].UpdateEndpointIP
}
}
peer.TxBytes = Bytes(p.TxBytes)
peer.RxBytes = Bytes(p.RxBytes)
if p.LastHandshake != 0 {
Expand Down
25 changes: 25 additions & 0 deletions conf/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func (conf *Config) ToWgQuick() string {
if peer.PersistentKeepalive > 0 {
output.WriteString(fmt.Sprintf("PersistentKeepalive = %d\n", peer.PersistentKeepalive))
}

if peer.UpdateEndpointIP > 0 {
output.WriteString(fmt.Sprintf("UpdateEndpointIP = %d\n", peer.UpdateEndpointIP))
}
}
return output.String()
}
Expand Down Expand Up @@ -138,3 +142,24 @@ func (config *Config) ToDriverConfiguration() (*driver.Interface, uint32) {
}
return c.Interface()
}

func PeerUpdateEndpointConfiguration(peer *Peer, newIP string) (*driver.Interface, uint32) {
preallocation := unsafe.Sizeof(driver.Interface{}) + uintptr(1)*unsafe.Sizeof(driver.Peer{})
var c driver.ConfigBuilder
c.Preallocate(uint32(preallocation))
c.AppendInterface(&driver.Interface{
PeerCount: uint32(1),
})

var endpoint winipcfg.RawSockaddrInet
addr, err := netip.ParseAddr(newIP)
if err == nil {
endpoint.SetAddrPort(netip.AddrPortFrom(addr, peer.Endpoint.Port))
}
c.AppendPeer(&driver.Peer{
Flags: driver.PeerHasEndpoint | driver.PeerHasPublicKey,
PublicKey: peer.PublicKey,
Endpoint: endpoint,
})
return c.Interface()
}
33 changes: 33 additions & 0 deletions tunnel/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
return
}

startUpdateEndpointIP(adapter, config)

changes <- svc.Status{State: serviceState, Accepts: svc.AcceptStop | svc.AcceptShutdown}

var started bool
Expand Down Expand Up @@ -247,6 +249,37 @@ func (service *tunnelService) Execute(args []string, r <-chan svc.ChangeRequest,
}
}

func startUpdateEndpointIP(adapter *driver.Adapter, config *conf.Config) {
for i := range config.Peers {
if !config.Peers[i].Endpoint.IsEmpty() && config.Peers[i].UpdateEndpointIP > 0 {
go resolveDNSLoop(adapter, &config.Peers[i], i+1)
}
}
}

func resolveDNSLoop(adapter *driver.Adapter, peer *conf.Peer, peerNumber int) {
for {
time.Sleep(time.Second * time.Duration(peer.UpdateEndpointIP))

log.Printf("Checking DNS of endpoint for peer %d", peerNumber)
resolvedIPString, err := conf.ResolveHostname(peer.UnresolvedHost)
if err != nil {
log.Printf("Error resolving hostname of peer %d", peerNumber)
continue
}

if resolvedIPString != peer.Endpoint.Host {
peer.Endpoint.Host = resolvedIPString
err = adapter.SetConfiguration(conf.PeerUpdateEndpointConfiguration(peer, resolvedIPString))
if err == nil {
log.Printf("IP for peer %d updated (%v)", peerNumber, resolvedIPString)
} else {
log.Printf("Error updating IP for peer %d: %v", peerNumber, err)
}
}
}
}

func Run(confPath string) error {
name, err := conf.NameFromPath(confPath)
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions ui/confview.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type peerView struct {
allowedIPs *labelTextLine
endpoint *labelTextLine
persistentKeepalive *labelTextLine
updateEndpointIP *labelTextLine
latestHandshake *labelTextLine
transfer *labelTextLine
lines []widgetsLine
Expand Down Expand Up @@ -337,6 +338,7 @@ func newPeerView(parent walk.Container) (*peerView, error) {
{l18n.Sprintf("Allowed IPs:"), &pv.allowedIPs},
{l18n.Sprintf("Endpoint:"), &pv.endpoint},
{l18n.Sprintf("Persistent keepalive:"), &pv.persistentKeepalive},
{l18n.Sprintf("Update endpoint IP:"), &pv.updateEndpointIP},
{l18n.Sprintf("Latest handshake:"), &pv.latestHandshake},
{l18n.Sprintf("Transfer:"), &pv.transfer},
}
Expand Down Expand Up @@ -476,6 +478,12 @@ func (pv *peerView) apply(c *conf.Peer) {
pv.persistentKeepalive.hide()
}

if c.UpdateEndpointIP > 0 {
pv.updateEndpointIP.show(strconv.Itoa(int(c.UpdateEndpointIP)))
} else {
pv.updateEndpointIP.hide()
}

if !c.LastHandshakeTime.IsEmpty() {
pv.latestHandshake.show(c.LastHandshakeTime.String())
} else {
Expand Down
13 changes: 13 additions & 0 deletions ui/syntax/highlighter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
highlightPort
highlightMTU
highlightKeepalive
highlightUpdateEndpointIP
highlightComment
highlightDelimiter
highlightTable
Expand Down Expand Up @@ -268,6 +269,13 @@ func (s stringSpan) isValidPersistentKeepAlive() bool {
return s.isValidUint(false, 0, 65535)
}

func (s stringSpan) isValidUpdateEndpointIP() bool {
if s.isSame("off") {
return true
}
return s.isValidUint(false, 0, 65535)
}

// It's probably not worthwhile to try to validate a bash expression. So instead we just demand non-zero length.
func (s stringSpan) isValidPrePostUpDown() bool {
return s.len != 0
Expand Down Expand Up @@ -376,6 +384,7 @@ const (
fieldAllowedIPs
fieldEndpoint
fieldPersistentKeepalive
fieldUpdateEndpointIP
fieldInvalid
)

Expand Down Expand Up @@ -413,6 +422,8 @@ func (s stringSpan) field() field {
return fieldEndpoint
case s.isCaselessSame("PersistentKeepalive"):
return fieldPersistentKeepalive
case s.isCaselessSame("UpdateEndpointIP"):
return fieldUpdateEndpointIP
case s.isCaselessSame("PreUp"):
return fieldPreUp
case s.isCaselessSame("PostUp"):
Expand Down Expand Up @@ -524,6 +535,8 @@ func (hsa *highlightSpanArray) highlightValue(parent, s stringSpan, section fiel
hsa.append(parent.s, s, validateHighlight(s.isValidPort(), highlightPort))
case fieldPersistentKeepalive:
hsa.append(parent.s, s, validateHighlight(s.isValidPersistentKeepAlive(), highlightKeepalive))
case fieldUpdateEndpointIP:
hsa.append(parent.s, s, validateHighlight(s.isValidUpdateEndpointIP(), highlightUpdateEndpointIP))
case fieldEndpoint:
if !s.isValidEndpoint() {
hsa.append(parent.s, s, highlightError)
Expand Down
33 changes: 17 additions & 16 deletions ui/syntax/syntaxedit.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,23 @@ type spanStyle struct {
}

var stylemap = map[highlight]spanStyle{
highlightSection: {color: win.RGB(0x32, 0x6D, 0x74), effects: win.CFE_BOLD},
highlightField: {color: win.RGB(0x9B, 0x23, 0x93), effects: win.CFE_BOLD},
highlightPrivateKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightPublicKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightPresharedKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightIP: {color: win.RGB(0x0E, 0x0E, 0xFF)},
highlightCidr: {color: win.RGB(0x81, 0x5F, 0x03)},
highlightHost: {color: win.RGB(0x0E, 0x0E, 0xFF)},
highlightPort: {color: win.RGB(0x81, 0x5F, 0x03)},
highlightMTU: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightTable: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightKeepalive: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightComment: {color: win.RGB(0x53, 0x65, 0x79), effects: win.CFE_ITALIC},
highlightDelimiter: {color: win.RGB(0x00, 0x00, 0x00)},
highlightCmd: {color: win.RGB(0x63, 0x75, 0x89)},
highlightError: {color: win.RGB(0xC4, 0x1A, 0x16), effects: win.CFE_UNDERLINE},
highlightSection: {color: win.RGB(0x32, 0x6D, 0x74), effects: win.CFE_BOLD},
highlightField: {color: win.RGB(0x9B, 0x23, 0x93), effects: win.CFE_BOLD},
highlightPrivateKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightPublicKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightPresharedKey: {color: win.RGB(0x64, 0x38, 0x20)},
highlightIP: {color: win.RGB(0x0E, 0x0E, 0xFF)},
highlightCidr: {color: win.RGB(0x81, 0x5F, 0x03)},
highlightHost: {color: win.RGB(0x0E, 0x0E, 0xFF)},
highlightPort: {color: win.RGB(0x81, 0x5F, 0x03)},
highlightMTU: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightTable: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightKeepalive: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightUpdateEndpointIP: {color: win.RGB(0x1C, 0x00, 0xCF)},
highlightComment: {color: win.RGB(0x53, 0x65, 0x79), effects: win.CFE_ITALIC},
highlightDelimiter: {color: win.RGB(0x00, 0x00, 0x00)},
highlightCmd: {color: win.RGB(0x63, 0x75, 0x89)},
highlightError: {color: win.RGB(0xC4, 0x1A, 0x16), effects: win.CFE_UNDERLINE},
}

func (se *SyntaxEdit) evaluateUntunneledBlocking(cfg string, spans []highlightSpan) {
Expand Down