From 0a783b37016415cd66b74b022d6b62b44d745dc2 Mon Sep 17 00:00:00 2001 From: Patrick Fairbank Date: Sat, 22 Jul 2017 00:08:55 -0700 Subject: [PATCH] Update network configuration to work with Linksys WRT1900ACS and Cisco Catalyst 3560G. --- access_point_config.go | 113 +++++++++ access_point_config.tar.gz | Bin 0 -> 6574 bytes access_point_config_test.go | 80 ++++++ aironet.go | 151 ------------ aironet_test.go | 77 ------ ap_config.txt | 316 ------------------------ arena_test.go | 4 +- catalyst_test.go | 39 --- driver_station_connection_test.go | 2 + catalyst.go => switch_config.go | 26 +- switch_config.txt | 394 +++++++++++++++++++----------- switch_config_test.go | 70 ++++++ templates/access_point.cfg | 46 ++++ templates/setup_settings.html | 2 +- 14 files changed, 580 insertions(+), 740 deletions(-) create mode 100644 access_point_config.go create mode 100644 access_point_config.tar.gz create mode 100644 access_point_config_test.go delete mode 100644 aironet.go delete mode 100644 aironet_test.go delete mode 100644 ap_config.txt delete mode 100644 catalyst_test.go rename catalyst.go => switch_config.go (82%) create mode 100644 switch_config_test.go create mode 100644 templates/access_point.cfg diff --git a/access_point_config.go b/access_point_config.go new file mode 100644 index 0000000..93c2cde --- /dev/null +++ b/access_point_config.go @@ -0,0 +1,113 @@ +// Copyright 2017 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Methods for configuring a Linksys WRT1900ACS access point running OpenWRT for team SSIDs and VLANs. + +package main + +import ( + "bytes" + "fmt" + "golang.org/x/crypto/ssh" + "os" + "sync" + "text/template" +) + +var accessPointSshPort = 22 + +const ( + red1Vlan = 10 + red2Vlan = 20 + red3Vlan = 30 + blue1Vlan = 40 + blue2Vlan = 50 + blue3Vlan = 60 +) + +var accessPointMutex sync.Mutex + +// Sets up wireless networks for the given set of teams. +func ConfigureTeamWifi(red1, red2, red3, blue1, blue2, blue3 *Team) error { + // Make sure multiple configurations aren't being set at the same time. + accessPointMutex.Lock() + defer accessPointMutex.Unlock() + + config, err := generateAccessPointConfig(red1, red2, red3, blue1, blue2, blue3) + if err != nil { + return err + } + command := fmt.Sprintf("cat < /etc/config/wireless && wifi radio0\n%sENDCONFIG\n", config) + return runAccessPointCommand(command) +} + +func generateAccessPointConfig(red1, red2, red3, blue1, blue2, blue3 *Team) (string, error) { + // Determine what new SSIDs are needed. + networks := make(map[int]*Team) + var err error + if err = addTeamNetwork(networks, red1, red1Vlan); err != nil { + return "", err + } + if err = addTeamNetwork(networks, red2, red2Vlan); err != nil { + return "", err + } + if err = addTeamNetwork(networks, red3, red3Vlan); err != nil { + return "", err + } + if err = addTeamNetwork(networks, blue1, blue1Vlan); err != nil { + return "", err + } + if err = addTeamNetwork(networks, blue2, blue2Vlan); err != nil { + return "", err + } + if err = addTeamNetwork(networks, blue3, blue3Vlan); err != nil { + return "", err + } + + // Generate the config file to be uploaded to the AP. + template, err := template.ParseFiles("templates/access_point.cfg") + if err != nil { + return "", err + } + var configFile bytes.Buffer + err = template.Execute(&configFile, networks) + if err != nil { + return "", err + } + + return configFile.String(), 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]*Team, team *Team, vlan int) 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 + return nil +} + +// Logs into the access point via SSH and runs the given shell command. +func runAccessPointCommand(command string) error { + // Open an SSH connection to the AP. + config := &ssh.ClientConfig{User: eventSettings.ApUsername, + Auth: []ssh.AuthMethod{ssh.Password(eventSettings.ApPassword)}, + HostKeyCallback: ssh.InsecureIgnoreHostKey()} + conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", eventSettings.ApAddress, accessPointSshPort), config) + if err != nil { + return err + } + session, err := conn.NewSession() + if err != nil { + return err + } + defer session.Close() + defer conn.Close() + session.Stdout = os.Stdout + + // Run the command. An error will be returned if the exit status is non-zero. + return session.Run(command) +} diff --git a/access_point_config.tar.gz b/access_point_config.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7679723614c8900bd80d21e69a11d8b065a43465 GIT binary patch literal 6574 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 x#IM}Pj0%~5k4_Sg+F#9Ts5;M}VUkgJSzM 63) { - return fmt.Errorf("Invalid WPA key '%s' configured for team %d.", team.WpaKey, team.Id) - } - } - - // Determine what new SSIDs are needed and build the commands to set them up. - oldSsids, err := getSsids() - if err != nil { - return err - } - addSsidsCommand := "" - associateSsidsCommand := "" - replaceSsid := func(team *Team, vlan int) { - if team == nil { - return - } - if oldSsids[strconv.Itoa(team.Id)] == vlan { - delete(oldSsids, strconv.Itoa(team.Id)) - } else { - addSsidsCommand += fmt.Sprintf("dot11 ssid %d\nvlan %d\nauthentication open\nauthentication "+ - "key-management wpa version 2\nmbssid guest-mode\nwpa-psk ascii %s\n", team.Id, vlan, team.WpaKey) - associateSsidsCommand += fmt.Sprintf("ssid %d\n", team.Id) - } - } - replaceSsid(red1, red1Vlan) - replaceSsid(red2, red2Vlan) - replaceSsid(red3, red3Vlan) - replaceSsid(blue1, blue1Vlan) - replaceSsid(blue2, blue2Vlan) - replaceSsid(blue3, blue3Vlan) - if len(addSsidsCommand) != 0 { - associateSsidsCommand = "interface Dot11Radio1\n" + associateSsidsCommand - } - - // Build the command to remove the SSIDs that are no longer needed. - removeSsidsCommand := "" - for ssid, _ := range oldSsids { - removeSsidsCommand += fmt.Sprintf("no dot11 ssid %s\n", ssid) - } - - // Build and run the overall command to do everything in a single telnet session. - command := removeSsidsCommand + addSsidsCommand + associateSsidsCommand - if len(command) > 0 { - _, err = runAironetConfigCommand(removeSsidsCommand + addSsidsCommand + associateSsidsCommand) - if err != nil { - return err - } - } - - return nil -} - -// Returns a map of currently-configured SSIDs to VLANs. -func getSsids() (map[string]int, error) { - // Get the entire config dump. - config, err := runAironetCommand("show running-config\n") - if err != nil { - return nil, err - } - - // Parse out the SSIDs and VLANs from the config dump. - re := regexp.MustCompile("(?s)dot11 ssid (\\w+)\\s+vlan (1[1-6])") - ssidMatches := re.FindAllStringSubmatch(config, -1) - if ssidMatches == nil { - // There are probably no SSIDs currently configured. - return nil, nil - } - - // Build the map of SSID to VLAN. - ssids := make(map[string]int) - for _, match := range ssidMatches { - vlan, _ := strconv.Atoi(match[2]) - ssids[match[1]] = vlan - } - return ssids, nil -} - -// Logs into the Aironet via Telnet and runs the given command in user exec mode. Reads the output and returns -// it as a string. -func runAironetCommand(command string) (string, error) { - // Open a Telnet connection to the AP. - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", eventSettings.ApAddress, aironetTelnetPort)) - if err != nil { - return "", err - } - defer conn.Close() - - // Login to the AP, send the command, and log out all at once. - writer := bufio.NewWriter(conn) - _, err = writer.WriteString(fmt.Sprintf("%s\n%s\nterminal length 0\n%sexit\n", eventSettings.ApUsername, - eventSettings.ApPassword, command)) - if err != nil { - return "", err - } - err = writer.Flush() - if err != nil { - return "", err - } - - // Read the response. - var reader bytes.Buffer - _, err = reader.ReadFrom(conn) - if err != nil { - return "", err - } - return reader.String(), nil -} - -// Logs into the Aironet via Telnet and runs the given command in global configuration mode. Reads the output -// and returns it as a string. -func runAironetConfigCommand(command string) (string, error) { - return runAironetCommand(fmt.Sprintf("config terminal\n%send\ncopy running-config startup-config\n\n", - command)) -} diff --git a/aironet_test.go b/aironet_test.go deleted file mode 100644 index 703c4d5..0000000 --- a/aironet_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package main - -import ( - "bytes" - "fmt" - "github.com/stretchr/testify/assert" - "net" - "testing" - "time" -) - -func TestConfigureAironet(t *testing.T) { - aironetTelnetPort = 9023 - eventSettings = &EventSettings{ApAddress: "127.0.0.1", ApUsername: "user", ApPassword: "password"} - var command string - - // Should do nothing if current configuration is blank. - mockTelnet(t, aironetTelnetPort, "", &command) - assert.Nil(t, ConfigureTeamWifi(nil, nil, nil, nil, nil, nil)) - assert.Equal(t, "", command) - - // Should remove any existing teams but not other SSIDs. - aironetTelnetPort += 1 - mockTelnet(t, aironetTelnetPort, - "dot11 ssid 1\nvlan 1\ndot11 ssid 254\nvlan 12\ndot11 ssid Cheesy Arena\nvlan 17\n", &command) - assert.Nil(t, ConfigureTeamWifi(nil, nil, nil, nil, nil, nil)) - assert.Equal(t, "user\npassword\nterminal length 0\nconfig terminal\nno dot11 ssid 254\nend\n"+ - "copy running-config startup-config\n\nexit\n", command) - - // Should configure new teams and leave existing ones alone if still needed. - aironetTelnetPort += 1 - mockTelnet(t, aironetTelnetPort, "dot11 ssid 254\nvlan 11\n", &command) - assert.Nil(t, ConfigureTeamWifi(&Team{Id: 254, WpaKey: "aaaaaaaa"}, nil, nil, nil, nil, - &Team{Id: 1114, WpaKey: "bbbbbbbb"})) - assert.Equal(t, "user\npassword\nterminal length 0\nconfig terminal\ndot11 ssid 1114\nvlan 16\n"+ - "authentication open\nauthentication key-management wpa version 2\nmbssid guest-mode\nwpa-psk ascii "+ - "bbbbbbbb\ninterface Dot11Radio1\nssid 1114\nend\ncopy running-config startup-config\n\nexit\n", - command) - - // Should reject a missing WPA key. - aironetTelnetPort += 1 - mockTelnet(t, aironetTelnetPort, "", &command) - err := ConfigureTeamWifi(&Team{Id: 254}, nil, nil, nil, nil, nil) - if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "Invalid WPA key") - } -} - -func mockTelnet(t *testing.T, port int, response string, command *string) { - go func() { - // Fake the first connection which should just get the configuration. - ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) - assert.Nil(t, err) - defer ln.Close() - conn, err := ln.Accept() - assert.Nil(t, err) - conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) - var reader bytes.Buffer - reader.ReadFrom(conn) - assert.Contains(t, reader.String(), "terminal length 0\nshow running-config\nexit\n") - conn.Write([]byte(response)) - conn.Close() - - // Fake the second connection which should configure stuff. - conn2, err := ln.Accept() - assert.Nil(t, err) - conn2.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) - var reader2 bytes.Buffer - reader2.ReadFrom(conn2) - *command = reader2.String() - conn2.Close() - }() - time.Sleep(100 * time.Millisecond) // Give it some time to open the socket. -} diff --git a/ap_config.txt b/ap_config.txt deleted file mode 100644 index 8fb8fde..0000000 --- a/ap_config.txt +++ /dev/null @@ -1,316 +0,0 @@ -! Baseline configuration for the Cisco Aironet AP1252AG access point. Load this into the AP prior to -! configuring Cheesy Arena to connect to it. Default user/pass is cheesyarena/1234Five. -! -version 15.2 -no service pad -service timestamps debug datetime msec -service timestamps log datetime msec -service password-encryption -! -hostname ChezyAP -! -logging rate-limit console 9 -! -aaa new-model -! -! -aaa authentication login default local -aaa authorization exec default local -! -! -! -! -! -aaa session-id common -! -! -dot11 syslog -dot11 vlan-name Blue1 vlan 14 -dot11 vlan-name Blue2 vlan 15 -dot11 vlan-name Blue3 vlan 16 -dot11 vlan-name CheesyArena vlan 2 -dot11 vlan-name Red1 vlan 11 -dot11 vlan-name Red2 vlan 12 -dot11 vlan-name Red3 vlan 13 -! -dot11 ssid 1 - vlan 11 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 0257550A5A575E701D -! -dot11 ssid 2 - vlan 12 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 06545D731E1C5B4B57 -! -dot11 ssid 3 - vlan 13 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 115A4A564441585F57 -! -dot11 ssid 4 - vlan 14 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 101A5D4D5143465F58 -! -dot11 ssid 5 - vlan 15 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 00514653510E5E535A -! -dot11 ssid 6 - vlan 16 - authentication open - authentication key-management wpa version 2 - mbssid guest-mode - wpa-psk ascii 7 1441445D5A527C7D72 -! -dot11 ssid Cheesy Arena - vlan 2 - authentication open - authentication key-management wpa version 2 - guest-mode - wpa-psk ascii 7 144640585822233D21 -! -crypto pki token default removal timeout 0 -! -! -username cheesyarena privilege 15 password 7 040A59555B0745580C -! -! -bridge irb -! -! -interface Dot11Radio0 - no ip address - no ip route-cache - ! - encryption mode ciphers aes-ccm tkip - ! - encryption vlan 2 mode ciphers aes-ccm tkip - ! - ssid Cheesy Arena - ! - antenna gain 0 - station-role root - no dot11 extension aironet - bridge-group 1 - bridge-group 1 subscriber-loop-control - bridge-group 1 spanning-disabled - bridge-group 1 block-unknown-source - no bridge-group 1 source-learning - no bridge-group 1 unicast-flooding -! -interface Dot11Radio0.2 - encapsulation dot1Q 2 - no ip route-cache - bridge-group 2 - bridge-group 2 subscriber-loop-control - bridge-group 2 spanning-disabled - bridge-group 2 block-unknown-source - no bridge-group 2 source-learning - no bridge-group 2 unicast-flooding -! -interface Dot11Radio1 - no ip address - no ip route-cache - ! - encryption mode ciphers aes-ccm tkip - ! - encryption vlan 11 mode ciphers aes-ccm tkip - ! - encryption vlan 12 mode ciphers aes-ccm tkip - ! - encryption vlan 13 mode ciphers aes-ccm tkip - ! - encryption vlan 14 mode ciphers aes-ccm tkip - ! - encryption vlan 15 mode ciphers aes-ccm tkip - ! - encryption vlan 16 mode ciphers aes-ccm tkip - ! - ssid 1 - ! - ssid 2 - ! - ssid 3 - ! - ssid 4 - ! - ssid 5 - ! - ssid 6 - ! - antenna gain 0 - dfs band 3 block - mbssid - channel width 40-above - channel dfs - station-role root - no dot11 extension aironet - bridge-group 1 - bridge-group 1 subscriber-loop-control - bridge-group 1 spanning-disabled - bridge-group 1 block-unknown-source - no bridge-group 1 source-learning - no bridge-group 1 unicast-flooding -! -interface Dot11Radio1.11 - encapsulation dot1Q 11 - no ip route-cache - bridge-group 11 - bridge-group 11 subscriber-loop-control - bridge-group 11 spanning-disabled - bridge-group 11 block-unknown-source - no bridge-group 11 source-learning - no bridge-group 11 unicast-flooding -! -interface Dot11Radio1.12 - encapsulation dot1Q 12 - no ip route-cache - bridge-group 12 - bridge-group 12 subscriber-loop-control - bridge-group 12 spanning-disabled - bridge-group 12 block-unknown-source - no bridge-group 12 source-learning - no bridge-group 12 unicast-flooding -! -interface Dot11Radio1.13 - encapsulation dot1Q 13 - no ip route-cache - bridge-group 13 - bridge-group 13 subscriber-loop-control - bridge-group 13 spanning-disabled - bridge-group 13 block-unknown-source - no bridge-group 13 source-learning - no bridge-group 13 unicast-flooding -! -interface Dot11Radio1.14 - encapsulation dot1Q 14 - no ip route-cache - bridge-group 14 - bridge-group 14 subscriber-loop-control - bridge-group 14 spanning-disabled - bridge-group 14 block-unknown-source - no bridge-group 14 source-learning - no bridge-group 14 unicast-flooding -! -interface Dot11Radio1.15 - encapsulation dot1Q 15 - no ip route-cache - bridge-group 15 - bridge-group 15 subscriber-loop-control - bridge-group 15 spanning-disabled - bridge-group 15 block-unknown-source - no bridge-group 15 source-learning - no bridge-group 15 unicast-flooding -! -interface Dot11Radio1.16 - encapsulation dot1Q 16 - no ip route-cache - bridge-group 16 - bridge-group 16 subscriber-loop-control - bridge-group 16 spanning-disabled - bridge-group 16 block-unknown-source - no bridge-group 16 source-learning - no bridge-group 16 unicast-flooding -! -interface GigabitEthernet0 - no ip address - no ip route-cache - duplex auto - speed auto - bridge-group 1 - bridge-group 1 spanning-disabled - no bridge-group 1 source-learning -! -interface GigabitEthernet0.2 - encapsulation dot1Q 2 - no ip route-cache - bridge-group 2 - bridge-group 2 spanning-disabled - no bridge-group 2 source-learning -! -interface GigabitEthernet0.11 - encapsulation dot1Q 11 - ip access-group 100 in - no ip route-cache - bridge-group 11 - bridge-group 11 spanning-disabled - no bridge-group 11 source-learning -! -interface GigabitEthernet0.12 - encapsulation dot1Q 12 - ip access-group 100 in - no ip route-cache - bridge-group 12 - bridge-group 12 spanning-disabled - no bridge-group 12 source-learning -! -interface GigabitEthernet0.13 - encapsulation dot1Q 13 - ip access-group 100 in - no ip route-cache - bridge-group 13 - bridge-group 13 spanning-disabled - no bridge-group 13 source-learning -! -interface GigabitEthernet0.14 - encapsulation dot1Q 14 - ip access-group 100 in - no ip route-cache - bridge-group 14 - bridge-group 14 spanning-disabled - no bridge-group 14 source-learning -! -interface GigabitEthernet0.15 - encapsulation dot1Q 15 - ip access-group 100 in - no ip route-cache - bridge-group 15 - bridge-group 15 spanning-disabled - no bridge-group 15 source-learning -! -interface GigabitEthernet0.16 - encapsulation dot1Q 16 - ip access-group 100 in - ip access-group 101 out - no ip route-cache - bridge-group 16 - bridge-group 16 spanning-disabled - no bridge-group 16 source-learning -! -interface BVI1 - ip address 10.0.0.60 255.0.0.0 - no ip route-cache -! -ip default-gateway 10.0.0.1 -ip http server -ip http authentication aaa -no ip http secure-server -ip http help-path http://www.cisco.com/warp/public/779/smbiz/prodconfig/help/eag -access-list 100 deny udp any any eq 1120 -access-list 100 permit ip any any -access-list 101 deny tcp any any eq 1750 -access-list 101 permit ip any any -! -bridge 1 route ip -! -! -! -line con 0 -line vty 0 4 - transport input all -! -sntp server 216.66.0.142 -end diff --git a/arena_test.go b/arena_test.go index 9cff24f..93b353f 100644 --- a/arena_test.go +++ b/arena_test.go @@ -495,8 +495,8 @@ func TestSetupNetwork(t *testing.T) { // Verify the setup ran by checking the log for the expected failure messages. eventSettings.NetworkSecurityEnabled = true - aironetTelnetPort = 10023 - catalystTelnetPort = 10023 + accessPointSshPort = 10022 + switchTelnetPort = 10023 mainArena.LoadMatch(&Match{Type: "test"}) var writer bytes.Buffer log.SetOutput(&writer) diff --git a/catalyst_test.go b/catalyst_test.go deleted file mode 100644 index a04c89a..0000000 --- a/catalyst_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2014 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package main - -import ( - "github.com/stretchr/testify/assert" - "testing" -) - -func TestConfigureCatalyst(t *testing.T) { - catalystTelnetPort = 9050 - eventSettings = &EventSettings{SwitchAddress: "127.0.0.1", SwitchPassword: "password"} - var command string - - // Should do nothing if current configuration is blank. - mockTelnet(t, catalystTelnetPort, "", &command) - assert.Nil(t, ConfigureTeamEthernet(nil, nil, nil, nil, nil, nil)) - assert.Equal(t, "", command) - - // Should remove any existing teams but not other SSIDs. - catalystTelnetPort += 1 - mockTelnet(t, catalystTelnetPort, - "interface Vlan2\nip address 10.0.100.2\ninterface Vlan15\nip address 10.2.54.61\n", &command) - assert.Nil(t, ConfigureTeamEthernet(nil, nil, nil, nil, nil, nil)) - assert.Equal(t, "password\nenable\npassword\nterminal length 0\nconfig terminal\ninterface Vlan15\nno ip"+ - " address\nno access-list 115\nend\ncopy running-config startup-config\n\nexit\n", command) - - // Should configure new teams and leave existing ones alone if still needed. - catalystTelnetPort += 1 - mockTelnet(t, catalystTelnetPort, "interface Vlan15\nip address 10.2.54.61\n", &command) - assert.Nil(t, ConfigureTeamEthernet(nil, &Team{Id: 1114}, nil, nil, &Team{Id: 254}, nil)) - assert.Equal(t, "password\nenable\npassword\nterminal length 0\nconfig terminal\n"+ - "ip dhcp excluded-address 10.11.14.1 10.11.14.100\nno ip dhcp pool dhcp12\nip dhcp pool dhcp12\n"+ - "network 10.11.14.0 255.255.255.0\ndefault-router 10.11.14.61\nlease 7\nno access-list 112\n"+ - "access-list 112 permit ip 10.11.14.0 0.0.0.255 host 10.0.100.5\n"+ - "access-list 112 permit udp any eq bootpc any eq bootps\ninterface Vlan12\n"+ - "ip address 10.11.14.61 255.255.255.0\nend\ncopy running-config startup-config\n\nexit\n", command) -} diff --git a/driver_station_connection_test.go b/driver_station_connection_test.go index 8c4fc22..fe8d1af 100644 --- a/driver_station_connection_test.go +++ b/driver_station_connection_test.go @@ -154,10 +154,12 @@ func TestListenForDriverStations(t *testing.T) { defer db.Close() eventSettings, _ = db.GetEventSettings() + oldAddress := driverStationTcpListenAddress driverStationTcpListenAddress = "127.0.0.1" go ListenForDriverStations() mainArena.Setup() time.Sleep(time.Millisecond * 10) + driverStationTcpListenAddress = oldAddress // Put it back to avoid affecting other tests. // Connect with an invalid initial packet. tcpConn, err := net.Dial("tcp", "127.0.0.1:1750") diff --git a/catalyst.go b/switch_config.go similarity index 82% rename from catalyst.go rename to switch_config.go index 3daa16d..4166bfa 100644 --- a/catalyst.go +++ b/switch_config.go @@ -1,7 +1,7 @@ // Copyright 2014 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) // -// Methods for configuring a Cisco Catalyst 3750 switch for team VLANs. +// Methods for configuring a Cisco Switch 3500-series switch for team VLANs. package main @@ -15,15 +15,15 @@ import ( "sync" ) -var catalystTelnetPort = 23 +var switchTelnetPort = 23 -var catalystMutex sync.Mutex +var switchMutex sync.Mutex // Sets up wired networks for the given set of teams. func ConfigureTeamEthernet(red1, red2, red3, blue1, blue2, blue3 *Team) error { // Make sure multiple configurations aren't being set at the same time. - catalystMutex.Lock() - defer catalystMutex.Unlock() + switchMutex.Lock() + defer switchMutex.Unlock() // Determine what new team VLANs are needed and build the commands to set them up. oldTeamVlans, err := getTeamVlans() @@ -70,7 +70,7 @@ func ConfigureTeamEthernet(red1, red2, red3, blue1, blue2, blue3 *Team) error { // Build and run the overall command to do everything in a single telnet session. command := removeTeamVlansCommand + addTeamVlansCommand if len(command) > 0 { - _, err = runCatalystConfigCommand(removeTeamVlansCommand + addTeamVlansCommand) + _, err = runSwitchConfigCommand(removeTeamVlansCommand + addTeamVlansCommand) if err != nil { return err } @@ -82,7 +82,7 @@ func ConfigureTeamEthernet(red1, red2, red3, blue1, blue2, blue3 *Team) error { // Returns a map of currently-configured teams to VLANs. func getTeamVlans() (map[int]int, error) { // Get the entire config dump. - config, err := runCatalystCommand("show running-config\n") + config, err := runSwitchCommand("show running-config\n") if err != nil { return nil, err } @@ -107,11 +107,11 @@ func getTeamVlans() (map[int]int, error) { return teamVlans, nil } -// Logs into the Catalyst via Telnet and runs the given command in user exec mode. Reads the output and +// Logs into the switch via Telnet and runs the given command in user exec mode. Reads the output and // returns it as a string. -func runCatalystCommand(command string) (string, error) { +func runSwitchCommand(command string) (string, error) { // Open a Telnet connection to the switch. - conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", eventSettings.SwitchAddress, catalystTelnetPort)) + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", eventSettings.SwitchAddress, switchTelnetPort)) if err != nil { return "", err } @@ -138,9 +138,9 @@ func runCatalystCommand(command string) (string, error) { return reader.String(), nil } -// Logs into the Catalyst via Telnet and runs the given command in global configuration mode. Reads the output +// Logs into the switch via Telnet and runs the given command in global configuration mode. Reads the output // and returns it as a string. -func runCatalystConfigCommand(command string) (string, error) { - return runCatalystCommand(fmt.Sprintf("config terminal\n%send\ncopy running-config startup-config\n\n", +func runSwitchConfigCommand(command string) (string, error) { + return runSwitchCommand(fmt.Sprintf("config terminal\n%send\ncopy running-config startup-config\n\n", command)) } diff --git a/switch_config.txt b/switch_config.txt index 950bdb0..00fa1b7 100644 --- a/switch_config.txt +++ b/switch_config.txt @@ -1,186 +1,299 @@ ! Baseline configuration for the Catalyst 3500-series switch. Load this into the switch prior to configuring ! Cheesy Arena to connect to it. Default password is 1234Five. ! -version 12.1 +version 12.2 no service pad -service timestamps debug uptime -service timestamps log uptime +service timestamps debug datetime msec +service timestamps log datetime msec no service password-encryption ! hostname ChezySwitch ! +boot-start-marker +boot-end-marker +! enable secret 5 $1$kKSW$fCMwnMdYvXui1TulfyYHN/ ! -ip subnet-zero +! +! +no aaa new-model +system mtu routing 1500 ip routing ip dhcp excluded-address 10.0.100.1 10.0.100.100 ! ip dhcp pool dhcppool network 10.0.100.0 255.255.255.0 - default-router 10.0.100.1 domain-name team254.com dns-server 8.8.8.8 8.8.4.4 lease 7 ! ! +! +! +! +! +! +! spanning-tree mode pvst spanning-tree portfast default spanning-tree extend system-id ! +vlan internal allocation policy ascending ! ! ! ! -interface FastEthernet0/1 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/2 - switchport access vlan 11 - switchport mode access -! -interface FastEthernet0/3 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/4 - switchport access vlan 12 - switchport mode access -! -interface FastEthernet0/5 - switchport mode access -! -interface FastEthernet0/6 - switchport access vlan 13 - switchport mode access -! -interface FastEthernet0/7 - switchport mode access -! -interface FastEthernet0/8 - switchport access vlan 14 - switchport mode access -! -interface FastEthernet0/9 - switchport trunk encapsulation dot1q - switchport mode trunk -! -interface FastEthernet0/10 - switchport access vlan 15 - switchport mode access -! -interface FastEthernet0/11 - switchport trunk encapsulation dot1q - switchport mode trunk -! -interface FastEthernet0/12 - switchport access vlan 16 - switchport mode access -! -interface FastEthernet0/13 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/14 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/15 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/16 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/17 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/18 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/19 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/20 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/21 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/22 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/23 - switchport access vlan 2 - switchport mode access -! -interface FastEthernet0/24 - switchport access vlan 2 - switchport mode access -! interface GigabitEthernet0/1 - switchport trunk encapsulation dot1q - switchport mode trunk + switchport access vlan 100 + switchport mode access ! interface GigabitEthernet0/2 - switchport access vlan 2 + switchport trunk encapsulation dot1q + switchport trunk native vlan 100 + switchport mode trunk +! +interface GigabitEthernet0/3 + switchport access vlan 100 switchport mode access ! +interface GigabitEthernet0/4 + switchport mode access +! +interface GigabitEthernet0/5 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/6 + switchport access vlan 10 + switchport mode access +! +interface GigabitEthernet0/7 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/8 + switchport access vlan 20 + switchport mode access +! +interface GigabitEthernet0/9 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/10 + switchport access vlan 30 + switchport mode access +! +interface GigabitEthernet0/11 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/12 + switchport access vlan 40 + switchport mode access +! +interface GigabitEthernet0/13 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/14 + switchport access vlan 50 + switchport mode access +! +interface GigabitEthernet0/15 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/16 + switchport access vlan 60 + switchport mode access +! +interface GigabitEthernet0/17 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/18 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/19 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/20 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/21 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/22 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/23 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/24 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/25 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/26 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/27 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/28 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/29 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/30 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/31 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/32 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/33 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/34 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/35 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/36 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/37 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/38 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/39 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/40 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/41 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/42 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/43 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/44 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/45 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/46 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/47 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/48 + switchport access vlan 100 + switchport mode access +! +interface GigabitEthernet0/49 +! +interface GigabitEthernet0/50 +! +interface GigabitEthernet0/51 +! +interface GigabitEthernet0/52 +! interface Vlan1 ip address 10.0.0.61 255.255.255.0 ! -interface Vlan2 +interface Vlan10 + ip address 10.0.1.61 255.255.255.0 + ip access-group 110 in +! +interface Vlan20 + ip address 10.0.2.61 255.255.255.0 + ip access-group 120 in +! +interface Vlan30 + ip address 10.0.3.61 255.255.255.0 + ip access-group 130 in +! +interface Vlan40 + ip address 10.0.4.61 255.255.255.0 + ip access-group 140 in +! +interface Vlan50 + ip address 10.0.5.61 255.255.255.0 + ip access-group 150 in +! +interface Vlan60 + ip address 10.0.6.61 255.255.255.0 + ip access-group 160 in +! +interface Vlan100 ip address 10.0.100.2 255.255.255.0 ! -interface Vlan11 - ip address 10.0.1.61 255.255.255.0 - ip access-group 111 in -! -interface Vlan12 - ip address 10.0.2.61 255.255.255.0 - ip access-group 112 in -! -interface Vlan13 - ip address 10.0.3.61 255.255.255.0 - ip access-group 113 in -! -interface Vlan14 - ip address 10.0.4.61 255.255.255.0 - ip access-group 114 in -! -interface Vlan15 - ip address 10.0.5.61 255.255.255.0 - ip access-group 115 in -! -interface Vlan16 - ip address 10.0.6.61 255.255.255.0 - ip access-group 116 in -! ip classless -ip http server +no ip http server +no ip http secure-server ! -access-list 111 permit ip 10.0.1.0 0.0.0.255 host 10.0.100.5 -access-list 111 permit udp any eq bootpc any eq bootps -access-list 112 permit ip 10.0.2.0 0.0.0.255 host 10.0.100.5 -access-list 112 permit udp any eq bootpc any eq bootps -access-list 113 permit ip 10.0.3.0 0.0.0.255 host 10.0.100.5 -access-list 113 permit udp any eq bootpc any eq bootps -access-list 114 permit ip 10.0.4.0 0.0.0.255 host 10.0.100.5 -access-list 114 permit udp any eq bootpc any eq bootps -access-list 115 permit ip 10.0.5.0 0.0.0.255 host 10.0.100.5 -access-list 115 permit udp any eq bootpc any eq bootps -access-list 116 permit ip 10.0.6.0 0.0.0.255 host 10.0.100.5 -access-list 116 permit udp any eq bootpc any eq bootps +! +access-list 110 permit ip 10.0.1.0 0.0.0.255 host 10.0.100.5 +access-list 110 permit udp any eq bootpc any eq bootps +access-list 120 permit ip 10.0.2.0 0.0.0.255 host 10.0.100.5 +access-list 120 permit udp any eq bootpc any eq bootps +access-list 130 permit ip 10.0.3.0 0.0.0.255 host 10.0.100.5 +access-list 130 permit udp any eq bootpc any eq bootps +access-list 140 permit ip 10.0.4.0 0.0.0.255 host 10.0.100.5 +access-list 140 permit udp any eq bootpc any eq bootps +access-list 150 permit ip 10.0.5.0 0.0.0.255 host 10.0.100.5 +access-list 150 permit udp any eq bootpc any eq bootps +access-list 160 permit ip 10.0.6.0 0.0.0.255 host 10.0.100.5 +access-list 160 permit udp any eq bootpc any eq bootps ! snmp-server community 1234Five RO ! +! line con 0 exec-timeout 0 0 line vty 0 4 @@ -190,5 +303,4 @@ line vty 5 15 password 1234Five login ! -! end diff --git a/switch_config_test.go b/switch_config_test.go new file mode 100644 index 0000000..f51d5d5 --- /dev/null +++ b/switch_config_test.go @@ -0,0 +1,70 @@ +// Copyright 2014 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) + +package main + +import ( + "bytes" + "fmt" + "github.com/stretchr/testify/assert" + "net" + "testing" + "time" +) + +func TestConfigureSwitch(t *testing.T) { + switchTelnetPort = 9050 + eventSettings = &EventSettings{SwitchAddress: "127.0.0.1", SwitchPassword: "password"} + var command string + + // Should do nothing if current configuration is blank. + mockTelnet(t, switchTelnetPort, "", &command) + assert.Nil(t, ConfigureTeamEthernet(nil, nil, nil, nil, nil, nil)) + assert.Equal(t, "", command) + + // Should remove any existing teams but not other SSIDs. + switchTelnetPort += 1 + mockTelnet(t, switchTelnetPort, + "interface Vlan100\nip address 10.0.100.2\ninterface Vlan50\nip address 10.2.54.61\n", &command) + assert.Nil(t, ConfigureTeamEthernet(nil, nil, nil, nil, nil, nil)) + assert.Equal(t, "password\nenable\npassword\nterminal length 0\nconfig terminal\ninterface Vlan50\nno ip"+ + " address\nno access-list 150\nend\ncopy running-config startup-config\n\nexit\n", command) + + // Should configure new teams and leave existing ones alone if still needed. + switchTelnetPort += 1 + mockTelnet(t, switchTelnetPort, "interface Vlan50\nip address 10.2.54.61\n", &command) + assert.Nil(t, ConfigureTeamEthernet(nil, &Team{Id: 1114}, nil, nil, &Team{Id: 254}, nil)) + assert.Equal(t, "password\nenable\npassword\nterminal length 0\nconfig terminal\n"+ + "ip dhcp excluded-address 10.11.14.1 10.11.14.100\nno ip dhcp pool dhcp20\nip dhcp pool dhcp20\n"+ + "network 10.11.14.0 255.255.255.0\ndefault-router 10.11.14.61\nlease 7\nno access-list 120\n"+ + "access-list 120 permit ip 10.11.14.0 0.0.0.255 host 10.0.100.5\n"+ + "access-list 120 permit udp any eq bootpc any eq bootps\ninterface Vlan20\n"+ + "ip address 10.11.14.61 255.255.255.0\nend\ncopy running-config startup-config\n\nexit\n", command) +} + +func mockTelnet(t *testing.T, port int, response string, command *string) { + go func() { + // Fake the first connection which should just get the configuration. + ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + assert.Nil(t, err) + defer ln.Close() + conn, err := ln.Accept() + assert.Nil(t, err) + conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) + var reader bytes.Buffer + reader.ReadFrom(conn) + assert.Contains(t, reader.String(), "terminal length 0\nshow running-config\nexit\n") + conn.Write([]byte(response)) + conn.Close() + + // Fake the second connection which should configure stuff. + conn2, err := ln.Accept() + assert.Nil(t, err) + conn2.SetReadDeadline(time.Now().Add(10 * time.Millisecond)) + var reader2 bytes.Buffer + reader2.ReadFrom(conn2) + *command = reader2.String() + conn2.Close() + }() + time.Sleep(100 * time.Millisecond) // Give it some time to open the socket. +} diff --git a/templates/access_point.cfg b/templates/access_point.cfg new file mode 100644 index 0000000..3843de9 --- /dev/null +++ b/templates/access_point.cfg @@ -0,0 +1,46 @@ +config wifi-device 'radio1' + option type 'mac80211' + option channel '11' + 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 '1234Five' + 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 '157' + option country 'US' + option htmode 'HT20' + option basic_rates '6500,7200' + option short_gi_20 '0' + +{{range $vlan, $team := .}} +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}} diff --git a/templates/setup_settings.html b/templates/setup_settings.html index 5d4096b..1210898 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -160,7 +160,7 @@
Networking -

Enable this setting if you have a Cisco Aironet AP1252AG access point and Catalyst 3500-series +

Enable this setting if you have a Linksys WRT1900ACS access point and Catalyst 3500-series switch available, for isolating each team to its own SSID and VLAN.