1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * This file contains routines responsible for getting the system's 31*0Sstevel@tonic-gate * name and boot params. Most of it comes from the SVR4 diskless boot 32*0Sstevel@tonic-gate * code (dlboot_inet), modified to work in a non socket environment. 33*0Sstevel@tonic-gate */ 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate #include <sys/types.h> 36*0Sstevel@tonic-gate #include <rpc/types.h> 37*0Sstevel@tonic-gate #include <sys/errno.h> 38*0Sstevel@tonic-gate #include <rpc/auth.h> 39*0Sstevel@tonic-gate #include <rpc/xdr.h> 40*0Sstevel@tonic-gate #include <rpc/rpc_msg.h> 41*0Sstevel@tonic-gate #include <sys/t_lock.h> 42*0Sstevel@tonic-gate #include "clnt.h" 43*0Sstevel@tonic-gate #include <rpc/rpc.h> 44*0Sstevel@tonic-gate #include <sys/utsname.h> 45*0Sstevel@tonic-gate #include <netinet/in.h> 46*0Sstevel@tonic-gate #include <sys/socket.h> 47*0Sstevel@tonic-gate #include <net/if.h> 48*0Sstevel@tonic-gate #include <netinet/if_ether.h> 49*0Sstevel@tonic-gate #include <netinet/in.h> 50*0Sstevel@tonic-gate #include <sys/promif.h> 51*0Sstevel@tonic-gate #include <rpcsvc/bootparam.h> 52*0Sstevel@tonic-gate #include "pmap.h" 53*0Sstevel@tonic-gate #include "brpc.h" 54*0Sstevel@tonic-gate #include "socket_inet.h" 55*0Sstevel@tonic-gate #include "ipv4.h" 56*0Sstevel@tonic-gate #include <sys/salib.h> 57*0Sstevel@tonic-gate #include <sys/bootdebug.h> 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate extern int errno; 60*0Sstevel@tonic-gate static struct bp_whoami_res bp; 61*0Sstevel@tonic-gate static char bp_hostname[SYS_NMLN+1]; 62*0Sstevel@tonic-gate static char bp_domainname[SYS_NMLN+1]; 63*0Sstevel@tonic-gate static struct in_addr responder; /* network order */ 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate static const char *noserver = 66*0Sstevel@tonic-gate "No bootparam (%s) server responding; still trying...\n"; 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate #define GETFILE_BTIMEO 1 69*0Sstevel@tonic-gate #define GETFILE_BRETRIES 2 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate #define dprintf if (boothowto & RB_DEBUG) printf 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate /* 74*0Sstevel@tonic-gate * Returns TRUE if it has set the global structure 'bp' to our boot 75*0Sstevel@tonic-gate * parameters, FALSE if some failure occurred. 76*0Sstevel@tonic-gate */ 77*0Sstevel@tonic-gate bool_t 78*0Sstevel@tonic-gate whoami(void) 79*0Sstevel@tonic-gate { 80*0Sstevel@tonic-gate struct bp_whoami_arg arg; 81*0Sstevel@tonic-gate struct sockaddr_in to, from; 82*0Sstevel@tonic-gate struct in_addr ipaddr; 83*0Sstevel@tonic-gate enum clnt_stat stat; 84*0Sstevel@tonic-gate bool_t retval = TRUE; 85*0Sstevel@tonic-gate int rexmit; /* retransmission interval */ 86*0Sstevel@tonic-gate int resp_wait; /* secs to wait for resp */ 87*0Sstevel@tonic-gate int namelen; 88*0Sstevel@tonic-gate int printed_waiting_msg; 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate /* 91*0Sstevel@tonic-gate * Set our destination IP address to the limited broadcast address 92*0Sstevel@tonic-gate * (INADDR_BROADCAST). 93*0Sstevel@tonic-gate */ 94*0Sstevel@tonic-gate to.sin_family = AF_INET; 95*0Sstevel@tonic-gate to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 96*0Sstevel@tonic-gate to.sin_port = htons(0); 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate /* 99*0Sstevel@tonic-gate * Set up the arguments expected by bootparamd. 100*0Sstevel@tonic-gate */ 101*0Sstevel@tonic-gate arg.client_address.address_type = IP_ADDR_TYPE; 102*0Sstevel@tonic-gate ipv4_getipaddr(&ipaddr); 103*0Sstevel@tonic-gate ipaddr.s_addr = htonl(ipaddr.s_addr); 104*0Sstevel@tonic-gate bcopy((caddr_t)&ipaddr, 105*0Sstevel@tonic-gate (caddr_t)&arg.client_address.bp_address_u.ip_addr, 106*0Sstevel@tonic-gate sizeof (ipaddr)); 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate /* 109*0Sstevel@tonic-gate * Retransmit/wait for up to resp_wait secs. 110*0Sstevel@tonic-gate */ 111*0Sstevel@tonic-gate rexmit = 0; /* start at default retransmission interval. */ 112*0Sstevel@tonic-gate resp_wait = 16; 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate bp.client_name = &bp_hostname[0]; 115*0Sstevel@tonic-gate bp.domain_name = &bp_domainname[0]; 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate /* 118*0Sstevel@tonic-gate * Do a broadcast call to find a bootparam daemon that 119*0Sstevel@tonic-gate * will tell us our hostname, domainname and any 120*0Sstevel@tonic-gate * router that we have to use to talk to our NFS server. 121*0Sstevel@tonic-gate */ 122*0Sstevel@tonic-gate printed_waiting_msg = 0; 123*0Sstevel@tonic-gate do { 124*0Sstevel@tonic-gate /* 125*0Sstevel@tonic-gate * First try the SunOS portmapper and if no reply is 126*0Sstevel@tonic-gate * received will then try the SVR4 rpcbind. 127*0Sstevel@tonic-gate * Either way, `bootpaddr' will be set to the 128*0Sstevel@tonic-gate * correct address for the bootparamd that responds. 129*0Sstevel@tonic-gate */ 130*0Sstevel@tonic-gate stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, 131*0Sstevel@tonic-gate (rpcvers_t)BOOTPARAMVERS, (rpcproc_t)BOOTPARAMPROC_WHOAMI, 132*0Sstevel@tonic-gate xdr_bp_whoami_arg, (caddr_t)&arg, 133*0Sstevel@tonic-gate xdr_bp_whoami_res, (caddr_t)&bp, rexmit, resp_wait, 134*0Sstevel@tonic-gate &to, &from, AUTH_NONE); 135*0Sstevel@tonic-gate if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { 136*0Sstevel@tonic-gate dprintf(noserver, "whoami"); 137*0Sstevel@tonic-gate printed_waiting_msg = 1; 138*0Sstevel@tonic-gate } 139*0Sstevel@tonic-gate /* 140*0Sstevel@tonic-gate * Retransmission interval for second and subsequent tries. 141*0Sstevel@tonic-gate * We expect first bpmap_rmtcall to retransmit and backoff to 142*0Sstevel@tonic-gate * at least this value. 143*0Sstevel@tonic-gate */ 144*0Sstevel@tonic-gate rexmit = resp_wait; 145*0Sstevel@tonic-gate resp_wait = 0; /* go to default wait now. */ 146*0Sstevel@tonic-gate } while (stat == RPC_TIMEDOUT); 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate if (stat != RPC_SUCCESS) { 149*0Sstevel@tonic-gate dprintf("whoami RPC call failed with rpc status: %d\n", stat); 150*0Sstevel@tonic-gate retval = FALSE; 151*0Sstevel@tonic-gate goto done; 152*0Sstevel@tonic-gate } else { 153*0Sstevel@tonic-gate if (printed_waiting_msg && (boothowto & RB_VERBOSE)) 154*0Sstevel@tonic-gate printf("Bootparam response received\n"); 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate /* Cache responder... We'll send our getfile here... */ 157*0Sstevel@tonic-gate responder.s_addr = from.sin_addr.s_addr; 158*0Sstevel@tonic-gate } 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate namelen = strlen(bp.client_name); 161*0Sstevel@tonic-gate if (namelen > SYS_NMLN) { 162*0Sstevel@tonic-gate dprintf("whoami: hostname too long"); 163*0Sstevel@tonic-gate retval = FALSE; 164*0Sstevel@tonic-gate goto done; 165*0Sstevel@tonic-gate } 166*0Sstevel@tonic-gate if (namelen > 0) { 167*0Sstevel@tonic-gate if (boothowto & RB_VERBOSE) 168*0Sstevel@tonic-gate printf("hostname: %s\n", bp.client_name); 169*0Sstevel@tonic-gate sethostname(bp.client_name, namelen); 170*0Sstevel@tonic-gate } else { 171*0Sstevel@tonic-gate dprintf("whoami: no host name\n"); 172*0Sstevel@tonic-gate retval = FALSE; 173*0Sstevel@tonic-gate goto done; 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate namelen = strlen(bp.domain_name); 177*0Sstevel@tonic-gate if (namelen > SYS_NMLN) { 178*0Sstevel@tonic-gate dprintf("whoami: domainname too long"); 179*0Sstevel@tonic-gate retval = FALSE; 180*0Sstevel@tonic-gate goto done; 181*0Sstevel@tonic-gate } 182*0Sstevel@tonic-gate if (namelen > 0) 183*0Sstevel@tonic-gate if (boothowto & RB_VERBOSE) 184*0Sstevel@tonic-gate printf("domainname: %s\n", bp.domain_name); 185*0Sstevel@tonic-gate else 186*0Sstevel@tonic-gate dprintf("whoami: no domain name\n"); 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate if (bp.router_address.address_type == IP_ADDR_TYPE) { 189*0Sstevel@tonic-gate bcopy((caddr_t)&bp.router_address.bp_address_u.ip_addr, 190*0Sstevel@tonic-gate (caddr_t)&ipaddr, sizeof (ipaddr)); 191*0Sstevel@tonic-gate if (ntohl(ipaddr.s_addr) != INADDR_ANY) { 192*0Sstevel@tonic-gate dprintf("whoami: Router ip is: %s\n", 193*0Sstevel@tonic-gate inet_ntoa(ipaddr)); 194*0Sstevel@tonic-gate /* ipv4_route expects IP addresses in network order */ 195*0Sstevel@tonic-gate (void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL, 196*0Sstevel@tonic-gate &ipaddr); 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate } else 199*0Sstevel@tonic-gate dprintf("whoami: unknown gateway addr family %d\n", 200*0Sstevel@tonic-gate bp.router_address.address_type); 201*0Sstevel@tonic-gate done: 202*0Sstevel@tonic-gate return (retval); 203*0Sstevel@tonic-gate } 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate /* 206*0Sstevel@tonic-gate * Returns: 207*0Sstevel@tonic-gate * 1) The ascii form of our root servers name in `server_name'. 208*0Sstevel@tonic-gate * 2) Pathname of our root on the server in `server_path'. 209*0Sstevel@tonic-gate * 210*0Sstevel@tonic-gate * NOTE: it's ok for getfile() to do dynamic allocation - it's only 211*0Sstevel@tonic-gate * used locally, then freed. If the server address returned from the 212*0Sstevel@tonic-gate * getfile call is different from our current destination address, 213*0Sstevel@tonic-gate * reset destination IP address to the new value. 214*0Sstevel@tonic-gate */ 215*0Sstevel@tonic-gate bool_t 216*0Sstevel@tonic-gate getfile(char *fileid, char *server_name, struct in_addr *server_ip, 217*0Sstevel@tonic-gate char *server_path) 218*0Sstevel@tonic-gate { 219*0Sstevel@tonic-gate struct bp_getfile_arg arg; 220*0Sstevel@tonic-gate struct bp_getfile_res res; 221*0Sstevel@tonic-gate enum clnt_stat stat; 222*0Sstevel@tonic-gate struct sockaddr_in to, from; 223*0Sstevel@tonic-gate int rexmit; 224*0Sstevel@tonic-gate int wait; 225*0Sstevel@tonic-gate uint_t max_retries = 0xFFFFFFFF; 226*0Sstevel@tonic-gate int def_rexmit = 0; 227*0Sstevel@tonic-gate int def_wait = 32; 228*0Sstevel@tonic-gate int printed_waiting_msg; 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate /* 231*0Sstevel@tonic-gate * For non-root requests, set a smaller timeout 232*0Sstevel@tonic-gate */ 233*0Sstevel@tonic-gate if (strcmp(fileid, "root") != 0) { 234*0Sstevel@tonic-gate /* 235*0Sstevel@tonic-gate * Only send one request per call 236*0Sstevel@tonic-gate */ 237*0Sstevel@tonic-gate def_wait = GETFILE_BTIMEO; 238*0Sstevel@tonic-gate def_rexmit = GETFILE_BTIMEO; 239*0Sstevel@tonic-gate max_retries = GETFILE_BRETRIES; 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate arg.client_name = bp.client_name; 243*0Sstevel@tonic-gate arg.file_id = fileid; 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate res.server_name = (bp_machine_name_t)bkmem_zalloc(SYS_NMLN + 1); 246*0Sstevel@tonic-gate res.server_path = (bp_path_t)bkmem_zalloc(SYS_NMLN + 1); 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate if (res.server_name == NULL || res.server_path == NULL) { 249*0Sstevel@tonic-gate dprintf("getfile: rpc_call failed: No memory\n"); 250*0Sstevel@tonic-gate errno = ENOMEM; 251*0Sstevel@tonic-gate if (res.server_name != NULL) 252*0Sstevel@tonic-gate bkmem_free(res.server_name, SYS_NMLN + 1); 253*0Sstevel@tonic-gate if (res.server_path != NULL) 254*0Sstevel@tonic-gate bkmem_free(res.server_path, SYS_NMLN + 1); 255*0Sstevel@tonic-gate return (FALSE); 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate to.sin_family = AF_INET; 259*0Sstevel@tonic-gate to.sin_addr.s_addr = responder.s_addr; 260*0Sstevel@tonic-gate to.sin_port = htons(0); 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate /* 263*0Sstevel@tonic-gate * Our addressing information was filled in by the call to 264*0Sstevel@tonic-gate * whoami(), so now send an rpc message to the 265*0Sstevel@tonic-gate * bootparam daemon requesting our server information. 266*0Sstevel@tonic-gate * 267*0Sstevel@tonic-gate * Wait only 32 secs for rpc_call to succeed. 268*0Sstevel@tonic-gate */ 269*0Sstevel@tonic-gate rexmit = def_rexmit; 270*0Sstevel@tonic-gate wait = def_wait; 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate stat = brpc_call((rpcprog_t)BOOTPARAMPROG, (rpcvers_t)BOOTPARAMVERS, 273*0Sstevel@tonic-gate (rpcproc_t)BOOTPARAMPROC_GETFILE, xdr_bp_getfile_arg, (caddr_t)&arg, 274*0Sstevel@tonic-gate xdr_bp_getfile_res, (caddr_t)&res, rexmit, wait, 275*0Sstevel@tonic-gate &to, &from, AUTH_NONE); 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate if (stat == RPC_TIMEDOUT) { 278*0Sstevel@tonic-gate /* 279*0Sstevel@tonic-gate * The server that answered the whoami doesn't 280*0Sstevel@tonic-gate * answer our getfile. Broadcast the call to all. Keep 281*0Sstevel@tonic-gate * trying forever. Set up for limited broadcast. 282*0Sstevel@tonic-gate */ 283*0Sstevel@tonic-gate to.sin_addr.s_addr = htonl(INADDR_BROADCAST); 284*0Sstevel@tonic-gate to.sin_port = htons(0); 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate rexmit = def_rexmit; /* use default rexmit interval */ 287*0Sstevel@tonic-gate wait = def_wait; 288*0Sstevel@tonic-gate printed_waiting_msg = 0; 289*0Sstevel@tonic-gate do { 290*0Sstevel@tonic-gate /* 291*0Sstevel@tonic-gate * Limit the number of retries 292*0Sstevel@tonic-gate */ 293*0Sstevel@tonic-gate if (max_retries-- == 0) 294*0Sstevel@tonic-gate break; 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate stat = bpmap_rmtcall((rpcprog_t)BOOTPARAMPROG, 297*0Sstevel@tonic-gate (rpcvers_t)BOOTPARAMVERS, 298*0Sstevel@tonic-gate (rpcproc_t)BOOTPARAMPROC_GETFILE, 299*0Sstevel@tonic-gate xdr_bp_getfile_arg, (caddr_t)&arg, 300*0Sstevel@tonic-gate xdr_bp_getfile_res, (caddr_t)&res, rexmit, 301*0Sstevel@tonic-gate wait, &to, &from, AUTH_NONE); 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate if (stat == RPC_SUCCESS) { 304*0Sstevel@tonic-gate /* 305*0Sstevel@tonic-gate * set our destination addresses to 306*0Sstevel@tonic-gate * those of the server that responded. 307*0Sstevel@tonic-gate * It's probably our server, and we 308*0Sstevel@tonic-gate * can thus save arping for no reason later. 309*0Sstevel@tonic-gate */ 310*0Sstevel@tonic-gate responder.s_addr = from.sin_addr.s_addr; 311*0Sstevel@tonic-gate if (printed_waiting_msg && 312*0Sstevel@tonic-gate (boothowto & RB_VERBOSE)) { 313*0Sstevel@tonic-gate printf( 314*0Sstevel@tonic-gate "Bootparam response received.\n"); 315*0Sstevel@tonic-gate } 316*0Sstevel@tonic-gate break; 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate if (stat == RPC_TIMEDOUT && !printed_waiting_msg) { 319*0Sstevel@tonic-gate dprintf(noserver, "getfile"); 320*0Sstevel@tonic-gate printed_waiting_msg = 1; 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate /* 323*0Sstevel@tonic-gate * Retransmission interval for second and 324*0Sstevel@tonic-gate * subsequent tries. We expect first bpmap_rmtcall 325*0Sstevel@tonic-gate * to retransmit and backoff to at least this 326*0Sstevel@tonic-gate * value. 327*0Sstevel@tonic-gate */ 328*0Sstevel@tonic-gate rexmit = wait; 329*0Sstevel@tonic-gate wait = def_wait; 330*0Sstevel@tonic-gate } while (stat == RPC_TIMEDOUT); 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate if (stat == RPC_SUCCESS) { 334*0Sstevel@tonic-gate /* got the goods */ 335*0Sstevel@tonic-gate bcopy(res.server_name, server_name, strlen(res.server_name)); 336*0Sstevel@tonic-gate bcopy(res.server_path, server_path, strlen(res.server_path)); 337*0Sstevel@tonic-gate switch (res.server_address.address_type) { 338*0Sstevel@tonic-gate case IP_ADDR_TYPE: 339*0Sstevel@tonic-gate /* 340*0Sstevel@tonic-gate * server_address is where we will get our root 341*0Sstevel@tonic-gate * from. Replace destination entries in address if 342*0Sstevel@tonic-gate * necessary. 343*0Sstevel@tonic-gate */ 344*0Sstevel@tonic-gate bcopy((caddr_t)&res.server_address.bp_address_u.ip_addr, 345*0Sstevel@tonic-gate (caddr_t)server_ip, sizeof (struct in_addr)); 346*0Sstevel@tonic-gate break; 347*0Sstevel@tonic-gate default: 348*0Sstevel@tonic-gate dprintf("getfile: unknown address type %d\n", 349*0Sstevel@tonic-gate res.server_address.address_type); 350*0Sstevel@tonic-gate server_ip->s_addr = htonl(INADDR_ANY); 351*0Sstevel@tonic-gate bkmem_free(res.server_name, SYS_NMLN + 1); 352*0Sstevel@tonic-gate bkmem_free(res.server_path, SYS_NMLN + 1); 353*0Sstevel@tonic-gate return (FALSE); 354*0Sstevel@tonic-gate } 355*0Sstevel@tonic-gate } else { 356*0Sstevel@tonic-gate dprintf("getfile: rpc_call failed.\n"); 357*0Sstevel@tonic-gate bkmem_free(res.server_name, SYS_NMLN + 1); 358*0Sstevel@tonic-gate bkmem_free(res.server_path, SYS_NMLN + 1); 359*0Sstevel@tonic-gate return (FALSE); 360*0Sstevel@tonic-gate } 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate bkmem_free(res.server_name, SYS_NMLN + 1); 363*0Sstevel@tonic-gate bkmem_free(res.server_path, SYS_NMLN + 1); 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate return (TRUE); 366*0Sstevel@tonic-gate } 367