Skip to content

Commit

Permalink
feat(hotspot): add access point on timer using dhcp, dnsmasg, hostapd
Browse files Browse the repository at this point in the history
  • Loading branch information
Zainrax committed Feb 14, 2023
1 parent 50775b5 commit 66088d5
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 3 deletions.
16 changes: 13 additions & 3 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,16 @@ func (api *ManagementAPI) ClearConfigSection(w http.ResponseWriter, r *http.Requ
}
}

func (api *ManagementAPI) GetLocation(w http.ResponseWriter, r *http.Request) {
location := goconfig.Location{}
jsonString, err := json.Marshal(location)
if err != nil {
serverError(&w, err)
return
}
w.Write(jsonString)
}

// SetLocation is for specifically writing to location setting.
func (api *ManagementAPI) SetLocation(w http.ResponseWriter, r *http.Request) {
log.Println("update location")
Expand Down Expand Up @@ -496,7 +506,7 @@ func (api *ManagementAPI) StartSaltUpdate(w http.ResponseWriter, r *http.Request
}
}

//GetSaltUpdateState will get the salt update status
// GetSaltUpdateState will get the salt update status
func (api *ManagementAPI) GetSaltUpdateState(w http.ResponseWriter, r *http.Request) {
state, err := saltrequester.State()
if err != nil {
Expand All @@ -507,7 +517,7 @@ func (api *ManagementAPI) GetSaltUpdateState(w http.ResponseWriter, r *http.Requ
json.NewEncoder(w).Encode(state)
}

//GetSaltAutoUpdate will check if salt auto update is enabled
// GetSaltAutoUpdate will check if salt auto update is enabled
func (api *ManagementAPI) GetSaltAutoUpdate(w http.ResponseWriter, r *http.Request) {
autoUpdate, err := saltrequester.IsAutoUpdateOn()
if err != nil {
Expand All @@ -518,7 +528,7 @@ func (api *ManagementAPI) GetSaltAutoUpdate(w http.ResponseWriter, r *http.Reque
json.NewEncoder(w).Encode(map[string]interface{}{"autoUpdate": autoUpdate})
}

//PostSaltAutoUpdate will set if auto update is enabled or not
// PostSaltAutoUpdate will set if auto update is enabled or not
func (api *ManagementAPI) PostSaltAutoUpdate(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
parseFormErrorResponse(&w, err)
Expand Down
12 changes: 12 additions & 0 deletions cmd/managementd/hostapd.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Note this file has no ssid as it is set by the script
country_code=GB
interface=wlan0
hw_mode=g
channel=7
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
177 changes: 177 additions & 0 deletions cmd/managementd/hotspot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package main

import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
)

// refactor createAPConfig to remove duplication
func createAPConfig(name string) error {
file_name := "/etc/hostapd/hostapd.conf"
config_lines := []string{
"country_code=NZ",
"interface=wlan0",
"ssid=" + name,
"hw_mode=g",
"channel=7",
"macaddr_acl=0",
"ignore_broadcast_ssid=0",
"wpa=2",
"wpa_passphrase=feathers",
"wpa_key_mgmt=WPA-PSK",
"wpa_pairwise=TKIP",
"rsn_pairwise=CCMP",
}
return creatConfigFile(file_name, config_lines)
}

func startAccessPoint(name string) error {
return exec.Command("systemctl", "restart", "hostapd").Start()
}

func stopAccessPoint() error {
return exec.Command("systemctl", "stop", "hostapd").Start()
}

const router_ip = "192.168.4.1"

var dhcp_config_default = []string{
"hostname",
"clientid",
"persistent",
"option rapid_commit",
"option domain_name_servers, domain_name, domain_search, host_name",
"option classless_static_routes",
"option ntp_servers",
"option interface_mtu",
"require dhcp_server_identifier",
"slaac private",
}

var dhcp_config_lines = []string{
"interface wlan0",
"static ip_address=" + router_ip + "/24",
"nohook wpa_supplicant",
}

func createDHCPConfig() error {
file_path := "/etc/dhcpcd.conf"

// append to dhcpcd.conf if lines don't already exist
config_lines := append(dhcp_config_default, dhcp_config_lines...)
if err := writeLines(file_path, config_lines); err != nil {
return err
}
return nil
}

func removeLines(file_path string, removed_lines []string) error {
file, err := os.Open(file_path)
if err != nil {
return err
}
defer file.Close()

var lines []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if !contains(removed_lines, line) {
lines = append(lines, line)
}
}

return writeLines(file_path, lines)
}

func writeLines(file_path string, lines []string) error {
file, err := os.Create(file_path)
if err != nil {
return err
}
defer file.Close()

w := bufio.NewWriter(file)
for _, line := range lines {
_, _ = fmt.Fprintln(w, line)
}
return w.Flush()
}

func contains(lines []string, line string) bool {
for _, l := range lines {
if strings.TrimSpace(l) == strings.TrimSpace(line) {
return true
}
}
return false
}

func startDHCP() error {
// modify /etc/dhcpcd.conf
if err := createDHCPConfig(); err != nil {
return err
}

return exec.Command("systemctl", "restart", "dhcpcd").Run()
}

func restartDHCP() error {
if err := writeLines("/etc/dhcpcd.conf", dhcp_config_default); err != nil {
return err
}
return exec.Command("systemctl", "restart", "dhcpcd").Run()
}

func checkIsConnectedToNetwork() error {
// check if network is up
if err := exec.Command("iwgetid", "wlan0", "-r").Run(); err != nil {
return err
}
return nil
}

func createDNSConfig(router_ip string, ip_range string) error {
// DNSMASQ config
file_name := "/etc/dnsmasq.conf"
config_lines := []string{
"interface=wlan0",
"dhcp-range=" + ip_range + ",12h",
"domain=wlan",
"address=/#/" + router_ip,
}
return creatConfigFile(file_name, config_lines)

}

func startDNS() error {
return exec.Command("systemctl", "restart", "dnsmasq").Start()
}

func stopDNS() error {
return exec.Command("systemctl", "stop", "dnsmasq").Start()
}

func creatConfigFile(name string, config []string) error {
file, err := os.Create(name)
if err != nil {
return err
}
defer file.Close()

w := bufio.NewWriter(file)
for _, line := range config {
_, err = fmt.Fprintln(w, line)
if err != nil {
return err
}
}
err = w.Flush()
if err != nil {
return err
}
return nil
}
64 changes: 64 additions & 0 deletions cmd/managementd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func main() {
apiRouter.HandleFunc("/config", apiObj.SetConfig).Methods("POST")
apiRouter.HandleFunc("/clear-config-section", apiObj.ClearConfigSection).Methods("POST")
apiRouter.HandleFunc("/location", apiObj.SetLocation).Methods("POST") // Set location via a POST request.
apiRouter.HandleFunc("/location", apiObj.GetLocation).Methods("GET") // Set location via a POST request.
apiRouter.HandleFunc("/clock", apiObj.GetClock).Methods("GET")
apiRouter.HandleFunc("/clock", apiObj.PostClock).Methods("POST")
apiRouter.HandleFunc("/version", apiObj.GetVersion).Methods("GET")
Expand All @@ -133,9 +134,72 @@ func main() {
apiRouter.HandleFunc("/service-restart", apiObj.RestartService).Methods("POST")
apiRouter.Use(basicAuth)

//Setup Hotspot and stop after 5 minutes using a new goroutine
go func() {
ssid := "bushnet"
router_ip := "192.168.4.1"
log.Printf("Setting DHCP to default...")
if err := restartDHCP(); err != nil {
log.Printf("Error restarting dhcpcd: %s", err)
}
// Wait 1 minute before starting hotspot
time.Sleep(1 * time.Minute)
// Check if already connected to a network
// If not connected to a network, start hotspot
if err := checkIsConnectedToNetwork(); err != nil {
log.Printf("Error checking network: %s", err)
log.Printf("Starting Hotspot...")
log.Printf("Creating Configs...")
if err := createAPConfig(ssid); err != nil {
log.Printf("Error creating hostapd config: %s", err)
}
if err := createDNSConfig(router_ip, "192.168.4.2,192.168.4.20"); err != nil {
log.Printf("Error creating dnsmasq config: %s", err)
}

log.Printf("Starting DHCP...")
if err := startDHCP(); err != nil {
log.Printf("Error starting dhcpcd: %s", err)
}
log.Printf("Starting DNS...")
if err := startDNS(); err != nil {
log.Printf("Error starting dnsmasq: %s", err)
}
log.Printf("Starting Access Point...")
if err := startAccessPoint(ssid); err != nil {
log.Printf("Error starting hostapd: %s", err)
}
// Wait 5 minutes before stopping hotspot extending timer
// if apiRouter is used
t := time.NewTimer(1 * time.Minute)
apiRouter.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Reset(5 * time.Minute)
next.ServeHTTP(w, r)
})
})

<-t.C

log.Printf("Stopping Hotspot")
if err := stopAccessPoint(); err != nil {
log.Printf("Error stopping hotspot: %s", err)
}
if err := stopDNS(); err != nil {
log.Printf("Error stopping dnsmasq: %s", err)
}
if err := restartDHCP(); err != nil {
log.Printf("Error restarting dhcp: %s", err)
}
} else {
log.Printf("Already connected to a network")
}
}()

listenAddr := fmt.Sprintf(":%d", config.Port)
log.Printf("listening on %s", listenAddr)
log.Fatal(http.ListenAndServe(listenAddr, router))

}

func basicAuth(next http.Handler) http.Handler {
Expand Down

0 comments on commit 66088d5

Please sign in to comment.