libs: merge libs/uci into libs/core
[project/luci.git] / libs / core / luasrc / model / network.lua
1 --[[
2 LuCI - Network model
3
4 Copyright 2009 Jo-Philipp Wich <xm@subsignal.org>
5
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
9
10         http://www.apache.org/licenses/LICENSE-2.0
11
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.
17
18 ]]--
19
20 local type, pairs, ipairs, loadfile, table, i18n
21         = type, pairs, ipairs, loadfile, table, luci.i18n
22
23 local lmo = require "lmo"
24 local nxo = require "nixio"
25 local nfs = require "nixio.fs"
26 local iwi = require "iwinfo"
27 local ipc = require "luci.ip"
28 local utl = require "luci.util"
29 local uct = require "luci.model.uci.bind"
30
31 module "luci.model.network"
32
33 -- load extensions
34 local ext
35 local handler = { }
36
37 for ext in nfs.glob(utl.libpath() .. "/model/network/*.lua") do
38         if nfs.access(ext) then
39                 local m = loadfile(ext)
40                 if m then
41                         handler[#handler+1] = m()
42                 end
43         end
44 end
45
46 function foreach_handler(code, ...)
47         local h
48         for _, h in ipairs(handler) do
49                 if code(h, ...) then
50                         return true
51                 end
52         end
53         return false
54 end
55
56 local ub = uct.bind("network")
57 local ifs, brs, sws
58
59 function init(cursor)
60         if cursor then
61                 cursor:unload("network")
62                 cursor:load("network")
63                 ub:init(cursor)
64
65                 ifs = { }
66                 brs = { }
67                 sws = { }
68
69                 -- init handler
70                 foreach_handler(function(h)
71                         h:init(cursor)
72                         h:find_interfaces(ifs, brs)
73                 end)
74
75                 -- read interface information
76                 local n, i
77                 for n, i in ipairs(nxo.getifaddrs()) do
78                         local name = i.name:match("[^:]+")
79                         local prnt = name:match("^([^%.]+)%.")
80
81                         if not _M:ignore_interface(name) then
82                                 ifs[name] = ifs[name] or {
83                                         idx      = i.ifindex or n,
84                                         name     = name,
85                                         rawname  = i.name,
86                                         flags    = { },
87                                         ipaddrs  = { },
88                                         ip6addrs = { }
89                                 }
90
91                                 if prnt then
92                                         sws[name] = true
93                                         sws[prnt] = true
94                                 end
95
96                                 if i.family == "packet" then
97                                         ifs[name].flags   = i.flags
98                                         ifs[name].stats   = i.data
99                                         ifs[name].macaddr = i.addr
100                                 elseif i.family == "inet" then
101                                         ifs[name].ipaddrs[#ifs[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask)
102                                 elseif i.family == "inet6" then
103                                         ifs[name].ip6addrs[#ifs[name].ip6addrs+1] = ipc.IPv6(i.addr, i.netmask)
104                                 end
105                         end
106                 end
107
108                 -- read bridge informaton
109                 local b, l
110                 for l in utl.execi("brctl show") do
111                         if not l:match("STP") then
112                                 local r = utl.split(l, "%s+", nil, true)
113                                 if #r == 4 then
114                                         b = {
115                                                 name    = r[1],
116                                                 id      = r[2],
117                                                 stp     = r[3] == "yes",
118                                                 ifnames = { ifs[r[4]] }
119                                         }
120                                         if b.ifnames[1] then
121                                                 b.ifnames[1].bridge = b
122                                         end
123                                         brs[r[1]] = b
124                                 elseif b then
125                                         b.ifnames[#b.ifnames+1] = ifs[r[2]]
126                                         b.ifnames[#b.ifnames].bridge = b
127                                 end
128                         end
129                 end
130         end
131 end
132
133 function has_ipv6(self)
134         return nfs.access("/proc/net/ipv6_route")
135 end
136
137 function add_network(self, n, options)
138         if n and #n > 0 and n:match("^[a-zA-Z0-9_]+$") and not self:get_network(n) then
139                 if ub.uci:section("network", "interface", n, options) then
140                         return network(n)
141                 end
142         end
143 end
144
145 function get_network(self, n)
146         if n and ub.uci:get("network", n) == "interface" then
147                 return network(n)
148         end
149 end
150
151 function get_networks(self)
152         local nets = { }
153         ub.uci:foreach("network", "interface",
154                 function(s)
155                         nets[#nets+1] = network(s['.name'])
156                 end)
157         return nets
158 end
159
160 function del_network(self, n)
161         local r = ub.uci:delete("network", n)
162         if r then
163                 ub.uci:foreach("network", "alias",
164                         function(s)
165                                 if s.interface == n then
166                                         ub.uci:delete("network", s['.name'])
167                                 end
168                         end)
169                 ub.uci:foreach("network", "route",
170                         function(s)
171                                 if s.interface == n then
172                                         ub.uci:delete("network", s['.name'])
173                                 end
174                         end)
175                 ub.uci:foreach("network", "route6",
176                         function(s)
177                                 if s.interface == n then
178                                         ub.uci:delete("network", s['.name'])
179                                 end
180                         end)
181
182                 foreach_handler(function(h) h:del_network(n) end)
183         end
184         return r
185 end
186
187 function rename_network(self, old, new)
188         local r
189         if new and #new > 0 and new:match("^[a-zA-Z0-9_]+$") and not self:get_network(new) then
190                 r = ub.uci:section("network", "interface", new,
191                         ub.uci:get_all("network", old))
192
193                 if r then
194                         ub.uci:foreach("network", "alias",
195                                 function(s)
196                                         if s.interface == old then
197                                                 ub.uci:set("network", s['.name'], "interface", new)
198                                         end
199                                 end)
200                         ub.uci:foreach("network", "route",
201                                 function(s)
202                                         if s.interface == old then
203                                                 ub.uci:set("network", s['.name'], "interface", new)
204                                         end
205                                 end)
206                         ub.uci:foreach("network", "route6",
207                                 function(s)
208                                         if s.interface == old then
209                                                 ub.uci:set("network", s['.name'], "interface", new)
210                                         end
211                                 end)
212
213                         foreach_handler(function(h) h:rename_network(old, new) end)
214                 end
215         end
216         return r or false
217 end
218
219 function get_interface(self, i)
220         if ifs[i] then
221                 return interface(i)
222         else
223                 local j
224                 for j, _ in pairs(ifs) do
225                         if ifs[j].sid == i then
226                                 return interface(j)
227                         end
228                 end
229         end
230 end
231
232 function get_interfaces(self)
233         local ifaces = { }
234         local iface
235         for iface, _ in pairs(ifs) do
236                 ifaces[#ifaces+1] = interface(iface)
237         end
238         return ifaces
239 end
240
241 function ignore_interface(self, x)
242         if foreach_handler(function(h) return h:ignore_interface(x) end) then
243                 return true
244         else
245                 return (x:match("^wmaster%d") or x:match("^wifi%d")
246                         or x:match("^hwsim%d") or x:match("^imq%d") or x == "lo")
247         end
248 end
249
250
251 network = ub:section("interface")
252 network:property("device")
253 network:property("ifname")
254 network:property("proto")
255 network:property("type")
256
257 function network.name(self)
258         return self.sid
259 end
260
261 function network.is_bridge(self)
262         return (self:type() == "bridge")
263 end
264
265 function network.add_interface(self, ifname)
266         local ifaces, iface
267
268         if type(ifname) ~= "string" then
269                 ifaces = { ifname:name() }
270         else
271                 ifaces = ub:list(ifname)
272         end
273
274         for _, iface in ipairs(ifaces) do
275                 if ifs[iface] then
276                         -- make sure the interface is removed from all networks
277                         local i = interface(iface)
278                         local n = i:get_network()
279                         if n then n:del_interface(iface) end
280
281                         if ifs[iface].handler then
282                                 ifs[iface].handler:add_interface(self, iface, ifs[iface])
283                         else
284                                 self:ifname(ub:list((self:ifname() or ''), iface))
285                         end
286                 end
287         end
288 end
289
290 function network.del_interface(self, ifname)
291         if type(ifname) ~= "string" then
292                 ifname = ifname:name()
293         end
294
295         if ifs[ifname] and ifs[ifname].handler then
296                 ifs[ifname].handler:del_interface(self, ifname, ifs[ifname])
297         else
298                 self:ifname(ub:list((self:ifname() or ''), nil, ifname))
299         end
300 end
301
302 function network.get_interfaces(self)
303         local ifaces = { }
304         local iface
305         for _, iface in ipairs(ub:list(self:ifname())) do
306                 iface = iface:match("[^:]+")
307                 if ifs[iface] then
308                         ifaces[#ifaces+1] = interface(iface)
309                 end
310         end
311         for iface, _ in pairs(ifs) do
312                 if ifs[iface].network == self:name() then
313                         ifaces[#ifaces+1] = interface(iface)
314                 end
315         end
316         return ifaces
317 end
318
319 function network.contains_interface(self, iface)
320         local i
321         local ifaces = ub:list(self:ifname())
322
323         if type(iface) ~= "string" then
324                 iface = iface:name()
325         end
326
327         for _, i in ipairs(ifaces) do
328                 if i == iface then
329                         return true
330                 end
331         end
332
333         for i, _ in pairs(ifs) do
334                 if ifs[i].dev and ifs[i].dev.network == self:name() then
335                         return true
336                 end
337         end
338
339         return false
340 end
341
342
343 interface = utl.class()
344 function interface.__init__(self, ifname)
345         if ifs[ifname] then
346                 self.ifname = ifname
347                 self.dev    = ifs[ifname]
348                 self.br     = brs[ifname]
349         end
350 end
351
352 function interface.name(self)
353         return self.ifname
354 end
355
356 function interface.mac(self)
357         return self.dev.macaddr or "00:00:00:00:00:00"
358 end
359
360 function interface.ipaddrs(self)
361         return self.dev.ipaddrs or { }
362 end
363
364 function interface.ip6addrs(self)
365         return self.dev.ip6addrs or { }
366 end
367
368 function interface.type(self)
369         if self.dev and self.dev.type then
370                 return self.dev.type
371         elseif brs[self.ifname] then
372                 return "bridge"
373         elseif sws[self.ifname] or self.ifname:match("%.") then
374                 return "switch"
375         else
376                 return "ethernet"
377         end
378 end
379
380 function interface.shortname(self)
381         if self.dev and self.dev.handler then
382                 return self.dev.handler:shortname(self)
383         else
384                 return self.ifname
385         end
386 end
387
388 function interface.get_i18n(self)
389         if self.dev and self.dev.handler then
390                 return self.dev.handler:get_i18n(self)
391         else
392                 return "%s: %q" %{ self:get_type_i18n(), self:name() }
393         end
394 end
395
396 function interface.get_type_i18n(self)
397         local x = self:type()
398         if x == "wifi" then
399                 return i18n.translate("Wireless Adapter")
400         elseif x == "bridge" then
401                 return i18n.translate("Bridge")
402         elseif x == "switch" then
403                 return i18n.translate("Ethernet Switch")
404         else
405                 return i18n.translate("Ethernet Adapter")
406         end
407 end
408
409 function interface.ports(self)
410         if self.br then
411                 local iface
412                 local ifaces = { }
413                 for _, iface in ipairs(self.br.ifnames) do
414                         ifaces[#ifaces+1] = interface(iface.name)
415                 end
416                 return ifaces
417         end
418 end
419
420 function interface.bridge_id(self)
421         if self.br then
422                 return self.br.id
423         else
424                 return nil
425         end
426 end
427
428 function interface.bridge_stp(self)
429         if self.br then
430                 return self.br.stp
431         else
432                 return false
433         end
434 end
435
436 function interface.is_up(self)
437         return self.dev.flags and self.dev.flags.up
438 end
439
440 function interface.is_bridge(self)
441         return (self:type() == "bridge")
442 end
443
444 function interface.is_bridgeport(self)
445         return self.dev and self.dev.bridge and true or false
446 end
447
448 function interface.tx_bytes(self)
449         return self.dev and self.dev.stats
450                 and self.dev.stats.tx_bytes or 0
451 end
452
453 function interface.rx_bytes(self)
454         return self.dev and self.dev.stats
455                 and self.dev.stats.rx_bytes or 0
456 end
457
458 function interface.tx_packets(self)
459         return self.dev and self.dev.stats
460                 and self.dev.stats.tx_packets or 0
461 end
462
463 function interface.rx_packets(self)
464         return self.dev and self.dev.stats
465                 and self.dev.stats.rx_packets or 0
466 end
467
468 function interface.get_network(self)
469         if self.dev and self.dev.network then
470                 self.network = _M:get_network(self.dev.network)
471         end
472
473         if not self.network then
474                 local net
475                 for _, net in ipairs(_M:get_networks()) do
476                         if net:contains_interface(self.ifname) then
477                                 self.network = net
478                                 return net
479                         end
480                 end
481         else
482                 return self.network
483         end
484 end
485
486 --[==[
487 #!/usr/bin/lua
488
489 local uci = require "luci.model.uci".cursor_state()
490 local utl = require "luci.util"
491 local sys = require "luci.sys"
492 local lip = require "luci.ip"
493 local nxo = require "nixio"
494 local nfs = require "nixio.fs"
495
496 -- patch uci
497 local x = getmetatable(uci)
498
499 function x:list(...)
500         local val = self:get(...)
501         local lst = { }
502
503         if type(val) == "list" then
504                 local _, v
505                 for _, v in ipairs(val) do
506                         local i
507                         for i in v:gmatch("%S+") do
508                                 lst[#lst+1] = i
509                         end
510                 end
511         elseif type(val) == "string" then
512                 local i
513                 for i in val:gmatch("%S+") do
514                         lst[#lst+1] = i
515                 end
516         end
517
518         return lst
519 end
520
521
522 system = utl.class()
523
524 system._switches = { }
525 system._vlans    = { }
526
527 function system:__init__()
528         self._networks = { }
529
530         uci:foreach("network2", "interface",
531                 function(s)
532                         self._networks[#self._networks+1] = system.network(s, self)
533                 end)
534 end
535
536 function system:networks()
537         local index = 0
538         return function()
539                 if index <= #self._networks then
540                         index = index + 1
541                         return self._networks[index]
542                 else
543                         return nil
544                 end
545         end
546 end
547
548 function system:find_network(name)
549         local v
550         for _, v in ipairs(self._networks) do
551                 if v:name() == name then
552                         return v
553                 end
554         end
555 end
556
557 function system:find_interface(name)
558         local v
559         for _, v in ipairs(self._networks) do
560                 local i
561                 for i in v:interfaces() do
562                         if i:is_bridge() then
563                                 local p
564                                 for p in i:interfaces() do
565                                         if p:name() == name then
566                                                 return p
567                                         end
568                                 end
569                         end
570
571                         if i:name() == name then
572                                 return i
573                         end
574                 end
575         end
576 end
577
578 function system:delete_network(name)
579         local i
580         for i = 1, #self._networks do
581                 if self._networks[i]:name() == name then
582                         local x
583
584                         for x in self._networks[i]:aliases() do
585                                 uci:delete("network2", x:name())
586                         end
587
588                         for x in self._networks[i]:routes() do
589                                 uci:delete("network2", x:name())
590                         end
591
592                         uci:delete("network2", self._networks[i])
593                         table.remove(self._networks, i)
594
595                         return true
596                 end
597         end
598         return false
599 end
600
601 function system:print()
602         local v
603         for v in self:networks() do
604                 print(v:name())
605                 v:print()
606                 print("--")
607         end
608 end
609
610 function system.ignore_iface(ifn)
611         return (nil ~= (
612                 ifn:match("^wlan%d") or 
613                 ifn:match("^ath%d")  or
614                 ifn:match("^wl%d")   or
615                 ifn:match("^imq%d")  or
616                 ifn:match("^br%-")   or
617                 ifn:match("^/dev/")
618         ))
619 end
620
621 function system.find_wifi_networks(net)
622         local lst = { }
623         local cnt = 0
624
625         uci:foreach("wireless", "wifi-iface",
626                 function(s)
627                         if s.device and s.network == net then
628                                 lst[#lst+1] = { s.device, s['.name'], cnt }
629                         end
630                         cnt = cnt + 1
631                 end)
632
633         return lst
634 end
635
636 function system.find_iface_names(net)
637         local lst = { }
638
639         local val = uci:list("network2", net, "device")
640         if #val == 0 or val[1]:match("^/dev/") then
641                 val = uci:list("network2", net, "ifname")
642         end
643
644         local ifn
645         for _, ifn in ipairs(val) do
646                 if not system.ignore_iface(ifn) then
647                         lst[#lst+1] = ifn
648                 end
649         end
650         
651         return lst
652 end
653
654 function system.find_switch(name)
655         local swname, swdev, swvlan
656
657         -- find switch
658         uci:foreach("network2", "switch",
659                 function(s)
660                         swname = s.name or s['.name']
661
662                         -- special: rtl8366s is eth0 (wan is eth1)
663                         if swname == "rtl8366s" then
664                                 swdev = "eth0"
665
666                         -- special: rtl8366rb is eth0 (wan + lan)
667                         elseif swname == "rtl8366rb" then
668                                 swdev = "eth0"
669
670                         -- treat swname as swdev
671                         else
672                                 swdev = swname
673                         end
674
675                         return false
676                 end)
677
678         -- find first vlan
679         if swdev then
680                 uci:foreach("network2", "switch_vlan",
681                         function(s)
682                                 if s.device == swname then
683                                         local vlan = tonumber(s.vlan)
684                                         if vlan and (not swvlan or vlan < swvlan) then
685                                                 swvlan = vlan
686                                         end
687                                 end
688                         end)
689         end
690
691
692         local veth, vlan = name:match("^(%S+)%.(%d+)$")
693
694         -- have vlan id and matching switch
695         if vlan and veth == swdev then
696                 return swname, swdev, vlan
697
698         -- have no vlan id but matching switch, assume first switch vlan
699         elseif not vlan and name == swdev then
700                 return swname, swdev, swvlan
701
702         -- have vlan and no matching switch, assume software vlan
703         elseif vlan then
704                 return nil, veth, vlan
705         end
706 end
707
708
709 system.network = utl.class()
710
711 function system.network:__init__(s, sys)
712         self._name    = s['.name']
713         self._sys     = sys
714         self._routes  = { }
715         self._aliases = { }
716
717         if s.type == "bridge" then
718                 self._interfaces = { system.network.bridge(s['.name'], self) }
719         else
720                 self._interfaces = { }
721
722                 local ifn
723
724                 -- find wired ifaces
725                 for _, ifn in ipairs(system.find_iface_names(self._name)) do
726                         self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self)
727                 end
728
729                 -- find wifi networks
730                 for _, ifn in ipairs(system.find_wifi_networks(self._name)) do
731                         self._interfaces[#self._interfaces+1] = system.network.iface(ifn, self)
732                 end
733         end
734
735         -- find ipv4 routes
736         uci:foreach("network2", "route",
737                 function(s)
738                         if s.interface == self._name and s.target then
739                                 self._routes[#self._routes+1] = system.network.route(s, self)
740                         end
741                 end)
742
743         -- find ipv6 routes
744         uci:foreach("network2", "route6",
745                 function(s)
746                         if s.interface == self._name and s.target then
747                                 self._routes[#self._routes+1] = system.network.route(s, self)
748                         end
749                 end)
750
751         -- find aliases
752         uci:foreach("network2", "alias",
753                 function(s)
754                         if s.interface == self._name and s.proto then
755                                 self._aliases[#self._aliases+1] = system.network.alias(s, self)
756                         end
757                 end)
758 end
759
760 function system.network:name()
761         return self._name
762 end
763
764 function system.network:system()
765         return self._sys
766 end
767
768 function system.network:interfaces()
769         local index = 0
770         return function()
771                 if index <= #self._interfaces then
772                         index = index + 1
773                         return self._interfaces[index]
774                 else
775                         return nil
776                 end
777         end
778 end
779
780 function system.network:interface()
781         return self._interfaces[1]
782 end
783
784 function system.network:num_routes()
785         return #self._routes
786 end
787
788 function system.network:routes()
789         local index = 0
790         return function()
791                 if index <= #self._routes then
792                         index = index + 1
793                         return self._routes[index]
794                 else
795                         return nil
796                 end
797         end
798 end
799
800 function system.network:num_aliases()
801         return #self._aliases
802 end
803
804 function system.network:aliases()
805         local index = 0
806         return function()
807                 if index <= #self._aliases then
808                         index = index + 1
809                         return self._aliases[index]
810                 else
811                         return nil
812                 end
813         end
814 end
815
816 function system.network:delete_route(rt)
817         local i
818         for i = 1, #self._routes do
819                 if self._routes[i]:name() == rt:name() then
820                         uci:delete("network2", rt:name())
821                         table.remove(self._routes, i)
822                         return true
823                 end
824         end
825         return false
826 end
827
828 function system.network:delete_alias(al)
829         local i
830         for i = 1, #self._aliases do
831                 if self._aliases[i]:name() == al:name() then
832                         uci:delete("network2", al:name())
833                         table.remove(self._aliases, i)
834                         return true
835                 end
836         end
837         return false
838 end
839
840 function system.network:print()
841         self:interface():print()
842 end
843
844
845 system.network.iface = utl.class()
846
847 function system.network.iface:__init__(ifn, net, parent)
848         self._net    = net
849         self._parent = parent
850
851         -- is a wifi iface
852         if type(ifn) == "table" then
853                 local wifidev, network, index = unpack(ifn)
854
855                 self._name    = "%s.%d" %{ wifidev, index }
856                 self._wifidev = wifidev
857                 self._wifinet = index
858                 self._ifname  = uci:get("wireless", network, "ifname") or self._name
859
860         -- is a wired iface
861         else
862                 self._name   = ifn
863                 self._ifname = ifn
864
865                 local switch, swdev, vlan = system.find_switch(self._ifname)
866
867                 if switch then
868                         self._switch = system.switch(switch, swdev, self)
869                 end
870
871                 if vlan then
872                         self._vlan = system.vlan(vlan, self._switch, self)
873                 end
874         end
875 end
876
877 function system.network.iface:name()
878         return self._name
879 end
880
881 function system.network.iface:parent()
882         return self._parent
883 end
884
885 function system.network.iface:network()
886         return self._net
887 end
888
889 function system.network.iface:is_managed()
890         return (self._net ~= nil)
891 end
892
893 function system.network.iface:is_vlan()
894         return (self._vlan ~= nil)
895 end
896
897 function system.network.iface:is_software_vlan()
898         return (not self._switch and self._vlan ~= nil)
899 end
900
901 function system.network.iface:is_hardware_vlan()
902         return (self._switch ~= nil and self._vlan ~= nil)
903 end
904
905 function system.network.iface:_sysfs(path, default)
906         path = "/sys/class/net/%s/%s" %{ self._ifname, path }
907
908         local data = nfs.readfile(path)
909
910         if type(default) == "number" then
911                 return tonumber(data) or default
912         elseif data and #data > 0 then
913                 return data and data:gsub("%s+$", "") or default
914         end
915
916         return default
917 end
918
919 function system.network.iface:rx_bytes()
920         return self:_sysfs("statistics/rx_bytes", 0)
921 end
922
923 function system.network.iface:tx_bytes()
924         return self:_sysfs("statistics/tx_bytes", 0)
925 end
926
927 function system.network.iface:rx_packets()
928         return self:_sysfs("statistics/rx_packets", 0)
929 end
930
931 function system.network.iface:tx_packets()
932         return self:_sysfs("statistics/tx_packets", 0)
933 end
934
935 function system.network.iface:macaddr()
936         return self:_sysfs("address")
937 end
938
939 function system.network.iface:mtu()
940         return self:_sysfs("mtu", 1500)
941 end
942
943 function system.network.iface:is_bridge()
944         return (self:_sysfs("bridge/max_age", 0) > 0)
945 end
946
947 function system.network.iface:is_bridge_port()
948         return (self:_sysfs("brport/port_no", 0) > 0)
949 end
950
951 function system.network.iface:delete()
952         if self._wifidev then
953                 local cnt = 0
954                 uci:foreach("wireless", "wifi-iface", 
955                         function(s)
956                                 cnt = cnt + 1
957                                 if s.device == self._wifidev and cnt == self._wifinet then
958                                         uci:delete("wireless", s['.name'])
959                                         return false
960                                 end
961                         end)
962         end
963 end
964
965 function system.network.iface:print()
966         if self._wifidev then
967                 print("  wifi: ", self._wifidev, "net: ", self._wifinet)
968         else
969                 print("  iface: ", self._name)
970         end
971
972         print("    rx: ", self:rx_bytes(), self:rx_packets())
973         print("    tx: ", self:tx_bytes(), self:tx_packets())
974         print("    mtu: ", self:mtu())
975         print("    mac: ", self:macaddr())
976         print("    bridge? ", self:is_bridge())
977         print("    port? ", self:is_bridge_port())
978         print("    swvlan? ", self:is_software_vlan())
979         print("    hwvlan? ", self:is_hardware_vlan())
980
981         if self._switch then
982                 self._switch:print()
983         end
984
985         if self._vlan then
986                 self._vlan:print()
987         end
988 end
989
990
991 system.network.bridge = utl.class(system.network.iface)
992
993 function system.network.bridge:__init__(brn, net)
994         self._net    = net
995         self._name   = "br-" .. brn
996         self._ifname = self._name
997         self._interfaces = { }
998
999         local ifn
1000
1001         -- find wired ifaces
1002         for _, ifn in ipairs(system.find_iface_names(brn)) do
1003                 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self)
1004         end
1005
1006         -- find wifi networks
1007         for _, ifn in ipairs(system.find_wifi_networks(brn)) do
1008                 self._interfaces[#self._interfaces+1] = system.network.iface(ifn, net, self)
1009         end
1010 end
1011
1012 function system.network.bridge:interfaces()
1013         local index = 0
1014         return function()
1015                 if index <= #self._interfaces then
1016                         index = index + 1
1017                         return self._interfaces[index]
1018                 else
1019                         return nil
1020                 end
1021         end
1022 end
1023
1024 function system.network.bridge:print()
1025         local v
1026         for v in self:interfaces() do
1027                 io.write("  port: ")
1028                 v:print()
1029         end
1030         print("  rx: ", self:rx_bytes(), self:rx_packets())
1031         print("  tx: ", self:tx_bytes(), self:tx_packets())
1032         print("  mtu: ", self:mtu())
1033         print("  mac: ", self:macaddr())
1034         print("  bridge? ", self:is_bridge())
1035         print("  port? ", self:is_bridge_port())
1036 end
1037
1038
1039 system.network.route = utl.class()
1040
1041 function system.network.route:__init__(rt, net)
1042         self._net    = net
1043         self._name   = rt['.name']
1044         self._ipv6   = (rt['.type'] == "route6")
1045         self._mtu    = tonumber(rt.mtu) or (net and net:interface():mtu() or 1500)
1046         self._metric = tonumber(rt.metric) or 0
1047
1048         if self._ipv6 then
1049                 self._gateway = lip.IPv6(rt.gateway or "::")
1050                 self._target  = lip.IPv6(rt.target  or "::")
1051         else
1052                 self._gateway = lip.IPv4(rt.gateway or "0.0.0.0")
1053                 self._target  = lip.IPv4(rt.target  or "0.0.0.0", rt.netmask or "0.0.0.0")
1054         end
1055 end
1056
1057 function system.network.route:name()
1058         return self._name
1059 end
1060
1061 function system.network.route:network()
1062         return self._net
1063 end
1064
1065 function system.network.route:mtu()
1066         return self._mtu
1067 end
1068
1069 function system.network.route:metric()
1070         return self._metric
1071 end
1072
1073 function system.network.route:is_ipv4()
1074         return not self._ipv6
1075 end
1076
1077 function system.network.route:is_ipv6()
1078         return self._ipv6
1079 end
1080
1081 function system.network.route:target()
1082         return self._target
1083 end
1084
1085 function system.network.route:gateway()
1086         return self._gateway
1087 end
1088
1089
1090 system.network.alias = utl.class()
1091
1092 function system.network.alias:__init__(a, net)
1093         self._net  = net
1094         self._name = a['.name']
1095 end
1096
1097
1098 system.switch = utl.class()
1099
1100 function system.switch:__init__(switch, swdev, net)
1101         self._name   = switch
1102         self._ifname = swdev
1103         self._net    = net
1104
1105         if not system._switches[switch] then
1106                 local x = io.popen("swconfig dev %q help 2>/dev/null" % switch)
1107                 if x then
1108                         local desc = x:read("*l")
1109
1110                         if desc then
1111                                 local name, num_ports, num_cpu, num_vlans =
1112                                         desc:match("Switch %d: %S+%((.-)%), ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)")
1113
1114                                 self._model   = name
1115                                 self._ports   = tonumber(num_ports)
1116                                 self._cpuport = tonumber(num_cpu)
1117                                 self._vlans   = tonumber(num_vlans)
1118                         end
1119
1120                         x:close()
1121
1122                 elseif nfs.access("/proc/switch/%s" % switch) then
1123                         self._model   = self:_proc("driver", switch)
1124                         self._ports   = self:_proc_count("port", 6)
1125                         self._vlans   = self:_proc_count("vlan", 16)
1126                 end
1127
1128                 -- defaults
1129                 self._model   = self._model   or switch
1130                 self._ports   = self._ports   or 6
1131                 self._vlans   = self._vlans   or 16
1132                 self._cpuport = self._cpuport or 5
1133
1134                 system._switches[switch] = self
1135         else
1136                 self._model   = system._switches[switch]._model
1137                 self._ports   = system._switches[switch]._ports
1138                 self._vlans   = system._switches[switch]._vlans
1139                 self._cpuport = system._switches[switch]._cpuport
1140         end
1141 end
1142
1143 function system.switch:_proc(path, default)
1144         local data = nfs.readfile("/proc/switch/%s/%s" %{ self._name, path })
1145         if data then
1146                 return data:gsub("%s+$", "")
1147         end
1148         return default
1149 end
1150
1151 function system.switch:_proc_count(path, default)
1152         local cnt = 0
1153         for _ in nfs.dir("/proc/switch/%s/%s" %{ self._name, path }) do
1154                 cnt = cnt + 1
1155         end
1156         return cnt > 0 and cnt or default
1157 end
1158
1159 function system.switch:name()
1160         return self._name
1161 end
1162
1163 function system.switch:model()
1164         return self._model
1165 end
1166
1167 function system.switch:num_possible_vlans()
1168         return self._vlans
1169 end
1170
1171 function system.switch:num_active_vlans()
1172         local cnt = 0
1173         uci:foreach("network2", "switch_vlan",
1174                 function(s)
1175                         if s.device == self._name then cnt = cnt + 1 end
1176                 end)
1177         return cnt
1178 end
1179
1180 function system.switch:vlans()
1181         local index = 0
1182         local vlans = { }
1183
1184         uci:foreach("network2", "switch_vlan",
1185                 function(s)
1186                         if s.device == self._name and tonumber(s.vlan) then
1187                                 vlans[#vlans+1] = tonumber(s.vlan)
1188                         end
1189                 end)
1190
1191         return function()
1192                 if index <= #vlans then
1193                         index = index + 1
1194                         return system.vlan(vlans[index], self)
1195                 else
1196                         return nil
1197                 end
1198         end
1199 end
1200
1201 function system.switch:num_ports()
1202         return self._ports
1203 end
1204
1205 function system.switch:delete_vlan(vlan)
1206         local rv = false
1207
1208         uci:foreach("network2", "switch_vlan",
1209                 function(s)
1210                         if s.device == self._name and tonumber(s.vlan) == vlan then
1211                                 rv = true
1212                                 uci:delete("network2", s['.name'])
1213
1214                                 if system._vlans[s.device] and system._vlans[s.device][vlan] then
1215                                         table.remove(system._vlans[s.device], vlan)
1216                                 end
1217
1218                                 return false
1219                         end
1220                 end)
1221
1222         return rv
1223 end
1224
1225 function system.switch:print()
1226         print("Switch:", self._model)
1227         print("  Ports:", self._ports, "Cpu:", self._cpuport)
1228         print("  Vlans:", self._vlans)
1229 end
1230
1231
1232 system.vlan = utl.class()
1233
1234 function system.vlan:__init__(vlan, switch, iface)
1235         self._vlan   = vlan
1236         self._switch = switch
1237         self._iface  = iface
1238
1239         local swid = (switch and switch:name()) or (iface and iface:name()) or ""
1240
1241         if not system._vlans[swid] or not system._vlans[swid][vlan] then
1242                 self._ports  = { }
1243
1244                 if switch then
1245                         uci:foreach("network2", "switch_vlan",
1246                                 function(s)
1247                                         if s.device == switch:name() and tonumber(s.vlan) == vlan then
1248                                                 local p
1249                                                 for _, p in ipairs(uci:list("network2", s['.name'], "ports")) do
1250                                                         self._ports[#self._ports+1] = system.vlan.port(p, self)
1251                                                 end
1252                                                 self._name = s['.name']
1253                                         end
1254                                 end)
1255                 else
1256                         self._ports[#self._ports+1] = system.vlan.port("0t", self)
1257                 end
1258
1259                 system._vlans[swid] = system._vlans[swid] or { }
1260                 system._vlans[swid][vlan] = self
1261         else
1262                 self._ports = system._vlans[swid][vlan]._ports
1263         end
1264 end
1265
1266 function system.vlan:name()
1267         return self._name
1268 end
1269
1270 function system.vlan:number()
1271         return self._vlan
1272 end
1273
1274 function system.vlan:switch()
1275         return self._switch
1276 end
1277
1278 function system.vlan:interface()
1279         return self._iface
1280 end
1281
1282 function system.vlan:is_software()
1283         return (self._switch == nil)
1284 end
1285
1286 function system.vlan:is_hardware()
1287         return not self:is_software()
1288 end
1289
1290 function system.vlan:num_ports()
1291         return #self._ports
1292 end
1293
1294 function system.vlan:ports()
1295         local index = 0
1296         return function()
1297                 if index <= #self._ports then
1298                         index = index + 1
1299                         return self._ports[index]
1300                 else
1301                         return nil
1302                 end
1303         end
1304 end
1305
1306 function system.vlan:_update()
1307         local i
1308         local ports = { }
1309
1310         for i = 1, #self._ports do
1311                 ports[#ports+1] = self._ports[i]:string()
1312         end
1313
1314         uci:set("network2", self._name, "ports", table.concat(ports, " "))
1315 end
1316
1317 function system.vlan:delete_port(port)
1318         if self._switch then
1319                 local i
1320                 for i = 1, #self._ports do
1321                         if self._ports[i]:number() == port then
1322                                 table.remove(self._ports, i)
1323                                 self:_update()
1324                                 return true
1325                         end
1326                 end
1327         end
1328         return false
1329 end
1330
1331 function system.vlan:print()
1332         print(" Vlan:", self._vlan, "Software?", self:is_software())
1333         local p
1334         for p in self:ports() do
1335                 p:print()
1336         end
1337 end
1338
1339
1340 system.vlan.port = utl.class()
1341
1342 function system.vlan.port:__init__(port, vlan)
1343         local num, tag = port:match("^(%d+)([tu]?)")
1344
1345         self._vlan   = vlan
1346         self._port   = tonumber(num)
1347         self._tagged = (tag == "t")
1348 end
1349
1350 function system.vlan.port:number()
1351         return self._port
1352 end
1353
1354 function system.vlan.port:vlan()
1355         return self._vlan
1356 end
1357
1358 function system.vlan.port:string()
1359         return "%i%s" %{ self._port, self._tagged ? "t" : "" }
1360 end
1361
1362 function system.vlan.port:is_tagged()
1363         return self._tagged
1364 end
1365
1366 function system.vlan.port:print()
1367         print("  Port:", self._port, "Tagged:", self._tagged)
1368 end
1369
1370
1371 -- ------------------------------
1372
1373 local s = system()
1374
1375 s:print()
1376
1377 s:find_network("wan"):print()
1378 s:find_interface("eth0"):parent():print()
1379
1380 ]==]