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