| 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 | |
|---|