#ifndef NETWORKD_ADDRESS_H
#define NETWORKD_ADDRESS_H
+#include <errno.h>
#include <netinet/ether.h>
#include <string.h>
#include <sys/random.h>
};
static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
+ if (!s)
+ return -EINVAL;
+
struct ether_addr* p = ether_aton_r(s, addr);
if (!p)
- return 1;
+ return -errno;
return 0;
}
#include <sys/queue.h>
#include <unistd.h>
+#include "address.h"
#include "config.h"
#include "logging.h"
#include "string.h"
char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
};
+struct nw_config_option {
+ STAILQ_ENTRY(nw_config_option) nodes;
+
+ const char* key;
+ void* data;
+
+ // Callbacks
+ nw_config_option_read_callback_t read_callback;
+ nw_config_option_write_callback_t write_callback;
+};
+
struct nw_config {
int nrefs;
// The path to the configuration file
char path[PATH_MAX];
- STAILQ_HEAD(entries, nw_config_entry) entries;
+ STAILQ_HEAD(config_entries, nw_config_entry) entries;
+
+ // Options
+ STAILQ_HEAD(parser_entries, nw_config_option) options;
};
static void nw_config_entry_free(struct nw_config_entry* entry) {
free(entry);
}
+static void nw_config_option_free(struct nw_config_option* option) {
+ free(option);
+}
+
static struct nw_config_entry* nw_config_entry_create(
nw_config* config, const char* key) {
int r;
}
static void nw_config_free(nw_config* config) {
+ struct nw_config_option* option = NULL;
+
// Flush all entries
nw_config_flush(config);
+ // Free all options
+ while (!STAILQ_EMPTY(&config->options)) {
+ option = STAILQ_FIRST(&config->options);
+ STAILQ_REMOVE_HEAD(&config->options, nodes);
+
+ // Free the options
+ nw_config_option_free(option);
+ }
+
free(config);
}
// Initialise entries
STAILQ_INIT(&c->entries);
+ // Initialise options
+ STAILQ_INIT(&c->options);
+
// Store the path
if (path) {
r = nw_string_set(c->path, path);
return unlink(config->path);
}
+int nw_config_copy(nw_config* config, nw_config** copy) {
+ struct nw_config_entry* entry = NULL;
+ nw_config* c = NULL;
+ int r;
+
+ // Create a new configuration
+ r = nw_config_create(&c, NULL);
+ if (r)
+ return r;
+
+ // Copy everything
+ STAILQ_FOREACH(entry, &config->entries, nodes) {
+ r = nw_config_set(c, entry->key, entry->value);
+ if (r)
+ goto ERROR;
+ }
+
+ *copy = c;
+ return 0;
+
+ERROR:
+ if (c)
+ nw_config_unref(c);
+
+ return r;
+}
+
const char* nw_config_path(nw_config* config) {
if (*config->path)
return config->path;
int nw_config_set(nw_config* config, const char* key, const char* value) {
struct nw_config_entry* entry = NULL;
+ // Log the change
+ DEBUG("%p: Setting %s = %s\n", config, key, value);
+
// Delete the entry if val is NULL
if (!value)
return nw_config_del(config, key);
int nw_config_set_bool(nw_config* config, const char* key, const int value) {
return nw_config_set(config, key, value ? "true" : "false");
}
+
+/*
+ Options
+*/
+
+int nw_config_options_read(nw_config* config) {
+ struct nw_config_option* option = NULL;
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+ r = option->read_callback(config, option->key, option->data);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int nw_config_options_write(nw_config* config) {
+ struct nw_config_option* option = NULL;
+ int r;
+
+ STAILQ_FOREACH(option, &config->options, nodes) {
+ r = option->write_callback(config, option->key, option->data);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int nw_config_option_add(nw_config* config, const char* key, void* data,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback) {
+ // Check input
+ if (!key || !data || !read_callback || !write_callback)
+ return -EINVAL;
+
+ // Allocate a new option
+ struct nw_config_option* option = calloc(1, sizeof(*option));
+ if (!option)
+ return -errno;
+
+ // Set key
+ option->key = key;
+
+ // Set data
+ option->data = data;
+
+ // Set callbacks
+ option->read_callback = read_callback;
+ option->write_callback = write_callback;
+
+ // Append the new option
+ STAILQ_INSERT_TAIL(&config->options, option, nodes);
+
+ return 0;
+}
+
+int nw_config_read_int(nw_config* config, const char* key, void* data) {
+ int* p = (int*)data;
+
+ // Fetch the value
+ *p = nw_config_get_int(config, key, -1);
+
+ return 0;
+}
+
+int nw_config_write_int(nw_config* config, const char* key, const void* data) {
+ return 0;
+}
+
+// String
+
+int nw_config_read_string(nw_config* config, const char* key, void* data) {
+ const char** p = (const char**)data;
+
+ // Fetch the value
+ const char* value = nw_config_get(config, key);
+ if (value)
+ *p = value;
+
+ return 0;
+}
+
+int nw_config_write_string(nw_config* config, const char* key, const void* data) {
+ const char** value = (const char**)data;
+
+ return nw_config_set(config, key, *value);
+}
+
+// Address
+
+int nw_config_read_address(nw_config* config, const char* key, void* data) {
+ nw_address_t* address = (nw_address_t*)data;
+ int r;
+
+ // Fetch the value
+ const char* value = nw_config_get(config, key);
+ if (!value)
+ return -EINVAL;
+
+ r = nw_address_from_string(address, value);
+ if (r < 0)
+ ERROR("Could not parse address: %s\n", value);
+
+ return r;
+}
+
+int nw_config_write_address(nw_config* config, const char* key, const void* data) {
+ const nw_address_t* address = (nw_address_t*)data;
+ int r;
+
+ // Format the address to string
+ char* value = nw_address_to_string(address);
+ if (!value)
+ return -errno;
+
+ // Store the value
+ r = nw_config_set(config, key, value);
+ if (r < 0)
+ goto ERROR;
+
+ // Success
+ r = 0;
+
+ERROR:
+ if (value)
+ free(value);
+
+ return r;
+}
nw_config* nw_config_unref(nw_config* config);
int nw_config_destroy(nw_config* config);
+int nw_config_copy(nw_config* config, nw_config** copy);
const char* nw_config_path(nw_config* config);
int nw_config_get_bool(nw_config* config, const char* key);
int nw_config_set_bool(nw_config* config, const char* key, const int value);
+/*
+ Options
+*/
+
+int nw_config_options_read(nw_config* config);
+int nw_config_options_write(nw_config* config);
+
+typedef int (*nw_config_option_read_callback_t)
+ (nw_config* config, const char* key, void* data);
+typedef int (*nw_config_option_write_callback_t)
+ (nw_config* config, const char* key, const void* data);
+
+int nw_config_option_add(nw_config* config, const char* key, void* value,
+ nw_config_option_read_callback_t read_callback,
+ nw_config_option_write_callback_t write_callback);
+
+#define NW_CONFIG_OPTION(config, key, data, read_callback, write_callback) \
+ nw_config_option_add(config, key, data, read_callback, write_callback)
+
+#define NW_CONFIG_OPTION_STRING(config, key, data) \
+ nw_config_option_add(config, key, data, nw_config_read_string, nw_config_write_string)
+
+int nw_config_read_string(nw_config* config, const char* key, void* data);
+int nw_config_write_string(nw_config* config, const char* key, const void* data);
+
+#define NW_CONFIG_OPTION_INT(config, key, data) \
+ nw_config_option_add(config, key, data, nw_config_read_int, nw_config_write_int)
+
+int nw_config_read_int(nw_config* config, const char* key, void* data);
+int nw_config_write_int(nw_config* config, const char* key, const void* data);
+
+#define NW_CONFIG_OPTION_ADDRESS(config, key, data) \
+ nw_config_option_add(config, key, data, nw_config_read_address, nw_config_write_address)
+
+int nw_config_read_address(nw_config* config, const char* key, void* data);
+int nw_config_write_address(nw_config* config, const char* key, const void* data);
+
#endif /* NETWORKD_CONFIG_H */
return NULL;
}
-static int nw_port_bonding_config_read(nw_port* port) {
- int r;
-
- // Mode
- const char* mode = nw_config_get(port->config, "BONDING_MODE");
- if (mode) {
- r = nw_port_bonding_set_mode(port, mode);
- if (r)
- return r;
+static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* data) {
+ int* mode = (int*)data;
+
+ const char* value = nw_config_get(config, key);
+ if (value) {
+ *mode = nw_port_bonding_mode_from_string(value);
+ if (!*mode)
+ return -errno;
}
return 0;
}
-static int nw_port_bonding_config_write(nw_port* port) {
+static int nw_port_bonding_write_mode(nw_config* config, const char* key, const void* data) {
+ const int* mode = (int*)data;
+
+ return nw_config_set(config, key, nw_port_bonding_mode_to_string(*mode));
+}
+
+static int nw_port_bonding_setup(nw_port* port) {
int r;
// Mode
- r = nw_config_set(port->config, "BONDING_MODE",
- nw_port_bonding_mode_to_string(port->bonding.mode));
- if (r)
+ r = NW_CONFIG_OPTION(port->config, "BONDING_MODE", &port->bonding.mode,
+ nw_port_bonding_read_mode, nw_port_bonding_write_mode);
+ if (r < 0)
return r;
return 0;
// Operations
.ops = {
// Configuration
- .config_read = nw_port_bonding_config_read,
- .config_write = nw_port_bonding_config_write,
+ .setup = nw_port_bonding_setup,
// Link
.create_link = nw_port_bonding_create_link,
NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
-static int nw_port_vlan_config_read(nw_port* port) {
- int r;
-
- // VLAN ID
- int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
- if (id) {
- r = nw_port_set_vlan_id(port, id);
- if (r)
- return r;
+static int nw_port_vlan_read_proto(nw_config* config, const char* key, void* data) {
+ nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
+
+ const char* value = nw_config_get(config, key);
+ if (value) {
+ *proto = nw_port_vlan_proto_from_string(data);
+ if (!*proto)
+ return -errno;
}
- // VLAN Protocol
- const char* proto = nw_config_get(port->config, "VLAN_PROTO");
- if (proto) {
- r = nw_port_set_vlan_proto(port, nw_port_vlan_proto_from_string(proto));
- if (r)
- return r;
- }
+ return 0;
+}
- // Parent Port
- const char* parent = nw_config_get(port->config, "VLAN_PARENT");
- if (parent) {
- r = nw_string_set(port->vlan.__parent_name, parent);
- if (r)
- return r;
- }
+static int nw_port_vlan_write_proto(nw_config* config, const char* key, const void* data) {
+ const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
- return 0;
+ return nw_config_set(config, key, nw_port_vlan_proto_to_string(*proto));
}
-static int nw_port_vlan_config_write(nw_port* port) {
+static int nw_port_vlan_setup(nw_port* port) {
int r;
// VLAN ID
- r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
- if (r)
+ r = NW_CONFIG_OPTION_INT(port->config, "VLAN_ID", &port->vlan.id);
+ if (r < 0)
return r;
// VLAN Protocol
- r = nw_config_set(port->config, "VLAN_PROTO",
- nw_port_vlan_proto_to_string(port->vlan.proto));
- if (r)
+ r = NW_CONFIG_OPTION(port->config, "VLAN_PROTO", &port->vlan.proto,
+ nw_port_vlan_read_proto, nw_port_vlan_write_proto);
+ if (r < 0)
return r;
// Parent Port
- r = nw_config_set(port->config, "VLAN_PARENT",
- (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
- if (r)
+ r = NW_CONFIG_OPTION_STRING(port->config, "VLAN_PARENT", &port->vlan.__parent_name);
+ if (r < 0)
return r;
return 0;
// Operations
.ops = {
// Configuration
- .config_read = nw_port_vlan_config_read,
- .config_write = nw_port_vlan_config_write,
+ .setup = nw_port_vlan_setup,
.get_parent_port = nw_port_get_vlan_parent,
free(port);
}
-static int nw_port_setup_address(nw_port* port) {
- char* __address = NULL;
- int r;
-
- // Read ADDRESS from configuration
- const char* s = nw_config_get(port->config, "ADDRESS");
- if (!s) {
- ERROR("Port %s: Address is not set\n", port->name);
- goto ERROR;
- }
-
- // Parse the address
- r = nw_address_from_string(&port->address, s);
- if (r) {
- ERROR("Port %s: Could not parse address: %m\n", port->name);
- goto ERROR;
- }
-
- // Check if this address is usable
- r = nw_address_is_multicast(&port->address);
- if (r) {
- DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
- goto ERROR;
- }
-
- return 0;
-
-ERROR:
- // Generate a random Ethernet address
- r = nw_address_generate(&port->address);
- if (r) {
- ERROR("Could not generate a random Ethernet address: %m\n");
- return r;
- }
-
- // Format the generated address
- __address = nw_address_to_string(&port->address);
- if (__address) {
- ERROR("Generated a random Ethernet address for %s: %s\n", port->name, __address);
-
- // Free the address
- free(__address);
- }
-
- return 0;
-}
-
-static int nw_port_setup_common(nw_port* port) {
- int r;
-
- // Address
- r = nw_port_setup_address(port);
- if (r)
- return r;
-
- return 0;
-}
-
static int nw_port_set_link(nw_port* port, nw_link* link) {
// Do nothing if the same link is being re-assigned
if (port->link == link)
static int nw_port_setup(nw_port* port) {
nw_link* link = NULL;
- char path[PATH_MAX];
int r;
// Find the link
goto ERROR;
}
- // Compose the path to the main configuration file
- r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
- if (r)
- goto ERROR;
-
- // Initialize the configuration
- r = nw_config_create(&port->config, path);
- if (r)
+ // Generate a random Ethernet address
+ r = nw_address_generate(&port->address);
+ if (r < 0) {
+ ERROR("Could not generate an Ethernet address: %s\n", strerror(-r));
goto ERROR;
+ }
- // Perform some common initialization
- r = nw_port_setup_common(port);
- if (r)
+ // Setup options
+ r = NW_CONFIG_OPTION_ADDRESS(port->config, "ADDRESS", &port->address);
+ if (r < 0)
goto ERROR;
// Call any custom initialization
- if (NW_PORT_OPS(port)->config_read) {
- r = NW_PORT_OPS(port)->config_read(port);
- if (r)
+ if (NW_PORT_OPS(port)->setup) {
+ r = NW_PORT_OPS(port)->setup(port);
+ if (r < 0)
goto ERROR;
}
+ // Parse the configuration
+ r = nw_config_options_read(port->config);
+ if (r < 0) {
+ ERROR("Could not read configuration for port %s: %s\n", port->name, strerror(-r));
+ goto ERROR;
+ }
+
ERROR:
if (link)
nw_link_unref(link);
return r;
}
-int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
+int nw_port_create(nw_port** port, nw_daemon* daemon,
+ nw_port_type_t type, const char* name, nw_config* config) {
int r;
// Allocate a new object
if (r)
goto ERROR;
+ // Copy the configuration
+ r = nw_config_copy(config, &p->config);
+ if (r)
+ goto ERROR;
+
// Setup the port
r = nw_port_setup(p);
if (r)
}
// Create a new port
- r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
+ r = nw_port_create(port, daemon, nw_port_type_from_string(type), name, config);
if (r)
goto ERROR;
int nw_port_save(nw_port* port) {
int r;
- // Call the custom handler
- if (NW_PORT_OPS(port)->config_write) {
- r = NW_PORT_OPS(port)->config_write(port);
- if (r)
- goto ERROR;
- }
+ // Write out the configuration
+ r = nw_config_options_write(port->config);
+ if (r < 0)
+ goto ERROR;
// Write the configuration
r = nw_config_write(port->config);
return 0;
ERROR:
- ERROR("Could not save configuration for port %s: %m\n", port->name);
+ ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
return 1;
}
struct nw_port_ops {
// Configuration
- int (*config_read)(nw_port* port);
- int (*config_write)(nw_port* port);
+ int (*setup)(nw_port* port);
// Get Parent Port
nw_port* (*get_parent_port)(nw_port* port);
};
int nw_port_create(nw_port** port, nw_daemon* daemon,
- nw_port_type_t type, const char* name);
+ nw_port_type_t type, const char* name, nw_config* config);
int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
const char* name, const char* path);