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, next, pairs, ipairs, loadfile, table, tonumber, math, i18n
21 = type, next, 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
96 function _get(c, s, o)
97 return uci_r:get(c, s, o)
100 function _set(c, s, o, v)
102 if type(v) == "boolean" then v = v and "1" or "0" end
103 return uci_r:set(c, s, o, v)
105 return uci_r:delete(c, s, o)
109 function _wifi_iface(x)
111 x:match("^wlan%d") or x:match("^wl%d") or x:match("^ath%d") or
112 x:match("^%w+%.network%d")
116 function _wifi_lookup(ifn)
117 -- got a radio#.network# pseudo iface, locate the corresponding section
118 local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
119 if radio and ifnidx then
123 ifnidx = tonumber(ifnidx)
124 uci_r:foreach("wireless", "wifi-iface",
126 if s.device == radio then
128 if num == ifnidx then
137 -- looks like wifi, try to locate the section via state vars
138 elseif _wifi_iface(ifn) then
141 uci_s:foreach("wireless", "wifi-iface",
143 if s.ifname == ifn then
153 function _iface_virtual(x)
155 x:match("^6in4-%w") or x:match("^6to4-%w") or x:match("^3g-%w") or
156 x:match("^ppp-%w") or x:match("^pppoe-%w") or x:match("^pppoa-%w") or
161 function _iface_ignore(x)
163 x:match("^wmaster%d") or x:match("^wifi%d") or x:match("^hwsim%d") or
164 x:match("^imq%d") or x:match("^mon.wlan%d") or
165 x == "sit0" or x == "lo" or _iface_virtual(x)
170 function init(cursor)
171 uci_r = cursor or uci_r or uci.cursor()
172 uci_s = uci_r:substate()
178 -- read interface information
180 for n, i in ipairs(nxo.getifaddrs()) do
181 local name = i.name:match("[^:]+")
182 local prnt = name:match("^([^%.]+)%.")
184 if _iface_virtual(name) or not _iface_ignore(name) then
185 ifs[name] = ifs[name] or {
186 idx = i.ifindex or n,
199 if i.family == "packet" then
200 ifs[name].flags = i.flags
201 ifs[name].stats = i.data
202 ifs[name].macaddr = i.addr
203 elseif i.family == "inet" then
204 ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
205 elseif i.family == "inet6" then
206 ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
211 -- read bridge informaton
213 for l in utl.execi("brctl show") do
214 if not l:match("STP") then
215 local r = utl.split(l, "%s+", nil, true)
221 ifnames = { ifs[r[4]] }
224 b.ifnames[1].bridge = b
228 b.ifnames[#b.ifnames+1] = ifs[r[2]]
229 b.ifnames[#b.ifnames].bridge = b
237 function save(self, ...)
242 function commit(self, ...)
247 function has_ipv6(self)
248 return nfs.access("/proc/net/ipv6_route")
251 function add_network(self, n, options)
252 local oldnet = self:get_network(n)
253 if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
254 if uci_r:section("network", "interface", n, options) then
257 elseif oldnet and oldnet:is_empty() then
260 for k, v in pairs(options) do
268 function get_network(self, n)
269 if n and uci_r:get("network", n) == "interface" then
274 function get_networks(self)
278 uci_r:foreach("network", "interface",
280 nls[s['.name']] = network(s['.name'])
284 for n in utl.kspairs(nls) do
285 nets[#nets+1] = nls[n]
291 function del_network(self, n)
292 local r = uci_r:delete("network", n)
294 uci_r:delete_all("network", "alias",
295 function(s) return (s.interface == n) end)
297 uci_r:delete_all("network", "route",
298 function(s) return (s.interface == n) end)
300 uci_r:delete_all("network", "route6",
301 function(s) return (s.interface == n) end)
303 uci_r:foreach("wireless", "wifi-iface",
305 if s.network == n then
306 uci_r:delete("wireless", s['.name'], "network")
313 function rename_network(self, old, new)
315 if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
316 r = uci_r:section("network", "interface", new, uci_r:get_all("network", old))
319 uci_r:foreach("network", "alias",
321 if s.interface == old then
322 uci_r:set("network", s['.name'], "interface", new)
326 uci_r:foreach("network", "route",
328 if s.interface == old then
329 uci_r:set("network", s['.name'], "interface", new)
333 uci_r:foreach("network", "route6",
335 if s.interface == old then
336 uci_r:set("network", s['.name'], "interface", new)
340 uci_r:foreach("wireless", "wifi-iface",
342 if s.network == old then
343 uci_r:set("wireless", s['.name'], "network", new)
347 uci_r:delete("network", old)
353 function get_interface(self, i)
354 if ifs[i] or _wifi_iface(i) then
359 uci_r:foreach("wireless", "wifi-iface",
362 num[s.device] = num[s.device] and num[s.device] + 1 or 1
363 if s['.name'] == i then
365 "%s.network%d" %{s.device, num[s.device] })
374 function get_interfaces(self)
380 -- find normal interfaces
381 uci_r:foreach("network", "interface",
383 for iface in utl.imatch(s.ifname) do
384 if not _iface_ignore(iface) and not _wifi_iface(iface) then
386 nfs[iface] = interface(iface)
391 for iface in utl.kspairs(ifs) do
392 if not (seen[iface] or _iface_ignore(iface) or _wifi_iface(iface)) then
393 nfs[iface] = interface(iface)
397 for iface in utl.kspairs(nfs) do
398 ifaces[#ifaces+1] = nfs[iface]
401 -- find wifi interfaces
404 uci_r:foreach("wireless", "wifi-iface",
407 num[s.device] = num[s.device] and num[s.device] + 1 or 1
408 local i = "%s.network%d" %{ s.device, num[s.device] }
409 wfs[i] = interface(i)
413 for iface in utl.kspairs(wfs) do
414 ifaces[#ifaces+1] = wfs[iface]
420 function ignore_interface(self, x)
421 return _iface_ignore(x)
424 function get_wifidev(self, dev)
425 if uci_r:get("wireless", dev) == "wifi-device" then
430 function get_wifidevs(self)
434 uci_r:foreach("wireless", "wifi-device",
435 function(s) wfd[#wfd+1] = s['.name'] end)
438 for _, dev in utl.vspairs(wfd) do
439 devs[#devs+1] = wifidev(dev)
445 function get_wifinet(self, net)
446 local wnet = _wifi_lookup(net)
452 function add_wifinet(self, net, options)
453 if type(options) == "table" and options.device and
454 uci_r:get("wireless", options.device) == "wifi-device"
456 local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
461 function del_wifinet(self, net)
462 local wnet = _wifi_lookup(net)
464 uci_r:delete("wireless", wnet)
471 network = utl.class()
473 function network.__init__(self, name)
477 function network._get(self, opt)
478 local v = uci_r:get("network", self.sid, opt)
479 if type(v) == "table" then
480 return table.concat(v, " ")
485 function network._ip(self, opt, family, list)
486 local ip = uci_s:get("network", self.sid, opt)
487 local fc = (family == 6) and ipc.IPv6 or ipc.IPv4
491 for ip in utl.imatch(ip) do
493 if ip then l[#l+1] = ip:string() end
498 return ip and ip:string()
503 function network.get(self, opt)
504 return _get("network", self.sid, opt)
507 function network.set(self, opt, val)
508 return _set("network", self.sid, opt, val)
511 function network.ifname(self)
512 local p = self:proto()
513 if self:is_bridge() then
514 return "br-" .. self.sid
515 elseif self:proto() == "relay" then
516 return uci_s:get("network", self.sid, "up") == "1" and "lo" or nil
517 elseif self:is_virtual() then
518 return p .. "-" .. self.sid
521 local dev = uci_r:get("network", self.sid, "ifname") or
522 uci_s:get("network", self.sid, "ifname")
524 dev = (type(dev) == "table") and dev[1] or dev
525 dev = (dev ~= nil) and dev:match("%S+")
528 uci_r:foreach("wireless", "wifi-iface",
531 num[s.device] = num[s.device]
532 and num[s.device] + 1 or 1
534 if s.network == self.sid then
535 dev = "%s.network%d" %{ s.device, num[s.device] }
546 function network.device(self)
547 local dev = uci_r:get("network", self.sid, "device") or
548 uci_s:get("network", self.sid, "device")
550 dev = (type(dev) == "table") and dev[1] or dev
552 if not dev or dev:match("[^%w%-%.%s]") then
553 dev = uci_r:get("network", self.sid, "ifname") or
554 uci_s:get("network", self.sid, "ifname")
556 dev = (type(dev) == "table") and dev[1] or dev
559 for dev in utl.imatch(dev) do
564 function network.proto(self)
565 return self:_get("proto") or "none"
568 function network.type(self)
569 return self:_get("type")
572 function network.name(self)
576 function network.uptime(self)
577 local cnt = tonumber(uci_s:get("network", self.sid, "connect_time"))
579 return nxo.sysinfo().uptime - cnt
585 function network.expires(self)
586 local a = tonumber(uci_s:get("network", self.sid, "lease_acquired"))
587 local l = tonumber(uci_s:get("network", self.sid, "lease_lifetime"))
589 l = l - (nxo.sysinfo().uptime - a)
590 return l > 0 and l or 0
595 function network.metric(self)
596 return tonumber(uci_s:get("network", self.sid, "metric")) or 0
599 function network.ipaddr(self)
600 return self:_ip("ipaddr", 4)
603 function network.netmask(self)
604 return self:_ip("netmask", 4)
607 function network.gwaddr(self)
608 return self:_ip("gateway", 4)
611 function network.dnsaddrs(self)
612 return self:_ip("dns", 4, true)
615 function network.ip6addr(self)
616 local ip6 = self:_ip("ip6addr", 6)
618 local ifc = ifs[self:ifname()]
619 if ifc and ifc.ip6addrs then
621 for _, a in ipairs(ifc.ip6addrs) do
622 if not a:is6linklocal() then
632 function network.gw6addr(self)
633 local ip6 = self:_ip("ip6gw", 6)
635 local dr6 = sys.net.defaultroute6()
636 if dr6 and dr6.device == self:ifname() then
637 return dr6.nexthop:string()
643 function network.dns6addrs(self)
644 return self:_ip("dns", 6, true)
647 function network.is_bridge(self)
648 return (self:type() == "bridge")
651 function network.is_virtual(self)
652 local p = self:proto()
654 p == "3g" or p == "6in4" or p == "6to4" or p == "ppp" or
655 p == "pppoe" or p == "pppoa" or p == "relay"
659 function network.is_empty(self)
660 if self:is_virtual() then
665 if (self:_get("ifname") or ""):match("%S+") then
669 uci_r:foreach("wireless", "wifi-iface",
671 if s.network == self.sid then
681 function network.add_interface(self, ifname)
682 if not self:is_virtual() then
683 if type(ifname) ~= "string" then
684 ifname = ifname:name()
686 ifname = ifname:match("[^%s:]+")
689 -- remove the interface from all ifaces
690 uci_r:foreach("network", "interface",
692 _list_del("network", s['.name'], "ifname", ifname)
695 -- if its a wifi interface, change its network option
696 local wif = _wifi_lookup(ifname)
698 uci_r:set("wireless", wif, "network", self.sid)
700 -- add iface to our iface list
702 _list_add("network", self.sid, "ifname", ifname)
707 function network.del_interface(self, ifname)
708 if not self:is_virtual() then
709 if utl.instanceof(ifname, interface) then
710 ifname = ifname:name()
712 ifname = ifname:match("[^%s:]+")
715 -- if its a wireless interface, clear its network option
716 local wif = _wifi_lookup(ifname)
717 if wif then uci_r:delete("wireless", wif, "network") end
719 -- remove the interface
720 _list_del("network", self.sid, "ifname", ifname)
724 function network.get_interfaces(self)
728 if self:is_virtual() then
729 ifn = self:proto() .. "-" .. self.sid
730 ifaces = { interface(ifn) }
733 for ifn in utl.imatch(self:get("ifname")) do
734 ifn = ifn:match("[^:]+")
735 nfs[ifn] = interface(ifn)
738 for ifn in utl.kspairs(nfs) do
739 ifaces[#ifaces+1] = nfs[ifn]
744 uci_r:foreach("wireless", "wifi-iface",
747 num[s.device] = num[s.device] and num[s.device] + 1 or 1
748 if s.network == self.sid then
749 ifn = "%s.network%d" %{ s.device, num[s.device] }
750 wfs[ifn] = interface(ifn)
755 for ifn in utl.kspairs(wfs) do
756 ifaces[#ifaces+1] = wfs[ifn]
763 function network.contains_interface(self, ifname)
764 if type(ifname) ~= "string" then
765 ifname = ifname:name()
767 ifname = ifname:match("[^%s:]+")
771 if self:is_virtual() then
772 ifn = self:proto() .. "-" .. self.sid
775 for ifn in utl.imatch(self:get("ifname")) do
776 ifn = ifn:match("[^:]+")
777 if ifn == ifname then
782 local wif = _wifi_lookup(ifname)
784 return (uci_r:get("wireless", wif, "network") == self.sid)
791 function network.adminlink(self)
792 return dsp.build_url("admin", "network", "network", self.sid)
796 interface = utl.class()
797 function interface.__init__(self, ifname)
798 local wif = _wifi_lookup(ifname)
799 if wif then self.wif = wifinet(wif) end
801 self.ifname = self.ifname or ifname
802 self.dev = ifs[self.ifname]
805 function interface.name(self)
806 return self.wif and self.wif:ifname() or self.ifname
809 function interface.mac(self)
810 return self.dev and self.dev.macaddr or "00:00:00:00:00:00"
813 function interface.ipaddrs(self)
814 return self.dev and self.dev.ipaddrs or { }
817 function interface.ip6addrs(self)
818 return self.dev and self.dev.ip6addrs or { }
821 function interface.type(self)
822 if self.wif or _wifi_iface(self.ifname) then
824 elseif brs[self.ifname] then
826 elseif sws[self.ifname] or self.ifname:match("%.") then
833 function interface.shortname(self)
836 self.wif:active_mode(),
837 self.wif:active_ssid() or self.wif:active_bssid()
844 function interface.get_i18n(self)
846 return "%s: %s %q" %{
847 i18n.translate("Wireless Network"),
848 self.wif:active_mode(),
849 self.wif:active_ssid() or self.wif:active_bssid()
852 return "%s: %q" %{ self:get_type_i18n(), self:name() }
856 function interface.get_type_i18n(self)
857 local x = self:type()
859 return i18n.translate("Wireless Adapter")
860 elseif x == "bridge" then
861 return i18n.translate("Bridge")
862 elseif x == "switch" then
863 return i18n.translate("Ethernet Switch")
865 return i18n.translate("Ethernet Adapter")
869 function interface.adminlink(self)
871 return self.wif:adminlink()
875 function interface.ports(self)
879 for _, iface in ipairs(self.br.ifnames) do
880 ifaces[#ifaces+1] = interface(iface.name)
886 function interface.bridge_id(self)
894 function interface.bridge_stp(self)
902 function interface.is_up(self)
904 return self.wif:is_up()
906 return self.dev and self.dev.flags and self.dev.flags.up or false
910 function interface.is_bridge(self)
911 return (self:type() == "bridge")
914 function interface.is_bridgeport(self)
915 return self.dev and self.dev.bridge and true or false
918 function interface.tx_bytes(self)
919 return self.dev and self.dev.stats
920 and self.dev.stats.tx_bytes or 0
923 function interface.rx_bytes(self)
924 return self.dev and self.dev.stats
925 and self.dev.stats.rx_bytes or 0
928 function interface.tx_packets(self)
929 return self.dev and self.dev.stats
930 and self.dev.stats.tx_packets or 0
933 function interface.rx_packets(self)
934 return self.dev and self.dev.stats
935 and self.dev.stats.rx_packets or 0
938 function interface.get_network(self)
939 if self.dev and self.dev.network then
940 self.network = _M:get_network(self.dev.network)
943 if not self.network then
945 for _, net in ipairs(_M:get_networks()) do
946 if net:contains_interface(self.ifname) or
947 net:ifname() == self.ifname
958 function interface.get_wifinet(self)
963 wifidev = utl.class()
964 function wifidev.__init__(self, dev)
966 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
969 function wifidev.get(self, opt)
970 return _get("wireless", self.sid, opt)
973 function wifidev.set(self, opt, val)
974 return _set("wireless", self.sid, opt, val)
977 function wifidev.name(self)
981 function wifidev.hwmodes(self)
982 local l = self.iwinfo.hwmodelist
983 if l and next(l) then
986 return { b = true, g = true }
990 function wifidev.get_i18n(self)
992 if self.iwinfo.type == "wl" then
994 elseif self.iwinfo.type == "madwifi" then
999 local l = self:hwmodes()
1000 if l.a then m = m .. "a" end
1001 if l.b then m = m .. "b" end
1002 if l.g then m = m .. "g" end
1003 if l.n then m = m .. "n" end
1005 return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1008 function wifidev.is_up(self)
1011 uci_s:foreach("wireless", "wifi-iface",
1013 if s.device == self.sid then
1024 function wifidev.get_wifinet(self, net)
1025 if uci_r:get("wireless", net) == "wifi-iface" then
1028 local wnet = _wifi_lookup(net)
1030 return wifinet(wnet)
1035 function wifidev.get_wifinets(self)
1038 uci_r:foreach("wireless", "wifi-iface",
1040 if s.device == self.sid then
1041 nets[#nets+1] = wifinet(s['.name'])
1048 function wifidev.add_wifinet(self, options)
1049 options = options or { }
1050 options.device = self.sid
1052 local wnet = uci_r:section("wireless", "wifi-iface", nil, options)
1054 return wifinet(wnet, options)
1058 function wifidev.del_wifinet(self, net)
1059 if utl.instanceof(net, wifinet) then
1061 elseif uci_r:get("wireless", net) ~= "wifi-iface" then
1062 net = _wifi_lookup(net)
1065 if net and uci_r:get("wireless", net, "device") == self.sid then
1066 uci_r:delete("wireless", net)
1074 wifinet = utl.class()
1075 function wifinet.__init__(self, net, data)
1080 uci_r:foreach("wireless", "wifi-iface",
1083 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1084 if s['.name'] == self.sid then
1085 netid = "%s.network%d" %{ s.device, num[s.device] }
1091 local dev = uci_s:get("wireless", self.sid, "ifname") or netid
1095 self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1096 self.iwdata = data or uci_s:get_all("wireless", self.sid) or
1097 uci_r:get_all("wireless", self.sid) or { }
1100 function wifinet.get(self, opt)
1101 return _get("wireless", self.sid, opt)
1104 function wifinet.set(self, opt, val)
1105 return _set("wireless", self.sid, opt, val)
1108 function wifinet.mode(self)
1109 return uci_s:get("wireless", self.sid, "mode") or "ap"
1112 function wifinet.ssid(self)
1113 return uci_s:get("wireless", self.sid, "ssid")
1116 function wifinet.bssid(self)
1117 return uci_s:get("wireless", self.sid, "bssid")
1120 function wifinet.network(self)
1121 return uci_s:get("wifinet", self.sid, "network")
1124 function wifinet.name(self)
1128 function wifinet.ifname(self)
1129 local ifname = self.iwinfo.ifname
1130 if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1136 function wifinet.get_device(self)
1137 if self.iwdata.device then
1138 return wifidev(self.iwdata.device)
1142 function wifinet.is_up(self)
1143 return (self.iwdata.up == "1")
1146 function wifinet.active_mode(self)
1147 local m = _stror(self.iwinfo.mode, self.iwdata.mode) or "ap"
1149 if m == "ap" then m = "Master"
1150 elseif m == "sta" then m = "Client"
1151 elseif m == "adhoc" then m = "Ad-Hoc"
1152 elseif m == "mesh" then m = "Mesh"
1153 elseif m == "monitor" then m = "Monitor"
1159 function wifinet.active_mode_i18n(self)
1160 return i18n.translate(self:active_mode())
1163 function wifinet.active_ssid(self)
1164 return _stror(self.iwinfo.ssid, self.iwdata.ssid)
1167 function wifinet.active_bssid(self)
1168 return _stror(self.iwinfo.bssid, self.iwdata.bssid) or "00:00:00:00:00:00"
1171 function wifinet.active_encryption(self)
1172 local enc = self.iwinfo and self.iwinfo.encryption
1173 return enc and enc.description or "-"
1176 function wifinet.assoclist(self)
1177 return self.iwinfo.assoclist or { }
1180 function wifinet.frequency(self)
1181 local freq = self.iwinfo.frequency
1182 if freq and freq > 0 then
1183 return "%.03f" % (freq / 1000)
1187 function wifinet.bitrate(self)
1188 local rate = self.iwinfo.bitrate
1189 if rate and rate > 0 then
1190 return (rate / 1000)
1194 function wifinet.channel(self)
1195 return self.iwinfo.channel or
1196 tonumber(uci_s:get("wireless", self.iwdata.device, "channel"))
1199 function wifinet.signal(self)
1200 return self.iwinfo.signal or 0
1203 function wifinet.noise(self)
1204 return self.iwinfo.noise or 0
1207 function wifinet.signal_level(self, s, n)
1208 if self:active_bssid() ~= "00:00:00:00:00:00" then
1209 local signal = s or self:signal()
1210 local noise = n or self:noise()
1212 if signal < 0 and noise < 0 then
1213 local snr = -1 * (noise - signal)
1214 return math.floor(snr / 5)
1223 function wifinet.signal_percent(self)
1224 local qc = self.iwinfo.quality or 0
1225 local qm = self.iwinfo.quality_max or 0
1227 if qc > 0 and qm > 0 then
1228 return math.floor((100 / qm) * qc)
1234 function wifinet.shortname(self)
1236 i18n.translate(self:active_mode()),
1237 self:active_ssid() or self:active_bssid()
1241 function wifinet.get_i18n(self)
1242 return "%s: %s %q (%s)" %{
1243 i18n.translate("Wireless Network"),
1244 i18n.translate(self:active_mode()),
1245 self:active_ssid() or self:active_bssid(),
1250 function wifinet.adminlink(self)
1251 return dsp.build_url("admin", "network", "wireless", self.netid)
1254 function wifinet.get_network(self)
1255 if uci_r:get("network", self.iwdata.network) == "interface" then
1256 return network(self.iwdata.network)
1260 function wifinet.get_interface(self)
1261 return interface(self:ifname())