1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
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>
38
39 #include <libdladm.h>
40 #include <libdlflow.h>
41 #include <libdlflow_impl.h>
42
43 /* max port number for UDP, TCP & SCTP */
44 #define MAX_PORT 65535
45
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;
51
52 static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
53
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 },
61 };
62
63 #define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t))
64
65 static dladm_status_t
do_check_local_ip(char * attr_val,flow_desc_t * fdesc)66 do_check_local_ip(char *attr_val, flow_desc_t *fdesc)
67 {
68 return (do_check_ip_addr(attr_val, B_TRUE, fdesc));
69 }
70
71 static dladm_status_t
do_check_remote_ip(char * attr_val,flow_desc_t * fdesc)72 do_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
73 {
74 return (do_check_ip_addr(attr_val, B_FALSE, fdesc));
75 }
76
77 dladm_status_t
do_check_ip_addr(char * addr_str,boolean_t local,flow_desc_t * fd)78 do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
79 {
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;
89
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);
96 }
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);
103 }
104
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;
114 }
115
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;
126 }
127
128 if (prefix_len == 0)
129 prefix_len = prefix_max;
130
131 status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
132
133 if (status != DLADM_STATUS_OK) {
134 return (DLADM_STATUS_INVALID_PREFIXLEN);
135 }
136
137 fd->fd_mask |= mask;
138 return (DLADM_STATUS_OK);
139 }
140
141 dladm_status_t
do_check_protocol(char * attr_val,flow_desc_t * fdesc)142 do_check_protocol(char *attr_val, flow_desc_t *fdesc)
143 {
144 uint8_t protocol;
145
146 protocol = dladm_str2proto(attr_val);
147
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);
154 }
155 }
156
157 dladm_status_t
do_check_local_port(char * attr_val,flow_desc_t * fdesc)158 do_check_local_port(char *attr_val, flow_desc_t *fdesc)
159 {
160 return (do_check_port(attr_val, B_TRUE, fdesc));
161 }
162
163 dladm_status_t
do_check_remote_port(char * attr_val,flow_desc_t * fdesc)164 do_check_remote_port(char *attr_val, flow_desc_t *fdesc)
165 {
166 return (do_check_port(attr_val, B_FALSE, fdesc));
167 }
168
169 dladm_status_t
do_check_port(char * attr_val,boolean_t local,flow_desc_t * fdesc)170 do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
171 {
172 char *endp = NULL;
173 long val;
174
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);
184 }
185
186 return (DLADM_STATUS_OK);
187 }
188
189 /*
190 * Check for invalid and/or duplicate attribute specification
191 */
192 static dladm_status_t
flow_attrlist_check(dladm_arg_list_t * attrlist)193 flow_attrlist_check(dladm_arg_list_t *attrlist)
194 {
195 uint_t i, j;
196 boolean_t isset[DLADM_MAX_FLOWATTRS];
197 boolean_t matched;
198
199 for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
200 isset[j] = B_FALSE;
201
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;
212 }
213 }
214 /*
215 * if the attribute did not match any of the attribute in
216 * attr_table, then it's an invalid attribute.
217 */
218 if (!matched)
219 return (DLADM_STATUS_BADARG);
220 }
221 return (DLADM_STATUS_OK);
222 }
223
224 /*
225 * Convert an attribute list to a flow_desc_t using the attribute ad_check()
226 * functions.
227 */
228 dladm_status_t
dladm_flow_attrlist_extract(dladm_arg_list_t * attrlist,flow_desc_t * flowdesc)229 dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
230 {
231 dladm_status_t status = DLADM_STATUS_BADARG;
232 uint_t i;
233
234 for (i = 0; i < attrlist->al_count; i++) {
235 dladm_arg_info_t *aip = &attrlist->al_info[i];
236 uint_t j;
237
238 if (aip->ai_val[0] == NULL)
239 return (DLADM_STATUS_BADARG);
240
241 for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
242 fattr_desc_t *adp = &attr_table[j];
243
244 if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
245 continue;
246
247 if (adp->ad_check != NULL)
248 status = adp->ad_check(*aip->ai_val, flowdesc);
249 else
250 status = DLADM_STATUS_BADARG;
251
252 if (status != DLADM_STATUS_OK)
253 return (status);
254 }
255 }
256
257 /*
258 * Make sure protocol is specified if either local or
259 * remote port is specified.
260 */
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);
265
266 return (status);
267 }
268
269 void
dladm_free_attrs(dladm_arg_list_t * list)270 dladm_free_attrs(dladm_arg_list_t *list)
271 {
272 dladm_free_args(list);
273 }
274
275 dladm_status_t
dladm_parse_flow_attrs(char * str,dladm_arg_list_t ** listp,boolean_t novalues)276 dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
277 {
278
279 if (dladm_parse_args(str, listp, novalues)
280 != DLADM_STATUS_OK)
281 return (DLADM_STATUS_ATTR_PARSE_ERR);
282
283 if (*listp != NULL && flow_attrlist_check(*listp)
284 != DLADM_STATUS_OK) {
285 dladm_free_attrs(*listp);
286 return (DLADM_STATUS_ATTR_PARSE_ERR);
287 }
288
289 return (DLADM_STATUS_OK);
290 }
291
292 dladm_status_t
do_check_dsfield(char * str,flow_desc_t * fd)293 do_check_dsfield(char *str, flow_desc_t *fd)
294 {
295 char *mask_str, *endp = NULL;
296 uint_t mask = 0xff, value;
297
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);
305 }
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);
311
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);
316 }
317
318 char *
dladm_proto2str(uint8_t protocol)319 dladm_proto2str(uint8_t protocol)
320 {
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 ("");
333 }
334
335 uint8_t
dladm_str2proto(const char * protostr)336 dladm_str2proto(const char *protostr)
337 {
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);
348
349 return (0);
350 }
351
352 void
dladm_flow_attr_ip2str(dladm_flow_attr_t * attrp,char * buf,size_t buf_len)353 dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
354 {
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];
359
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;
370 }
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;
384 }
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';
390 }
391 }
392
393 void
dladm_flow_attr_proto2str(dladm_flow_attr_t * attrp,char * buf,size_t buf_len)394 dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
395 {
396 flow_desc_t fdesc = attrp->fa_flow_desc;
397
398 (void) snprintf(buf, buf_len, "%s",
399 dladm_proto2str(fdesc.fd_protocol));
400 }
401
402 void
dladm_flow_attr_port2str(dladm_flow_attr_t * attrp,char * buf,size_t buf_len)403 dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
404 {
405 flow_desc_t fdesc = attrp->fa_flow_desc;
406
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';
415 }
416 }
417
418 void
dladm_flow_attr_dsfield2str(dladm_flow_attr_t * attrp,char * buf,size_t buf_len)419 dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
420 {
421 flow_desc_t fdesc = attrp->fa_flow_desc;
422
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';
428 }
429 }
430