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