Skip to content
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

mdns provisioning #54

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.23.1
require (
github.com/Masterminds/semver/v3 v3.3.0
github.com/Otterverse/gonetworkmanager/v2 v2.2.1
github.com/edaniels/zeroconf v1.0.10
github.com/google/uuid v1.6.0
github.com/jessevdk/go-flags v1.6.1
github.com/nightlyone/lockfile v1.0.0
Expand All @@ -29,7 +30,6 @@ require (
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgottlieb/smarty-assertions v1.2.5 // indirect
github.com/edaniels/golog v0.0.0-20230215213219-28954395e8d0 // indirect
github.com/edaniels/zeroconf v1.0.10 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
Expand Down
4 changes: 4 additions & 0 deletions subsystems/provisioning/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@ type Config struct {

// If set, will explicitly enable or disable power save for all wifi connections managed by NetworkManager.
WifiPowerSave *bool `json:"wifi_power_save"`

// When true, advertise via mdns on pre-existing network instead of using static IP and captive portal.
// (Instead of the default behavior of starting a captive portal).
MDNSMode bool `json:"mdns_mode"`
}

// Timeout allows parsing golang-style durations (1h20m30s) OR seconds-as-float from/to json.
Expand Down
5 changes: 4 additions & 1 deletion subsystems/provisioning/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@ import (
"errors"
"fmt"
"net"
"strconv"
"time"

errw "github.com/pkg/errors"
pb "go.viam.com/api/provisioning/v1"
"google.golang.org/grpc"
)

const GRPCPort = 4772

func (w *Provisioning) startGRPC() error {
bind := PortalBindAddr + ":4772"
bind := w.listenAddr() + ":" + strconv.Itoa(GRPCPort)
lis, err := net.Listen("tcp", bind)
if err != nil {
return errw.Wrapf(err, "listening on: %s", bind)
Expand Down
46 changes: 36 additions & 10 deletions subsystems/provisioning/networkmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import (
"time"

gnm "github.com/Otterverse/gonetworkmanager/v2"
"github.com/edaniels/zeroconf"
errw "github.com/pkg/errors"
)

const provisioningMDNSHost = "provisioning-poc"

func (w *Provisioning) warnIfMultiplePrimaryNetworks() {
if w.cfg.RoamingMode {
return
Expand Down Expand Up @@ -62,6 +65,9 @@ func (w *Provisioning) getLastNetworkTried() NetworkInfo {
}

func (w *Provisioning) checkOnline(force bool) error {
if w.cfg.MDNSMode {
return nil
}
if force {
if err := w.nm.CheckConnectivity(); err != nil {
w.logger.Error(err)
Expand Down Expand Up @@ -166,16 +172,18 @@ func (w *Provisioning) StartProvisioning(ctx context.Context, inputChan chan<- u
defer w.opMu.Unlock()

w.logger.Info("Starting provisioning mode.")
_, err := w.addOrUpdateConnection(NetworkConfig{
Type: NetworkTypeHotspot,
Interface: w.Config().HotspotInterface,
SSID: w.Config().hotspotSSID,
})
if err != nil {
return err
}
if err := w.activateConnection(ctx, w.Config().HotspotInterface, w.Config().hotspotSSID); err != nil {
return errw.Wrap(err, "starting provisioning mode hotspot")
if !w.cfg.MDNSMode {
_, err := w.addOrUpdateConnection(NetworkConfig{
Type: NetworkTypeHotspot,
Interface: w.Config().HotspotInterface,
SSID: w.Config().hotspotSSID,
})
if err != nil {
return err
}
if err := w.activateConnection(ctx, w.Config().HotspotInterface, w.Config().hotspotSSID); err != nil {
return errw.Wrap(err, "starting provisioning mode hotspot")
}
}

// start portal with ssid list and known connections
Expand All @@ -184,6 +192,16 @@ func (w *Provisioning) StartProvisioning(ctx context.Context, inputChan chan<- u
return errw.Wrap(err, "starting web/grpc portal")
}

if w.cfg.MDNSMode {
w.logger.Infof("mdnsMode is set, advertising provisioning host %s", provisioningMDNSHost)
var err error
w.mdnsServer, err = zeroconf.RegisterDynamic(
provisioningMDNSHost, "_rpc._tcp", "local.", GRPCPort, []string{"grpc"}, nil, w.logger.AsZap())
if err != nil {
return err
}
}

w.connState.setProvisioning(true)
return nil
}
Expand Down Expand Up @@ -286,6 +304,9 @@ func (w *Provisioning) DeactivateConnection(ifName, ssid string) error {
}

func (w *Provisioning) deactivateConnection(ifName, ssid string) error {
if w.cfg.MDNSMode {
return nil
}
activeConn := w.netState.ActiveConn(ifName)
if activeConn == nil {
return errw.Wrapf(ErrNoActiveConnectionFound, "interface: %s", ifName)
Expand Down Expand Up @@ -550,6 +571,11 @@ func (w *Provisioning) backgroundLoop(ctx context.Context, scanChan chan<- bool)
return
}

if w.cfg.MDNSMode {
w.logger.Info("skipping background loop because mdnsmode")
return
}

w.checkConfigured()
if err := w.networkScan(ctx); err != nil {
w.logger.Error(err)
Expand Down
15 changes: 14 additions & 1 deletion subsystems/provisioning/portal.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ func (w *Provisioning) startPortal(inputChan chan<- userInput) error {
return nil
}

// the address to listen on.
func (w *Provisioning) listenAddr() string {
if w.cfg.MDNSMode {
return "0.0.0.0"
}
return PortalBindAddr
}

func (w *Provisioning) startWeb() error {
mux := http.NewServeMux()
mux.HandleFunc("/", w.portalIndex)
Expand All @@ -53,7 +61,7 @@ func (w *Provisioning) startWeb() error {
Handler: mux,
ReadTimeout: time.Second * 10,
}
bind := PortalBindAddr + ":80"
bind := w.listenAddr() + ":80"
lis, err := net.Listen("tcp", bind)
if err != nil {
return errw.Wrapf(err, "listening on: %s", bind)
Expand Down Expand Up @@ -89,6 +97,11 @@ func (w *Provisioning) stopPortal() error {
w.portalData.workers.Wait()
w.portalData = &portalData{input: &userInput{}}

if w.mdnsServer != nil {
w.mdnsServer.Shutdown()
w.mdnsServer = nil
}

return err
}

Expand Down
9 changes: 9 additions & 0 deletions subsystems/provisioning/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

semver "github.com/Masterminds/semver/v3"
gnm "github.com/Otterverse/gonetworkmanager/v2"
"github.com/edaniels/zeroconf"
errw "github.com/pkg/errors"
"github.com/viamrobotics/agent"
"github.com/viamrobotics/agent/subsystems"
Expand Down Expand Up @@ -60,6 +61,7 @@ type Provisioning struct {
// portal
webServer *http.Server
grpcServer *grpc.Server
mdnsServer *zeroconf.Server
portalData *portalData

pb.UnimplementedProvisioningServiceServer
Expand Down Expand Up @@ -143,6 +145,10 @@ func (w *Provisioning) init(ctx context.Context) error {
w.mainLoopHealth.MarkGood()
w.bgLoopHealth.MarkGood()

if w.cfg.MDNSMode {
w.logger.Info("skipping networkmanager init because mdnsmode")
return nil
}
nm, err := w.getNM()
if err != nil {
return err
Expand Down Expand Up @@ -384,6 +390,9 @@ func (w *Provisioning) updateHotspotSSID(cfg *Config) {

// must be run inside dataMu lock.
func (w *Provisioning) writeWifiPowerSave(ctx context.Context) error {
if w.cfg.MDNSMode {
return nil
}
contents := wifiPowerSaveContentsDefault
if w.cfg.WifiPowerSave != nil {
if *w.cfg.WifiPowerSave {
Expand Down