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