diff options
Diffstat (limited to 'mdk-stage1/network.c')
-rw-r--r-- | mdk-stage1/network.c | 451 |
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; } |