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