Bug 786700 - Wifi: Add the ability to set a static IP instead of using DHCP. r=vchang, r=mrbkap

This commit is contained in:
Dimi Lee 2013-06-24 11:02:30 +08:00
parent 3b0a4cfc5b
commit f6a5c82043
3 changed files with 224 additions and 37 deletions

View File

@ -85,6 +85,7 @@ DOMWifiManager.prototype = {
"WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
"WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
"WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
"WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
"WifiManager:wifiDown", "WifiManager:wifiUp",
"WifiManager:onconnecting", "WifiManager:onassociate",
"WifiManager:onconnect", "WifiManager:ondisconnect",
@ -191,6 +192,16 @@ DOMWifiManager.prototype = {
Services.DOMRequest.fireError(request, msg.data);
break;
case "WifiManager:setStaticIpMode:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiManager:setStaticIpMode:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, msg.data);
break;
case "WifiManager:wifiDown":
this._enabled = false;
this._currentNetwork = null;
@ -347,6 +358,14 @@ DOMWifiManager.prototype = {
return request;
},
setStaticIpMode: function nsIDOMWifiManager_setStaticIpMode(network, info) {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
var request = this.createRequest();
this._sendMessageForRequest("WifiManager:setStaticIpMode", {network: network,info: info}, request);
return request;
},
get enabled() {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);

View File

@ -11,7 +11,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var DEBUG = false; // set to true to show debug messages
var DEBUG = false; // set to true to show debug messages.
const WIFIWORKER_CONTRACTID = "@mozilla.org/wifi/worker;1";
const WIFIWORKER_CID = Components.ID("{a14e8977-d259-433a-a88d-58dd44657e5b}");
@ -137,7 +137,7 @@ var WifiManager = (function() {
// Polling the status worker
var recvErrors = 0;
eventWorker.onmessage = function(e) {
// process the event and tell the event worker to listen for more events
// Process the event and tell the event worker to listen for more events.
if (handleEvent(e.data.event))
waitForEvent();
};
@ -146,7 +146,7 @@ var WifiManager = (function() {
eventWorker.postMessage({ cmd: "wait_for_event" });
}
// Commands to the control worker
// Commands to the control worker.
function voidControlMessage(cmd, callback) {
controlMessage({ cmd: cmd }, function (data) {
@ -616,11 +616,77 @@ var WifiManager = (function() {
});
}
var staticIpConfig = Object.create(null);
function setStaticIpMode(network, info, callback) {
let setNetworkKey = getNetworkKey(network);
let curNetworkKey = null;
let currentNetwork = Object.create(null);
currentNetwork.netId = manager.connectionInfo.id;
manager.getNetworkConfiguration(currentNetwork, function (){
curNetworkKey = getNetworkKey(currentNetwork);
// Add additional information to static ip configuration
// It is used to compatiable with information dhcp callback.
info.ipaddr = stringToIp(info.ipaddr_str);
info.gateway = stringToIp(info.gateway_str);
info.mask_str = makeMask(info.maskLength);
// Optional
info.dns1 = stringToIp("dns1_str" in info ? info.dns1_str : "");
info.dns2 = stringToIp("dns2_str" in info ? info.dns2_str : "");
info.proxy = stringToIp("proxy_str" in info ? info.proxy_str : "");
staticIpConfig[setNetworkKey] = info;
// If the ssid of current connection is the same as configured ssid
// It means we need update current connection to use static IP address.
if (setNetworkKey == curNetworkKey) {
// Use configureInterface directly doesn't work, the network iterface
// and routing table is changed but still cannot connect to network
// so the workaround here is disable interface the enable again to
// trigger network reconnect with static ip.
disableInterface(manager.ifname, function (ok) {
enableInterface(manager.ifname, function (ok) {
});
});
}
});
}
var dhcpInfo = null;
function runDhcp(ifname, callback) {
function runDhcp(ifname) {
debug("Run Dhcp");
controlMessage({ cmd: "dhcp_do_request", ifname: ifname }, function(data) {
dhcpInfo = data.status ? null : data;
callback(dhcpInfo);
runIpConfig(ifname, dhcpInfo);
});
}
function runStaticIp(ifname, key) {
debug("Run static ip");
// Read static ip information from settings.
let staticIpInfo;
if (!(key in staticIpConfig))
return;
staticIpInfo = staticIpConfig[key];
// Stop dhcpd when use static IP
if (dhcpInfo != null) {
stopDhcp(manager.ifname, function() {});
}
// Set ip, mask length, gateway, dns to network interface
configureInterface(ifname,
staticIpInfo.ipaddr,
staticIpInfo.maskLength,
staticIpInfo.gateway,
staticIpInfo.dns1,
staticIpInfo.dns2, function (data) {
runIpConfig(ifname, staticIpInfo);
});
}
@ -767,7 +833,7 @@ var WifiManager = (function() {
return;
}
if (connectTries++ < 3) {
// try again in 5 seconds
// Try again in 5 seconds.
if (!retryTimer)
retryTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@ -803,32 +869,48 @@ var WifiManager = (function() {
function onconnected() {
// For now we do our own DHCP. In the future, this should be handed
// off to the Network Manager.
runDhcp(manager.ifname, function (data) {
if (!data) {
debug("DHCP failed to run");
notify("dhcpconnected", { info: data });
let currentNetwork = Object.create(null);
currentNetwork.netId = manager.connectionInfo.id;
manager.getNetworkConfiguration(currentNetwork, function (){
let key = getNetworkKey(currentNetwork);
if (staticIpConfig &&
(key in staticIpConfig) &&
staticIpConfig[key].enabled) {
debug("Run static ip");
runStaticIp(manager.ifname, key);
return;
}
runDhcp(manager.ifname);
});
}
function runIpConfig(name, data) {
if (!data) {
debug("IP config failed to run");
notify("networkconnected", { info: data });
return;
}
setProperty("net." + name + ".dns1", ipToString(data.dns1),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns1");
return;
}
setProperty("net." + manager.ifname + ".dns1", ipToString(data.dns1),
setProperty("net." + name + ".dns2", ipToString(data.dns2),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns1");
debug("Unable to set net.<ifname>.dns2");
return;
}
setProperty("net." + manager.ifname + ".dns2", ipToString(data.dns2),
setProperty("net." + name + ".gw", ipToString(data.gateway),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.dns2");
debug("Unable to set net.<ifname>.gw");
return;
}
setProperty("net." + manager.ifname + ".gw", ipToString(data.gateway),
function(ok) {
if (!ok) {
debug("Unable to set net.<ifname>.gw");
return;
}
notify("dhcpconnected", { info: data });
});
notify("networkconnected", { info: data });
});
});
});
@ -865,7 +947,7 @@ var WifiManager = (function() {
});
}
// handle events sent to us by the event worker
// Handle events sent to us by the event worker.
function handleEvent(event) {
debug("Event coming in: " + event);
if (event.indexOf("CTRL-EVENT-") !== 0 && event.indexOf("WPS") !== 0) {
@ -898,7 +980,7 @@ var WifiManager = (function() {
var space = event.indexOf(" ");
var eventData = event.substr(0, space + 1);
if (eventData.indexOf("CTRL-EVENT-STATE-CHANGE") === 0) {
// Parse the event data
// Parse the event data.
var fields = {};
var tokens = event.substr(space + 1).split(" ");
for (var n = 0; n < tokens.length; ++n) {
@ -938,7 +1020,7 @@ var WifiManager = (function() {
}
// As long we haven't seen too many recv errors yet, we
// will keep going for a bit longer
// will keep going for a bit longer.
if (eventData.indexOf("recv error") !== -1 && ++recvErrors < 10)
return true;
@ -976,8 +1058,11 @@ var WifiManager = (function() {
if (eventData.indexOf("CTRL-EVENT-CONNECTED") === 0) {
// Format: CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]
var bssid = event.split(" ")[4];
var id = event.substr(event.indexOf("id=")).split(" ")[0];
var keyword = "id=";
var id = event.substr(event.indexOf(keyword) + keyword.length).split(" ")[0];
// Read current BSSID here, it will always being provided.
manager.connectionInfo.id = id;
manager.connectionInfo.bssid = bssid;
return true;
}
@ -1002,7 +1087,7 @@ var WifiManager = (function() {
notifyStateChange({ state: "WPS_OVERLAP_DETECTED", BSSID: null, id: -1 });
return true;
}
// unknown event
// Unknown event.
return true;
}
@ -1065,7 +1150,7 @@ var WifiManager = (function() {
});
}
// Initial state
// Initial state.
manager.state = "UNINITIALIZED";
manager.tetheringState = "UNINITIALIZED";
manager.enabled = false;
@ -1089,7 +1174,7 @@ var WifiManager = (function() {
Ci.nsITimer.TYPE_ONE_SHOT);
};
// Public interface of the wifi service
// Public interface of the wifi service.
manager.setWifiEnabled = function(enable, callback) {
if (enable === manager.enabled) {
callback("no change");
@ -1281,7 +1366,7 @@ var WifiManager = (function() {
});
}
}
// If config didn't contain any of the fields we want, don't lose the error callback
// If config didn't contain any of the fields we want, don't lose the error callback.
if (done == networkConfigurationFields.length)
callback(false);
}
@ -1318,7 +1403,7 @@ var WifiManager = (function() {
++errors;
if (++done == lines.length - 1) {
if (errors) {
// If an error occured, delete the new netId
// If an error occured, delete the new netId.
removeNetworkCommand(netId, function() {
callback(null);
});
@ -1357,6 +1442,43 @@ var WifiManager = (function() {
((n >> 24) & 0xFF);
}
function stringToIp(string) {
let ip = 0;
let start, end = -1;
for (let i = 0; i < 4; i++) {
start = end + 1;
end = string.indexOf(".", start);
if (end == -1) {
end = string.length;
}
let num = parseInt(string.slice(start, end), 10);
if (isNaN(num)) {
return 0;
}
ip |= num << (i * 8);
}
return ip;
}
function swap32(n) {
return (((n >> 24) & 0xFF) << 0) |
(((n >> 16) & 0xFF) << 8) |
(((n >> 8) & 0xFF) << 16) |
(((n >> 0) & 0xFF) << 24);
}
function ntohl(n) {
return swap32(n);
}
function makeMask(len) {
let mask = 0;
for (let i = 0; i < len; ++i) {
mask |= (0x80000000 >> i);
}
return ntohl(mask);
}
manager.saveConfig = function(callback) {
saveConfigCommand(callback);
}
@ -1378,6 +1500,7 @@ var WifiManager = (function() {
manager.wpsCancel = wpsCancelCommand;
manager.setPowerMode = setPowerModeCommand;
manager.setSuspendOptimizations = setSuspendOptimizationsCommand;
manager.setStaticIpMode = setStaticIpMode;
manager.getRssiApprox = getRssiApproxCommand;
manager.getLinkSpeed = getLinkSpeedCommand;
manager.getDhcpInfo = function() { return dhcpInfo; }
@ -1499,7 +1622,7 @@ function getNetworkKey(network)
// ssid here must be dequoted, and it's safer to esacpe it.
// encryption won't be empty and always be assigned one of the followings :
// "OPEN"/"WEP"/"WPA-PSK"/"WPA-EAP".
// So for a invalid network object, the returned key will be "OPEN"
// So for a invalid network object, the returned key will be "OPEN".
return escape(ssid) + encryption;
}
@ -1665,6 +1788,7 @@ function WifiWorker() {
"WifiManager:associate", "WifiManager:forget",
"WifiManager:wps", "WifiManager:getState",
"WifiManager:setPowerSavingMode",
"WifiManager:setStaticIpMode",
"child-process-shutdown"];
messages.forEach((function(msgName) {
@ -1679,10 +1803,10 @@ function WifiWorker() {
this._needToEnableNetworks = false;
this._highestPriority = -1;
// networks is a map from SSID -> a scan result.
// Networks is a map from SSID -> a scan result.
this.networks = Object.create(null);
// configuredNetworks is a map from SSID -> our view of a network. It only
// ConfiguredNetworks is a map from SSID -> our view of a network. It only
// lists networks known to the wpa_supplicant. The SSID field (and other
// fields) are quoted for ease of use with WifiManager commands.
// Note that we don't have to worry about escaping embedded quotes since in
@ -2024,7 +2148,7 @@ function WifiWorker() {
}
};
WifiManager.ondhcpconnected = function() {
WifiManager.onnetworkconnected = function() {
if (this.info) {
WifiNetworkInterface.state =
Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED;
@ -2152,7 +2276,7 @@ function WifiWorker() {
// Read the 'wifi.enabled' setting in order to start with a known
// value at boot time. The handle() will be called after reading.
//
// nsISettingsServiceCallback implementation
// nsISettingsServiceCallback implementation.
var initWifiEnabledCb = {
handle: function handle(aName, aResult) {
if (aName !== SETTINGS_WIFI_ENABLED)
@ -2501,6 +2625,9 @@ WifiWorker.prototype = {
case "WifiManager:setPowerSavingMode":
this.setPowerSavingMode(msg);
break;
case "WifiManager:setStaticIpMode":
this.setStaticIpMode(msg);
break;
case "WifiManager:getState": {
let i;
if ((i = this._domManagers.indexOf(msg.manager)) === -1) {
@ -2970,6 +3097,30 @@ WifiWorker.prototype = {
});
},
setStaticIpMode: function(msg) {
const message = "WifiManager:setStaticMode:Return";
let self = this;
let network = msg.data.network;
let info = msg.data.info;
netFromDOM(network, null);
// To compatiable with DHCP returned info structure, do translation here
info.ipaddr_str = info.ipaddr;
info.proxy_str = info.proxy;
info.gateway_str = info.gateway;
info.dns1_str = info.dns1;
info.dns2_str = info.dns2;
WifiManager.setStaticIpMode(network, info, function(ok) {
if (ok) {
self._sendMessage(message, true, true, msg);
} else {
self._sendMessage(message, false, "Set static ip mode failed", msg);
}
});
},
// This is a bit ugly, but works. In particular, this depends on the fact
// that RadioManager never actually tries to get the worker from us.
get worker() { throw "Not implemented"; },

View File

@ -59,7 +59,7 @@ interface nsIWifi : nsISupports
void getWifiScanResults(in nsIWifiScanResultsReady callback);
};
[scriptable, uuid(caa76ee3-8ffe-4ea5-bc59-3b53a9df0d07)]
[scriptable, uuid(3f21012d-6e75-4632-b87c-acdd7c57fbf3)]
interface nsIDOMWifiManager : nsISupports
{
/**
@ -126,6 +126,23 @@ interface nsIDOMWifiManager : nsISupports
*/
nsIDOMDOMRequest setPowerSavingMode(in boolean enabled);
/**
* Given a network, configure using static IP instead of running DHCP
* @param network A network object with the SSID of the network to set static ip.
* @param info info should have following field:
* - enabled True to enable static IP, false to use DHCP
* - ipaddr configured static IP address
* - proxy configured proxy server address
* - maskLength configured mask length
* - gateway configured gateway address
* - dns1 configured first DNS server address
* - dns2 configured seconf DNS server address
* onsuccess: We have successfully configure the static ip mode.
* onerror: We have failed to configure the static ip mode.
*/
nsIDOMDOMRequest setStaticIpMode(in jsval network,
in jsval info);
/**
* Returns whether or not wifi is currently enabled.
*/