xref: /titanic_41/usr/src/uts/common/os/iscsiboot_prop.c (revision dedec472759b1a1a25044d504201ef59ccbffb56)
16cefaae1SJack Meng /*
26cefaae1SJack Meng  * CDDL HEADER START
36cefaae1SJack Meng  *
46cefaae1SJack Meng  * The contents of this file are subject to the terms of the
56cefaae1SJack Meng  * Common Development and Distribution License (the "License").
66cefaae1SJack Meng  * You may not use this file except in compliance with the License.
76cefaae1SJack Meng  *
86cefaae1SJack Meng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96cefaae1SJack Meng  * or http://www.opensolaris.org/os/licensing.
106cefaae1SJack Meng  * See the License for the specific language governing permissions
116cefaae1SJack Meng  * and limitations under the License.
126cefaae1SJack Meng  *
136cefaae1SJack Meng  * When distributing Covered Code, include this CDDL HEADER in each
146cefaae1SJack Meng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156cefaae1SJack Meng  * If applicable, add the following below this CDDL HEADER, with the
166cefaae1SJack Meng  * fields enclosed by brackets "[]" replaced with your own identifying
176cefaae1SJack Meng  * information: Portions Copyright [yyyy] [name of copyright owner]
186cefaae1SJack Meng  *
196cefaae1SJack Meng  * CDDL HEADER END
206cefaae1SJack Meng  */
216cefaae1SJack Meng 
226cefaae1SJack Meng /*
23*dedec472SJack Meng  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
246cefaae1SJack Meng  * Use is subject to license terms.
256cefaae1SJack Meng  */
266cefaae1SJack Meng 
276cefaae1SJack Meng /*
286cefaae1SJack Meng  * Commmon routines, handling iscsi boot props
296cefaae1SJack Meng  */
306cefaae1SJack Meng 
316cefaae1SJack Meng #include <sys/types.h>
326cefaae1SJack Meng #include <sys/bootprops.h>
336cefaae1SJack Meng #include <sys/cmn_err.h>
346cefaae1SJack Meng #include <sys/socket.h>
356cefaae1SJack Meng #include <sys/kmem.h>
366cefaae1SJack Meng #include <netinet/in.h>
376cefaae1SJack Meng 
386cefaae1SJack Meng extern void *memset(void *s, int c, size_t n);
396cefaae1SJack Meng extern int memcmp(const void *s1, const void *s2, size_t n);
406cefaae1SJack Meng extern void bcopy(const void *s1, void *s2, size_t n);
416cefaae1SJack Meng extern size_t strlen(const char *s);
426cefaae1SJack Meng static void kinet_ntoa(char *buf, void *in, int af);
436cefaae1SJack Meng extern ib_boot_prop_t *iscsiboot_prop;
446cefaae1SJack Meng 
456cefaae1SJack Meng int  iscsi_print_bootprop	=	0;
466cefaae1SJack Meng 
476cefaae1SJack Meng #define	ISCSI_BOOTPROP_BUFLEN	256
486cefaae1SJack Meng 
496cefaae1SJack Meng #ifndef	NULL
506cefaae1SJack Meng #define	NULL	0
516cefaae1SJack Meng #endif
526cefaae1SJack Meng 
53*dedec472SJack Meng static int replace_sp_c(unsigned char *dst, unsigned char *source, size_t n);
54*dedec472SJack Meng 
556cefaae1SJack Meng static void
iscsi_bootprop_print(int level,char * str)566cefaae1SJack Meng iscsi_bootprop_print(int level, char *str)
576cefaae1SJack Meng {
586cefaae1SJack Meng 	if (str == NULL) {
596cefaae1SJack Meng 		return;
606cefaae1SJack Meng 	}
616cefaae1SJack Meng 	if (iscsi_print_bootprop == 1) {
626cefaae1SJack Meng 		cmn_err(level, "%s", str);
636cefaae1SJack Meng 	}
646cefaae1SJack Meng }
656cefaae1SJack Meng 
666cefaae1SJack Meng static void
iscsi_print_initiator_property(ib_ini_prop_t * ibinitp)676cefaae1SJack Meng iscsi_print_initiator_property(ib_ini_prop_t *ibinitp)
686cefaae1SJack Meng {
696cefaae1SJack Meng 	char	outbuf[ISCSI_BOOTPROP_BUFLEN] = {0};
706cefaae1SJack Meng 
716cefaae1SJack Meng 	if (ibinitp == NULL) {
726cefaae1SJack Meng 		return;
736cefaae1SJack Meng 	}
746cefaae1SJack Meng 
756cefaae1SJack Meng 	if (ibinitp->ini_name != NULL) {
766cefaae1SJack Meng 		(void) sprintf(outbuf,
776cefaae1SJack Meng 		    "Initiator Name : %s\n",
786cefaae1SJack Meng 		    ibinitp->ini_name);
796cefaae1SJack Meng 		iscsi_bootprop_print(CE_CONT, outbuf);
806cefaae1SJack Meng 	}
816cefaae1SJack Meng 
826cefaae1SJack Meng 	if (ibinitp->ini_chap_name != NULL) {
836cefaae1SJack Meng 		(void) memset(outbuf, 0, ISCSI_BOOTPROP_BUFLEN);
846cefaae1SJack Meng 		(void) sprintf(outbuf,
856cefaae1SJack Meng 		    "Initiator CHAP Name  : %s\n",
866cefaae1SJack Meng 		    ibinitp->ini_chap_name);
876cefaae1SJack Meng 
886cefaae1SJack Meng 		iscsi_bootprop_print(CE_CONT, outbuf);
896cefaae1SJack Meng 	}
906cefaae1SJack Meng }
916cefaae1SJack Meng 
926cefaae1SJack Meng static void
iscsi_print_nic_property(ib_nic_prop_t * nicp)936cefaae1SJack Meng iscsi_print_nic_property(ib_nic_prop_t *nicp)
946cefaae1SJack Meng {
956cefaae1SJack Meng 	char	outbuf[ISCSI_BOOTPROP_BUFLEN] = {0};
966cefaae1SJack Meng 	char	ipaddr[50]  =	{0};
976cefaae1SJack Meng 	int	n	    =	0;
986cefaae1SJack Meng 
996cefaae1SJack Meng 	if (nicp == NULL) {
1006cefaae1SJack Meng 		return;
1016cefaae1SJack Meng 	}
1026cefaae1SJack Meng 
1036cefaae1SJack Meng 	kinet_ntoa(ipaddr, &nicp->nic_ip_u, nicp->sin_family);
1046cefaae1SJack Meng 	n = snprintf(outbuf, ISCSI_BOOTPROP_BUFLEN,
1056cefaae1SJack Meng 	    "Local IP addr  : %s\n", ipaddr);
1066cefaae1SJack Meng 
1076cefaae1SJack Meng 	(void) memset(ipaddr, 0, 50);
1086cefaae1SJack Meng 	kinet_ntoa(ipaddr, &nicp->nic_gw_u, nicp->sin_family);
1096cefaae1SJack Meng 	n = n + snprintf(outbuf + n, ISCSI_BOOTPROP_BUFLEN - n,
1106cefaae1SJack Meng 	    "Local gateway  : %s\n", ipaddr);
1116cefaae1SJack Meng 
1126cefaae1SJack Meng 	(void) memset(ipaddr, 0, 50);
1136cefaae1SJack Meng 	kinet_ntoa(ipaddr, &nicp->nic_dhcp_u, nicp->sin_family);
1146cefaae1SJack Meng 	n = n + snprintf(outbuf + n, ISCSI_BOOTPROP_BUFLEN - n,
1156cefaae1SJack Meng 	    "Local DHCP     : %s\n", ipaddr);
1166cefaae1SJack Meng 
1176cefaae1SJack Meng 	(void) snprintf(outbuf + n, ISCSI_BOOTPROP_BUFLEN - n,
1186cefaae1SJack Meng 	    "Local MAC      : %02x:%02x:%02x:%02x:%02x:%02x\n",
1196cefaae1SJack Meng 	    nicp->nic_mac[0],
1206cefaae1SJack Meng 	    nicp->nic_mac[1],
1216cefaae1SJack Meng 	    nicp->nic_mac[2],
1226cefaae1SJack Meng 	    nicp->nic_mac[3],
1236cefaae1SJack Meng 	    nicp->nic_mac[4],
1246cefaae1SJack Meng 	    nicp->nic_mac[5]);
1256cefaae1SJack Meng 
1266cefaae1SJack Meng 	iscsi_bootprop_print(CE_CONT, outbuf);
1276cefaae1SJack Meng }
1286cefaae1SJack Meng 
1296cefaae1SJack Meng static void
iscsi_print_tgt_property(ib_tgt_prop_t * itgtp)1306cefaae1SJack Meng iscsi_print_tgt_property(ib_tgt_prop_t *itgtp)
1316cefaae1SJack Meng {
1326cefaae1SJack Meng 	char	outbuf[ISCSI_BOOTPROP_BUFLEN] = {0};
1336cefaae1SJack Meng 	char	ipaddr[50]  =	{0};
1346cefaae1SJack Meng 
1356cefaae1SJack Meng 	if (itgtp == NULL) {
1366cefaae1SJack Meng 		return;
1376cefaae1SJack Meng 	}
1386cefaae1SJack Meng 
1396cefaae1SJack Meng 	if (itgtp->tgt_name != NULL) {
1406cefaae1SJack Meng 		(void) memset(outbuf, 0, ISCSI_BOOTPROP_BUFLEN);
1416cefaae1SJack Meng 		(void) sprintf(outbuf,
1426cefaae1SJack Meng 		    "Target Name    : %s\n",
1436cefaae1SJack Meng 		    itgtp->tgt_name);
1446cefaae1SJack Meng 		iscsi_bootprop_print(CE_CONT, outbuf);
1456cefaae1SJack Meng 	}
1466cefaae1SJack Meng 
1476cefaae1SJack Meng 	kinet_ntoa(ipaddr, &itgtp->tgt_ip_u, itgtp->sin_family);
1486cefaae1SJack Meng 	(void) sprintf(outbuf,
1496cefaae1SJack Meng 	    "Target IP      : %s\n"
1506cefaae1SJack Meng 	    "Target Port    : %d\n"
1516cefaae1SJack Meng 	    "Boot LUN       : %02x%02x-%02x%02x-%02x%02x-%02x%02x\n",
1526cefaae1SJack Meng 	    ipaddr,
1536cefaae1SJack Meng 	    itgtp->tgt_port,
1546cefaae1SJack Meng 	    itgtp->tgt_boot_lun[0],
1556cefaae1SJack Meng 	    itgtp->tgt_boot_lun[1],
1566cefaae1SJack Meng 	    itgtp->tgt_boot_lun[2],
1576cefaae1SJack Meng 	    itgtp->tgt_boot_lun[3],
1586cefaae1SJack Meng 	    itgtp->tgt_boot_lun[4],
1596cefaae1SJack Meng 	    itgtp->tgt_boot_lun[5],
1606cefaae1SJack Meng 	    itgtp->tgt_boot_lun[6],
1616cefaae1SJack Meng 	    itgtp->tgt_boot_lun[7]);
1626cefaae1SJack Meng 	iscsi_bootprop_print(CE_CONT, outbuf);
1636cefaae1SJack Meng 
1646cefaae1SJack Meng 	if (itgtp->tgt_chap_name != NULL) {
1656cefaae1SJack Meng 		(void) memset(outbuf, 0, ISCSI_BOOTPROP_BUFLEN);
1666cefaae1SJack Meng 		(void) sprintf(outbuf,
1676cefaae1SJack Meng 		    "CHAP Name      : %s\n",
1686cefaae1SJack Meng 		    itgtp->tgt_chap_name);
1696cefaae1SJack Meng 		iscsi_bootprop_print(CE_CONT, outbuf);
1706cefaae1SJack Meng 	}
1716cefaae1SJack Meng }
1726cefaae1SJack Meng 
1736cefaae1SJack Meng void
iscsi_print_boot_property()1746cefaae1SJack Meng iscsi_print_boot_property()
1756cefaae1SJack Meng {
1766cefaae1SJack Meng 	if (iscsiboot_prop == NULL) {
1776cefaae1SJack Meng 		return;
1786cefaae1SJack Meng 	}
1796cefaae1SJack Meng 
1806cefaae1SJack Meng 	iscsi_print_initiator_property(
1816cefaae1SJack Meng 	    &iscsiboot_prop->boot_init);
1826cefaae1SJack Meng 
1836cefaae1SJack Meng 	iscsi_print_nic_property(&iscsiboot_prop->boot_nic);
1846cefaae1SJack Meng 
1856cefaae1SJack Meng 	iscsi_print_tgt_property(&iscsiboot_prop->boot_tgt);
1866cefaae1SJack Meng }
1876cefaae1SJack Meng 
1886cefaae1SJack Meng void
iscsi_boot_free_ini(ib_ini_prop_t * init)1896cefaae1SJack Meng iscsi_boot_free_ini(ib_ini_prop_t *init)
1906cefaae1SJack Meng {
1916cefaae1SJack Meng 	if (init == NULL) {
1926cefaae1SJack Meng 		return;
1936cefaae1SJack Meng 	}
1946cefaae1SJack Meng 
1956cefaae1SJack Meng 	if (init->ini_name != NULL) {
196*dedec472SJack Meng 		kmem_free(init->ini_name, init->ini_name_len);
1976cefaae1SJack Meng 		init->ini_name = NULL;
198*dedec472SJack Meng 		init->ini_name_len = 0;
1996cefaae1SJack Meng 	}
2006cefaae1SJack Meng 	if (init->ini_chap_name != NULL) {
2016cefaae1SJack Meng 		kmem_free(init->ini_chap_name,
202*dedec472SJack Meng 		    init->ini_chap_name_len);
2036cefaae1SJack Meng 		init->ini_chap_name = NULL;
204*dedec472SJack Meng 		init->ini_chap_name_len = 0;
2056cefaae1SJack Meng 	}
2066cefaae1SJack Meng 	if (init->ini_chap_sec != NULL) {
2076cefaae1SJack Meng 		kmem_free(init->ini_chap_sec,
208*dedec472SJack Meng 		    init->ini_chap_sec_len);
2096cefaae1SJack Meng 		init->ini_chap_sec = NULL;
210*dedec472SJack Meng 		init->ini_chap_sec_len = 0;
2116cefaae1SJack Meng 	}
2126cefaae1SJack Meng }
2136cefaae1SJack Meng 
2146cefaae1SJack Meng void
iscsi_boot_free_tgt(ib_tgt_prop_t * target)2156cefaae1SJack Meng iscsi_boot_free_tgt(ib_tgt_prop_t *target)
2166cefaae1SJack Meng {
2176cefaae1SJack Meng 	if (target == NULL) {
2186cefaae1SJack Meng 		return;
2196cefaae1SJack Meng 	}
2206cefaae1SJack Meng 
2216cefaae1SJack Meng 	if (target->tgt_name != NULL) {
2226cefaae1SJack Meng 		kmem_free(target->tgt_name,
223*dedec472SJack Meng 		    target->tgt_name_len);
2246cefaae1SJack Meng 		target->tgt_name = NULL;
225*dedec472SJack Meng 		target->tgt_name_len = 0;
2266cefaae1SJack Meng 	}
2276cefaae1SJack Meng 	if (target->tgt_chap_name != NULL) {
2286cefaae1SJack Meng 		kmem_free(target->tgt_chap_name,
229*dedec472SJack Meng 		    target->tgt_chap_name_len);
2306cefaae1SJack Meng 		target->tgt_chap_name = NULL;
231*dedec472SJack Meng 		target->tgt_chap_name_len = 0;
2326cefaae1SJack Meng 	}
2336cefaae1SJack Meng 	if (target->tgt_chap_sec != NULL) {
2346cefaae1SJack Meng 		kmem_free(target->tgt_chap_sec,
235*dedec472SJack Meng 		    target->tgt_chap_sec_len);
2366cefaae1SJack Meng 		target->tgt_chap_sec = NULL;
237*dedec472SJack Meng 		target->tgt_chap_sec_len = 0;
238*dedec472SJack Meng 	}
239*dedec472SJack Meng 	if (target->tgt_boot_par != NULL) {
240*dedec472SJack Meng 		kmem_free(target->tgt_boot_par,
241*dedec472SJack Meng 		    target->tgt_boot_par_len);
242*dedec472SJack Meng 		target->tgt_boot_par = NULL;
243*dedec472SJack Meng 		target->tgt_boot_par_len = 0;
2446cefaae1SJack Meng 	}
2456cefaae1SJack Meng }
2466cefaae1SJack Meng 
2476cefaae1SJack Meng /*
2486cefaae1SJack Meng  * Free the memory used by boot property.
2496cefaae1SJack Meng  */
2506cefaae1SJack Meng void
iscsi_boot_prop_free()2516cefaae1SJack Meng iscsi_boot_prop_free()
2526cefaae1SJack Meng {
2536cefaae1SJack Meng 	ib_boot_prop_t	*tmp;
2546cefaae1SJack Meng 
2556cefaae1SJack Meng 	if (iscsiboot_prop == NULL) {
2566cefaae1SJack Meng 		return;
2576cefaae1SJack Meng 	}
2586cefaae1SJack Meng 	tmp = iscsiboot_prop;
2596cefaae1SJack Meng 	iscsiboot_prop = NULL;
2606cefaae1SJack Meng 	iscsi_boot_free_ini(&(tmp->boot_init));
2616cefaae1SJack Meng 	iscsi_boot_free_tgt(&(tmp->boot_tgt));
2626cefaae1SJack Meng }
2636cefaae1SJack Meng 
2646cefaae1SJack Meng static void
kinet_ntoa(char * buf,void * in,int af)2656cefaae1SJack Meng kinet_ntoa(char *buf, void *in, int af)
2666cefaae1SJack Meng {
2676cefaae1SJack Meng 	unsigned char   *p =    NULL;
2686cefaae1SJack Meng 	int	i = 0;
2696cefaae1SJack Meng 
2706cefaae1SJack Meng 	if (buf == NULL || in == NULL) {
2716cefaae1SJack Meng 		return;
2726cefaae1SJack Meng 	}
2736cefaae1SJack Meng 	p = (unsigned char *)in;
2746cefaae1SJack Meng 	if (af == AF_INET) {
2756cefaae1SJack Meng 		(void) sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
2766cefaae1SJack Meng 	} else {
2776cefaae1SJack Meng 		for (i = 0; i < 14; i = i + 2) {
2786cefaae1SJack Meng 			(void) sprintf(buf, "%02x%02x:", p[i], p[i+1]);
2796cefaae1SJack Meng 			buf = buf + 5;
2806cefaae1SJack Meng 		}
2816cefaae1SJack Meng 		(void) sprintf(buf, "%02x%02x", p[i], p[i+1]);
2826cefaae1SJack Meng 	}
2836cefaae1SJack Meng }
284*dedec472SJack Meng 
285*dedec472SJack Meng #ifndef	BO_MAXOBJNAME
286*dedec472SJack Meng #define	BO_MAXOBJNAME	256
287*dedec472SJack Meng #endif
288*dedec472SJack Meng 
289*dedec472SJack Meng #ifndef ISCSI_BOOT_ISID
290*dedec472SJack Meng #define	ISCSI_BOOT_ISID	"0000"
291*dedec472SJack Meng #endif
292*dedec472SJack Meng 
293*dedec472SJack Meng /*
294*dedec472SJack Meng  * Generate the 'ssd' bootpath of an iSCSI boot device
295*dedec472SJack Meng  * The caller is responsible to alloc the buf with BO_MAXOBJNAME length
296*dedec472SJack Meng  */
297*dedec472SJack Meng void
get_iscsi_bootpath_vhci(char * bootpath)298*dedec472SJack Meng get_iscsi_bootpath_vhci(char *bootpath)
299*dedec472SJack Meng {
300*dedec472SJack Meng 	uint16_t	*lun_num;
301*dedec472SJack Meng 
302*dedec472SJack Meng 	if (iscsiboot_prop == NULL)
303*dedec472SJack Meng 		ld_ib_prop();
304*dedec472SJack Meng 	if (iscsiboot_prop == NULL)
305*dedec472SJack Meng 		return;
306*dedec472SJack Meng 	lun_num = (uint16_t *)(&iscsiboot_prop->boot_tgt.tgt_boot_lun[0]);
307*dedec472SJack Meng 	(void) snprintf(bootpath, BO_MAXOBJNAME, "/iscsi/ssd@%s%s%04X,%d:%s",
308*dedec472SJack Meng 	    ISCSI_BOOT_ISID, iscsiboot_prop->boot_tgt.tgt_name,
309*dedec472SJack Meng 	    iscsiboot_prop->boot_tgt.tgt_tpgt, lun_num[0],
310*dedec472SJack Meng 	    iscsiboot_prop->boot_tgt.tgt_boot_par);
311*dedec472SJack Meng }
312*dedec472SJack Meng 
313*dedec472SJack Meng /*
314*dedec472SJack Meng  * Generate the 'disk' bootpath of an iSCSI boot device
315*dedec472SJack Meng  * The caller is responsible to alloc the buf with BO_MAXOBJNAME length
316*dedec472SJack Meng  */
317*dedec472SJack Meng void
get_iscsi_bootpath_phy(char * bootpath)318*dedec472SJack Meng get_iscsi_bootpath_phy(char *bootpath)
319*dedec472SJack Meng {
320*dedec472SJack Meng 	uint16_t	lun_num		= 0;
321*dedec472SJack Meng 	uchar_t		replaced_name[BO_MAXOBJNAME] = {0};
322*dedec472SJack Meng 
323*dedec472SJack Meng 	if (iscsiboot_prop == NULL)
324*dedec472SJack Meng 		ld_ib_prop();
325*dedec472SJack Meng 	if (iscsiboot_prop == NULL)
326*dedec472SJack Meng 		return;
327*dedec472SJack Meng 	if (replace_sp_c(replaced_name, iscsiboot_prop->boot_tgt.tgt_name,
328*dedec472SJack Meng 	    iscsiboot_prop->boot_tgt.tgt_name_len) != 0) {
329*dedec472SJack Meng 		return;
330*dedec472SJack Meng 	}
331*dedec472SJack Meng 	lun_num = *(uint16_t *)(&iscsiboot_prop->boot_tgt.tgt_boot_lun[0]);
332*dedec472SJack Meng 	(void) snprintf(bootpath, BO_MAXOBJNAME, "/iscsi/disk@%s%s%04X,%d:%s",
333*dedec472SJack Meng 	    ISCSI_BOOT_ISID, replaced_name, iscsiboot_prop->boot_tgt.tgt_tpgt,
334*dedec472SJack Meng 	    lun_num, iscsiboot_prop->boot_tgt.tgt_boot_par);
335*dedec472SJack Meng }
336*dedec472SJack Meng 
replace_sp_c(unsigned char * dst,unsigned char * source,size_t n)337*dedec472SJack Meng static int replace_sp_c(unsigned char *dst, unsigned char *source, size_t n)
338*dedec472SJack Meng {
339*dedec472SJack Meng 	unsigned char	*p	= NULL;
340*dedec472SJack Meng 	int		i	= 0;
341*dedec472SJack Meng 
342*dedec472SJack Meng 	if (source == NULL || dst == NULL || n == 0) {
343*dedec472SJack Meng 		return (-1);
344*dedec472SJack Meng 	}
345*dedec472SJack Meng 
346*dedec472SJack Meng 	for (p = source; *p != '\0'; p++, i++) {
347*dedec472SJack Meng 		if (i >= n) {
348*dedec472SJack Meng 			return (-1);
349*dedec472SJack Meng 		}
350*dedec472SJack Meng 		switch (*p) {
351*dedec472SJack Meng 		case ':':
352*dedec472SJack Meng 			*dst = '%';
353*dedec472SJack Meng 			dst++;
354*dedec472SJack Meng 			*dst = '3';
355*dedec472SJack Meng 			dst++;
356*dedec472SJack Meng 			*dst = 'A';
357*dedec472SJack Meng 			dst++;
358*dedec472SJack Meng 			break;
359*dedec472SJack Meng 		case ' ':
360*dedec472SJack Meng 			*dst = '%';
361*dedec472SJack Meng 			dst++;
362*dedec472SJack Meng 			*dst = '2';
363*dedec472SJack Meng 			dst++;
364*dedec472SJack Meng 			*dst = '0';
365*dedec472SJack Meng 			dst++;
366*dedec472SJack Meng 			break;
367*dedec472SJack Meng 		case '@':
368*dedec472SJack Meng 			*dst = '%';
369*dedec472SJack Meng 			dst++;
370*dedec472SJack Meng 			*dst = '4';
371*dedec472SJack Meng 			dst++;
372*dedec472SJack Meng 			*dst = '0';
373*dedec472SJack Meng 			dst++;
374*dedec472SJack Meng 			break;
375*dedec472SJack Meng 		case '/':
376*dedec472SJack Meng 			*dst = '%';
377*dedec472SJack Meng 			dst++;
378*dedec472SJack Meng 			*dst = '2';
379*dedec472SJack Meng 			dst++;
380*dedec472SJack Meng 			*dst = 'F';
381*dedec472SJack Meng 			dst++;
382*dedec472SJack Meng 			break;
383*dedec472SJack Meng 		default:
384*dedec472SJack Meng 			*dst = *p;
385*dedec472SJack Meng 			dst++;
386*dedec472SJack Meng 		}
387*dedec472SJack Meng 	}
388*dedec472SJack Meng 	*dst = '\0';
389*dedec472SJack Meng 
390*dedec472SJack Meng 	return (0);
391*dedec472SJack Meng }
392