4 Copyright 2009-2010 Jo-Philipp Wich <xm@subsignal.org>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
20 local type, pairs, ipairs, loadfile, table, tonumber, math, i18n
21 = type, pairs, ipairs, loadfile, table, tonumber, math, luci.i18n
23 local nxo = require "nixio"
24 local ipc = require "luci.ip"
25 local sys = require "luci.sys"
26 local utl = require "luci.util"
27 local dsp = require "luci.dispatcher"
28 local uci = require "luci.model.uci"
30 module "luci.model.network"
33 local ifs, brs, sws, uci_r, uci_s
35 function _list_del(c, s, o, r)
36 local val = uci_r:get(c, s, o)
39 if type(val) == "string" then
40 for val in val:gmatch("%S+") do
46 uci_r:set(c, s, o, table.concat(l, " "))
50 elseif type(val) == "table" then
51 for _, val in ipairs(val) do
65 function _list_add(c, s, o, a)
66 local val = uci_r:get(c, s, o) or ""
67 if type(val) == "string" then
69 for val in val:gmatch("%S+") do
75 uci_r:set(c, s, o, table.concat(l, " "))
76 elseif type(val) == "table" then
78 for _, val in ipairs(val) do
88 function _stror(s1, s2)
89 if not s1 or #s1 == 0 then
90 return s2 and #s2 > 0 and s2
99 elseif type(x) == "table" then
100 x = table.concat(x, " ")
103 return x:gmatch("%S+")
106 function _get(c, s, o)
107 return uci_r:get(c, s, o)
110 function _set(c, s, o, v)
112 if type(v) == "boolean" then v = v and "1" or "0" end
113 return uci_r:set(c, s, o, v)
115 return uci_r:del(c, s, o, v)
119 function _wifi_iface(x)
121 x:match("^wlan%d") or x:match("^wl%d") or x:match("^ath%d") or
122 x:match("^%w+%.network%d")
126 function _wifi_lookup(ifn)
127 -- got a radio#.network# pseudo iface, locate the corresponding section
128 local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
129 if radio and ifnidx then
133 ifnidx = tonumber(ifnidx)
134 uci_r:foreach("wireless", "wifi-iface",
136 if s.device == radio then
138 if num == ifnidx then
147 -- looks like wifi, try to locate the section via state vars
148 elseif _wifi_iface(ifn) then
151 uci_s:foreach("wireless", "wifi-iface",
153 if s.ifname == ifn then
163 function _iface_ignore(x)
165 x:match("^wmaster%d") or x:match("^wifi%d") or x:match("^hwsim%d") or
166 x:match("^imq%d") or x:match("^mon.wlan%d") or x:match("^6in4-%w") or
167 x:match("^3g-%w") or x:match("^ppp-%w") or x:match("^pppoe-%w") or
168 x:match("^pppoa-%w") or x == "lo"
173 function init(cursor)
174 uci_r = cursor or uci_r or uci.cursor()
175 uci_s = uci_r:substate()
181 -- read interface information
183 for n, i in ipairs(nxo.getifaddrs()) do
184 local name = i.name:match("[^:]+")
185 local prnt = name:match("^([^%.]+)%.")
187 if not _iface_ignore(name) then
188 ifs[name] = ifs[name] or {
189 idx = i.ifindex or n,
202 if i.family == "packet" then
203 ifs[name].flags = i.flags
204 ifs[name].stats = i.data
205 ifs[name].macaddr = i.addr
206 elseif i.family == "inet" then
207 ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
208 elseif i.family == "inet6" then
209 ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
214 -- read bridge informaton
216 for l in utl.execi("brctl show") do
217 if not l:match("STP") then
218 local r = utl.split(l, "%s+", nil, true)
224 ifnames = { ifs[r[4]] }
227 b.ifnames[1].bridge = b
231 b.ifnames[#b.ifnames+1] = ifs[r[2]]
232 b.ifnames[#b.ifnames].bridge = b
240 function save(self, ...)
245 function commit(self, ...)
250 function has_ipv6(self)
251 return nfs.access("/proc/net/ipv6_route")
254 function add_network(self, n, options)
255 if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_network(n) then
256 if uci_r:section("network", "interface", n, options) then
262 function get_network(self, n)
263 if n and uci_r:get("network", n) == "interface" then
268 function get_networks(self)
272 uci_r:foreach("network", "interface",
274 nls[s['.name']] = network(s['.name'])
278 for n in utl.kspairs(nls) do
279 nets[#nets+1] = nls[n]
285 function del_network(self, n)
286 local r = uci_r:delete("network", n)
288 uci_r:foreach("network", "alias",
290 if s.interface == n then
291 uci_r:delete("network", s['.name'])
295 uci_r:foreach("network", "route",
297 if s.interface == n then
298 uci_r:delete("network", s['.name'])
302 uci_r:foreach("network", "route6",
304 if s.interface == n then
305 uci_r:delete("network", s['.name'])
309 uci_r:foreach("wireless", "wifi-iface",
311 if s.network == n then
312 uci_r:delete("wireless", s['.name'], "network")
316 uci_r:delete("network", n)
321 function rename_network(self, old, new)
323 if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
324 r = uci_r:section("network", "interface", new, uci_r:get_all("network", old))
327 uci_r:foreach("network", "alias",
329 if s.interface == old then
330 uci_r:set("network", s['.name'], "interface", new)
334 uci_r:foreach("network", "route",
336 if s.interface == old then
337 uci_r:set("network", s['.name'], "interface", new)
341 uci_r:foreach("network", "route6",
343 if s.interface == old then
344 uci_r:set("network", s['.name'], "interface", new)
348 uci_r:foreach("wireless", "wifi-iface",
350 if s.network == old then
351 uci_r:set("wireless", s['.name'], "network", new)
355 uci_r:delete("network", old)
361 function get_interface(self, i)
362 if ifs[i] or _wifi_iface(i) then
367 uci_r:foreach("wireless", "wifi-iface",
370 num[s.device] = num[s.device] and num[s.device] + 1 or 1
371 if s['.name'] == i then
373 "%s.network%d" %{s.device, num[s.device] })
382 function get_interfaces(self)
386 -- find normal interfaces
387 for iface in utl.kspairs(ifs) do
388 if not _iface_ignore(iface) and not _wifi_iface(iface) then
389 ifaces[#ifaces+1] = interface(iface)
393 -- find wifi interfaces
396 uci_r:foreach("wireless", "wifi-iface",
399 num[s.device] = num[s.device] and num[s.device] + 1 or 1
400 local i = "%s.network%d" %{ s.device, num[s.device] }
401 wfs[i] = interface(i)
405 for iface in utl.kspairs(wfs) do
406 ifaces[#ifaces+1] = wfs[iface]
412 function ignore_interface(self, x)
413 return _iface_ignore(x)
416 function get_wifidev(self, dev)
417 if uci_r:get("wireless", dev) == "wifi-device" then
422 function get_wifidevs(self)
426 uci_r:foreach("wireless", "wifi-device",
427 function(s) wfd[#wfd+1] = s['.name'] end)
430 for _, dev in utl.vspairs(wfd) do
431 devs[#devs+1] = wifidev(dev)
437 function get_wifinet(self, net)
438 local wnet = _wifi_lookup(net)
444 function add_wifinet(self, net, options)
445 if type(options) == "table" and options.device and
446 uci_r:get("wireless", options.device) == "wifi-device"
448 local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
453 function del_wifinet(self, net)
454 local wnet = _wifi_lookup(net)
456 uci_r:delete("wireless", wnet)
463 network = utl.class()
465 function network.__init__(self, name)
469 function network._get(self, opt)
470 local v = uci_r:get("network", self.sid, opt)
471 if type(v) == "table" then
472 return table.concat(v, " ")
477 function network.get(self, opt)
478 return _get("network", self.sid, opt)
481 function network.set(self, opt, val)
482 return _set("network", self.sid, opt, val)
485 function network.ifname(self)
486 local p = self:proto()
487 if self:is_bridge() then
488 return "br-" .. self.sid
489 elseif self:is_virtual() then
490 return p .. "-" .. self.sid
492 local dev = self:_get("ifname") or
493 uci_r:get("network", self.sid, "ifname")
495 dev = dev and dev:match("%S+")
498 uci_r:foreach("wireless", "wifi-iface",
501 num[s.device] = num[s.device]
502 and num[s.device] + 1 or 1
504 if s.network == self.sid then
505 dev = "%s.network%d" %{ s.device, num[s.device] }
516 function network.device(self)
517 local dev = self:_get("device")
518 if not dev or dev:match("[^%w%-%.%s]") then
519 dev = uci_r:get("network", self.sid, "ifname")
524 function network.proto(self)
525 return self:_get("proto") or "none"
528 function network.type(self)
529 return self:_get("type")
532 function network.name(self)
536 function network.is_bridge(self)
537 return (self:type() == "bridge")
540 function network.is_virtual(self)
541 local p = self:proto()
543 p == "3g" or p == "6in4" or p == "ppp" or
544 p == "pppoe" or p == "pppoa"
548 function network.is_empty(self)
549 if self:is_virtual() then
554 if (self:_get("ifname") or ""):match("%S+") then
558 uci_r:foreach("wireless", "wifi-iface",
560 if s.network == self.sid then
570 function network.add_interface(self, ifname)
571 if not self:is_virtual() then
572 if type(ifname) ~= "string" then
573 ifname = ifname:name()
575 ifname = ifname:match("[^%s:]+")
578 -- remove the interface from all ifaces
579 uci_r:foreach("network", "interface",
581 _list_del("network", s['.name'], "ifname", ifname)
584 -- if its a wifi interface, change its network option
585 local wif = _wifi_lookup(ifname)
587 uci_r:set("wireless", wif, "network", self.sid)
589 -- add iface to our iface list
591 _list_add("network", self.sid, "ifname", ifname)
596 function network.del_interface(self, ifname)
597 if not self:is_virtual() then
598 if utl.instanceof(ifname, interface) then
599 ifname = ifname:name()
601 ifname = ifname:match("[^%s:]+")
604 -- if its a wireless interface, clear its network option
605 local wif = _wifi_lookup(ifname)
606 if wif then uci_r:delete("wireless", wif, "network") end
608 -- remove the interface
609 _list_del("network", self.sid, "ifname", ifname)
613 function network.get_interfaces(self)
617 if self:is_virtual() then
618 ifn = self:proto() .. "-" .. self.sid
619 ifaces = { interface(ifn) }
622 for ifn in _strlist(self:get("ifname")) do
623 ifn = ifn:match("[^:]+")
624 nfs[ifn] = interface(ifn)
627 for ifn in utl.kspairs(nfs) do
628 ifaces[#ifaces+1] = nfs[ifn]
633 uci_r:foreach("wireless", "wifi-iface",
636 num[s.device] = num[s.device] and num[s.device] + 1 or 1
637 if s.network == self.sid then
638 ifn = "%s.network%d" %{ s.device, num[s.device] }
639 wfs[ifn] = interface(ifn)
644 for ifn in utl.kspairs(wfs) do
645 ifaces[#ifaces+1] = wfs[ifn]
652 function network.contains_interface(self, ifname)
653 if type(ifname) ~= "string" then
654 ifname = ifname:name()
656 ifname = ifname:match("[^%s:]+")
660 if self:is_virtual() then
661 ifn = self:proto() .. "-" .. self.sid
664 for ifn in _strlist(self:get("ifname")) do
665 ifn = ifn:match("[^:]+")
666 if ifn == ifname then
671 local wif = _wifi_lookup(ifname)
673 return (uci_r:get("wireless", wif, "network") == self.sid)
680 function network.adminlink(self)
681 return dsp.build_url("admin", "network", "network", self.sid)
685 interface = utl.class()
686 function interface.__init__(self, ifname)
687 local wif = _wifi_lookup(ifname)
688 if wif then self.wif = wifinet(wif) end
690 self.ifname = self.ifname or ifname
691 self.dev = ifs[self.ifname]
694 function interface.name(self)
695 return self.wif and self.wif:ifname() or self.ifname
698 function interface.mac(self)
699 return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
702 function interface.ipaddrs(self)
703 return self.dev and self.dev.ipaddrs or { }
706 function interface.ip6addrs(self)
707 return self.dev and self.dev.ip6addrs or { }
710 function interface.type(self)
711 if self.wif or _wifi_iface(self.ifname) then
713 elseif brs[self.ifname] then
715 elseif sws[self.ifname] or self.ifname:match("%.") then
722 function interface.shortname(self)
725 self.wif:active_mode(),
726 self.wif:active_ssid() or self.wif:active_bssid()
733 function interface.get_i18n(self)
735 return "%s: %s %q" %{
736 i18n.translate("Wireless Network"),
737 self.wif:active_mode(),
738 self.wif:active_ssid() or self.wif:active_bssid()
741 return "%s: %q" %{ self:get_type_i18n(), self:name() }
745 function interface.get_type_i18n(self)
746 local x = self:type()
748 return i18n.translate("Wireless Adapter")
749 elseif x == "bridge" then
750 return i18n.translate("Bridge")
751 elseif x == "switch" then
752 return i18n.translate("Ethernet Switch")
754 return i18n.translate("Ethernet Adapter")
758 function interface.adminlink(self)
760 return self.wif:adminlink()
764 function interface.ports(self)
768 for _, iface in ipairs(self.br.ifnames) do
769 ifaces[#ifaces+1] = interface(iface.name)
775 function interface.bridge_id(self)
783 function interface.bridge_stp(self)
791 function interface.is_up(self)
793 return self.wif:is_up()
795 return self.dev and self.dev.flags and self.dev.flags.up or false
799 function interface.is_bridge(self)
800 return (self:type() == "bridge")
803 function interface.is_bridgeport(self)
804 return self.dev and self.dev.bridge and true or false
807 function interface.tx_bytes(self)
808 return self.dev and self.dev.stats
809 and self.dev.stats.tx_bytes or 0
812 function interface.rx_bytes(self)
813 return self.dev and self.dev.stats
814 and self.dev.stats.rx_bytes or 0
817 function interface.tx_packets(self)
818 return self.dev and self.dev.stats
819 and self.dev.stats.tx_packets or 0
822 function interface.rx_packets(self)
823 return self.dev and self.dev.stats
824 and self.dev.stats.rx_packets or 0
827 function interface.get_network(self)
828 if self.dev and self.dev.network then
829 self.network = _M:get_network(self.dev.network)
832 if not self.network then
834 for _, net in ipairs(_M:get_networks()) do
835 if net:contains_interface(self.ifname) then
845 function interface.get_wifinet(self)
850 wifidev = utl.class()
851 function wifidev.__init__(self, dev)
855 function wifidev.get(self, opt)
856 return _get("wireless", self.sid, opt)
859 function wifidev.set(self, opt, val)
860 return _set("wireless", self.sid, opt, val)
863 function wifidev.name(self)
867 function wifidev.is_up(self)
870 uci_s:foreach("wireless", "wifi-iface",
872 if s.device == self.sid then
883 function wifidev.get_wifinet(self, net)
884 if uci_r:get("wireless", net) == "wifi-iface" then
887 local wnet = _wifi_lookup(net)
894 function wifidev.get_wifinets(self)
897 uci_r:foreach("wireless", "wifi-iface",
899 if s.device == self.sid then
900 nets[#nets+1] = wifinet(s['.name'])
907 function wifidev.add_wifinet(self, options)
908 options = options or { }
909 options.device = self.sid
911 local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
913 return wifinet(wnet, options)
917 function wifidev.del_wifinet(self, net)
918 if utl.instanceof(net, wifinet) then
920 elseif uci_r:get("wireless", net) ~= "wifi-iface" then
921 net = _wifi_lookup(net)
924 if net and uci_r:get("wireless", net, "device") == self.sid then
925 uci_r:delete("wireless", net)
933 wifinet = utl.class()
934 function wifinet.__init__(self, net, data)
937 local dev = uci_s:get("wireless", self.sid, "ifname")
940 uci_r:foreach("wireless", "wifi-iface",
943 num[s.device] = num[s.device] and num[s.device] + 1 or 1
944 if s['.name'] == self.sid then
945 dev = "%s.network%d" %{ s.device, num[s.device] }
953 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
954 self.iwdata = data or uci_s:get_all("wireless", self.sid) or
955 uci_r:get_all("wireless", self.sid) or { }
958 function wifinet.get(self, opt)
959 return _get("wireless", self.sid, opt)
962 function wifinet.set(self, opt, val)
963 return _set("wireless", self.sid, opt, val)
966 function wifinet.mode(self)
967 return uci_s:get("wireless", self.sid, "mode") or "ap"
970 function wifinet.ssid(self)
971 return uci_s:get("wireless", self.sid, "ssid")
974 function wifinet.bssid(self)
975 return uci_s:get("wireless", self.sid, "bssid")
978 function wifinet.network(self)
979 return uci_s:get("wifinet", self.sid, "network")
982 function wifinet.name(self)
986 function wifinet.ifname(self)
987 return self.iwinfo.ifname or self.wdev
990 function wifinet.get_device(self)
991 if self.iwdata.device then
992 return wifidev(self.iwdata.device)
996 function wifinet.is_up(self)
997 return (self.iwdata.up == "1")
1000 function wifinet.active_mode(self)
1001 local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1003 if m == "ap" then m = "AP"
1004 elseif m == "sta" then m = "Client"
1005 elseif m == "adhoc" then m = "Ad-Hoc"
1006 elseif m == "mesh" then m = "Mesh"
1007 elseif m == "monitor" then m = "Monitor"
1013 function wifinet.active_mode_i18n(self)
1014 return i18n.translate(self:active_mode())
1017 function wifinet.active_ssid(self)
1018 return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1021 function wifinet.active_bssid(self)
1022 return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1025 function wifinet.active_encryption(self)
1026 local enc = self.iwinfo and self.iwinfo.encryption
1027 return enc and enc.description or "-"
1030 function wifinet.assoclist(self)
1031 return self.iwinfo.assoclist or { }
1034 function wifinet.frequency(self)
1035 local freq = self.iwinfo.frequency
1036 if freq and freq > 0 then
1037 return "%.03f" % (freq / 1000)
1041 function wifinet.bitrate(self)
1042 local rate = self.iwinfo.bitrate
1043 if rate and rate > 0 then
1044 return (rate / 1000)
1048 function wifinet.channel(self)
1049 return self.iwinfo.channel or
1050 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1053 function wifinet.signal(self)
1054 return self.iwinfo.signal or 0
1057 function wifinet.noise(self)
1058 return self.iwinfo.noise or 0
1061 function wifinet.signal_level(self, s, n)
1062 if self:active_bssid() ~= "00:00:00:00:00:00" then
1063 local signal = s or self:signal()
1064 local noise = n or self:noise()
1066 if signal < 0 and noise < 0 then
1067 local snr = -1 * (noise - signal)
1068 return math.floor(snr / 5)
1077 function wifinet.signal_percent(self)
1078 local qc = self.iwinfo.quality or 0
1079 local qm = self.iwinfo.quality_max or 0
1081 if qc > 0 and qm > 0 then
1082 return math.floor((100 / qm) * qc)
1088 function wifinet.shortname(self)
1090 i18n.translate(self:active_mode()),
1091 self:active_ssid() or self:active_bssid()
1095 function wifinet.get_i18n(self)
1096 return "%s: %s %q (%s)" %{
1097 i18n.translate("Wireless Network"),
1098 i18n.translate(self:active_mode()),
1099 self:active_ssid() or self:active_bssid(),
1104 function wifinet.adminlink(self)
1105 return dsp.build_url("admin", "network", "wireless",
1106 self.iwdata.device, self.wdev)
1109 function wifinet.get_network(self)
1110 if uci_r:get("network", self.iwdata.network) == "interface" then
1111 return network(self.iwdata.network)
1115 function wifinet.get_interface(self)
1116 return interface(self:ifname())