+ if (s['.type'] == 'wifi-iface' && s.device)
+ {
+ var r = parseInt(s.device.replace(/^[^0-9]+/, ''));
+ var n = wificount[s.device] = (wificount[s.device] || 0) + 1;
+ var id = 'radio%d.network%d'.format(r, n);
+ var ifname = id;
+
+ if (self.rpcCache.wifistate[s.device])
+ {
+ var ifcs = self.rpcCache.wifistate[s.device].interfaces;
+ for (var ifc in ifcs)
+ {
+ if (ifcs[ifc].section == sid && ifcs[ifc].ifname)
+ {
+ ifname = ifcs[ifc].ifname;
+ break;
+ }
+ }
+ }
+
+ var entry = self.getDeviceObject(ifname);
+
+ entry.kind = 'wifi';
+ entry.sid = sid;
+ entry.wid = id;
+ entry.wdev = s.device;
+ entry.wmode = s.mode;
+ entry.wssid = s.ssid;
+ entry.wbssid = s.bssid;
+ }
+ }
+
+ for (var i = 0; i < net.length; i++)
+ {
+ var s = net[i];
+ var sid = s['.name'];
+
+ if (s['.type'] == 'interface' && !s['.anonymous'] && s.type == 'bridge')
+ {
+ var ifnames = L.toArray(s.ifname);
+
+ for (var ifname in self.deviceObjects)
+ {
+ var dev = self.deviceObjects[ifname];
+
+ if (dev.kind != 'wifi')
+ continue;
+
+ var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network'));
+ if ($.inArray(sid, wnets) > -1)
+ ifnames.push(ifname);
+ }
+
+ entry = self.getDeviceObject('br-%s'.format(s['.name']));
+ entry.type = 1;
+ entry.kind = 'bridge';
+ entry.sid = sid;
+ entry.ports = ifnames.sort();
+ }
+ }
+ },
+
+ loadInterfacesCallback: function()
+ {
+ var self = L.NetworkModel;
+ var net = L.uci.sections('network');
+
+ for (var i = 0; i < net.length; i++)
+ {
+ var s = net[i];
+ var sid = s['.name'];
+
+ if (s['.type'] == 'interface' && !s['.anonymous'] && s.proto)
+ {
+ var entry = self.getInterfaceObject(s['.name']);
+ var proto = self.protocolHandlers[s.proto] || self.protocolHandlers.none;
+
+ var l3dev = undefined;
+ var l2dev = undefined;
+
+ var ifnames = L.toArray(s.ifname);
+
+ for (var ifname in self.deviceObjects)
+ {
+ var dev = self.deviceObjects[ifname];
+
+ if (dev.kind != 'wifi')
+ continue;
+
+ var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network'));
+ if ($.inArray(entry.name, wnets) > -1)
+ ifnames.push(ifname);
+ }
+
+ if (proto.virtual)
+ l3dev = '%s-%s'.format(s.proto, entry.name);
+ else if (s.type == 'bridge')
+ l3dev = 'br-%s'.format(entry.name);
+ else
+ l3dev = ifnames[0];
+
+ if (!proto.virtual && s.type == 'bridge')
+ l2dev = 'br-%s'.format(entry.name);
+ else if (!proto.virtual)
+ l2dev = ifnames[0];
+
+ entry.proto = proto;
+ entry.sid = sid;
+ entry.l3dev = l3dev;
+ entry.l2dev = l2dev;
+ }
+ }
+
+ for (var i = 0; i < self.rpcCache.ifstate.length; i++)
+ {
+ var iface = self.rpcCache.ifstate[i];
+ var entry = self.getInterfaceObject(iface['interface']);
+ var proto = self.protocolHandlers[iface.proto] || self.protocolHandlers.none;
+
+ /* this is a virtual interface, either deleted from config but
+ not applied yet or set up from external tools (6rd) */
+ if (!entry.sid)
+ {
+ entry.proto = proto;
+ entry.l2dev = iface.device;
+ entry.l3dev = iface.l3_device;
+ }
+ }
+ },
+
+ init: function()
+ {
+ var self = this;
+
+ if (self.rpcCache)
+ return L.deferrable();
+
+ self.rpcCache = { };
+ self.deviceObjects = { };
+ self.interfaceObjects = { };
+ self.protocolHandlers = { };
+
+ return self.loadCache()
+ .then(self.loadProtocolHandlers)
+ .then(self.loadDevicesCallback)
+ .then(self.loadInterfacesCallback);
+ },
+
+ update: function()
+ {
+ delete this.rpcCache;
+ return this.init();
+ },
+
+ refreshInterfaceStatus: function()
+ {
+ return this.loadCache(1).then(this.loadInterfacesCallback);
+ },
+
+ refreshDeviceStatus: function()
+ {
+ return this.loadCache(2).then(this.loadDevicesCallback);
+ },
+
+ refreshStatus: function()
+ {
+ return this.loadCache(1)
+ .then(this.loadCache(2))
+ .then(this.loadDevicesCallback)
+ .then(this.loadInterfacesCallback);
+ },
+
+ getDevices: function()
+ {
+ var devs = [ ];
+
+ for (var ifname in this.deviceObjects)
+ if (ifname != 'lo')
+ devs.push(new L.NetworkModel.Device(this.deviceObjects[ifname]));
+
+ return devs.sort(this.sortDevicesCallback);
+ },
+
+ getDeviceByInterface: function(iface)
+ {
+ if (iface instanceof L.NetworkModel.Interface)
+ iface = iface.name();
+
+ if (this.interfaceObjects[iface])
+ return this.getDevice(this.interfaceObjects[iface].l3dev) ||
+ this.getDevice(this.interfaceObjects[iface].l2dev);
+
+ return undefined;
+ },
+
+ getDevice: function(ifname)
+ {
+ if (this.deviceObjects[ifname])
+ return new L.NetworkModel.Device(this.deviceObjects[ifname]);
+
+ return undefined;
+ },
+
+ createDevice: function(name)
+ {
+ return new L.NetworkModel.Device(this.getDeviceObject(name));
+ },
+
+ getInterfaces: function()
+ {
+ var ifaces = [ ];
+
+ for (var name in this.interfaceObjects)
+ if (name != 'loopback')
+ ifaces.push(this.getInterface(name));
+
+ ifaces.sort(function(a, b) {
+ if (a.name() < b.name())
+ return -1;
+ else if (a.name() > b.name())
+ return 1;
+ else
+ return 0;
+ });
+
+ return ifaces;
+ },
+
+ getInterfacesByDevice: function(dev)
+ {
+ var ifaces = [ ];
+
+ if (dev instanceof L.NetworkModel.Device)
+ dev = dev.name();
+
+ for (var name in this.interfaceObjects)
+ {
+ var iface = this.interfaceObjects[name];
+ if (iface.l2dev == dev || iface.l3dev == dev)
+ ifaces.push(this.getInterface(name));
+ }
+
+ ifaces.sort(function(a, b) {
+ if (a.name() < b.name())
+ return -1;
+ else if (a.name() > b.name())
+ return 1;
+ else
+ return 0;
+ });
+
+ return ifaces;
+ },
+
+ getInterface: function(iface)
+ {
+ if (this.interfaceObjects[iface])
+ return new L.NetworkModel.Interface(this.interfaceObjects[iface]);
+
+ return undefined;
+ },
+
+ getProtocols: function()
+ {
+ var rv = [ ];
+
+ for (var proto in this.protocolHandlers)
+ {
+ var pr = this.protocolHandlers[proto];
+
+ rv.push({
+ name: proto,
+ description: pr.description,
+ virtual: pr.virtual,
+ tunnel: pr.tunnel
+ });
+ }
+
+ return rv.sort(function(a, b) {
+ if (a.name < b.name)
+ return -1;
+ else if (a.name > b.name)
+ return 1;
+ else
+ return 0;
+ });
+ },
+
+ findWANByAddr: function(ipaddr)
+ {
+ for (var i = 0; i < this.rpcCache.ifstate.length; i++)
+ {
+ var ifstate = this.rpcCache.ifstate[i];
+
+ if (!ifstate.route)
+ continue;
+
+ for (var j = 0; j < ifstate.route.length; j++)
+ if (ifstate.route[j].mask == 0 &&
+ ifstate.route[j].target == ipaddr &&
+ typeof(ifstate.route[j].table) == 'undefined')
+ {
+ return this.getInterface(ifstate['interface']);
+ }
+ }
+
+ return undefined;
+ },
+
+ findWAN: function()
+ {
+ return this.findWANByAddr('0.0.0.0');
+ },
+
+ findWAN6: function()
+ {
+ return this.findWANByAddr('::');
+ },
+
+ resolveAlias: function(ifname)
+ {
+ if (ifname instanceof L.NetworkModel.Device)
+ ifname = ifname.name();
+
+ var dev = this.deviceObjects[ifname];
+ var seen = { };
+
+ while (dev && dev.kind == 'alias')
+ {
+ // loop
+ if (seen[dev.ifname])
+ return undefined;
+
+ var ifc = this.interfaceObjects[dev.sid];
+
+ seen[dev.ifname] = true;
+ dev = ifc ? this.deviceObjects[ifc.l3dev] : undefined;
+ }
+
+ return dev ? this.getDevice(dev.ifname) : undefined;
+ }
+ };
+
+ this.NetworkModel.Device = Class.extend({
+ wifiModeStrings: {
+ ap: L.tr('Master'),
+ sta: L.tr('Client'),
+ adhoc: L.tr('Ad-Hoc'),
+ monitor: L.tr('Monitor'),
+ wds: L.tr('Static WDS')
+ },
+
+ getStatus: function(key)
+ {
+ var s = L.NetworkModel.rpcCache.devstate[this.options.ifname];
+
+ if (s)
+ return key ? s[key] : s;
+
+ return undefined;
+ },
+
+ get: function(key)
+ {
+ var sid = this.options.sid;
+ var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network';
+ return L.uci.get(pkg, sid, key);
+ },
+
+ set: function(key, val)
+ {
+ var sid = this.options.sid;
+ var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network';
+ return L.uci.set(pkg, sid, key, val);
+ },
+
+ init: function()
+ {
+ if (typeof(this.options.type) == 'undefined')
+ this.options.type = 1;
+
+ if (typeof(this.options.kind) == 'undefined')
+ this.options.kind = 'ethernet';
+
+ if (typeof(this.options.networks) == 'undefined')
+ this.options.networks = [ ];
+ },
+
+ name: function()
+ {
+ return this.options.ifname;
+ },
+
+ description: function()
+ {
+ switch (this.options.kind)
+ {
+ case 'alias':
+ return L.tr('Alias for network "%s"').format(this.options.ifname.substring(1));
+
+ case 'bridge':
+ return L.tr('Network bridge');
+
+ case 'ethernet':
+ return L.tr('Network device');
+
+ case 'tunnel':
+ switch (this.options.type)
+ {
+ case 1: /* tuntap */
+ return L.tr('TAP device');
+
+ case 512: /* PPP */
+ return L.tr('PPP tunnel');
+
+ case 768: /* IP-IP Tunnel */
+ return L.tr('IP-in-IP tunnel');
+
+ case 769: /* IP6-IP6 Tunnel */
+ return L.tr('IPv6-in-IPv6 tunnel');
+
+ case 776: /* IPv6-in-IPv4 */
+ return L.tr('IPv6-over-IPv4 tunnel');
+ break;
+
+ case 778: /* GRE over IP */
+ return L.tr('GRE-over-IP tunnel');
+
+ default:
+ return L.tr('Tunnel device');
+ }
+
+ case 'vlan':
+ return L.tr('VLAN %d on %s').format(this.options.vid, this.options.vsw.model);
+
+ case 'wifi':
+ var o = this.options;
+ return L.trc('(Wifi-Mode) "(SSID)" on (radioX)', '%s "%h" on %s').format(
+ o.wmode ? this.wifiModeStrings[o.wmode] : L.tr('Unknown mode'),
+ o.wssid || '?', o.wdev
+ );
+ }
+
+ return L.tr('Unknown device');
+ },
+
+ icon: function(up)
+ {
+ var kind = this.options.kind;
+
+ if (kind == 'alias')
+ kind = 'ethernet';
+
+ if (typeof(up) == 'undefined')
+ up = this.isUp();
+
+ return L.globals.resource + '/icons/%s%s.png'.format(kind, up ? '' : '_disabled');
+ },
+
+ isUp: function()
+ {
+ var l = L.NetworkModel.rpcCache.devlist;
+
+ for (var i = 0; i < l.length; i++)
+ if (l[i].device == this.options.ifname)
+ return (l[i].is_up === true);
+
+ return false;
+ },
+
+ isAlias: function()
+ {
+ return (this.options.kind == 'alias');
+ },
+
+ isBridge: function()
+ {
+ return (this.options.kind == 'bridge');
+ },
+
+ isBridgeable: function()
+ {
+ return (this.options.type == 1 && this.options.kind != 'bridge');
+ },
+
+ isWireless: function()
+ {
+ return (this.options.kind == 'wifi');
+ },
+
+ isInNetwork: function(net)
+ {
+ if (!(net instanceof L.NetworkModel.Interface))
+ net = L.NetworkModel.getInterface(net);
+
+ if (net)
+ {
+ if (net.options.l3dev == this.options.ifname ||
+ net.options.l2dev == this.options.ifname)
+ return true;
+
+ var dev = L.NetworkModel.deviceObjects[net.options.l2dev];
+ if (dev && dev.kind == 'bridge' && dev.ports)
+ return ($.inArray(this.options.ifname, dev.ports) > -1);
+ }
+
+ return false;
+ },
+
+ getMTU: function()
+ {
+ var dev = L.NetworkModel.rpcCache.devstate[this.options.ifname];
+ if (dev && !isNaN(dev.mtu))
+ return dev.mtu;
+
+ return undefined;
+ },
+
+ getMACAddress: function()
+ {
+ if (this.options.type != 1)
+ return undefined;
+
+ var dev = L.NetworkModel.rpcCache.devstate[this.options.ifname];
+ if (dev && dev.macaddr)
+ return dev.macaddr.toUpperCase();
+
+ return undefined;
+ },
+
+ getInterfaces: function()
+ {
+ return L.NetworkModel.getInterfacesByDevice(this.options.name);
+ },
+
+ getStatistics: function()
+ {
+ var s = this.getStatus('statistics') || { };
+ return {
+ rx_bytes: (s.rx_bytes || 0),
+ tx_bytes: (s.tx_bytes || 0),
+ rx_packets: (s.rx_packets || 0),
+ tx_packets: (s.tx_packets || 0)
+ };
+ },
+
+ getTrafficHistory: function()
+ {
+ var def = new Array(120);
+
+ for (var i = 0; i < 120; i++)
+ def[i] = 0;
+
+ var h = L.NetworkModel.rpcCache.bwstate[this.options.ifname] || { };
+ return {
+ rx_bytes: (h.rx_bytes || def),
+ tx_bytes: (h.tx_bytes || def),
+ rx_packets: (h.rx_packets || def),
+ tx_packets: (h.tx_packets || def)
+ };
+ },
+
+ removeFromInterface: function(iface)
+ {
+ if (!(iface instanceof L.NetworkModel.Interface))
+ iface = L.NetworkModel.getInterface(iface);
+
+ if (!iface)
+ return;
+
+ var ifnames = L.toArray(iface.get('ifname'));
+ if ($.inArray(this.options.ifname, ifnames) > -1)
+ iface.set('ifname', L.filterArray(ifnames, this.options.ifname));
+
+ if (this.options.kind != 'wifi')
+ return;
+
+ var networks = L.toArray(this.get('network'));
+ if ($.inArray(iface.name(), networks) > -1)
+ this.set('network', L.filterArray(networks, iface.name()));
+ },
+
+ attachToInterface: function(iface)
+ {
+ if (!(iface instanceof L.NetworkModel.Interface))
+ iface = L.NetworkModel.getInterface(iface);
+
+ if (!iface)
+ return;
+
+ if (this.options.kind != 'wifi')
+ {
+ var ifnames = L.toArray(iface.get('ifname'));
+ if ($.inArray(this.options.ifname, ifnames) < 0)
+ {
+ ifnames.push(this.options.ifname);
+ iface.set('ifname', (ifnames.length > 1) ? ifnames : ifnames[0]);
+ }
+ }
+ else
+ {
+ var networks = L.toArray(this.get('network'));
+ if ($.inArray(iface.name(), networks) < 0)
+ {
+ networks.push(iface.name());
+ this.set('network', (networks.length > 1) ? networks : networks[0]);
+ }
+ }
+ }
+ });
+
+ this.NetworkModel.Interface = Class.extend({
+ getStatus: function(key)
+ {
+ var s = L.NetworkModel.rpcCache.ifstate;
+
+ for (var i = 0; i < s.length; i++)
+ if (s[i]['interface'] == this.options.name)
+ return key ? s[i][key] : s[i];
+
+ return undefined;
+ },
+
+ get: function(key)
+ {
+ return L.uci.get('network', this.options.name, key);
+ },
+
+ set: function(key, val)
+ {
+ return L.uci.set('network', this.options.name, key, val);
+ },
+
+ name: function()
+ {
+ return this.options.name;
+ },
+
+ protocol: function()
+ {
+ return (this.get('proto') || 'none');
+ },
+
+ isUp: function()
+ {
+ return (this.getStatus('up') === true);
+ },
+
+ isVirtual: function()
+ {
+ return (typeof(this.options.sid) != 'string');
+ },
+
+ getProtocol: function()
+ {
+ var prname = this.get('proto') || 'none';
+ return L.NetworkModel.protocolHandlers[prname] || L.NetworkModel.protocolHandlers.none;
+ },
+
+ getUptime: function()
+ {
+ var uptime = this.getStatus('uptime');
+ return isNaN(uptime) ? 0 : uptime;
+ },
+
+ getDevice: function(resolveAlias)
+ {
+ if (this.options.l3dev)
+ return L.NetworkModel.getDevice(this.options.l3dev);
+
+ return undefined;
+ },
+
+ getPhysdev: function()
+ {
+ if (this.options.l2dev)
+ return L.NetworkModel.getDevice(this.options.l2dev);
+
+ return undefined;
+ },
+
+ getSubdevices: function()
+ {
+ var rv = [ ];
+ var dev = this.options.l2dev ?
+ L.NetworkModel.deviceObjects[this.options.l2dev] : undefined;
+
+ if (dev && dev.kind == 'bridge' && dev.ports && dev.ports.length)
+ for (var i = 0; i < dev.ports.length; i++)
+ rv.push(L.NetworkModel.getDevice(dev.ports[i]));
+
+ return rv;
+ },
+
+ getIPv4Addrs: function(mask)
+ {
+ var rv = [ ];
+ var addrs = this.getStatus('ipv4-address');
+
+ if (addrs)
+ for (var i = 0; i < addrs.length; i++)
+ if (!mask)
+ rv.push(addrs[i].address);
+ else
+ rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
+
+ return rv;
+ },
+
+ getIPv6Addrs: function(mask)
+ {
+ var rv = [ ];
+ var addrs;
+
+ addrs = this.getStatus('ipv6-address');
+
+ if (addrs)
+ for (var i = 0; i < addrs.length; i++)
+ if (!mask)
+ rv.push(addrs[i].address);
+ else
+ rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
+
+ addrs = this.getStatus('ipv6-prefix-assignment');
+
+ if (addrs)
+ for (var i = 0; i < addrs.length; i++)
+ if (!mask)
+ rv.push('%s1'.format(addrs[i].address));
+ else
+ rv.push('%s1/%d'.format(addrs[i].address, addrs[i].mask));
+
+ return rv;
+ },
+
+ getDNSAddrs: function()
+ {
+ var rv = [ ];
+ var addrs = this.getStatus('dns-server');
+
+ if (addrs)
+ for (var i = 0; i < addrs.length; i++)
+ rv.push(addrs[i]);
+
+ return rv;
+ },
+
+ getIPv4DNS: function()
+ {
+ var rv = [ ];
+ var dns = this.getStatus('dns-server');
+
+ if (dns)
+ for (var i = 0; i < dns.length; i++)
+ if (dns[i].indexOf(':') == -1)
+ rv.push(dns[i]);
+
+ return rv;
+ },
+
+ getIPv6DNS: function()
+ {
+ var rv = [ ];
+ var dns = this.getStatus('dns-server');
+
+ if (dns)
+ for (var i = 0; i < dns.length; i++)
+ if (dns[i].indexOf(':') > -1)
+ rv.push(dns[i]);
+
+ return rv;
+ },
+
+ getIPv4Gateway: function()
+ {
+ var rt = this.getStatus('route');
+
+ if (rt)
+ for (var i = 0; i < rt.length; i++)
+ if (rt[i].target == '0.0.0.0' && rt[i].mask == 0)
+ return rt[i].nexthop;
+
+ return undefined;
+ },
+
+ getIPv6Gateway: function()
+ {
+ var rt = this.getStatus('route');
+
+ if (rt)
+ for (var i = 0; i < rt.length; i++)
+ if (rt[i].target == '::' && rt[i].mask == 0)
+ return rt[i].nexthop;
+
+ return undefined;
+ },
+
+ getStatistics: function()
+ {
+ var dev = this.getDevice() || new L.NetworkModel.Device({});
+ return dev.getStatistics();
+ },
+
+ getTrafficHistory: function()
+ {
+ var dev = this.getDevice() || new L.NetworkModel.Device({});
+ return dev.getTrafficHistory();
+ },
+
+ renderBadge: function()
+ {
+ var badge = $('<span />')
+ .addClass('badge')
+ .text('%s: '.format(this.name()));
+
+ var dev = this.getDevice();
+ var subdevs = this.getSubdevices();
+
+ if (subdevs.length)
+ for (var j = 0; j < subdevs.length; j++)
+ badge.append($('<img />')
+ .attr('src', subdevs[j].icon())
+ .attr('title', '%s (%s)'.format(subdevs[j].description(), subdevs[j].name() || '?')));
+ else if (dev)
+ badge.append($('<img />')
+ .attr('src', dev.icon())
+ .attr('title', '%s (%s)'.format(dev.description(), dev.name() || '?')));
+ else
+ badge.append($('<em />').text(L.tr('(No devices attached)')));
+
+ return badge;
+ },
+
+ setDevices: function(devs)
+ {
+ var dev = this.getPhysdev();
+ var old_devs = [ ];
+ var changed = false;
+
+ if (dev && dev.isBridge())
+ old_devs = this.getSubdevices();
+ else if (dev)
+ old_devs = [ dev ];
+
+ if (old_devs.length != devs.length)
+ changed = true;
+ else
+ for (var i = 0; i < old_devs.length; i++)
+ {
+ var dev = devs[i];
+
+ if (dev instanceof L.NetworkModel.Device)
+ dev = dev.name();
+
+ if (!dev || old_devs[i].name() != dev)
+ {
+ changed = true;
+ break;
+ }
+ }
+
+ if (changed)
+ {
+ for (var i = 0; i < old_devs.length; i++)
+ old_devs[i].removeFromInterface(this);
+
+ for (var i = 0; i < devs.length; i++)
+ {
+ var dev = devs[i];
+
+ if (!(dev instanceof L.NetworkModel.Device))
+ dev = L.NetworkModel.getDevice(dev);
+
+ if (dev)
+ dev.attachToInterface(this);
+ }
+ }
+ },
+
+ changeProtocol: function(proto)
+ {
+ var pr = L.NetworkModel.protocolHandlers[proto];
+
+ if (!pr)
+ return;
+
+ for (var opt in (this.get() || { }))
+ {
+ switch (opt)
+ {
+ case 'type':
+ case 'ifname':
+ case 'macaddr':
+ if (pr.virtual)
+ this.set(opt, undefined);
+ break;
+
+ case 'auto':
+ case 'mtu':
+ break;
+
+ case 'proto':
+ this.set(opt, pr.protocol);
+ break;
+
+ default:
+ this.set(opt, undefined);
+ break;
+ }
+ }
+ },
+
+ createForm: function(mapwidget)
+ {
+ var self = this;
+ var proto = self.getProtocol();
+ var device = self.getDevice();
+
+ if (!mapwidget)
+ mapwidget = L.cbi.Map;
+
+ var map = new mapwidget('network', {
+ caption: L.tr('Configure "%s"').format(self.name())
+ });
+
+ var section = map.section(L.cbi.SingleSection, self.name(), {
+ anonymous: true
+ });
+
+ section.tab({
+ id: 'general',
+ caption: L.tr('General Settings')
+ });
+
+ section.tab({
+ id: 'advanced',
+ caption: L.tr('Advanced Settings')
+ });
+
+ section.tab({
+ id: 'ipv6',
+ caption: L.tr('IPv6')
+ });
+
+ section.tab({
+ id: 'physical',
+ caption: L.tr('Physical Settings')
+ });
+
+
+ section.taboption('general', L.cbi.CheckboxValue, 'auto', {
+ caption: L.tr('Start on boot'),
+ optional: true,
+ initial: true
+ });
+
+ var pr = section.taboption('general', L.cbi.ListValue, 'proto', {
+ caption: L.tr('Protocol')
+ });
+
+ pr.ucivalue = function(sid) {
+ return self.get('proto') || 'none';
+ };
+
+ var ok = section.taboption('general', L.cbi.ButtonValue, '_confirm', {
+ caption: L.tr('Really switch?'),
+ description: L.tr('Changing the protocol will clear all configuration for this interface!'),
+ text: L.tr('Change protocol')
+ });
+
+ ok.on('click', function(ev) {
+ self.changeProtocol(pr.formvalue(ev.data.sid));
+ self.createForm(mapwidget).show();
+ });
+
+ var protos = L.NetworkModel.getProtocols();
+
+ for (var i = 0; i < protos.length; i++)
+ pr.value(protos[i].name, protos[i].description);
+
+ proto.populateForm(section, self);
+
+ if (!proto.virtual)
+ {
+ var br = section.taboption('physical', L.cbi.CheckboxValue, 'type', {
+ caption: L.tr('Network bridge'),
+ description: L.tr('Merges multiple devices into one logical bridge'),
+ optional: true,
+ enabled: 'bridge',
+ disabled: '',
+ initial: ''
+ });
+
+ section.taboption('physical', L.cbi.DeviceList, '__iface_multi', {
+ caption: L.tr('Devices'),
+ multiple: true,
+ bridges: false
+ }).depends('type', true);
+
+ section.taboption('physical', L.cbi.DeviceList, '__iface_single', {
+ caption: L.tr('Device'),
+ multiple: false,
+ bridges: true
+ }).depends('type', false);
+
+ var mac = section.taboption('physical', L.cbi.InputValue, 'macaddr', {
+ caption: L.tr('Override MAC'),
+ optional: true,
+ placeholder: device ? device.getMACAddress() : undefined,
+ datatype: 'macaddr'
+ })
+
+ mac.ucivalue = function(sid)
+ {
+ if (device)
+ return device.get('macaddr');
+
+ return this.callSuper('ucivalue', sid);
+ };
+
+ mac.save = function(sid)
+ {
+ if (!this.changed(sid))
+ return false;
+
+ if (device)
+ device.set('macaddr', this.formvalue(sid));
+ else
+ this.callSuper('set', sid);
+
+ return true;
+ };
+ }
+
+ section.taboption('physical', L.cbi.InputValue, 'mtu', {
+ caption: L.tr('Override MTU'),
+ optional: true,
+ placeholder: device ? device.getMTU() : undefined,
+ datatype: 'range(1, 9000)'
+ });