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> 339107Sjames.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"; 75*10734SEric Cheng static const char *REMOTE_PORT = "remote_port"; 768275SEric Cheng static const char *DSFIELD = "dsfield"; 778275SEric Cheng 788275SEric Cheng /* 798275SEric Cheng * Open and lock the flowadm configuration file lock. The lock is 808275SEric Cheng * acquired as a reader (F_RDLCK) or writer (F_WRLCK). 818275SEric Cheng */ 828275SEric Cheng static int 838275SEric Cheng i_dladm_flow_lock_db(short type) 848275SEric Cheng { 858275SEric Cheng int lock_fd; 868275SEric Cheng struct flock lock; 878275SEric Cheng 888275SEric Cheng if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC, 898275SEric Cheng DLADM_FLOW_DB_PERMS)) < 0) 908275SEric Cheng return (-1); 918275SEric Cheng 928275SEric Cheng lock.l_type = type; 938275SEric Cheng lock.l_whence = SEEK_SET; 948275SEric Cheng lock.l_start = 0; 958275SEric Cheng lock.l_len = 0; 968275SEric Cheng 978275SEric Cheng if (fcntl(lock_fd, F_SETLKW, &lock) < 0) { 988275SEric Cheng (void) close(lock_fd); 998275SEric Cheng (void) unlink(DLADM_FLOW_DB_LOCK); 1008275SEric Cheng return (-1); 1018275SEric Cheng } 1028275SEric Cheng return (lock_fd); 1038275SEric Cheng } 1048275SEric Cheng 1058275SEric Cheng /* 1068275SEric Cheng * Unlock and close the specified file. 1078275SEric Cheng */ 1088275SEric Cheng static void 1098275SEric Cheng i_dladm_flow_unlock_db(int fd) 1108275SEric Cheng { 1118275SEric Cheng struct flock lock; 1128275SEric Cheng 1138275SEric Cheng if (fd < 0) 1148275SEric Cheng return; 1158275SEric Cheng 1168275SEric Cheng lock.l_type = F_UNLCK; 1178275SEric Cheng lock.l_whence = SEEK_SET; 1188275SEric Cheng lock.l_start = 0; 1198275SEric Cheng lock.l_len = 0; 1208275SEric Cheng 1218275SEric Cheng (void) fcntl(fd, F_SETLKW, &lock); 1228275SEric Cheng (void) close(fd); 1238275SEric Cheng (void) unlink(DLADM_FLOW_DB_LOCK); 1248275SEric Cheng } 1258275SEric Cheng 1268275SEric Cheng /* 1278275SEric Cheng * Parse one line of the link flowadm DB 1288275SEric Cheng * Returns -1 on failure, 0 on success. 1298275SEric Cheng */ 1308275SEric Cheng dladm_status_t 1318275SEric Cheng dladm_flow_parse_db(char *line, dld_flowinfo_t *attr) 1328275SEric Cheng { 1338275SEric Cheng char *token; 1348275SEric Cheng char *value, *name = 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 = 159*10734SEric Cheng (uint32_t)strtol(value, NULL, 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 = 167*10734SEric Cheng (uint64_t)strtol(value, NULL, 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 = 197*10734SEric Cheng (uint8_t)strtol(value, NULL, 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 = 202*10734SEric Cheng (uint16_t)strtol(value, NULL, 10); 2038275SEric Cheng attr->fi_flow_desc.fd_local_port = 2048275SEric Cheng htons(attr->fi_flow_desc.fd_local_port); 205*10734SEric Cheng } else if (strcmp(name, REMOTE_PORT) == 0) { 206*10734SEric Cheng attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_REMOTE; 207*10734SEric Cheng attr->fi_flow_desc.fd_remote_port = 208*10734SEric Cheng (uint16_t)strtol(value, NULL, 10); 209*10734SEric Cheng attr->fi_flow_desc.fd_remote_port = 210*10734SEric Cheng htons(attr->fi_flow_desc.fd_remote_port); 2118275SEric Cheng } 2128275SEric Cheng free(name); 2138275SEric Cheng name = NULL; 2148275SEric Cheng } 2158275SEric Cheng if (attr->fi_linkid != DATALINK_INVALID_LINKID) 2168275SEric Cheng status = DLADM_STATUS_OK; 2178275SEric Cheng done: 2188275SEric Cheng free(name); 2198275SEric Cheng return (status); 2208275SEric Cheng } 2218275SEric Cheng 2228275SEric Cheng #define FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1); 2238275SEric Cheng 2248275SEric Cheng /* 2258275SEric Cheng * Write the attribute of a group to the specified file. Returns 0 on 2268275SEric Cheng * success, -1 on failure. 2278275SEric Cheng */ 2288275SEric Cheng static int 2298275SEric Cheng i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr) 2308275SEric Cheng { 2318275SEric Cheng 2328275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t", 2338275SEric Cheng attr->fi_flowname, attr->fi_linkid)); 2348275SEric Cheng 2358275SEric Cheng /* flow policy */ 2368275SEric Cheng if (attr->fi_resource_props.mrp_mask & MRP_MAXBW) 2378275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT, 2388275SEric Cheng attr->fi_resource_props.mrp_maxbw)); 2398275SEric Cheng 2408275SEric Cheng if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY) 2418275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY, 2428275SEric Cheng attr->fi_resource_props.mrp_priority)); 2438275SEric Cheng 2448275SEric Cheng /* flow descriptor */ 2458275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD) 2468275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD, 2478275SEric Cheng attr->fi_flow_desc.fd_dsfield, 2488275SEric Cheng attr->fi_flow_desc.fd_dsfield_mask)); 2498275SEric Cheng 2508275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) { 2518275SEric Cheng char abuf[INET6_ADDRSTRLEN], *ap; 2528275SEric Cheng struct in_addr ipaddr; 2538275SEric Cheng int prefix_len, prefix_max; 2548275SEric Cheng 2558275SEric Cheng if (attr->fi_flow_desc.fd_ipversion != 6) { 2568275SEric Cheng ipaddr.s_addr = 2578275SEric Cheng attr->fi_flow_desc. 2588275SEric Cheng fd_local_addr._S6_un._S6_u32[3]; 2598275SEric Cheng 2608275SEric Cheng ap = inet_ntoa(ipaddr); 2618275SEric Cheng prefix_max = IP_ABITS; 2628275SEric Cheng } else { 2638275SEric Cheng (void) inet_ntop(AF_INET6, 2648275SEric Cheng &attr->fi_flow_desc.fd_local_addr, 2658275SEric Cheng abuf, INET6_ADDRSTRLEN); 2668275SEric Cheng 2678275SEric Cheng ap = abuf; 2688275SEric Cheng prefix_max = IPV6_ABITS; 2698275SEric Cheng } 2708275SEric Cheng (void) dladm_mask2prefixlen( 2718275SEric Cheng &attr->fi_flow_desc.fd_local_netmask, prefix_max, 2728275SEric Cheng &prefix_len); 2738275SEric Cheng 2748275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR, 2758275SEric Cheng ap, prefix_len)); 2768275SEric Cheng } 2778275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) { 2788275SEric Cheng char abuf[INET6_ADDRSTRLEN], *ap; 2798275SEric Cheng struct in_addr ipaddr; 2808275SEric Cheng int prefix_len, prefix_max; 2818275SEric Cheng 2828275SEric Cheng if (attr->fi_flow_desc.fd_ipversion != 6) { 2838275SEric Cheng ipaddr.s_addr = 2848275SEric Cheng attr->fi_flow_desc. 2858275SEric Cheng fd_remote_addr._S6_un._S6_u32[3]; 2868275SEric Cheng 2878275SEric Cheng ap = inet_ntoa(ipaddr); 2888275SEric Cheng prefix_max = IP_ABITS; 2898275SEric Cheng } else { 2908275SEric Cheng (void) inet_ntop(AF_INET6, 2918275SEric Cheng &(attr->fi_flow_desc.fd_remote_addr), 2928275SEric Cheng abuf, INET6_ADDRSTRLEN); 2938275SEric Cheng 2948275SEric Cheng ap = abuf; 2958275SEric Cheng prefix_max = IPV6_ABITS; 2968275SEric Cheng } 2978275SEric Cheng (void) dladm_mask2prefixlen( 2988275SEric Cheng &attr->fi_flow_desc.fd_remote_netmask, prefix_max, 2998275SEric Cheng &prefix_len); 3008275SEric Cheng 3018275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR, 3028275SEric Cheng ap, prefix_len)); 3038275SEric Cheng } 3048275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL) 3058275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT, 3068275SEric Cheng attr->fi_flow_desc.fd_protocol)); 3078275SEric Cheng 3088275SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) 3098275SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT, 3108275SEric Cheng ntohs(attr->fi_flow_desc.fd_local_port))); 3118275SEric Cheng 312*10734SEric Cheng if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE) 313*10734SEric Cheng FPRINTF_ERR(fprintf(fp, "%s=%d\t", REMOTE_PORT, 314*10734SEric Cheng ntohs(attr->fi_flow_desc.fd_remote_port))); 315*10734SEric Cheng 3168275SEric Cheng FPRINTF_ERR(fprintf(fp, "\n")); 3178275SEric Cheng 3188275SEric Cheng return (0); 3198275SEric Cheng 3208275SEric Cheng } 3218275SEric Cheng 3228275SEric Cheng static dladm_status_t 3238275SEric Cheng i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *), 3248275SEric Cheng void *arg, 3258275SEric Cheng const char *root) 3268275SEric Cheng { 3278275SEric Cheng FILE *fp, *nfp; 3288275SEric Cheng int nfd, fn_rc, lock_fd; 3298275SEric Cheng char line[MAXLINELEN]; 3308275SEric Cheng dld_flowinfo_t attr; 3318275SEric Cheng char *db_file, *tmp_db_file; 3328275SEric Cheng char db_file_buf[MAXPATHLEN]; 3338275SEric Cheng char tmp_db_file_buf[MAXPATHLEN]; 3348275SEric Cheng dladm_status_t status = DLADM_STATUS_FLOW_DB_ERR; 3358275SEric Cheng 3368275SEric Cheng if (root == NULL) { 3378275SEric Cheng db_file = DLADM_FLOW_DB; 3388275SEric Cheng tmp_db_file = DLADM_FLOW_DB_TMP; 3398275SEric Cheng } else { 3408275SEric Cheng (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 3418275SEric Cheng DLADM_FLOW_DB); 3428275SEric Cheng (void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root, 3438275SEric Cheng DLADM_FLOW_DB_TMP); 3448275SEric Cheng db_file = db_file_buf; 3458275SEric Cheng tmp_db_file = tmp_db_file_buf; 3468275SEric Cheng } 3478275SEric Cheng 3488275SEric Cheng if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0) 3498275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 3508275SEric Cheng 3518275SEric Cheng if ((fp = fopen(db_file, "r")) == NULL) { 3528275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3538275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3548275SEric Cheng } 3558275SEric Cheng 3568275SEric Cheng if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC, 3578275SEric Cheng DLADM_FLOW_DB_PERMS)) == -1) { 3588275SEric Cheng (void) fclose(fp); 3598275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3608275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3618275SEric Cheng } 3628275SEric Cheng 3638275SEric Cheng if ((nfp = fdopen(nfd, "w")) == NULL) { 3648275SEric Cheng (void) close(nfd); 3658275SEric Cheng (void) fclose(fp); 3668275SEric Cheng (void) unlink(tmp_db_file); 3678275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 3688275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 3698275SEric Cheng } 3708275SEric Cheng 3718275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 3728275SEric Cheng 3738275SEric Cheng /* skip comments */ 3748275SEric Cheng if (BLANK_LINE(line)) { 3758275SEric Cheng if (fputs(line, nfp) == EOF) 3768275SEric Cheng goto failed; 3778275SEric Cheng continue; 3788275SEric Cheng } 3798275SEric Cheng (void) strtok(line, " \n"); 3808275SEric Cheng 3818275SEric Cheng if ((status = dladm_flow_parse_db(line, &attr)) != 3828275SEric Cheng DLADM_STATUS_OK) 3838275SEric Cheng goto failed; 3848275SEric Cheng 3858275SEric Cheng fn_rc = fn(arg, &attr); 3868275SEric Cheng 3878275SEric Cheng switch (fn_rc) { 3888275SEric Cheng case -1: 3898275SEric Cheng /* failure, stop walking */ 3908275SEric Cheng goto failed; 3918275SEric Cheng case 0: 3928275SEric Cheng /* 3938275SEric Cheng * Success, write group attributes, which could 3948275SEric Cheng * have been modified by fn(). 3958275SEric Cheng */ 3968275SEric Cheng if (i_dladm_flow_fput_grp(nfp, &attr) != 0) 3978275SEric Cheng goto failed; 3988275SEric Cheng break; 3998275SEric Cheng case 1: 4008275SEric Cheng /* skip current group */ 4018275SEric Cheng break; 4028275SEric Cheng } 4038275SEric Cheng } 4048275SEric Cheng if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1) 4058275SEric Cheng goto failed; 4068275SEric Cheng 4078275SEric Cheng if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1) 4088275SEric Cheng goto failed; 4098275SEric Cheng 4108275SEric Cheng if (fflush(nfp) == EOF) 4118275SEric Cheng goto failed; 4128275SEric Cheng 4138275SEric Cheng (void) fclose(fp); 4148275SEric Cheng (void) fclose(nfp); 4158275SEric Cheng 4168275SEric Cheng if (rename(tmp_db_file, db_file) == -1) { 4178275SEric Cheng (void) unlink(tmp_db_file); 4188275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4198275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 4208275SEric Cheng } 4218275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4228275SEric Cheng return (DLADM_STATUS_OK); 4238275SEric Cheng 4248275SEric Cheng failed: 4258275SEric Cheng (void) fclose(fp); 4268275SEric Cheng (void) fclose(nfp); 4278275SEric Cheng (void) unlink(tmp_db_file); 4288275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 4298275SEric Cheng 4308275SEric Cheng return (status); 4318275SEric Cheng } 4328275SEric Cheng 4338275SEric Cheng /* 4348275SEric Cheng * Remove existing flow from DB. 4358275SEric Cheng */ 4368275SEric Cheng 4378275SEric Cheng typedef struct remove_db_state { 4388275SEric Cheng dld_flowinfo_t rs_newattr; 4398275SEric Cheng dld_flowinfo_t rs_oldattr; 4408275SEric Cheng boolean_t rs_found; 4418275SEric Cheng } remove_db_state_t; 4428275SEric Cheng 4438275SEric Cheng static int 4448275SEric Cheng i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp) 4458275SEric Cheng { 4468275SEric Cheng remove_db_state_t *state = (remove_db_state_t *)arg; 4478275SEric Cheng dld_flowinfo_t *attr = &state->rs_newattr; 4488275SEric Cheng 4498275SEric Cheng if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0) 4508275SEric Cheng return (0); 4518275SEric Cheng else { 4528275SEric Cheng bcopy(grp, &state->rs_oldattr, 4538275SEric Cheng sizeof (dld_flowinfo_t)); 4548275SEric Cheng state->rs_found = B_TRUE; 4558275SEric Cheng return (1); 4568275SEric Cheng } 4578275SEric Cheng } 4588275SEric Cheng 4598275SEric Cheng /* ARGSUSED */ 4608275SEric Cheng static int 4618275SEric Cheng i_dladm_flow_remove_db(remove_db_state_t *state, const char *root) 4628275SEric Cheng { 4638275SEric Cheng if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root) 4648275SEric Cheng != 0) 4658275SEric Cheng return (-1); 4668275SEric Cheng 4678275SEric Cheng if (!state->rs_found) { 4688275SEric Cheng errno = ENOENT; 4698275SEric Cheng return (-1); 4708275SEric Cheng } 4718275SEric Cheng 4728275SEric Cheng return (0); 4738275SEric Cheng } 4748275SEric Cheng 4758275SEric Cheng /* 4768275SEric Cheng * Create a flow in the DB. 4778275SEric Cheng */ 4788275SEric Cheng 4798275SEric Cheng typedef struct modify_db_state { 4808275SEric Cheng dld_flowinfo_t ms_newattr; 4818275SEric Cheng dld_flowinfo_t ms_oldattr; 4828275SEric Cheng boolean_t ms_found; 4838275SEric Cheng } modify_db_state_t; 4848275SEric Cheng 4858275SEric Cheng static dladm_status_t 4868275SEric Cheng i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root) 4878275SEric Cheng { 4888275SEric Cheng FILE *fp; 4898275SEric Cheng char line[MAXLINELEN]; 4908275SEric Cheng char *db_file; 4918275SEric Cheng char db_file_buf[MAXPATHLEN]; 4928275SEric Cheng int lock_fd; 4938275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 4948275SEric Cheng 4958275SEric Cheng if (root == NULL) { 4968275SEric Cheng db_file = DLADM_FLOW_DB; 4978275SEric Cheng } else { 4988275SEric Cheng (void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root, 4998275SEric Cheng DLADM_FLOW_DB); 5008275SEric Cheng db_file = db_file_buf; 5018275SEric Cheng } 5028275SEric Cheng 5038275SEric Cheng if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0) 5048275SEric Cheng return (DLADM_STATUS_FLOW_DB_ERR); 5058275SEric Cheng 5068275SEric Cheng if ((fp = fopen(db_file, "r+")) == NULL && 5078275SEric Cheng (fp = fopen(db_file, "w")) == NULL) { 5088275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 5098275SEric Cheng return (DLADM_STATUS_FLOW_DB_OPEN_ERR); 5108275SEric Cheng } 5118275SEric Cheng 5128275SEric Cheng /* look for existing group with same flowname */ 5138275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 5148275SEric Cheng char *holder, *lasts; 5158275SEric Cheng 5168275SEric Cheng /* skip comments */ 5178275SEric Cheng if (BLANK_LINE(line)) 5188275SEric Cheng continue; 5198275SEric Cheng 5208275SEric Cheng /* ignore corrupted lines */ 5218275SEric Cheng holder = strtok_r(line, " \t", &lasts); 5228275SEric Cheng if (holder == NULL) 5238275SEric Cheng continue; 5248275SEric Cheng 5258275SEric Cheng /* flow id */ 5268275SEric Cheng if (strcmp(holder, attr->fi_flowname) == 0) { 5278275SEric Cheng /* group with flow id already exists */ 5288275SEric Cheng status = DLADM_STATUS_PERSIST_FLOW_EXISTS; 5298275SEric Cheng goto failed; 5308275SEric Cheng } 5318275SEric Cheng } 5328275SEric Cheng /* 5338275SEric Cheng * If we get here, we've verified that no existing group with 5348275SEric Cheng * the same flow id already exists. Its now time to add the new 5358275SEric Cheng * group to the DB. 5368275SEric Cheng */ 5378275SEric Cheng if (i_dladm_flow_fput_grp(fp, attr) != 0) 5388275SEric Cheng status = DLADM_STATUS_FLOW_DB_PARSE_ERR; 5398275SEric Cheng 5408275SEric Cheng failed: 5418275SEric Cheng (void) fclose(fp); 5428275SEric Cheng i_dladm_flow_unlock_db(lock_fd); 5438275SEric Cheng return (status); 5448275SEric Cheng } 5458275SEric Cheng 5468275SEric Cheng static dladm_status_t 5478453SAnurag.Maskey@Sun.COM i_dladm_flow_add(dladm_handle_t handle, char *flowname, datalink_id_t linkid, 5488453SAnurag.Maskey@Sun.COM flow_desc_t *flowdesc, mac_resource_props_t *mrp) 5498275SEric Cheng { 5508275SEric Cheng dld_ioc_addflow_t attr; 5518275SEric Cheng 5528275SEric Cheng /* create flow */ 5538275SEric Cheng bzero(&attr, sizeof (attr)); 5548275SEric Cheng bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t)); 5558275SEric Cheng if (mrp != NULL) { 5568275SEric Cheng bcopy(mrp, &attr.af_resource_props, 5578275SEric Cheng sizeof (mac_resource_props_t)); 5588275SEric Cheng } 5598275SEric Cheng 5608275SEric Cheng (void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name)); 5618275SEric Cheng attr.af_linkid = linkid; 5628275SEric Cheng 5638453SAnurag.Maskey@Sun.COM if (ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &attr) < 0) 5648275SEric Cheng return (dladm_errno2status(errno)); 5658275SEric Cheng 5668275SEric Cheng return (DLADM_STATUS_OK); 5678275SEric Cheng } 5688275SEric Cheng 5698275SEric Cheng static dladm_status_t 5708453SAnurag.Maskey@Sun.COM i_dladm_flow_remove(dladm_handle_t handle, char *flowname) 5718275SEric Cheng { 5728275SEric Cheng dld_ioc_removeflow_t attr; 5738275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 5748275SEric Cheng 5758275SEric Cheng (void) strlcpy(attr.rf_name, flowname, 5768275SEric Cheng sizeof (attr.rf_name)); 5778275SEric Cheng 5788453SAnurag.Maskey@Sun.COM if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0) 5798275SEric Cheng status = dladm_errno2status(errno); 5808275SEric Cheng 5818275SEric Cheng return (status); 5828275SEric Cheng } 5838275SEric Cheng 5848275SEric Cheng 5858275SEric Cheng /* ARGSUSED */ 5868275SEric Cheng dladm_status_t 5878453SAnurag.Maskey@Sun.COM dladm_flow_add(dladm_handle_t handle, datalink_id_t linkid, 5888453SAnurag.Maskey@Sun.COM dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname, 5898453SAnurag.Maskey@Sun.COM boolean_t tempop, const char *root) 5908275SEric Cheng { 5918275SEric Cheng dld_flowinfo_t db_attr; 5928275SEric Cheng flow_desc_t flowdesc; 5938275SEric Cheng mac_resource_props_t mrp; 5948275SEric Cheng dladm_status_t status; 5958275SEric Cheng 5968275SEric Cheng /* Extract flow attributes from attrlist */ 5978275SEric Cheng bzero(&flowdesc, sizeof (flow_desc_t)); 5988275SEric Cheng if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist, 5998275SEric Cheng &flowdesc)) != DLADM_STATUS_OK) { 6008275SEric Cheng return (status); 6018275SEric Cheng } 6028275SEric Cheng 6038275SEric Cheng /* Extract resource_ctl and cpu_list from proplist */ 6048275SEric Cheng bzero(&mrp, sizeof (mac_resource_props_t)); 6058275SEric Cheng if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist, 6068275SEric Cheng &mrp)) != DLADM_STATUS_OK) { 6078275SEric Cheng return (status); 6088275SEric Cheng } 6098275SEric Cheng 6108275SEric Cheng /* Add flow in kernel */ 6118453SAnurag.Maskey@Sun.COM status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp); 6128275SEric Cheng if (status != DLADM_STATUS_OK) 6138275SEric Cheng return (status); 6148275SEric Cheng 6158275SEric Cheng /* Add flow to DB */ 6168275SEric Cheng if (!tempop) { 6178275SEric Cheng bzero(&db_attr, sizeof (db_attr)); 6188275SEric Cheng bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t)); 6198275SEric Cheng (void) strlcpy(db_attr.fi_flowname, flowname, 6208275SEric Cheng sizeof (db_attr.fi_flowname)); 6218275SEric Cheng db_attr.fi_linkid = linkid; 6228275SEric Cheng 6238275SEric Cheng if ((status = i_dladm_flow_create_db(&db_attr, root)) != 6248275SEric Cheng DLADM_STATUS_OK) { 6258453SAnurag.Maskey@Sun.COM (void) i_dladm_flow_remove(handle, flowname); 6268275SEric Cheng return (status); 6278275SEric Cheng } 6288275SEric Cheng /* set flow properties */ 6298275SEric Cheng if (proplist != NULL) { 6308453SAnurag.Maskey@Sun.COM status = i_dladm_set_flow_proplist_db(handle, flowname, 6318275SEric Cheng proplist); 6328275SEric Cheng if (status != DLADM_STATUS_OK) { 6338453SAnurag.Maskey@Sun.COM (void) i_dladm_flow_remove(handle, flowname); 6348275SEric Cheng return (status); 6358275SEric Cheng } 6368275SEric Cheng } 6378275SEric Cheng } 6388275SEric Cheng return (status); 6398275SEric Cheng } 6408275SEric Cheng 6418275SEric Cheng /* 6428275SEric Cheng * Remove a flow. 6438275SEric Cheng */ 6448275SEric Cheng /* ARGSUSED */ 6458275SEric Cheng dladm_status_t 6468453SAnurag.Maskey@Sun.COM dladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop, 6478275SEric Cheng const char *root) 6488275SEric Cheng { 6498275SEric Cheng remove_db_state_t state; 6508275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 6518275SEric Cheng dladm_status_t s = DLADM_STATUS_OK; 6528275SEric Cheng 6538275SEric Cheng /* remove flow */ 6548453SAnurag.Maskey@Sun.COM status = i_dladm_flow_remove(handle, flowname); 6558275SEric Cheng if ((status != DLADM_STATUS_OK) && 6568275SEric Cheng (tempop || status != DLADM_STATUS_NOTFOUND)) 6578275SEric Cheng goto done; 6588275SEric Cheng 6598275SEric Cheng /* remove flow from DB */ 6608275SEric Cheng if (!tempop) { 6618275SEric Cheng bzero(&state, sizeof (state)); 6628275SEric Cheng (void) strlcpy(state.rs_newattr.fi_flowname, flowname, 6638275SEric Cheng sizeof (state.rs_newattr.fi_flowname)); 6648275SEric Cheng state.rs_found = B_FALSE; 6658275SEric Cheng 6668275SEric Cheng /* flow DB */ 6678275SEric Cheng if (i_dladm_flow_remove_db(&state, root) < 0) { 6688275SEric Cheng s = dladm_errno2status(errno); 6698275SEric Cheng goto done; 6708275SEric Cheng } 6718275SEric Cheng 6728275SEric Cheng /* flow prop DB */ 6738453SAnurag.Maskey@Sun.COM s = dladm_set_flowprop(handle, flowname, NULL, NULL, 0, 6748275SEric Cheng DLADM_OPT_PERSIST, NULL); 6758275SEric Cheng } 6768275SEric Cheng 6778275SEric Cheng done: 6788275SEric Cheng if (!tempop) { 6798275SEric Cheng if (s == DLADM_STATUS_OK) { 6808275SEric Cheng if (status == DLADM_STATUS_NOTFOUND) 6818275SEric Cheng status = s; 6828275SEric Cheng } else { 6838275SEric Cheng if (s != DLADM_STATUS_NOTFOUND) 6848275SEric Cheng status = s; 6858275SEric Cheng } 6868275SEric Cheng } 6878275SEric Cheng return (status); 6888275SEric Cheng } 6898275SEric Cheng 6908275SEric Cheng /* 6918275SEric Cheng * Get an existing flow in the DB. 6928275SEric Cheng */ 6938275SEric Cheng 6948275SEric Cheng typedef struct get_db_state { 6959421SMichael.Lim@Sun.COM int (*gs_fn)(dladm_handle_t, dladm_flow_attr_t *, void *); 6968275SEric Cheng void *gs_arg; 6978275SEric Cheng datalink_id_t gs_linkid; 6988275SEric Cheng } get_db_state_t; 6998275SEric Cheng 7008275SEric Cheng /* 7018275SEric Cheng * For each flow which matches the linkid, copy all flow information 7028275SEric Cheng * to a new dladm_flow_attr_t structure and call the provided 7038275SEric Cheng * function. This is used to display perisistent flows from 7048275SEric Cheng * the database. 7058275SEric Cheng */ 7068275SEric Cheng 7078275SEric Cheng static int 7088275SEric Cheng i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp) 7098275SEric Cheng { 7108275SEric Cheng get_db_state_t *state = (get_db_state_t *)arg; 7118275SEric Cheng dladm_flow_attr_t attr; 7129421SMichael.Lim@Sun.COM dladm_handle_t handle = NULL; 7138275SEric Cheng 7148275SEric Cheng if (grp->fi_linkid == state->gs_linkid) { 7158275SEric Cheng attr.fa_linkid = state->gs_linkid; 7168275SEric Cheng bcopy(grp->fi_flowname, &attr.fa_flowname, 7178275SEric Cheng sizeof (attr.fa_flowname)); 7188275SEric Cheng bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc, 7198275SEric Cheng sizeof (attr.fa_flow_desc)); 7208275SEric Cheng bcopy(&grp->fi_resource_props, &attr.fa_resource_props, 7218275SEric Cheng sizeof (attr.fa_resource_props)); 7229421SMichael.Lim@Sun.COM (void) state->gs_fn(handle, &attr, state->gs_arg); 7238275SEric Cheng } 7248275SEric Cheng return (0); 7258275SEric Cheng } 7268275SEric Cheng 7278275SEric Cheng /* 7288275SEric Cheng * Walk through the flows defined on the system and for each flow 7298275SEric Cheng * invoke <fn>(<arg>, <flow>); 7308275SEric Cheng * Currently used for show-flow. 7318275SEric Cheng */ 7328275SEric Cheng /* ARGSUSED */ 7338275SEric Cheng dladm_status_t 7349421SMichael.Lim@Sun.COM dladm_walk_flow(int (*fn)(dladm_handle_t, dladm_flow_attr_t *, void *), 7359421SMichael.Lim@Sun.COM dladm_handle_t handle, datalink_id_t linkid, void *arg, boolean_t persist) 7368275SEric Cheng { 7378275SEric Cheng dld_flowinfo_t *flow; 7388453SAnurag.Maskey@Sun.COM int i, bufsize; 7398275SEric Cheng dld_ioc_walkflow_t *ioc = NULL; 7408275SEric Cheng dladm_flow_attr_t attr; 7418275SEric Cheng dladm_status_t status = DLADM_STATUS_OK; 7428275SEric Cheng 7438275SEric Cheng if (fn == NULL) 7448275SEric Cheng return (DLADM_STATUS_BADARG); 7458275SEric Cheng 7468275SEric Cheng if (persist) { 7478275SEric Cheng get_db_state_t state; 7488275SEric Cheng 7498275SEric Cheng bzero(&state, sizeof (state)); 7508275SEric Cheng 7518275SEric Cheng state.gs_linkid = linkid; 7528275SEric Cheng state.gs_fn = fn; 7538275SEric Cheng state.gs_arg = arg; 7548275SEric Cheng status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn, 7558275SEric Cheng &state, NULL); 7568275SEric Cheng if (status != DLADM_STATUS_OK) 7578275SEric Cheng return (status); 7588275SEric Cheng } else { 7598275SEric Cheng bufsize = MIN_INFO_SIZE; 7608275SEric Cheng if ((ioc = calloc(1, bufsize)) == NULL) { 7618275SEric Cheng status = dladm_errno2status(errno); 7628275SEric Cheng return (status); 7638275SEric Cheng } 7648275SEric Cheng 7658275SEric Cheng ioc->wf_linkid = linkid; 7668275SEric Cheng ioc->wf_len = bufsize - sizeof (*ioc); 7678275SEric Cheng 7688453SAnurag.Maskey@Sun.COM while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) { 7698275SEric Cheng if (errno == ENOSPC) { 7708275SEric Cheng bufsize *= 2; 7718275SEric Cheng ioc = realloc(ioc, bufsize); 7728275SEric Cheng if (ioc != NULL) { 7738275SEric Cheng ioc->wf_linkid = linkid; 7748275SEric Cheng ioc->wf_len = bufsize - sizeof (*ioc); 7758275SEric Cheng continue; 7768275SEric Cheng } 7778275SEric Cheng } 7788275SEric Cheng goto bail; 7798275SEric Cheng } 7808275SEric Cheng 7818275SEric Cheng flow = (dld_flowinfo_t *)(void *)(ioc + 1); 7828275SEric Cheng for (i = 0; i < ioc->wf_nflows; i++, flow++) { 7838275SEric Cheng bzero(&attr, sizeof (attr)); 7848275SEric Cheng 7858275SEric Cheng attr.fa_linkid = flow->fi_linkid; 7868275SEric Cheng bcopy(&flow->fi_flowname, &attr.fa_flowname, 7878275SEric Cheng sizeof (attr.fa_flowname)); 7888275SEric Cheng bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc, 7898275SEric Cheng sizeof (attr.fa_flow_desc)); 7908275SEric Cheng bcopy(&flow->fi_resource_props, &attr.fa_resource_props, 7918275SEric Cheng sizeof (attr.fa_resource_props)); 7928275SEric Cheng 7939421SMichael.Lim@Sun.COM if (fn(handle, &attr, arg) == DLADM_WALK_TERMINATE) 7948275SEric Cheng break; 7958275SEric Cheng } 7968275SEric Cheng } 7978275SEric Cheng 7988275SEric Cheng bail: 7998275SEric Cheng free(ioc); 8008275SEric Cheng return (status); 8018275SEric Cheng } 8028275SEric Cheng 8038275SEric Cheng dladm_status_t 8048453SAnurag.Maskey@Sun.COM dladm_flow_init(dladm_handle_t handle) 8058275SEric Cheng { 8068275SEric Cheng flow_desc_t flowdesc; 8078275SEric Cheng datalink_id_t linkid; 8088275SEric Cheng dladm_status_t s, status = DLADM_STATUS_OK; 8098558SGirish.Moodalbail@Sun.COM char name[MAXFLOWNAMELEN]; 8108275SEric Cheng char line[MAXLINELEN]; 8118275SEric Cheng dld_flowinfo_t attr; 8128275SEric Cheng FILE *fp; 8138275SEric Cheng 8148275SEric Cheng if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL) 8158275SEric Cheng return (DLADM_STATUS_DB_NOTFOUND); 8168275SEric Cheng 8178275SEric Cheng while (fgets(line, MAXLINELEN, fp) != NULL) { 8188275SEric Cheng /* skip comments */ 8198275SEric Cheng if (BLANK_LINE(line)) 8208275SEric Cheng continue; 8218275SEric Cheng 8228275SEric Cheng (void) strtok(line, " \n"); 8238275SEric Cheng 8248275SEric Cheng s = dladm_flow_parse_db(line, &attr); 8258275SEric Cheng if (s != DLADM_STATUS_OK) { 8268275SEric Cheng status = s; 8278275SEric Cheng continue; 8288275SEric Cheng } 8298275SEric Cheng bzero(&flowdesc, sizeof (flowdesc)); 8308275SEric Cheng bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t)); 8318275SEric Cheng (void) strlcpy(name, attr.fi_flowname, 8328275SEric Cheng sizeof (attr.fi_flowname)); 8338275SEric Cheng linkid = attr.fi_linkid; 8348275SEric Cheng 8358453SAnurag.Maskey@Sun.COM s = i_dladm_flow_add(handle, name, linkid, &flowdesc, NULL); 8368275SEric Cheng if (s != DLADM_STATUS_OK) 8378275SEric Cheng status = s; 8388275SEric Cheng } 8398453SAnurag.Maskey@Sun.COM s = i_dladm_init_flowprop_db(handle); 8408275SEric Cheng if (s != DLADM_STATUS_OK) 8418275SEric Cheng status = s; 8428275SEric Cheng 8438275SEric Cheng (void) fclose(fp); 8448275SEric Cheng return (status); 8458275SEric Cheng } 8468275SEric Cheng 8478275SEric Cheng dladm_status_t 8488275SEric Cheng dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask) 8498275SEric Cheng { 8508275SEric Cheng if (prefixlen < 0 || prefixlen > maxlen) 8518275SEric Cheng return (DLADM_STATUS_BADARG); 8528275SEric Cheng 8538275SEric Cheng while (prefixlen > 0) { 8548275SEric Cheng if (prefixlen >= 8) { 8558275SEric Cheng *mask++ = 0xFF; 8568275SEric Cheng prefixlen -= 8; 8578275SEric Cheng continue; 8588275SEric Cheng } 8598275SEric Cheng *mask |= 1 << (8 - prefixlen); 8608275SEric Cheng prefixlen--; 8618275SEric Cheng } 8628275SEric Cheng return (DLADM_STATUS_OK); 8638275SEric Cheng } 8648275SEric Cheng 8658275SEric Cheng dladm_status_t 8668275SEric Cheng dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen) 8678275SEric Cheng { 8688275SEric Cheng int bits; 8698275SEric Cheng int i, end; 8708275SEric Cheng 8718275SEric Cheng switch (plen) { 8728275SEric Cheng case IP_ABITS: 8738275SEric Cheng end = 3; 8748275SEric Cheng break; 8758275SEric Cheng case IPV6_ABITS: 8768275SEric Cheng end = 0; 8778275SEric Cheng break; 8788275SEric Cheng default: 8798275SEric Cheng return (DLADM_STATUS_BADARG); 8808275SEric Cheng } 8818275SEric Cheng 8828275SEric Cheng for (i = 3; i >= end; i--) { 8838275SEric Cheng if (mask->_S6_un._S6_u32[i] == 0) { 8848275SEric Cheng plen -= 32; 8858275SEric Cheng continue; 8868275SEric Cheng } 8878275SEric Cheng bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1; 8888275SEric Cheng if (bits == 0) 8898275SEric Cheng break; 8908275SEric Cheng plen -= bits; 8918275SEric Cheng } 8928275SEric Cheng *prefixlen = plen; 8938275SEric Cheng return (DLADM_STATUS_OK); 8948275SEric Cheng } 895