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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <sys/param.h>
  27 #include <sys/atomic.h>
  28 #include <sys/kmem.h>
  29 #include <sys/rwlock.h>
  30 #include <sys/errno.h>
  31 #include <sys/queue.h>
  32 #include <inet/common.h>
  33 #include <inet/led.h>
  34 #include <inet/ip.h>
  35 #include <sys/neti.h>
  36 #include <sys/zone.h>
  37 
  38 static net_handle_t net_find(const char *protocol, neti_stack_t *ns);
  39 
  40 static net_handle_t
  41 net_find(const char *protocol, neti_stack_t *nts)
  42 {
  43         struct net_data *n;
  44 
  45         ASSERT(protocol != NULL);
  46         ASSERT(nts != NULL);
  47 
  48         LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
  49                 ASSERT(n->netd_info.netp_name != NULL);
  50                 /*
  51                  * If they're trying to find a protocol that is being
  52                  * shutdown, just ignore it..
  53                  */
  54                 if (n->netd_condemned != 0)
  55                         continue;
  56                 if (strcmp(n->netd_info.netp_name, protocol) == 0) {
  57                         break;
  58                 }
  59         }
  60 
  61         return (n);
  62 }
  63 
  64 net_handle_t
  65 net_protocol_register(netid_t id, const net_protocol_t *info)
  66 {
  67         struct net_data *n, *new;
  68         neti_stack_t *nts;
  69 
  70         ASSERT(info != NULL);
  71 
  72         nts = net_getnetistackbyid(id);
  73         if (nts == NULL)
  74                 return (NULL);
  75 
  76         new = kmem_alloc(sizeof (*new), KM_SLEEP);
  77         new->netd_refcnt = 1;
  78         new->netd_hooks = NULL;
  79         new->netd_info = *info;
  80         new->netd_stack = nts;
  81         new->netd_condemned = 0;
  82 
  83         mutex_enter(&nts->nts_lock);
  84         n = net_find(info->netp_name, nts);
  85         if (n != NULL) {
  86                 mutex_exit(&nts->nts_lock);
  87                 kmem_free(new, sizeof (*new));
  88                 return (NULL);
  89         }
  90 
  91         if (LIST_EMPTY(&nts->nts_netd_head)) {
  92                 LIST_INSERT_HEAD(&nts->nts_netd_head, new, netd_list);
  93         } else {
  94                 LIST_INSERT_AFTER(LIST_FIRST(&nts->nts_netd_head),
  95                     new, netd_list);
  96         }
  97         mutex_exit(&nts->nts_lock);
  98 
  99         return (new);
 100 }
 101 
 102 int
 103 net_protocol_unregister(net_handle_t info)
 104 {
 105         neti_stack_t *nts;
 106 
 107         ASSERT(info != NULL);
 108 
 109         nts = info->netd_stack;
 110         ASSERT(nts != NULL);
 111 
 112         mutex_enter(&nts->nts_lock);
 113         LIST_REMOVE(info, netd_list);
 114         info->netd_stack = NULL;
 115         mutex_exit(&nts->nts_lock);
 116 
 117         (void) net_protocol_release(info);
 118 
 119         return (0);
 120 }
 121 
 122 net_handle_t
 123 net_protocol_lookup(netid_t netid, const char *protocol)
 124 {
 125         neti_stack_t *nts;
 126         net_handle_t nd;
 127 
 128         ASSERT(protocol != NULL);
 129 
 130         nts = net_getnetistackbyid(netid);
 131         if (nts == NULL)
 132                 return (NULL);
 133 
 134         mutex_enter(&nts->nts_lock);
 135         nd = net_find(protocol, nts);
 136         if (nd != NULL)
 137                 atomic_inc_32((uint_t *)&nd->netd_refcnt);
 138         mutex_exit(&nts->nts_lock);
 139         return (nd);
 140 }
 141 
 142 /*
 143  * Note: the man page specifies "returns -1 if the value passed in is unknown
 144  * to this framework".  We are not doing a lookup in this function, just a
 145  * simply add to the netd_refcnt of the net_handle_t passed in, so -1 is never a
 146  * return value.
 147  */
 148 int
 149 net_protocol_release(net_handle_t info)
 150 {
 151 
 152         ASSERT(info->netd_refcnt > 0);
 153         /*
 154          * Is this safe? No hold on nts_lock? Consider that if the caller
 155          * of net_protocol_release() is going to free this structure then
 156          * it is now the only owner (refcnt==1) and it will have been
 157          * removed from the nts_netd_head list on the neti_stack_t from a
 158          * call to net_protocol_unregister already, so it is thus an orphan.
 159          */
 160         if (atomic_dec_32_nv((uint_t *)&info->netd_refcnt) == 0) {
 161                 ASSERT(info->netd_hooks == NULL);
 162                 ASSERT(info->netd_stack == NULL);
 163                 kmem_free(info, sizeof (struct net_data));
 164         }
 165 
 166         return (0);
 167 }
 168 
 169 net_handle_t
 170 net_protocol_walk(netid_t netid, net_handle_t info)
 171 {
 172         struct net_data *n = NULL;
 173         boolean_t found = B_FALSE;
 174         neti_stack_t *nts;
 175 
 176         nts = net_getnetistackbyid(netid);
 177         ASSERT(nts != NULL);
 178 
 179         if (info == NULL)
 180                 found = B_TRUE;
 181 
 182         mutex_enter(&nts->nts_lock);
 183         LIST_FOREACH(n, &nts->nts_netd_head, netd_list) {
 184                 if (found) {
 185                         /*
 186                          * We are only interested in finding protocols that
 187                          * are not in some sort of shutdown state.  There is
 188                          * no need to check for netd_stack==NULL because
 189                          * that implies it is no longer on this list.
 190                          */
 191                         if (n->netd_condemned == 0)
 192                                 continue;
 193                         break;
 194                 }
 195 
 196                 if (n == info)
 197                         found = B_TRUE;
 198         }
 199 
 200         if (info != NULL)
 201                 (void) net_protocol_release(info);
 202 
 203         if (n != NULL)
 204                 atomic_inc_32((uint_t *)&n->netd_refcnt);
 205 
 206         mutex_exit(&nts->nts_lock);
 207 
 208         return (n);
 209 }
 210 
 211 /*
 212  * Public accessor functions
 213  */
 214 int
 215 net_getifname(net_handle_t info, phy_if_t nic, char *buffer,
 216     const size_t buflen)
 217 {
 218 
 219         ASSERT(info != NULL);
 220 
 221         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 222                 return (-1);
 223 
 224         return (info->netd_info.netp_getifname(info, nic, buffer, buflen));
 225 }
 226 
 227 int
 228 net_getmtu(net_handle_t info, phy_if_t nic, lif_if_t ifdata)
 229 {
 230 
 231         ASSERT(info != NULL);
 232 
 233         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 234                 return (-1);
 235 
 236         return (info->netd_info.netp_getmtu(info, nic, ifdata));
 237 }
 238 
 239 int
 240 net_getpmtuenabled(net_handle_t info)
 241 {
 242 
 243         ASSERT(info != NULL);
 244 
 245         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 246                 return (-1);
 247 
 248         return (info->netd_info.netp_getpmtuenabled(info));
 249 }
 250 
 251 int
 252 net_getlifaddr(net_handle_t info, phy_if_t nic, lif_if_t ifdata,
 253     int nelem, net_ifaddr_t type[], void *storage)
 254 {
 255 
 256         ASSERT(info != NULL);
 257 
 258         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 259                 return (-1);
 260 
 261         return (info->netd_info.netp_getlifaddr(info, nic, ifdata,
 262             nelem, type, storage));
 263 }
 264 
 265 int
 266 net_getlifzone(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata,
 267     zoneid_t *zoneid)
 268 {
 269         ASSERT(info != NULL);
 270 
 271         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 272                 return (-1);
 273 
 274         return (info->netd_info.neti_getlifzone(info, phy_ifdata, ifdata,
 275             zoneid));
 276 }
 277 
 278 int
 279 net_getlifflags(net_handle_t info, phy_if_t phy_ifdata, lif_if_t ifdata,
 280     uint64_t *flags)
 281 {
 282         ASSERT(info != NULL);
 283 
 284         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 285                 return (-1);
 286 
 287         return (info->netd_info.neti_getlifflags(info, phy_ifdata, ifdata,
 288             flags));
 289 }
 290 
 291 phy_if_t
 292 net_phygetnext(net_handle_t info, phy_if_t nic)
 293 {
 294 
 295         ASSERT(info != NULL);
 296 
 297         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 298                 return ((phy_if_t)-1);
 299 
 300         return (info->netd_info.netp_phygetnext(info, nic));
 301 }
 302 
 303 phy_if_t
 304 net_phylookup(net_handle_t info, const char *name)
 305 {
 306 
 307         ASSERT(info != NULL);
 308 
 309         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 310                 return ((phy_if_t)-1);
 311 
 312         return (info->netd_info.netp_phylookup(info, name));
 313 }
 314 
 315 lif_if_t
 316 net_lifgetnext(net_handle_t info, phy_if_t ifidx, lif_if_t ifdata)
 317 {
 318 
 319         ASSERT(info != NULL);
 320 
 321         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 322                 return ((lif_if_t)-1);
 323 
 324         return (info->netd_info.netp_lifgetnext(info, ifidx, ifdata));
 325 }
 326 
 327 int
 328 net_inject(net_handle_t info, inject_t style, net_inject_t *packet)
 329 {
 330 
 331         ASSERT(info != NULL);
 332 
 333         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 334                 return (-1);
 335 
 336         return (info->netd_info.netp_inject(info, style, packet));
 337 }
 338 
 339 phy_if_t
 340 net_routeto(net_handle_t info, struct sockaddr *address, struct sockaddr *next)
 341 {
 342 
 343         ASSERT(info != NULL);
 344 
 345         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 346                 return ((phy_if_t)-1);
 347 
 348         return (info->netd_info.netp_routeto(info, address, next));
 349 }
 350 
 351 int
 352 net_ispartialchecksum(net_handle_t info, mblk_t *mp)
 353 {
 354 
 355         ASSERT(info != NULL);
 356         ASSERT(mp != NULL);
 357 
 358         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 359                 return (-1);
 360 
 361         return (info->netd_info.netp_ispartialchecksum(info, mp));
 362 }
 363 
 364 int
 365 net_isvalidchecksum(net_handle_t info, mblk_t *mp)
 366 {
 367 
 368         ASSERT(info != NULL);
 369         ASSERT(mp != NULL);
 370 
 371         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 372                 return (-1);
 373 
 374         return (info->netd_info.netp_isvalidchecksum(info, mp));
 375 }
 376 
 377 /*
 378  * Hooks related functions
 379  */
 380 
 381 /*
 382  * Function:    net_family_register
 383  * Returns:     int - 0 = Succ, Else = Fail
 384  * Parameters:  info(I) - protocol
 385  *              hf(I) - family pointer
 386  *
 387  * Call hook_family_add to register family
 388  *
 389  * There is no need to bump netd_refcnt in the two functions
 390  * net_family_register and net_family_unregister because the caller of these
 391  * two functions is assumed to "own" a reference on 'info' via an earlier
 392  * call to net_protocol_register().  Thus the owner is expected to do a
 393  * call to net_protocol_unregister() after having done a
 394  * net_family_unregister() to make sure things are properly cleaned up.
 395  * Passing a pointer to info->netd_hooks into hook_family_add is required
 396  * so that this can be set before the notify functions are called. If this
 397  * does not happen, the notify function may do something that seems fine,
 398  * like add a notify function to the family but cause a panic because
 399  * netd_hooks is NULL when we get to hook_family_notify_register.
 400  */
 401 int
 402 net_family_register(net_handle_t info, hook_family_t *hf)
 403 {
 404         netstack_t *ns;
 405 
 406         ASSERT(info != NULL);
 407         ASSERT(hf != NULL);
 408 
 409         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 410                 return (ESHUTDOWN);
 411 
 412         if (info->netd_hooks != NULL)
 413                 return (EEXIST);
 414 
 415         ns = info->netd_stack->nts_netstack;
 416         ASSERT(ns != NULL);
 417         if (hook_family_add(hf, ns->netstack_hook,
 418             (void **)&info->netd_hooks) == NULL)
 419                 return (EEXIST);
 420 
 421         return (0);
 422 }
 423 
 424 /*
 425  * Function:    net_family_unregister
 426  * Returns:     int - transparent value, explained by caller
 427  * Parameters:  info(I) - protocol
 428  *              hf(I) - family pointer
 429  *
 430  * Call hook_family_remove to unregister family
 431  */
 432 int
 433 net_family_unregister(net_handle_t info, hook_family_t *hf)
 434 {
 435         int ret;
 436 
 437         ASSERT(info != NULL);
 438         ASSERT(hf != NULL);
 439 
 440         if (info->netd_hooks == NULL)
 441                 return (ENXIO);
 442 
 443         if (strcmp(info->netd_hooks->hfi_family.hf_name,
 444             hf->hf_name) != 0)
 445                 return (EINVAL);
 446 
 447         ret = hook_family_remove(info->netd_hooks);
 448         if (ret == 0)
 449                 info->netd_hooks = NULL;
 450 
 451         return (ret);
 452 }
 453 
 454 int
 455 net_family_shutdown(net_handle_t info, hook_family_t *hf)
 456 {
 457 
 458         ASSERT(info != NULL);
 459         ASSERT(hf != NULL);
 460 
 461         if (info->netd_hooks == NULL)
 462                 return (ENXIO);
 463 
 464         if (strcmp(info->netd_hooks->hfi_family.hf_name,
 465             hf->hf_name) != 0)
 466                 return (EINVAL);
 467 
 468         return (hook_family_shutdown(info->netd_hooks));
 469 }
 470 
 471 /*
 472  * Function:    net_event_register
 473  * Returns:     internal event pointer - NULL = Fail
 474  * Parameters:  info(I) - protocol
 475  *              he(I) - event pointer
 476  *
 477  * Call hook_event_add to register event on specific family
 478  *      Internal event pointer is returned so caller can get
 479  *      handle to run hooks
 480  */
 481 hook_event_token_t
 482 net_event_register(net_handle_t info, hook_event_t *he)
 483 {
 484         hook_event_int_t *hei;
 485 
 486         ASSERT(info != NULL);
 487         ASSERT(he != NULL);
 488 
 489         if (info->netd_hooks == NULL || info->netd_condemned != 0 ||
 490             info->netd_stack == NULL)
 491                 return (NULL);
 492 
 493         hei = hook_event_add(info->netd_hooks, he);
 494         return ((hook_event_token_t)hei);
 495 }
 496 
 497 /*
 498  * Function:    net_event_unregister
 499  * Returns:     int - transparent value, explained by caller
 500  * Parameters:  info(I) - protocol
 501  *              he(I) - event pointer
 502  *
 503  * Call hook_event_remove to unregister event on specific family
 504  */
 505 int
 506 net_event_unregister(net_handle_t info, hook_event_t *he)
 507 {
 508 
 509         ASSERT(info != NULL);
 510         ASSERT(he != NULL);
 511 
 512         if (info->netd_hooks == NULL)
 513                 return (ENXIO);
 514 
 515         return (hook_event_remove(info->netd_hooks, he));
 516 }
 517 
 518 int
 519 net_event_shutdown(net_handle_t info, hook_event_t *he)
 520 {
 521 
 522         ASSERT(info != NULL);
 523         ASSERT(he != NULL);
 524 
 525         if (info->netd_hooks == NULL)
 526                 return (ENXIO);
 527 
 528         return (hook_event_shutdown(info->netd_hooks, he));
 529 }
 530 
 531 /*
 532  * Function:    net_hook_register
 533  * Returns:     int - transparent value, explained by caller
 534  * Parameters:  info(I) - protocol
 535  *              event(I) - event name
 536  *              h(I) - hook pointer
 537  *
 538  * Call hook_register to add hook on specific family/event
 539  */
 540 int
 541 net_hook_register(net_handle_t info, char *event, hook_t *h)
 542 {
 543 
 544         ASSERT(info != NULL);
 545         ASSERT(event != NULL);
 546         ASSERT(h != NULL);
 547 
 548         if (info->netd_condemned != 0 || info->netd_stack == NULL)
 549                 return (ESHUTDOWN);
 550 
 551         if (info->netd_hooks == NULL)
 552                 return (ENXIO);
 553 
 554         return (hook_register(info->netd_hooks, event, h));
 555 }
 556 
 557 /*
 558  * Function:    net_hook_unregister
 559  * Returns:     int - transparent value, explained by caller
 560  * Parameters:  info(I) - protocol
 561  *              event(I) - event name
 562  *              h(I) - hook pointer
 563  *
 564  * Call hook_unregister to remove hook on specific family/event
 565  */
 566 int
 567 net_hook_unregister(net_handle_t info, char *event, hook_t *h)
 568 {
 569 
 570         ASSERT(info != NULL);
 571         ASSERT(event != NULL);
 572         ASSERT(h != NULL);
 573 
 574         if (info->netd_hooks == NULL)
 575                 return (ENXIO);
 576 
 577         return (hook_unregister(info->netd_hooks, event, h));
 578 }
 579 
 580 netid_t
 581 net_getnetid(net_handle_t netd)
 582 {
 583 
 584         if (netd->netd_stack == NULL)
 585                 return (-1);
 586         return (netd->netd_stack->nts_id);
 587 }
 588 
 589 net_inject_t *
 590 net_inject_alloc(const int version)
 591 {
 592         net_inject_t *ni;
 593 
 594         ni = kmem_zalloc(sizeof (*ni), KM_NOSLEEP);
 595         if (ni == NULL)
 596                 return (NULL);
 597 
 598         ni->ni_version = version;
 599         return (ni);
 600 }
 601 
 602 void
 603 net_inject_free(net_inject_t *ni)
 604 {
 605         kmem_free(ni, sizeof (*ni));
 606 }
 607 
 608 kstat_t *
 609 net_kstat_create(netid_t netid, char *module, int instance, char *name,
 610     char *class, uchar_t type, ulong_t ndata, uchar_t ks_flag)
 611 {
 612         netstackid_t stackid = net_getnetstackidbynetid(netid);
 613 
 614         if (stackid == -1)
 615                 return (NULL);
 616 
 617         return (kstat_create_netstack(module, instance, name, class, type,
 618             ndata, ks_flag, stackid));
 619 }
 620 
 621 void
 622 net_kstat_delete(netid_t netid, kstat_t *ks)
 623 {
 624         netstackid_t stackid = net_getnetstackidbynetid(netid);
 625 
 626         if (stackid != -1)
 627                 kstat_delete_netstack(ks, stackid);
 628 }
 629 
 630 int
 631 net_event_notify_register(net_handle_t family, char *event,
 632     hook_notify_fn_t callback, void *arg)
 633 {
 634         int error;
 635 
 636         if (family->netd_condemned != 0 || family->netd_stack == NULL)
 637                 return (ESHUTDOWN);
 638 
 639         error = hook_event_notify_register(family->netd_hooks, event,
 640             callback, arg);
 641 
 642         return (error);
 643 }
 644 
 645 int
 646 net_event_notify_unregister(net_handle_t family, char *event,
 647     hook_notify_fn_t callback)
 648 {
 649         int error;
 650 
 651         error = hook_event_notify_unregister(family->netd_hooks, event,
 652             callback);
 653 
 654         return (error);
 655 }
 656 
 657 int
 658 net_protocol_notify_register(net_handle_t family, hook_notify_fn_t callback,
 659     void *arg)
 660 {
 661         int error;
 662 
 663         if (family->netd_condemned != 0 || family->netd_stack == NULL)
 664                 return (ESHUTDOWN);
 665 
 666         error = hook_family_notify_register(family->netd_hooks, callback,
 667             arg);
 668 
 669         return (error);
 670 }
 671 
 672 int
 673 net_protocol_notify_unregister(net_handle_t family, hook_notify_fn_t callback)
 674 {
 675         int error;
 676 
 677         error = hook_family_notify_unregister(family->netd_hooks, callback);
 678 
 679         return (error);
 680 }