Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netconf support IPv6 #2114

Merged
merged 11 commits into from
Apr 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions api/util/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
#ifndef UTIL_CONFIG_HPP
#define UTIL_CONFIG_HPP

#ifndef RAPIDJSON_HAS_STDSTRING
#define RAPIDJSON_HAS_STDSTRING 1
#endif

#ifndef RAPIDJSON_THROWPARSEEXCEPTION
#define RAPIDJSON_THROWPARSEEXCEPTION 1
#endif

#include <rapidjson/document.h>
#include <cstddef>

/**
Expand All @@ -35,6 +44,13 @@ class Config {
*/
static const Config& get() noexcept;

/**
* @brief Retrieve the the config as a parsed json document.
*
* @return Json Doc
*/
static const rapidjson::Document& doc();

const char* data() const noexcept
{ return start_; }

Expand Down
290 changes: 248 additions & 42 deletions src/net/configure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,41 +22,188 @@

#define MYINFO(X,...) INFO("Netconf",X,##__VA_ARGS__)

//#define NETCONF_DEBUG 1
#ifdef NETCONF_DEBUG
#define PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define PRINT(fmt, ...) /* fmt */
#endif

namespace net {

using Addresses = std::vector<ip4::Addr>;
struct ipv4_res
{
ip4::Addr addr;
ip4::Addr netmask;
};

struct ipv6_res
{
ip6::Addr addr;
uint8_t prefix;
};

struct v4_config
{
ip4::Addr addr;
ip4::Addr netmask;
ip4::Addr gateway;
ip4::Addr dns;
};

struct v6_config
{
std::vector<ipv6_res> addr;
std::vector<ip6::Addr> gateway;
std::vector<ip6::Addr> dns;

void clear()
{
addr.clear();
gateway.clear();
dns.clear();
}
};

template <typename T>
Addresses parse_iface(T& obj)
inline bool is_v6(const std::string& str)
{
if(not obj.HasMember("address") and not obj.HasMember("netmask"))
return {};
Expects(obj.HasMember("address"));
Expects(obj.HasMember("netmask"));

ip4::Addr address{obj["address"].GetString()};
ip4::Addr netmask{obj["netmask"].GetString()};
ip4::Addr gateway = (obj.HasMember("gateway")) ? ip4::Addr{obj["gateway"].GetString()} : 0;
ip4::Addr dns = (not obj.HasMember("dns")) ? gateway : ip4::Addr{obj["dns"].GetString()};

Addresses addresses{address, netmask, gateway, dns};
return addresses;
Expects(not str.empty() && "Address string can't be empty");
return str.find(':') != std::string::npos;
}

inline void config_stack(Inet& stack, const Addresses& addrs)
inline ipv4_res parse_addr4(const std::string& str)
{
if(addrs.empty()) {
MYINFO("! WARNING: No config for stack %s", stack.ifname().c_str());
return;
if(auto n = str.rfind('/'); n != std::string::npos)
{
uint8_t bits = std::stoi(str.substr(n+1));
ip4::Addr netmask{ntohl(0xFFFFFFFF << (32ul-bits))};
//PRINT("Bits %u Netmask %s\n", bits, netmask.to_string().c_str());
return {str.substr(0, n), netmask};
}
else
{
return {str, 0};
}
}

Expects((addrs.size() > 2 and addrs.size() < 5)
&& "A network config needs to be between 3 and 4 addresses");
inline ipv6_res parse_addr6(const std::string& str)
{
if(auto n = str.rfind('/'); n != std::string::npos)
{
uint8_t prefix = std::stoi(str.substr(n+1));
return {str.substr(0, n), prefix};
}
else
{
return {str, 0};
}
}

inline void parse_addr(v4_config& cfg4, v6_config& cfg6, std::string str)
{
if(is_v6(str))
{
cfg6.addr.push_back(parse_addr6(str));
PRINT("v6 Addr: %s Prefix: %u\n",
cfg6.addr.back().addr.to_string().c_str(), cfg6.addr.back().prefix);
}
else
{
if(cfg4.addr != 0)
{
MYINFO("WARN: Multiple v4 addresses not supported: skipping %s", str.c_str());
return;
}

stack.network_config(
addrs[0], addrs[1], addrs[2],
((addrs.size() == 4) ? addrs[3] : 0)
);
auto [addr, netmask] = parse_addr4(str);
cfg4.addr = addr;
cfg4.netmask = netmask;
PRINT("v4 Addr: %s Netmask: %s\n",
cfg4.addr.to_string().c_str(), cfg4.netmask.to_string().c_str());
}
}

inline void parse_gateway(v4_config& cfg4, v6_config& cfg6, std::string str)
{
if(is_v6(str))
{
cfg6.gateway.push_back(parse_addr6(str).addr);
PRINT("v6 Gateway: %s\n",
cfg6.gateway.back().to_string().c_str());
}
else
{
if(cfg4.gateway != 0)
{
MYINFO("WARN: Multiple v4 gateways not supported: skipping %s", str.c_str());
return;
}

cfg4.gateway = parse_addr4(str).addr;
PRINT("v4 Gateway: %s\n",
cfg4.gateway.to_string().c_str());
}
}

inline void parse_dns(v4_config& cfg4, v6_config& cfg6, std::string str)
{
if(is_v6(str))
{
cfg6.dns.push_back(parse_addr6(str).addr);
PRINT("v6 DNS: %s\n",
cfg6.dns.back().to_string().c_str());
}
else
{
if(cfg4.dns != 0)
{
MYINFO("WARN: Multiple v4 DNS not supported: skipping %s", str.c_str());
return;
}

cfg4.dns = parse_addr4(str).addr;
PRINT("v4 DNS: %s\n",
cfg4.dns.to_string().c_str());
}
}

inline void config4(Inet& stack, const v4_config& cfg)
{
Expects(cfg.addr != 0 && "Missing address (v4)");
Expects(cfg.netmask != 0 && "Missing netmask");
stack.network_config(cfg.addr, cfg.netmask, cfg.gateway, cfg.dns);
}

inline void config6(Inet& stack, const v6_config& cfg)
{
// add address
for(const auto& [addr, prefix] : cfg.addr)
(prefix) ? stack.add_addr(addr, prefix) : stack.add_addr(addr);

// add routers
for(const auto& addr : cfg.gateway)
stack.ndp().add_router(addr, 0xFFFF); // waiting for API to change

// add dns
for(const auto& addr : cfg.dns) {
stack.set_dns_server6(addr);
break; // currently only support one
}
}

template <typename Val, typename Func>
inline void parse(const Val& val, v4_config& cfg4, v6_config& cfg6, Func func)
{
if(val.IsArray())
{
PRINT("Member is array\n");
for(auto& addrstr : val.GetArray()) {
func(cfg4, cfg6, addrstr.GetString());
}
}
else {
func(cfg4, cfg6, val.GetString());
}
}

void configure(const rapidjson::Value& net)
Expand All @@ -67,7 +214,7 @@ void configure(const rapidjson::Value& net)

auto configs = net.GetArray();
if(configs.Size() > Interfaces::get().size())
MYINFO("! WARNING: Found more configs than there are interfaces");
MYINFO("WARN: Found more configs than there are interfaces");
// Iterate all interfaces in config
for(auto& val : configs)
{
Expand All @@ -80,33 +227,92 @@ void configure(const rapidjson::Value& net)

// if config is not set, just ignore
if(not val.HasMember("config")) {
MYINFO("NOTE: Config method not set, ignoring");
MYINFO("WARN: Config method not set, ignoring");
continue;
}

std::string method = val["config"].GetString();
v4_config v4cfg;
v6_config v6cfg;

// "address"
if(val.HasMember("address"))
{
parse(val["address"], v4cfg, v6cfg, &parse_addr);
}

double timeout = (val.HasMember("timeout")) ? val["timeout"].GetDouble() : 10.0;
// "netmask" (ipv4 only)
if(v4cfg.netmask == 0 and val.HasMember("netmask"))
v4cfg.netmask = {val["netmask"].GetString()};

if(method == "dhcp")
// "gateway"
if(val.HasMember("gateway"))
{
stack.negotiate_dhcp(timeout);
parse(val["gateway"], v4cfg, v6cfg, &parse_gateway);
}
else if(method == "static")

// "dns"
if(val.HasMember("dns"))
{
parse(val["dns"], v4cfg, v6cfg, &parse_dns);
}

std::vector<std::string> methods;
const auto& config = val["config"];

// parse all the config methods
if(config.IsArray())
{
config_stack(stack, parse_iface(val));
for(const auto& method : val["config"].GetArray())
methods.push_back(method.GetString());
}
else if(method == "dhcp-with-fallback")
else
{
auto addresses = parse_iface(val);
auto static_cfg = [addresses, &stack] (bool timedout)
methods.push_back(val["config"].GetString());
}

for(const auto& method : methods)
{
const double timeout = (val.HasMember("timeout")) ? val["timeout"].GetDouble() : 10.0;

if(method == "dhcp") {
stack.negotiate_dhcp(timeout);
// do DHCPv6
}
else if(method == "dhcp4") {
stack.negotiate_dhcp(timeout);
}
else if(method == "dhcp6") {
MYINFO("WARN: DHCPv6 not supported");
}
else if(method == "static") {
config4(stack, v4cfg);
config6(stack, v6cfg);
v6cfg.clear();
}
else if(method == "static4") {
config4(stack, v4cfg);
}
else if(method == "static6") {
config6(stack, v6cfg);
v6cfg.clear();
}
else if(method == "slaac") {
stack.autoconf_v6();
}
else if(method == "dhcp-with-fallback") // TBD...
{
if(timedout) {
MYINFO("DHCP timeout (%s) - falling back to static configuration", stack.ifname().c_str());
config_stack(stack, addresses);
}
};
stack.negotiate_dhcp(timeout, static_cfg);
auto static_cfg = [v4cfg, &stack] (bool timedout)
{
if(timedout) {
MYINFO("DHCP timeout (%s) - falling back to static configuration", stack.ifname().c_str());
config4(stack, v4cfg);
}
};
stack.negotiate_dhcp(timeout, static_cfg);
}
else {
MYINFO("WARN: Unrecognized config method \"%s\"", method.c_str());
}
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/net/inet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,16 @@ void Inet::autoconf_v6(int retries, slaac_timeout_func handler,
uint64_t token, bool use_token)
{

INFO("Inet", "Attempting automatic configuration of ipv6 address");
INFO("Inet6", "Attempting automatic configuration of IPv6 address");
if (!slaac_)
slaac_ = std::make_unique<Slaac>(*this);
slaac_ = std::make_unique<Slaac>(*this);

// @Retries for Slaac auto-configuration
slaac_->autoconf_start(retries, token, use_token);

// add failure_handler if supplied
if (handler)
slaac_->on_config(handler);
slaac_->on_config(handler);
}

void Inet::network_config(ip4::Addr addr,
Expand Down Expand Up @@ -283,10 +283,11 @@ void Inet::network_config6(IP6::addr addr6,
void Inet::add_addr(const ip6::Addr& addr, uint8_t prefix,
uint32_t pref_lifetime, uint32_t valid_lifetime)
{
Expects(prefix != 0);
int r = this->ip6_.addr_list().input(
addr, prefix, pref_lifetime, valid_lifetime);
if(r == 1) {
INFO("Inet6", "Address configured %s", addr.to_string().c_str());
INFO("Inet6", "Address configured %s/%u", addr.to_string().c_str(), prefix);
}
}

Expand Down
Loading