6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libdladm / common / flowattr.c
blob6b170d033e170388ad11f701e62c1372993443d2
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <strings.h>
28 #include <sys/mac_flow.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <net/if_types.h>
35 #include <net/if_dl.h>
36 #include <inet/ip.h>
37 #include <inet/ip6.h>
39 #include <libdladm.h>
40 #include <libdlflow.h>
41 #include <libdlflow_impl.h>
43 /* max port number for UDP, TCP & SCTP */
44 #define MAX_PORT 65535
46 static fad_checkf_t do_check_local_ip;
47 static fad_checkf_t do_check_remote_ip;
48 static fad_checkf_t do_check_protocol;
49 static fad_checkf_t do_check_local_port;
50 static fad_checkf_t do_check_remote_port;
52 static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
54 static fattr_desc_t attr_table[] = {
55 { "local_ip", do_check_local_ip },
56 { "remote_ip", do_check_remote_ip },
57 { "transport", do_check_protocol },
58 { "local_port", do_check_local_port },
59 { "remote_port", do_check_remote_port },
60 { "dsfield", do_check_dsfield },
63 #define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t))
65 static dladm_status_t
66 do_check_local_ip(char *attr_val, flow_desc_t *fdesc)
68 return (do_check_ip_addr(attr_val, B_TRUE, fdesc));
71 static dladm_status_t
72 do_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
74 return (do_check_ip_addr(attr_val, B_FALSE, fdesc));
77 dladm_status_t
78 do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
80 dladm_status_t status;
81 int prefix_max, prefix_len = 0;
82 char *prefix_str, *endp = NULL;
83 flow_mask_t mask;
84 in6_addr_t *addr;
85 uchar_t *netmask;
86 struct in_addr v4addr;
87 struct in6_addr v6addr;
88 int family;
90 if ((prefix_str = strchr(addr_str, '/')) != NULL) {
91 *prefix_str++ = '\0';
92 errno = 0;
93 prefix_len = (int)strtol(prefix_str, &endp, 10);
94 if (errno != 0 || prefix_len == 0 || *endp != '\0')
95 return (DLADM_STATUS_INVALID_PREFIXLEN);
97 if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) {
98 family = AF_INET;
99 } else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) {
100 family = AF_INET6;
101 } else {
102 return (DLADM_STATUS_INVALID_IP);
105 mask = FLOW_IP_VERSION;
106 if (local) {
107 mask |= FLOW_IP_LOCAL;
108 addr = &fd->fd_local_addr;
109 netmask = (uchar_t *)&fd->fd_local_netmask;
110 } else {
111 mask |= FLOW_IP_REMOTE;
112 addr = &fd->fd_remote_addr;
113 netmask = (uchar_t *)&fd->fd_remote_netmask;
116 if (family == AF_INET) {
117 IN6_INADDR_TO_V4MAPPED(&v4addr, addr);
118 prefix_max = IP_ABITS;
119 fd->fd_ipversion = IPV4_VERSION;
120 netmask = (uchar_t *)
121 &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask))));
122 } else {
123 *addr = v6addr;
124 prefix_max = IPV6_ABITS;
125 fd->fd_ipversion = IPV6_VERSION;
128 if (prefix_len == 0)
129 prefix_len = prefix_max;
131 status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
133 if (status != DLADM_STATUS_OK) {
134 return (DLADM_STATUS_INVALID_PREFIXLEN);
137 fd->fd_mask |= mask;
138 return (DLADM_STATUS_OK);
141 dladm_status_t
142 do_check_protocol(char *attr_val, flow_desc_t *fdesc)
144 uint8_t protocol;
146 protocol = dladm_str2proto(attr_val);
148 if (protocol != 0) {
149 fdesc->fd_mask |= FLOW_IP_PROTOCOL;
150 fdesc->fd_protocol = protocol;
151 return (DLADM_STATUS_OK);
152 } else {
153 return (DLADM_STATUS_INVALID_PROTOCOL);
157 dladm_status_t
158 do_check_local_port(char *attr_val, flow_desc_t *fdesc)
160 return (do_check_port(attr_val, B_TRUE, fdesc));
163 dladm_status_t
164 do_check_remote_port(char *attr_val, flow_desc_t *fdesc)
166 return (do_check_port(attr_val, B_FALSE, fdesc));
169 dladm_status_t
170 do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
172 char *endp = NULL;
173 long val;
175 val = strtol(attr_val, &endp, 10);
176 if (val < 1 || val > MAX_PORT || *endp != '\0')
177 return (DLADM_STATUS_INVALID_PORT);
178 if (local) {
179 fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL;
180 fdesc->fd_local_port = htons((uint16_t)val);
181 } else {
182 fdesc->fd_mask |= FLOW_ULP_PORT_REMOTE;
183 fdesc->fd_remote_port = htons((uint16_t)val);
186 return (DLADM_STATUS_OK);
190 * Check for invalid and/or duplicate attribute specification
192 static dladm_status_t
193 flow_attrlist_check(dladm_arg_list_t *attrlist)
195 int i, j;
196 boolean_t isset[DLADM_MAX_FLOWATTRS];
197 boolean_t matched;
199 for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
200 isset[j] = B_FALSE;
202 for (i = 0; i < attrlist->al_count; i++) {
203 matched = B_FALSE;
204 for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
205 if (strcmp(attrlist->al_info[i].ai_name,
206 attr_table[j].ad_name) == 0) {
207 if (isset[j])
208 return (DLADM_STATUS_FLOW_INCOMPATIBLE);
209 else
210 isset[j] = B_TRUE;
211 matched = B_TRUE;
215 * if the attribute did not match any of the attribute in
216 * attr_table, then it's an invalid attribute.
218 if (!matched)
219 return (DLADM_STATUS_BADARG);
221 return (DLADM_STATUS_OK);
225 * Convert an attribute list to a flow_desc_t using the attribute ad_check()
226 * functions.
228 dladm_status_t
229 dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
231 dladm_status_t status = DLADM_STATUS_BADARG;
232 int i;
234 for (i = 0; i < attrlist->al_count; i++) {
235 dladm_arg_info_t *aip = &attrlist->al_info[i];
236 int j;
238 for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
239 fattr_desc_t *adp = &attr_table[j];
241 if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
242 continue;
244 if ((aip->ai_val == NULL) || (*aip->ai_val == NULL))
245 return (DLADM_STATUS_BADARG);
247 if (adp->ad_check != NULL)
248 status = adp->ad_check(*aip->ai_val, flowdesc);
249 else
250 status = DLADM_STATUS_BADARG;
252 if (status != DLADM_STATUS_OK)
253 return (status);
258 * Make sure protocol is specified if either local or
259 * remote port is specified.
261 if ((flowdesc->fd_mask &
262 (FLOW_ULP_PORT_LOCAL | FLOW_ULP_PORT_REMOTE)) != 0 &&
263 (flowdesc->fd_mask & FLOW_IP_PROTOCOL) == 0)
264 return (DLADM_STATUS_PORT_NOPROTO);
266 return (status);
269 void
270 dladm_free_attrs(dladm_arg_list_t *list)
272 dladm_free_args(list);
275 dladm_status_t
276 dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
279 if (dladm_parse_args(str, listp, novalues)
280 != DLADM_STATUS_OK)
281 return (DLADM_STATUS_ATTR_PARSE_ERR);
283 if (*listp != NULL && flow_attrlist_check(*listp)
284 != DLADM_STATUS_OK) {
285 dladm_free_attrs(*listp);
286 return (DLADM_STATUS_ATTR_PARSE_ERR);
289 return (DLADM_STATUS_OK);
292 dladm_status_t
293 do_check_dsfield(char *str, flow_desc_t *fd)
295 char *mask_str, *endp = NULL;
296 uint_t mask = 0xff, value;
298 if ((mask_str = strchr(str, ':')) != NULL) {
299 *mask_str++ = '\0';
300 errno = 0;
301 mask = strtoul(mask_str, &endp, 16);
302 if (errno != 0 || mask == 0 || mask > 0xff ||
303 *endp != '\0')
304 return (DLADM_STATUS_INVALID_DSFMASK);
306 errno = 0;
307 endp = NULL;
308 value = strtoul(str, &endp, 16);
309 if (errno != 0 || value == 0 || value > 0xff || *endp != '\0')
310 return (DLADM_STATUS_INVALID_DSF);
312 fd->fd_dsfield = (uint8_t)value;
313 fd->fd_dsfield_mask = (uint8_t)mask;
314 fd->fd_mask |= FLOW_IP_DSFIELD;
315 return (DLADM_STATUS_OK);
318 char *
319 dladm_proto2str(uint8_t protocol)
321 if (protocol == IPPROTO_TCP)
322 return ("tcp");
323 if (protocol == IPPROTO_UDP)
324 return ("udp");
325 if (protocol == IPPROTO_SCTP)
326 return ("sctp");
327 if (protocol == IPPROTO_ICMPV6)
328 return ("icmpv6");
329 if (protocol == IPPROTO_ICMP)
330 return ("icmp");
331 else
332 return ("");
335 uint8_t
336 dladm_str2proto(const char *protostr)
338 if (strncasecmp(protostr, "tcp", 3) == 0)
339 return (IPPROTO_TCP);
340 else if (strncasecmp(protostr, "udp", 3) == 0)
341 return (IPPROTO_UDP);
342 else if (strncasecmp(protostr, "sctp", 4) == 0)
343 return (IPPROTO_SCTP);
344 else if (strncasecmp(protostr, "icmpv6", 6) == 0)
345 return (IPPROTO_ICMPV6);
346 else if (strncasecmp(protostr, "icmp", 4) == 0)
347 return (IPPROTO_ICMP);
349 return (0);
352 void
353 dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
355 flow_desc_t fdesc = attrp->fa_flow_desc;
356 struct in_addr ipaddr;
357 int prefix_len, prefix_max;
358 char *cp, abuf[INET6_ADDRSTRLEN];
360 if (fdesc.fd_mask & FLOW_IP_LOCAL) {
361 if (fdesc.fd_ipversion == IPV6_VERSION) {
362 (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf,
363 INET6_ADDRSTRLEN);
364 cp = abuf;
365 prefix_max = IPV6_ABITS;
366 } else {
367 ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3];
368 cp = inet_ntoa(ipaddr);
369 prefix_max = IP_ABITS;
371 (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask,
372 prefix_max, &prefix_len);
373 (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len);
374 } else if (fdesc.fd_mask & FLOW_IP_REMOTE) {
375 if (fdesc.fd_ipversion == IPV6_VERSION) {
376 (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf,
377 INET6_ADDRSTRLEN);
378 cp = abuf;
379 prefix_max = IPV6_ABITS;
380 } else {
381 ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3];
382 cp = inet_ntoa(ipaddr);
383 prefix_max = IP_ABITS;
385 (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask,
386 prefix_max, &prefix_len);
387 (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len);
388 } else {
389 buf[0] = '\0';
393 void
394 dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
396 flow_desc_t fdesc = attrp->fa_flow_desc;
398 (void) snprintf(buf, buf_len, "%s",
399 dladm_proto2str(fdesc.fd_protocol));
402 void
403 dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
405 flow_desc_t fdesc = attrp->fa_flow_desc;
407 if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) {
408 (void) snprintf(buf, buf_len, "%d",
409 ntohs(fdesc.fd_local_port));
410 } else if (fdesc.fd_mask & FLOW_ULP_PORT_REMOTE) {
411 (void) snprintf(buf, buf_len, "%d",
412 ntohs(fdesc.fd_remote_port));
413 } else {
414 buf[0] = '\0';
418 void
419 dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
421 flow_desc_t fdesc = attrp->fa_flow_desc;
423 if (fdesc.fd_mask & FLOW_IP_DSFIELD) {
424 (void) snprintf(buf, buf_len, "0x%x:0x%x",
425 fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
426 } else {
427 buf[0] = '\0';