18549Sjkh/*
250479Speter * $FreeBSD$
38549Sjkh *
48549Sjkh * Copyright (c) 1995
58549Sjkh *      Gary J Palmer. All rights reserved.
617985Sjkh * Copyright (c) 1996
717985Sjkh *      Jordan K. Hubbard. All rights reserved.
88549Sjkh *
98549Sjkh * Redistribution and use in source and binary forms, with or without
108549Sjkh * modification, are permitted provided that the following conditions
118549Sjkh * are met:
128549Sjkh * 1. Redistributions of source code must retain the above copyright
138881Srgrimes *    notice, this list of conditions and the following disclaimer,
148881Srgrimes *    verbatim and that no modifications are made prior to this
158549Sjkh *    point in the file.
168549Sjkh * 2. Redistributions in binary form must reproduce the above copyright
178549Sjkh *    notice, this list of conditions and the following disclaimer in the
188549Sjkh *    documentation and/or other materials provided with the distribution.
198549Sjkh *
2017985Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
218677Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
228677Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2317985Sjkh * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
248677Sjkh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
258677Sjkh * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
268677Sjkh * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
278677Sjkh * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
288677Sjkh * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
298677Sjkh * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
308549Sjkh *
318549Sjkh */
328549Sjkh
338756Sjkh/*
348756Sjkh * All kinds of hacking also performed by jkh on this code.  Don't
358756Sjkh * blame Gary for every bogosity you see here.. :-)
368756Sjkh *
378756Sjkh * -jkh
388756Sjkh */
398756Sjkh
4021243Sjkh#include "sysinstall.h"
418549Sjkh#include <sys/param.h>
4263118Sume#include <sys/sysctl.h>
43198317Srink#include <sys/types.h>
4463118Sume#include <sys/socket.h>
45198317Srink#include <sys/sockio.h>
46198317Srink
4763118Sume#include <netinet/in.h>
48198317Srink#include <net/if.h>
49198317Srink#include <net/if_media.h>
50198317Srink
5150780Sjkh#include <netdb.h>
52147902Sscottl#include <paths.h>
53198317Srink#include <ifaddrs.h>
548549Sjkh
5514321Sjkh/* The help file for the TCP/IP setup screen */
5614321Sjkh#define TCP_HELPFILE		"tcp"
5714321Sjkh
588574Sgpalmer/* These are nasty, but they make the layout structure a lot easier ... */
598574Sgpalmer
6012661Speterstatic char	hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
6163118Sume		gateway[IPADDR_FIELD_LEN], nameserver[INET6_ADDRSTRLEN];
6212661Speterstatic int	okbutton, cancelbutton;
6312661Speterstatic char	ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
6463118Sumestatic char	ipv6addr[INET6_ADDRSTRLEN];
658549Sjkh
668574Sgpalmer/* What the screen size is meant to be */
678549Sjkh#define TCP_DIALOG_Y		0
688549Sjkh#define TCP_DIALOG_X		8
698549Sjkh#define TCP_DIALOG_WIDTH	COLS - 16
708549Sjkh#define TCP_DIALOG_HEIGHT	LINES - 2
718549Sjkh
728549Sjkhstatic Layout layout[] = {
7320484Sjkh#define LAYOUT_HOSTNAME		0
7421243Sjkh    { 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
75213763Sbrucec      "Host:", "Your fully-qualified hostname, e.g. foo.example.com",
768549Sjkh      hostname, STRINGOBJ, NULL },
7720484Sjkh#define LAYOUT_DOMAINNAME	1
7821243Sjkh    { 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
7917985Sjkh      "Domain:",
80213763Sbrucec      "The name of the domain that your machine is in, e.g. example.com",
818549Sjkh      domainname, STRINGOBJ, NULL },
8220484Sjkh#define LAYOUT_GATEWAY		2
8321243Sjkh    { 5, 2, 18, IPADDR_FIELD_LEN - 1,
8463118Sume      "IPv4 Gateway:",
8563118Sume      "IPv4 address of host forwarding packets to non-local destinations",
868549Sjkh      gateway, STRINGOBJ, NULL },
8720484Sjkh#define LAYOUT_NAMESERVER	3
8863118Sume    { 5, 35, 18, INET6_ADDRSTRLEN - 1,
8963118Sume      "Name server:", "IPv4 or IPv6 address of your local DNS server",
908556Sjkh      nameserver, STRINGOBJ, NULL },
9120484Sjkh#define LAYOUT_IPADDR		4
9221243Sjkh    { 10, 10, 18, IPADDR_FIELD_LEN - 1,
9363118Sume      "IPv4 Address:",
9463118Sume      "The IPv4 address to be used for this interface",
958549Sjkh      ipaddr, STRINGOBJ, NULL },
9620484Sjkh#define LAYOUT_NETMASK		5
9721243Sjkh    { 10, 35, 18, IPADDR_FIELD_LEN - 1,
988549Sjkh      "Netmask:",
9912661Speter      "The netmask for this interface, e.g. 0xffffff00 for a class C network",
1008549Sjkh      netmask, STRINGOBJ, NULL },
10120484Sjkh#define LAYOUT_EXTRAS		6
10221243Sjkh    { 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
103125027Seivind      "Extra options to ifconfig (usually empty):",
10417985Sjkh      "Any interface-specific options to ifconfig you would like to add",
1058549Sjkh      extras, STRINGOBJ, NULL },
10620484Sjkh#define LAYOUT_OKBUTTON		7
10721243Sjkh    { 19, 15, 0, 0,
1088549Sjkh      "OK", "Select this if you are happy with these settings",
1098549Sjkh      &okbutton, BUTTONOBJ, NULL },
11020484Sjkh#define LAYOUT_CANCELBUTTON	8
11121243Sjkh    { 19, 35, 0, 0,
1128549Sjkh      "CANCEL", "Select this if you wish to cancel this screen",
1138549Sjkh      &cancelbutton, BUTTONOBJ, NULL },
114156123Sjhb    LAYOUT_END,
1158549Sjkh};
1168549Sjkh
11710882Speter#define _validByte(b) ((b) >= 0 && (b) <= 255)
1188549Sjkh
1198756Sjkh/* whine */
1208549Sjkhstatic void
1218549Sjkhfeepout(char *msg)
1228549Sjkh{
1238549Sjkh    beep();
12479304Skris    msgConfirm("%s", msg);
1258549Sjkh}
1268549Sjkh
12770023Seivind/* Verify IP address integrity */
1288549Sjkhstatic int
12979605SjesperverifyIP(char *ip, unsigned long *mask, unsigned long *out)
1308549Sjkh{
13170023Seivind    long a, b, c, d;
132169652Sdelphij    char *endptr, *endptr_prev;
1338549Sjkh
13479605Sjesper    unsigned long parsedip;
13579605Sjesper    unsigned long max_addr = (255 << 24) | (255 << 16) | (255 << 8) | 255;
13679605Sjesper
13770023Seivind    if (ip == NULL)
1388549Sjkh	return 0;
13970023Seivind    a = strtol(ip, &endptr, 10);
140169652Sdelphij    if (endptr - ip == 0 || *endptr++ != '.')
14170023Seivind	return 0;
142169652Sdelphij    endptr_prev = endptr;
14370023Seivind    b = strtol(endptr, &endptr, 10);
144169652Sdelphij    if (endptr - endptr_prev == 0 || *endptr++ != '.')
14570023Seivind	return 0;
146169652Sdelphij    endptr_prev = endptr;
14770023Seivind    c = strtol(endptr, &endptr, 10);
148169652Sdelphij    if (endptr - endptr_prev == 0 || *endptr++ != '.')
14970023Seivind	return 0;
150169652Sdelphij    endptr_prev = endptr;
15170023Seivind    d = strtol(endptr, &endptr, 10);
152169652Sdelphij    if (*endptr != '\0' || endptr - endptr_prev == 0)
15370023Seivind	return 0;
15479605Sjesper    if (!_validByte(a) || !_validByte(b) || !_validByte(c) || !_validByte(d))
15570023Seivind	return 0;
15679605Sjesper    parsedip = (a << 24) | (b << 16) | (c << 8) | d;
15770023Seivind    if (out)
15879605Sjesper	*out = parsedip;
15979605Sjesper    /*
16079605Sjesper     * The ip address must not be network or broadcast address.
16179605Sjesper     */
16279605Sjesper    if (mask && ((parsedip == (parsedip & *mask)) ||
16379605Sjesper	(parsedip == ((parsedip & *mask) + max_addr - *mask))))
16479605Sjesper	return 0;
16570023Seivind    return 1;
1668549Sjkh}
1678549Sjkh
16863118Sumestatic int
16963118SumeverifyIP6(char *ip)
17063118Sume{
17163118Sume    struct addrinfo hints, *res;
17263118Sume
17363118Sume    memset(&hints, 0, sizeof(hints));
17463118Sume    hints.ai_family = AF_INET6;
17563118Sume    hints.ai_socktype = SOCK_STREAM;
17663118Sume    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
17763118Sume    if (getaddrinfo(ip, NULL, &hints, &res) == 0) {
17863118Sume	freeaddrinfo(res);
17963118Sume	return 1;
18063118Sume    }
18163118Sume    return 0;
18263118Sume}
18363118Sume
18470023Seivind/* Verify IPv4 netmask as being well-formed as
18570023Seivind   a 0x or AAA.BBB.CCC.DDD mask */
18670023Seivindstatic int
18770023SeivindverifyNetmask(const char *netmask, unsigned long *out)
18870023Seivind{
18970023Seivind    unsigned long mask;
190156123Sjhb    long tmp;
19170023Seivind    char *endptr;
19270023Seivind
19370023Seivind    if (netmask[0] == '0' && (netmask[1] == 'x' || netmask[1] == 'X')) {
19470023Seivind        /* Parse out hex mask */
19570023Seivind        mask = strtoul(netmask, &endptr, 0);
19670023Seivind        if (*endptr != '\0')
19770023Seivind            return 0;
19870023Seivind    } else {
19970023Seivind        /* Parse out quad decimal mask */
200156123Sjhb        tmp = strtoul(netmask, &endptr, 10);
201156123Sjhb        if (!_validByte(tmp) || *endptr++ != '.')
20270023Seivind            return 0;
203156123Sjhb	mask = tmp;
20470023Seivind        tmp = strtoul(endptr, &endptr, 10);
20570023Seivind        if (!_validByte(tmp) || *endptr++ != '.')
20670023Seivind            return 0;
20770023Seivind	mask = (mask << 8) + tmp;
20870023Seivind        tmp = strtoul(endptr, &endptr, 10);
20970023Seivind        if (!_validByte(tmp) || *endptr++ != '.')
21070023Seivind            return 0;
21170023Seivind	mask = (mask << 8) + tmp;
21270023Seivind        tmp = strtoul(endptr, &endptr, 10);
21370023Seivind        if (!_validByte(tmp) || *endptr++ != '\0')
21470023Seivind            return 0;
21570023Seivind	mask = (mask << 8) + tmp;
21670023Seivind    }
21770023Seivind    /* Verify that we have a continous netmask */
21870023Seivind    if ((((-mask & mask) - 1) | mask) != 0xffffffff)
21970023Seivind        return 0;
22070023Seivind    if (out)
22170023Seivind        *out = mask;
22270023Seivind    return 1;
22370023Seivind}
22470023Seivind
22570023Seivindstatic int
22670023SeivindverifyGW(char *gw, unsigned long *ip, unsigned long *mask)
22770023Seivind{
22870023Seivind    unsigned long parsedgw;
22970023Seivind
23079605Sjesper    if (!verifyIP(gw, mask, &parsedgw))
23170023Seivind	return 0;
23270023Seivind    /* Gateway needs to be within the set of IPs reachable through the
23370023Seivind       interface */
23470023Seivind    if (ip && mask && ((parsedgw & *mask) != (*ip & *mask)))
23570023Seivind	return 0;
23670023Seivind    return 1;
23770023Seivind}
23870023Seivind
23921243Sjkh/* Check for the settings on the screen - the per-interface stuff is
2408574Sgpalmer   moved to the main handling code now to do it on the fly - sigh */
2418549Sjkhstatic int
2428549SjkhverifySettings(void)
2438549Sjkh{
24470023Seivind    unsigned long parsedip;
24570023Seivind    unsigned long parsednetmask;
24670023Seivind
2478549Sjkh    if (!hostname[0])
2488549Sjkh	feepout("Must specify a host name of some sort!");
24979605Sjesper    else if (netmask[0] && !verifyNetmask(netmask, &parsednetmask))
25079605Sjesper	feepout("Invalid netmask value");
25179605Sjesper    else if (nameserver[0] && !verifyIP(nameserver, NULL, NULL) &&
25270023Seivind		    !verifyIP6(nameserver))
2538549Sjkh	feepout("Invalid name server IP address specified");
25479605Sjesper    else if (ipaddr[0] && !verifyIP(ipaddr, &parsednetmask, &parsedip))
25570023Seivind	feepout("Invalid IPv4 address");
25670023Seivind    else if (gateway[0] && strcmp(gateway, "NO") &&
25770023Seivind	     !verifyGW(gateway, ipaddr[0] ? &parsedip : NULL,
25870023Seivind		     netmask[0] ? &parsednetmask : NULL))
25970023Seivind	feepout("Invalid gateway IPv4 address specified");
2608549Sjkh    else
2618549Sjkh	return 1;
2628549Sjkh    return 0;
2638549Sjkh}
2648549Sjkh
26550780Sjkhstatic void
26650780SjkhdhcpGetInfo(Device *devp)
26750780Sjkh{
268147902Sscottl    char leasefile[PATH_MAX];
269147902Sscottl
270147902Sscottl    snprintf(leasefile, sizeof(leasefile), "%sdhclient.leases.%s",
271147902Sscottl	_PATH_VARDB, devp->name);
27250780Sjkh    /* If it fails, do it the old-fashioned way */
273147902Sscottl    if (dhcpParseLeases(leasefile, hostname, domainname,
27450780Sjkh			 nameserver, ipaddr, gateway, netmask) == -1) {
27550780Sjkh	FILE *ifp;
27650780Sjkh	char *cp, cmd[256], data[2048];
27750780Sjkh	int i, j;
27850780Sjkh
27950780Sjkh	/* Bah, now we have to kludge getting the information from ifconfig */
28050780Sjkh	snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
28150780Sjkh	ifp = popen(cmd, "r");
28250780Sjkh	if (ifp) {
28350780Sjkh	    j = fread(data, 1, sizeof(data), ifp);
28450780Sjkh	    fclose(ifp);
28550780Sjkh	    if (j < 0)	/* paranoia */
28650780Sjkh		j = 0;
28750780Sjkh	    data[j] = '\0';
28850780Sjkh	    if (isDebug())
28950780Sjkh		msgDebug("DHCP configured interface returns %s\n", data);
29050780Sjkh	    /* XXX This is gross as it assumes a certain ordering to
29150780Sjkh	       ifconfig's output! XXX */
29263118Sume	    if ((cp = strstr(data, "inet ")) != NULL) {
29350780Sjkh		i = 0;
29450780Sjkh		cp += 5;	/* move over keyword */
29550780Sjkh		while (*cp != ' ')
29650780Sjkh		    ipaddr[i++] = *(cp++);
29750780Sjkh		ipaddr[i] = '\0';
29850780Sjkh		if (!strncmp(++cp, "netmask", 7)) {
29950780Sjkh		    i = 0;
30050780Sjkh		    cp += 8;
30150780Sjkh		    while (*cp != ' ')
30250780Sjkh			netmask[i++] = *(cp++);
30350780Sjkh		    netmask[i] = '\0';
30450780Sjkh		}
30550780Sjkh	    }
30650780Sjkh	}
30750780Sjkh    }
30850780Sjkh
30950780Sjkh    /* If we didn't get a name server value, hunt for it in resolv.conf */
31050780Sjkh    if (!nameserver[0] && file_readable("/etc/resolv.conf"))
31150780Sjkh	configEnvironmentResolv("/etc/resolv.conf");
31250780Sjkh    if (hostname[0])
31350780Sjkh	variable_set2(VAR_HOSTNAME, hostname, 0);
31450780Sjkh}
31550780Sjkh
31663118Sumestatic void
31763118SumertsolGetInfo(Device *devp)
31863118Sume{
31963118Sume    FILE *ifp;
32063118Sume    char *cp, cmd[256], data[2048];
32163118Sume    int i;
32263118Sume
32363118Sume    snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
32463118Sume    if ((ifp = popen(cmd, "r")) == NULL)
32563118Sume	return;
32663118Sume    while (fgets(data, sizeof(data), ifp) != NULL) {
32763118Sume	if (isDebug())
328106279Skuriyama	    msgDebug("RTSOL configured interface returns %s\n", data);
32963118Sume	if ((cp = strstr(data, "inet6 ")) != NULL) {
33063118Sume	    cp += 6;	/* move over keyword */
33163118Sume	    if (strncmp(cp, "fe80:", 5)) {
33263118Sume		i = 0;
33363118Sume		while (*cp != ' ')
33463118Sume		    ipv6addr[i++] = *(cp++);
33563118Sume		ipv6addr[i] = '\0';
33663118Sume	    }
33763118Sume	}
33863118Sume    }
33963118Sume    fclose(ifp);
34063118Sume}
34163118Sume
3428549Sjkh/* This is it - how to get TCP setup values */
3438556Sjkhint
3448756SjkhtcpOpenDialog(Device *devp)
3458549Sjkh{
34626514Sjkh    WINDOW              *ds_win, *save = NULL;
3478549Sjkh    ComposeObj          *obj = NULL;
34846617Sjkh    int                 n = 0, filled = 0, cancel = FALSE;
34926576Sjkh    int			max, ret = DITEM_SUCCESS;
35050780Sjkh    int			use_dhcp = FALSE;
35163118Sume    int			use_rtsol = FALSE;
3528549Sjkh    char                *tmp;
3538803Sjkh    char		title[80];
3548549Sjkh
35554722Sjkh    save = savescr();
3568756Sjkh    /* Initialise vars from previous device values */
3578756Sjkh    if (devp->private) {
3588756Sjkh	DevInfo *di = (DevInfo *)devp->private;
3598881Srgrimes
36020247Sjkh	SAFE_STRCPY(ipaddr, di->ipaddr);
36120247Sjkh	SAFE_STRCPY(netmask, di->netmask);
36220247Sjkh	SAFE_STRCPY(extras, di->extras);
36350780Sjkh	use_dhcp = di->use_dhcp;
36463118Sume	use_rtsol = di->use_rtsol;
3658756Sjkh    }
36612661Speter    else { /* See if there are any defaults */
36712661Speter	char *cp;
36897668Sjhb	char *old_interactive = NULL;
3698756Sjkh
37083486Sjkh	/*
37197668Sjhb	 * This is a hack so that the dialogs below are interactive in a
37297668Sjhb	 * script if we have requested interactive behavior.
37397668Sjhb	 */
37497668Sjhb	if (variable_get(VAR_NONINTERACTIVE) &&
37597668Sjhb	  variable_get(VAR_NETINTERACTIVE)) {
37697668Sjhb	    old_interactive = strdup(VAR_NONINTERACTIVE);
37797668Sjhb	    variable_unset(VAR_NONINTERACTIVE);
37897668Sjhb	}
37997668Sjhb
38097668Sjhb
38197668Sjhb	/*
38283486Sjkh	 * Try a RTSOL scan if such behavior is desired.
38383486Sjkh	 * If the variable was configured and is YES, do it.
38483486Sjkh	 * If it was configured to anything else, treat it as NO.
38583486Sjkh	 * Otherwise, ask the question interactively.
38683486Sjkh	 */
387111644Ssobomax	if (!variable_get(VAR_NO_INET6) &&
388111644Ssobomax	    (!variable_cmp(VAR_TRY_RTSOL, "YES") ||
389111644Ssobomax	    (variable_get(VAR_TRY_RTSOL)==0 && !msgNoYes("Do you want to try IPv6 configuration of the interface?")))) {
39063118Sume	    int i;
39183843Smurray	    size_t len;
39263118Sume
39363118Sume	    i = 0;
39463118Sume	    sysctlbyname("net.inet6.ip6.forwarding", NULL, 0, &i, sizeof(i));
39563118Sume	    i = 1;
39663118Sume	    sysctlbyname("net.inet6.ip6.accept_rtadv", NULL, 0, &i, sizeof(i));
39763118Sume	    vsystem("ifconfig %s up", devp->name);
39869576Sume	    len = sizeof(i);
39969576Sume	    sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
40069576Sume	    sleep(i + 1);
40163118Sume	    Mkdir("/var/run");
40263118Sume	    msgNotify("Scanning for RA servers...");
40363118Sume	    if (0 == vsystem("rtsol %s", devp->name)) {
40469576Sume		len = sizeof(i);
40569576Sume		sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
40669576Sume		sleep(i + 1);
40763118Sume		rtsolGetInfo(devp);
40863118Sume		use_rtsol = TRUE;
40963118Sume	    } else
41063118Sume		use_rtsol = FALSE;
41163118Sume	}
41263118Sume
41383486Sjkh
41483486Sjkh	/*
41583486Sjkh	 * First try a DHCP scan if such behavior is desired.
41683486Sjkh	 * If the variable was configured and is YES, do it.
41783486Sjkh	 * If it was configured to anything else, treat it as NO.
41883486Sjkh	 * Otherwise, ask the question interactively.
41983486Sjkh	 */
42065167Smurray	if (!variable_cmp(VAR_TRY_DHCP, "YES") ||
42183486Sjkh	    (variable_get(VAR_TRY_DHCP)==0 && !msgNoYes("Do you want to try DHCP configuration of the interface?"))) {
42250780Sjkh	    Mkdir("/var/db");
42350780Sjkh	    Mkdir("/var/run");
42450780Sjkh	    Mkdir("/tmp");
42550780Sjkh	    msgNotify("Scanning for DHCP servers...");
426147902Sscottl	    /* XXX clear any existing lease */
427147902Sscottl	    /* XXX limit protocol to N tries */
428147902Sscottl	    if (0 == vsystem("dhclient %s", devp->name)) {
42951019Sjkh		dhcpGetInfo(devp);
43051019Sjkh		use_dhcp = TRUE;
43150780Sjkh	    }
43251019Sjkh	    else
43351019Sjkh		use_dhcp = FALSE;
43450780Sjkh	}
43550780Sjkh
43697668Sjhb	/* Restore old VAR_NONINTERACTIVE if needed. */
43797668Sjhb	if (old_interactive != NULL) {
43897668Sjhb	    variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
43997668Sjhb	    free(old_interactive);
44097668Sjhb	}
44197668Sjhb
44251019Sjkh	/* Special hack so it doesn't show up oddly in the tcpip setup menu */
44351019Sjkh	if (!strcmp(gateway, "NO"))
44451019Sjkh	    gateway[0] = '\0';
44551019Sjkh
44650780Sjkh	/* Get old IP address from variable space, if available */
44712661Speter	if (!ipaddr[0]) {
44812661Speter	    if ((cp = variable_get(VAR_IPADDR)) != NULL)
44920247Sjkh		SAFE_STRCPY(ipaddr, cp);
45012661Speter	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL)
45120247Sjkh		SAFE_STRCPY(ipaddr, cp);
45212661Speter	}
45350780Sjkh
45450780Sjkh	/* Get old netmask from variable space, if available */
45512661Speter	if (!netmask[0]) {
45612661Speter	    if ((cp = variable_get(VAR_NETMASK)) != NULL)
45720247Sjkh		SAFE_STRCPY(netmask, cp);
45812661Speter	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL)
45920247Sjkh		SAFE_STRCPY(netmask, cp);
46012661Speter	}
46150780Sjkh
46250780Sjkh	/* Get old extras string from variable space, if available */
46312661Speter	if (!extras[0]) {
46412661Speter	    if ((cp = variable_get(VAR_EXTRAS)) != NULL)
46520247Sjkh		SAFE_STRCPY(extras, cp);
46612661Speter	    else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL)
46720247Sjkh		SAFE_STRCPY(extras, cp);
46812661Speter	}
46912661Speter    }
47021243Sjkh
4718756Sjkh    /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
47250780Sjkh    if (!hostname[0]) {
47350780Sjkh	tmp = variable_get(VAR_HOSTNAME);
47450780Sjkh	if (tmp)
47550780Sjkh	    SAFE_STRCPY(hostname, tmp);
47650780Sjkh    }
47750780Sjkh    if (!domainname[0]) {
47850780Sjkh	tmp = variable_get(VAR_DOMAINNAME);
47950780Sjkh	if (tmp)
48050780Sjkh	    SAFE_STRCPY(domainname, tmp);
48150780Sjkh    }
48250780Sjkh    if (!gateway[0]) {
48350780Sjkh	tmp = variable_get(VAR_GATEWAY);
48451019Sjkh	if (tmp && strcmp(tmp, "NO"))
48550780Sjkh	    SAFE_STRCPY(gateway, tmp);
48650780Sjkh    }
48750780Sjkh    if (!nameserver[0]) {
48850780Sjkh	tmp = variable_get(VAR_NAMESERVER);
48950780Sjkh	if (tmp)
49050780Sjkh	    SAFE_STRCPY(nameserver, tmp);
49150780Sjkh    }
4928549Sjkh
49326514Sjkh    /* If non-interactive, jump straight over the dialog crap and into config section */
49429539Spst    if (variable_get(VAR_NONINTERACTIVE) &&
49529539Spst	!variable_get(VAR_NETINTERACTIVE)) {
49629537Sjkh	if (!hostname[0])
49729537Sjkh	    msgConfirm("WARNING: hostname variable not set and is a non-optional\n"
49829537Sjkh		       "parameter.  Please add this to your installation script\n"
49929537Sjkh		       "or set the netInteractive variable (see sysinstall man page)");
50029537Sjkh	else
50129537Sjkh	    goto netconfig;
50229537Sjkh    }
50326514Sjkh
50426514Sjkh    /* Now do all the screen I/O */
50526514Sjkh    dialog_clear_norefresh();
50626514Sjkh
50787609Smurray    /* Modify the help line for PLIP config */
508155613Sceri    if (!strncmp(devp->name, "plip", 4))
50987609Smurray	layout[LAYOUT_EXTRAS].help =
51087609Smurray         "For PLIP configuration, you must enter the peer's IP address here.";
51187609Smurray
51226514Sjkh    /* We need a curses window */
51363118Sume    tmp = " Network Configuration ";
51463118Sume    if (ipv6addr[0])
51563118Sume	tmp = string_concat(tmp, "(IPv6 ready) ");
51663118Sume    if (!(ds_win = openLayoutDialog(TCP_HELPFILE, tmp,
51726514Sjkh				    TCP_DIALOG_X, TCP_DIALOG_Y, TCP_DIALOG_WIDTH, TCP_DIALOG_HEIGHT))) {
51826514Sjkh	beep();
51926514Sjkh	msgConfirm("Cannot open TCP/IP dialog window!!");
52026514Sjkh	restorescr(save);
52126514Sjkh	return DITEM_FAILURE;
52226514Sjkh    }
52326514Sjkh
52426514Sjkh    /* Draw interface configuration box */
52526514Sjkh    draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
52626514Sjkh	     dialog_attr, border_attr);
52726514Sjkh    wattrset(ds_win, dialog_attr);
52826514Sjkh    sprintf(title, " Configuration for Interface %s ", devp->name);
52926514Sjkh    mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
53026514Sjkh
53121243Sjkh    /* Some more initialisation before we go into the main input loop */
53221243Sjkh    obj = initLayoutDialog(ds_win, layout, TCP_DIALOG_X, TCP_DIALOG_Y, &max);
5338549Sjkh
53421243Sjkhreenter:
5358556Sjkh    cancelbutton = okbutton = 0;
53621243Sjkh    while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel)) {
53746617Sjkh	/* Prevent this from being irritating if user really means NO */
53846639Sjkh	if (filled < 3) {
53946617Sjkh	    /* Insert a default value for the netmask, 0xffffff00 is
54046639Sjkh	     * the most appropriate one (entire class C, or subnetted
54146639Sjkh	     * class A/B network).
54246639Sjkh	     */
54350780Sjkh	    if (!netmask[0]) {
54446617Sjkh		strcpy(netmask, "255.255.255.0");
54546617Sjkh		RefreshStringObj(layout[LAYOUT_NETMASK].obj);
54646639Sjkh		++filled;
54746617Sjkh	    }
54846617Sjkh	    if (!index(hostname, '.') && domainname[0]) {
54946617Sjkh		strcat(hostname, ".");
55046617Sjkh		strcat(hostname, domainname);
55146617Sjkh		RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
55246639Sjkh		++filled;
55346617Sjkh	    }
55446617Sjkh	    else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
55546639Sjkh		SAFE_STRCPY(domainname, tmp + 1);
55646639Sjkh		RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
55746639Sjkh		++filled;
55846617Sjkh	    }
55921243Sjkh	}
5608549Sjkh    }
56122721Sjkh    if (!cancel && !verifySettings())
56221243Sjkh	goto reenter;
5638556Sjkh
5648574Sgpalmer    /* Clear this crap off the screen */
56523588Sjkh    delwin(ds_win);
56617404Sjkh    dialog_clear_norefresh();
5678576Sjkh    use_helpfile(NULL);
5688881Srgrimes
5698574Sgpalmer    /* We actually need to inform the rest of sysinstall about this
57021243Sjkh       data now if the user hasn't selected cancel.  Save the stuff
57121243Sjkh       out to the environment via the variable_set() mechanism */
5728556Sjkh
57326514Sjkhnetconfig:
5748549Sjkh    if (!cancel) {
5758756Sjkh	DevInfo *di;
57612661Speter	char temp[512], ifn[255];
57763118Sume	int ipv4_enable = FALSE;
5788756Sjkh
57950780Sjkh	if (hostname[0]) {
58056713Sjkh	    variable_set2(VAR_HOSTNAME, hostname, 1);
58150780Sjkh	    sethostname(hostname, strlen(hostname));
58250780Sjkh	}
5839202Srgrimes	if (domainname[0])
58443685Sjkh	    variable_set2(VAR_DOMAINNAME, domainname, 0);
5858556Sjkh	if (gateway[0])
58657365Sjkh	    variable_set2(VAR_GATEWAY, gateway, use_dhcp ? 0 : 1);
5878556Sjkh	if (nameserver[0])
58843685Sjkh	    variable_set2(VAR_NAMESERVER, nameserver, 0);
58950780Sjkh	if (ipaddr[0])
59050780Sjkh	    variable_set2(VAR_IPADDR, ipaddr, 0);
59163118Sume	if (ipv6addr[0])
59263118Sume	    variable_set2(VAR_IPV6ADDR, ipv6addr, 0);
5938574Sgpalmer
5948756Sjkh	if (!devp->private)
59516330Sjkh	    devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo));
5968756Sjkh	di = devp->private;
59720247Sjkh	SAFE_STRCPY(di->ipaddr, ipaddr);
59820247Sjkh	SAFE_STRCPY(di->netmask, netmask);
59920247Sjkh	SAFE_STRCPY(di->extras, extras);
60050780Sjkh	di->use_dhcp = use_dhcp;
60163118Sume	di->use_rtsol = use_rtsol;
6028756Sjkh
60363118Sume	if (use_dhcp || ipaddr[0])
60463118Sume	    ipv4_enable = TRUE;
60563118Sume	if (ipv4_enable) {
60663118Sume	    sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
607155380Sdelphij	    if (use_dhcp) {
608155380Sdelphij		if (strlen(extras) > 0)
609155380Sdelphij		    sprintf(temp, "DHCP %s", extras);
610155380Sdelphij		else
611155380Sdelphij		    sprintf(temp, "DHCP");
612155380Sdelphij	    } else
61363118Sume		sprintf(temp, "inet %s %s netmask %s",
61463118Sume			ipaddr, extras, netmask);
61563118Sume	    variable_set2(ifn, temp, 1);
61663118Sume	}
61763118Sume	if (use_rtsol)
61863118Sume	    variable_set2(VAR_IPV6_ENABLE, "YES", 1);
61950780Sjkh	if (!use_dhcp)
62050780Sjkh	    configResolv(NULL);	/* XXX this will do it on the MFS copy XXX */
62121243Sjkh	ret = DITEM_SUCCESS;
6228556Sjkh    }
62321243Sjkh    else
62421243Sjkh	ret = DITEM_FAILURE;
62515355Sjkh    restorescr(save);
62621243Sjkh    return ret;
6278549Sjkh}
6288677Sjkh
62922842Sjkhstatic Device *NetDev;
63022842Sjkh
6318677Sjkhstatic int
63215242SjkhnetHook(dialogMenuItem *self)
6338677Sjkh{
6348677Sjkh    Device **devs;
6358677Sjkh
63621730Sjkh    devs = deviceFindDescr(self->prompt, self->title, DEVICE_TYPE_NETWORK);
6378756Sjkh    if (devs) {
63817007Sjkh	if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE)
63922842Sjkh	    NetDev = devs[0];
64017007Sjkh	else
64122842Sjkh	    NetDev = NULL;
6428756Sjkh    }
64315242Sjkh    return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
6448677Sjkh}
6458677Sjkh
646198317Srinkstatic char *
647198317SrinktcpDeviceScan(void)
648198317Srink{
649198317Srink	int s;
650198317Srink	struct ifmediareq ifmr;
651198317Srink	struct ifaddrs *ifap, *ifa;
652198317Srink	char *network_dev;
653198317Srink
654198317Srink	if ((s = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0)
655198317Srink		return (NULL);
656198317Srink
657198317Srink	if (getifaddrs(&ifap) < 0)
658198317Srink		return (NULL);
659198317Srink
660198317Srink	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
661198317Srink		memset(&ifmr, 0, sizeof(ifmr));
662198317Srink		strlcpy(ifmr.ifm_name, ifa->ifa_name, sizeof(ifmr.ifm_name));
663198317Srink
664198317Srink		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
665198317Srink			continue;	/* some devices don't support this */
666198317Srink
667198317Srink		if ((ifmr.ifm_status & IFM_AVALID) == 0)
668198317Srink			continue;	/* not active */
669198317Srink
670198317Srink		if (IFM_TYPE(ifmr.ifm_active) != IFM_ETHER)
671198317Srink			continue;	/* not an ethernet device */
672198317Srink
673198317Srink		if (ifmr.ifm_status & IFM_ACTIVE) {
674198317Srink			network_dev = strdup(ifa->ifa_name);
675198317Srink			freeifaddrs(ifap);
676198317Srink
677198317Srink			if (!variable_get(VAR_NONINTERACTIVE))
678198317Srink				msgConfirm("Using interface %s", network_dev);
679198317Srink
680198317Srink			msgDebug("tcpDeviceScan found %s", network_dev);
681198317Srink			return (network_dev);
682198317Srink		}
683198317Srink	}
684198317Srink
685206995Srandi	close(s);
686206995Srandi
687198317Srink	freeifaddrs(ifap);
688198317Srink
689198317Srink	return (NULL);
690198317Srink}
691198317Srink
6928677Sjkh/* Get a network device */
69322842SjkhDevice *
6949202SrgrimestcpDeviceSelect(void)
6958677Sjkh{
6968677Sjkh    DMenu *menu;
69722842Sjkh    Device **devs, *rval;
698192130Scperciva    char *dev, *network_dev;
6999202Srgrimes    int cnt;
7008677Sjkh
701192130Scperciva    rval = NULL;
702192130Scperciva
703198317Srink    if (variable_get(VAR_NETWORK_DEVICE)) {
704192130Scperciva	network_dev = variable_get(VAR_NETWORK_DEVICE);
705192130Scperciva
706198317Srink	/*
707198317Srink	 * netDev can be set to several types of values.
708198317Srink	 * If netDev is set to ANY, scan all network devices
709198317Srink	 * looking for a valid link, and go with the first
710198317Srink	 * device found. netDev can also be specified as a
711198317Srink	 * comma delimited list, with each network device
712198317Srink	 * tried in order. netDev can also be set to a single
713198317Srink	 * network device.
714198317Srink	 */
715198317Srink	if (!strcmp(network_dev, "ANY"))
716198317Srink		network_dev = strdup(tcpDeviceScan());
717198317Srink
718192130Scperciva	while ((dev = strsep(&network_dev, ",")) != NULL) {
719192130Scperciva	    devs = deviceFind(dev, DEVICE_TYPE_NETWORK);
720192130Scperciva	    cnt = deviceCount(devs);
721198317Srink
722192130Scperciva	    if (cnt) {
723198317Srink		if (DITEM_STATUS(tcpOpenDialog(devs[0])) == DITEM_SUCCESS)
724198317Srink		    return (devs[0]);
725192130Scperciva	    }
726192130Scperciva	}
727192130Scperciva
728198317Srink	if (!variable_get(VAR_NONINTERACTIVE))
729198317Srink		msgConfirm("No network devices available!");
73026514Sjkh
731198317Srink	return (NULL);
73229222Sjkh    }
733198317Srink
734209004Srandi    devs = deviceFind(NULL, DEVICE_TYPE_NETWORK);
735209004Srandi    cnt = deviceCount(devs);
736209004Srandi
737198317Srink    if ((!RunningAsInit) && (variable_check("NETWORK_CONFIGURED=NO") != TRUE)) {
73829222Sjkh	if (!msgYesNo("Running multi-user, assume that the network is already configured?"))
73929222Sjkh	    return devs[0];
74029222Sjkh    }
74129222Sjkh    if (cnt == 1) {
74226514Sjkh	if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
74326514Sjkh	    rval = devs[0];
7449202Srgrimes    }
7459202Srgrimes    else {
74622856Sjkh	int status;
74722856Sjkh
74815242Sjkh	menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL);
7499202Srgrimes	if (!menu)
7509202Srgrimes	    msgFatal("Unable to create network device menu!  Argh!");
75116887Sjkh	status = dmenuOpenSimple(menu, FALSE);
7529202Srgrimes	free(menu);
75326514Sjkh	if (status)
75422856Sjkh	    rval = NetDev;
7559202Srgrimes    }
75622842Sjkh    return rval;
7579202Srgrimes}
7589202Srgrimes
7599202Srgrimes/* Do it from a menu that doesn't care about status */
7609202Srgrimesint
76115091SjkhtcpMenuSelect(dialogMenuItem *self)
7629202Srgrimes{
76322842Sjkh    Device *tmp;
76463832Sjkh    WINDOW *save;
76522842Sjkh
76659057Smurray    variable_set("NETWORK_CONFIGURED=NO",0);
76722842Sjkh    tmp = tcpDeviceSelect();
76859057Smurray    variable_unset("NETWORK_CONFIGURED");
76963832Sjkh    save = savescr();
77053019Sjkh    if (tmp && tmp->private && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
77179065Sdd	if (!DEVICE_INIT(tmp))
77222842Sjkh	    msgConfirm("Initialization of %s device failed.", tmp->name);
77363832Sjkh    restorescr(save);
77454587Sjkh    return DITEM_SUCCESS;
7758677Sjkh}
776