Print this page
    
OS-249
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/libdladm/common/libdllink.c
          +++ new/usr/src/lib/libdladm/common/libdllink.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  
    | 
      ↓ open down ↓ | 
    12 lines elided | 
    
      ↑ open up ↑ | 
  
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
       23 + * Copyright (c) 2011, Joyent Inc. All rights reserved.
  23   24   */
  24   25  
  25   26  #include <sys/types.h>
  26   27  #include <unistd.h>
  27   28  #include <errno.h>
  28   29  #include <fcntl.h>
  29   30  #include <assert.h>
  30   31  #include <ctype.h>
  31   32  #include <strings.h>
  32   33  #include <sys/stat.h>
  33   34  #include <sys/dld.h>
  34   35  #include <sys/vlan.h>
  35   36  #include <zone.h>
  36   37  #include <librcm.h>
  37   38  #include <libdlpi.h>
  38   39  #include <libdevinfo.h>
  39   40  #include <libdlaggr.h>
  40   41  #include <libdlvlan.h>
  41   42  #include <libdlvnic.h>
  42   43  #include <libdlib.h>
  43   44  #include <libdllink.h>
  44   45  #include <libdlmgmt.h>
  45   46  #include <libdladm_impl.h>
  46   47  #include <libinetutil.h>
  47   48  
  48   49  /*
  49   50   * Return the attributes of the specified datalink from the DLD driver.
  50   51   */
  51   52  static dladm_status_t
  52   53  i_dladm_info(dladm_handle_t handle, const datalink_id_t linkid,
  53   54      dladm_attr_t *dap)
  54   55  {
  55   56          dld_ioc_attr_t  dia;
  56   57  
  57   58          dia.dia_linkid = linkid;
  58   59  
  59   60          if (ioctl(dladm_dld_fd(handle), DLDIOC_ATTR, &dia) < 0)
  60   61                  return (dladm_errno2status(errno));
  61   62  
  62   63          dap->da_max_sdu = dia.dia_max_sdu;
  63   64  
  64   65          return (DLADM_STATUS_OK);
  65   66  }
  66   67  
  67   68  static dladm_status_t
  68   69  dladm_usagelog(dladm_handle_t handle, dladm_logtype_t type,
  69   70      dld_ioc_usagelog_t *log_info)
  70   71  {
  71   72          if (type == DLADM_LOGTYPE_FLOW)
  72   73                  log_info->ul_type = MAC_LOGTYPE_FLOW;
  73   74          else
  74   75                  log_info->ul_type = MAC_LOGTYPE_LINK;
  75   76  
  76   77          if (ioctl(dladm_dld_fd(handle), DLDIOC_USAGELOG, log_info) < 0)
  77   78                  return (DLADM_STATUS_IOERR);
  78   79  
  79   80          return (DLADM_STATUS_OK);
  80   81  }
  81   82  
  82   83  dladm_status_t
  83   84  dladm_start_usagelog(dladm_handle_t handle, dladm_logtype_t type,
  84   85      uint_t interval)
  85   86  {
  86   87          dld_ioc_usagelog_t      log_info;
  87   88  
  88   89          log_info.ul_onoff = B_TRUE;
  89   90          log_info.ul_interval = interval;
  90   91  
  91   92          return (dladm_usagelog(handle, type, &log_info));
  92   93  }
  93   94  
  94   95  dladm_status_t
  95   96  dladm_stop_usagelog(dladm_handle_t handle, dladm_logtype_t type)
  96   97  {
  97   98          dld_ioc_usagelog_t      log_info;
  98   99  
  99  100          log_info.ul_onoff = B_FALSE;
 100  101          log_info.ul_interval = 0;
 101  102  
 102  103          return (dladm_usagelog(handle, type, &log_info));
 103  104  }
 104  105  
 105  106  struct i_dladm_walk_arg {
 106  107          dladm_walkcb_t *fn;
 107  108          void *arg;
 108  109  };
 109  110  
 110  111  static int
 111  112  i_dladm_walk(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 112  113  {
 113  114          struct i_dladm_walk_arg *walk_arg = arg;
 114  115          char link[MAXLINKNAMELEN];
 115  116  
 116  117          if (dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, link,
 117  118              sizeof (link)) == DLADM_STATUS_OK) {
 118  119                  return (walk_arg->fn(link, walk_arg->arg));
 119  120          }
 120  121  
 121  122          return (DLADM_WALK_CONTINUE);
 122  123  }
 123  124  
 124  125  /*
 125  126   * Walk all datalinks.
 126  127   */
 127  128  dladm_status_t
 128  129  dladm_walk(dladm_walkcb_t *fn, dladm_handle_t handle, void *arg,
 129  130      datalink_class_t class, datalink_media_t dmedia, uint32_t flags)
 130  131  {
 131  132          struct i_dladm_walk_arg walk_arg;
 132  133  
 133  134          walk_arg.fn = fn;
 134  135          walk_arg.arg = arg;
 135  136          return (dladm_walk_datalink_id(i_dladm_walk, handle, &walk_arg,
 136  137              class, dmedia, flags));
 137  138  }
 138  139  
 139  140  #define MAXGRPPERLINK   64
 140  141  
 141  142  int
 142  143  dladm_walk_hwgrp(dladm_handle_t handle, datalink_id_t linkid, void *arg,
 143  144      boolean_t (*fn)(void *, dladm_hwgrp_attr_t *))
 144  145  {
 145  146          int             bufsize, ret;
 146  147          int             nhwgrp = MAXGRPPERLINK;
 147  148          dld_ioc_hwgrpget_t *iomp = NULL;
 148  149  
 149  150          bufsize = sizeof (dld_ioc_hwgrpget_t) +
 150  151              nhwgrp * sizeof (dld_hwgrpinfo_t);
 151  152  
 152  153          if ((iomp = (dld_ioc_hwgrpget_t *)calloc(1, bufsize)) == NULL)
 153  154                  return (-1);
 154  155  
 155  156          iomp->dih_size = nhwgrp * sizeof (dld_hwgrpinfo_t);
 156  157          iomp->dih_linkid = linkid;
 157  158  
 158  159          ret = ioctl(dladm_dld_fd(handle), DLDIOC_GETHWGRP, iomp);
 159  160          if (ret == 0) {
 160  161                  int                     i;
 161  162                  int                     j;
 162  163                  dld_hwgrpinfo_t         *dhip;
 163  164                  dladm_hwgrp_attr_t      attr;
 164  165  
 165  166                  dhip = (dld_hwgrpinfo_t *)(iomp + 1);
 166  167                  for (i = 0; i < iomp->dih_n_groups; i++) {
 167  168                          bzero(&attr, sizeof (attr));
 168  169  
 169  170                          (void) strlcpy(attr.hg_link_name,
 170  171                              dhip->dhi_link_name, sizeof (attr.hg_link_name));
 171  172                          attr.hg_grp_num = dhip->dhi_grp_num;
 172  173                          attr.hg_grp_type = dhip->dhi_grp_type;
 173  174                          attr.hg_n_rings = dhip->dhi_n_rings;
 174  175                          for (j = 0; j < dhip->dhi_n_rings; j++)
 175  176                                  attr.hg_rings[j] = dhip->dhi_rings[j];
 176  177                          dladm_sort_index_list(attr.hg_rings, attr.hg_n_rings);
 177  178                          attr.hg_n_clnts = dhip->dhi_n_clnts;
 178  179                          (void) strlcpy(attr.hg_client_names,
 179  180                              dhip->dhi_clnts, sizeof (attr.hg_client_names));
 180  181  
 181  182                          if (!(*fn)(arg, &attr))
 182  183                                  break;
 183  184                          dhip++;
 184  185                  }
 185  186          }
 186  187          free(iomp);
 187  188          return (ret);
 188  189  }
 189  190  
 190  191  /*
 191  192   * Invoke the specified callback for each MAC address entry defined on
 192  193   * the specified device.
 193  194   */
 194  195  int
 195  196  dladm_walk_macaddr(dladm_handle_t handle, datalink_id_t linkid, void *arg,
 196  197      boolean_t (*fn)(void *, dladm_macaddr_attr_t *))
 197  198  {
 198  199          int             bufsize, ret;
 199  200          int             nmacaddr = 1024;
 200  201          dld_ioc_macaddrget_t *iomp = NULL;
 201  202  
 202  203          bufsize = sizeof (dld_ioc_macaddrget_t) +
 203  204              nmacaddr * sizeof (dld_macaddrinfo_t);
 204  205  
 205  206          if ((iomp = (dld_ioc_macaddrget_t *)calloc(1, bufsize)) == NULL)
 206  207                  return (-1);
 207  208  
 208  209          iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t);
 209  210          iomp->dig_linkid = linkid;
 210  211  
 211  212          ret = ioctl(dladm_dld_fd(handle), DLDIOC_MACADDRGET, iomp);
 212  213          if (ret == 0) {
 213  214                  int i;
 214  215                  dld_macaddrinfo_t *dmip;
 215  216                  dladm_macaddr_attr_t attr;
 216  217  
 217  218                  dmip = (dld_macaddrinfo_t *)(iomp + 1);
 218  219                  for (i = 0; i < iomp->dig_count; i++) {
 219  220                          bzero(&attr, sizeof (attr));
 220  221  
 221  222                          attr.ma_slot = dmip->dmi_slot;
 222  223                          attr.ma_flags = 0;
 223  224                          if (dmip->dmi_flags & DLDIOCMACADDR_USED)
 224  225                                  attr.ma_flags |= DLADM_MACADDR_USED;
 225  226                          bcopy(dmip->dmi_addr, attr.ma_addr,
 226  227                              dmip->dmi_addrlen);
 227  228                          attr.ma_addrlen = dmip->dmi_addrlen;
 228  229                          (void) strlcpy(attr.ma_client_name,
 229  230                              dmip->dmi_client_name, MAXNAMELEN);
 230  231                          attr.ma_client_linkid = dmip->dma_client_linkid;
 231  232  
 232  233                          if (!(*fn)(arg, &attr))
 233  234                                  break;
 234  235                          dmip++;
 235  236                  }
 236  237          }
 237  238          free(iomp);
 238  239          return (ret);
 239  240  }
 240  241  
 241  242  /*
 242  243   * These routines are used by administration tools such as dladm(1M) to
 243  244   * iterate through the list of MAC interfaces
 244  245   */
 245  246  
 246  247  typedef struct dladm_mac_dev {
 247  248          char                    dm_name[MAXNAMELEN];
 248  249          struct dladm_mac_dev    *dm_next;
 249  250  } dladm_mac_dev_t;
 250  251  
 251  252  typedef struct macadm_walk {
 252  253          dladm_mac_dev_t  *dmd_dev_list;
 253  254  } dladm_mac_walk_t;
 254  255  
 255  256  /*
 256  257   * Local callback invoked for each DDI_NT_NET node.
 257  258   */
 258  259  /* ARGSUSED */
 259  260  static int
 260  261  i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg)
 261  262  {
 262  263          dladm_mac_walk_t        *dmwp = arg;
 263  264          dladm_mac_dev_t         *dmdp = dmwp->dmd_dev_list;
 264  265          dladm_mac_dev_t         **last_dmdp = &dmwp->dmd_dev_list;
 265  266          char                    mac[MAXNAMELEN];
 266  267  
 267  268          (void) snprintf(mac, MAXNAMELEN, "%s%d",
 268  269              di_driver_name(node), di_instance(node));
 269  270  
 270  271          /*
 271  272           * Skip aggregations.
 272  273           */
 273  274          if (strcmp("aggr", di_driver_name(node)) == 0)
 274  275                  return (DI_WALK_CONTINUE);
 275  276  
 276  277          /*
 277  278           * Skip softmacs.
 278  279           */
 279  280          if (strcmp("softmac", di_driver_name(node)) == 0)
 280  281                  return (DI_WALK_CONTINUE);
 281  282  
 282  283          while (dmdp) {
 283  284                  /*
 284  285                   * Skip duplicates.
 285  286                   */
 286  287                  if (strcmp(dmdp->dm_name, mac) == 0)
 287  288                          return (DI_WALK_CONTINUE);
 288  289  
 289  290                  last_dmdp = &dmdp->dm_next;
 290  291                  dmdp = dmdp->dm_next;
 291  292          }
 292  293  
 293  294          if ((dmdp = malloc(sizeof (*dmdp))) == NULL)
 294  295                  return (DI_WALK_CONTINUE);
 295  296  
 296  297          (void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN);
 297  298          dmdp->dm_next = NULL;
 298  299          *last_dmdp = dmdp;
 299  300  
 300  301          return (DI_WALK_CONTINUE);
 301  302  }
 302  303  
 303  304  /*
 304  305   * Invoke the specified callback for each DDI_NT_NET node.
 305  306   */
 306  307  dladm_status_t
 307  308  dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg)
 308  309  {
 309  310          di_node_t               root;
 310  311          dladm_mac_walk_t        dmw;
 311  312          dladm_mac_dev_t         *dmdp, *next;
 312  313          boolean_t               done = B_FALSE;
 313  314  
 314  315          if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
 315  316                  return (dladm_errno2status(errno));
 316  317  
 317  318          dmw.dmd_dev_list = NULL;
 318  319  
 319  320          (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw,
 320  321              i_dladm_mac_walk);
 321  322  
 322  323          di_fini(root);
 323  324  
 324  325          dmdp = dmw.dmd_dev_list;
 325  326          for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) {
 326  327                  next = dmdp->dm_next;
 327  328                  if (!done &&
 328  329                      ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) {
 329  330                          done = B_TRUE;
 330  331                  }
 331  332                  free(dmdp);
 332  333          }
 333  334  
 334  335          return (DLADM_STATUS_OK);
 335  336  }
 336  337  
 337  338  /*
 338  339   * Get the current attributes of the specified datalink.
 339  340   */
 340  341  dladm_status_t
 341  342  dladm_info(dladm_handle_t handle, datalink_id_t linkid, dladm_attr_t *dap)
 342  343  {
 343  344          return (i_dladm_info(handle, linkid, dap));
 344  345  }
 345  346  
 346  347  const char *
 347  348  dladm_linkstate2str(link_state_t state, char *buf)
 348  349  {
 349  350          const char      *s;
 350  351  
 351  352          switch (state) {
 352  353          case LINK_STATE_UP:
 353  354                  s = "up";
 354  355                  break;
 355  356          case LINK_STATE_DOWN:
 356  357                  s = "down";
 357  358                  break;
 358  359          default:
 359  360                  s = "unknown";
 360  361                  break;
 361  362          }
 362  363          (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 363  364          return (buf);
 364  365  }
 365  366  
 366  367  const char *
 367  368  dladm_linkduplex2str(link_duplex_t duplex, char *buf)
 368  369  {
 369  370          const char      *s;
 370  371  
 371  372          switch (duplex) {
 372  373          case LINK_DUPLEX_FULL:
 373  374                  s = "full";
 374  375                  break;
 375  376          case LINK_DUPLEX_HALF:
 376  377                  s = "half";
 377  378                  break;
 378  379          default:
  
    | 
      ↓ open down ↓ | 
    346 lines elided | 
    
      ↑ open up ↑ | 
  
 379  380                  s = "unknown";
 380  381                  break;
 381  382          }
 382  383          (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 383  384          return (buf);
 384  385  }
 385  386  
 386  387  /*
 387  388   * Case 1: rename an existing link1 to a link2 that does not exist.
 388  389   * Result: <linkid1, link2>
      390 + * The zonename parameter is used to allow us to create a VNIC in the global
      391 + * zone which is assigned to a non-global zone.  Since there is a race condition
      392 + * in the create process if two VNICs have the same name, we need to rename it
      393 + * after it has been assigned to the zone.
 389  394   */
 390  395  static dladm_status_t
 391  396  i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
 392      -    const char *link1, const char *link2, uint32_t flags)
      397 +    const char *link1, const char *link2, uint32_t flags, const char *zonename)
 393  398  {
 394  399          dld_ioc_rename_t        dir;
 395  400          dladm_status_t          status = DLADM_STATUS_OK;
 396  401  
 397  402          /*
 398  403           * Link is currently available. Check to see whether anything is
 399  404           * holding this link to prevent a rename operation.
 400  405           */
 401  406          if (flags & DLADM_OPT_ACTIVE) {
 402  407                  dir.dir_linkid1 = linkid1;
 403  408                  dir.dir_linkid2 = DATALINK_INVALID_LINKID;
 404  409                  (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
      410 +                if (zonename != NULL)
      411 +                        dir.dir_zoneinit = B_TRUE;
      412 +                else
      413 +                        dir.dir_zoneinit = B_FALSE;
 405  414  
 406  415                  if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) {
 407  416                          status = dladm_errno2status(errno);
 408  417                          return (status);
 409  418                  }
 410  419          }
 411  420  
 412  421          status = dladm_remap_datalink_id(handle, linkid1, link2);
 413  422          if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
 414  423                  (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
      424 +                dir.dir_zoneinit = B_FALSE;
 415  425                  (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
 416  426          }
 417  427          return (status);
 418  428  }
 419  429  
 420  430  typedef struct link_hold_arg_s {
 421  431          datalink_id_t   linkid;
 422  432          datalink_id_t   holder;
 423  433          uint32_t        flags;
 424  434  } link_hold_arg_t;
 425  435  
 426  436  static int
 427  437  i_dladm_aggr_link_hold(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
 428  438  {
 429  439          link_hold_arg_t         *hold_arg = arg;
 430  440          dladm_aggr_grp_attr_t   ginfo;
 431  441          dladm_status_t          status;
 432  442          int                     i;
 433  443  
 434  444          status = dladm_aggr_info(handle, aggrid, &ginfo, hold_arg->flags);
 435  445          if (status != DLADM_STATUS_OK)
 436  446                  return (DLADM_WALK_CONTINUE);
 437  447  
 438  448          for (i = 0; i < ginfo.lg_nports; i++) {
 439  449                  if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
 440  450                          hold_arg->holder = aggrid;
 441  451                          return (DLADM_WALK_TERMINATE);
 442  452                  }
 443  453          }
 444  454          return (DLADM_WALK_CONTINUE);
 445  455  }
 446  456  
 447  457  static int
 448  458  i_dladm_vlan_link_hold(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
 449  459  {
 450  460          link_hold_arg_t         *hold_arg = arg;
 451  461          dladm_vlan_attr_t       vinfo;
 452  462          dladm_status_t          status;
 453  463  
 454  464          status = dladm_vlan_info(handle, vlanid, &vinfo, hold_arg->flags);
 455  465          if (status != DLADM_STATUS_OK)
 456  466                  return (DLADM_WALK_CONTINUE);
 457  467  
 458  468          if (vinfo.dv_linkid == hold_arg->linkid) {
 459  469                  hold_arg->holder = vlanid;
 460  470                  return (DLADM_WALK_TERMINATE);
 461  471          }
 462  472          return (DLADM_WALK_CONTINUE);
 463  473  }
 464  474  
 465  475  /*
 466  476   * Case 2: rename an available physical link link1 to a REMOVED physical link
 467  477   *     link2.  As a result, link1 directly inherits all datalinks configured
 468  478   *     over link2 (linkid2).
 469  479   * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
 470  480   *     link2_other_attr>
 471  481   */
 472  482  static dladm_status_t
 473  483  i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1,
 474  484      datalink_id_t linkid2)
 475  485  {
 476  486          rcm_handle_t            *rcm_hdl = NULL;
 477  487          nvlist_t                *nvl = NULL;
 478  488          link_hold_arg_t         arg;
 479  489          dld_ioc_rename_t        dir;
 480  490          dladm_conf_t            conf1, conf2;
 481  491          char                    devname[MAXLINKNAMELEN];
 482  492          uint64_t                phymaj, phyinst;
 483  493          dladm_status_t          status = DLADM_STATUS_OK;
 484  494  
 485  495          /*
 486  496           * First check if linkid1 is associated with any persistent
 487  497           * aggregations or VLANs. If yes, return BUSY.
 488  498           */
 489  499          arg.linkid = linkid1;
 490  500          arg.holder = DATALINK_INVALID_LINKID;
 491  501          arg.flags = DLADM_OPT_PERSIST;
 492  502          (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, handle, &arg,
 493  503              DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 494  504          if (arg.holder != DATALINK_INVALID_LINKID)
 495  505                  return (DLADM_STATUS_LINKBUSY);
 496  506  
 497  507          arg.flags = DLADM_OPT_PERSIST;
 498  508          (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, handle, &arg,
 499  509              DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 500  510          if (arg.holder != DATALINK_INVALID_LINKID)
  
    | 
      ↓ open down ↓ | 
    76 lines elided | 
    
      ↑ open up ↑ | 
  
 501  511                  return (DLADM_STATUS_LINKBUSY);
 502  512  
 503  513          /*
 504  514           * Send DLDIOC_RENAME to request to rename link1's linkid to
 505  515           * be linkid2. This will check whether link1 is used by any
 506  516           * aggregations or VLANs, or is held by any application. If yes,
 507  517           * return failure.
 508  518           */
 509  519          dir.dir_linkid1 = linkid1;
 510  520          dir.dir_linkid2 = linkid2;
      521 +        dir.dir_zoneinit = B_FALSE;
 511  522          if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0)
 512  523                  status = dladm_errno2status(errno);
 513  524  
 514  525          if (status != DLADM_STATUS_OK) {
 515  526                  return (status);
 516  527          }
 517  528  
 518  529          /*
 519  530           * Now change the phymaj, phyinst and devname associated with linkid1
 520  531           * to be associated with linkid2. Before doing that, the old active
 521  532           * linkprop of linkid1 should be deleted.
 522  533           */
 523  534          (void) dladm_set_linkprop(handle, linkid1, NULL, NULL, 0,
 524  535              DLADM_OPT_ACTIVE);
 525  536  
 526  537          if (((status = dladm_getsnap_conf(handle, linkid1, &conf1)) !=
 527  538              DLADM_STATUS_OK) ||
 528  539              ((status = dladm_get_conf_field(handle, conf1, FDEVNAME, devname,
 529  540              MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
 530  541              ((status = dladm_get_conf_field(handle, conf1, FPHYMAJ, &phymaj,
 531  542              sizeof (uint64_t))) != DLADM_STATUS_OK) ||
 532  543              ((status = dladm_get_conf_field(handle, conf1, FPHYINST, &phyinst,
 533  544              sizeof (uint64_t))) != DLADM_STATUS_OK) ||
 534  545              ((status = dladm_open_conf(handle, linkid2, &conf2)) !=
 535  546              DLADM_STATUS_OK)) {
 536  547                  dir.dir_linkid1 = linkid2;
 537  548                  dir.dir_linkid2 = linkid1;
 538  549                  (void) dladm_init_linkprop(handle, linkid1, B_FALSE);
 539  550                  (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
 540  551                  return (status);
 541  552          }
 542  553  
 543  554          dladm_destroy_conf(handle, conf1);
 544  555          (void) dladm_set_conf_field(handle, conf2, FDEVNAME, DLADM_TYPE_STR,
 545  556              devname);
 546  557          (void) dladm_set_conf_field(handle, conf2, FPHYMAJ, DLADM_TYPE_UINT64,
 547  558              &phymaj);
 548  559          (void) dladm_set_conf_field(handle, conf2, FPHYINST,
 549  560              DLADM_TYPE_UINT64, &phyinst);
 550  561          (void) dladm_write_conf(handle, conf2);
 551  562          dladm_destroy_conf(handle, conf2);
 552  563  
 553  564          /*
 554  565           * Delete link1 and mark link2 up.
 555  566           */
 556  567          (void) dladm_remove_conf(handle, linkid1);
 557  568          (void) dladm_destroy_datalink_id(handle, linkid1, DLADM_OPT_ACTIVE |
 558  569              DLADM_OPT_PERSIST);
 559  570          (void) dladm_up_datalink_id(handle, linkid2);
 560  571  
 561  572          /*
 562  573           * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
 563  574           * consumed by the RCM framework to restore all the datalink and
 564  575           * IP configuration.
 565  576           */
 566  577          status = DLADM_STATUS_FAILED;
 567  578          if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
 568  579              (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
 569  580                  goto done;
 570  581          }
 571  582  
 572  583          if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
 573  584                  goto done;
 574  585  
 575  586          if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
 576  587              RCM_SUCCESS) {
 577  588                  status = DLADM_STATUS_OK;
 578  589          }
 579  590  
 580  591  done:
 581  592          if (rcm_hdl != NULL)
 582  593                  (void) rcm_free_handle(rcm_hdl);
 583  594          if (nvl != NULL)
 584  595                  nvlist_free(nvl);
 585  596          return (status);
 586  597  }
 587  598  
 588  599  /*
 589  600   * case 3: rename a non-existent link to a REMOVED physical link.
 590  601   * Set the removed physical link's device name to link1, so that
 591  602   * when link1 attaches, it inherits all the link configuration of
 592  603   * the removed physical link.
 593  604   */
 594  605  static dladm_status_t
 595  606  i_dladm_rename_link_c3(dladm_handle_t handle, const char *link1,
 596  607      datalink_id_t linkid2)
 597  608  {
 598  609          dladm_conf_t    conf;
 599  610          dladm_status_t  status;
 600  611  
 601  612          if (!dladm_valid_linkname(link1))
 602  613                  return (DLADM_STATUS_LINKINVAL);
 603  614  
 604  615          status = dladm_open_conf(handle, linkid2, &conf);
 605  616          if (status != DLADM_STATUS_OK)
 606  617                  goto done;
 607  618  
 608  619          if ((status = dladm_set_conf_field(handle, conf, FDEVNAME,
 609  620              DLADM_TYPE_STR, link1)) == DLADM_STATUS_OK) {
  
    | 
      ↓ open down ↓ | 
    89 lines elided | 
    
      ↑ open up ↑ | 
  
 610  621                  status = dladm_write_conf(handle, conf);
 611  622          }
 612  623  
 613  624          dladm_destroy_conf(handle, conf);
 614  625  
 615  626  done:
 616  627          return (status);
 617  628  }
 618  629  
 619  630  dladm_status_t
 620      -dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
      631 +dladm_rename_link(dladm_handle_t handle, const char *zonename,
      632 +    const char *link1, const char *link2)
 621  633  {
 622  634          datalink_id_t           linkid1 = DATALINK_INVALID_LINKID;
 623  635          datalink_id_t           linkid2 = DATALINK_INVALID_LINKID;
 624  636          uint32_t                flags1, flags2;
 625  637          datalink_class_t        class1, class2;
 626  638          uint32_t                media1, media2;
 627  639          boolean_t               remphy2 = B_FALSE;
 628  640          dladm_status_t          status;
 629  641  
 630      -        (void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1,
 631      -            &media1);
 632      -        if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2,
 633      -            &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
 634      -            (flags2 == DLADM_OPT_PERSIST)) {
      642 +        (void) dladm_zname2info(handle, zonename, link1, &linkid1, &flags1,
      643 +            &class1, &media1);
      644 +        if ((dladm_zname2info(handle, zonename, link2, &linkid2, &flags2,
      645 +            &class2, &media2) == DLADM_STATUS_OK) &&
      646 +            (class2 == DATALINK_CLASS_PHYS) && (flags2 == DLADM_OPT_PERSIST)) {
 635  647                  /*
 636  648                   * see whether link2 is a removed physical link.
 637  649                   */
 638  650                  remphy2 = B_TRUE;
 639  651          }
 640  652  
 641  653          if (linkid1 != DATALINK_INVALID_LINKID) {
 642  654                  if (linkid2 == DATALINK_INVALID_LINKID) {
 643  655                          /*
 644  656                           * case 1: rename an existing link to a link that
 645  657                           * does not exist.
 646  658                           */
 647  659                          status = i_dladm_rename_link_c1(handle, linkid1, link1,
 648      -                            link2, flags1);
      660 +                            link2, flags1, zonename);
 649  661                  } else if (remphy2) {
 650  662                          /*
 651  663                           * case 2: rename an available link to a REMOVED
 652  664                           * physical link. Return failure if link1 is not
 653  665                           * an active physical link.
 654  666                           */
 655  667                          if ((class1 != class2) || (media1 != media2) ||
 656  668                              !(flags1 & DLADM_OPT_ACTIVE)) {
 657  669                                  status = DLADM_STATUS_BADARG;
 658  670                          } else {
 659  671                                  status = i_dladm_rename_link_c2(handle, linkid1,
 660  672                                      linkid2);
 661  673                          }
 662  674                  } else {
 663  675                          status = DLADM_STATUS_EXIST;
 664  676                  }
 665  677          } else if (remphy2) {
 666  678                  status = i_dladm_rename_link_c3(handle, link1, linkid2);
 667  679          } else {
 668  680                  status = DLADM_STATUS_NOTFOUND;
 669  681          }
 670  682          return (status);
 671  683  }
 672  684  
 673  685  typedef struct consumer_del_phys_arg_s {
 674  686          datalink_id_t   linkid;
 675  687  } consumer_del_phys_arg_t;
 676  688  
 677  689  static int
 678  690  i_dladm_vlan_link_del(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
 679  691  {
 680  692          consumer_del_phys_arg_t *del_arg = arg;
 681  693          dladm_vlan_attr_t       vinfo;
 682  694          dladm_status_t          status;
 683  695  
 684  696          status = dladm_vlan_info(handle, vlanid, &vinfo, DLADM_OPT_PERSIST);
 685  697          if (status != DLADM_STATUS_OK)
 686  698                  return (DLADM_WALK_CONTINUE);
 687  699  
 688  700          if (vinfo.dv_linkid == del_arg->linkid)
 689  701                  (void) dladm_vlan_delete(handle, vlanid, DLADM_OPT_PERSIST);
 690  702          return (DLADM_WALK_CONTINUE);
 691  703  }
 692  704  
 693  705  static int
 694  706  i_dladm_part_link_del(dladm_handle_t handle, datalink_id_t partid, void *arg)
 695  707  {
 696  708          consumer_del_phys_arg_t *del_arg = arg;
 697  709          dladm_part_attr_t       pinfo;
 698  710          dladm_status_t          status;
 699  711  
 700  712          status = dladm_part_info(handle, partid, &pinfo, DLADM_OPT_PERSIST);
 701  713          if (status != DLADM_STATUS_OK)
 702  714                  return (DLADM_WALK_CONTINUE);
 703  715  
 704  716          if (pinfo.dia_physlinkid == del_arg->linkid)
 705  717                  (void) dladm_part_delete(handle, partid, DLADM_OPT_PERSIST);
 706  718          return (DLADM_WALK_CONTINUE);
 707  719  }
 708  720  
 709  721  static int
 710  722  i_dladm_aggr_link_del(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
 711  723  {
 712  724          consumer_del_phys_arg_t         *del_arg = arg;
 713  725          dladm_aggr_grp_attr_t           ginfo;
 714  726          dladm_status_t                  status;
 715  727          dladm_aggr_port_attr_db_t       port[1];
 716  728          int                             i;
 717  729  
 718  730          status = dladm_aggr_info(handle, aggrid, &ginfo, DLADM_OPT_PERSIST);
 719  731          if (status != DLADM_STATUS_OK)
 720  732                  return (DLADM_WALK_CONTINUE);
 721  733  
 722  734          for (i = 0; i < ginfo.lg_nports; i++)
 723  735                  if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
 724  736                          break;
 725  737  
 726  738          if (i != ginfo.lg_nports) {
 727  739                  if (ginfo.lg_nports == 1 && i == 0) {
 728  740                          consumer_del_phys_arg_t aggr_del_arg;
 729  741  
 730  742                          /*
 731  743                           * First delete all the VLANs on this aggregation, then
 732  744                           * delete the aggregation itself.
 733  745                           */
 734  746                          aggr_del_arg.linkid = aggrid;
 735  747                          (void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
 736  748                              handle, &aggr_del_arg, DATALINK_CLASS_VLAN,
 737  749                              DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 738  750                          (void) dladm_aggr_delete(handle, aggrid,
 739  751                              DLADM_OPT_PERSIST);
 740  752                  } else {
 741  753                          port[0].lp_linkid = del_arg->linkid;
 742  754                          (void) dladm_aggr_remove(handle, aggrid, 1, port,
 743  755                              DLADM_OPT_PERSIST);
 744  756                  }
 745  757          }
 746  758          return (DLADM_WALK_CONTINUE);
 747  759  }
 748  760  
 749  761  typedef struct del_phys_arg_s {
 750  762          dladm_status_t  rval;
 751  763  } del_phys_arg_t;
 752  764  
 753  765  static int
 754  766  i_dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 755  767  {
 756  768          uint32_t                flags;
 757  769          datalink_class_t        class;
 758  770          uint32_t                media;
 759  771          dladm_status_t          status = DLADM_STATUS_OK;
 760  772          del_phys_arg_t          *del_phys_arg = arg;
 761  773          consumer_del_phys_arg_t del_arg;
 762  774  
 763  775          if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
 764  776              &media, NULL, 0)) != DLADM_STATUS_OK) {
 765  777                  goto done;
 766  778          }
 767  779  
 768  780          /*
 769  781           * see whether this link is a removed physical link.
 770  782           */
 771  783          if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
 772  784              (flags & DLADM_OPT_ACTIVE)) {
 773  785                  status = DLADM_STATUS_BADARG;
 774  786                  goto done;
 775  787          }
 776  788  
 777  789          if (media == DL_ETHER) {
 778  790                  del_arg.linkid = linkid;
 779  791                  (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, handle,
 780  792                      &del_arg, DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
 781  793                      DLADM_OPT_PERSIST);
 782  794                  (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, handle,
 783  795                      &del_arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
 784  796                      DLADM_OPT_PERSIST);
 785  797          } else if (media == DL_IB) {
 786  798                  del_arg.linkid = linkid;
 787  799                  (void) dladm_walk_datalink_id(i_dladm_part_link_del, handle,
 788  800                      &del_arg, DATALINK_CLASS_PART, DL_IB, DLADM_OPT_PERSIST);
 789  801          }
 790  802  
 791  803          (void) dladm_remove_conf(handle, linkid);
 792  804          (void) dladm_destroy_datalink_id(handle, linkid, DLADM_OPT_PERSIST);
 793  805  done:
 794  806          del_phys_arg->rval = status;
 795  807          return (DLADM_WALK_CONTINUE);
 796  808  }
 797  809  
 798  810  dladm_status_t
 799  811  dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid)
 800  812  {
 801  813          del_phys_arg_t  arg = {DLADM_STATUS_OK};
 802  814  
 803  815          if (linkid == DATALINK_ALL_LINKID) {
 804  816                  (void) dladm_walk_datalink_id(i_dladm_phys_delete, handle, &arg,
 805  817                      DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
 806  818                      DLADM_OPT_PERSIST);
 807  819                  return (DLADM_STATUS_OK);
 808  820          } else {
 809  821                  (void) i_dladm_phys_delete(handle, linkid, &arg);
 810  822                  return (arg.rval);
 811  823          }
 812  824  }
 813  825  
 814  826  dladm_status_t
 815  827  dladm_phys_info(dladm_handle_t handle, datalink_id_t linkid,
 816  828      dladm_phys_attr_t *dpap, uint32_t flags)
 817  829  {
 818  830          dladm_status_t  status;
 819  831  
 820  832          assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
 821  833  
 822  834          switch (flags) {
 823  835          case DLADM_OPT_PERSIST: {
 824  836                  dladm_conf_t    conf;
 825  837  
 826  838                  status = dladm_getsnap_conf(handle, linkid, &conf);
 827  839                  if (status != DLADM_STATUS_OK)
 828  840                          return (status);
 829  841  
 830  842                  status = dladm_get_conf_field(handle, conf, FDEVNAME,
 831  843                      dpap->dp_dev, MAXLINKNAMELEN);
 832  844                  dladm_destroy_conf(handle, conf);
 833  845                  return (status);
 834  846          }
 835  847          case DLADM_OPT_ACTIVE: {
 836  848                  dld_ioc_phys_attr_t     dip;
 837  849  
 838  850                  dip.dip_linkid = linkid;
 839  851                  if (ioctl(dladm_dld_fd(handle), DLDIOC_PHYS_ATTR, &dip) < 0) {
 840  852                          status = dladm_errno2status(errno);
 841  853                          return (status);
 842  854                  }
 843  855                  dpap->dp_novanity = dip.dip_novanity;
 844  856                  (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
 845  857                  return (DLADM_STATUS_OK);
 846  858          }
 847  859          default:
 848  860                  return (DLADM_STATUS_BADARG);
 849  861          }
 850  862  }
 851  863  
 852  864  typedef struct i_walk_dev_state_s {
 853  865          const char *devname;
 854  866          datalink_id_t linkid;
 855  867          boolean_t found;
 856  868  } i_walk_dev_state_t;
 857  869  
 858  870  int
 859  871  i_dladm_walk_dev2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 860  872  {
 861  873          dladm_phys_attr_t dpa;
 862  874          dladm_status_t status;
 863  875          i_walk_dev_state_t *statep = arg;
 864  876  
 865  877          status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_PERSIST);
 866  878          if ((status == DLADM_STATUS_OK) &&
 867  879              (strcmp(statep->devname, dpa.dp_dev) == 0)) {
 868  880                  statep->found = B_TRUE;
 869  881                  statep->linkid = linkid;
 870  882                  return (DLADM_WALK_TERMINATE);
 871  883          }
 872  884          return (DLADM_WALK_CONTINUE);
 873  885  }
 874  886  
 875  887  /*
 876  888   * Get the linkid from the physical device name.
 877  889   */
 878  890  dladm_status_t
 879  891  dladm_dev2linkid(dladm_handle_t handle, const char *devname,
 880  892      datalink_id_t *linkidp)
 881  893  {
 882  894          i_walk_dev_state_t state;
 883  895  
 884  896          state.found = B_FALSE;
 885  897          state.devname = devname;
 886  898  
 887  899          (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, handle, &state,
 888  900              DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 889  901          if (state.found == B_TRUE) {
 890  902                  *linkidp = state.linkid;
 891  903                  return (DLADM_STATUS_OK);
 892  904          } else {
 893  905                  return (dladm_errno2status(ENOENT));
 894  906          }
 895  907  }
 896  908  
 897  909  static int
 898  910  parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
 899  911  {
 900  912          char    *cp, *tp;
 901  913          int     len;
 902  914  
 903  915          /*
 904  916           * device name length must not be 0, and it must end with digit.
 905  917           */
 906  918          if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
 907  919                  return (EINVAL);
 908  920  
 909  921          (void) strlcpy(driver, devname, maxlen);
 910  922          cp = (char *)&driver[len - 1];
 911  923  
 912  924          for (tp = cp; isdigit(*tp); tp--) {
 913  925                  if (tp <= driver)
 914  926                          return (EINVAL);
 915  927          }
 916  928  
 917  929          *ppa = atoi(tp + 1);
 918  930          *(tp + 1) = '\0';
 919  931          return (0);
 920  932  }
 921  933  
 922  934  dladm_status_t
 923  935  dladm_linkid2legacyname(dladm_handle_t handle, datalink_id_t linkid, char *dev,
 924  936      size_t len)
 925  937  {
 926  938          char                    devname[MAXLINKNAMELEN];
 927  939          uint16_t                vid = VLAN_ID_NONE;
 928  940          datalink_class_t        class;
 929  941          dladm_status_t          status;
 930  942  
 931  943          status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
 932  944              NULL, 0);
 933  945          if (status != DLADM_STATUS_OK)
 934  946                  goto done;
 935  947  
 936  948          /*
 937  949           * If this is a VLAN, we must first determine the class and linkid of
 938  950           * the link the VLAN has been created over.
 939  951           */
 940  952          if (class == DATALINK_CLASS_VLAN) {
 941  953                  dladm_vlan_attr_t       dva;
 942  954  
 943  955                  status = dladm_vlan_info(handle, linkid, &dva,
 944  956                      DLADM_OPT_ACTIVE);
 945  957                  if (status != DLADM_STATUS_OK)
 946  958                          goto done;
 947  959                  linkid = dva.dv_linkid;
 948  960                  vid = dva.dv_vid;
 949  961  
 950  962                  if ((status = dladm_datalink_id2info(handle, linkid, NULL,
 951  963                      &class, NULL, NULL, 0)) != DLADM_STATUS_OK) {
 952  964                          goto done;
 953  965                  }
 954  966          }
 955  967  
 956  968          switch (class) {
 957  969          case DATALINK_CLASS_AGGR: {
 958  970                  dladm_aggr_grp_attr_t   dga;
 959  971  
 960  972                  status = dladm_aggr_info(handle, linkid, &dga,
 961  973                      DLADM_OPT_ACTIVE);
 962  974                  if (status != DLADM_STATUS_OK)
 963  975                          goto done;
 964  976  
 965  977                  if (dga.lg_key == 0) {
 966  978                          /*
 967  979                           * If the key was not specified when the aggregation
 968  980                           * is created, we cannot guess its /dev node name.
 969  981                           */
 970  982                          status = DLADM_STATUS_BADARG;
 971  983                          goto done;
 972  984                  }
 973  985                  (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
 974  986                  break;
 975  987          }
 976  988          case DATALINK_CLASS_PHYS: {
 977  989                  dladm_phys_attr_t       dpa;
 978  990  
 979  991                  status = dladm_phys_info(handle, linkid, &dpa,
 980  992                      DLADM_OPT_PERSIST);
 981  993                  if (status != DLADM_STATUS_OK)
 982  994                          goto done;
 983  995  
 984  996                  (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
 985  997                  break;
 986  998          }
 987  999          default:
 988 1000                  status = DLADM_STATUS_BADARG;
 989 1001                  goto done;
 990 1002          }
 991 1003  
 992 1004          if (vid != VLAN_ID_NONE) {
 993 1005                  char            drv[MAXNAMELEN];
 994 1006                  uint_t          ppa;
 995 1007  
 996 1008                  if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
 997 1009                          status = DLADM_STATUS_BADARG;
 998 1010                          goto done;
 999 1011                  }
1000 1012                  if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
1001 1013                          status = DLADM_STATUS_TOOSMALL;
1002 1014          } else {
1003 1015                  if (strlcpy(dev, devname, len) >= len)
1004 1016                          status = DLADM_STATUS_TOOSMALL;
1005 1017          }
1006 1018  
1007 1019  done:
1008 1020          return (status);
1009 1021  }
1010 1022  
1011 1023  dladm_status_t
1012 1024  dladm_parselink(const char *dev, char *provider, uint_t *ppa)
1013 1025  {
1014 1026          ifspec_t        ifsp;
1015 1027  
1016 1028          if (dev == NULL || !ifparse_ifspec(dev, &ifsp))
1017 1029                  return (DLADM_STATUS_LINKINVAL);
1018 1030  
1019 1031          if (provider != NULL)
1020 1032                  (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
1021 1033  
1022 1034          if (ppa != NULL)
1023 1035                  *ppa = ifsp.ifsp_ppa;
1024 1036  
1025 1037          return (DLADM_STATUS_OK);
1026 1038  }
  
    | 
      ↓ open down ↓ | 
    368 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX