18275SEric Cheng /* 28275SEric Cheng * CDDL HEADER START 38275SEric Cheng * 48275SEric Cheng * The contents of this file are subject to the terms of the 58275SEric Cheng * Common Development and Distribution License (the "License"). 68275SEric Cheng * You may not use this file except in compliance with the License. 78275SEric Cheng * 88275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98275SEric Cheng * or http://www.opensolaris.org/os/licensing. 108275SEric Cheng * See the License for the specific language governing permissions 118275SEric Cheng * and limitations under the License. 128275SEric Cheng * 138275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 148275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the 168275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 178275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 188275SEric Cheng * 198275SEric Cheng * CDDL HEADER END 208275SEric Cheng */ 218275SEric Cheng /* 228558SGirish.Moodalbail@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238275SEric Cheng * Use is subject to license terms. 248275SEric Cheng */ 258275SEric Cheng 268275SEric Cheng #include <stdio.h> 278275SEric Cheng #include <sys/types.h> 288275SEric Cheng #include <sys/socket.h> 298275SEric Cheng #include <sys/ethernet.h> 308275SEric Cheng #include <netinet/in.h> 318275SEric Cheng #include <arpa/inet.h> 328275SEric Cheng #include <sys/stat.h> 33*9107Sjames.d.carlson@sun.com #include <sys/dld_ioc.h> 348275SEric Cheng #include <string.h> 358275SEric Cheng #include <fcntl.h> 368275SEric Cheng #include <unistd.h> 378275SEric Cheng #include <stropts.h> 388275SEric Cheng #include <stdlib.h> 398275SEric Cheng #include <errno.h> 408275SEric Cheng #include <strings.h> 418275SEric Cheng #include <libintl.h> 428275SEric Cheng #include <netdb.h> 438275SEric Cheng #include <net/if_types.h> 448275SEric Cheng #include <net/if_dl.h> 458275SEric Cheng #include <inet/ip.h> 468275SEric Cheng #include <inet/ip6.h> 478275SEric Cheng #include <libdlflow.h> 488275SEric Cheng #include <libdlflow_impl.h> 498275SEric Cheng #include <libdladm_impl.h> 508275SEric Cheng 518275SEric Cheng /* minimum buffer size for DLDIOCWALKFLOW */ 528275SEric Cheng #define MIN_INFO_SIZE (4 * 1024) 538275SEric Cheng 548275SEric Cheng #define DLADM_FLOW_DB "/etc/dladm/flowadm.conf" 558275SEric Cheng #define DLADM_FLOW_DB_TMP "/etc/dladm/flowadm.conf.new" 568275SEric Cheng #define DLADM_FLOW_DB_LOCK "/tmp/flowadm.conf.lock" 578275SEric Cheng 588275SEric Cheng #define DLADM_FLOW_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 598275SEric Cheng #define DLADM_FLOW_DB_OWNER UID_DLADM 608275SEric Cheng #define DLADM_FLOW_DB_GROUP GID_SYS 618275SEric Cheng 628275SEric Cheng #define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n')) 638275SEric Cheng #define MAXLINELEN 1024 648275SEric Cheng #define MAXPATHLEN 1024 658275SEric Cheng 668275SEric Cheng #define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3]) 678275SEric Cheng 688275SEric Cheng /* database file parameters */ 698275SEric Cheng static const char *BW_LIMIT = "bw_limit"; 708275SEric Cheng static const char *PRIORITY = "priority"; 718275SEric Cheng static const char *LOCAL_IP_ADDR = "local_ip"; 728275SEric Cheng static const char *REMOTE_IP_ADDR = "remote_ip"; 738275SEric Cheng static const char *TRANSPORT = "transport"; 748275SEric Cheng static const char *LOCAL_PORT = "local_port"; 758275SEric Cheng static const char *DSFIELD = "dsfield"; 768275SEric Cheng 778275SEric Cheng /* 788275SEric Cheng * Open and lock the flowadm configuration file lock. The lock is 798275SEric Cheng * acquired as a reader (F_RDLCK) or writer (F_WRLCK). 808275SEric Cheng */ 818275SEric Cheng static int 828275SEric Cheng i_dladm_flow_lock_db(short type) 838275SEric Cheng { 848275SEric Cheng int lock_fd; 858275SEric Cheng struct flock lock; 868275SEric Cheng 878275SEric Cheng if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC, 888275SEric Cheng DLADM_FLOW_DB_PERMS)) < 0) 898275SEric Cheng return (-1); 908275SEric Cheng 918275SEric Cheng lock.l_type = type; 928275SEric Cheng lock.l_whence = SEEK_SET; 938275SEric Cheng lock.l_start = 0; 948275SEric Cheng lock.l_len = 0; 958275SEric Cheng 968275SEric Cheng if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 978275SEric Cheng (void) close(lock_fd); 988275SEric Cheng (void) unlink(DLADM_FLOW_DB_LOCK); 998275SEric Cheng return (-1); 1008275SEric Cheng } 1018275SEric Cheng return (lock_fd); 1028275SEric Cheng } 1038275SEric Cheng 1048275SEric Cheng /* 1058275SEric Cheng * Unlock and close the specified file. 1068275SEric Cheng */ 1078275SEric Cheng static void 1088275SEric Cheng i_dladm_flow_unlock_db(int fd) 1098275SEric Cheng { 1108275SEric Cheng struct flock lock; 1118275SEric Cheng 1128275SEric Cheng if (fd < 0) 1138275SEric Cheng return; 1148275SEric Cheng 1158275SEric Cheng lock.l_type = F_UNLCK; 1168275SEric Cheng lock.l_whence = SEEK_SET; 1178275SEric Cheng lock.l_start = 0; 1188275SEric Cheng lock.l_len = 0; 1198275SEric Cheng 1208275SEric Cheng (void) fcntl(fd, F_SETLKW, &lock); 1218275SEric Cheng (void) close(fd); 1228275SEric Cheng (void) unlink(DLADM_FLOW_DB_LOCK); 1238275SEric Cheng } 1248275SEric Cheng 1258275SEric Cheng /* 1268275SEric Cheng * Parse one line of the link flowadm DB 1278275SEric Cheng * Returns -1 on failure, 0 on success. 1288275SEric Cheng */ 1298275SEric Cheng dladm_status_t 1308275SEric Cheng dladm_flow_parse_db(char *line, dld_flowinfo_t *attr) 1318275SEric Cheng { 1328275SEric Cheng char *token; 1338275SEric Cheng char *value, *name = NULL; 1348275SEric Cheng char *endp = NULL; 1358275SEric Cheng char *lasts = NULL; 1368275SEric Cheng dladm_status_t status = DLADM_STATUS_FLOW_DB_PARSE_ERR; 1378275SEric Cheng 1388275SEric Cheng bzero(attr, sizeof (*attr)); 1398275SEric Cheng 1408275SEric Cheng /* flow name */ 1418275SEric Cheng if ((token = strtok_r(line, " \t", &lasts)) == NULL) 1428275SEric Cheng goto done; 1438275SEric Cheng 1448558SGirish.Moodalbail@Sun.COM if (strlcpy(attr->fi_flowname, token, MAXFLOWNAMELEN) >= MAXFLOWNAMELEN) 1458275SEric Cheng goto done; 1468275SEric Cheng 1478275SEric Cheng /* resource control and flow descriptor parameters */ 1488275SEric Cheng while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) { 1498275SEric Cheng if ((name = strdup(token)) == NULL) 1508275SEric Cheng goto done; 1518275SEric Cheng 1528275SEric Cheng (void) strtok(name, "="); 1538275SEric Cheng value = strtok(NULL, "="); 1548275SEric Cheng if (value == NULL) 1558275SEric Cheng goto done; 1568275SEric Cheng 1578275SEric Cheng if (strcmp(name, "linkid") == 0) { 1588275SEric Cheng if ((attr->fi_linkid = 1598275SEric Cheng (uint32_t)strtol(value, &endp, 10)) == 1608275SEric Cheng DATALINK_INVALID_LINKID) 1618275SEric Cheng goto done; 1628275SEric Cheng 1638275SEric Cheng } else if (strcmp(name, BW_LIMIT) == 0) { 1648275SEric Cheng attr->fi_resource_props.mrp_mask |= 1658275SEric Cheng MRP_MAXBW; 1668275SEric Cheng attr->fi_resource_props.mrp_maxbw = 1678275SEric Cheng (uint64_t)strtol(value, &endp, 0); 1688275SEric Cheng 1698275SEric Cheng } else if (strcmp(name, PRIORITY) == 0) { 1708275SEric Cheng attr->fi_resource_props.mrp_mask |= MRP_PRIORITY; 1718275SEric Cheng status = dladm_str2pri(value, 1728275SEric Cheng &attr->fi_resource_props.mrp_priority); 1738275SEric Cheng if (status != DLADM_STATUS_OK) 1748275SEric Cheng goto done; 1758275SEric Cheng 1768275SEric Cheng } else if (strcmp(name, DSFIELD) == 0) { 1778275SEric Cheng status = do_check_dsfield(value, 1788275SEric Cheng &attr->fi_flow_desc); 1798275SEric Cheng if (status != DLADM_STATUS_OK) 1808275SEric Cheng goto done; 1818275SEric Cheng 1828275SEric Cheng } else if (strcmp(name, LOCAL_IP_ADDR) == 0) { 1838275SEric Cheng status = do_check_ip_addr(value, B_TRUE, 1848275SEric Cheng &attr->fi_flow_desc); 1858275SEric Cheng if (status != DLADM_STATUS_OK) 1868275SEric Cheng goto done; 1878275SEric Cheng 1888275SEric Cheng } else if (strcmp(name, REMOTE_IP_ADDR) == 0) { 1898275SEric Cheng status = do_check_ip_addr(value, B_FALSE, 1908275SEric Cheng &attr->fi_flow_desc); 1918275SEric Cheng if (status != DLADM_STATUS_OK) 1928275SEric Cheng goto done; 1938275SEric Cheng 1948275SEric Cheng } else if (strcmp(name, TRANSPORT) == 0) { 1958275SEric Cheng attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL; 1968275SEric Cheng attr->fi_flow_desc.fd_protocol = 1978275SEric Cheng (uint8_t)strtol(value, &endp, 0); 1988275SEric Cheng 1998275SEric Cheng } else if (strcmp(name, LOCAL_PORT) == 0) { 2008275SEric Cheng attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL; 2018275SEric Cheng attr->fi_flow_desc.fd_local_port = 2028275SEric Cheng (uint16_t)strtol(value, &endp, 10); 2038275SEric Cheng attr->fi_flow_desc.fd_local_port = 2048275SEric Cheng htons(attr->fi_flow_desc.fd_local_port); 2058275SEric Cheng } 2068275SEric Cheng free(name); 2078275SEric Cheng name = NULL; 2088275SEric Cheng } 2098275SEric Cheng if (attr->fi_linkid != DATALINK_INVALID_LINKID) 2108275SEric Cheng status = DLADM_STATUS_OK; 2118275SEric Cheng done: 2128275SEric Cheng free(name); 2138275SEric Cheng return (status); 2148275SEric Cheng } 2158275SEric Cheng 2168275SEric Cheng #define FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1); 2178275SEric Cheng 2188275SEric Cheng /* 2198275SEric Cheng * Write the attribute of a group to the specified file. Returns 0 on 2208275SEric Cheng * success, -1 on failure. 2218275SEric Cheng */ 2228275SEric Cheng static int 2238275SEric Cheng i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr) 2248275SEric Cheng { 2258275SEric Cheng 2268275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t", 2278275SEric Cheng attr->fi_flowname, attr->fi_linkid)); 2288275SEric Cheng 2298275SEric Cheng /* flow policy */ 2308275SEric Cheng if (attr->fi_resource_props.mrp_mask & MRP_MAXBW) 2318275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT, 2328275SEric Cheng attr->fi_resource_props.mrp_maxbw)); 2338275SEric Cheng 2348275SEric Cheng if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY) 2358275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY, 2368275SEric Cheng attr->fi_resource_props.mrp_priority)); 2378275SEric Cheng 2388275SEric Cheng /* flow descriptor */ 2398275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD) 2408275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD, 2418275SEric Cheng attr->fi_flow_desc.fd_dsfield, 2428275SEric Cheng attr->fi_flow_desc.fd_dsfield_mask)); 2438275SEric Cheng 2448275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) { 2458275SEric Cheng char abuf[INET6_ADDRSTRLEN], *ap; 2468275SEric Cheng struct in_addr ipaddr; 2478275SEric Cheng int prefix_len, prefix_max; 2488275SEric Cheng 2498275SEric Cheng if (attr->fi_flow_desc.fd_ipversion != 6) { 2508275SEric Cheng ipaddr.s_addr = 2518275SEric Cheng attr->fi_flow_desc. 2528275SEric Cheng fd_local_addr._S6_un._S6_u32[3]; 2538275SEric Cheng 2548275SEric Cheng ap = inet_ntoa(ipaddr); 2558275SEric Cheng prefix_max = IP_ABITS; 2568275SEric Cheng } else { 2578275SEric Cheng (void) inet_ntop(AF_INET6, 2588275SEric Cheng &attr->fi_flow_desc.fd_local_addr, 2598275SEric Cheng abuf, INET6_ADDRSTRLEN); 2608275SEric Cheng 2618275SEric Cheng ap = abuf; 2628275SEric Cheng prefix_max = IPV6_ABITS; 2638275SEric Cheng } 2648275SEric Cheng (void) dladm_mask2prefixlen( 2658275SEric Cheng &attr->fi_flow_desc.fd_local_netmask, prefix_max, 2668275SEric Cheng &prefix_len); 2678275SEric Cheng 2688275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR, 2698275SEric Cheng ap, prefix_len)); 2708275SEric Cheng } 2718275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) { 2728275SEric Cheng char abuf[INET6_ADDRSTRLEN], *ap; 2738275SEric Cheng struct in_addr ipaddr; 2748275SEric Cheng int prefix_len, prefix_max; 2758275SEric Cheng 2768275SEric Cheng if (attr->fi_flow_desc.fd_ipversion != 6) { 2778275SEric Cheng ipaddr.s_addr = 2788275SEric Cheng attr->fi_flow_desc. 2798275SEric Cheng fd_remote_addr._S6_un._S6_u32[3]; 2808275SEric Cheng 2818275SEric Cheng ap = inet_ntoa(ipaddr); 2828275SEric Cheng prefix_max = IP_ABITS; 2838275SEric Cheng } else { 2848275SEric Cheng (void) inet_ntop(AF_INET6, 2858275SEric Cheng &(attr->fi_flow_desc.fd_remote_addr), 2868275SEric Cheng abuf, INET6_ADDRSTRLEN); 2878275SEric Cheng 2888275SEric Cheng ap = abuf; 2898275SEric Cheng prefix_max = IPV6_ABITS; 2908275SEric Cheng } 2918275SEric Cheng (void) dladm_mask2prefixlen( 2928275SEric Cheng &attr->fi_flow_desc.fd_remote_netmask, prefix_max, 2938275SEric Cheng &prefix_len); 2948275SEric Cheng 2958275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR, 2968275SEric Cheng ap, prefix_len)); 2978275SEric Cheng } 2988275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL) 2998275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT, 3008275SEric Cheng attr->fi_flow_desc.fd_protocol)); 3018275SEric Cheng 3028275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) 3038275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT, 3048275SEric Cheng ntohs(attr->fi_flow_desc.fd_local_port))); 3058275SEric Cheng 3068275SEric Cheng FPRINTF_ERR(fprintf(fp, "\n")); 3078275SEric Cheng 3088275SEric Cheng return (0); 3098275SEric Cheng 3108275SEric Cheng } 3118275SEric Cheng 3128275SEric Cheng static dladm_status_t 3138275SEric Cheng i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *), 3148275SEric Cheng void *arg, 3158275SEric Cheng const char *root) 3168275SEric Cheng { 3178275SEric Cheng FILE *fp, *nfp; 3188275SEric Cheng int nfd, fn_rc, lock_fd; 3198275SEric Cheng char line[MAXLINELEN]; 3208275SEric Cheng dld_flowinfo_t attr; 3218275SEric Cheng char *db_file, *tmp_db_file; 3228275SEric Cheng char db_file_buf[MAXPATHLEN]; 3238275SEric Cheng char tmp_db_file_buf[MAXPATHLEN]; 3248275SEric Cheng dladm_status_t status = DLADM_STATUS_FLOW_DB_ERR; 3258275SEric Cheng 3268275SEric Cheng if (root == NULL) { 3278275SEric Cheng db_file = DLADM_FLOW_DB; 3288275SEric Cheng tmp_db_file = DLADM_FLOW_DB_TMP; 3298275SEric Cheng } else { 3308275SEric Cheng (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 3318275SEric Cheng DLADM_FLOW_DB); 3328275SEric Cheng (void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root, 3338275SEric Cheng DLADM_FLOW_DB_TMP); 3348275SEric Cheng db_file = db_file_buf; 3358275SEric Cheng tmp_db_file = tmp_db_file_buf; 3368275SEric Cheng } 3378275SEric Cheng 3388275SEric Cheng if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0) 3398275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 3408275SEric Cheng 3418275SEric Cheng if ((fp = fopen(db_file, "r")) == NULL) { 3428275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3438275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3448275SEric Cheng } 3458275SEric Cheng 3468275SEric Cheng if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC, 3478275SEric Cheng DLADM_FLOW_DB_PERMS)) == -1) { 3488275SEric Cheng (void) fclose(fp); 3498275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3508275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3518275SEric Cheng } 3528275SEric Cheng 3538275SEric Cheng if ((nfp = fdopen(nfd, "w")) == NULL) { 3548275SEric Cheng (void) close(nfd); 3558275SEric Cheng (void) fclose(fp); 3568275SEric Cheng (void) unlink(tmp_db_file); 3578275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3588275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3598275SEric Cheng } 3608275SEric Cheng 3618275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 3628275SEric Cheng 3638275SEric Cheng /* skip comments */ 3648275SEric Cheng if (BLANK_LINE(line)) { 3658275SEric Cheng if (fputs(line, nfp) == EOF) 3668275SEric Cheng goto failed; 3678275SEric Cheng continue; 3688275SEric Cheng } 3698275SEric Cheng (void) strtok(line, " \n"); 3708275SEric Cheng 3718275SEric Cheng if ((status = dladm_flow_parse_db(line, &attr)) != 3728275SEric Cheng DLADM_STATUS_OK) 3738275SEric Cheng goto failed; 3748275SEric Cheng 3758275SEric Cheng fn_rc = fn(arg, &attr); 3768275SEric Cheng 3778275SEric Cheng switch (fn_rc) { 3788275SEric Cheng case -1: 3798275SEric Cheng /* failure, stop walking */ 3808275SEric Cheng goto failed; 3818275SEric Cheng case 0: 3828275SEric Cheng /* 3838275SEric Cheng * Success, write group attributes, which could 3848275SEric Cheng * have been modified by fn(). 3858275SEric Cheng */ 3868275SEric Cheng if (i_dladm_flow_fput_grp(nfp, &attr) != 0) 3878275SEric Cheng goto failed; 3888275SEric Cheng break; 3898275SEric Cheng case 1: 3908275SEric Cheng /* skip current group */ 3918275SEric Cheng break; 3928275SEric Cheng } 3938275SEric Cheng } 3948275SEric Cheng if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1) 3958275SEric Cheng goto failed; 3968275SEric Cheng 3978275SEric Cheng if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1) 3988275SEric Cheng goto failed; 3998275SEric Cheng 4008275SEric Cheng if (fflush(nfp) == EOF) 4018275SEric Cheng goto failed; 4028275SEric Cheng 4038275SEric Cheng (void) fclose(fp); 4048275SEric Cheng (void) fclose(nfp); 4058275SEric Cheng 4068275SEric Cheng if (rename(tmp_db_file, db_file) == -1) { 4078275SEric Cheng (void) unlink(tmp_db_file); 4088275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4098275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 4108275SEric Cheng } 4118275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4128275SEric Cheng return (DLADM_STATUS_OK); 4138275SEric Cheng 4148275SEric Cheng failed: 4158275SEric Cheng (void) fclose(fp); 4168275SEric Cheng (void) fclose(nfp); 4178275SEric Cheng (void) unlink(tmp_db_file); 4188275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4198275SEric Cheng 4208275SEric Cheng return (status); 4218275SEric Cheng } 4228275SEric Cheng 4238275SEric Cheng /* 4248275SEric Cheng * Remove existing flow from DB. 4258275SEric Cheng */ 4268275SEric Cheng 4278275SEric Cheng typedef struct remove_db_state { 4288275SEric Cheng dld_flowinfo_t rs_newattr; 4298275SEric Cheng dld_flowinfo_t rs_oldattr; 4308275SEric Cheng boolean_t rs_found; 4318275SEric Cheng } remove_db_state_t; 4328275SEric Cheng 4338275SEric Cheng static int 4348275SEric Cheng i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp) 4358275SEric Cheng { 4368275SEric Cheng remove_db_state_t *state = (remove_db_state_t *)arg; 4378275SEric Cheng dld_flowinfo_t *attr = &state->rs_newattr; 4388275SEric Cheng 4398275SEric Cheng if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0) 4408275SEric Cheng return (0); 4418275SEric Cheng else { 4428275SEric Cheng bcopy(grp, &state->rs_oldattr, 4438275SEric Cheng sizeof (dld_flowinfo_t)); 4448275SEric Cheng state->rs_found = B_TRUE; 4458275SEric Cheng return (1); 4468275SEric Cheng } 4478275SEric Cheng } 4488275SEric Cheng 4498275SEric Cheng /* ARGSUSED */ 4508275SEric Cheng static int 4518275SEric Cheng i_dladm_flow_remove_db(remove_db_state_t *state, const char *root) 4528275SEric Cheng { 4538275SEric Cheng if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root) 4548275SEric Cheng != 0) 4558275SEric Cheng return (-1); 4568275SEric Cheng 4578275SEric Cheng if (!state->rs_found) { 4588275SEric Cheng errno = ENOENT; 4598275SEric Cheng return (-1); 4608275SEric Cheng } 4618275SEric Cheng 4628275SEric Cheng return (0); 4638275SEric Cheng } 4648275SEric Cheng 4658275SEric Cheng /* 4668275SEric Cheng * Create a flow in the DB. 4678275SEric Cheng */ 4688275SEric Cheng 4698275SEric Cheng typedef struct modify_db_state { 4708275SEric Cheng dld_flowinfo_t ms_newattr; 4718275SEric Cheng dld_flowinfo_t ms_oldattr; 4728275SEric Cheng boolean_t ms_found; 4738275SEric Cheng } modify_db_state_t; 4748275SEric Cheng 4758275SEric Cheng static dladm_status_t 4768275SEric Cheng i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root) 4778275SEric Cheng { 4788275SEric Cheng FILE *fp; 4798275SEric Cheng char line[MAXLINELEN]; 4808275SEric Cheng char *db_file; 4818275SEric Cheng char db_file_buf[MAXPATHLEN]; 4828275SEric Cheng int lock_fd; 4838275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 4848275SEric Cheng 4858275SEric Cheng if (root == NULL) { 4868275SEric Cheng db_file = DLADM_FLOW_DB; 4878275SEric Cheng } else { 4888275SEric Cheng (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 4898275SEric Cheng DLADM_FLOW_DB); 4908275SEric Cheng db_file = db_file_buf; 4918275SEric Cheng } 4928275SEric Cheng 4938275SEric Cheng if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0) 4948275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 4958275SEric Cheng 4968275SEric Cheng if ((fp = fopen(db_file, "r+")) == NULL && 4978275SEric Cheng (fp = fopen(db_file, "w")) == NULL) { 4988275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4998275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 5008275SEric Cheng } 5018275SEric Cheng 5028275SEric Cheng /* look for existing group with same flowname */ 5038275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 5048275SEric Cheng char *holder, *lasts; 5058275SEric Cheng 5068275SEric Cheng /* skip comments */ 5078275SEric Cheng if (BLANK_LINE(line)) 5088275SEric Cheng continue; 5098275SEric Cheng 5108275SEric Cheng /* ignore corrupted lines */ 5118275SEric Cheng holder = strtok_r(line, " \t", &lasts); 5128275SEric Cheng if (holder == NULL) 5138275SEric Cheng continue; 5148275SEric Cheng 5158275SEric Cheng /* flow id */ 5168275SEric Cheng if (strcmp(holder, attr->fi_flowname) == 0) { 5178275SEric Cheng /* group with flow id already exists */ 5188275SEric Cheng status = DLADM_STATUS_PERSIST_FLOW_EXISTS; 5198275SEric Cheng goto failed; 5208275SEric Cheng } 5218275SEric Cheng } 5228275SEric Cheng /* 5238275SEric Cheng * If we get here, we've verified that no existing group with 5248275SEric Cheng * the same flow id already exists. Its now time to add the new 5258275SEric Cheng * group to the DB. 5268275SEric Cheng */ 5278275SEric Cheng if (i_dladm_flow_fput_grp(fp, attr) != 0) 5288275SEric Cheng status = DLADM_STATUS_FLOW_DB_PARSE_ERR; 5298275SEric Cheng 5308275SEric Cheng failed: 5318275SEric Cheng (void) fclose(fp); 5328275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 5338275SEric Cheng return (status); 5348275SEric Cheng } 5358275SEric Cheng 5368275SEric Cheng static dladm_status_t 5378453SAnurag.Maskey@Sun.COM i_dladm_flow_add(dladm_handle_t handle, char *flowname, datalink_id_t linkid, 5388453SAnurag.Maskey@Sun.COM flow_desc_t *flowdesc, mac_resource_props_t *mrp) 5398275SEric Cheng { 5408275SEric Cheng dld_ioc_addflow_t attr; 5418275SEric Cheng 5428275SEric Cheng /* create flow */ 5438275SEric Cheng bzero(&attr, sizeof (attr)); 5448275SEric Cheng bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t)); 5458275SEric Cheng if (mrp != NULL) { 5468275SEric Cheng bcopy(mrp, &attr.af_resource_props, 5478275SEric Cheng sizeof (mac_resource_props_t)); 5488275SEric Cheng } 5498275SEric Cheng 5508275SEric Cheng (void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name)); 5518275SEric Cheng attr.af_linkid = linkid; 5528275SEric Cheng 5538453SAnurag.Maskey@Sun.COM if (ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &attr) < 0) 5548275SEric Cheng return (dladm_errno2status(errno)); 5558275SEric Cheng 5568275SEric Cheng return (DLADM_STATUS_OK); 5578275SEric Cheng } 5588275SEric Cheng 5598275SEric Cheng static dladm_status_t 5608453SAnurag.Maskey@Sun.COM i_dladm_flow_remove(dladm_handle_t handle, char *flowname) 5618275SEric Cheng { 5628275SEric Cheng dld_ioc_removeflow_t attr; 5638275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 5648275SEric Cheng 5658275SEric Cheng (void) strlcpy(attr.rf_name, flowname, 5668275SEric Cheng sizeof (attr.rf_name)); 5678275SEric Cheng 5688453SAnurag.Maskey@Sun.COM if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0) 5698275SEric Cheng status = dladm_errno2status(errno); 5708275SEric Cheng 5718275SEric Cheng return (status); 5728275SEric Cheng } 5738275SEric Cheng 5748275SEric Cheng 5758275SEric Cheng /* ARGSUSED */ 5768275SEric Cheng dladm_status_t 5778453SAnurag.Maskey@Sun.COM dladm_flow_add(dladm_handle_t handle, datalink_id_t linkid, 5788453SAnurag.Maskey@Sun.COM dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname, 5798453SAnurag.Maskey@Sun.COM boolean_t tempop, const char *root) 5808275SEric Cheng { 5818275SEric Cheng dld_flowinfo_t db_attr; 5828275SEric Cheng flow_desc_t flowdesc; 5838275SEric Cheng mac_resource_props_t mrp; 5848275SEric Cheng dladm_status_t status; 5858275SEric Cheng 5868275SEric Cheng /* Extract flow attributes from attrlist */ 5878275SEric Cheng bzero(&flowdesc, sizeof (flow_desc_t)); 5888275SEric Cheng if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist, 5898275SEric Cheng &flowdesc)) != DLADM_STATUS_OK) { 5908275SEric Cheng return (status); 5918275SEric Cheng } 5928275SEric Cheng 5938275SEric Cheng /* Extract resource_ctl and cpu_list from proplist */ 5948275SEric Cheng bzero(&mrp, sizeof (mac_resource_props_t)); 5958275SEric Cheng if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist, 5968275SEric Cheng &mrp)) != DLADM_STATUS_OK) { 5978275SEric Cheng return (status); 5988275SEric Cheng } 5998275SEric Cheng 6008275SEric Cheng /* Add flow in kernel */ 6018453SAnurag.Maskey@Sun.COM status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp); 6028275SEric Cheng if (status != DLADM_STATUS_OK) 6038275SEric Cheng return (status); 6048275SEric Cheng 6058275SEric Cheng /* Add flow to DB */ 6068275SEric Cheng if (!tempop) { 6078275SEric Cheng bzero(&db_attr, sizeof (db_attr)); 6088275SEric Cheng bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t)); 6098275SEric Cheng (void) strlcpy(db_attr.fi_flowname, flowname, 6108275SEric Cheng sizeof (db_attr.fi_flowname)); 6118275SEric Cheng db_attr.fi_linkid = linkid; 6128275SEric Cheng 6138275SEric Cheng if ((status = i_dladm_flow_create_db(&db_attr, root)) != 6148275SEric Cheng DLADM_STATUS_OK) { 6158453SAnurag.Maskey@Sun.COM (void) i_dladm_flow_remove(handle, flowname); 6168275SEric Cheng return (status); 6178275SEric Cheng } 6188275SEric Cheng /* set flow properties */ 6198275SEric Cheng if (proplist != NULL) { 6208453SAnurag.Maskey@Sun.COM status = i_dladm_set_flow_proplist_db(handle, flowname, 6218275SEric Cheng proplist); 6228275SEric Cheng if (status != DLADM_STATUS_OK) { 6238453SAnurag.Maskey@Sun.COM (void) i_dladm_flow_remove(handle, flowname); 6248275SEric Cheng return (status); 6258275SEric Cheng } 6268275SEric Cheng } 6278275SEric Cheng } 6288275SEric Cheng return (status); 6298275SEric Cheng } 6308275SEric Cheng 6318275SEric Cheng /* 6328275SEric Cheng * Remove a flow. 6338275SEric Cheng */ 6348275SEric Cheng /* ARGSUSED */ 6358275SEric Cheng dladm_status_t 6368453SAnurag.Maskey@Sun.COM dladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop, 6378275SEric Cheng const char *root) 6388275SEric Cheng { 6398275SEric Cheng remove_db_state_t state; 6408275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 6418275SEric Cheng dladm_status_t s = DLADM_STATUS_OK; 6428275SEric Cheng 6438275SEric Cheng /* remove flow */ 6448453SAnurag.Maskey@Sun.COM status = i_dladm_flow_remove(handle, flowname); 6458275SEric Cheng if ((status != DLADM_STATUS_OK) && 6468275SEric Cheng (tempop || status != DLADM_STATUS_NOTFOUND)) 6478275SEric Cheng goto done; 6488275SEric Cheng 6498275SEric Cheng /* remove flow from DB */ 6508275SEric Cheng if (!tempop) { 6518275SEric Cheng bzero(&state, sizeof (state)); 6528275SEric Cheng (void) strlcpy(state.rs_newattr.fi_flowname, flowname, 6538275SEric Cheng sizeof (state.rs_newattr.fi_flowname)); 6548275SEric Cheng state.rs_found = B_FALSE; 6558275SEric Cheng 6568275SEric Cheng /* flow DB */ 6578275SEric Cheng if (i_dladm_flow_remove_db(&state, root) < 0) { 6588275SEric Cheng s = dladm_errno2status(errno); 6598275SEric Cheng goto done; 6608275SEric Cheng } 6618275SEric Cheng 6628275SEric Cheng /* flow prop DB */ 6638453SAnurag.Maskey@Sun.COM s = dladm_set_flowprop(handle, flowname, NULL, NULL, 0, 6648275SEric Cheng DLADM_OPT_PERSIST, NULL); 6658275SEric Cheng } 6668275SEric Cheng 6678275SEric Cheng done: 6688275SEric Cheng if (!tempop) { 6698275SEric Cheng if (s == DLADM_STATUS_OK) { 6708275SEric Cheng if (status == DLADM_STATUS_NOTFOUND) 6718275SEric Cheng status = s; 6728275SEric Cheng } else { 6738275SEric Cheng if (s != DLADM_STATUS_NOTFOUND) 6748275SEric Cheng status = s; 6758275SEric Cheng } 6768275SEric Cheng } 6778275SEric Cheng return (status); 6788275SEric Cheng } 6798275SEric Cheng 6808275SEric Cheng /* 6818275SEric Cheng * Get an existing flow in the DB. 6828275SEric Cheng */ 6838275SEric Cheng 6848275SEric Cheng typedef struct get_db_state { 6858275SEric Cheng int (*gs_fn)(dladm_flow_attr_t *, void *); 6868275SEric Cheng void *gs_arg; 6878275SEric Cheng datalink_id_t gs_linkid; 6888275SEric Cheng } get_db_state_t; 6898275SEric Cheng 6908275SEric Cheng /* 6918275SEric Cheng * For each flow which matches the linkid, copy all flow information 6928275SEric Cheng * to a new dladm_flow_attr_t structure and call the provided 6938275SEric Cheng * function. This is used to display perisistent flows from 6948275SEric Cheng * the database. 6958275SEric Cheng */ 6968275SEric Cheng 6978275SEric Cheng static int 6988275SEric Cheng i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp) 6998275SEric Cheng { 7008275SEric Cheng get_db_state_t *state = (get_db_state_t *)arg; 7018275SEric Cheng dladm_flow_attr_t attr; 7028275SEric Cheng 7038275SEric Cheng if (grp->fi_linkid == state->gs_linkid) { 7048275SEric Cheng attr.fa_linkid = state->gs_linkid; 7058275SEric Cheng bcopy(grp->fi_flowname, &attr.fa_flowname, 7068275SEric Cheng sizeof (attr.fa_flowname)); 7078275SEric Cheng bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc, 7088275SEric Cheng sizeof (attr.fa_flow_desc)); 7098275SEric Cheng bcopy(&grp->fi_resource_props, &attr.fa_resource_props, 7108275SEric Cheng sizeof (attr.fa_resource_props)); 7118275SEric Cheng (void) state->gs_fn(&attr, state->gs_arg); 7128275SEric Cheng } 7138275SEric Cheng return (0); 7148275SEric Cheng } 7158275SEric Cheng 7168275SEric Cheng /* 7178275SEric Cheng * Walk through the flows defined on the system and for each flow 7188275SEric Cheng * invoke <fn>(<arg>, <flow>); 7198275SEric Cheng * Currently used for show-flow. 7208275SEric Cheng */ 7218275SEric Cheng /* ARGSUSED */ 7228275SEric Cheng dladm_status_t 7238453SAnurag.Maskey@Sun.COM dladm_walk_flow(int (*fn)(dladm_flow_attr_t *, void *), dladm_handle_t handle, 7248275SEric Cheng datalink_id_t linkid, void *arg, boolean_t persist) 7258275SEric Cheng { 7268275SEric Cheng dld_flowinfo_t *flow; 7278453SAnurag.Maskey@Sun.COM int i, bufsize; 7288275SEric Cheng dld_ioc_walkflow_t *ioc = NULL; 7298275SEric Cheng dladm_flow_attr_t attr; 7308275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 7318275SEric Cheng 7328275SEric Cheng if (fn == NULL) 7338275SEric Cheng return (DLADM_STATUS_BADARG); 7348275SEric Cheng 7358275SEric Cheng if (persist) { 7368275SEric Cheng get_db_state_t state; 7378275SEric Cheng 7388275SEric Cheng bzero(&state, sizeof (state)); 7398275SEric Cheng 7408275SEric Cheng state.gs_linkid = linkid; 7418275SEric Cheng state.gs_fn = fn; 7428275SEric Cheng state.gs_arg = arg; 7438275SEric Cheng status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn, 7448275SEric Cheng &state, NULL); 7458275SEric Cheng if (status != DLADM_STATUS_OK) 7468275SEric Cheng return (status); 7478275SEric Cheng } else { 7488275SEric Cheng bufsize = MIN_INFO_SIZE; 7498275SEric Cheng if ((ioc = calloc(1, bufsize)) == NULL) { 7508275SEric Cheng status = dladm_errno2status(errno); 7518275SEric Cheng return (status); 7528275SEric Cheng } 7538275SEric Cheng 7548275SEric Cheng ioc->wf_linkid = linkid; 7558275SEric Cheng ioc->wf_len = bufsize - sizeof (*ioc); 7568275SEric Cheng 7578453SAnurag.Maskey@Sun.COM while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) { 7588275SEric Cheng if (errno == ENOSPC) { 7598275SEric Cheng bufsize *= 2; 7608275SEric Cheng ioc = realloc(ioc, bufsize); 7618275SEric Cheng if (ioc != NULL) { 7628275SEric Cheng ioc->wf_linkid = linkid; 7638275SEric Cheng ioc->wf_len = bufsize - sizeof (*ioc); 7648275SEric Cheng continue; 7658275SEric Cheng } 7668275SEric Cheng } 7678275SEric Cheng goto bail; 7688275SEric Cheng } 7698275SEric Cheng 7708275SEric Cheng flow = (dld_flowinfo_t *)(void *)(ioc + 1); 7718275SEric Cheng for (i = 0; i < ioc->wf_nflows; i++, flow++) { 7728275SEric Cheng bzero(&attr, sizeof (attr)); 7738275SEric Cheng 7748275SEric Cheng attr.fa_linkid = flow->fi_linkid; 7758275SEric Cheng bcopy(&flow->fi_flowname, &attr.fa_flowname, 7768275SEric Cheng sizeof (attr.fa_flowname)); 7778275SEric Cheng bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc, 7788275SEric Cheng sizeof (attr.fa_flow_desc)); 7798275SEric Cheng bcopy(&flow->fi_resource_props, &attr.fa_resource_props, 7808275SEric Cheng sizeof (attr.fa_resource_props)); 7818275SEric Cheng 7828275SEric Cheng if (fn(&attr, arg) == DLADM_WALK_TERMINATE) 7838275SEric Cheng break; 7848275SEric Cheng } 7858275SEric Cheng } 7868275SEric Cheng 7878275SEric Cheng bail: 7888275SEric Cheng free(ioc); 7898275SEric Cheng return (status); 7908275SEric Cheng } 7918275SEric Cheng 7928275SEric Cheng dladm_status_t 7938453SAnurag.Maskey@Sun.COM dladm_flow_init(dladm_handle_t handle) 7948275SEric Cheng { 7958275SEric Cheng flow_desc_t flowdesc; 7968275SEric Cheng datalink_id_t linkid; 7978275SEric Cheng dladm_status_t s, status = DLADM_STATUS_OK; 7988558SGirish.Moodalbail@Sun.COM char name[MAXFLOWNAMELEN]; 7998275SEric Cheng char line[MAXLINELEN]; 8008275SEric Cheng dld_flowinfo_t attr; 8018275SEric Cheng FILE *fp; 8028275SEric Cheng 8038275SEric Cheng if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL) 8048275SEric Cheng return (DLADM_STATUS_DB_NOTFOUND); 8058275SEric Cheng 8068275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 8078275SEric Cheng /* skip comments */ 8088275SEric Cheng if (BLANK_LINE(line)) 8098275SEric Cheng continue; 8108275SEric Cheng 8118275SEric Cheng (void) strtok(line, " \n"); 8128275SEric Cheng 8138275SEric Cheng s = dladm_flow_parse_db(line, &attr); 8148275SEric Cheng if (s != DLADM_STATUS_OK) { 8158275SEric Cheng status = s; 8168275SEric Cheng continue; 8178275SEric Cheng } 8188275SEric Cheng bzero(&flowdesc, sizeof (flowdesc)); 8198275SEric Cheng bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t)); 8208275SEric Cheng (void) strlcpy(name, attr.fi_flowname, 8218275SEric Cheng sizeof (attr.fi_flowname)); 8228275SEric Cheng linkid = attr.fi_linkid; 8238275SEric Cheng 8248453SAnurag.Maskey@Sun.COM s = i_dladm_flow_add(handle, name, linkid, &flowdesc, NULL); 8258275SEric Cheng if (s != DLADM_STATUS_OK) 8268275SEric Cheng status = s; 8278275SEric Cheng } 8288453SAnurag.Maskey@Sun.COM s = i_dladm_init_flowprop_db(handle); 8298275SEric Cheng if (s != DLADM_STATUS_OK) 8308275SEric Cheng status = s; 8318275SEric Cheng 8328275SEric Cheng (void) fclose(fp); 8338275SEric Cheng return (status); 8348275SEric Cheng } 8358275SEric Cheng 8368275SEric Cheng dladm_status_t 8378275SEric Cheng dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask) 8388275SEric Cheng { 8398275SEric Cheng if (prefixlen < 0 || prefixlen > maxlen) 8408275SEric Cheng return (DLADM_STATUS_BADARG); 8418275SEric Cheng 8428275SEric Cheng while (prefixlen > 0) { 8438275SEric Cheng if (prefixlen >= 8) { 8448275SEric Cheng *mask++ = 0xFF; 8458275SEric Cheng prefixlen -= 8; 8468275SEric Cheng continue; 8478275SEric Cheng } 8488275SEric Cheng *mask |= 1 << (8 - prefixlen); 8498275SEric Cheng prefixlen--; 8508275SEric Cheng } 8518275SEric Cheng return (DLADM_STATUS_OK); 8528275SEric Cheng } 8538275SEric Cheng 8548275SEric Cheng dladm_status_t 8558275SEric Cheng dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen) 8568275SEric Cheng { 8578275SEric Cheng int bits; 8588275SEric Cheng int i, end; 8598275SEric Cheng 8608275SEric Cheng switch (plen) { 8618275SEric Cheng case IP_ABITS: 8628275SEric Cheng end = 3; 8638275SEric Cheng break; 8648275SEric Cheng case IPV6_ABITS: 8658275SEric Cheng end = 0; 8668275SEric Cheng break; 8678275SEric Cheng default: 8688275SEric Cheng return (DLADM_STATUS_BADARG); 8698275SEric Cheng } 8708275SEric Cheng 8718275SEric Cheng for (i = 3; i >= end; i--) { 8728275SEric Cheng if (mask->_S6_un._S6_u32[i] == 0) { 8738275SEric Cheng plen -= 32; 8748275SEric Cheng continue; 8758275SEric Cheng } 8768275SEric Cheng bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1; 8778275SEric Cheng if (bits == 0) 8788275SEric Cheng break; 8798275SEric Cheng plen -= bits; 8808275SEric Cheng } 8818275SEric Cheng *prefixlen = plen; 8828275SEric Cheng return (DLADM_STATUS_OK); 8838275SEric Cheng } 884