10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*2088Smeem * Common Development and Distribution License (the "License"). 6*2088Smeem * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*2088Smeem * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <unistd.h> 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <sys/stat.h> 310Sstevel@tonic-gate #include <stdlib.h> 320Sstevel@tonic-gate #include <netinet/in.h> /* struct in_addr */ 330Sstevel@tonic-gate #include <netinet/dhcp.h> 340Sstevel@tonic-gate #include <signal.h> 350Sstevel@tonic-gate #include <sys/dlpi.h> 360Sstevel@tonic-gate #include <sys/sockio.h> 370Sstevel@tonic-gate #include <sys/socket.h> 380Sstevel@tonic-gate #include <errno.h> 390Sstevel@tonic-gate #include <net/route.h> 400Sstevel@tonic-gate #include <net/if_arp.h> 410Sstevel@tonic-gate #include <string.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 430Sstevel@tonic-gate #include <ctype.h> 440Sstevel@tonic-gate #include <netdb.h> 450Sstevel@tonic-gate #include <fcntl.h> 460Sstevel@tonic-gate #include <stdio.h> 470Sstevel@tonic-gate 480Sstevel@tonic-gate #include "states.h" 490Sstevel@tonic-gate #include "agent.h" 500Sstevel@tonic-gate #include "interface.h" 510Sstevel@tonic-gate #include "util.h" 520Sstevel@tonic-gate #include "packet.h" 530Sstevel@tonic-gate #include "defaults.h" 540Sstevel@tonic-gate 550Sstevel@tonic-gate /* 560Sstevel@tonic-gate * this file contains utility functions that have no real better home 570Sstevel@tonic-gate * of their own. they can largely be broken into six categories: 580Sstevel@tonic-gate * 590Sstevel@tonic-gate * o conversion functions -- functions to turn integers into strings, 600Sstevel@tonic-gate * or to convert between units of a similar measure. 610Sstevel@tonic-gate * 620Sstevel@tonic-gate * o ipc-related functions -- functions to simplify the generation of 630Sstevel@tonic-gate * ipc messages to the agent's clients. 640Sstevel@tonic-gate * 650Sstevel@tonic-gate * o signal-related functions -- functions to clean up the agent when 660Sstevel@tonic-gate * it receives a signal. 670Sstevel@tonic-gate * 680Sstevel@tonic-gate * o routing table manipulation functions 690Sstevel@tonic-gate * 700Sstevel@tonic-gate * o acknak handler functions 710Sstevel@tonic-gate * 720Sstevel@tonic-gate * o true miscellany -- anything else 730Sstevel@tonic-gate */ 740Sstevel@tonic-gate 750Sstevel@tonic-gate /* 760Sstevel@tonic-gate * pkt_type_to_string(): stringifies a packet type 770Sstevel@tonic-gate * 780Sstevel@tonic-gate * input: uchar_t: a DHCP packet type value, as defined in RFC2131 790Sstevel@tonic-gate * output: const char *: the stringified packet type 800Sstevel@tonic-gate */ 810Sstevel@tonic-gate 820Sstevel@tonic-gate const char * 830Sstevel@tonic-gate pkt_type_to_string(uchar_t type) 840Sstevel@tonic-gate { 850Sstevel@tonic-gate /* 860Sstevel@tonic-gate * note: the ordering here allows direct indexing of the table 870Sstevel@tonic-gate * based on the RFC2131 packet type value passed in. 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate 900Sstevel@tonic-gate static const char *types[] = { 910Sstevel@tonic-gate "BOOTP", "DISCOVER", "OFFER", "REQUEST", "DECLINE", 920Sstevel@tonic-gate "ACK", "NAK", "RELEASE", "INFORM" 930Sstevel@tonic-gate }; 940Sstevel@tonic-gate 950Sstevel@tonic-gate if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL) 960Sstevel@tonic-gate return ("<unknown>"); 970Sstevel@tonic-gate 980Sstevel@tonic-gate return (types[type]); 990Sstevel@tonic-gate } 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate /* 1020Sstevel@tonic-gate * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types 1030Sstevel@tonic-gate * 1040Sstevel@tonic-gate * input: uchar_t: the DLPI datalink type 1050Sstevel@tonic-gate * output: uchar_t: the ARP datalink type (0 if no corresponding code) 1060Sstevel@tonic-gate */ 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate uchar_t 1090Sstevel@tonic-gate dlpi_to_arp(uchar_t dlpi_type) 1100Sstevel@tonic-gate { 1110Sstevel@tonic-gate switch (dlpi_type) { 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate case DL_ETHER: 1140Sstevel@tonic-gate return (1); 1150Sstevel@tonic-gate 1160Sstevel@tonic-gate case DL_FRAME: 1170Sstevel@tonic-gate return (15); 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate case DL_ATM: 1200Sstevel@tonic-gate return (16); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate case DL_HDLC: 1230Sstevel@tonic-gate return (17); 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate case DL_FC: 1260Sstevel@tonic-gate return (18); 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate case DL_CSMACD: /* ieee 802 networks */ 1290Sstevel@tonic-gate case DL_TPB: 1300Sstevel@tonic-gate case DL_TPR: 1310Sstevel@tonic-gate case DL_METRO: 1320Sstevel@tonic-gate case DL_FDDI: 1330Sstevel@tonic-gate return (6); 1340Sstevel@tonic-gate case DL_IB: 1350Sstevel@tonic-gate return (ARPHRD_IB); 1360Sstevel@tonic-gate } 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate return (0); 1390Sstevel@tonic-gate } 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate /* 1420Sstevel@tonic-gate * monosec_to_string(): converts a monosec_t into a date string 1430Sstevel@tonic-gate * 1440Sstevel@tonic-gate * input: monosec_t: the monosec_t to convert 1450Sstevel@tonic-gate * output: const char *: the corresponding date string 1460Sstevel@tonic-gate */ 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate const char * 1490Sstevel@tonic-gate monosec_to_string(monosec_t monosec) 1500Sstevel@tonic-gate { 1510Sstevel@tonic-gate time_t time = monosec_to_time(monosec); 1520Sstevel@tonic-gate char *time_string = ctime(&time); 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate /* strip off the newline -- ugh, why, why, why.. */ 1550Sstevel@tonic-gate time_string[strlen(time_string) - 1] = '\0'; 1560Sstevel@tonic-gate return (time_string); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate /* 1600Sstevel@tonic-gate * monosec(): returns a monotonically increasing time in seconds that 1610Sstevel@tonic-gate * is not affected by stime(2) or adjtime(2). 1620Sstevel@tonic-gate * 1630Sstevel@tonic-gate * input: void 1640Sstevel@tonic-gate * output: monosec_t: the number of seconds since some time in the past 1650Sstevel@tonic-gate */ 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate monosec_t 1680Sstevel@tonic-gate monosec(void) 1690Sstevel@tonic-gate { 1700Sstevel@tonic-gate return (gethrtime() / NANOSEC); 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1740Sstevel@tonic-gate * monosec_to_time(): converts a monosec_t into real wall time 1750Sstevel@tonic-gate * 1760Sstevel@tonic-gate * input: monosec_t: the absolute monosec_t to convert 1770Sstevel@tonic-gate * output: time_t: the absolute time that monosec_t represents in wall time 1780Sstevel@tonic-gate */ 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate time_t 1810Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec) 1820Sstevel@tonic-gate { 1830Sstevel@tonic-gate return (abs_monosec - monosec()) + time(NULL); 1840Sstevel@tonic-gate } 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* 1870Sstevel@tonic-gate * send_ok_reply(): sends an "ok" reply to a request and closes the ipc 1880Sstevel@tonic-gate * connection 1890Sstevel@tonic-gate * 1900Sstevel@tonic-gate * input: dhcp_ipc_request_t *: the request to reply to 1910Sstevel@tonic-gate * int *: the ipc connection file descriptor (set to -1 on return) 1920Sstevel@tonic-gate * output: void 1930Sstevel@tonic-gate * note: the request is freed (thus the request must be on the heap). 1940Sstevel@tonic-gate */ 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate void 1970Sstevel@tonic-gate send_ok_reply(dhcp_ipc_request_t *request, int *control_fd) 1980Sstevel@tonic-gate { 1990Sstevel@tonic-gate send_error_reply(request, 0, control_fd); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * send_error_reply(): sends an "error" reply to a request and closes the ipc 2040Sstevel@tonic-gate * connection 2050Sstevel@tonic-gate * 2060Sstevel@tonic-gate * input: dhcp_ipc_request_t *: the request to reply to 2070Sstevel@tonic-gate * int: the error to send back on the ipc connection 2080Sstevel@tonic-gate * int *: the ipc connection file descriptor (set to -1 on return) 2090Sstevel@tonic-gate * output: void 2100Sstevel@tonic-gate * note: the request is freed (thus the request must be on the heap). 2110Sstevel@tonic-gate */ 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate void 2140Sstevel@tonic-gate send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd) 2150Sstevel@tonic-gate { 2160Sstevel@tonic-gate send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL); 2170Sstevel@tonic-gate } 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * send_data_reply(): sends a reply to a request and closes the ipc connection 2210Sstevel@tonic-gate * 2220Sstevel@tonic-gate * input: dhcp_ipc_request_t *: the request to reply to 2230Sstevel@tonic-gate * int *: the ipc connection file descriptor (set to -1 on return) 2240Sstevel@tonic-gate * int: the status to send back on the ipc connection (zero for 2250Sstevel@tonic-gate * success, DHCP_IPC_E_* otherwise). 2260Sstevel@tonic-gate * dhcp_data_type_t: the type of the payload in the reply 2270Sstevel@tonic-gate * void *: the payload for the reply, or NULL if there is no payload 2280Sstevel@tonic-gate * size_t: the size of the payload 2290Sstevel@tonic-gate * output: void 2300Sstevel@tonic-gate * note: the request is freed (thus the request must be on the heap). 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate void 2340Sstevel@tonic-gate send_data_reply(dhcp_ipc_request_t *request, int *control_fd, 2350Sstevel@tonic-gate int error, dhcp_data_type_t type, void *buffer, size_t size) 2360Sstevel@tonic-gate { 2370Sstevel@tonic-gate dhcp_ipc_reply_t *reply; 2380Sstevel@tonic-gate 2390Sstevel@tonic-gate if (*control_fd == -1) 2400Sstevel@tonic-gate return; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type); 2430Sstevel@tonic-gate if (reply == NULL) 2440Sstevel@tonic-gate dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply"); 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate else if (dhcp_ipc_send_reply(*control_fd, reply) != 0) 2470Sstevel@tonic-gate dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply"); 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate /* 2500Sstevel@tonic-gate * free the request since we've now used it to send our reply. 2510Sstevel@tonic-gate * we can also close the socket since the reply has been sent. 2520Sstevel@tonic-gate */ 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate free(reply); 2550Sstevel@tonic-gate free(request); 2560Sstevel@tonic-gate (void) dhcp_ipc_close(*control_fd); 2570Sstevel@tonic-gate *control_fd = -1; 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate /* 2610Sstevel@tonic-gate * print_server_msg(): prints a message from a DHCP server 2620Sstevel@tonic-gate * 2630Sstevel@tonic-gate * input: struct ifslist *: the interface the message came in on 2640Sstevel@tonic-gate * DHCP_OPT *: the option containing the string to display 2650Sstevel@tonic-gate * output: void 2660Sstevel@tonic-gate */ 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate void 2690Sstevel@tonic-gate print_server_msg(struct ifslist *ifsp, DHCP_OPT *p) 2700Sstevel@tonic-gate { 2710Sstevel@tonic-gate dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name, 2720Sstevel@tonic-gate p->len, p->value); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate /* 2760Sstevel@tonic-gate * alrm_exit(): Signal handler for SIGARLM. terminates grandparent. 2770Sstevel@tonic-gate * 2780Sstevel@tonic-gate * input: int: signal the handler was called with. 2790Sstevel@tonic-gate * 2800Sstevel@tonic-gate * output: void 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate static void 2840Sstevel@tonic-gate alrm_exit(int sig) 2850Sstevel@tonic-gate { 2860Sstevel@tonic-gate int exitval; 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate if (sig == SIGALRM && grandparent != 0) 2890Sstevel@tonic-gate exitval = EXIT_SUCCESS; 2900Sstevel@tonic-gate else 2910Sstevel@tonic-gate exitval = EXIT_FAILURE; 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate _exit(exitval); 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate /* 2970Sstevel@tonic-gate * daemonize(): daemonizes the process 2980Sstevel@tonic-gate * 2990Sstevel@tonic-gate * input: void 3000Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 3010Sstevel@tonic-gate */ 3020Sstevel@tonic-gate 3030Sstevel@tonic-gate int 3040Sstevel@tonic-gate daemonize(void) 3050Sstevel@tonic-gate { 3060Sstevel@tonic-gate /* 3070Sstevel@tonic-gate * We've found that adoption takes sufficiently long that 3080Sstevel@tonic-gate * a dhcpinfo run after dhcpagent -a is started may occur 3090Sstevel@tonic-gate * before the agent is ready to process the request. 3100Sstevel@tonic-gate * The result is an error message and an unhappy user. 3110Sstevel@tonic-gate * 3120Sstevel@tonic-gate * The initial process now sleeps for DHCP_ADOPT_SLEEP, 3130Sstevel@tonic-gate * unless interrupted by a SIGALRM, in which case it 3140Sstevel@tonic-gate * exits immediately. This has the effect that the 3150Sstevel@tonic-gate * grandparent doesn't exit until the dhcpagent is ready 3160Sstevel@tonic-gate * to process requests. This defers the the balance of 3170Sstevel@tonic-gate * the system start-up script processing until the 3180Sstevel@tonic-gate * dhcpagent is ready to field requests. 3190Sstevel@tonic-gate * 3200Sstevel@tonic-gate * grandparent is only set for the adopt case; other 3210Sstevel@tonic-gate * cases do not require the wait. 3220Sstevel@tonic-gate */ 3230Sstevel@tonic-gate 3240Sstevel@tonic-gate if (grandparent != 0) 3250Sstevel@tonic-gate (void) signal(SIGALRM, alrm_exit); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate switch (fork()) { 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate case -1: 3300Sstevel@tonic-gate return (0); 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate case 0: 3330Sstevel@tonic-gate if (grandparent != 0) 3340Sstevel@tonic-gate (void) signal(SIGALRM, SIG_DFL); 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate /* 3370Sstevel@tonic-gate * setsid() makes us lose our controlling terminal, 3380Sstevel@tonic-gate * and become both a session leader and a process 3390Sstevel@tonic-gate * group leader. 3400Sstevel@tonic-gate */ 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate (void) setsid(); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate /* 3450Sstevel@tonic-gate * under POSIX, a session leader can accidentally 3460Sstevel@tonic-gate * (through open(2)) acquire a controlling terminal if 3470Sstevel@tonic-gate * it does not have one. just to be safe, fork again 3480Sstevel@tonic-gate * so we are not a session leader. 3490Sstevel@tonic-gate */ 3500Sstevel@tonic-gate 3510Sstevel@tonic-gate switch (fork()) { 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate case -1: 3540Sstevel@tonic-gate return (0); 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate case 0: 3570Sstevel@tonic-gate (void) signal(SIGHUP, SIG_IGN); 3580Sstevel@tonic-gate (void) chdir("/"); 3590Sstevel@tonic-gate (void) umask(022); 3600Sstevel@tonic-gate closefrom(0); 3610Sstevel@tonic-gate break; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate default: 3640Sstevel@tonic-gate _exit(EXIT_SUCCESS); 3650Sstevel@tonic-gate } 3660Sstevel@tonic-gate break; 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate default: 3690Sstevel@tonic-gate if (grandparent != 0) { 3700Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN); 3710Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: " 3720Sstevel@tonic-gate "waiting for adoption to complete."); 3730Sstevel@tonic-gate if (sleep(DHCP_ADOPT_SLEEP) == 0) { 3740Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: " 3750Sstevel@tonic-gate "timed out awaiting adoption."); 3760Sstevel@tonic-gate } 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate _exit(EXIT_SUCCESS); 3790Sstevel@tonic-gate } 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate return (1); 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate /* 3850Sstevel@tonic-gate * update_default_route(): update the interface's default route 3860Sstevel@tonic-gate * 3870Sstevel@tonic-gate * input: int: the type of message; either RTM_ADD or RTM_DELETE 3880Sstevel@tonic-gate * struct in_addr: the default gateway to use 3890Sstevel@tonic-gate * const char *: the interface associated with the route 3900Sstevel@tonic-gate * int: any additional flags (besides RTF_STATIC and RTF_GATEWAY) 3910Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 3920Sstevel@tonic-gate */ 3930Sstevel@tonic-gate 3940Sstevel@tonic-gate static int 3950Sstevel@tonic-gate update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo, 3960Sstevel@tonic-gate int flags) 3970Sstevel@tonic-gate { 3980Sstevel@tonic-gate static int rtsock_fd = -1; 3990Sstevel@tonic-gate struct { 4000Sstevel@tonic-gate struct rt_msghdr rm_mh; 4010Sstevel@tonic-gate struct sockaddr_in rm_dst; 4020Sstevel@tonic-gate struct sockaddr_in rm_gw; 4030Sstevel@tonic-gate struct sockaddr_in rm_mask; 4040Sstevel@tonic-gate struct sockaddr_dl rm_ifp; 4050Sstevel@tonic-gate } rtmsg; 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate if (rtsock_fd == -1) { 4080Sstevel@tonic-gate rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); 4090Sstevel@tonic-gate if (rtsock_fd == -1) { 4100Sstevel@tonic-gate dhcpmsg(MSG_ERR, "update_default_route: " 4110Sstevel@tonic-gate "cannot create routing socket"); 4120Sstevel@tonic-gate return (0); 4130Sstevel@tonic-gate } 4140Sstevel@tonic-gate } 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate (void) memset(&rtmsg, 0, sizeof (rtmsg)); 4170Sstevel@tonic-gate rtmsg.rm_mh.rtm_version = RTM_VERSION; 4180Sstevel@tonic-gate rtmsg.rm_mh.rtm_msglen = sizeof (rtmsg); 4190Sstevel@tonic-gate rtmsg.rm_mh.rtm_type = type; 4200Sstevel@tonic-gate rtmsg.rm_mh.rtm_pid = getpid(); 4210Sstevel@tonic-gate rtmsg.rm_mh.rtm_flags = RTF_GATEWAY | RTF_STATIC | flags; 4220Sstevel@tonic-gate rtmsg.rm_mh.rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate rtmsg.rm_gw.sin_family = AF_INET; 4250Sstevel@tonic-gate rtmsg.rm_gw.sin_addr = *gateway_nbo; 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate rtmsg.rm_dst.sin_family = AF_INET; 4280Sstevel@tonic-gate rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY); 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate rtmsg.rm_mask.sin_family = AF_INET; 4310Sstevel@tonic-gate rtmsg.rm_mask.sin_addr.s_addr = htonl(0); 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate rtmsg.rm_ifp.sdl_family = AF_LINK; 4340Sstevel@tonic-gate rtmsg.rm_ifp.sdl_index = if_nametoindex(ifname); 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg)); 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate 4390Sstevel@tonic-gate /* 4400Sstevel@tonic-gate * add_default_route(): add the default route to the given gateway 4410Sstevel@tonic-gate * 4420Sstevel@tonic-gate * input: const char *: the name of the interface associated with the route 4430Sstevel@tonic-gate * struct in_addr: the default gateway to add 4440Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 4450Sstevel@tonic-gate */ 4460Sstevel@tonic-gate 4470Sstevel@tonic-gate int 4480Sstevel@tonic-gate add_default_route(const char *ifname, struct in_addr *gateway_nbo) 4490Sstevel@tonic-gate { 4500Sstevel@tonic-gate if (strchr(ifname, ':') != NULL) /* see README */ 4510Sstevel@tonic-gate return (1); 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP)); 4540Sstevel@tonic-gate } 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate /* 4570Sstevel@tonic-gate * del_default_route(): deletes the default route to the given gateway 4580Sstevel@tonic-gate * 4590Sstevel@tonic-gate * input: const char *: the name of the interface associated with the route 4600Sstevel@tonic-gate * struct in_addr: if not INADDR_ANY, the default gateway to remove 4610Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 4620Sstevel@tonic-gate */ 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate int 4650Sstevel@tonic-gate del_default_route(const char *ifname, struct in_addr *gateway_nbo) 4660Sstevel@tonic-gate { 4670Sstevel@tonic-gate if (strchr(ifname, ':') != NULL) 4680Sstevel@tonic-gate return (1); 4690Sstevel@tonic-gate 4700Sstevel@tonic-gate if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */ 4710Sstevel@tonic-gate return (1); 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0)); 4740Sstevel@tonic-gate } 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate /* 4770Sstevel@tonic-gate * inactivity_shutdown(): shuts down agent if there are no interfaces to manage 4780Sstevel@tonic-gate * 4790Sstevel@tonic-gate * input: iu_tq_t *: unused 4800Sstevel@tonic-gate * void *: unused 4810Sstevel@tonic-gate * output: void 4820Sstevel@tonic-gate */ 4830Sstevel@tonic-gate 4840Sstevel@tonic-gate /* ARGSUSED */ 4850Sstevel@tonic-gate void 4860Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg) 4870Sstevel@tonic-gate { 4880Sstevel@tonic-gate if (ifs_count() > 0) /* shouldn't happen, but... */ 4890Sstevel@tonic-gate return; 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate /* 4950Sstevel@tonic-gate * graceful_shutdown(): shuts down the agent gracefully 4960Sstevel@tonic-gate * 4970Sstevel@tonic-gate * input: int: the signal that caused graceful_shutdown to be called 4980Sstevel@tonic-gate * output: void 4990Sstevel@tonic-gate */ 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate void 5020Sstevel@tonic-gate graceful_shutdown(int sig) 5030Sstevel@tonic-gate { 504*2088Smeem iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE : 505*2088Smeem DHCP_REASON_SIGNAL), drain_script, NULL); 5060Sstevel@tonic-gate } 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate /* 5090Sstevel@tonic-gate * register_acknak(): registers dhcp_acknak() to be called back when ACK or 5100Sstevel@tonic-gate * NAK packets are received on a given interface 5110Sstevel@tonic-gate * 5120Sstevel@tonic-gate * input: struct ifslist *: the interface to register for 5130Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 5140Sstevel@tonic-gate */ 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate int 5170Sstevel@tonic-gate register_acknak(struct ifslist *ifsp) 5180Sstevel@tonic-gate { 5190Sstevel@tonic-gate iu_event_id_t ack_id, ack_bcast_id = -1; 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate /* 5220Sstevel@tonic-gate * having an acknak id already registered isn't impossible; 5230Sstevel@tonic-gate * handle the situation as gracefully as possible. 5240Sstevel@tonic-gate */ 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate if (ifsp->if_acknak_id != -1) { 5270Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "register_acknak: acknak id pending, " 5280Sstevel@tonic-gate "attempting to cancel"); 5290Sstevel@tonic-gate if (unregister_acknak(ifsp) == 0) 5300Sstevel@tonic-gate return (0); 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate switch (ifsp->if_state) { 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate case BOUND: 5360Sstevel@tonic-gate case REBINDING: 5370Sstevel@tonic-gate case RENEWING: 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate ack_bcast_id = iu_register_event(eh, ifsp->if_sock_fd, POLLIN, 5400Sstevel@tonic-gate dhcp_acknak, ifsp); 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate if (ack_bcast_id == -1) { 5430Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "register_acknak: cannot " 5440Sstevel@tonic-gate "register to receive socket broadcasts"); 5450Sstevel@tonic-gate return (0); 5460Sstevel@tonic-gate } 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate ack_id = iu_register_event(eh, ifsp->if_sock_ip_fd, POLLIN, 5490Sstevel@tonic-gate dhcp_acknak, ifsp); 5500Sstevel@tonic-gate break; 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate default: 5530Sstevel@tonic-gate ack_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN, 5540Sstevel@tonic-gate dhcp_acknak, ifsp); 5550Sstevel@tonic-gate break; 5560Sstevel@tonic-gate } 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate if (ack_id == -1) { 5590Sstevel@tonic-gate dhcpmsg(MSG_WARNING, "register_acknak: cannot register event"); 5600Sstevel@tonic-gate (void) iu_unregister_event(eh, ack_bcast_id, NULL); 5610Sstevel@tonic-gate return (0); 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate ifsp->if_acknak_id = ack_id; 5650Sstevel@tonic-gate hold_ifs(ifsp); 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate ifsp->if_acknak_bcast_id = ack_bcast_id; 5680Sstevel@tonic-gate if (ifsp->if_acknak_bcast_id != -1) { 5690Sstevel@tonic-gate hold_ifs(ifsp); 5700Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "register_acknak: registered broadcast id " 5710Sstevel@tonic-gate "%d", ack_bcast_id); 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "register_acknak: registered acknak id %d", ack_id); 5750Sstevel@tonic-gate return (1); 5760Sstevel@tonic-gate } 5770Sstevel@tonic-gate 5780Sstevel@tonic-gate /* 5790Sstevel@tonic-gate * unregister_acknak(): unregisters dhcp_acknak() to be called back 5800Sstevel@tonic-gate * 5810Sstevel@tonic-gate * input: struct ifslist *: the interface to unregister for 5820Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 5830Sstevel@tonic-gate */ 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate int 5860Sstevel@tonic-gate unregister_acknak(struct ifslist *ifsp) 5870Sstevel@tonic-gate { 5880Sstevel@tonic-gate if (ifsp->if_acknak_id != -1) { 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate if (iu_unregister_event(eh, ifsp->if_acknak_id, NULL) == 0) { 5910Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot " 5920Sstevel@tonic-gate "unregister acknak id %d on %s", 5930Sstevel@tonic-gate ifsp->if_acknak_id, ifsp->if_name); 5940Sstevel@tonic-gate return (0); 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered acknak id " 5980Sstevel@tonic-gate "%d", ifsp->if_acknak_id); 5990Sstevel@tonic-gate 6000Sstevel@tonic-gate ifsp->if_acknak_id = -1; 6010Sstevel@tonic-gate (void) release_ifs(ifsp); 6020Sstevel@tonic-gate } 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate if (ifsp->if_acknak_bcast_id != -1) { 6050Sstevel@tonic-gate 6060Sstevel@tonic-gate if (iu_unregister_event(eh, ifsp->if_acknak_bcast_id, NULL) 6070Sstevel@tonic-gate == 0) { 6080Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot " 6090Sstevel@tonic-gate "unregister broadcast id %d on %s", 6100Sstevel@tonic-gate ifsp->if_acknak_id, ifsp->if_name); 6110Sstevel@tonic-gate return (0); 6120Sstevel@tonic-gate } 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered " 6150Sstevel@tonic-gate "broadcast id %d", ifsp->if_acknak_bcast_id); 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate ifsp->if_acknak_bcast_id = -1; 6180Sstevel@tonic-gate (void) release_ifs(ifsp); 6190Sstevel@tonic-gate } 6200Sstevel@tonic-gate 6210Sstevel@tonic-gate return (1); 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate /* 6250Sstevel@tonic-gate * bind_sock(): binds a socket to a given IP address and port number 6260Sstevel@tonic-gate * 6270Sstevel@tonic-gate * input: int: the socket to bind 6280Sstevel@tonic-gate * in_port_t: the port number to bind to, host byte order 6290Sstevel@tonic-gate * in_addr_t: the address to bind to, host byte order 6300Sstevel@tonic-gate * output: int: 1 on success, 0 on failure 6310Sstevel@tonic-gate */ 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate int 6340Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo) 6350Sstevel@tonic-gate { 6360Sstevel@tonic-gate struct sockaddr_in sin; 6370Sstevel@tonic-gate int on = 1; 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (struct sockaddr_in)); 6400Sstevel@tonic-gate sin.sin_family = AF_INET; 6410Sstevel@tonic-gate sin.sin_port = htons(port_hbo); 6420Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(addr_hbo); 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int)); 6450Sstevel@tonic-gate 6460Sstevel@tonic-gate return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0); 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate /* 6500Sstevel@tonic-gate * valid_hostname(): check whether a string is a valid hostname 6510Sstevel@tonic-gate * 6520Sstevel@tonic-gate * input: const char *: the string to verify as a hostname 6530Sstevel@tonic-gate * output: boolean_t: B_TRUE if the string is a valid hostname 6540Sstevel@tonic-gate * 6550Sstevel@tonic-gate * Note that we accept both host names beginning with a digit and 6560Sstevel@tonic-gate * those containing hyphens. Neither is strictly legal according 6570Sstevel@tonic-gate * to the RFCs, but both are in common practice, so we endeavour 6580Sstevel@tonic-gate * to not break what customers are using. 6590Sstevel@tonic-gate */ 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate static boolean_t 6620Sstevel@tonic-gate valid_hostname(const char *hostname) 6630Sstevel@tonic-gate { 6640Sstevel@tonic-gate unsigned int i; 6650Sstevel@tonic-gate 6660Sstevel@tonic-gate for (i = 0; hostname[i] != '\0'; i++) { 6670Sstevel@tonic-gate 6680Sstevel@tonic-gate if (isalpha(hostname[i]) || isdigit(hostname[i]) || 6690Sstevel@tonic-gate (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0))) 6700Sstevel@tonic-gate continue; 6710Sstevel@tonic-gate 6720Sstevel@tonic-gate return (B_FALSE); 6730Sstevel@tonic-gate } 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate return (i > 0); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate /* 6790Sstevel@tonic-gate * iffile_to_hostname(): return the hostname contained on a line of the form 6800Sstevel@tonic-gate * 6810Sstevel@tonic-gate * [ ^I]*inet[ ^I]+hostname[\n]*\0 6820Sstevel@tonic-gate * 6830Sstevel@tonic-gate * in the file located at the specified path 6840Sstevel@tonic-gate * 6850Sstevel@tonic-gate * input: const char *: the path of the file to look in for the hostname 6860Sstevel@tonic-gate * output: const char *: the hostname at that path, or NULL on failure 6870Sstevel@tonic-gate */ 6880Sstevel@tonic-gate 6890Sstevel@tonic-gate #define IFLINE_MAX 1024 /* maximum length of a hostname.<if> line */ 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate const char * 6920Sstevel@tonic-gate iffile_to_hostname(const char *path) 6930Sstevel@tonic-gate { 6940Sstevel@tonic-gate FILE *fp; 6950Sstevel@tonic-gate static char ifline[IFLINE_MAX]; 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate fp = fopen(path, "r"); 6980Sstevel@tonic-gate if (fp == NULL) 6990Sstevel@tonic-gate return (NULL); 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate /* 7020Sstevel@tonic-gate * /etc/hostname.<if> may contain multiple ifconfig commands, but each 7030Sstevel@tonic-gate * such command is on a separate line (see the "while read ifcmds" code 7040Sstevel@tonic-gate * in /etc/init.d/inetinit). Thus we will read the file a line at a 7050Sstevel@tonic-gate * time, searching for a line of the form 7060Sstevel@tonic-gate * 7070Sstevel@tonic-gate * [ ^I]*inet[ ^I]+hostname[\n]*\0 7080Sstevel@tonic-gate * 7090Sstevel@tonic-gate * extract the host name from it, and check it for validity. 7100Sstevel@tonic-gate */ 7110Sstevel@tonic-gate while (fgets(ifline, sizeof (ifline), fp) != NULL) { 7120Sstevel@tonic-gate char *p; 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate if ((p = strstr(ifline, "inet")) != NULL) { 7150Sstevel@tonic-gate if ((p != ifline) && !isspace(p[-1])) { 7160Sstevel@tonic-gate (void) fclose(fp); 7170Sstevel@tonic-gate return (NULL); 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate p += 4; /* skip over "inet" and expect spaces or tabs */ 7200Sstevel@tonic-gate if ((*p == '\n') || (*p == '\0')) { 7210Sstevel@tonic-gate (void) fclose(fp); 7220Sstevel@tonic-gate return (NULL); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate if (isspace(*p)) { 7250Sstevel@tonic-gate char *nlptr; 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate /* no need to read more of the file */ 7280Sstevel@tonic-gate (void) fclose(fp); 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate while (isspace(*p)) 7310Sstevel@tonic-gate p++; 7320Sstevel@tonic-gate if ((nlptr = strrchr(p, '\n')) != NULL) 7330Sstevel@tonic-gate *nlptr = '\0'; 7340Sstevel@tonic-gate if (strlen(p) > MAXHOSTNAMELEN) { 7350Sstevel@tonic-gate dhcpmsg(MSG_WARNING, 7360Sstevel@tonic-gate "iffile_to_hostname:" 7370Sstevel@tonic-gate " host name too long"); 7380Sstevel@tonic-gate return (NULL); 7390Sstevel@tonic-gate } 7400Sstevel@tonic-gate if (valid_hostname(p)) { 7410Sstevel@tonic-gate return (p); 7420Sstevel@tonic-gate } else { 7430Sstevel@tonic-gate dhcpmsg(MSG_WARNING, 7440Sstevel@tonic-gate "iffile_to_hostname:" 7450Sstevel@tonic-gate " host name not valid"); 7460Sstevel@tonic-gate return (NULL); 7470Sstevel@tonic-gate } 7480Sstevel@tonic-gate } else { 7490Sstevel@tonic-gate (void) fclose(fp); 7500Sstevel@tonic-gate return (NULL); 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate } 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate (void) fclose(fp); 7560Sstevel@tonic-gate return (NULL); 7570Sstevel@tonic-gate } 758