Skip to content

Commit bd0db29

Browse files
pandafynemesifier
andauthored
[change] OpenWrt: simplified bridged wifi interface #191
Made it easier for users to bridge wifi interfaces without having to know they have to define the "network" attribute in the WiFi interface. Closes #191 Co-authored-by: Federico Capoano <f.capoano@openwisp.io>
1 parent 9bceb18 commit bd0db29

3 files changed

Lines changed: 123 additions & 18 deletions

File tree

docs/source/backends/openwrt.rst

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,8 @@ UCI output::
745745
Wireless attached to a different network
746746
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
747747

748-
In some cases you might want to attach a wireless interface to a different network,
749-
for example, you might want to attach a wireless interface to a bridge:
748+
In most cases you want to bridge a wireless interface to a different network,
749+
usually the LAN bridge:
750750

751751
.. code-block:: python
752752
@@ -763,8 +763,8 @@ for example, you might want to attach a wireless interface to a bridge:
763763
"radio": "radio0",
764764
"mode": "access_point",
765765
"ssid": "wifi service",
766-
# the wireless interface will be attached to the "lan" network
767-
"network": ["lan"]
766+
# "network": ["lan"] this proeprty can be omitted
767+
# but may be overridden if needed
768768
}
769769
},
770770
{
@@ -776,9 +776,7 @@ for example, you might want to attach a wireless interface to a bridge:
776776
],
777777
"addresses": [
778778
{
779-
"address": "192.168.0.2",
780-
"mask": 24,
781-
"proto": "static",
779+
"proto": "dhcp",
782780
"family": "ipv4"
783781
}
784782
]
@@ -800,9 +798,7 @@ Will be rendered as follows::
800798

801799
config interface 'lan'
802800
option ifname 'eth0 wlan0'
803-
option ipaddr '192.168.0.2'
804-
option netmask '255.255.255.0'
805-
option proto 'static'
801+
option proto 'dhcp'
806802
option type 'bridge'
807803

808804
package wireless

netjsonconfig/backends/openwrt/converters/wireless.py

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ class Wireless(OpenWrtConverter):
66
intermediate_key = 'wireless'
77
_uci_types = ['wifi-iface']
88

9+
def to_intermediate(self):
10+
self._track_bridged_wifi()
11+
return super().to_intermediate()
12+
913
def to_intermediate_loop(self, block, result, index=None):
1014
wireless = self.__intermediate_wireless(block)
1115
if wireless:
@@ -62,9 +66,16 @@ def __intermediate_wireless(self, interface):
6266
# to its defining interface
6367
# but this behaviour can be overridden
6468
if not wireless.get('network'):
65-
# get network, default to ifname
66-
network = interface.get('network', interface['name'])
67-
wireless['network'] = [network]
69+
# try to automatically determine whether
70+
# we should attach this interface to a bridge
71+
try:
72+
bridges = self._bridged_wifi[interface['name']]
73+
except KeyError:
74+
# default to the value of "network" or inteface name
75+
network = [interface.get('network', interface['name'])]
76+
else:
77+
network = bridges
78+
wireless['network'] = network
6879
wireless['network'] = (
6980
' '.join(wireless['network']).replace('.', '_').replace('-', '_')
7081
)
@@ -280,3 +291,54 @@ def __get_netjson_interface(self, wifi):
280291
if interface['name'] == wifi['ifname']:
281292
interface['type'] = 'wireless'
282293
return interface
294+
295+
def to_netjson_clean(self, intermediate_data):
296+
result = super().to_netjson_clean(intermediate_data)
297+
return self.__fix_netjson_network(result)
298+
299+
def __fix_netjson_network(self, result):
300+
"""
301+
Figures out whether it should remove the network attribute
302+
From the netjson wifi interface (because it's redundant)
303+
"""
304+
self._track_bridged_wifi(self.backend._intermediate_copy)
305+
for index, interface in enumerate(result):
306+
try:
307+
bridges = self._bridged_wifi[interface['ifname']]
308+
except KeyError:
309+
continue
310+
else:
311+
if bridges == interface.get('network', '').split(' '):
312+
del result[index]['network']
313+
return result
314+
315+
def _track_bridged_wifi(self, intermediate_data=None):
316+
"""
317+
Keeps track of wireless interfaces which are members of
318+
bridges in order to automatically determine the "network"
319+
attribute value of the UCI or NetJSON configuration.
320+
"""
321+
self._bridged_wifi = {}
322+
if not intermediate_data:
323+
intermediate_data = self.intermediate_data
324+
interfaces = intermediate_data.get('network', [])
325+
# Create a mapping of physical interface to bride interface name
326+
for interface in interfaces:
327+
if interface.get('type', None) != 'bridge':
328+
continue
329+
# Get list of bridge members
330+
try:
331+
bridge_members = interface.get('ifname', None).split(' ')
332+
except AttributeError:
333+
# Bridge interface does not contain bridge members.
334+
# Bridge is empty.
335+
continue
336+
bridge_name = interface['.name']
337+
for physical_interface in bridge_members:
338+
# A physical interface can be a member of multiple
339+
# bridges. Hence, we create a list of bridge interfaces
340+
# for every physical interface.
341+
if physical_interface not in self._bridged_wifi:
342+
self._bridged_wifi[physical_interface] = [bridge_name]
343+
elif bridge_name not in self._bridged_wifi[physical_interface]:
344+
self._bridged_wifi[physical_interface].append(bridge_name)

tests/openwrt/test_wireless.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def test_parse_multiple_wifi(self):
200200
option device 'radio0'
201201
option ifname 'wlan0'
202202
option mode 'ap'
203-
option network 'wlan0'
203+
option network 'br_lan'
204204
option ssid 'open'
205205
"""
206206

@@ -211,7 +211,8 @@ def test_render_wifi_bridge(self):
211211

212212
def test_parse_wifi_bridge(self):
213213
o = OpenWrt(native=self._wifi_bridge_uci)
214-
self.assertEqual(o.config, self._wifi_bridge_netjson)
214+
wifi_bridge_netjson = self._wifi_bridge_netjson.copy()
215+
self.assertEqual(o.config, wifi_bridge_netjson)
215216

216217
_wifi_networks_netjson = {
217218
"interfaces": [
@@ -663,7 +664,6 @@ def test_maclist_format(self):
663664
"wireless": {
664665
"mode": "station",
665666
"radio": "radio0",
666-
"network": ["wds_bridge"],
667667
"ssid": "FreeRomaWifi",
668668
"bssid": "C0:4A:00:2D:05:FD",
669669
"wds": True,
@@ -676,7 +676,6 @@ def test_maclist_format(self):
676676
"wireless": {
677677
"mode": "access_point",
678678
"radio": "radio1",
679-
"network": ["wds_bridge"],
680679
"ssid": "FreeRomaWifi",
681680
},
682681
},
@@ -801,7 +800,6 @@ def test_parse_access_point_80211r(self):
801800
"radio": "radio0",
802801
"mode": "802.11s",
803802
"mesh_id": "ninux",
804-
"network": ["lan"],
805803
},
806804
},
807805
{
@@ -1042,3 +1040,52 @@ def test_render_wifi_custom_id(self):
10421040
def test_parse_wifi_custom_id(self):
10431041
o = OpenWrt(native=self._custom_id_uci)
10441042
self.assertEqual(o.config, self._custom_id_netjson)
1043+
1044+
_wifi_simplified_bridge_netjson = {
1045+
"interfaces": [
1046+
{
1047+
"name": "br-lan",
1048+
"network": "lan",
1049+
"type": "bridge",
1050+
"bridge_members": ["eth0", "wlan0"],
1051+
},
1052+
{
1053+
"name": "wlan0",
1054+
"type": "wireless",
1055+
"wireless": {
1056+
"radio": "radio0",
1057+
"mode": "access_point",
1058+
"ssid": "open",
1059+
},
1060+
},
1061+
]
1062+
}
1063+
_wifi_simplified_bridge_uci = """package network
1064+
1065+
config interface 'lan'
1066+
option ifname 'eth0 wlan0'
1067+
option proto 'none'
1068+
option type 'bridge'
1069+
1070+
config interface 'wlan0'
1071+
option ifname 'wlan0'
1072+
option proto 'none'
1073+
1074+
package wireless
1075+
1076+
config wifi-iface 'wifi_wlan0'
1077+
option device 'radio0'
1078+
option ifname 'wlan0'
1079+
option mode 'ap'
1080+
option network 'lan'
1081+
option ssid 'open'
1082+
"""
1083+
1084+
def test_render_simplified_wifi_bridge(self):
1085+
o = OpenWrt(self._wifi_simplified_bridge_netjson)
1086+
expected = self._tabs(self._wifi_simplified_bridge_uci)
1087+
self.assertEqual(o.render(), expected)
1088+
1089+
def test_parse_simplified_wifi_bridge(self):
1090+
o = OpenWrt(native=self._wifi_simplified_bridge_uci)
1091+
self.assertEqual(o.config, self._wifi_simplified_bridge_netjson)

0 commit comments

Comments
 (0)