4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include <sys/param.h>
38 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
46 #include <inet/ip6_asp.h>
49 * The size of the table we initially use to retrieve the kernel's policy
50 * table. If this value is too small, we use the value returned from the
51 * SIOCGIP6ADDRPOLICY ioctl.
53 #define KERN_POLICY_SIZE 32
54 #define IPV6DAS_MAXLINELEN 1024
55 #define IPV6DAS_MAXENTRIES 512
63 static char *myname
; /* Copied from argv[0] */
65 static int parseconf(const char *, ip6_asp_t
**);
66 static int setpolicy(int, ip6_asp_t
*, int);
67 static int printpolicy(int);
68 static int ip_mask_to_plen_v6(const in6_addr_t
*);
69 static in6_addr_t
*ip_plen_to_mask_v6(int, in6_addr_t
*);
70 static int strioctl(int, int, void *, int);
71 static void usage(void);
74 main(int argc
, char **argv
)
76 int opt
, status
, sock
, count
;
78 ipv6das_cmd_t ipv6das_cmd
= IPV6DAS_PRINTPOLICY
;
79 ip6_asp_t
*policy_table
;
83 (void) setlocale(LC_ALL
, "");
85 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
86 #define TEXT_DOMAIN "SYS_TEST"
89 (void) textdomain(TEXT_DOMAIN
);
91 while ((opt
= getopt(argc
, argv
, "df:")) != EOF
)
94 ipv6das_cmd
= IPV6DAS_SETDEFAULT
;
97 conf_filename
= optarg
;
98 ipv6das_cmd
= IPV6DAS_SETPOLICY
;
102 return (EXIT_FAILURE
);
105 /* shouldn't be any extra args */
107 return (EXIT_FAILURE
);
110 /* Open a socket that we can use to send ioctls down to IP. */
111 if ((sock
= socket(PF_INET6
, SOCK_DGRAM
, 0)) == -1) {
113 return (EXIT_FAILURE
);
116 switch (ipv6das_cmd
) {
117 case IPV6DAS_SETPOLICY
:
118 if ((count
= parseconf(conf_filename
, &policy_table
)) <= 0)
119 return (EXIT_FAILURE
);
120 status
= setpolicy(sock
, policy_table
, count
);
123 case IPV6DAS_SETDEFAULT
:
124 status
= setpolicy(sock
, NULL
, 0);
126 case IPV6DAS_PRINTPOLICY
:
128 status
= printpolicy(sock
);
137 * parseconf(filename, new_policy)
139 * Parses the file identified by filename, filling in new_policy
140 * with the address selection policy table specified in filename.
141 * Returns -1 on failure, or the number of table entries found
145 parseconf(const char *filename
, ip6_asp_t
**new_policy
)
148 char line
[IPV6DAS_MAXLINELEN
];
151 uint_t lineno
= 0, entryindex
= 0;
152 int plen
, precedence
;
156 ip6_asp_t tmp_policy
[IPV6DAS_MAXENTRIES
];
157 boolean_t have_default
= B_FALSE
;
158 in6_addr_t prefix
, mask
;
159 boolean_t comment_found
= B_FALSE
, end_of_line
= B_FALSE
;
161 if ((fp
= fopen(filename
, "r")) == NULL
) {
166 while (fgets(line
, sizeof (line
), fp
) != NULL
) {
167 if (entryindex
== IPV6DAS_MAXENTRIES
) {
168 (void) fprintf(stderr
,
169 gettext("%s: too many entries\n"), filename
);
177 /* Skip leading whitespace */
181 /* Is this a comment or blank line? */
182 if (*cp
== '#' || *cp
== '\0')
186 * Anything else must be of the form:
187 * <IPv6-addr>/<plen> <precedence> <label>
190 if ((cp
= strchr(cp
, '/')) == NULL
) {
191 (void) fprintf(stderr
,
192 gettext("%s: invalid prefix on line %d: %s\n"),
193 filename
, lineno
, prefixstr
);
197 if (inet_pton(AF_INET6
, prefixstr
, &prefix
) != 1) {
198 (void) fprintf(stderr
,
199 gettext("%s: invalid prefix on line %d: %s\n"),
200 filename
, lineno
, prefixstr
);
206 plen
= strtol(cp
, &end
, 10);
207 if (cp
== end
|| errno
!= 0) {
208 (void) fprintf(stderr
,
209 gettext("%s: invalid prefix length on line %d\n"),
213 if (ip_plen_to_mask_v6(plen
, &mask
) == NULL
) {
214 (void) fprintf(stderr
,
215 gettext("%s: invalid prefix length on line %d:"
216 " %d\n"), filename
, lineno
, plen
);
222 precedence
= strtol(cp
, &end
, 10);
223 if (cp
== end
|| precedence
< 0 || errno
!= 0) {
224 (void) fprintf(stderr
,
225 gettext("%s: invalid precedence on line %d\n"),
235 * NULL terminate the label string. The label string is
236 * composed of non-blank characters, and can optionally be
237 * followed by a comment.
239 while (*cp
!= '\0' && !isspace(*cp
) && *cp
!= '#')
242 comment_found
= B_TRUE
;
243 else if (*cp
== '\0' || *cp
== '\n')
244 end_of_line
= B_TRUE
;
247 labellen
= cp
- label
;
249 (void) fprintf(stderr
,
250 gettext("%s: missing label on line %d\n"),
254 if (labellen
>= IP6_ASP_MAXLABELSIZE
) {
255 (void) fprintf(stderr
,
256 gettext("%s: label too long on line %d, labels "
257 "have a %d character limit.\n"), filename
, lineno
,
258 IP6_ASP_MAXLABELSIZE
- 1);
262 tmp_policy
[entryindex
].ip6_asp_prefix
= prefix
;
263 tmp_policy
[entryindex
].ip6_asp_mask
= mask
;
264 tmp_policy
[entryindex
].ip6_asp_precedence
= precedence
;
266 * We're specifically using strncpy() to copy the label
267 * to take advantage of the fact that strncpy will add
268 * NULL characters to the target string up to the given
269 * length, so don't change the call to strncpy() with
270 * out also taking into account this requirement. The
271 * labels are stored in the kernel in that way in order
272 * to make comparisons more efficient: all 16 bytes of
273 * the labels are compared to each other; random bytes
274 * after the NULL terminator would yield incorrect
277 (void) strncpy(tmp_policy
[entryindex
].ip6_asp_label
, label
,
278 IP6_ASP_MAXLABELSIZE
);
281 * Anything else on the line should be a comment; print
282 * a warning if that's not the case.
284 if (!comment_found
&& !end_of_line
) {
286 while (*cp
!= '\0' && isspace(*cp
) && *cp
!= '#')
288 if (*cp
!= '\0' && *cp
!= '#') {
289 (void) fprintf(stderr
,
290 gettext("%s: characters following label "
291 "on line %d will be ignored\n"),
296 if (IN6_IS_ADDR_UNSPECIFIED(&prefix
) && plen
== 0)
297 have_default
= B_TRUE
;
299 comment_found
= B_FALSE
;
300 end_of_line
= B_FALSE
;
305 (void) fprintf(stderr
,
306 gettext("%s: config doesn't contain a default entry.\n"),
312 /* Allocate the caller's array. */
313 if ((*new_policy
= malloc(entryindex
* sizeof (ip6_asp_t
))) == NULL
) {
319 (void) memcpy(*new_policy
, tmp_policy
, entryindex
* sizeof (ip6_asp_t
));
328 * setpolicy(sock, new_policy, count)
330 * Sends an SIOCSIP6ADDRPOLICY ioctl to the kernel to set the address
331 * selection policy table pointed to by new_policy. count should be
332 * the number of entries in the table; sock should be an open INET6
333 * socket. Returns EXIT_FAILURE or EXIT_SUCCESS.
336 setpolicy(int sock
, ip6_asp_t
*new_policy
, int count
)
338 if (strioctl(sock
, SIOCSIP6ADDRPOLICY
, new_policy
,
339 count
* sizeof (ip6_asp_t
)) < 0) {
340 perror("SIOCSIP6ADDRPOLICY");
341 return (EXIT_FAILURE
);
343 return (EXIT_SUCCESS
);
349 * Queries the kernel for the current address selection policy using
350 * the open socket sock, and prints the result. Returns EXIT_FAILURE
351 * if the table cannot be obtained, or EXIT_SUCCESS if the table is
352 * obtained and printed successfully.
355 printpolicy(int sock
)
357 ip6_asp_t policy
[KERN_POLICY_SIZE
];
358 ip6_asp_t
*policy_ptr
= policy
;
359 int count
, policy_index
;
360 char prefixstr
[INET6_ADDRSTRLEN
+ sizeof ("/128")];
362 if ((count
= strioctl(sock
, SIOCGIP6ADDRPOLICY
, policy_ptr
,
363 KERN_POLICY_SIZE
* sizeof (ip6_asp_t
))) < 0) {
364 perror("SIOCGIP6ADDRPOLICY");
365 return (EXIT_FAILURE
);
367 if (count
> KERN_POLICY_SIZE
) {
368 policy_ptr
= malloc(count
* sizeof (ip6_asp_t
));
369 if (policy_ptr
== NULL
) {
371 return (EXIT_FAILURE
);
373 if ((count
= strioctl(sock
, SIOCGIP6ADDRPOLICY
, policy_ptr
,
374 count
* sizeof (ip6_asp_t
))) < 0) {
375 perror("SIOCGIP6ADDRPOLICY");
376 return (EXIT_FAILURE
);
382 * There should always at least be a default entry in the
383 * policy table, so the minimum acceptable value of
386 (void) fprintf(stderr
, gettext("%s: ERROR: "
387 "IPv6 address selection policy is empty.\n"), myname
);
388 return (EXIT_FAILURE
);
392 * The format printed here must also be parsable by parseconf(),
393 * since we expect users to be able to redirect this output to
394 * a usable configuration file if need be.
396 (void) printf("# Prefix "
397 " Precedence Label\n");
398 for (policy_index
= 0; policy_index
< count
; policy_index
++) {
399 (void) snprintf(prefixstr
, sizeof (prefixstr
), "%s/%d",
401 &policy_ptr
[policy_index
].ip6_asp_prefix
, prefixstr
,
403 ip_mask_to_plen_v6(&policy_ptr
[policy_index
].ip6_asp_mask
));
404 (void) printf("%-45s %10d %s\n", prefixstr
,
405 policy_ptr
[policy_index
].ip6_asp_precedence
,
406 policy_ptr
[policy_index
].ip6_asp_label
);
409 if (policy_ptr
!= policy
)
411 return (EXIT_SUCCESS
);
415 * ip_mask_to_plen_v6(v6mask)
417 * This function takes a mask and returns number of bits set in the
418 * mask (the represented prefix length). Assumes a contigious mask.
421 ip_mask_to_plen_v6(const in6_addr_t
*v6mask
)
427 if (v6mask
->_S6_un
._S6_u32
[3] == 0xffffffff) /* check for all ones */
430 /* Find number of words with 32 ones */
432 for (i
= 0; i
< 4; i
++) {
433 if (v6mask
->_S6_un
._S6_u32
[i
] == 0xffffffff) {
441 * Find number of bits in the last word by searching
442 * for the first one from the right
444 mask
= ntohl(v6mask
->_S6_un
._S6_u32
[i
]);
448 return (bits
+ 32 - (ffs(mask
) - 1));
452 * ip_plen_to_mask_v6(plen, bitmask)
454 * Convert a prefix length to the mask for that prefix.
455 * Returns the argument bitmask.
458 ip_plen_to_mask_v6(int plen
, in6_addr_t
*bitmask
)
462 if (plen
> IPV6_ABITS
|| plen
< 0)
465 (void) memset(bitmask
, 0, sizeof (in6_addr_t
));
469 ptr
= (uint32_t *)bitmask
;
471 *ptr
++ = 0xffffffffU
;
474 *ptr
= htonl(0xffffffffU
<< (32 - plen
));
479 * strioctl(fd, cmd, ptr, ilen)
481 * Passes an I_STR ioctl to fd. The ioctl type is specified by cmd, and
482 * any date to be sent down is specified by a pointer to the buffer (ptr)
483 * and the buffer size (ilen). Returns the return value from the ioctl()
487 strioctl(int fd
, int cmd
, void *ptr
, int ilen
)
497 while ((retv
= ioctl(fd
, I_STR
, &str
)) == -1) {
507 (void) fprintf(stderr
, gettext(
509 " %s -f <filename>\n"
510 " %s -d\n"), myname
, myname
, myname
);