49d91b875aedbe5e2e6ae7ad6166595e03159401
[project/luci.git] / modules / luci-base / luasrc / model / network.lua
1 -- Copyright 2009-2015 Jo-Philipp Wich <jow@openwrt.org>
2 -- Licensed to the public under the Apache License 2.0.
3
4 local type, next, pairs, ipairs, loadfile, table, select
5         = type, next, pairs, ipairs, loadfile, table, select
6
7 local tonumber, tostring, math = tonumber, tostring, math
8
9 local require = require
10
11 local nxo = require "nixio"
12 local nfs = require "nixio.fs"
13 local ipc = require "luci.ip"
14 local sys = require "luci.sys"
15 local utl = require "luci.util"
16 local dsp = require "luci.dispatcher"
17 local uci = require "luci.model.uci"
18 local lng = require "luci.i18n"
19 local jsc = require "luci.jsonc"
20
21 module "luci.model.network"
22
23
24 IFACE_PATTERNS_VIRTUAL  = { }
25 IFACE_PATTERNS_IGNORE   = { "^wmaster%d", "^wifi%d", "^hwsim%d", "^imq%d", "^ifb%d", "^mon%.wlan%d", "^sit%d", "^gre%d", "^gretap%d", "^ip6gre%d", "^ip6tnl%d", "^tunl%d", "^lo$" }
26 IFACE_PATTERNS_WIRELESS = { "^wlan%d", "^wl%d", "^ath%d", "^%w+%.network%d" }
27
28
29 protocol = utl.class()
30
31 local _protocols = { }
32
33 local _interfaces, _bridge, _switch, _tunnel, _swtopo
34 local _ubusnetcache, _ubusdevcache, _ubuswificache
35 local _uci
36
37 function _filter(c, s, o, r)
38         local val = _uci:get(c, s, o)
39         if val then
40                 local l = { }
41                 if type(val) == "string" then
42                         for val in val:gmatch("%S+") do
43                                 if val ~= r then
44                                         l[#l+1] = val
45                                 end
46                         end
47                         if #l > 0 then
48                                 _uci:set(c, s, o, table.concat(l, " "))
49                         else
50                                 _uci:delete(c, s, o)
51                         end
52                 elseif type(val) == "table" then
53                         for _, val in ipairs(val) do
54                                 if val ~= r then
55                                         l[#l+1] = val
56                                 end
57                         end
58                         if #l > 0 then
59                                 _uci:set(c, s, o, l)
60                         else
61                                 _uci:delete(c, s, o)
62                         end
63                 end
64         end
65 end
66
67 function _append(c, s, o, a)
68         local val = _uci:get(c, s, o) or ""
69         if type(val) == "string" then
70                 local l = { }
71                 for val in val:gmatch("%S+") do
72                         if val ~= a then
73                                 l[#l+1] = val
74                         end
75                 end
76                 l[#l+1] = a
77                 _uci:set(c, s, o, table.concat(l, " "))
78         elseif type(val) == "table" then
79                 local l = { }
80                 for _, val in ipairs(val) do
81                         if val ~= a then
82                                 l[#l+1] = val
83                         end
84                 end
85                 l[#l+1] = a
86                 _uci:set(c, s, o, l)
87         end
88 end
89
90 function _stror(s1, s2)
91         if not s1 or #s1 == 0 then
92                 return s2 and #s2 > 0 and s2
93         else
94                 return s1
95         end
96 end
97
98 function _get(c, s, o)
99         return _uci:get(c, s, o)
100 end
101
102 function _set(c, s, o, v)
103         if v ~= nil then
104                 if type(v) == "boolean" then v = v and "1" or "0" end
105                 return _uci:set(c, s, o, v)
106         else
107                 return _uci:delete(c, s, o)
108         end
109 end
110
111 function _wifi_iface(x)
112         local _, p
113         for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do
114                 if x:match(p) then
115                         return true
116                 end
117         end
118         return false
119 end
120
121 function _wifi_state(key, val, field)
122         local radio, radiostate, ifc, ifcstate
123
124         if not next(_ubuswificache) then
125                 _ubuswificache = utl.ubus("network.wireless", "status", {}) or {}
126
127                 -- workaround extended section format
128                 for radio, radiostate in pairs(_ubuswificache) do
129                         for ifc, ifcstate in pairs(radiostate.interfaces) do
130                                 if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then
131                                         local s = _uci:get_all('wireless.%s' % ifcstate.section)
132                                         if s then
133                                                 ifcstate.section = s['.name']
134                                         end
135                                 end
136                         end
137                 end
138         end
139
140         for radio, radiostate in pairs(_ubuswificache) do
141                 for ifc, ifcstate in pairs(radiostate.interfaces) do
142                         if ifcstate[key] == val then
143                                 return ifcstate[field]
144                         end
145                 end
146         end
147 end
148
149 function _wifi_lookup(ifn)
150         -- got a radio#.network# pseudo iface, locate the corresponding section
151         local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$")
152         if radio and ifnidx then
153                 local sid = nil
154                 local num = 0
155
156                 ifnidx = tonumber(ifnidx)
157                 _uci:foreach("wireless", "wifi-iface",
158                         function(s)
159                                 if s.device == radio then
160                                         num = num + 1
161                                         if num == ifnidx then
162                                                 sid = s['.name']
163                                                 return false
164                                         end
165                                 end
166                         end)
167
168                 return sid
169
170         -- looks like wifi, try to locate the section via ubus state
171         elseif _wifi_iface(ifn) then
172                 return _wifi_state("ifname", ifn, "section")
173         end
174 end
175
176 function _iface_virtual(x)
177         local _, p
178         for _, p in ipairs(IFACE_PATTERNS_VIRTUAL) do
179                 if x:match(p) then
180                         return true
181                 end
182         end
183         return false
184 end
185
186 function _iface_ignore(x)
187         local _, p
188         for _, p in ipairs(IFACE_PATTERNS_IGNORE) do
189                 if x:match(p) then
190                         return true
191                 end
192         end
193         return false
194 end
195
196 function init(cursor)
197         _uci = cursor or _uci or uci.cursor()
198
199         _interfaces = { }
200         _bridge     = { }
201         _switch     = { }
202         _tunnel     = { }
203         _swtopo     = { }
204
205         _ubusnetcache  = { }
206         _ubusdevcache  = { }
207         _ubuswificache = { }
208
209         -- read interface information
210         local n, i
211         for n, i in ipairs(nxo.getifaddrs()) do
212                 local name = i.name:match("[^:]+")
213
214                 if _iface_virtual(name) then
215                         _tunnel[name] = true
216                 end
217
218                 if _tunnel[name] or not (_iface_ignore(name) or _iface_virtual(name)) then
219                         _interfaces[name] = _interfaces[name] or {
220                                 idx      = i.ifindex or n,
221                                 name     = name,
222                                 rawname  = i.name,
223                                 flags    = { },
224                                 ipaddrs  = { },
225                                 ip6addrs = { }
226                         }
227
228                         if i.family == "packet" then
229                                 _interfaces[name].flags   = i.flags
230                                 _interfaces[name].stats   = i.data
231                                 _interfaces[name].macaddr = i.addr
232                         elseif i.family == "inet" then
233                                 _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
234                         elseif i.family == "inet6" then
235                                 _interfaces[name].ip6addrs[#_interfaces[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
236                         end
237                 end
238         end
239
240         -- read bridge informaton
241         local b, l
242         for l in utl.execi("brctl show") do
243                 if not l:match("STP") then
244                         local r = utl.split(l, "%s+", nil, true)
245                         if #r == 4 then
246                                 b = {
247                                         name    = r[1],
248                                         id      = r[2],
249                                         stp     = r[3] == "yes",
250                                         ifnames = { _interfaces[r[4]] }
251                                 }
252                                 if b.ifnames[1] then
253                                         b.ifnames[1].bridge = b
254                                 end
255                                 _bridge[r[1]] = b
256                         elseif b then
257                                 b.ifnames[#b.ifnames+1] = _interfaces[r[2]]
258                                 b.ifnames[#b.ifnames].bridge = b
259                         end
260                 end
261         end
262
263         -- read switch topology
264         local boardinfo = jsc.parse(nfs.readfile("/etc/board.json") or "")
265         if type(boardinfo) == "table" and type(boardinfo.switch) == "table" then
266                 local switch, layout
267                 for switch, layout in pairs(boardinfo.switch) do
268                         if type(layout) == "table" and type(layout.ports) == "table" then
269                                 local _, port
270                                 local ports = { }
271                                 local nports = { }
272                                 local netdevs = { }
273
274                                 for _, port in ipairs(layout.ports) do
275                                         if type(port) == "table" and
276                                            type(port.num) == "number" and
277                                            (type(port.role) == "string" or
278                                             type(port.device) == "string")
279                                         then
280                                                 local spec = {
281                                                         num    = port.num,
282                                                         role   = port.role or "cpu",
283                                                         index  = port.index or port.num
284                                                 }
285
286                                                 if port.device then
287                                                         spec.device = port.device
288                                                         spec.tagged = port.need_tag
289                                                         netdevs[tostring(port.num)] = port.device
290                                                 end
291
292                                                 ports[#ports+1] = spec
293
294                                                 if port.role then
295                                                         nports[port.role] = (nports[port.role] or 0) + 1
296                                                 end
297                                         end
298                                 end
299
300                                 table.sort(ports, function(a, b)
301                                         if a.role ~= b.role then
302                                                 return (a.role < b.role)
303                                         end
304
305                                         return (a.index < b.index)
306                                 end)
307
308                                 local pnum, role
309                                 for _, port in ipairs(ports) do
310                                         if port.role ~= role then
311                                                 role = port.role
312                                                 pnum = 1
313                                         end
314
315                                         if role == "cpu" then
316                                                 port.label = "CPU (%s)" % port.device
317                                         elseif nports[role] > 1 then
318                                                 port.label = "%s %d" %{ role:upper(), pnum }
319                                                 pnum = pnum + 1
320                                         else
321                                                 port.label = role:upper()
322                                         end
323
324                                         port.role = nil
325                                         port.index = nil
326                                 end
327
328                                 _swtopo[switch] = {
329                                         ports = ports,
330                                         netdevs = netdevs
331                                 }
332                         end
333                 end
334         end
335
336         return _M
337 end
338
339 function save(self, ...)
340         _uci:save(...)
341         _uci:load(...)
342 end
343
344 function commit(self, ...)
345         _uci:commit(...)
346         _uci:load(...)
347 end
348
349 function ifnameof(self, x)
350         if utl.instanceof(x, interface) then
351                 return x:name()
352         elseif utl.instanceof(x, protocol) then
353                 return x:ifname()
354         elseif type(x) == "string" then
355                 return x:match("^[^:]+")
356         end
357 end
358
359 function get_protocol(self, protoname, netname)
360         local v = _protocols[protoname]
361         if v then
362                 return v(netname or "__dummy__")
363         end
364 end
365
366 function get_protocols(self)
367         local p = { }
368         local _, v
369         for _, v in ipairs(_protocols) do
370                 p[#p+1] = v("__dummy__")
371         end
372         return p
373 end
374
375 function register_protocol(self, protoname)
376         local proto = utl.class(protocol)
377
378         function proto.__init__(self, name)
379                 self.sid = name
380         end
381
382         function proto.proto(self)
383                 return protoname
384         end
385
386         _protocols[#_protocols+1] = proto
387         _protocols[protoname]     = proto
388
389         return proto
390 end
391
392 function register_pattern_virtual(self, pat)
393         IFACE_PATTERNS_VIRTUAL[#IFACE_PATTERNS_VIRTUAL+1] = pat
394 end
395
396
397 function has_ipv6(self)
398         return nfs.access("/proc/net/ipv6_route")
399 end
400
401 function add_network(self, n, options)
402         local oldnet = self:get_network(n)
403         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not oldnet then
404                 if _uci:section("network", "interface", n, options) then
405                         return network(n)
406                 end
407         elseif oldnet and oldnet:is_empty() then
408                 if options then
409                         local k, v
410                         for k, v in pairs(options) do
411                                 oldnet:set(k, v)
412                         end
413                 end
414                 return oldnet
415         end
416 end
417
418 function get_network(self, n)
419         if n and _uci:get("network", n) == "interface" then
420                 return network(n)
421         end
422 end
423
424 function get_networks(self)
425         local nets = { }
426         local nls = { }
427
428         _uci:foreach("network", "interface",
429                 function(s)
430                         nls[s['.name']] = network(s['.name'])
431                 end)
432
433         local n
434         for n in utl.kspairs(nls) do
435                 nets[#nets+1] = nls[n]
436         end
437
438         return nets
439 end
440
441 function del_network(self, n)
442         local r = _uci:delete("network", n)
443         if r then
444                 _uci:delete_all("network", "alias",
445                         function(s) return (s.interface == n) end)
446
447                 _uci:delete_all("network", "route",
448                         function(s) return (s.interface == n) end)
449
450                 _uci:delete_all("network", "route6",
451                         function(s) return (s.interface == n) end)
452
453                 _uci:foreach("wireless", "wifi-iface",
454                         function(s)
455                                 local net
456                                 local rest = { }
457                                 for net in utl.imatch(s.network) do
458                                         if net ~= n then
459                                                 rest[#rest+1] = net
460                                         end
461                                 end
462                                 if #rest > 0 then
463                                         _uci:set("wireless", s['.name'], "network",
464                                                       table.concat(rest, " "))
465                                 else
466                                         _uci:delete("wireless", s['.name'], "network")
467                                 end
468                         end)
469         end
470         return r
471 end
472
473 function rename_network(self, old, new)
474         local r
475         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
476                 r = _uci:section("network", "interface", new, _uci:get_all("network", old))
477
478                 if r then
479                         _uci:foreach("network", "alias",
480                                 function(s)
481                                         if s.interface == old then
482                                                 _uci:set("network", s['.name'], "interface", new)
483                                         end
484                                 end)
485
486                         _uci:foreach("network", "route",
487                                 function(s)
488                                         if s.interface == old then
489                                                 _uci:set("network", s['.name'], "interface", new)
490                                         end
491                                 end)
492
493                         _uci:foreach("network", "route6",
494                                 function(s)
495                                         if s.interface == old then
496                                                 _uci:set("network", s['.name'], "interface", new)
497                                         end
498                                 end)
499
500                         _uci:foreach("wireless", "wifi-iface",
501                                 function(s)
502                                         local net
503                                         local list = { }
504                                         for net in utl.imatch(s.network) do
505                                                 if net == old then
506                                                         list[#list+1] = new
507                                                 else
508                                                         list[#list+1] = net
509                                                 end
510                                         end
511                                         if #list > 0 then
512                                                 _uci:set("wireless", s['.name'], "network",
513                                                               table.concat(list, " "))
514                                         end
515                                 end)
516
517                         _uci:delete("network", old)
518                 end
519         end
520         return r or false
521 end
522
523 function get_interface(self, i)
524         if _interfaces[i] or _wifi_iface(i) then
525                 return interface(i)
526         else
527                 local ifc
528                 local num = { }
529                 _uci:foreach("wireless", "wifi-iface",
530                         function(s)
531                                 if s.device then
532                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
533                                         if s['.name'] == i then
534                                                 ifc = interface(
535                                                         "%s.network%d" %{s.device, num[s.device] })
536                                                 return false
537                                         end
538                                 end
539                         end)
540                 return ifc
541         end
542 end
543
544 function get_interfaces(self)
545         local iface
546         local ifaces = { }
547         local nfs = { }
548
549         -- find normal interfaces
550         _uci:foreach("network", "interface",
551                 function(s)
552                         for iface in utl.imatch(s.ifname) do
553                                 if not _iface_ignore(iface) and not _iface_virtual(iface) and not _wifi_iface(iface) then
554                                         nfs[iface] = interface(iface)
555                                 end
556                         end
557                 end)
558
559         for iface in utl.kspairs(_interfaces) do
560                 if not (nfs[iface] or _iface_ignore(iface) or _iface_virtual(iface) or _wifi_iface(iface)) then
561                         nfs[iface] = interface(iface)
562                 end
563         end
564
565         -- find vlan interfaces
566         _uci:foreach("network", "switch_vlan",
567                 function(s)
568                         if type(s.ports) ~= "string" or
569                            type(s.device) ~= "string" or
570                            type(_swtopo[s.device]) ~= "table"
571                         then
572                                 return
573                         end
574
575                         local pnum, ptag
576                         for pnum, ptag in s.ports:gmatch("(%d+)([tu]?)") do
577                                 local netdev = _swtopo[s.device].netdevs[pnum]
578                                 if netdev then
579                                         if not nfs[netdev] then
580                                                 nfs[netdev] = interface(netdev)
581                                         end
582                                         _switch[netdev] = true
583
584                                         if ptag == "t" then
585                                                 local vid = tonumber(s.vid or s.vlan)
586                                                 if vid ~= nil and vid >= 0 and vid <= 4095 then
587                                                         local iface = "%s.%d" %{ netdev, vid }
588                                                         if not nfs[iface] then
589                                                                 nfs[iface] = interface(iface)
590                                                         end
591                                                         _switch[iface] = true
592                                                 end
593                                         end
594                                 end
595                         end
596                 end)
597
598         for iface in utl.kspairs(nfs) do
599                 ifaces[#ifaces+1] = nfs[iface]
600         end
601
602         -- find wifi interfaces
603         local num = { }
604         local wfs = { }
605         _uci:foreach("wireless", "wifi-iface",
606                 function(s)
607                         if s.device then
608                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
609                                 local i = "%s.network%d" %{ s.device, num[s.device] }
610                                 wfs[i] = interface(i)
611                         end
612                 end)
613
614         for iface in utl.kspairs(wfs) do
615                 ifaces[#ifaces+1] = wfs[iface]
616         end
617
618         return ifaces
619 end
620
621 function ignore_interface(self, x)
622         return _iface_ignore(x)
623 end
624
625 function get_wifidev(self, dev)
626         if _uci:get("wireless", dev) == "wifi-device" then
627                 return wifidev(dev)
628         end
629 end
630
631 function get_wifidevs(self)
632         local devs = { }
633         local wfd  = { }
634
635         _uci:foreach("wireless", "wifi-device",
636                 function(s) wfd[#wfd+1] = s['.name'] end)
637
638         local dev
639         for _, dev in utl.vspairs(wfd) do
640                 devs[#devs+1] = wifidev(dev)
641         end
642
643         return devs
644 end
645
646 function get_wifinet(self, net)
647         local wnet = _wifi_lookup(net)
648         if wnet then
649                 return wifinet(wnet)
650         end
651 end
652
653 function add_wifinet(self, net, options)
654         if type(options) == "table" and options.device and
655                 _uci:get("wireless", options.device) == "wifi-device"
656         then
657                 local wnet = _uci:section("wireless", "wifi-iface", nil, options)
658                 return wifinet(wnet)
659         end
660 end
661
662 function del_wifinet(self, net)
663         local wnet = _wifi_lookup(net)
664         if wnet then
665                 _uci:delete("wireless", wnet)
666                 return true
667         end
668         return false
669 end
670
671 function get_status_by_route(self, addr, mask)
672         local _, object
673         for _, object in ipairs(utl.ubus()) do
674                 local net = object:match("^network%.interface%.(.+)")
675                 if net then
676                         local s = utl.ubus(object, "status", {})
677                         if s and s.route then
678                                 local rt
679                                 for _, rt in ipairs(s.route) do
680                                         if not rt.table and rt.target == addr and rt.mask == mask then
681                                                 return net, s
682                                         end
683                                 end
684                         end
685                 end
686         end
687 end
688
689 function get_status_by_address(self, addr)
690         local _, object
691         for _, object in ipairs(utl.ubus()) do
692                 local net = object:match("^network%.interface%.(.+)")
693                 if net then
694                         local s = utl.ubus(object, "status", {})
695                         if s and s['ipv4-address'] then
696                                 local a
697                                 for _, a in ipairs(s['ipv4-address']) do
698                                         if a.address == addr then
699                                                 return net, s
700                                         end
701                                 end
702                         end
703                         if s and s['ipv6-address'] then
704                                 local a
705                                 for _, a in ipairs(s['ipv6-address']) do
706                                         if a.address == addr then
707                                                 return net, s
708                                         end
709                                 end
710                         end
711                 end
712         end
713 end
714
715 function get_wannet(self)
716         local net, stat = self:get_status_by_route("0.0.0.0", 0)
717         return net and network(net, stat.proto)
718 end
719
720 function get_wandev(self)
721         local _, stat = self:get_status_by_route("0.0.0.0", 0)
722         return stat and interface(stat.l3_device or stat.device)
723 end
724
725 function get_wan6net(self)
726         local net, stat = self:get_status_by_route("::", 0)
727         return net and network(net, stat.proto)
728 end
729
730 function get_wan6dev(self)
731         local _, stat = self:get_status_by_route("::", 0)
732         return stat and interface(stat.l3_device or stat.device)
733 end
734
735 function get_switch_topologies(self)
736         return _swtopo
737 end
738
739
740 function network(name, proto)
741         if name then
742                 local p = proto or _uci:get("network", name, "proto")
743                 local c = p and _protocols[p] or protocol
744                 return c(name)
745         end
746 end
747
748 function protocol.__init__(self, name)
749         self.sid = name
750 end
751
752 function protocol._get(self, opt)
753         local v = _uci:get("network", self.sid, opt)
754         if type(v) == "table" then
755                 return table.concat(v, " ")
756         end
757         return v or ""
758 end
759
760 function protocol._ubus(self, field)
761         if not _ubusnetcache[self.sid] then
762                 _ubusnetcache[self.sid] = utl.ubus("network.interface.%s" % self.sid,
763                                                    "status", { })
764         end
765         if _ubusnetcache[self.sid] and field then
766                 return _ubusnetcache[self.sid][field]
767         end
768         return _ubusnetcache[self.sid]
769 end
770
771 function protocol.get(self, opt)
772         return _get("network", self.sid, opt)
773 end
774
775 function protocol.set(self, opt, val)
776         return _set("network", self.sid, opt, val)
777 end
778
779 function protocol.ifname(self)
780         local ifname
781         if self:is_floating() then
782                 ifname = self:_ubus("l3_device")
783         else
784                 ifname = self:_ubus("device")
785         end
786         if not ifname then
787                 local num = { }
788                 _uci:foreach("wireless", "wifi-iface",
789                         function(s)
790                                 if s.device then
791                                         num[s.device] = num[s.device]
792                                                 and num[s.device] + 1 or 1
793
794                                         local net
795                                         for net in utl.imatch(s.network) do
796                                                 if net == self.sid then
797                                                         ifname = "%s.network%d" %{ s.device, num[s.device] }
798                                                         return false
799                                                 end
800                                         end
801                                 end
802                         end)
803         end
804         return ifname
805 end
806
807 function protocol.proto(self)
808         return "none"
809 end
810
811 function protocol.get_i18n(self)
812         local p = self:proto()
813         if p == "none" then
814                 return lng.translate("Unmanaged")
815         elseif p == "static" then
816                 return lng.translate("Static address")
817         elseif p == "dhcp" then
818                 return lng.translate("DHCP client")
819         else
820                 return lng.translate("Unknown")
821         end
822 end
823
824 function protocol.type(self)
825         return self:_get("type")
826 end
827
828 function protocol.name(self)
829         return self.sid
830 end
831
832 function protocol.uptime(self)
833         return self:_ubus("uptime") or 0
834 end
835
836 function protocol.expires(self)
837         local u = self:_ubus("uptime")
838         local d = self:_ubus("data")
839
840         if type(u) == "number" and type(d) == "table" and
841            type(d.leasetime) == "number"
842         then
843                 local r = (d.leasetime - (u % d.leasetime))
844                 return r > 0 and r or 0
845         end
846
847         return -1
848 end
849
850 function protocol.metric(self)
851         return self:_ubus("metric") or 0
852 end
853
854 function protocol.ipaddr(self)
855         local addrs = self:_ubus("ipv4-address")
856         return addrs and #addrs > 0 and addrs[1].address
857 end
858
859 function protocol.ipaddrs(self)
860         local addrs = self:_ubus("ipv4-address")
861         local rv = { }
862
863         if type(addrs) == "table" then
864                 local n, addr
865                 for n, addr in ipairs(addrs) do
866                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
867                 end
868         end
869
870         return rv
871 end
872
873 function protocol.netmask(self)
874         local addrs = self:_ubus("ipv4-address")
875         return addrs and #addrs > 0 and
876                 ipc.IPv4("0.0.0.0/%d" % addrs[1].mask):mask():string()
877 end
878
879 function protocol.gwaddr(self)
880         local _, route
881         for _, route in ipairs(self:_ubus("route") or { }) do
882                 if route.target == "0.0.0.0" and route.mask == 0 then
883                         return route.nexthop
884                 end
885         end
886 end
887
888 function protocol.dnsaddrs(self)
889         local dns = { }
890         local _, addr
891         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
892                 if not addr:match(":") then
893                         dns[#dns+1] = addr
894                 end
895         end
896         return dns
897 end
898
899 function protocol.ip6addr(self)
900         local addrs = self:_ubus("ipv6-address")
901         if addrs and #addrs > 0 then
902                 return "%s/%d" %{ addrs[1].address, addrs[1].mask }
903         else
904                 addrs = self:_ubus("ipv6-prefix-assignment")
905                 if addrs and #addrs > 0 then
906                         return "%s/%d" %{ addrs[1].address, addrs[1].mask }
907                 end
908         end
909 end
910
911 function protocol.ip6addrs(self)
912         local addrs = self:_ubus("ipv6-address")
913         local rv = { }
914         local n, addr
915
916         if type(addrs) == "table" then
917                 for n, addr in ipairs(addrs) do
918                         rv[#rv+1] = "%s/%d" %{ addr.address, addr.mask }
919                 end
920         end
921
922         addrs = self:_ubus("ipv6-prefix-assignment")
923
924         if type(addrs) == "table" then
925                 for n, addr in ipairs(addrs) do
926                         rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask }
927                 end
928         end
929
930         return rv
931 end
932
933 function protocol.gw6addr(self)
934         local _, route
935         for _, route in ipairs(self:_ubus("route") or { }) do
936                 if route.target == "::" and route.mask == 0 then
937                         return ipc.IPv6(route.nexthop):string()
938                 end
939         end
940 end
941
942 function protocol.dns6addrs(self)
943         local dns = { }
944         local _, addr
945         for _, addr in ipairs(self:_ubus("dns-server") or { }) do
946                 if addr:match(":") then
947                         dns[#dns+1] = addr
948                 end
949         end
950         return dns
951 end
952
953 function protocol.ip6prefix(self)
954         local prefix = self:_ubus("ipv6-prefix")
955         if prefix and #prefix > 0 then
956                 return "%s/%d" %{ prefix[1].address, prefix[1].mask }
957         end
958 end
959
960 function protocol.is_bridge(self)
961         return (not self:is_virtual() and self:type() == "bridge")
962 end
963
964 function protocol.opkg_package(self)
965         return nil
966 end
967
968 function protocol.is_installed(self)
969         return true
970 end
971
972 function protocol.is_virtual(self)
973         return false
974 end
975
976 function protocol.is_floating(self)
977         return false
978 end
979
980 function protocol.is_empty(self)
981         if self:is_floating() then
982                 return false
983         else
984                 local rv = true
985
986                 if (self:_get("ifname") or ""):match("%S+") then
987                         rv = false
988                 end
989
990                 _uci:foreach("wireless", "wifi-iface",
991                         function(s)
992                                 local n
993                                 for n in utl.imatch(s.network) do
994                                         if n == self.sid then
995                                                 rv = false
996                                                 return false
997                                         end
998                                 end
999                         end)
1000
1001                 return rv
1002         end
1003 end
1004
1005 function protocol.add_interface(self, ifname)
1006         ifname = _M:ifnameof(ifname)
1007         if ifname and not self:is_floating() then
1008                 -- if its a wifi interface, change its network option
1009                 local wif = _wifi_lookup(ifname)
1010                 if wif then
1011                         _append("wireless", wif, "network", self.sid)
1012
1013                 -- add iface to our iface list
1014                 else
1015                         _append("network", self.sid, "ifname", ifname)
1016                 end
1017         end
1018 end
1019
1020 function protocol.del_interface(self, ifname)
1021         ifname = _M:ifnameof(ifname)
1022         if ifname and not self:is_floating() then
1023                 -- if its a wireless interface, clear its network option
1024                 local wif = _wifi_lookup(ifname)
1025                 if wif then _filter("wireless", wif, "network", self.sid) end
1026
1027                 -- remove the interface
1028                 _filter("network", self.sid, "ifname", ifname)
1029         end
1030 end
1031
1032 function protocol.get_interface(self)
1033         if self:is_virtual() then
1034                 _tunnel[self:proto() .. "-" .. self.sid] = true
1035                 return interface(self:proto() .. "-" .. self.sid, self)
1036         elseif self:is_bridge() then
1037                 _bridge["br-" .. self.sid] = true
1038                 return interface("br-" .. self.sid, self)
1039         else
1040                 local ifn = nil
1041                 local num = { }
1042                 for ifn in utl.imatch(_uci:get("network", self.sid, "ifname")) do
1043                         ifn = ifn:match("^[^:/]+")
1044                         return ifn and interface(ifn, self)
1045                 end
1046                 ifn = nil
1047                 _uci:foreach("wireless", "wifi-iface",
1048                         function(s)
1049                                 if s.device then
1050                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
1051
1052                                         local net
1053                                         for net in utl.imatch(s.network) do
1054                                                 if net == self.sid then
1055                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
1056                                                         return false
1057                                                 end
1058                                         end
1059                                 end
1060                         end)
1061                 return ifn and interface(ifn, self)
1062         end
1063 end
1064
1065 function protocol.get_interfaces(self)
1066         if self:is_bridge() or (self:is_virtual() and not self:is_floating()) then
1067                 local ifaces = { }
1068
1069                 local ifn
1070                 local nfs = { }
1071                 for ifn in utl.imatch(self:get("ifname")) do
1072                         ifn = ifn:match("^[^:/]+")
1073                         nfs[ifn] = interface(ifn, self)
1074                 end
1075
1076                 for ifn in utl.kspairs(nfs) do
1077                         ifaces[#ifaces+1] = nfs[ifn]
1078                 end
1079
1080                 local num = { }
1081                 local wfs = { }
1082                 _uci:foreach("wireless", "wifi-iface",
1083                         function(s)
1084                                 if s.device then
1085                                         num[s.device] = num[s.device] and num[s.device] + 1 or 1
1086
1087                                         local net
1088                                         for net in utl.imatch(s.network) do
1089                                                 if net == self.sid then
1090                                                         ifn = "%s.network%d" %{ s.device, num[s.device] }
1091                                                         wfs[ifn] = interface(ifn, self)
1092                                                 end
1093                                         end
1094                                 end
1095                         end)
1096
1097                 for ifn in utl.kspairs(wfs) do
1098                         ifaces[#ifaces+1] = wfs[ifn]
1099                 end
1100
1101                 return ifaces
1102         end
1103 end
1104
1105 function protocol.contains_interface(self, ifname)
1106         ifname = _M:ifnameof(ifname)
1107         if not ifname then
1108                 return false
1109         elseif self:is_virtual() and self:proto() .. "-" .. self.sid == ifname then
1110                 return true
1111         elseif self:is_bridge() and "br-" .. self.sid == ifname then
1112                 return true
1113         else
1114                 local ifn
1115                 for ifn in utl.imatch(self:get("ifname")) do
1116                         ifn = ifn:match("[^:]+")
1117                         if ifn == ifname then
1118                                 return true
1119                         end
1120                 end
1121
1122                 local wif = _wifi_lookup(ifname)
1123                 if wif then
1124                         local n
1125                         for n in utl.imatch(_uci:get("wireless", wif, "network")) do
1126                                 if n == self.sid then
1127                                         return true
1128                                 end
1129                         end
1130                 end
1131         end
1132
1133         return false
1134 end
1135
1136 function protocol.adminlink(self)
1137         return dsp.build_url("admin", "network", "network", self.sid)
1138 end
1139
1140
1141 interface = utl.class()
1142
1143 function interface.__init__(self, ifname, network)
1144         local wif = _wifi_lookup(ifname)
1145         if wif then
1146                 self.wif    = wifinet(wif)
1147                 self.ifname = _wifi_state("section", wif, "ifname")
1148         end
1149
1150         self.ifname  = self.ifname or ifname
1151         self.dev     = _interfaces[self.ifname]
1152         self.network = network
1153 end
1154
1155 function interface._ubus(self, field)
1156         if not _ubusdevcache[self.ifname] then
1157                 _ubusdevcache[self.ifname] = utl.ubus("network.device", "status",
1158                                                       { name = self.ifname })
1159         end
1160         if _ubusdevcache[self.ifname] and field then
1161                 return _ubusdevcache[self.ifname][field]
1162         end
1163         return _ubusdevcache[self.ifname]
1164 end
1165
1166 function interface.name(self)
1167         return self.wif and self.wif:ifname() or self.ifname
1168 end
1169
1170 function interface.mac(self)
1171         local mac = self:_ubus("macaddr")
1172         return mac and mac:upper()
1173 end
1174
1175 function interface.ipaddrs(self)
1176         return self.dev and self.dev.ipaddrs or { }
1177 end
1178
1179 function interface.ip6addrs(self)
1180         return self.dev and self.dev.ip6addrs or { }
1181 end
1182
1183 function interface.type(self)
1184         if self.wif or _wifi_iface(self.ifname) then
1185                 return "wifi"
1186         elseif _bridge[self.ifname] then
1187                 return "bridge"
1188         elseif _tunnel[self.ifname] then
1189                 return "tunnel"
1190         elseif self.ifname:match("%.") then
1191                 return "vlan"
1192         elseif _switch[self.ifname] then
1193                 return "switch"
1194         else
1195                 return "ethernet"
1196         end
1197 end
1198
1199 function interface.shortname(self)
1200         if self.wif then
1201                 return self.wif:shortname()
1202         else
1203                 return self.ifname
1204         end
1205 end
1206
1207 function interface.get_i18n(self)
1208         if self.wif then
1209                 return "%s: %s %q" %{
1210                         lng.translate("Wireless Network"),
1211                         self.wif:active_mode(),
1212                         self.wif:active_ssid() or self.wif:active_bssid() or self.wif:id()
1213                 }
1214         else
1215                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
1216         end
1217 end
1218
1219 function interface.get_type_i18n(self)
1220         local x = self:type()
1221         if x == "wifi" then
1222                 return lng.translate("Wireless Adapter")
1223         elseif x == "bridge" then
1224                 return lng.translate("Bridge")
1225         elseif x == "switch" then
1226                 return lng.translate("Ethernet Switch")
1227         elseif x == "vlan" then
1228                 if _switch[self.ifname] then
1229                         return lng.translate("Switch VLAN")
1230                 else
1231                         return lng.translate("Software VLAN")
1232                 end
1233         elseif x == "tunnel" then
1234                 return lng.translate("Tunnel Interface")
1235         else
1236                 return lng.translate("Ethernet Adapter")
1237         end
1238 end
1239
1240 function interface.adminlink(self)
1241         if self.wif then
1242                 return self.wif:adminlink()
1243         end
1244 end
1245
1246 function interface.ports(self)
1247         local members = self:_ubus("bridge-members")
1248         if members then
1249                 local _, iface
1250                 local ifaces = { }
1251                 for _, iface in ipairs(members) do
1252                         ifaces[#ifaces+1] = interface(iface)
1253                 end
1254         end
1255 end
1256
1257 function interface.bridge_id(self)
1258         if self.br then
1259                 return self.br.id
1260         else
1261                 return nil
1262         end
1263 end
1264
1265 function interface.bridge_stp(self)
1266         if self.br then
1267                 return self.br.stp
1268         else
1269                 return false
1270         end
1271 end
1272
1273 function interface.is_up(self)
1274         return self:_ubus("up") or false
1275 end
1276
1277 function interface.is_bridge(self)
1278         return (self:type() == "bridge")
1279 end
1280
1281 function interface.is_bridgeport(self)
1282         return self.dev and self.dev.bridge and true or false
1283 end
1284
1285 function interface.tx_bytes(self)
1286         local stat = self:_ubus("statistics")
1287         return stat and stat.tx_bytes or 0
1288 end
1289
1290 function interface.rx_bytes(self)
1291         local stat = self:_ubus("statistics")
1292         return stat and stat.rx_bytes or 0
1293 end
1294
1295 function interface.tx_packets(self)
1296         local stat = self:_ubus("statistics")
1297         return stat and stat.tx_packets or 0
1298 end
1299
1300 function interface.rx_packets(self)
1301         local stat = self:_ubus("statistics")
1302         return stat and stat.rx_packets or 0
1303 end
1304
1305 function interface.get_network(self)
1306         return self:get_networks()[1]
1307 end
1308
1309 function interface.get_networks(self)
1310         if not self.networks then
1311                 local nets = { }
1312                 local _, net
1313                 for _, net in ipairs(_M:get_networks()) do
1314                         if net:contains_interface(self.ifname) or
1315                            net:ifname() == self.ifname
1316                         then
1317                                 nets[#nets+1] = net
1318                         end
1319                 end
1320                 table.sort(nets, function(a, b) return a.sid < b.sid end)
1321                 self.networks = nets
1322                 return nets
1323         else
1324                 return self.networks
1325         end
1326 end
1327
1328 function interface.get_wifinet(self)
1329         return self.wif
1330 end
1331
1332
1333 wifidev = utl.class()
1334
1335 function wifidev.__init__(self, dev)
1336         self.sid    = dev
1337         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1338 end
1339
1340 function wifidev.get(self, opt)
1341         return _get("wireless", self.sid, opt)
1342 end
1343
1344 function wifidev.set(self, opt, val)
1345         return _set("wireless", self.sid, opt, val)
1346 end
1347
1348 function wifidev.name(self)
1349         return self.sid
1350 end
1351
1352 function wifidev.hwmodes(self)
1353         local l = self.iwinfo.hwmodelist
1354         if l and next(l) then
1355                 return l
1356         else
1357                 return { b = true, g = true }
1358         end
1359 end
1360
1361 function wifidev.get_i18n(self)
1362         local t = "Generic"
1363         if self.iwinfo.type == "wl" then
1364                 t = "Broadcom"
1365         elseif self.iwinfo.type == "madwifi" then
1366                 t = "Atheros"
1367         end
1368
1369         local m = ""
1370         local l = self:hwmodes()
1371         if l.a then m = m .. "a" end
1372         if l.b then m = m .. "b" end
1373         if l.g then m = m .. "g" end
1374         if l.n then m = m .. "n" end
1375         if l.ac then m = "ac" end
1376
1377         return "%s 802.11%s Wireless Controller (%s)" %{ t, m, self:name() }
1378 end
1379
1380 function wifidev.is_up(self)
1381         if _ubuswificache[self.sid] then
1382                 return (_ubuswificache[self.sid].up == true)
1383         end
1384
1385         return false
1386 end
1387
1388 function wifidev.get_wifinet(self, net)
1389         if _uci:get("wireless", net) == "wifi-iface" then
1390                 return wifinet(net)
1391         else
1392                 local wnet = _wifi_lookup(net)
1393                 if wnet then
1394                         return wifinet(wnet)
1395                 end
1396         end
1397 end
1398
1399 function wifidev.get_wifinets(self)
1400         local nets = { }
1401
1402         _uci:foreach("wireless", "wifi-iface",
1403                 function(s)
1404                         if s.device == self.sid then
1405                                 nets[#nets+1] = wifinet(s['.name'])
1406                         end
1407                 end)
1408
1409         return nets
1410 end
1411
1412 function wifidev.add_wifinet(self, options)
1413         options = options or { }
1414         options.device = self.sid
1415
1416         local wnet = _uci:section("wireless", "wifi-iface", nil, options)
1417         if wnet then
1418                 return wifinet(wnet, options)
1419         end
1420 end
1421
1422 function wifidev.del_wifinet(self, net)
1423         if utl.instanceof(net, wifinet) then
1424                 net = net.sid
1425         elseif _uci:get("wireless", net) ~= "wifi-iface" then
1426                 net = _wifi_lookup(net)
1427         end
1428
1429         if net and _uci:get("wireless", net, "device") == self.sid then
1430                 _uci:delete("wireless", net)
1431                 return true
1432         end
1433
1434         return false
1435 end
1436
1437
1438 wifinet = utl.class()
1439
1440 function wifinet.__init__(self, net, data)
1441         self.sid = net
1442
1443         local n = 0
1444         local num = { }
1445         local netid, sid
1446         _uci:foreach("wireless", "wifi-iface",
1447                 function(s)
1448                         n = n + 1
1449                         if s.device then
1450                                 num[s.device] = num[s.device] and num[s.device] + 1 or 1
1451                                 if s['.name'] == self.sid then
1452                                         sid = "@wifi-iface[%d]" % n
1453                                         netid = "%s.network%d" %{ s.device, num[s.device] }
1454                                         return false
1455                                 end
1456                         end
1457                 end)
1458
1459         if sid then
1460                 local _, k, r, i
1461                 for k, r in pairs(_ubuswificache) do
1462                         if type(r) == "table" and
1463                            type(r.interfaces) == "table"
1464                         then
1465                                 for _, i in ipairs(r.interfaces) do
1466                                         if type(i) == "table" and i.section == sid then
1467                                                 self._ubusdata = {
1468                                                         radio = k,
1469                                                         dev = r,
1470                                                         net = i
1471                                                 }
1472                                         end
1473                                 end
1474                         end
1475                 end
1476         end
1477
1478         local dev = _wifi_state("section", self.sid, "ifname") or netid
1479
1480         self.netid  = netid
1481         self.wdev   = dev
1482         self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { }
1483 end
1484
1485 function wifinet.ubus(self, ...)
1486         local n, v = self._ubusdata
1487         for n = 1, select('#', ...) do
1488                 if type(v) == "table" then
1489                         v = v[select(n, ...)]
1490                 else
1491                         return nil
1492                 end
1493         end
1494         return v
1495 end
1496
1497 function wifinet.get(self, opt)
1498         return _get("wireless", self.sid, opt)
1499 end
1500
1501 function wifinet.set(self, opt, val)
1502         return _set("wireless", self.sid, opt, val)
1503 end
1504
1505 function wifinet.mode(self)
1506         return self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1507 end
1508
1509 function wifinet.ssid(self)
1510         return self:ubus("net", "config", "ssid") or self:get("ssid")
1511 end
1512
1513 function wifinet.bssid(self)
1514         return self:ubus("net", "config", "bssid") or self:get("bssid")
1515 end
1516
1517 function wifinet.network(self)
1518         local net, networks = nil, { }
1519         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1520                 networks[#networks+1] = net
1521         end
1522         return networks
1523 end
1524
1525 function wifinet.id(self)
1526         return self.netid
1527 end
1528
1529 function wifinet.name(self)
1530         return self.sid
1531 end
1532
1533 function wifinet.ifname(self)
1534         local ifname = self:ubus("net", "ifname") or self.iwinfo.ifname
1535         if not ifname or ifname:match("^wifi%d") or ifname:match("^radio%d") then
1536                 ifname = self.wdev
1537         end
1538         return ifname
1539 end
1540
1541 function wifinet.get_device(self)
1542         local dev = self:ubus("radio") or self:get("device")
1543         return dev and wifidev(dev) or nil
1544 end
1545
1546 function wifinet.is_up(self)
1547         local ifc = self:get_interface()
1548         return (ifc and ifc:is_up() or false)
1549 end
1550
1551 function wifinet.active_mode(self)
1552         local m = self.iwinfo.mode or self:ubus("net", "config", "mode") or self:get("mode") or "ap"
1553
1554         if     m == "ap"      then m = "Master"
1555         elseif m == "sta"     then m = "Client"
1556         elseif m == "adhoc"   then m = "Ad-Hoc"
1557         elseif m == "mesh"    then m = "Mesh"
1558         elseif m == "monitor" then m = "Monitor"
1559         end
1560
1561         return m
1562 end
1563
1564 function wifinet.active_mode_i18n(self)
1565         return lng.translate(self:active_mode())
1566 end
1567
1568 function wifinet.active_ssid(self)
1569         return self.iwinfo.ssid or self:ubus("net", "config", "ssid") or self:get("ssid")
1570 end
1571
1572 function wifinet.active_bssid(self)
1573         return self.iwinfo.bssid or self:ubus("net", "config", "bssid") or self:get("bssid")
1574 end
1575
1576 function wifinet.active_encryption(self)
1577         local enc = self.iwinfo and self.iwinfo.encryption
1578         return enc and enc.description or "-"
1579 end
1580
1581 function wifinet.assoclist(self)
1582         return self.iwinfo.assoclist or { }
1583 end
1584
1585 function wifinet.frequency(self)
1586         local freq = self.iwinfo.frequency
1587         if freq and freq > 0 then
1588                 return "%.03f" % (freq / 1000)
1589         end
1590 end
1591
1592 function wifinet.bitrate(self)
1593         local rate = self.iwinfo.bitrate
1594         if rate and rate > 0 then
1595                 return (rate / 1000)
1596         end
1597 end
1598
1599 function wifinet.channel(self)
1600         return self.iwinfo.channel or self:ubus("dev", "config", "channel") or
1601                 tonumber(self:get("channel"))
1602 end
1603
1604 function wifinet.signal(self)
1605         return self.iwinfo.signal or 0
1606 end
1607
1608 function wifinet.noise(self)
1609         return self.iwinfo.noise or 0
1610 end
1611
1612 function wifinet.country(self)
1613         return self.iwinfo.country or self:ubus("dev", "config", "country") or "00"
1614 end
1615
1616 function wifinet.txpower(self)
1617         local pwr = (self.iwinfo.txpower or 0)
1618         return pwr + self:txpower_offset()
1619 end
1620
1621 function wifinet.txpower_offset(self)
1622         return self.iwinfo.txpower_offset or 0
1623 end
1624
1625 function wifinet.signal_level(self, s, n)
1626         if self:active_bssid() ~= "00:00:00:00:00:00" then
1627                 local signal = s or self:signal()
1628                 local noise  = n or self:noise()
1629
1630                 if signal < 0 and noise < 0 then
1631                         local snr = -1 * (noise - signal)
1632                         return math.floor(snr / 5)
1633                 else
1634                         return 0
1635                 end
1636         else
1637                 return -1
1638         end
1639 end
1640
1641 function wifinet.signal_percent(self)
1642         local qc = self.iwinfo.quality or 0
1643         local qm = self.iwinfo.quality_max or 0
1644
1645         if qc > 0 and qm > 0 then
1646                 return math.floor((100 / qm) * qc)
1647         else
1648                 return 0
1649         end
1650 end
1651
1652 function wifinet.shortname(self)
1653         return "%s %q" %{
1654                 lng.translate(self:active_mode()),
1655                 self:active_ssid() or self:active_bssid() or self:id()
1656         }
1657 end
1658
1659 function wifinet.get_i18n(self)
1660         return "%s: %s %q (%s)" %{
1661                 lng.translate("Wireless Network"),
1662                 lng.translate(self:active_mode()),
1663                 self:active_ssid() or self:active_bssid() or self:id(),
1664                 self:ifname()
1665         }
1666 end
1667
1668 function wifinet.adminlink(self)
1669         return dsp.build_url("admin", "network", "wireless", self.netid)
1670 end
1671
1672 function wifinet.get_network(self)
1673         return self:get_networks()[1]
1674 end
1675
1676 function wifinet.get_networks(self)
1677         local nets = { }
1678         local net
1679         for net in utl.imatch(self:ubus("net", "config", "network") or self:get("network")) do
1680                 if _uci:get("network", net) == "interface" then
1681                         nets[#nets+1] = network(net)
1682                 end
1683         end
1684         table.sort(nets, function(a, b) return a.sid < b.sid end)
1685         return nets
1686 end
1687
1688 function wifinet.get_interface(self)
1689         return interface(self:ifname())
1690 end
1691
1692
1693 -- setup base protocols
1694 _M:register_protocol("static")
1695 _M:register_protocol("dhcp")
1696 _M:register_protocol("none")
1697
1698 -- load protocol extensions
1699 local exts = nfs.dir(utl.libpath() .. "/model/network")
1700 if exts then
1701         local ext
1702         for ext in exts do
1703                 if ext:match("%.lua$") then
1704                         require("luci.model.network." .. ext:gsub("%.lua$", ""))
1705                 end
1706         end
1707 end