1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* Copyright (c) 1990 Mentat Inc. */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/systm.h> 31 #include <inet/common.h> 32 #include <sys/stream.h> 33 #include <sys/stropts.h> 34 #include <inet/mi.h> 35 #include <sys/sunddi.h> 36 #include <sys/cmn_err.h> 37 #include <sys/policy.h> 38 #include <inet/nd.h> 39 40 /* Free the table pointed to by 'ndp' */ 41 void 42 nd_free(caddr_t *nd_pparam) 43 { 44 ND *nd; 45 46 if ((nd = (ND *)(*nd_pparam)) != NULL) { 47 if (nd->nd_tbl) 48 mi_free((char *)nd->nd_tbl); 49 mi_free((char *)nd); 50 *nd_pparam = NULL; 51 } 52 } 53 54 int 55 nd_getset(queue_t *q, caddr_t nd_param, MBLKP mp) 56 { 57 int err; 58 IOCP iocp; 59 MBLKP mp1; 60 ND *nd; 61 NDE *nde; 62 char *valp; 63 long avail; 64 65 if (!nd_param) 66 return (B_FALSE); 67 nd = (ND *)nd_param; 68 iocp = (IOCP)mp->b_rptr; 69 if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) { 70 mp->b_datap->db_type = M_IOCACK; 71 iocp->ioc_count = 0; 72 iocp->ioc_error = EINVAL; 73 return (B_TRUE); 74 } 75 /* 76 * NOTE - logic throughout nd_xxx assumes single data block for ioctl. 77 * However, existing code sends in some big buffers. 78 */ 79 avail = iocp->ioc_count; 80 if (mp1->b_cont) { 81 freemsg(mp1->b_cont); 82 mp1->b_cont = NULL; 83 } 84 85 mp1->b_datap->db_lim[-1] = '\0'; /* Force null termination */ 86 valp = (char *)mp1->b_rptr; 87 for (nde = nd->nd_tbl; ; nde++) { 88 if (!nde->nde_name) 89 return (B_FALSE); 90 if (mi_strcmp(nde->nde_name, valp) == 0) 91 break; 92 } 93 err = EINVAL; 94 while (*valp++) 95 noop; 96 if (!*valp || valp >= (char *)mp1->b_wptr) 97 valp = NULL; 98 switch (iocp->ioc_cmd) { 99 case ND_GET: 100 /* 101 * (temporary) hack: "*valp" is size of user buffer for 102 * copyout. If result of action routine is too big, free 103 * excess and return ioc_rval as buffer size needed. Return 104 * as many mblocks as will fit, free the rest. For backward 105 * compatibility, assume size of original ioctl buffer if 106 * "*valp" bad or not given. 107 */ 108 if (valp) 109 (void) ddi_strtol(valp, NULL, 10, &avail); 110 /* We overwrite the name/value with the reply data */ 111 { 112 mblk_t *mp2 = mp1; 113 114 while (mp2) { 115 mp2->b_wptr = mp2->b_rptr; 116 mp2 = mp2->b_cont; 117 } 118 } 119 err = (*nde->nde_get_pfi)(q, mp1, nde->nde_data, iocp->ioc_cr); 120 if (!err) { 121 int size_out; 122 int excess; 123 124 iocp->ioc_rval = 0; 125 126 /* Tack on the null */ 127 (void) mi_mpprintf_putc((char *)mp1, '\0'); 128 size_out = msgdsize(mp1); 129 excess = size_out - avail; 130 if (excess > 0) { 131 iocp->ioc_rval = size_out; 132 size_out -= excess; 133 (void) adjmsg(mp1, -(excess + 1)); 134 (void) mi_mpprintf_putc((char *)mp1, '\0'); 135 } 136 iocp->ioc_count = size_out; 137 } 138 break; 139 140 case ND_SET: 141 if (valp) { 142 if ((iocp->ioc_cr != NULL) && 143 ((err = secpolicy_ip_config(iocp->ioc_cr, B_FALSE)) 144 == 0)) { 145 err = (*nde->nde_set_pfi)(q, mp1, valp, 146 nde->nde_data, iocp->ioc_cr); 147 } 148 iocp->ioc_count = 0; 149 freemsg(mp1); 150 mp->b_cont = NULL; 151 } 152 break; 153 154 default: 155 break; 156 } 157 iocp->ioc_error = err; 158 mp->b_datap->db_type = M_IOCACK; 159 return (B_TRUE); 160 } 161 162 /* ARGSUSED */ 163 int 164 nd_get_default(queue_t *q, MBLKP mp, caddr_t data, cred_t *ioc_cr) 165 { 166 return (EACCES); 167 } 168 169 /* 170 * This routine may be used as the get dispatch routine in nd tables 171 * for long variables. To use this routine instead of a module 172 * specific routine, call nd_load as 173 * nd_load(&nd_ptr, "name", nd_get_long, set_pfi, &long_variable) 174 * The name of the variable followed by a space and the value of the 175 * variable will be printed in response to a get_status call. 176 */ 177 /* ARGSUSED */ 178 int 179 nd_get_long(queue_t *q, MBLKP mp, caddr_t data, cred_t *ioc_cr) 180 { 181 ulong_t *lp; 182 183 lp = (ulong_t *)data; 184 (void) mi_mpprintf(mp, "%ld", *lp); 185 return (0); 186 } 187 188 /* ARGSUSED */ 189 int 190 nd_get_names(queue_t *q, MBLKP mp, caddr_t nd_param, cred_t *ioc_cr) 191 { 192 ND *nd; 193 NDE *nde; 194 char *rwtag; 195 boolean_t get_ok, set_ok; 196 197 nd = (ND *)nd_param; 198 if (!nd) 199 return (ENOENT); 200 for (nde = nd->nd_tbl; nde->nde_name; nde++) { 201 get_ok = nde->nde_get_pfi != nd_get_default; 202 set_ok = nde->nde_set_pfi != nd_set_default; 203 if (get_ok) { 204 if (set_ok) 205 rwtag = "read and write"; 206 else 207 rwtag = "read only"; 208 } else if (set_ok) 209 rwtag = "write only"; 210 else 211 rwtag = "no read or write"; 212 (void) mi_mpprintf(mp, "%s (%s)", nde->nde_name, rwtag); 213 } 214 return (0); 215 } 216 217 /* 218 * Load 'name' into the named dispatch table pointed to by 'ndp'. 219 * 'ndp' should be the address of a char pointer cell. If the table 220 * does not exist (*ndp == 0), a new table is allocated and 'ndp' 221 * is stuffed. If there is not enough space in the table for a new 222 * entry, more space is allocated. 223 * Never fails due to memory allocation failures. 224 */ 225 boolean_t 226 nd_load(caddr_t *nd_pparam, char *name, ndgetf_t get_pfi, ndsetf_t set_pfi, 227 caddr_t data) 228 { 229 ND *nd; 230 NDE *nde; 231 232 if (!nd_pparam) 233 return (B_FALSE); 234 if ((nd = (ND *)(*nd_pparam)) == NULL) { 235 nd = (ND *)mi_alloc_sleep(sizeof (ND), BPRI_MED); 236 bzero((caddr_t)nd, sizeof (ND)); 237 *nd_pparam = (caddr_t)nd; 238 } 239 if (nd->nd_tbl) { 240 for (nde = nd->nd_tbl; nde->nde_name; nde++) { 241 if (mi_strcmp(name, nde->nde_name) == 0) 242 goto fill_it; 243 } 244 } 245 if (nd->nd_free_count <= 1) { 246 nde = (NDE *)mi_alloc_sleep(nd->nd_size + 247 NDE_ALLOC_SIZE, BPRI_MED); 248 bzero((char *)nde, nd->nd_size + NDE_ALLOC_SIZE); 249 nd->nd_free_count += NDE_ALLOC_COUNT; 250 if (nd->nd_tbl) { 251 bcopy((char *)nd->nd_tbl, (char *)nde, nd->nd_size); 252 mi_free((char *)nd->nd_tbl); 253 } else { 254 nd->nd_free_count--; 255 nde->nde_name = "?"; 256 nde->nde_get_pfi = nd_get_names; 257 nde->nde_set_pfi = nd_set_default; 258 } 259 nde->nde_data = (caddr_t)nd; 260 nd->nd_tbl = nde; 261 nd->nd_size += NDE_ALLOC_SIZE; 262 } 263 for (nde = nd->nd_tbl; nde->nde_name; nde++) 264 noop; 265 nd->nd_free_count--; 266 fill_it: 267 nde->nde_name = name; 268 nde->nde_get_pfi = get_pfi ? get_pfi : nd_get_default; 269 nde->nde_set_pfi = set_pfi ? set_pfi : nd_set_default; 270 nde->nde_data = data; 271 return (B_TRUE); 272 } 273 274 /* 275 * Unload 'name' from the named dispatch table. If the table does not 276 * exist, I return. I do not free up space, but I do raise the 277 * free count so future nd_load()s don't take as much memory. 278 */ 279 280 void 281 nd_unload(caddr_t *nd_pparam, char *name) 282 { 283 ND *nd; 284 NDE *nde; 285 boolean_t foundit = B_FALSE; 286 287 /* My apologies for the in-boolean assignment. */ 288 if (nd_pparam == NULL || (nd = (ND *)(*nd_pparam)) == NULL || 289 nd->nd_tbl == NULL) 290 return; 291 292 for (nde = nd->nd_tbl; nde->nde_name != NULL; nde++) { 293 if (foundit) 294 *(nde - 1) = *nde; 295 if (mi_strcmp(name, nde->nde_name) == 0) 296 foundit = B_TRUE; 297 } 298 if (foundit) 299 bzero(nde - 1, sizeof (NDE)); 300 } 301 302 /* ARGSUSED */ 303 int 304 nd_set_default(queue_t *q, MBLKP mp, char *value, caddr_t data, cred_t *ioc_cr) 305 { 306 return (EACCES); 307 } 308 309 /* ARGSUSED */ 310 int 311 nd_set_long(queue_t *q, MBLKP mp, char *value, caddr_t data, cred_t *ioc_cr) 312 { 313 ulong_t *lp; 314 long new_value; 315 316 if (ddi_strtol(value, NULL, 10, &new_value) != 0) 317 return (EINVAL); 318 lp = (ulong_t *)data; 319 *lp = new_value; 320 return (0); 321 } 322