Commit 6eae0395 authored by stevenjb's avatar stevenjb Committed by Commit bot

Translate Saved/StaticIPConfig properties from ONC to Shill

BUG=279351

Review URL: https://codereview.chromium.org/540613002

Cr-Commit-Position: refs/heads/master@{#293331}
parent eaa12c7b
...@@ -152,8 +152,10 @@ void NetworkConfigMessageHandler::GetPropertiesSuccess( ...@@ -152,8 +152,10 @@ void NetworkConfigMessageHandler::GetPropertiesSuccess(
return_arg_list.AppendInteger(callback_id); return_arg_list.AppendInteger(callback_id);
base::DictionaryValue* network_properties = dictionary.DeepCopy(); base::DictionaryValue* network_properties = dictionary.DeepCopy();
// Set the 'ServicePath' property for debugging.
network_properties->SetStringWithoutPathExpansion( network_properties->SetStringWithoutPathExpansion(
::onc::network_config::kGUID, service_path); "ServicePath", service_path);
return_arg_list.Append(network_properties); return_arg_list.Append(network_properties);
InvokeCallback(return_arg_list); InvokeCallback(return_arg_list);
} }
...@@ -185,6 +187,8 @@ void NetworkConfigMessageHandler::GetShillPropertiesSuccess( ...@@ -185,6 +187,8 @@ void NetworkConfigMessageHandler::GetShillPropertiesSuccess(
const std::string& service_path, const std::string& service_path,
const base::DictionaryValue& dictionary) const { const base::DictionaryValue& dictionary) const {
scoped_ptr<base::DictionaryValue> dictionary_copy(dictionary.DeepCopy()); scoped_ptr<base::DictionaryValue> dictionary_copy(dictionary.DeepCopy());
// Set the 'ServicePath' property for debugging.
dictionary_copy->SetStringWithoutPathExpansion("ServicePath", service_path);
// Get the device properties for debugging. // Get the device properties for debugging.
std::string device; std::string device;
...@@ -196,8 +200,21 @@ void NetworkConfigMessageHandler::GetShillPropertiesSuccess( ...@@ -196,8 +200,21 @@ void NetworkConfigMessageHandler::GetShillPropertiesSuccess(
base::DictionaryValue* device_dictionary = base::DictionaryValue* device_dictionary =
device_state->properties().DeepCopy(); device_state->properties().DeepCopy();
dictionary_copy->Set(shill::kDeviceProperty, device_dictionary); dictionary_copy->Set(shill::kDeviceProperty, device_dictionary);
// Convert IPConfig dictionary to a ListValue.
base::ListValue* ip_configs = new base::ListValue;
for (base::DictionaryValue::Iterator iter(device_state->ip_configs());
!iter.IsAtEnd(); iter.Advance()) {
ip_configs->Append(iter.value().DeepCopy());
}
device_dictionary->SetWithoutPathExpansion(
shill::kIPConfigsProperty, ip_configs);
} }
GetPropertiesSuccess(callback_id, service_path, *dictionary_copy);
base::ListValue return_arg_list;
return_arg_list.AppendInteger(callback_id);
return_arg_list.Append(dictionary_copy.release());
InvokeCallback(return_arg_list);
} }
void NetworkConfigMessageHandler::InvokeCallback( void NetworkConfigMessageHandler::InvokeCallback(
......
...@@ -178,6 +178,7 @@ const OncFieldSignature ipconfig_fields[] = { ...@@ -178,6 +178,7 @@ const OncFieldSignature ipconfig_fields[] = {
{ ::onc::ipconfig::kRoutingPrefix, &kIntegerSignature}, { ::onc::ipconfig::kRoutingPrefix, &kIntegerSignature},
{ ::onc::network_config::kSearchDomains, &kStringListSignature}, { ::onc::network_config::kSearchDomains, &kStringListSignature},
{ ::onc::ipconfig::kType, &kStringSignature}, { ::onc::ipconfig::kType, &kStringSignature},
{ ::onc::ipconfig::kWebProxyAutoDiscoveryUrl, &kStringSignature},
{NULL}}; {NULL}};
const OncFieldSignature proxy_location_fields[] = { const OncFieldSignature proxy_location_fields[] = {
...@@ -284,23 +285,32 @@ const OncFieldSignature cellular_with_state_fields[] = { ...@@ -284,23 +285,32 @@ const OncFieldSignature cellular_with_state_fields[] = {
{NULL}}; {NULL}};
const OncFieldSignature network_configuration_fields[] = { const OncFieldSignature network_configuration_fields[] = {
{ ::onc::kRecommended, &kRecommendedSignature}, { ::onc::network_config::kCellular, &kCellularSignature},
{ ::onc::network_config::kEthernet, &kEthernetSignature}, { ::onc::network_config::kEthernet, &kEthernetSignature},
{ ::onc::network_config::kGUID, &kStringSignature}, { ::onc::network_config::kGUID, &kStringSignature},
// Not supported for policy but for reading network state. // Not supported for policy but for reading network state.
// TODO(pneubeck@): Resolve IPConfigs vs. StaticIPConfig, crbug.com/410877
{ ::onc::network_config::kIPConfigs, &kIPConfigListSignature}, { ::onc::network_config::kIPConfigs, &kIPConfigListSignature},
{ ::onc::network_config::kName, &kStringSignature}, { ::onc::network_config::kName, &kStringSignature},
// Not supported, yet. // Not supported, yet.
{ ::onc::network_config::kNameServers, &kStringListSignature}, { ::onc::network_config::kNameServers, &kStringListSignature},
{ ::onc::network_config::kPriority, &kIntegerSignature}, { ::onc::network_config::kPriority, &kIntegerSignature},
{ ::onc::network_config::kProxySettings, &kProxySettingsSignature}, { ::onc::network_config::kProxySettings, &kProxySettingsSignature},
{ ::onc::kRecommended, &kRecommendedSignature},
{ ::onc::kRemove, &kBoolSignature}, { ::onc::kRemove, &kBoolSignature},
// Not supported, yet. // Not supported, yet.
{ ::onc::network_config::kSearchDomains, &kStringListSignature}, { ::onc::network_config::kSearchDomains, &kStringListSignature},
{ ::onc::network_config::kSavedIPConfig, &kSavedIPConfigSignature},
{ ::onc::network_config::kStaticIPConfig, &kStaticIPConfigSignature},
{ ::onc::network_config::kType, &kStringSignature}, { ::onc::network_config::kType, &kStringSignature},
{ ::onc::network_config::kVPN, &kVPNSignature}, { ::onc::network_config::kVPN, &kVPNSignature},
{ ::onc::network_config::kWiFi, &kWiFiSignature}, { ::onc::network_config::kWiFi, &kWiFiSignature},
{ ::onc::network_config::kCellular, &kCellularSignature},
{NULL}}; {NULL}};
const OncFieldSignature network_with_state_fields[] = { const OncFieldSignature network_with_state_fields[] = {
...@@ -380,6 +390,12 @@ const OncValueSignature kEthernetSignature = { ...@@ -380,6 +390,12 @@ const OncValueSignature kEthernetSignature = {
const OncValueSignature kIPConfigSignature = { const OncValueSignature kIPConfigSignature = {
base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
}; };
const OncValueSignature kSavedIPConfigSignature = {
base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
};
const OncValueSignature kStaticIPConfigSignature = {
base::Value::TYPE_DICTIONARY, ipconfig_fields, NULL
};
const OncValueSignature kProxyLocationSignature = { const OncValueSignature kProxyLocationSignature = {
base::Value::TYPE_DICTIONARY, proxy_location_fields, NULL base::Value::TYPE_DICTIONARY, proxy_location_fields, NULL
}; };
......
...@@ -47,6 +47,8 @@ CHROMEOS_EXPORT extern const OncValueSignature kVerifyX509Signature; ...@@ -47,6 +47,8 @@ CHROMEOS_EXPORT extern const OncValueSignature kVerifyX509Signature;
CHROMEOS_EXPORT extern const OncValueSignature kVPNSignature; CHROMEOS_EXPORT extern const OncValueSignature kVPNSignature;
CHROMEOS_EXPORT extern const OncValueSignature kEthernetSignature; CHROMEOS_EXPORT extern const OncValueSignature kEthernetSignature;
CHROMEOS_EXPORT extern const OncValueSignature kIPConfigSignature; CHROMEOS_EXPORT extern const OncValueSignature kIPConfigSignature;
CHROMEOS_EXPORT extern const OncValueSignature kSavedIPConfigSignature;
CHROMEOS_EXPORT extern const OncValueSignature kStaticIPConfigSignature;
CHROMEOS_EXPORT extern const OncValueSignature kProxyLocationSignature; CHROMEOS_EXPORT extern const OncValueSignature kProxyLocationSignature;
CHROMEOS_EXPORT extern const OncValueSignature kProxyManualSignature; CHROMEOS_EXPORT extern const OncValueSignature kProxyManualSignature;
CHROMEOS_EXPORT extern const OncValueSignature kProxySettingsSignature; CHROMEOS_EXPORT extern const OncValueSignature kProxySettingsSignature;
......
...@@ -185,6 +185,24 @@ const FieldTranslationEntry ipconfig_fields[] = { ...@@ -185,6 +185,24 @@ const FieldTranslationEntry ipconfig_fields[] = {
// This field is converted during translation, see ShillToONCTranslator:: // This field is converted during translation, see ShillToONCTranslator::
// TranslateIPConfig. It is only converted from Shill->ONC. // TranslateIPConfig. It is only converted from Shill->ONC.
// { ::onc::ipconfig::kType, shill::kMethodProperty}, // { ::onc::ipconfig::kType, shill::kMethodProperty},
{ ::onc::ipconfig::kWebProxyAutoDiscoveryUrl,
shill::kWebProxyAutoDiscoveryUrlProperty},
{NULL}};
const FieldTranslationEntry saved_ipconfig_fields[] = {
{ ::onc::ipconfig::kIPAddress, shill::kSavedIPAddressProperty},
{ ::onc::ipconfig::kGateway, shill::kSavedIPGatewayProperty},
{ ::onc::ipconfig::kRoutingPrefix, shill::kSavedIPPrefixlenProperty},
// NameServers are converted during translation, see onc_translator_*.
// { ::onc::ipconfig::kNameServers, shill::kSavedIPNameServersProperty},
{NULL}};
const FieldTranslationEntry static_ipconfig_fields[] = {
{ ::onc::ipconfig::kIPAddress, shill::kStaticIPAddressProperty},
{ ::onc::ipconfig::kGateway, shill::kStaticIPGatewayProperty},
{ ::onc::ipconfig::kRoutingPrefix, shill::kStaticIPPrefixlenProperty},
// NameServers are converted during translation, see onc_translator_*.
// { ::onc::ipconfig::kNameServers, shill::kStaticIPNameServersProperty},
{NULL}}; {NULL}};
struct OncValueTranslationEntry { struct OncValueTranslationEntry {
...@@ -211,6 +229,8 @@ const OncValueTranslationEntry onc_value_translation_table[] = { ...@@ -211,6 +229,8 @@ const OncValueTranslationEntry onc_value_translation_table[] = {
{ &kNetworkWithStateSignature, network_fields }, { &kNetworkWithStateSignature, network_fields },
{ &kNetworkConfigurationSignature, network_fields }, { &kNetworkConfigurationSignature, network_fields },
{ &kIPConfigSignature, ipconfig_fields }, { &kIPConfigSignature, ipconfig_fields },
{ &kSavedIPConfigSignature, saved_ipconfig_fields },
{ &kStaticIPConfigSignature, static_ipconfig_fields },
{ NULL } { NULL }
}; };
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/values.h" #include "base/values.h"
#include "chromeos/network/network_state.h" #include "chromeos/network/network_state.h"
#include "chromeos/network/network_util.h" #include "chromeos/network/network_util.h"
...@@ -77,6 +78,9 @@ class ShillToONCTranslator { ...@@ -77,6 +78,9 @@ class ShillToONCTranslator {
void TranslateCellularDevice(); void TranslateCellularDevice();
void TranslateNetworkWithState(); void TranslateNetworkWithState();
void TranslateIPConfig(); void TranslateIPConfig();
void TranslateSavedOrStaticIPConfig(const std::string& nameserver_property);
void TranslateSavedIPConfig();
void TranslateStaticIPConfig();
// Creates an ONC object from |dictionary| according to the signature // Creates an ONC object from |dictionary| according to the signature
// associated to |onc_field_name| and adds it to |onc_object_| at // associated to |onc_field_name| and adds it to |onc_object_| at
...@@ -157,6 +161,10 @@ ShillToONCTranslator::CreateTranslatedONCObject() { ...@@ -157,6 +161,10 @@ ShillToONCTranslator::CreateTranslatedONCObject() {
TranslateCellularWithState(); TranslateCellularWithState();
} else if (onc_signature_ == &kIPConfigSignature) { } else if (onc_signature_ == &kIPConfigSignature) {
TranslateIPConfig(); TranslateIPConfig();
} else if (onc_signature_ == &kSavedIPConfigSignature) {
TranslateSavedIPConfig();
} else if (onc_signature_ == &kStaticIPConfigSignature) {
TranslateStaticIPConfig();
} else { } else {
CopyPropertiesAccordingToSignature(); CopyPropertiesAccordingToSignature();
} }
...@@ -371,8 +379,7 @@ void ShillToONCTranslator::TranslateNetworkWithState() { ...@@ -371,8 +379,7 @@ void ShillToONCTranslator::TranslateNetworkWithState() {
// Since Name is a read only field in Shill unless it's a VPN, it is copied // Since Name is a read only field in Shill unless it's a VPN, it is copied
// here, but not when going the other direction (if it's not a VPN). // here, but not when going the other direction (if it's not a VPN).
std::string name; std::string name;
shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, &name);
&name);
onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName, onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
name); name);
...@@ -410,6 +417,9 @@ void ShillToONCTranslator::TranslateNetworkWithState() { ...@@ -410,6 +417,9 @@ void ShillToONCTranslator::TranslateNetworkWithState() {
TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs, TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
*shill_ipconfigs); *shill_ipconfigs);
} }
TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig);
TranslateAndAddNestedObject(::onc::network_config::kStaticIPConfig);
} }
void ShillToONCTranslator::TranslateIPConfig() { void ShillToONCTranslator::TranslateIPConfig() {
...@@ -431,6 +441,34 @@ void ShillToONCTranslator::TranslateIPConfig() { ...@@ -431,6 +441,34 @@ void ShillToONCTranslator::TranslateIPConfig() {
onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type); onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
} }
void ShillToONCTranslator::TranslateSavedOrStaticIPConfig(
const std::string& nameserver_property) {
CopyPropertiesAccordingToSignature();
// Saved/Static IP config nameservers are stored as a comma separated list.
std::string shill_nameservers;
shill_dictionary_->GetStringWithoutPathExpansion(
nameserver_property, &shill_nameservers);
std::vector<std::string> onc_nameserver_vector;
if (Tokenize(shill_nameservers, ",", &onc_nameserver_vector) > 0) {
scoped_ptr<base::ListValue> onc_nameservers(new base::ListValue);
for (std::vector<std::string>::iterator iter =
onc_nameserver_vector.begin();
iter != onc_nameserver_vector.end(); ++iter) {
onc_nameservers->AppendString(*iter);
}
onc_object_->SetWithoutPathExpansion(::onc::ipconfig::kNameServers,
onc_nameservers.release());
}
}
void ShillToONCTranslator::TranslateSavedIPConfig() {
TranslateSavedOrStaticIPConfig(shill::kSavedIPNameServersProperty);
}
void ShillToONCTranslator::TranslateStaticIPConfig() {
TranslateSavedOrStaticIPConfig(shill::kStaticIPNameServersProperty);
}
void ShillToONCTranslator::TranslateAndAddNestedObject( void ShillToONCTranslator::TranslateAndAddNestedObject(
const std::string& onc_field_name) { const std::string& onc_field_name) {
TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
...@@ -509,6 +547,8 @@ void ShillToONCTranslator::CopyPropertiesAccordingToSignature( ...@@ -509,6 +547,8 @@ void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
const OncValueSignature* value_signature) { const OncValueSignature* value_signature) {
if (value_signature->base_signature) if (value_signature->base_signature)
CopyPropertiesAccordingToSignature(value_signature->base_signature); CopyPropertiesAccordingToSignature(value_signature->base_signature);
if (!value_signature->fields)
return;
for (const OncFieldSignature* field_signature = value_signature->fields; for (const OncFieldSignature* field_signature = value_signature->fields;
field_signature->onc_field_name != NULL; ++field_signature) { field_signature->onc_field_name != NULL; ++field_signature) {
CopyProperty(field_signature); CopyProperty(field_signature);
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
"1.1.1.2", "1.1.1.2",
"1.1.1.3" "1.1.1.3"
], ],
"Prefixlen":24 "Prefixlen":24,
"WebProxyAutoDiscoveryUrl":"proxy.url"
}, },
{ {
"Address":"2001:0db8:85a3:0000:0000:8a2e:0370:7334", "Address":"2001:0db8:85a3:0000:0000:8a2e:0370:7334",
...@@ -19,5 +20,13 @@ ...@@ -19,5 +20,13 @@
"Prefixlen":12 "Prefixlen":12
} }
], ],
"SavedIP.Address":"124.124.124.124",
"SavedIP.Gateway":"1.1.1.4",
"SavedIP.NameServers":"1.1.1.5,1.1.1.6",
"SavedIP.Prefixlen":25,
"StaticIP.Address":"125.125.125.125",
"StaticIP.Gateway":"1.1.1.7",
"StaticIP.NameServers":"1.1.1.8",
"StaticIP.Prefixlen":26,
"Type":"ethernet" "Type":"ethernet"
} }
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
"1.1.1.3" "1.1.1.3"
], ],
"RoutingPrefix":24, "RoutingPrefix":24,
"Type":"IPv4" "Type":"IPv4",
"WebProxyAutoDiscoveryUrl":"proxy.url"
}, },
{ {
"Gateway":"2001:db8:85a3::7a2e:370:7331", "Gateway":"2001:db8:85a3::7a2e:370:7331",
...@@ -23,5 +24,22 @@ ...@@ -23,5 +24,22 @@
} }
], ],
"Name":"", "Name":"",
"SavedIPConfig":{
"Gateway":"1.1.1.4",
"IPAddress":"124.124.124.124",
"NameServers":[
"1.1.1.5",
"1.1.1.6"
],
"RoutingPrefix":25,
},
"StaticIPConfig":{
"Gateway":"1.1.1.7",
"IPAddress":"125.125.125.125",
"NameServers":[
"1.1.1.8"
],
"RoutingPrefix":26,
},
"Type":"Ethernet" "Type":"Ethernet"
} }
...@@ -252,13 +252,33 @@ ...@@ -252,13 +252,33 @@
</dd> </dd>
<dt class="field">IPConfigs</dt> <dt class="field">IPConfigs</dt>
<dd>
<span class="field_meta">
(optional for connected networks, read-only)
<span class="type">array of IPConfig</span>
</span>
Array of IPConfig properties associated with this connection.
</dd>
<dt class="field">StaticIPConfig</dt>
<dd> <dd>
<span class="field_meta"> <span class="field_meta">
(optional if <span class="field">Remove</span> is (optional if <span class="field">Remove</span> is
<span class="value">false</span>, otherwise ignored) <span class="value">false</span>, otherwise ignored)
<span class="type">array of IPConfig</span> <span class="type">IPConfig</span>
</span> </span>
Static IPv4 or IPv6 parameters to associate with this connection. Each property set in this IPConfig object overrides the respective
parameter received over DHCP.
</dd>
<dt class="field">SavedIPConfig</dt>
<dd>
<span class="field_meta">
(optional for connected networks, read-only)
<span class="type">IPConfig</span>
</span>
IPConfig property containing the configuration that was received from the
DHCP server prior to applying any StaticIPConfig parameters.
</dd> </dd>
<dt class="field">Name</dt> <dt class="field">Name</dt>
...@@ -532,6 +552,16 @@ ...@@ -532,6 +552,16 @@
SearchDomains field for this configuration. If not specified, top level SearchDomains field for this configuration. If not specified, top level
values will be used. values will be used.
</dd> </dd>
<dt class="field">WebProxyAutoDiscoveryUrl</dt>
<dd>
<span class="field_meta">
(optional if part of <span class="field">IPConfigs</span>)
<span class="type">string</span>
</span>
The Web Proxy Auto-Discovery URL for this network as reported over DHCP.
</dd>
</dl> </dl>
</section> </section>
......
...@@ -38,6 +38,8 @@ const char kDevice[] = "Device"; ...@@ -38,6 +38,8 @@ const char kDevice[] = "Device";
const char kEthernet[] = "Ethernet"; const char kEthernet[] = "Ethernet";
const char kGUID[] = "GUID"; const char kGUID[] = "GUID";
const char kIPConfigs[] = "IPConfigs"; const char kIPConfigs[] = "IPConfigs";
const char kSavedIPConfig[] = "SavedIPConfig";
const char kStaticIPConfig[] = "StaticIPConfig";
const char kMacAddress[] = "MacAddress"; const char kMacAddress[] = "MacAddress";
const char kName[] = "Name"; const char kName[] = "Name";
const char kNameServers[] = "NameServers"; const char kNameServers[] = "NameServers";
...@@ -159,6 +161,7 @@ const char kIPv6[] = "IPv6"; ...@@ -159,6 +161,7 @@ const char kIPv6[] = "IPv6";
const char kNameServers[] = "NameServers"; const char kNameServers[] = "NameServers";
const char kRoutingPrefix[] = "RoutingPrefix"; const char kRoutingPrefix[] = "RoutingPrefix";
const char kType[] = "Type"; const char kType[] = "Type";
const char kWebProxyAutoDiscoveryUrl[] = "WebProxyAutoDiscoveryUrl";
} // namespace ipconfig } // namespace ipconfig
namespace wifi { namespace wifi {
......
...@@ -62,6 +62,8 @@ ONC_EXPORT extern const char kDevice[]; ...@@ -62,6 +62,8 @@ ONC_EXPORT extern const char kDevice[];
ONC_EXPORT extern const char kEthernet[]; ONC_EXPORT extern const char kEthernet[];
ONC_EXPORT extern const char kGUID[]; ONC_EXPORT extern const char kGUID[];
ONC_EXPORT extern const char kIPConfigs[]; ONC_EXPORT extern const char kIPConfigs[];
ONC_EXPORT extern const char kSavedIPConfig[];
ONC_EXPORT extern const char kStaticIPConfig[];
ONC_EXPORT extern const char kMacAddress[]; ONC_EXPORT extern const char kMacAddress[];
ONC_EXPORT extern const char kName[]; ONC_EXPORT extern const char kName[];
ONC_EXPORT extern const char kNameServers[]; ONC_EXPORT extern const char kNameServers[];
...@@ -169,6 +171,7 @@ ONC_EXPORT extern const char kIPv6[]; ...@@ -169,6 +171,7 @@ ONC_EXPORT extern const char kIPv6[];
ONC_EXPORT extern const char kNameServers[]; ONC_EXPORT extern const char kNameServers[];
ONC_EXPORT extern const char kRoutingPrefix[]; ONC_EXPORT extern const char kRoutingPrefix[];
ONC_EXPORT extern const char kType[]; ONC_EXPORT extern const char kType[];
ONC_EXPORT extern const char kWebProxyAutoDiscoveryUrl[];
} // namespace ipconfig } // namespace ipconfig
namespace ethernet { namespace ethernet {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment