summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'mdk-stage1/network.c')
-rw-r--r--mdk-stage1/network.c451
1 files changed, 445 insertions, 6 deletions
diff --git a/mdk-stage1/network.c b/mdk-stage1/network.c
index de0e0070e..31042b1f6 100644
--- a/mdk-stage1/network.c
+++ b/mdk-stage1/network.c
@@ -21,15 +21,416 @@
#include <stdlib.h>
#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <resolv.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+
#include "stage1.h"
#include "frontend.h"
#include "modules.h"
#include "probing.h"
#include "log.h"
+#include "dns.h"
#include "network.h"
+static void error_message_net(void) /* reduce code size */
+{
+ error_message("Could not configure network");
+}
+
+
+static int configure_net_device(struct interface_info * intf)
+{
+ struct ifreq req;
+ struct rtentry route;
+ int s;
+ struct sockaddr_in addr;
+ struct in_addr ia;
+ char ip[20], nm[20], nw[20], bc[20];
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+
+ memcpy(&ia, &intf->ip, sizeof(intf->ip));
+ strcpy(ip, inet_ntoa(ia));
+
+ memcpy(&ia, &intf->netmask, sizeof(intf->netmask));
+ strcpy(nm, inet_ntoa(ia));
+
+ memcpy(&ia, &intf->broadcast, sizeof(intf->broadcast));
+ strcpy(bc, inet_ntoa(ia));
+
+ memcpy(&ia, &intf->network, sizeof(intf->network));
+ strcpy(nw, inet_ntoa(ia));
+
+ log_message("configuring device %s ip: %s nm: %s nw: %s bc: %s", intf->device, ip, nm, nw, bc);
+
+ if (IS_TESTING)
+ return 0;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_perror("socket");
+ error_message_net();
+ return 1;
+ }
+
+ strcpy(req.ifr_name, intf->device);
+
+ if (intf->is_up == 1) {
+ log_message("interface already up, downing");
+
+ req.ifr_flags = 0;
+ if (ioctl(s, SIOCSIFFLAGS, &req)) {
+ close(s);
+ log_perror("SIOCSIFFLAGS (downing)");
+ error_message_net();
+ return 1;
+ }
+ }
+
+ /* sets IP address */
+ addr.sin_port = 0;
+ memcpy(&addr.sin_addr, &intf->ip, sizeof(intf->ip));
+ memcpy(&req.ifr_addr, &addr, sizeof(addr));
+ if (ioctl(s, SIOCSIFADDR, &req)) {
+ close(s);
+ log_perror("SIOCSIFADDR");
+ error_message_net();
+ return 1;
+ }
+
+ /* sets broadcast */
+ memcpy(&addr.sin_addr, &intf->broadcast, sizeof(intf->broadcast));
+ memcpy(&req.ifr_broadaddr, &addr, sizeof(addr));
+ if (ioctl(s, SIOCSIFBRDADDR, &req)) {
+ close(s);
+ log_perror("SIOCSIFBRDADDR");
+ error_message_net();
+ return 1;
+ }
+
+ /* sets netmask */
+ memcpy(&addr.sin_addr, &intf->netmask, sizeof(intf->netmask));
+ memcpy(&req.ifr_netmask, &addr, sizeof(addr));
+ if (ioctl(s, SIOCSIFNETMASK, &req)) {
+ close(s);
+ log_perror("SIOCSIFNETMASK");
+ error_message_net();
+ return 1;
+ }
+
+ if (intf->is_ptp)
+ req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_POINTOPOINT | IFF_NOARP;
+ else
+ req.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST;
+
+ /* brings up networking! */
+ if (ioctl(s, SIOCSIFFLAGS, &req)) {
+ close(s);
+ log_perror("SIOCSIFFLAGS (upping)");
+ error_message_net();
+ return 1;
+ }
+
+ memset(&route, 0, sizeof(route));
+ route.rt_dev = intf->device;
+ route.rt_flags = RTF_UP;
+
+ memcpy(&addr.sin_addr, &intf->network, sizeof(intf->network));
+ memcpy(&route.rt_dst, &addr, sizeof(addr));
+
+ memcpy(&addr.sin_addr, &intf->netmask, sizeof(intf->netmask));
+ memcpy(&route.rt_genmask, &addr, sizeof(addr));
+
+ /* adds route */
+ if (ioctl(s, SIOCADDRT, &route)) {
+ close(s);
+ log_perror("SIOCADDRT");
+ error_message_net();
+ return 1;
+ }
+
+ close(s);
+
+ intf->is_up = 1;
+
+ return 0;
+}
+
+/* host network informations */
+static char * hostname = NULL;
+static char * domain = NULL;
+static struct in_addr gateway = { 0 };
+static struct in_addr dns_server = { 0 };
+
+static int add_default_route(void)
+{
+ int s;
+ struct rtentry route;
+ struct sockaddr_in addr;
+
+ if (IS_TESTING)
+ return 0;
+
+ if (gateway.s_addr == 0) {
+ log_message("no gateway provided, can't add default route");
+ return 0;
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ close(s);
+ log_perror("socket");
+ error_message_net();
+ return 1;
+ }
+
+ memset(&route, 0, sizeof(route));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0;
+ addr.sin_addr = gateway;
+ memcpy(&route.rt_gateway, &addr, sizeof(addr));
+
+ addr.sin_addr.s_addr = INADDR_ANY;
+ memcpy(&route.rt_dst, &addr, sizeof(addr));
+ memcpy(&route.rt_genmask, &addr, sizeof(addr));
+
+ route.rt_flags = RTF_UP | RTF_GATEWAY;
+ route.rt_metric = 0;
+
+ if (ioctl(s, SIOCADDRT, &route)) {
+ close(s);
+ log_perror("SIOCADDRT");
+ error_message_net();
+ return 1;
+ }
+
+ close(s);
+
+ return 0;
+}
+
+
+static int write_resolvconf(void) {
+ char * filename = "/etc/resolv.conf";
+ FILE * f;
+
+ if (IS_TESTING)
+ return 1;
+
+ if (dns_server.s_addr == 0) {
+ log_message("resolvconf needs a dns server");
+ return -1;
+ }
+
+ f = fopen(filename, "w");
+ if (!f) {
+ log_perror(filename);
+ return -1;
+ }
+
+ if (domain)
+ fprintf(f, "search %s\n", domain); /* we can live without the domain search (user will have to enter fully-qualified names) */
+ fprintf(f, "nameserver %s\n", inet_ntoa(dns_server));
+
+ fclose(f);
+ res_init(); /* reinit the resolver so DNS changes take affect */
+
+ return 0;
+}
+
+
+static void guess_netmask(struct interface_info * intf, struct in_addr * addr)
+{
+ unsigned long int tmp = ntohl(intf->ip.s_addr);
+ if (((tmp & 0xFF000000) >> 24) <= 127)
+ inet_aton("255.0.0.0", addr);
+ else if (((tmp & 0xFF000000) >> 24) <= 191)
+ inet_aton("255.255.0.0", addr);
+ else
+ inet_aton("255.255.255.0", addr);
+ log_message("netmask guess: %s", inet_ntoa(*addr));
+}
+
+
+static enum return_type setup_network_interface(struct interface_info * intf)
+{
+ enum return_type results;
+ char * bootprotos[] = { "Static", "DHCP", NULL };
+ char * choice;
+
+ results = ask_from_list("Please choose the desired IP attribution.", bootprotos, &choice);
+ if (results != RETURN_OK)
+ return results;
+
+ if (!strcmp(choice, "Static")) {
+ char * questions[] = { "IP of this machine", "IP of Domain Name Server", "IP of default gateway", NULL };
+ char ** answers;
+ struct in_addr addr;
+
+ results = ask_from_entries("Please enter the network information.", questions, &answers, 16);
+ if (results != RETURN_OK)
+ return setup_network_interface(intf);
+
+ if (!inet_aton(answers[0], &addr)) {
+ error_message("Invalid IP address");
+ return setup_network_interface(intf);
+ }
+ memcpy(&intf->ip, &addr, sizeof(addr));
+
+ if (!inet_aton(answers[1], &dns_server)) {
+ log_message("invalid DNS");
+ dns_server.s_addr = 0; /* keep an understandable state */
+ }
+
+ if (!inet_aton(answers[2], &gateway)) {
+ log_message("invalid gateway");
+ gateway.s_addr = 0; /* keep an understandable state */
+ }
+
+ if (IS_EXPERT) {
+ char * questions_expert[] = { "Netmask", NULL };
+ char ** answers_expert;
+ results = ask_from_entries("Please enter additional network information.", questions_expert, &answers_expert, 16);
+ if (results != RETURN_OK)
+ return results;
+
+ if (!inet_aton(answers_expert[0], &addr)) {
+ error_message("Invalid netmask");
+ return setup_network_interface(intf);
+ }
+ }
+ else
+ guess_netmask(intf, &addr);
+ memcpy(&intf->netmask, &addr, sizeof(addr));
+
+ *((uint32_t *) &intf->broadcast) = (*((uint32_t *) &intf->ip) &
+ *((uint32_t *) &intf->netmask)) | ~(*((uint32_t *) &intf->netmask));
+
+ inet_aton("255.255.255.255", &addr);
+ if (!memcmp(&addr, &intf->netmask, sizeof(addr))) {
+ log_message("netmask is 255.255.255.255 -> point to point device");
+ intf->network = gateway;
+ intf->is_ptp = 1;
+ } else {
+ *((uint32_t *) &intf->network) = *((uint32_t *) &intf->ip) & *((uint32_t *) &intf->netmask);
+ intf->is_ptp = 0;
+ }
+ intf->boot_proto = BOOTPROTO_STATIC;
+ } else {
+ error_message("DHCP not implemented yet");
+ intf->boot_proto = BOOTPROTO_DHCP;
+ }
+
+ if (configure_net_device(intf))
+ return RETURN_ERROR;
+ return add_default_route();
+}
+
+
+static enum return_type configure_network(struct interface_info * intf)
+{
+ char ips[50];
+ char * name;
+
+ write_resolvconf();
+
+ wait_message("Trying to guess hostname and domain...");
+ strcpy(ips, inet_ntoa(intf->ip));
+ name = mygethostbyaddr(ips);
+ remove_wait_message();
+
+ if (!name) {
+ enum return_type results;
+ char * questions[] = { "Host name", "Domain name", NULL };
+ char ** answers;
+ char * boulet;
+
+ log_message("reverse name lookup on self failed");
+
+ results = ask_from_entries("I could not guess hostname and domain name; please fill in this information. "
+ "Valid answers are for example: `mybox' for hostname and `mynetwork.com' for domain name, "
+ "for a machine called `mybox.mynetwork.com' on the Internet.",
+ questions, &answers, 32);
+ if (results != RETURN_OK)
+ return results;
+
+ hostname = answers[0];
+ if ((boulet = strchr(hostname, '.')) != NULL)
+ boulet[0] = '\0';
+ domain = answers[1];
+ }
+ else {
+ hostname = strdup(name);
+ domain = strchr(strdup(name), '.');
+ }
+
+ return RETURN_OK;
+}
+
+
+static enum return_type bringup_networking(struct interface_info * intf)
+{
+ static struct interface_info loopback;
+ enum { BRINGUP_NET, BRINGUP_CONF, BRINGUP_DONE } step = BRINGUP_NET;
+
+ my_insmod("af_packet");
+
+// if (intf->is_up == 1)
+// log_message("interface already up (with IP %s)", inet_ntoa(intf->ip));
+
+ while (step != BRINGUP_DONE) {
+ enum return_type results = RETURN_ERROR;
+ switch (step) {
+ case BRINGUP_NET:
+ results = setup_network_interface(intf);
+ if (results != RETURN_OK)
+ return results;
+ step = BRINGUP_CONF;
+ break;
+
+ case BRINGUP_CONF:
+ results = configure_network(intf);
+ if (results != RETURN_OK)
+ step = BRINGUP_NET;
+ else
+ step = BRINGUP_DONE;
+ break;
+
+ case BRINGUP_DONE:
+ break;
+ }
+ }
+
+ write_resolvconf(); /* maybe we have now domain to write also */
+
+ if (loopback.is_up == 0) {
+ int rc;
+ strcpy(loopback.device, "lo");
+ loopback.is_ptp = 0;
+ loopback.is_up = 0;
+ loopback.ip.s_addr = htonl(0x7f000001);
+ loopback.netmask.s_addr = htonl(0xff000000);
+ loopback.broadcast.s_addr = htonl(0x7fffffff);
+ loopback.network.s_addr = htonl(0x7f000000);
+ rc = configure_net_device(&loopback);
+ if (rc)
+ return RETURN_ERROR;
+ }
+
+ return RETURN_OK;
+}
+
+
static char * interface_select(void)
{
char ** interfaces, ** ptr;
@@ -64,23 +465,61 @@ static char * interface_select(void)
return choice;
}
-enum return_type nfs_prepare(void)
+
+
+/* -=-=-- */
+
+
+static enum return_type intf_select_and_up(void)
{
- char * iface = interface_select();
+ static struct interface_info intf[20];
+ static int num_interfaces = 0;
+ enum return_type results;
- if (iface == NULL)
- return RETURN_BACK;
+ do {
+ struct interface_info * sel_intf = NULL;
+ int i;
+ char * iface = interface_select();
+
+ if (iface == NULL)
+ return RETURN_BACK;
+
+ for (i = 0; i < num_interfaces ; i++)
+ if (!strcmp(intf[i].device, iface))
+ sel_intf = &(intf[i]);
+
+ if (sel_intf == NULL) {
+ sel_intf = &(intf[num_interfaces]);
+ strcpy(sel_intf->device, iface);
+ sel_intf->is_up = 0;
+ num_interfaces++;
+ }
+ results = bringup_networking(sel_intf);
+ }
+ while (results == RETURN_BACK);
return RETURN_ERROR;
}
+enum return_type nfs_prepare(void)
+{
+ enum return_type results = intf_select_and_up();
+
+ return results;
+}
+
+
enum return_type ftp_prepare(void)
{
- return RETURN_ERROR;
+ enum return_type results = intf_select_and_up();
+
+ return results;
}
enum return_type http_prepare(void)
{
- return RETURN_ERROR;
+ enum return_type results = intf_select_and_up();
+
+ return results;
}