1 /* $NetBSD: net.c,v 1.43 2022/05/18 16:39:03 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* net.c -- routines to fetch files off the network. */ 36 37 #include <sys/ioctl.h> 38 #include <sys/param.h> 39 #include <sys/resource.h> 40 #include <sys/socket.h> 41 #include <sys/stat.h> 42 #include <sys/statvfs.h> 43 #include <sys/statvfs.h> 44 #include <sys/sysctl.h> 45 #include <sys/wait.h> 46 #include <arpa/inet.h> 47 #include <net/if.h> 48 #include <net/if_media.h> 49 #include <netinet/in.h> 50 #include <net80211/ieee80211_ioctl.h> 51 #include <netinet/ip_var.h> 52 #ifdef INET6 53 #include <netinet6/ip6_var.h> 54 #endif 55 56 #include <err.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <curses.h> 61 #include <time.h> 62 #include <unistd.h> 63 64 #include "defs.h" 65 #include "md.h" 66 #include "msg_defs.h" 67 #include "menu_defs.h" 68 #include "txtwalk.h" 69 70 int network_up = 0; 71 /* Access to network information */ 72 #define MAX_NETS 15 73 struct net_desc { 74 char if_dev[STRSIZE]; 75 char name[STRSIZE]; // TODO 76 }; 77 78 static char net_dev[STRSIZE]; 79 static char net_domain[STRSIZE]; 80 static char net_host[STRSIZE]; 81 static char net_ip[SSTRSIZE]; 82 static char net_srv_ip[SSTRSIZE]; 83 static char net_mask[SSTRSIZE]; 84 char net_namesvr[STRSIZE]; 85 static char net_defroute[STRSIZE]; 86 static char net_media[STRSIZE]; 87 static char net_ssid[STRSIZE]; 88 static char net_passphrase[STRSIZE]; 89 static char sl_flags[STRSIZE]; 90 static int net_dhcpconf; 91 #define DHCPCONF_IPADDR 0x01 92 #define DHCPCONF_NAMESVR 0x02 93 #define DHCPCONF_HOST 0x04 94 #define DHCPCONF_DOMAIN 0x08 95 #ifdef INET6 96 static char net_ip6[STRSIZE]; 97 #define IP6CONF_AUTOHOST 0x01 98 #endif 99 100 101 /* URL encode unsafe characters. */ 102 103 static char *url_encode (char *dst, const char *src, const char *ep, 104 const char *safe_chars, 105 int encode_leading_slash); 106 107 static void write_etc_hosts(FILE *f); 108 109 #define DHCPCD "/sbin/dhcpcd" 110 #define WPA_SUPPLICANT "/usr/sbin/wpa_supplicant" 111 #include <signal.h> 112 static int config_eth_medium(char *); 113 static int config_dhcp(char *); 114 static int config_wlan(char *); 115 116 #ifdef INET6 117 static int is_v6kernel (void); 118 #endif 119 120 /* 121 * URL encode unsafe characters. See RFC 1738. 122 * 123 * Copies src string to dst, encoding unsafe or reserved characters 124 * in %hex form as it goes, and returning a pointer to the result. 125 * The result is always a nul-terminated string even if it had to be 126 * truncated to avoid overflowing the available space. 127 * 128 * This url_encode() function does not operate on complete URLs, it 129 * operates on strings that make up parts of URLs. For example, in a 130 * URL like "ftp://username:password@host/path", the username, password, 131 * host and path should each be encoded separately before they are 132 * joined together with the punctuation characters. 133 * 134 * In most ordinary use, the path portion of a URL does not start with 135 * a slash; the slash is a separator between the host portion and the 136 * path portion, and is dealt with by software outside the url_encode() 137 * function. However, it is valid for url_encode() to be passed a 138 * string that does begin with a slash. For example, the string might 139 * represent a password, or a path part of a URL that the user really 140 * does want to begin with a slash. 141 * 142 * len is the length of the destination buffer. The result will be 143 * truncated if necessary to fit in the destination buffer. 144 * 145 * safe_chars is a string of characters that should not be encoded. If 146 * safe_chars is non-NULL, any characters in safe_chars as well as any 147 * alphanumeric characters will be copied from src to dst without 148 * encoding. Some potentially useful settings for this parameter are: 149 * 150 * NULL Everything is encoded (even alphanumerics) 151 * "" Everything except alphanumerics are encoded 152 * "/" Alphanumerics and '/' remain unencoded 153 * "$-_.+!*'()," Consistent with a strict reading of RFC 1738 154 * "$-_.+!*'(),/" As above, except '/' is not encoded 155 * "-_.+!,/" As above, except shell special characters are encoded 156 * 157 * encode_leading_slash is a flag that determines whether or not to 158 * encode a leading slash in a string. If this flag is set, and if the 159 * first character in the src string is '/', then the leading slash will 160 * be encoded (as "%2F"), even if '/' is one of the characters in the 161 * safe_chars string. Note that only the first character of the src 162 * string is affected by this flag, and that leading slashes are never 163 * deleted, but either retained unchanged or encoded. 164 * 165 * Unsafe and reserved characters are defined in RFC 1738 section 2.2. 166 * The most important parts are: 167 * 168 * The characters ";", "/", "?", ":", "@", "=" and "&" are the 169 * characters which may be reserved for special meaning within a 170 * scheme. No other characters may be reserved within a scheme. 171 * [...] 172 * 173 * Thus, only alphanumerics, the special characters "$-_.+!*'(),", 174 * and reserved characters used for their reserved purposes may be 175 * used unencoded within a URL. 176 * 177 */ 178 179 #define RFC1738_SAFE "$-_.+!*'()," 180 #define RFC1738_SAFE_LESS_SHELL "-_.+!," 181 #define RFC1738_SAFE_LESS_SHELL_PLUS_SLASH "-_.+!,/" 182 183 static char * 184 url_encode(char *dst, const char *src, const char *ep, 185 const char *safe_chars, int encode_leading_slash) 186 { 187 int ch; 188 189 ep--; 190 191 for (; dst < ep; src++) { 192 ch = *src & 0xff; 193 if (ch == 0) 194 break; 195 if (safe_chars != NULL && 196 (ch != '/' || !encode_leading_slash) && 197 (isalnum(ch) || strchr(safe_chars, ch))) { 198 *dst++ = ch; 199 } else { 200 /* encode this char */ 201 if (ep - dst < 3) 202 break; 203 snprintf(dst, ep - dst, "%%%02X", ch); 204 dst += 3; 205 } 206 encode_leading_slash = 0; 207 } 208 *dst = '\0'; 209 return dst; 210 } 211 212 static const char *ignored_if_names[] = { 213 "gre", /* net */ 214 "ipip", /* netinet */ 215 "gif", /* netinet6 */ 216 "faith", /* netinet6 */ 217 "lo", /* net */ 218 "lo0", /* net */ 219 #if 0 220 "mdecap", /* netinet -- never in IF list (?) XXX */ 221 #endif 222 "ppp", /* net */ 223 #if 0 224 "sl", /* net */ 225 #endif 226 "strip", /* net */ 227 "tun", /* net */ 228 /* XXX others? */ 229 NULL, 230 }; 231 232 static bool 233 have_working_ipv4(void) 234 { 235 uint64_t ipstats[IP_NSTATS]; 236 size_t size = sizeof(ipstats); 237 238 /* At least some packets delivered to upper layers? */ 239 if (sysctlbyname("net.inet.ip.stats", ipstats, &size, NULL, 0) == -1) 240 return false; 241 if (ipstats[IP_STAT_DELIVERED] < 10) /* arbitrary threshold */ 242 return false; 243 244 /* do we have a default route? */ 245 if (run_program(RUN_SILENT|RUN_ERROR_OK, 246 "/sbin/route get -inet default") != 0) 247 return false; 248 249 return true; 250 } 251 252 #ifdef INET6 253 static bool 254 have_working_ipv6(void) 255 { 256 uint64_t ipstats[IP6_NSTATS]; 257 size_t size = sizeof(ipstats); 258 259 /* At least some packets delivered to upper layers? */ 260 if (sysctlbyname("net.inet6.ip6.stats", ipstats, &size, NULL, 0) == -1) 261 return false; 262 if (ipstats[IP6_STAT_DELIVERED] < 10) /* arbitrary threshold */ 263 return false; 264 265 /* do we have a default route? */ 266 if (run_program(RUN_SILENT|RUN_ERROR_OK, 267 "/sbin/route get -inet6 default") != 0) 268 return false; 269 270 return true; 271 } 272 #else 273 #define have_working_ipv6() false 274 #endif 275 276 static int 277 get_ifconfig_info(struct net_desc *devs) 278 { 279 char *buf_in; 280 char *buf_tmp; 281 const char **ignore; 282 char *buf; 283 char *tmp; 284 int textsize; 285 int i; 286 287 /* Get ifconfig information */ 288 textsize = collect(T_OUTPUT, &buf_in, "/sbin/ifconfig -l 2>/dev/null"); 289 if (textsize < 0) { 290 if (logfp) 291 (void)fprintf(logfp, 292 "Aborting: Could not run ifconfig.\n"); 293 (void)fprintf(stderr, "Could not run ifconfig."); 294 exit(1); 295 } 296 297 buf = malloc (STRSIZE * sizeof(char)); 298 for (i = 0, buf_tmp = buf_in; i < MAX_NETS && strlen(buf_tmp) > 0 299 && buf_tmp < buf_in + strlen(buf_in);) { 300 tmp = stpncpy(buf, buf_tmp, strcspn(buf_tmp," \n")); 301 *tmp='\0'; 302 buf_tmp += (strcspn(buf_tmp, " \n") + 1) * sizeof(char); 303 304 /* Skip ignored interfaces */ 305 for (ignore = ignored_if_names; *ignore != NULL; ignore++) { 306 size_t len = strlen(*ignore); 307 if (strncmp(buf, *ignore, len) == 0 && 308 isdigit((unsigned char)buf[len])) 309 break; 310 } 311 if (*ignore != NULL) 312 continue; 313 314 strlcpy (devs[i].if_dev, buf, STRSIZE); 315 i++; 316 } 317 if (i < MAX_NETS) 318 devs[i].if_dev[0] = 0; /* XXX ? */ 319 320 free(buf); 321 free(buf_in); 322 return i; 323 } 324 325 static int 326 do_ifreq(struct ifreq *ifr, unsigned long cmd, void *data) 327 { 328 int sock; 329 int rval; 330 331 sock = socket(PF_INET, SOCK_DGRAM, 0); 332 if (sock == -1) 333 return -1; 334 335 memset(ifr, 0, sizeof *ifr); 336 ifr->ifr_data = data; 337 strlcpy(ifr->ifr_name, net_dev, sizeof ifr->ifr_name); 338 rval = ioctl(sock, cmd, ifr); 339 close(sock); 340 341 return rval; 342 } 343 344 static int 345 do_ifmreq(struct ifmediareq *ifmr, unsigned long cmd) 346 { 347 int sock; 348 int rval; 349 350 sock = socket(PF_INET, SOCK_DGRAM, 0); 351 if (sock == -1) 352 return -1; 353 354 memset(ifmr, 0, sizeof *ifmr); 355 strlcpy(ifmr->ifm_name, net_dev, sizeof ifmr->ifm_name); 356 rval = ioctl(sock, cmd, ifmr); 357 close(sock); 358 359 return rval; 360 } 361 362 /* Fill in defaults network values for the selected interface */ 363 static void 364 get_ifinterface_info(void) 365 { 366 struct ifreq ifr; 367 struct ifmediareq ifmr; 368 struct sockaddr_in *sa_in = (void*)&ifr.ifr_addr; 369 int modew; 370 const char *media_opt; 371 const char *sep; 372 373 if (do_ifreq(&ifr, SIOCGIFADDR, NULL) == 0 && 374 sa_in->sin_addr.s_addr != 0) 375 strlcpy(net_ip, inet_ntoa(sa_in->sin_addr), sizeof net_ip); 376 377 if (do_ifreq(&ifr, SIOCGIFNETMASK, NULL) == 0 && 378 sa_in->sin_addr.s_addr != 0) 379 strlcpy(net_mask, inet_ntoa(sa_in->sin_addr), sizeof net_mask); 380 381 if (do_ifmreq(&ifmr, SIOCGIFMEDIA) == 0) { 382 /* Get the name of the media word */ 383 modew = ifmr.ifm_current; 384 strlcpy(net_media, get_media_subtype_string(modew), 385 sizeof net_media); 386 /* and add any media options */ 387 sep = " mediaopt "; 388 while ((media_opt = get_media_option_string(&modew)) != NULL) { 389 strlcat(net_media, sep, sizeof net_media); 390 strlcat(net_media, media_opt, sizeof net_media); 391 sep = ","; 392 } 393 } 394 } 395 396 #ifndef INET6 397 #define get_if6interface_info() 398 #else 399 static void 400 get_if6interface_info(void) 401 { 402 char *textbuf, *t; 403 int textsize; 404 405 textsize = collect(T_OUTPUT, &textbuf, 406 "/sbin/ifconfig %s inet6 2>/dev/null", net_dev); 407 if (textsize >= 0) { 408 char *p; 409 410 (void)strtok(textbuf, "\n"); /* ignore first line */ 411 while ((t = strtok(NULL, "\n")) != NULL) { 412 if (strncmp(t, "\tinet6 ", 7) != 0) 413 continue; 414 t += 7; 415 if (strstr(t, "tentative") || strstr(t, "duplicated")) 416 continue; 417 if (strncmp(t, "fe80:", 5) == 0) 418 continue; 419 420 p = t; 421 while (*p && *p != ' ' && *p != '\n') 422 p++; 423 *p = '\0'; 424 strlcpy(net_ip6, t, sizeof(net_ip6)); 425 break; 426 } 427 } 428 free(textbuf); 429 } 430 #endif 431 432 static void 433 get_host_info(void) 434 { 435 char hostname[MAXHOSTNAMELEN + 1]; 436 char *dot; 437 438 /* Check host (and domain?) name */ 439 if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != 0) { 440 hostname[sizeof(hostname) - 1] = 0; 441 /* check for a . */ 442 dot = strchr(hostname, '.'); 443 if (dot == NULL) { 444 /* if not found its just a host, punt on domain */ 445 strlcpy(net_host, hostname, sizeof net_host); 446 } else { 447 /* split hostname into host/domain parts */ 448 *dot++ = 0; 449 strlcpy(net_host, hostname, sizeof net_host); 450 strlcpy(net_domain, dot, sizeof net_domain); 451 } 452 } 453 } 454 455 /* 456 * recombine name parts split in get_host_info and config_network 457 * (common code moved here from write_etc_hosts) 458 */ 459 static char * 460 recombine_host_domain(void) 461 { 462 static char recombined[MAXHOSTNAMELEN + 1]; 463 int l = strlen(net_host) - strlen(net_domain); 464 465 strlcpy(recombined, net_host, sizeof(recombined)); 466 467 if (strlen(net_domain) != 0 && (l <= 0 || 468 net_host[l - 1] != '.' || 469 strcasecmp(net_domain, net_host + l) != 0)) { 470 /* net_host isn't an FQDN. */ 471 strlcat(recombined, ".", sizeof(recombined)); 472 strlcat(recombined, net_domain, sizeof(recombined)); 473 } 474 return recombined; 475 } 476 477 #ifdef INET6 478 static int 479 is_v6kernel(void) 480 { 481 int s; 482 483 s = socket(PF_INET6, SOCK_DGRAM, 0); 484 if (s < 0) 485 return 0; 486 close(s); 487 return 1; 488 } 489 #endif 490 491 static int 492 handle_license(const char *dev) 493 { 494 static struct { 495 const char *dev; 496 const char *lic; 497 } licdev[] = { 498 { "iwi", "/libdata/firmware/if_iwi/LICENSE.ipw2200-fw" }, 499 { "ipw", "/libdata/firmware/if_ipw/LICENSE" }, 500 }; 501 502 size_t i; 503 504 for (i = 0; i < __arraycount(licdev); i++) 505 if (strncmp(dev, licdev[i].dev, 3) == 0) { 506 char buf[64]; 507 int val; 508 size_t len = sizeof(int); 509 (void)snprintf(buf, sizeof(buf), "hw.%s.accept_eula", 510 licdev[i].dev); 511 if (sysctlbyname(buf, &val, &len, NULL, 0) != -1 512 && val != 0) 513 return 1; 514 msg_fmt_display(MSG_license, "%s%s", 515 dev, licdev[i].lic); 516 if (ask_yesno(NULL)) { 517 val = 1; 518 if (sysctlbyname(buf, NULL, NULL, &val, 519 0) == -1) 520 return 0; 521 add_sysctl_conf("%s=1", buf); 522 return 1; 523 } else 524 return 0; 525 } 526 return 1; 527 } 528 529 /* 530 * Get the information to configure the network, configure it and 531 * make sure both the gateway and the name server are up. 532 */ 533 int 534 config_network(int force) 535 { 536 char *textbuf; 537 int octet0; 538 int dhcp_config; 539 int nfs_root = 0; 540 int slip = 0; 541 int pid, status; 542 char **ap, *slcmd[10], *in_buf; 543 char buffer[STRSIZE]; 544 char hostname[MAXHOSTNAMELEN + 1]; 545 struct statvfs sb; 546 struct net_desc net_devs[MAX_NETS]; 547 menu_ent *net_menu; 548 int menu_no; 549 int num_devs; 550 int selected_net; 551 int i; 552 #ifdef INET6 553 int v6config = 1, rv; 554 #endif 555 556 FILE *f; 557 time_t now; 558 559 if (network_up) 560 return (1); 561 562 num_devs = get_ifconfig_info(net_devs); 563 564 if (num_devs < 1) { 565 /* No network interfaces found! */ 566 hit_enter_to_continue(NULL, MSG_nonet); 567 return -1; 568 } 569 570 if (!force && (have_working_ipv4() || have_working_ipv6())) { 571 if (ask_yesno(MSG_network_ok)) { 572 network_up = 1; 573 return 1; 574 } 575 } 576 577 net_menu = calloc(num_devs, sizeof(*net_menu)); 578 if (net_menu == NULL) { 579 err_msg_win(err_outofmem); 580 return -1; 581 } 582 583 for (i = 0; i < num_devs; i++) { 584 net_menu[i].opt_name = net_devs[i].if_dev; 585 net_menu[i].opt_flags = OPT_EXIT; 586 net_menu[i].opt_action = set_menu_select; 587 } 588 589 menu_no = new_menu(MSG_netdevs, 590 net_menu, num_devs, -1, 4, 0, 0, 591 MC_SCROLL, 592 NULL, NULL, NULL, NULL, MSG_cancel); 593 again: 594 selected_net = -1; 595 msg_display(MSG_asknetdev); 596 process_menu(menu_no, &selected_net); 597 598 if (selected_net == -1) { 599 free_menu(menu_no); 600 free(net_menu); 601 return 0; 602 } 603 604 network_up = 1; 605 dhcp_config = 0; 606 607 strlcpy(net_dev, net_devs[selected_net].if_dev, sizeof net_dev); 608 609 if (!handle_license(net_dev)) 610 goto done; 611 612 slip = net_dev[0] == 's' && net_dev[1] == 'l' && 613 isdigit((unsigned char)net_dev[2]); 614 615 /* If root is on NFS do not reconfigure the interface. */ 616 if (statvfs("/", &sb) == 0 && strcmp(sb.f_fstypename, "nfs") == 0) { 617 nfs_root = 1; 618 get_ifinterface_info(); 619 get_if6interface_info(); 620 get_host_info(); 621 } else if (!slip) { 622 /* Preload any defaults we can find */ 623 get_ifinterface_info(); 624 get_if6interface_info(); 625 get_host_info(); 626 627 /* domain and host */ 628 msg_display(MSG_netinfo); 629 630 if (!config_wlan(net_dev)) { 631 config_eth_medium(net_dev); 632 } 633 634 net_dhcpconf = 0; 635 /* try a dhcp configuration */ 636 dhcp_config = config_dhcp(net_dev); 637 if (dhcp_config) { 638 char *nline; 639 640 /* Get newly configured data off interface. */ 641 get_ifinterface_info(); 642 get_if6interface_info(); 643 get_host_info(); 644 645 net_dhcpconf |= DHCPCONF_IPADDR; 646 647 /* 648 * Extract default route from output of 649 * 'route -n show' 650 */ 651 if (collect(T_OUTPUT, &textbuf, 652 "/sbin/route -n show | " 653 "while read dest gateway flags;" 654 " do [ \"$dest\" = default ] && {" 655 " echo \"$gateway\"; break; };" 656 " done" ) > 0) 657 strlcpy(net_defroute, textbuf, 658 sizeof net_defroute); 659 free(textbuf); 660 if ((nline = strchr(net_defroute, '\n'))) 661 *nline = '\0'; 662 663 /* pull nameserver info out of /etc/resolv.conf */ 664 if (collect(T_OUTPUT, &textbuf, 665 "cat /etc/resolv.conf 2>/dev/null |" 666 " while read keyword address rest;" 667 " do [ \"$keyword\" = nameserver ] &&" 668 " { echo \"$address\"; break; };" 669 " done" ) > 0) 670 strlcpy(net_namesvr, textbuf, 671 sizeof net_namesvr); 672 free(textbuf); 673 if ((nline = strchr(net_namesvr, '\n'))) 674 *nline = '\0'; 675 if (net_namesvr[0] != '\0') 676 net_dhcpconf |= DHCPCONF_NAMESVR; 677 678 /* pull domain info out of /etc/resolv.conf */ 679 if (collect(T_OUTPUT, &textbuf, 680 "cat /etc/resolv.conf 2>/dev/null |" 681 " while read keyword domain rest;" 682 " do [ \"$keyword\" = domain ] &&" 683 " { echo \"$domain\"; break; };" 684 " done" ) > 0) 685 strlcpy(net_domain, textbuf, 686 sizeof net_domain); 687 free(textbuf); 688 if (net_domain[0] == '\0') { 689 /* pull domain info out of /etc/resolv.conf */ 690 if (collect(T_OUTPUT, &textbuf, 691 "cat /etc/resolv.conf 2>/dev/null |" 692 " while read keyword search rest;" 693 " do [ \"$keyword\" = search ] &&" 694 " { echo \"$search\"; break; };" 695 " done" ) > 0) 696 strlcpy(net_domain, textbuf, 697 sizeof net_domain); 698 free(textbuf); 699 } 700 if ((nline = strchr(net_domain, '\n'))) 701 *nline = '\0'; 702 if (net_domain[0] != '\0') 703 net_dhcpconf |= DHCPCONF_DOMAIN; 704 705 if (gethostname(net_host, sizeof(net_host)) == 0 && 706 net_host[0] != 0) 707 net_dhcpconf |= DHCPCONF_HOST; 708 } 709 } 710 711 /* 712 * Prompt for hostname and domain, even when using DHCP. The names 713 * discovered on the network may not match the desired values 714 * for the target system. 715 */ 716 strlcpy(hostname, recombine_host_domain(), MAXHOSTNAMELEN); 717 msg_prompt_add(MSG_net_host, net_host, net_host, 718 sizeof net_host); 719 msg_prompt_add(MSG_net_domain, net_domain, net_domain, 720 sizeof net_domain); 721 if (strcmp(hostname, recombine_host_domain()) != 0) { 722 net_dhcpconf &= ~(DHCPCONF_DOMAIN|DHCPCONF_HOST); 723 } 724 725 if (!dhcp_config) { 726 /* Manually configure IPv4 */ 727 if (!nfs_root) 728 msg_prompt_add(MSG_net_ip, net_ip, net_ip, 729 sizeof net_ip); 730 if (slip) 731 msg_prompt_add(MSG_net_srv_ip, net_srv_ip, net_srv_ip, 732 sizeof net_srv_ip); 733 else if (!nfs_root) { 734 /* We don't want netmasks for SLIP */ 735 octet0 = atoi(net_ip); 736 if (!net_mask[0]) { 737 if (0 <= octet0 && octet0 <= 127) 738 strlcpy(net_mask, "0xff000000", 739 sizeof(net_mask)); 740 else if (128 <= octet0 && octet0 <= 191) 741 strlcpy(net_mask, "0xffff0000", 742 sizeof(net_mask)); 743 else if (192 <= octet0 && octet0 <= 223) 744 strlcpy(net_mask, "0xffffff00", 745 sizeof(net_mask)); 746 } 747 msg_prompt_add(MSG_net_mask, net_mask, net_mask, 748 sizeof net_mask); 749 } 750 msg_prompt_add(MSG_net_defroute, net_defroute, net_defroute, 751 sizeof net_defroute); 752 } 753 754 if (!(net_dhcpconf & DHCPCONF_NAMESVR)) { 755 #ifdef INET6 756 if (v6config) { 757 rv = 0; 758 process_menu(MENU_namesrv6, &rv); 759 if (!rv) 760 msg_prompt_add(MSG_net_namesrv, net_namesvr, 761 net_namesvr, sizeof net_namesvr); 762 } else 763 #endif 764 msg_prompt_add(MSG_net_namesrv, net_namesvr, net_namesvr, 765 sizeof net_namesvr); 766 } 767 768 /* confirm the setting */ 769 msg_clear(); 770 if (slip) 771 msg_fmt_table_add(MSG_netok_slip, "%s%s%s%s%s%s%s%s%s", 772 net_domain, 773 net_host, 774 *net_namesvr == '\0' ? "<none>" : net_namesvr, 775 net_dev, 776 *net_media == '\0' ? "<default>" : net_media, 777 *net_ip == '\0' ? "<none>" : net_ip, 778 *net_srv_ip == '\0' ? "<none>" : net_srv_ip, 779 *net_mask == '\0' ? "<none>" : net_mask, 780 *net_defroute == '\0' ? "<none>" : net_defroute); 781 else 782 msg_fmt_table_add(MSG_netok, "%s%s%s%s%s%s%s%s", 783 net_domain, 784 net_host, 785 *net_namesvr == '\0' ? "<none>" : net_namesvr, 786 net_dev, 787 *net_media == '\0' ? "<default>" : net_media, 788 *net_ip == '\0' ? "<none>" : net_ip, 789 *net_mask == '\0' ? "<none>" : net_mask, 790 *net_defroute == '\0' ? "<none>" : net_defroute); 791 #ifdef INET6 792 msg_fmt_table_add(MSG_netokv6, "%s", 793 !is_v6kernel() ? "<not supported>" : net_ip6); 794 #endif 795 done: 796 if (!ask_yesno(MSG_netok_ok)) 797 goto again; 798 799 free_menu(menu_no); 800 free(net_menu); 801 802 run_program(0, "/sbin/ifconfig lo0 127.0.0.1"); 803 804 /* dhcpcd will have configured it all for us */ 805 if (dhcp_config) { 806 fflush(NULL); 807 network_up = 1; 808 return network_up; 809 } 810 811 /* 812 * we may want to perform checks against inconsistent configuration, 813 * like IPv4 DNS server without IPv4 configuration. 814 */ 815 816 /* Create /etc/resolv.conf if a nameserver was given */ 817 if (net_namesvr[0] != '\0') { 818 f = fopen("/etc/resolv.conf", "w"); 819 if (f == NULL) { 820 if (logfp) 821 (void)fprintf(logfp, 822 "%s", msg_string(MSG_resolv)); 823 (void)fprintf(stderr, "%s", msg_string(MSG_resolv)); 824 exit(1); 825 } 826 scripting_fprintf(NULL, "cat <<EOF >/etc/resolv.conf\n"); 827 time(&now); 828 scripting_fprintf(f, ";\n; BIND data file\n; %s %s;\n", 829 "Created by NetBSD sysinst on", safectime(&now)); 830 if (net_domain[0] != '\0') 831 scripting_fprintf(f, "search %s\n", net_domain); 832 if (net_namesvr[0] != '\0') 833 scripting_fprintf(f, "nameserver %s\n", net_namesvr); 834 scripting_fprintf(NULL, "EOF\n"); 835 fflush(NULL); 836 fclose(f); 837 } 838 839 if (net_ip[0] != '\0') { 840 if (slip) { 841 /* XXX: needs 'ifconfig sl0 create' much earlier */ 842 /* Set SLIP interface UP */ 843 run_program(0, "/sbin/ifconfig %s inet %s %s up", 844 net_dev, net_ip, net_srv_ip); 845 strcpy(sl_flags, "-s 115200 -l /dev/tty00"); 846 msg_prompt_win(MSG_slattach, -1, 12, 70, 0, 847 sl_flags, sl_flags, sizeof sl_flags); 848 849 /* XXX: wtf isn't run_program() used here? */ 850 pid = fork(); 851 if (pid == 0) { 852 strcpy(buffer, "/sbin/slattach "); 853 strcat(buffer, sl_flags); 854 in_buf = buffer; 855 856 for (ap = slcmd; (*ap = strsep(&in_buf, " ")) != NULL;) 857 if (**ap != '\0') 858 ++ap; 859 860 execvp(slcmd[0], slcmd); 861 } else 862 wait4(pid, &status, WNOHANG, 0); 863 } else if (!nfs_root) { 864 if (net_mask[0] != '\0') { 865 run_program(0, "/sbin/ifconfig %s inet %s netmask %s", 866 net_dev, net_ip, net_mask); 867 } else { 868 run_program(0, "/sbin/ifconfig %s inet %s", 869 net_dev, net_ip); 870 } 871 } 872 } 873 874 /* Set host name */ 875 if (net_host[0] != '\0') 876 sethostname(net_host, strlen(net_host)); 877 878 /* Set a default route if one was given */ 879 if (!nfs_root && net_defroute[0] != '\0') { 880 run_program(RUN_DISPLAY | RUN_PROGRESS, 881 "/sbin/route -n flush -inet"); 882 run_program(RUN_DISPLAY | RUN_PROGRESS, 883 "/sbin/route -n add default %s", net_defroute); 884 } 885 886 /* 887 * wait for addresses to become valid 888 */ 889 if (!nfs_root) { 890 msg_display_add(MSG_wait_network); 891 network_up = !run_program(RUN_DISPLAY | RUN_PROGRESS, 892 "/sbin/ifconfig -w 15 -W 5"); 893 } else { 894 /* Assume network is up. */ 895 network_up = 1; 896 } 897 898 fflush(NULL); 899 900 return network_up; 901 } 902 903 const char * 904 url_proto(unsigned int xfer) 905 { 906 switch (xfer) { 907 case XFER_FTP: return "ftp"; 908 case XFER_HTTP: return "http"; 909 } 910 911 return ""; 912 } 913 914 void 915 make_url(char *urlbuffer, struct ftpinfo *f, const char *dir) 916 { 917 char ftp_user_encoded[STRSIZE]; 918 char ftp_dir_encoded[STRSIZE]; 919 char *cp; 920 const char *dir2; 921 922 /* 923 * f->pass is quite likely to contain unsafe characters 924 * that need to be encoded in the URL (for example, 925 * "@", ":" and "/" need quoting). Let's be 926 * paranoid and also encode f->user and f->dir. (For 927 * example, f->dir could easily contain '~', which is 928 * unsafe by a strict reading of RFC 1738). 929 */ 930 if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) { 931 ftp_user_encoded[0] = 0; 932 } else { 933 cp = url_encode(ftp_user_encoded, f->user, 934 ftp_user_encoded + sizeof ftp_user_encoded - 1, 935 RFC1738_SAFE_LESS_SHELL, 0); 936 *cp++ = ':'; 937 cp = url_encode(cp, f->pass, 938 ftp_user_encoded + sizeof ftp_user_encoded - 1, 939 NULL, 0); 940 *cp++ = '@'; 941 *cp = 0; 942 } 943 cp = url_encode(ftp_dir_encoded, f->dir, 944 ftp_dir_encoded + sizeof ftp_dir_encoded - 1, 945 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 1); 946 if (cp != ftp_dir_encoded && cp[-1] != '/') 947 *cp++ = '/'; 948 949 dir2 = dir; 950 while (*dir2 == '/') 951 ++dir2; 952 953 url_encode(cp, dir2, 954 ftp_dir_encoded + sizeof ftp_dir_encoded, 955 RFC1738_SAFE_LESS_SHELL_PLUS_SLASH, 0); 956 957 snprintf(urlbuffer, STRSIZE, "%s://%s%s/%s", url_proto(f->xfer), 958 ftp_user_encoded, f->xfer_host[f->xfer], ftp_dir_encoded); 959 } 960 961 962 /* ftp_fetch() and pkgsrc_fetch() are essentially the same, with a different 963 * ftpinfo var and pkgsrc always using .tgz suffix, while for 964 * regular sets we only use .tgz for source sets on some architectures. */ 965 static int do_ftp_fetch(const char *, bool, struct ftpinfo *); 966 967 static int 968 ftp_fetch(const char *set_name) 969 { 970 return do_ftp_fetch(set_name, use_tgz_for_set(set_name), &ftp); 971 } 972 973 static int 974 pkgsrc_fetch(const char *set_name) 975 { 976 return do_ftp_fetch(set_name, true, &pkgsrc); 977 } 978 979 static int 980 do_ftp_fetch(const char *set_name, bool force_tgz, struct ftpinfo *f) 981 { 982 const char *ftp_opt; 983 char url[STRSIZE]; 984 int rval; 985 986 /* 987 * Invoke ftp to fetch the file. 988 */ 989 if (strcmp("ftp", f->user) == 0 && f->pass[0] == 0) { 990 /* do anon ftp */ 991 ftp_opt = "-a "; 992 } else { 993 ftp_opt = ""; 994 } 995 996 make_url(url, f, set_dir_for_set(set_name)); 997 rval = run_program(RUN_DISPLAY | RUN_PROGRESS | RUN_XFER_DIR, 998 "/usr/bin/ftp %s%s/%s%s", 999 ftp_opt, url, set_name, 1000 force_tgz ? dist_tgz_postfix : dist_postfix); 1001 1002 return rval ? SET_RETRY : SET_OK; 1003 } 1004 1005 1006 // XXX: check MSG_netnotup_continueanyway and MSG_netnotup 1007 1008 int 1009 get_pkgsrc(void) 1010 { 1011 int rv = -1; 1012 1013 process_menu(MENU_pkgsrc, &rv); 1014 1015 if (rv == SET_SKIP) 1016 return SET_SKIP; 1017 1018 fetch_fn = pkgsrc_fetch; 1019 snprintf(ext_dir_pkgsrc, sizeof ext_dir_pkgsrc, "%s/%s", 1020 target_prefix(), xfer_dir + (*xfer_dir == '/')); 1021 1022 return SET_OK; 1023 } 1024 1025 int 1026 get_via_ftp(unsigned int xfer) 1027 { 1028 arg_rv arg; 1029 1030 if (!network_up) 1031 config_network(0); 1032 1033 arg.rv = -1; 1034 arg.arg = (void*)(uintptr_t)(xfer); 1035 process_menu(MENU_ftpsource, &arg); 1036 1037 if (arg.rv == SET_RETRY) 1038 return SET_RETRY; 1039 1040 /* We'll fetch each file just before installing it */ 1041 fetch_fn = ftp_fetch; 1042 ftp.xfer = xfer; 1043 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", target_prefix(), 1044 xfer_dir + (*xfer_dir == '/')); 1045 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", target_prefix(), 1046 xfer_dir + (*xfer_dir == '/')); 1047 1048 return SET_OK; 1049 } 1050 1051 int 1052 get_via_nfs(void) 1053 { 1054 struct statvfs sb; 1055 int rv; 1056 1057 /* If root is on NFS and we have sets, skip this step. */ 1058 if (statvfs(set_dir_bin, &sb) == 0 && 1059 strcmp(sb.f_fstypename, "nfs") == 0) { 1060 strlcpy(ext_dir_bin, set_dir_bin, sizeof ext_dir_bin); 1061 strlcpy(ext_dir_src, set_dir_src, sizeof ext_dir_src); 1062 return SET_OK; 1063 } 1064 1065 /* Get server and filepath */ 1066 rv = -1; 1067 process_menu(MENU_nfssource, &rv); 1068 1069 if (rv == SET_RETRY) 1070 return SET_RETRY; 1071 1072 /* Mount it */ 1073 if (run_program(0, "/sbin/mount -r -o -2,-i,-r=1024 -t nfs %s:%s /mnt2", 1074 nfs_host, nfs_dir)) 1075 return SET_RETRY; 1076 1077 mnt2_mounted = 1; 1078 1079 snprintf(ext_dir_bin, sizeof ext_dir_bin, "/mnt2/%s", set_dir_bin); 1080 snprintf(ext_dir_src, sizeof ext_dir_src, "/mnt2/%s", set_dir_src); 1081 1082 /* return location, don't clean... */ 1083 return SET_OK; 1084 } 1085 1086 /* 1087 * write the new contents of /etc/hosts to the specified file 1088 */ 1089 static void 1090 write_etc_hosts(FILE *f) 1091 { 1092 scripting_fprintf(f, "#\n"); 1093 scripting_fprintf(f, "# Added by NetBSD sysinst\n"); 1094 scripting_fprintf(f, "#\n"); 1095 1096 if (net_domain[0] != '\0') 1097 scripting_fprintf(f, "127.0.0.1 localhost.%s\n", net_domain); 1098 1099 scripting_fprintf(f, "%s\t", net_ip); 1100 if (net_domain[0] != '\0') 1101 scripting_fprintf(f, "%s ", recombine_host_domain()); 1102 scripting_fprintf(f, "%s\n", net_host); 1103 } 1104 1105 /* 1106 * Write the network config info the user entered via menus into the 1107 * config files in the target disk. Be careful not to lose any 1108 * information we don't immediately add back, in case the install 1109 * target is the currently-active root. 1110 */ 1111 void 1112 mnt_net_config(void) 1113 { 1114 char ifconfig_fn[STRSIZE]; 1115 FILE *ifconf = NULL; 1116 1117 if (!network_up) 1118 return; 1119 if (!ask_yesno(MSG_mntnetconfig)) 1120 return; 1121 1122 /* Write hostname to /etc/rc.conf */ 1123 if ((net_dhcpconf & DHCPCONF_HOST) == 0) 1124 if (del_rc_conf("hostname") == 0) 1125 add_rc_conf("hostname=%s\n", recombine_host_domain()); 1126 1127 /* Copy resolv.conf to target. If DHCP was used to create it, 1128 * it will be replaced on next boot anyway. */ 1129 if (net_namesvr[0] != '\0') 1130 dup_file_into_target("/etc/resolv.conf"); 1131 1132 /* Copy wpa_supplicant.conf to target. */ 1133 if (net_ssid[0] != '\0') 1134 dup_file_into_target("/etc/wpa_supplicant.conf"); 1135 1136 /* 1137 * bring the interface up, it will be necessary for IPv6, and 1138 * it won't make trouble with IPv4 case either 1139 */ 1140 snprintf(ifconfig_fn, sizeof ifconfig_fn, "/etc/ifconfig.%s", net_dev); 1141 ifconf = target_fopen(ifconfig_fn, "w"); 1142 if (ifconf != NULL) { 1143 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n", 1144 target_prefix(), ifconfig_fn); 1145 scripting_fprintf(ifconf, "up\n"); 1146 if (*net_media != '\0') 1147 scripting_fprintf(ifconf, "media %s\n", net_media); 1148 scripting_fprintf(NULL, "EOF\n"); 1149 } 1150 1151 if ((net_dhcpconf & DHCPCONF_IPADDR) == 0) { 1152 FILE *hosts; 1153 1154 /* Write IPaddr and netmask to /etc/ifconfig.if[0-9] */ 1155 if (ifconf != NULL) { 1156 scripting_fprintf(NULL, "cat <<EOF >>%s%s\n", 1157 target_prefix(), ifconfig_fn); 1158 if (*net_media != '\0') 1159 scripting_fprintf(ifconf, 1160 "%s netmask %s media %s\n", 1161 net_ip, net_mask, net_media); 1162 else 1163 scripting_fprintf(ifconf, "%s netmask %s\n", 1164 net_ip, net_mask); 1165 scripting_fprintf(NULL, "EOF\n"); 1166 } 1167 1168 /* 1169 * Add IPaddr/hostname to /etc/hosts. 1170 * Be careful not to clobber any existing contents. 1171 * Relies on ordered search of /etc/hosts. XXX YP? 1172 */ 1173 hosts = target_fopen("/etc/hosts", "a"); 1174 if (hosts != 0) { 1175 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/hosts\n", 1176 target_prefix()); 1177 write_etc_hosts(hosts); 1178 (void)fclose(hosts); 1179 scripting_fprintf(NULL, "EOF\n"); 1180 } 1181 1182 if (del_rc_conf("defaultroute") == 0) 1183 add_rc_conf("defaultroute=\"%s\"\n", net_defroute); 1184 } else { 1185 /* 1186 * Start dhcpcd quietly and in master mode, but restrict 1187 * it to our interface 1188 */ 1189 add_rc_conf("dhcpcd=YES\n"); 1190 add_rc_conf("dhcpcd_flags=\"-qM %s\"\n", net_dev); 1191 } 1192 1193 if (net_ssid[0] != '\0') { 1194 add_rc_conf("wpa_supplicant=YES\n"); 1195 add_rc_conf("wpa_supplicant_flags=\"-B -s -i %s -D bsd -c /etc/wpa_supplicant.conf\"\n", net_dev); 1196 } 1197 1198 if (ifconf) 1199 fclose(ifconf); 1200 1201 fflush(NULL); 1202 } 1203 1204 int 1205 config_wlan(char *inter) 1206 { 1207 FILE *wpa_conf = NULL; 1208 char wpa_cmd[256]; 1209 struct ifreq ifr = {0}; 1210 struct ieee80211_nwid nwid = {0}; 1211 1212 /* skip non-WLAN devices */ 1213 if (do_ifreq(&ifr, SIOCG80211NWID, &nwid) == -1) 1214 return 0; 1215 1216 if (!file_mode_match(WPA_SUPPLICANT, S_IFREG)) 1217 return 0; 1218 1219 msg_prompt_add(MSG_net_ssid, net_ssid, net_ssid, 1220 sizeof net_ssid); 1221 if (net_ssid[0] == '\0') 1222 return 0; 1223 1224 msg_prompt_noecho(MSG_net_passphrase, net_passphrase, net_passphrase, 1225 sizeof net_passphrase); 1226 1227 wpa_conf = fopen("/etc/wpa_supplicant.conf", "a"); 1228 if (wpa_conf == NULL) 1229 return 0; 1230 1231 scripting_fprintf(NULL, 1232 "cat <<EOF >>%s/etc/wpa_supplicant.conf\n", 1233 target_prefix()); 1234 scripting_fprintf(wpa_conf, "\n#\n"); 1235 scripting_fprintf(wpa_conf, "# Added by NetBSD sysinst\n"); 1236 scripting_fprintf(wpa_conf, "#\n"); 1237 scripting_fprintf(wpa_conf, "network={\n"); 1238 scripting_fprintf(wpa_conf, 1239 "\tssid=\"%s\"\n", net_ssid); 1240 if (net_passphrase[0] != '\0') { 1241 scripting_fprintf(wpa_conf, "\tpsk=\"%s\"\n", 1242 net_passphrase); 1243 } else { 1244 scripting_fprintf(wpa_conf, "\tkey_mgmt=NONE\n"); 1245 } 1246 scripting_fprintf(wpa_conf, "\tscan_ssid=1\n"); 1247 scripting_fprintf(wpa_conf, "}\n"); 1248 (void)fclose(wpa_conf); 1249 scripting_fprintf(NULL, "EOF\n"); 1250 1251 if (run_program(RUN_DISPLAY | RUN_PROGRESS, 1252 "/sbin/ifconfig %s up", inter) != 0) 1253 return 0; 1254 1255 /* 1256 * have to use system() here to avoid the server process dying 1257 */ 1258 if (snprintf(wpa_cmd, sizeof(wpa_cmd), 1259 WPA_SUPPLICANT 1260 " -B -s -i %s -D bsd -c /etc/wpa_supplicant.conf", inter) < 0) 1261 return 0; 1262 (void)do_system(wpa_cmd); 1263 1264 return 1; 1265 } 1266 1267 int 1268 config_dhcp(char *inter) 1269 { 1270 int dhcpautoconf; 1271 1272 /* 1273 * Don't bother checking for an existing instance of dhcpcd, just 1274 * ask it to renew the lease. It will fork and daemonize if there 1275 * wasn't already an instance. 1276 */ 1277 1278 if (!file_mode_match(DHCPCD, S_IFREG)) 1279 return 0; 1280 if (ask_yesno(MSG_Perform_autoconfiguration)) { 1281 /* spawn off dhcpcd and wait for parent to exit */ 1282 dhcpautoconf = run_program(RUN_DISPLAY | RUN_PROGRESS, 1283 "%s -d -n %s", DHCPCD, inter); 1284 return dhcpautoconf ? 0 : 1; 1285 } 1286 return 0; 1287 } 1288 1289 1290 int 1291 config_eth_medium(char *inter) 1292 { 1293 char *textbuf = NULL; 1294 1295 for (;;) { 1296 msg_prompt_add(MSG_net_media, net_media, net_media, 1297 sizeof net_media); 1298 1299 /* 1300 * ifconfig does not allow media specifiers on 1301 * IFM_MANUAL interfaces. Our UI gives no way 1302 * to set an option back 1303 * to null-string if it gets accidentally set. 1304 * Check for plausible alternatives. 1305 */ 1306 if (strcmp(net_media, "<default>") == 0 || 1307 strcmp(net_media, "default") == 0 || 1308 strcmp(net_media, "<manual>") == 0 || 1309 strcmp(net_media, "manual") == 0 || 1310 strcmp(net_media, "<none>") == 0 || 1311 strcmp(net_media, "none") == 0 || 1312 strcmp(net_media, " ") == 0) { 1313 *net_media = '\0'; 1314 } 1315 1316 if (*net_media == '\0') 1317 break; 1318 /* 1319 * We must set the media type here - to give dhcp 1320 * a chance 1321 */ 1322 if (run_program(0, "/sbin/ifconfig %s media %s", 1323 net_dev, net_media) == 0) 1324 break; 1325 /* Failed to set - output the supported values */ 1326 if (collect(T_OUTPUT, &textbuf, "/sbin/ifconfig -m %s |" 1327 "while IFS=; read line;" 1328 " do [ \"$line\" = \"${line#*media}\" ] || " 1329 "echo $line;" 1330 " done", net_dev ) > 0) 1331 msg_display(textbuf); 1332 free(textbuf); 1333 } 1334 return 0; 1335 } 1336