2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $FreeBSD: src/sbin/ifconfig/af_inet6.c,v 1.3 2005/06/16 19:37:09 ume Exp $
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
36 #include <net/if_var.h> /* for struct ifaddr */
37 #include <netinet/in.h>
38 #include <netinet/in_var.h>
39 #include <netinet6/nd6.h> /* Define ND6_INFINITE_LIFETIME */
40 #include <arpa/inet.h>
54 static struct in6_ifreq in6_ridreq
;
55 static struct in6_aliasreq in6_addreq
=
61 { 0, 0, ND6_INFINITE_LIFETIME
, ND6_INFINITE_LIFETIME
} };
62 static int ip6lifetime
;
64 static void in6_fillscopeid(struct sockaddr_in6
*sin6
);
65 static int prefix(void *, int);
66 static char *sec2str(time_t);
67 static int explicit_prefix
= 0;
69 static char addr_buf
[NI_MAXHOST
]; /* for getnameinfo() */
72 setifprefixlen(const char *addr
, int dummy __unused
, int s __unused
,
73 const struct afswtch
*afp
)
75 if (afp
->af_getprefix
!= NULL
)
76 afp
->af_getprefix(addr
, MASK
);
81 setip6flags(const char *addr __unused
, int flag
, int s __unused
,
82 const struct afswtch
*afp
)
84 if (afp
->af_af
!= AF_INET6
)
85 err(1, "address flags can be set only for inet6 addresses");
88 in6_addreq
.ifra_flags
&= ~(-flag
);
90 in6_addreq
.ifra_flags
|= flag
;
94 setip6lifetime(const char *cmd
, const char *val
, int s __unused
,
95 const struct afswtch
*afp
)
101 clock_gettime(CLOCK_MONOTONIC_FAST
, &now
);
102 newval
= (time_t)strtoul(val
, &ep
, 0);
104 errx(1, "invalid %s", cmd
);
105 if (afp
->af_af
!= AF_INET6
)
106 errx(1, "%s not allowed for the AF", cmd
);
107 if (strcmp(cmd
, "vltime") == 0) {
108 in6_addreq
.ifra_lifetime
.ia6t_expire
= now
.tv_sec
+ newval
;
109 in6_addreq
.ifra_lifetime
.ia6t_vltime
= newval
;
110 } else if (strcmp(cmd
, "pltime") == 0) {
111 in6_addreq
.ifra_lifetime
.ia6t_preferred
= now
.tv_sec
+ newval
;
112 in6_addreq
.ifra_lifetime
.ia6t_pltime
= newval
;
117 setip6pltime(const char *seconds
, int dummy __unused
, int s
,
118 const struct afswtch
*afp
)
120 setip6lifetime("pltime", seconds
, s
, afp
);
124 setip6vltime(const char *seconds
, int dummy __unused
, int s
,
125 const struct afswtch
*afp
)
127 setip6lifetime("vltime", seconds
, s
, afp
);
131 setip6eui64(const char *cmd
, int dummy __unused
, int s __unused
,
132 const struct afswtch
*afp
)
134 struct ifaddrs
*ifap
, *ifa
;
135 const struct sockaddr_in6
*sin6
= NULL
;
136 const struct in6_addr
*lladdr
= NULL
;
137 struct in6_addr
*in6
;
139 if (afp
->af_af
!= AF_INET6
)
140 errx(EXIT_FAILURE
, "%s not allowed for the AF", cmd
);
141 in6
= (struct in6_addr
*)&in6_addreq
.ifra_addr
.sin6_addr
;
142 if (memcmp(&in6addr_any
.s6_addr
[8], &in6
->s6_addr
[8], 8) != 0)
143 errx(EXIT_FAILURE
, "interface index is already filled");
144 if (getifaddrs(&ifap
) != 0)
145 err(EXIT_FAILURE
, "getifaddrs");
146 for (ifa
= ifap
; ifa
; ifa
= ifa
->ifa_next
) {
147 if (ifa
->ifa_addr
->sa_family
== AF_INET6
&&
148 strcmp(ifa
->ifa_name
, IfName
) == 0) {
149 sin6
= (const struct sockaddr_in6
*)ifa
->ifa_addr
;
150 if (IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
)) {
151 lladdr
= &sin6
->sin6_addr
;
157 errx(EXIT_FAILURE
, "could not determine link local address");
159 memcpy(&in6
->s6_addr
[8], &lladdr
->s6_addr
[8], 8);
165 in6_fillscopeid(struct sockaddr_in6
*sin6
)
167 #if defined(__KAME__) && defined(KAME_SCOPEID)
168 if (IN6_IS_ADDR_LINKLOCAL(&sin6
->sin6_addr
)) {
169 sin6
->sin6_scope_id
=
170 ntohs(*(u_int16_t
*)&sin6
->sin6_addr
.s6_addr
[2]);
171 sin6
->sin6_addr
.s6_addr
[2] = sin6
->sin6_addr
.s6_addr
[3] = 0;
179 in6_status(int s __unused
, const struct ifaddrs
*ifa
)
181 struct sockaddr_in6
*sin
, null_sin
;
182 struct in6_ifreq ifr6
;
185 struct in6_addrlifetime lifetime
;
187 int n_flags
, prefixlen
;
191 clock_gettime(CLOCK_MONOTONIC_FAST
, &now
);
193 memset(&null_sin
, 0, sizeof(null_sin
));
195 sin
= (struct sockaddr_in6
*)ifa
->ifa_addr
;
199 strlcpy(ifr6
.ifr_name
, IfName
, sizeof(ifr6
.ifr_name
));
200 if ((s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)) < 0) {
201 warn("socket(AF_INET6,SOCK_DGRAM)");
204 ifr6
.ifr_addr
= *sin
;
205 if (ioctl(s6
, SIOCGIFAFLAG_IN6
, &ifr6
) < 0) {
206 warn("ioctl(SIOCGIFAFLAG_IN6)");
210 flags6
= ifr6
.ifr_ifru
.ifru_flags6
;
211 memset(&lifetime
, 0, sizeof(lifetime
));
212 ifr6
.ifr_addr
= *sin
;
213 if (ioctl(s6
, SIOCGIFALIFETIME_IN6
, &ifr6
) < 0) {
214 warn("ioctl(SIOCGIFALIFETIME_IN6)");
218 lifetime
= ifr6
.ifr_ifru
.ifru_lifetime
;
221 /* XXX: embedded link local addr check */
222 if (IN6_IS_ADDR_LINKLOCAL(&sin
->sin6_addr
) &&
223 *(u_short
*)&sin
->sin6_addr
.s6_addr
[2] != 0) {
226 index
= *(u_short
*)&sin
->sin6_addr
.s6_addr
[2];
227 *(u_short
*)&sin
->sin6_addr
.s6_addr
[2] = 0;
228 if (sin
->sin6_scope_id
== 0)
229 sin
->sin6_scope_id
= ntohs(index
);
231 scopeid
= sin
->sin6_scope_id
;
233 if (f_addr
!= NULL
&& strcmp(f_addr
, "fqdn") == 0)
235 else if (f_addr
!= NULL
&& strcmp(f_addr
, "host") == 0)
238 n_flags
= NI_NUMERICHOST
;
240 error
= getnameinfo((struct sockaddr
*)sin
, sin
->sin6_len
, addr_buf
,
241 sizeof(addr_buf
), NULL
, 0, n_flags
);
243 inet_ntop(AF_INET6
, &sin
->sin6_addr
, addr_buf
,
245 printf("\tinet6 %s", addr_buf
);
247 if (ifa
->ifa_flags
& IFF_POINTOPOINT
) {
248 sin
= (struct sockaddr_in6
*)ifa
->ifa_dstaddr
;
250 * some of the interfaces do not have valid destination
253 if (sin
!= NULL
&& sin
->sin6_family
== AF_INET6
) {
254 /* XXX: embedded link local addr check */
255 if (IN6_IS_ADDR_LINKLOCAL(&sin
->sin6_addr
) &&
256 *(u_short
*)&sin
->sin6_addr
.s6_addr
[2] != 0) {
259 index
= *(u_short
*)&sin
->sin6_addr
.s6_addr
[2];
260 *(u_short
*)&sin
->sin6_addr
.s6_addr
[2] = 0;
261 if (sin
->sin6_scope_id
== 0)
262 sin
->sin6_scope_id
= ntohs(index
);
265 error
= getnameinfo((struct sockaddr
*)sin
,
266 sin
->sin6_len
, addr_buf
,
267 sizeof(addr_buf
), NULL
, 0,
270 inet_ntop(AF_INET6
, &sin
->sin6_addr
, addr_buf
,
272 printf(" --> %s", addr_buf
);
276 sin
= (struct sockaddr_in6
*)ifa
->ifa_netmask
;
279 prefixlen
= prefix(&sin
->sin6_addr
, sizeof(struct in6_addr
));
280 if (f_inet6
!= NULL
&& strcmp(f_inet6
, "cidr") == 0)
281 printf("/%d", prefixlen
);
283 printf(" prefixlen %d", prefixlen
);
285 if ((flags6
& IN6_IFF_ANYCAST
) != 0)
287 if ((flags6
& IN6_IFF_TENTATIVE
) != 0)
288 printf(" tentative");
289 if ((flags6
& IN6_IFF_DUPLICATED
) != 0)
290 printf(" duplicated");
291 if ((flags6
& IN6_IFF_DETACHED
) != 0)
293 if ((flags6
& IN6_IFF_DEPRECATED
) != 0)
294 printf(" deprecated");
295 if ((flags6
& IN6_IFF_AUTOCONF
) != 0)
297 if ((flags6
& IN6_IFF_TEMPORARY
) != 0)
298 printf(" temporary");
301 printf(" scopeid 0x%x", scopeid
);
303 if (ip6lifetime
&& (lifetime
.ia6t_preferred
|| lifetime
.ia6t_expire
)) {
305 if (lifetime
.ia6t_preferred
) {
306 printf(" %s", lifetime
.ia6t_preferred
< now
.tv_sec
307 ? "0" : sec2str(lifetime
.ia6t_preferred
- now
.tv_sec
));
313 if (lifetime
.ia6t_expire
) {
314 printf(" %s", lifetime
.ia6t_expire
< now
.tv_sec
315 ? "0" : sec2str(lifetime
.ia6t_expire
- now
.tv_sec
));
324 #define SIN6(x) ((struct sockaddr_in6 *) &(x))
325 static struct sockaddr_in6
*sin6tab
[] = {
326 SIN6(in6_ridreq
.ifr_addr
), SIN6(in6_addreq
.ifra_addr
),
327 SIN6(in6_addreq
.ifra_prefixmask
), SIN6(in6_addreq
.ifra_dstaddr
)
331 in6_getprefix(const char *plen
, int which
)
333 struct sockaddr_in6
*sin
= sin6tab
[which
];
335 int len
= atoi(plen
);
337 if ((len
< 0) || (len
> 128))
338 errx(1, "%s: bad value", plen
);
339 sin
->sin6_len
= sizeof(*sin
);
341 sin
->sin6_family
= AF_INET6
;
342 if ((len
== 0) || (len
== 128)) {
343 memset(&sin
->sin6_addr
, 0xff, sizeof(struct in6_addr
));
346 memset(&sin
->sin6_addr
, 0x00, sizeof(sin
->sin6_addr
));
347 for (cp
= (u_char
*)&sin
->sin6_addr
; len
> 7; len
-= 8)
349 *cp
= 0xff << (8 - len
);
353 in6_getaddr(const char *s
, int which
)
355 struct sockaddr_in6
*sin
= sin6tab
[which
];
356 struct addrinfo hints
, *res
;
361 sin
->sin6_len
= sizeof(*sin
);
363 sin
->sin6_family
= AF_INET6
;
367 if((p
= strrchr(s
, '/')) != NULL
) {
369 in6_getprefix(p
+ 1, MASK
);
374 if (sin
->sin6_family
== AF_INET6
) {
375 memset(&hints
, 0, sizeof(struct addrinfo
));
376 hints
.ai_family
= AF_INET6
;
377 error
= getaddrinfo(s
, NULL
, &hints
, &res
);
380 if (inet_pton(AF_INET6
, s
, &sin
->sin6_addr
) != 1)
381 errx(1, "%s: bad value", s
);
383 memcpy(sin
, res
->ai_addr
, res
->ai_addrlen
);
387 prefix(void *val
, int size
)
389 u_char
*name
= (u_char
*)val
;
390 int byte
, bit
, plen
= 0;
392 for (byte
= 0; byte
< size
; byte
++, plen
+= 8)
393 if (name
[byte
] != 0xff)
397 for (bit
= 7; bit
!= 0; bit
--, plen
++)
398 if (!(name
[byte
] & (1 << bit
)))
400 for (; bit
!= 0; bit
--)
401 if (name
[byte
] & (1 << bit
))
404 for (; byte
< size
; byte
++)
411 sec2str(time_t total
)
413 static char result
[256];
414 int days
, hours
, mins
, secs
;
419 days
= total
/ 3600 / 24;
420 hours
= (total
/ 3600) % 24;
421 mins
= (total
/ 60) % 60;
426 p
+= sprintf(p
, "%dd", days
);
428 if (!first
|| hours
) {
430 p
+= sprintf(p
, "%dh", hours
);
432 if (!first
|| mins
) {
434 p
+= sprintf(p
, "%dm", mins
);
436 sprintf(p
, "%ds", secs
);
438 sprintf(result
, "%lu", (unsigned long)total
);
444 in6_postproc(int s
, const struct afswtch
*afp
)
446 if (explicit_prefix
== 0) {
447 /* Aggregatable address architecture defines all prefixes
448 are 64. So, it is convenient to set prefixlen to 64 if
449 it is not specified. */
450 setifprefixlen("64", 0, s
, afp
);
451 /* in6_getprefix("64", MASK) if MASK is available here... */
456 in6_status_tunnel(int s
)
458 char src
[NI_MAXHOST
];
459 char dst
[NI_MAXHOST
];
460 struct in6_ifreq in6_ifr
;
461 const struct sockaddr
*sa
= (const struct sockaddr
*) &in6_ifr
.ifr_addr
;
463 memset(&in6_ifr
, 0, sizeof(in6_ifr
));
464 strlcpy(in6_ifr
.ifr_name
, IfName
, sizeof(in6_ifr
.ifr_name
));
466 if (ioctl(s
, SIOCGIFPSRCADDR_IN6
, &in6_ifr
) < 0)
468 if (sa
->sa_family
!= AF_INET6
)
470 in6_fillscopeid(&in6_ifr
.ifr_addr
);
471 if (getnameinfo(sa
, sa
->sa_len
, src
, sizeof(src
), 0, 0,
472 NI_NUMERICHOST
) != 0)
475 if (ioctl(s
, SIOCGIFPDSTADDR_IN6
, &in6_ifr
) < 0)
477 if (sa
->sa_family
!= AF_INET6
)
479 in6_fillscopeid(&in6_ifr
.ifr_addr
);
480 if (getnameinfo(sa
, sa
->sa_len
, dst
, sizeof(dst
), 0, 0,
481 NI_NUMERICHOST
) != 0)
484 printf("\ttunnel inet6 %s --> %s\n", src
, dst
);
488 in6_set_tunnel(int s
, struct addrinfo
*srcres
, struct addrinfo
*dstres
)
490 struct in6_aliasreq addreq
;
492 memset(&addreq
, 0, sizeof(addreq
));
493 strlcpy(addreq
.ifra_name
, IfName
, sizeof(addreq
.ifra_name
));
494 memcpy(&addreq
.ifra_addr
, srcres
->ai_addr
, srcres
->ai_addr
->sa_len
);
495 memcpy(&addreq
.ifra_dstaddr
, dstres
->ai_addr
, dstres
->ai_addr
->sa_len
);
497 if (ioctl(s
, SIOCSIFPHYADDR_IN6
, &addreq
) < 0)
498 warn("SIOCSIFPHYADDR_IN6");
501 static struct cmd inet6_cmds
[] = {
502 DEF_CMD_ARG("prefixlen", setifprefixlen
),
503 DEF_CMD("anycast", IN6_IFF_ANYCAST
, setip6flags
),
504 DEF_CMD("tentative", IN6_IFF_TENTATIVE
, setip6flags
),
505 DEF_CMD("-tentative", -IN6_IFF_TENTATIVE
, setip6flags
),
506 DEF_CMD("deprecated", IN6_IFF_DEPRECATED
, setip6flags
),
507 DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED
, setip6flags
),
508 DEF_CMD("autoconf", IN6_IFF_AUTOCONF
, setip6flags
),
509 DEF_CMD("-autoconf", -IN6_IFF_AUTOCONF
, setip6flags
),
510 DEF_CMD_ARG("pltime", setip6pltime
),
511 DEF_CMD_ARG("vltime", setip6vltime
),
512 DEF_CMD("eui64", 0, setip6eui64
),
515 static struct afswtch af_inet6
= {
518 .af_status
= in6_status
,
519 .af_getaddr
= in6_getaddr
,
520 .af_getprefix
= in6_getprefix
,
521 .af_postproc
= in6_postproc
,
522 .af_status_tunnel
= in6_status_tunnel
,
523 .af_settunnel
= in6_set_tunnel
,
524 .af_difaddr
= SIOCDIFADDR_IN6
,
525 .af_aifaddr
= SIOCAIFADDR_IN6
,
526 .af_ridreq
= &in6_ridreq
,
527 .af_addreq
= &in6_addreq
,
531 in6_Lopt_cb(const char *arg __unused
)
533 ip6lifetime
++; /* print IPv6 address lifetime */
535 static struct option in6_Lopt
= { "L", "[-L]", in6_Lopt_cb
, NULL
};
543 for (i
= 0; i
< nitems(inet6_cmds
); i
++)
544 cmd_register(&inet6_cmds
[i
]);
546 af_register(&af_inet6
);
547 opt_register(&in6_Lopt
);