#include <stdint.h>
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/netlink.h>
#include <log/log.h>
#include <list/list.h>
+#include <types/types.h>
#include <talloc/talloc.h>
#include <waiter/waiter.h>
-#include <pb-config/pb-config.h>
+#include <process/process.h>
#include <system/system.h>
+#include "file.h"
#include "network.h"
+#include "sysinfo.h"
+#include "platform.h"
+#include "device-handler.h"
#define HWADDR_SIZE 6
#define PIDFILE_BASE (LOCAL_STATE_DIR "/petitboot/")
+#define INITIAL_BUFSIZE 4096
#define for_each_nlmsg(buf, nlmsg, len) \
for (nlmsg = (struct nlmsghdr *)buf; \
} state;
struct list_item list;
+ struct process *udhcpc_process;
+ struct discover_device *dev;
};
struct network {
- struct list interfaces;
- struct waiter *waiter;
- int netlink_sd;
- bool manual_config;
- bool dry_run;
+ struct list interfaces;
+ struct device_handler *handler;
+ struct waiter *waiter;
+ int netlink_sd;
+ void *netlink_buf;
+ unsigned int netlink_buf_size;
+ bool manual_config;
+ bool dry_run;
};
-static const struct network_config *find_config_by_hwaddr(
+static const struct interface_config *find_config_by_hwaddr(
uint8_t *hwaddr)
{
const struct config *config;
- int i;
+ unsigned int i;
config = config_get();
if (!config)
return NULL;
- for (i = 0; i < config->n_network_configs; i++) {
- struct network_config *netconf = config->network_configs[i];
+ for (i = 0; i < config->network.n_interfaces; i++) {
+ struct interface_config *ifconf = config->network.interfaces[i];
- if (!memcmp(netconf->hwaddr, hwaddr, HWADDR_SIZE))
- return netconf;
+ if (!memcmp(ifconf->hwaddr, hwaddr, HWADDR_SIZE))
+ return ifconf;
}
return NULL;
return -1;
}
+ network->netlink_buf_size = INITIAL_BUFSIZE;
+ network->netlink_buf = talloc_array(network, char,
+ network->netlink_buf_size);
+
return 0;
}
return 0;
}
-static int interface_up(struct network *network, struct interface *interface)
+static void add_interface(struct network *network,
+ struct interface *interface)
{
+ list_add(&network->interfaces, &interface->list);
+ interface->dev = discover_device_create(network->handler,
+ interface->name);
+ interface->dev->device->type = DEVICE_TYPE_NETWORK;
+ device_handler_add_device(network->handler, interface->dev);
+}
+
+static void remove_interface(struct network *network,
+ struct interface *interface)
+{
+ device_handler_remove(network->handler, interface->dev);
+ list_remove(&interface->list);
+ talloc_free(interface);
+}
+
+static int interface_change(struct interface *interface, bool up)
+{
+ const char *statestr = up ? "up" : "down";
int rc;
- const char *argv[] = {
- pb_system_apps.ip,
- "link",
- "set",
- interface->name,
- "up",
- NULL,
- };
- rc = pb_run_cmd(argv, 1, network->dry_run);
+ if (!up && interface->udhcpc_process) {
+ /* we don't care about the callback from here */
+ interface->udhcpc_process->exit_cb = NULL;
+ interface->udhcpc_process->data = NULL;
+ process_stop_async(interface->udhcpc_process);
+ process_release(interface->udhcpc_process);
+ }
+
+ if (!up) {
+ rc = process_run_simple(interface, pb_system_apps.ip,
+ "address", "flush", "dev", interface->name,
+ NULL);
+ if (rc)
+ pb_log("failed to flush addresses from interface %s\n",
+ interface->name);
+ }
+
+ rc = process_run_simple(interface, pb_system_apps.ip,
+ "link", "set", interface->name, statestr, NULL);
if (rc) {
- pb_log("failed to bring interface %s up\n", interface->name);
+ pb_log("failed to bring interface %s %s\n", interface->name,
+ statestr);
return -1;
}
return 0;
}
-static void configure_interface_dhcp(struct network *network,
- struct interface *interface)
+static int interface_up(struct interface *interface)
+{
+ return interface_change(interface, true);
+}
+
+static int interface_down(struct interface *interface)
+{
+ return interface_change(interface, false);
+}
+
+static void udhcpc_process_exit(struct process *process)
{
- char pidfile[256];
+ struct interface *interface = process->data;
+ pb_debug("udhcp client [pid %d] for interface %s exited, rc %d\n",
+ process->pid, interface->name, process->exit_status);
+ interface->udhcpc_process = NULL;
+ process_release(process);
+}
+
+static void configure_interface_dhcp(struct interface *interface)
+{
+ const struct platform *platform;
+ char pidfile[256], id[10];
+ struct process *process;
+ int rc;
const char *argv[] = {
pb_system_apps.udhcpc,
"-R",
- "-n",
+ "-f",
+ "-O", "pxeconffile",
+ "-O", "pxepathprefix",
"-p", pidfile,
"-i", interface->name,
+ "-x", id, /* [11,12] - dhcp client identifier */
NULL,
};
+
snprintf(pidfile, sizeof(pidfile), "%s/udhcpc-%s.pid",
PIDFILE_BASE, interface->name);
- pb_run_cmd(argv, 0, network->dry_run);
+ platform = platform_get();
+ if (platform && platform->dhcp_arch_id != 0xffff)
+ snprintf(id, sizeof(id), "0x5d:%04x", platform->dhcp_arch_id);
+ else
+ argv[11] = NULL;
+
+ process = process_create(interface);
+
+ process->path = pb_system_apps.udhcpc;
+ process->argv = argv;
+ process->exit_cb = udhcpc_process_exit;
+ process->data = interface;
+
+ rc = process_run_async(process);
+
+ if (rc)
+ process_release(process);
+ else
+ interface->udhcpc_process = process;
+
return;
}
-static void configure_interface_static(struct network *network,
- struct interface *interface,
- const struct network_config *config)
+static void configure_interface_static(struct interface *interface,
+ const struct interface_config *config)
{
- const char *addr_argv[] = {
- pb_system_apps.ip,
- "address",
- "add",
- config->static_config.address,
- "dev",
- interface->name,
- NULL,
- };
- const char *route_argv[] = {
- pb_system_apps.ip,
- "route",
- "add",
- "default",
- "via",
- config->static_config.gateway,
- NULL,
- };
int rc;
+ rc = process_run_simple(interface, pb_system_apps.ip,
+ "address", "add", config->static_config.address,
+ "dev", interface->name, NULL);
+
- rc = pb_run_cmd(addr_argv, 1, network->dry_run);
if (rc) {
pb_log("failed to add address %s to interface %s\n",
config->static_config.address,
}
/* we need the interface up before we can route through it */
- rc = interface_up(network, interface);
+ rc = interface_up(interface);
if (rc)
return;
if (config->static_config.gateway)
- rc = pb_run_cmd(route_argv, 1, network->dry_run);
+ rc = process_run_simple(interface, pb_system_apps.ip,
+ "route", "add", "default",
+ "via", config->static_config.gateway,
+ NULL);
if (rc) {
pb_log("failed to add default route %s on interface %s\n",
static void configure_interface(struct network *network,
struct interface *interface, bool up, bool link)
{
- const struct network_config *config = NULL;
+ const struct interface_config *config = NULL;
if (interface->state == IFSTATE_IGNORED)
return;
/* always up the lookback, no other handling required */
if (!strcmp(interface->name, "lo")) {
if (interface->state == IFSTATE_NEW)
- interface_up(network, interface);
+ interface_up(interface);
interface->state = IFSTATE_CONFIGURED;
return;
}
/* new interface? bring up to the point so we can detect a link */
if (interface->state == IFSTATE_NEW) {
if (!up) {
- interface_up(network, interface);
+ interface_up(interface);
pb_log("network: bringing up interface %s\n",
interface->name);
return;
pb_log("network: configuring interface %s\n", interface->name);
if (!config || config->method == CONFIG_METHOD_DHCP) {
- configure_interface_dhcp(network, interface);
+ configure_interface_dhcp(interface);
} else if (config->method == CONFIG_METHOD_STATIC) {
- configure_interface_static(network, interface, config);
+ configure_interface_static(interface, config);
}
}
struct interface *interface;
struct ifinfomsg *info;
struct rtattr *attr;
+ unsigned int mtu;
uint8_t ifaddr[6];
char ifname[IFNAMSIZ+1];
int attrlen, type;
info = NLMSG_DATA(nlmsg);
have_ifaddr = have_ifname = false;
+ mtu = 1;
attrlen = nlmsg->nlmsg_len - sizeof(*info);
strncpy(ifname, data, IFNAMSIZ);
have_ifname = true;
break;
+
+ case IFLA_MTU:
+ mtu = *(unsigned int *)data;
+ break;
}
}
if (!have_ifaddr || !have_ifname)
return -1;
- if (type == RTM_DELLINK) {
+ if (type == RTM_DELLINK || mtu == 0) {
interface = find_interface_by_ifindex(network, info->ifi_index);
if (!interface)
return 0;
pb_log("network: interface %s removed\n", interface->name);
- list_remove(&interface->list);
- talloc_free(interface);
+ remove_interface(network, interface);
return 0;
}
interface->state = IFSTATE_NEW;
memcpy(interface->hwaddr, ifaddr, sizeof(interface->hwaddr));
strncpy(interface->name, ifname, sizeof(interface->name) - 1);
+ add_interface(network, interface);
}
+ /* notify the sysinfo code about changes to this interface */
+ if (strcmp(interface->name, "lo"))
+ system_info_register_interface(
+ sizeof(interface->hwaddr),
+ interface->hwaddr, interface->name,
+ info->ifi_flags & IFF_LOWER_UP);
+
configure_interface(network, interface,
info->ifi_flags & IFF_UP,
info->ifi_flags & IFF_LOWER_UP);
{
struct network *network = arg;
struct nlmsghdr *nlmsg;
+ struct msghdr msg;
+ struct iovec iov;
unsigned int len;
- char buf[4096];
- int rc;
+ int rc, flags;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ flags = MSG_PEEK;
+
+retry:
+ iov.iov_len = network->netlink_buf_size;
+ iov.iov_base = network->netlink_buf;
+
+ rc = recvmsg(network->netlink_sd, &msg, flags);
- rc = recv(network->netlink_sd, buf, sizeof(buf), 0);
if (rc < 0) {
- perror("netlink recv");
+ perror("netlink recv header");
return -1;
}
len = rc;
- for_each_nlmsg(buf, nlmsg, len)
+ /* if the netlink message was larger than our buffer, realloc
+ * before reading again */
+ if (len > network->netlink_buf_size || msg.msg_flags & MSG_TRUNC) {
+ network->netlink_buf_size *= 2;
+ network->netlink_buf = talloc_realloc(network,
+ network->netlink_buf,
+ char *,
+ network->netlink_buf_size);
+ goto retry;
+ }
+
+ /* otherwise, we're good to read the entire message without PEEK */
+ if (flags == MSG_PEEK) {
+ flags = 0;
+ goto retry;
+ }
+
+ for_each_nlmsg(network->netlink_buf, nlmsg, len)
network_handle_nlmsg(network, nlmsg);
return 0;
}
-struct network *network_init(void *ctx, struct waitset *waitset, bool dry_run)
+static void network_init_dns(struct network *network)
+{
+ const struct config *config;
+ unsigned int i;
+ int rc, len;
+ bool modified;
+ char *buf;
+
+ if (network->dry_run)
+ return;
+
+ config = config_get();
+ if (!config || !config->network.n_dns_servers)
+ return;
+
+ rc = read_file(network, "/etc/resolv.conf", &buf, &len);
+
+ if (rc) {
+ buf = talloc_strdup(network, "");
+ len = 0;
+ }
+
+ modified = false;
+
+ for (i = 0; i < config->network.n_dns_servers; i++) {
+ int dns_conf_len;
+ char *dns_conf;
+
+ dns_conf = talloc_asprintf(network, "nameserver %s\n",
+ config->network.dns_servers[i]);
+
+ if (strstr(buf, dns_conf)) {
+ talloc_free(dns_conf);
+ continue;
+ }
+
+ dns_conf_len = strlen(dns_conf);
+ buf = talloc_realloc(network, buf, char, len + dns_conf_len + 1);
+ memcpy(buf + len, dns_conf, dns_conf_len);
+ len += dns_conf_len;
+ buf[len] = '\0';
+ modified = true;
+
+ talloc_free(dns_conf);
+ }
+
+ if (modified) {
+ rc = replace_file("/etc/resolv.conf", buf, len);
+ if (rc)
+ pb_log("error replacing resolv.conf: %s\n",
+ strerror(errno));
+ }
+
+ talloc_free(buf);
+}
+
+struct network *network_init(struct device_handler *handler,
+ struct waitset *waitset, bool dry_run)
{
struct network *network;
int rc;
- network = talloc(ctx, struct network);
+ network = talloc(handler, struct network);
list_init(&network->interfaces);
- network->manual_config = false;
+ network->handler = handler;
network->dry_run = dry_run;
+ network->manual_config = config_get()->network.n_interfaces != 0;
+
+ network_init_dns(network);
rc = network_init_netlink(network);
if (rc)
return NULL;
}
-
int network_shutdown(struct network *network)
{
+ struct interface *interface;
+
if (network->waiter)
waiter_remove(network->waiter);
+ list_for_each_entry(&network->interfaces, interface, list) {
+ if (interface->state == IFSTATE_IGNORED)
+ continue;
+ if (!strcmp(interface->name, "lo"))
+ continue;
+ interface_down(interface);
+ }
+
close(network->netlink_sd);
talloc_free(network);
return 0;