From b7038311f72b2666e847d08d4b5fc70aede458d3 Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Sat, 23 Nov 2019 09:42:19 +0100
Subject: wireless: T1627: support station mode

Tested using:
-------------

set interfaces wireless wlan0 address 'dhcp'
set interfaces wireless wlan0 channel '0'
set interfaces wireless wlan0 description '1'
set interfaces wireless wlan0 physical-device 'phy0'
set interfaces wireless wlan0 security wpa passphrase '12345678'
set interfaces wireless wlan0 ssid 'VyOS-TEST'
set interfaces wireless wlan0 type 'station'
---
 src/conf_mode/interfaces-wireless.py | 141 ++++++++++++++++++++++++++---------
 1 file changed, 105 insertions(+), 36 deletions(-)

diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 9cfc0bfc5..17b0876a0 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -752,6 +752,18 @@ wmm_ac_vo_acm=0
 
 """
 
+# Please be careful if you edit the template.
+config_wpa_suppl_tmpl = """
+# WPA supplicant config
+network={
+    ssid="{{ ssid }}"
+{%- if sec_wpa_passphrase %}
+    psk="{{ sec_wpa_passphrase }}"
+{% endif %}
+}
+
+"""
+
 default_config_data = {
     'address': [],
     'address_remove': [],
@@ -822,22 +834,51 @@ default_config_data = {
     'vif_remove': []
 }
 
-def get_config_name(intf):
-    cfg_file = r'/etc/hostapd/{}.cfg'.format(intf)
+def get_conf_file(conf_type, intf):
+    cfg_dir = '/var/run/' + conf_type
+
+    # create directory on demand
+    if not os.path.exists(cfg_dir):
+        os.mkdir(cfg_dir)
+        # fix permissions - corresponds to mode 755
+        os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+        uid = getpwnam(user).pw_uid
+        gid = getgrnam(group).gr_gid
+        os.chown(cfg_dir, uid, gid)
+
+    cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
     return cfg_file
 
+def get_pid(conf_type, intf):
+    cfg_dir = '/var/run/' + conf_type
 
-def wifi_mkdir(directory):
     # create directory on demand
-    if not os.path.exists(directory):
-        os.mkdir(directory)
+    if not os.path.exists(cfg_dir):
+        os.mkdir(cfg_dir)
+        # fix permissions - corresponds to mode 755
+        os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+        uid = getpwnam(user).pw_uid
+        gid = getgrnam(group).gr_gid
+        os.chown(cfg_dir, uid, gid)
+
+    cfg_file = cfg_dir + r'/{}.pid'.format(intf)
+    return cfg_file
 
-    # fix permissions - corresponds to mode 755
-    os.chmod(directory, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
-    uid = getpwnam(user).pw_uid
-    gid = getgrnam(group).gr_gid
-    os.chown(directory, uid, gid)
 
+def get_wpa_suppl_config_name(intf):
+    cfg_dir = '/var/run/wpa_supplicant'
+
+    # create directory on demand
+    if not os.path.exists(cfg_dir):
+        os.mkdir(cfg_dir)
+        # fix permissions - corresponds to mode 755
+        os.chmod(cfg_dir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+        uid = getpwnam(user).pw_uid
+        gid = getgrnam(group).gr_gid
+        os.chown(cfg_dir, uid, gid)
+
+    cfg_file = cfg_dir + r'/{}.cfg'.format(intf)
+    return cfg_file
 
 def subprocess_cmd(command):
     p = Popen(command, stdout=PIPE, shell=True)
@@ -1267,37 +1308,57 @@ def verify(wifi):
     return None
 
 def generate(wifi):
-    if wifi['deleted']:
-        if os.path.isfile(get_config_name(wifi['intf'])):
-            os.unlink(get_config_name(wifi['intf']))
-
-        return None
-
-    # create config directory on demand
-    directory = os.path.dirname(get_config_name(wifi['intf']))
-    wifi_mkdir(directory)
-
-    tmpl = Template(config_hostapd_tmpl)
-    config_text = tmpl.render(wifi)
-    with open(get_config_name(wifi['intf']), 'w') as f:
-        f.write(config_text)
+    pid = 0
+    # always stop hostapd service first before reconfiguring it
+    pidfile = get_pid('hostapd', wifi['intf'])
+    if os.path.isfile(pidfile):
+        pid = 0
+        with open(pidfile, 'r') as f:
+            pid = int(f.read())
 
-    return None
+    if pid_exists(pid):
+        cmd  = 'start-stop-daemon --stop --quiet'
+        cmd += ' --pidfile ' + pidfile
+        subprocess_cmd(cmd)
 
-def apply(wifi):
-    pid = 0
-    pidfile = '/var/run/hostapd/{}.pid'.format(wifi['intf'])
+    # always stop wpa_supplicant service first before reconfiguring it
+    pidfile = get_pid('wpa_supplicant', wifi['intf'])
     if os.path.isfile(pidfile):
         pid = 0
         with open(pidfile, 'r') as f:
             pid = int(f.read())
 
-    # always stop hostapd service first before reconfiguring it
     if pid_exists(pid):
         cmd  = 'start-stop-daemon --stop --quiet'
         cmd += ' --pidfile ' + pidfile
         subprocess_cmd(cmd)
 
+    # Delete config files if interface is removed
+    if wifi['deleted']:
+        if os.path.isfile(get_conf_file('hostapd', wifi['intf'])):
+            os.unlink(get_conf_file('hostapd', wifi['intf']))
+
+        if os.path.isfile(get_conf_file('wpa_supplicant', wifi['intf'])):
+            os.unlink(get_conf_file('wpa_supplicant', wifi['intf']))
+
+        return None
+
+    # render appropriate new config files depending on access-point or station mode
+    if wifi['type'] == 'access-point':
+        tmpl = Template(config_hostapd_tmpl)
+        config_text = tmpl.render(wifi)
+        with open(get_conf_file('hostapd', wifi['intf']), 'w') as f:
+            f.write(config_text)
+
+    elif wifi['type'] == 'station':
+        tmpl = Template(config_wpa_suppl_tmpl)
+        config_text = tmpl.render(wifi)
+        with open(get_conf_file('wpa_supplicant', wifi['intf']), 'w') as f:
+            f.write(config_text)
+
+    return None
+
+def apply(wifi):
     w = EthernetIf(wifi['intf'])
     if wifi['deleted']:
         # delete interface
@@ -1377,14 +1438,22 @@ def apply(wifi):
             vlan = e.add_vlan(vif['id'])
             apply_vlan_config(vlan, vif)
 
-    # Physical interface is now configured. Proceed by starting hostapd
+    # Physical interface is now configured. Proceed by starting hostapd or
+    # wpa_supplicant daemon
     cmd  = 'start-stop-daemon --start --quiet'
-    cmd += ' --pidfile ' + pidfile
-    cmd += ' --exec /usr/sbin/hostapd'
-    # now pass arguments to hostapd binary
-    cmd += ' --'
-    cmd += ' -P ' + pidfile
-    cmd += ' -B ' + get_config_name(wifi['intf'])
+    if wifi['type'] == 'access-point':
+        cmd += ' --exec /usr/sbin/hostapd'
+        # now pass arguments to hostapd binary
+        cmd += ' -- -B'
+        cmd += ' -P {}'.format(get_pid('hostapd', wifi['intf']))
+        cmd += ' {}'.format(get_conf_file('hostapd', wifi['intf']))
+    elif wifi['type'] == 'station':
+        cmd += ' --exec /sbin/wpa_supplicant'
+        # now pass arguments to hostapd binary
+        cmd += ' -- -s -B -D nl80211'
+        cmd += ' -P {}'.format(get_pid('wpa_supplicant', wifi['intf']))
+        cmd += ' -i {}'.format(wifi['intf'])
+        cmd += ' -c {}'.format(get_conf_file('wpa_supplicant', wifi['intf']))
 
     # execute assembled command
     subprocess_cmd(cmd)
-- 
cgit v1.2.3