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]
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
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>
34 #include <net/if_types.h>
35 #include <net/if_dl.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))
66 do_check_local_ip(char *attr_val
, flow_desc_t
*fdesc
)
68 return (do_check_ip_addr(attr_val
, B_TRUE
, fdesc
));
72 do_check_remote_ip(char *attr_val
, flow_desc_t
*fdesc
)
74 return (do_check_ip_addr(attr_val
, B_FALSE
, fdesc
));
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
;
86 struct in_addr v4addr
;
87 struct in6_addr v6addr
;
90 if ((prefix_str
= strchr(addr_str
, '/')) != NULL
) {
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) {
99 } else if (inet_pton(AF_INET6
, addr_str
, v6addr
.s6_addr
) == 1) {
102 return (DLADM_STATUS_INVALID_IP
);
105 mask
= FLOW_IP_VERSION
;
107 mask
|= FLOW_IP_LOCAL
;
108 addr
= &fd
->fd_local_addr
;
109 netmask
= (uchar_t
*)&fd
->fd_local_netmask
;
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
))));
124 prefix_max
= IPV6_ABITS
;
125 fd
->fd_ipversion
= IPV6_VERSION
;
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
);
138 return (DLADM_STATUS_OK
);
142 do_check_protocol(char *attr_val
, flow_desc_t
*fdesc
)
146 protocol
= dladm_str2proto(attr_val
);
149 fdesc
->fd_mask
|= FLOW_IP_PROTOCOL
;
150 fdesc
->fd_protocol
= protocol
;
151 return (DLADM_STATUS_OK
);
153 return (DLADM_STATUS_INVALID_PROTOCOL
);
158 do_check_local_port(char *attr_val
, flow_desc_t
*fdesc
)
160 return (do_check_port(attr_val
, B_TRUE
, fdesc
));
164 do_check_remote_port(char *attr_val
, flow_desc_t
*fdesc
)
166 return (do_check_port(attr_val
, B_FALSE
, fdesc
));
170 do_check_port(char *attr_val
, boolean_t local
, flow_desc_t
*fdesc
)
175 val
= strtol(attr_val
, &endp
, 10);
176 if (val
< 1 || val
> MAX_PORT
|| *endp
!= '\0')
177 return (DLADM_STATUS_INVALID_PORT
);
179 fdesc
->fd_mask
|= FLOW_ULP_PORT_LOCAL
;
180 fdesc
->fd_local_port
= htons((uint16_t)val
);
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
)
196 boolean_t isset
[DLADM_MAX_FLOWATTRS
];
199 for (j
= 0; j
< DLADM_MAX_FLOWATTRS
; j
++)
202 for (i
= 0; i
< attrlist
->al_count
; i
++) {
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) {
208 return (DLADM_STATUS_FLOW_INCOMPATIBLE
);
215 * if the attribute did not match any of the attribute in
216 * attr_table, then it's an invalid attribute.
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()
229 dladm_flow_attrlist_extract(dladm_arg_list_t
*attrlist
, flow_desc_t
*flowdesc
)
231 dladm_status_t status
= DLADM_STATUS_BADARG
;
234 for (i
= 0; i
< attrlist
->al_count
; i
++) {
235 dladm_arg_info_t
*aip
= &attrlist
->al_info
[i
];
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)
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
);
250 status
= DLADM_STATUS_BADARG
;
252 if (status
!= DLADM_STATUS_OK
)
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
);
270 dladm_free_attrs(dladm_arg_list_t
*list
)
272 dladm_free_args(list
);
276 dladm_parse_flow_attrs(char *str
, dladm_arg_list_t
**listp
, boolean_t novalues
)
279 if (dladm_parse_args(str
, listp
, novalues
)
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
);
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
) {
301 mask
= strtoul(mask_str
, &endp
, 16);
302 if (errno
!= 0 || mask
== 0 || mask
> 0xff ||
304 return (DLADM_STATUS_INVALID_DSFMASK
);
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
);
319 dladm_proto2str(uint8_t protocol
)
321 if (protocol
== IPPROTO_TCP
)
323 if (protocol
== IPPROTO_UDP
)
325 if (protocol
== IPPROTO_SCTP
)
327 if (protocol
== IPPROTO_ICMPV6
)
329 if (protocol
== IPPROTO_ICMP
)
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
);
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
,
365 prefix_max
= IPV6_ABITS
;
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
,
379 prefix_max
= IPV6_ABITS
;
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
);
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
));
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
));
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
);