Rewrite access point configuration logic to use uci instead of uploading the whole wireless file each time.

This commit is contained in:
Patrick Fairbank
2018-08-19 20:02:03 -07:00
parent 7cb35baeda
commit 55b8f63f8e
5 changed files with 60 additions and 137 deletions

Binary file not shown.

View File

@@ -6,15 +6,13 @@
package field
import (
"bytes"
"fmt"
"github.com/Team254/cheesy-arena/model"
"golang.org/x/crypto/ssh"
"log"
"os"
"path/filepath"
"strings"
"sync"
"text/template"
"time"
)
@@ -23,15 +21,6 @@ const accessPointConnectTimeoutSec = 1
const accessPointCommandTimeoutSec = 3
const accessPointRetryCount = 2
const (
red1Vlan = 10
red2Vlan = 20
red3Vlan = 30
blue1Vlan = 40
blue2Vlan = 50
blue3Vlan = 60
)
type AccessPoint struct {
address string
port int
@@ -58,16 +47,22 @@ func (ap *AccessPoint) ConfigureTeamWifi(red1, red2, red3, blue1, blue2, blue3 *
if err != nil {
return err
}
command := fmt.Sprintf("cat <<ENDCONFIG > /etc/config/wireless && wifi radio0\n%sENDCONFIG\n", config)
command := fmt.Sprintf("uci batch <<ENDCONFIG && wifi radio0\n%s\nENDCONFIG\n", config)
return ap.runCommand(command)
}
func (ap *AccessPoint) ConfigureAdminWifi() error {
config, err := ap.generateAccessPointConfig(nil, nil, nil, nil, nil, nil)
if err != nil {
return err
// Make sure multiple configurations aren't being set at the same time.
ap.mutex.Lock()
defer ap.mutex.Unlock()
commands := []string{
fmt.Sprintf("set wireless.radio0.channel='%d'", ap.teamChannel),
fmt.Sprintf("set wireless.radio1.channel='%d'", ap.adminChannel),
fmt.Sprintf("set wireless.@wifi-iface[0].key='%s'", ap.adminWpaKey),
"commit wireless",
}
command := fmt.Sprintf("cat <<ENDCONFIG > /etc/config/wireless && wifi radio1\n%sENDCONFIG\n", config)
command := fmt.Sprintf("uci batch <<ENDCONFIG && wifi\n%s\nENDCONFIG\n", strings.Join(commands, "\n"))
return ap.runCommand(command)
}
@@ -122,55 +117,43 @@ func (ap *AccessPoint) runCommand(command string) error {
func (ap *AccessPoint) generateAccessPointConfig(red1, red2, red3, blue1, blue2, blue3 *model.Team) (string, error) {
// Determine what new SSIDs are needed.
networks := make(map[int]*model.Team)
commands := &[]string{}
var err error
if err = addTeamNetwork(networks, red1, red1Vlan); err != nil {
if err = addTeamConfigCommands(1, red1, commands); err != nil {
return "", err
}
if err = addTeamNetwork(networks, red2, red2Vlan); err != nil {
if err = addTeamConfigCommands(2, red2, commands); err != nil {
return "", err
}
if err = addTeamNetwork(networks, red3, red3Vlan); err != nil {
if err = addTeamConfigCommands(3, red3, commands); err != nil {
return "", err
}
if err = addTeamNetwork(networks, blue1, blue1Vlan); err != nil {
if err = addTeamConfigCommands(4, blue1, commands); err != nil {
return "", err
}
if err = addTeamNetwork(networks, blue2, blue2Vlan); err != nil {
if err = addTeamConfigCommands(5, blue2, commands); err != nil {
return "", err
}
if err = addTeamNetwork(networks, blue3, blue3Vlan); err != nil {
if err = addTeamConfigCommands(6, blue3, commands); err != nil {
return "", err
}
// Generate the config file to be uploaded to the AP.
template, err := template.ParseFiles(filepath.Join(model.BaseDir, "templates/access_point.cfg"))
if err != nil {
return "", err
}
data := struct {
Networks map[int]*model.Team
TeamChannel int
AdminChannel int
AdminWpaKey string
}{networks, ap.teamChannel, ap.adminChannel, ap.adminWpaKey}
var configFile bytes.Buffer
err = template.Execute(&configFile, data)
if err != nil {
return "", err
}
*commands = append(*commands, "commit wireless")
return configFile.String(), nil
return strings.Join(*commands, "\n"), nil
}
// Verifies the validity of the given team's WPA key and adds a network for it to the list to be configured.
func addTeamNetwork(networks map[int]*model.Team, team *model.Team, vlan int) error {
func addTeamConfigCommands(position int, team *model.Team, commands *[]string) error {
if team == nil {
return nil
}
if len(team.WpaKey) < 8 || len(team.WpaKey) > 63 {
return fmt.Errorf("Invalid WPA key '%s' configured for team %d.", team.WpaKey, team.Id)
}
networks[vlan] = team
*commands = append(*commands, fmt.Sprintf("set wireless.@wifi-iface[%d].ssid='%d'", position, team.Id),
fmt.Sprintf("set wireless.@wifi-iface[%d].key='%s'", position, team.WpaKey))
return nil
}

View File

@@ -7,80 +7,57 @@ import (
"github.com/Team254/cheesy-arena/model"
"github.com/stretchr/testify/assert"
"regexp"
"strconv"
"testing"
)
func TestConfigureAccessPoint(t *testing.T) {
model.BaseDir = ".."
radioRe := regexp.MustCompile("option device 'radio0'")
ssidRe := regexp.MustCompile("option ssid '([-\\w ]+)'")
wpaKeyRe := regexp.MustCompile("option key '([-\\w ]+)'")
vlanRe := regexp.MustCompile("option network 'vlan(\\d+)'")
channelRe := regexp.MustCompile("option channel '(\\d+)'")
ssidRe := regexp.MustCompile("ssid='([-\\w ]+)'")
wpaKeyRe := regexp.MustCompile("key='([-\\w ]+)'")
ap := AccessPoint{teamChannel: 1234, adminChannel: 4321, adminWpaKey: "blorpy"}
// Should not configure any team SSIDs if there are no teams.
config, _ := ap.generateAccessPointConfig(nil, nil, nil, nil, nil, nil)
assert.NotContains(t, config, "option device 'radio0'")
assert.NotContains(t, config, "set")
ssids := ssidRe.FindAllStringSubmatch(config, -1)
wpaKeys := wpaKeyRe.FindAllStringSubmatch(config, -1)
vlans := vlanRe.FindAllStringSubmatch(config, -1)
channels := channelRe.FindAllStringSubmatch(config, -1)
assert.Equal(t, "Cheesy Arena", ssids[0][1])
assert.Equal(t, ap.adminWpaKey, wpaKeys[0][1])
assert.Equal(t, "100", vlans[0][1])
assert.Equal(t, strconv.Itoa(ap.adminChannel), channels[0][1])
assert.Equal(t, strconv.Itoa(ap.teamChannel), channels[1][1])
assert.Equal(t, 0, len(ssids))
assert.Equal(t, 0, len(wpaKeys))
// Should configure two SSID for two teams.
config, _ = ap.generateAccessPointConfig(&model.Team{Id: 254, WpaKey: "aaaaaaaa"}, nil, nil, nil, nil,
&model.Team{Id: 1114, WpaKey: "bbbbbbbb"})
assert.Equal(t, 2, len(radioRe.FindAllString(config, -1)))
ssids = ssidRe.FindAllStringSubmatch(config, -1)
wpaKeys = wpaKeyRe.FindAllStringSubmatch(config, -1)
vlans = vlanRe.FindAllStringSubmatch(config, -1)
assert.Equal(t, "Cheesy Arena", ssids[0][1])
assert.Equal(t, ap.adminWpaKey, wpaKeys[0][1])
assert.Equal(t, "100", vlans[0][1])
assert.Equal(t, "254", ssids[1][1])
assert.Equal(t, "aaaaaaaa", wpaKeys[1][1])
assert.Equal(t, "10", vlans[1][1])
assert.Equal(t, "1114", ssids[2][1])
assert.Equal(t, "bbbbbbbb", wpaKeys[2][1])
assert.Equal(t, "60", vlans[2][1])
if assert.Equal(t, 2, len(ssids)) && assert.Equal(t, 2, len(wpaKeys)) {
assert.Equal(t, "254", ssids[0][1])
assert.Equal(t, "aaaaaaaa", wpaKeys[0][1])
assert.Equal(t, "1114", ssids[1][1])
assert.Equal(t, "bbbbbbbb", wpaKeys[1][1])
}
// Should configure all SSIDs for six teams.
config, _ = ap.generateAccessPointConfig(&model.Team{Id: 1, WpaKey: "11111111"},
&model.Team{Id: 2, WpaKey: "22222222"}, &model.Team{Id: 3, WpaKey: "33333333"},
&model.Team{Id: 4, WpaKey: "44444444"}, &model.Team{Id: 5, WpaKey: "55555555"},
&model.Team{Id: 6, WpaKey: "66666666"})
assert.Equal(t, 6, len(radioRe.FindAllString(config, -1)))
ssids = ssidRe.FindAllStringSubmatch(config, -1)
wpaKeys = wpaKeyRe.FindAllStringSubmatch(config, -1)
vlans = vlanRe.FindAllStringSubmatch(config, -1)
assert.Equal(t, "Cheesy Arena", ssids[0][1])
assert.Equal(t, ap.adminWpaKey, wpaKeys[0][1])
assert.Equal(t, "100", vlans[0][1])
assert.Equal(t, "1", ssids[1][1])
assert.Equal(t, "11111111", wpaKeys[1][1])
assert.Equal(t, "10", vlans[1][1])
assert.Equal(t, "2", ssids[2][1])
assert.Equal(t, "22222222", wpaKeys[2][1])
assert.Equal(t, "20", vlans[2][1])
assert.Equal(t, "3", ssids[3][1])
assert.Equal(t, "33333333", wpaKeys[3][1])
assert.Equal(t, "30", vlans[3][1])
assert.Equal(t, "4", ssids[4][1])
assert.Equal(t, "44444444", wpaKeys[4][1])
assert.Equal(t, "40", vlans[4][1])
assert.Equal(t, "5", ssids[5][1])
assert.Equal(t, "55555555", wpaKeys[5][1])
assert.Equal(t, "50", vlans[5][1])
assert.Equal(t, "6", ssids[6][1])
assert.Equal(t, "66666666", wpaKeys[6][1])
assert.Equal(t, "60", vlans[6][1])
if assert.Equal(t, 6, len(ssids)) && assert.Equal(t, 6, len(wpaKeys)) {
assert.Equal(t, "1", ssids[0][1])
assert.Equal(t, "11111111", wpaKeys[0][1])
assert.Equal(t, "2", ssids[1][1])
assert.Equal(t, "22222222", wpaKeys[1][1])
assert.Equal(t, "3", ssids[2][1])
assert.Equal(t, "33333333", wpaKeys[2][1])
assert.Equal(t, "4", ssids[3][1])
assert.Equal(t, "44444444", wpaKeys[3][1])
assert.Equal(t, "5", ssids[4][1])
assert.Equal(t, "55555555", wpaKeys[4][1])
assert.Equal(t, "6", ssids[5][1])
assert.Equal(t, "66666666", wpaKeys[5][1])
}
// Should reject a missing WPA key.
_, err := ap.generateAccessPointConfig(&model.Team{Id: 254}, nil, nil, nil, nil, nil)

View File

@@ -18,6 +18,15 @@ import (
const switchTelnetPort = 23
const (
red1Vlan = 10
red2Vlan = 20
red3Vlan = 30
blue1Vlan = 40
blue2Vlan = 50
blue3Vlan = 60
)
type NetworkSwitch struct {
address string
port int

View File

@@ -1,46 +0,0 @@
config wifi-device 'radio1'
option type 'mac80211'
option channel '{{.AdminChannel}}'
option hwmode '11g'
option path 'soc/soc:pcie-controller/pci0000:00/0000:00:02.0/0000:02:00.0'
option htmode 'HT20'
option disabled '0'
option txpower '20'
option country 'US'
config wifi-iface
option device 'radio1'
option mode 'ap'
option hidden '1'
option encryption 'psk2+ccmp'
option network 'vlan100'
option ssid 'Cheesy Arena'
option key '{{.AdminWpaKey}}'
option macaddr '62:38:e0:12:6b:17'
config wifi-device 'radio0'
option type 'mac80211'
option hwmode '11a'
option path 'soc/soc:pcie-controller/pci0000:00/0000:00:01.0/0000:01:00.0'
option txpower '23'
option channel '{{.TeamChannel}}'
option country 'US'
option htmode 'HT20'
option basic_rates '6500,7200'
option short_gi_20 '0'
{{range $vlan, $team := .Networks}}
config wifi-iface
option device 'radio0'
option mode 'ap'
option isolate '0'
option bgscan '0'
option wds '0'
option maxassoc '1'
option hidden '1'
option network 'vlan{{$vlan}}'
option encryption 'psk2+ccmp'
option ssid '{{$team.Id}}'
option key '{{$team.WpaKey}}'
option disabled '0'
{{end}}