1 /* $KAME: prefixconf.c,v 1.33 2005/09/16 11:30:15 suz Exp $ */
4 * Copyright (C) 2002 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/queue.h>
35 #include <sys/ioctl.h>
39 #include <net/if_var.h>
42 #include <netinet/in.h>
45 #include <netinet6/in6_var.h>
46 #include <netinet6/nd6.h>
60 #include "dhcp6c_ia.h"
61 #include "prefixconf.h"
63 TAILQ_HEAD(siteprefix_list
, siteprefix
);
66 struct pifc_list
*pifc_head
;
67 struct siteprefix_list siteprefix_head
;
69 #define iacpd_ia common.iactl_ia
70 #define iacpd_callback common.callback
71 #define iacpd_isvalid common.isvalid
72 #define iacpd_duration common.duration
73 #define iacpd_renew_data common.renew_data
74 #define iacpd_rebind_data common.rebind_data
75 #define iacpd_reestablish_data common.reestablish_data
76 #define iacpd_release_data common.release_data
77 #define iacpd_cleanup common.cleanup
80 TAILQ_ENTRY (siteprefix
) link
;
82 struct dhcp6_prefix prefix
;
84 struct dhcp6_timer
*timer
;
86 TAILQ_HEAD(, dhcp6_ifprefix
) ifprefix_list
; /* interface prefixes */
89 struct dhcp6_ifprefix
{
90 TAILQ_ENTRY(dhcp6_ifprefix
) plink
;
92 /* interface configuration */
93 struct prefix_ifconf
*ifconf
;
95 /* interface prefix parameters */
96 struct sockaddr_in6 paddr
;
99 /* address assigned on the interface based on the prefix */
100 struct sockaddr_in6 ifaddr
;
103 static struct siteprefix
*find_siteprefix
__P((struct siteprefix_list
*,
104 struct dhcp6_prefix
*, int));
105 static void remove_siteprefix
__P((struct siteprefix
*));
106 static int isvalid
__P((struct iactl
*));
107 static u_int32_t duration
__P((struct iactl
*));
108 static void cleanup
__P((struct iactl
*));
109 static int renew_prefix
__P((struct iactl
*, struct dhcp6_ia
*,
110 struct dhcp6_eventdata
**, struct dhcp6_eventdata
*));
111 static void renew_data_free
__P((struct dhcp6_eventdata
*));
113 static struct dhcp6_timer
*siteprefix_timo
__P((void *));
115 static int add_ifprefix
__P((struct siteprefix
*,
116 struct dhcp6_prefix
*, struct prefix_ifconf
*));
118 extern struct dhcp6_timer
*client6_timo
__P((void *));
119 static int pd_ifaddrconf
__P((ifaddrconf_cmd_t
, struct dhcp6_ifprefix
*ifpfx
));
122 update_prefix(ia
, pinfo
, pifc
, dhcpifp
, ctlp
, callback
)
124 struct dhcp6_prefix
*pinfo
;
125 struct pifc_list
*pifc
;
126 struct dhcp6_if
*dhcpifp
;
128 void (*callback
)__P((struct ia
*));
130 struct iactl_pd
*iac_pd
= (struct iactl_pd
*)*ctlp
;
131 struct siteprefix
*sp
;
132 struct prefix_ifconf
*pif
;
137 * A client discards any addresses for which the preferred
138 * lifetime is greater than the valid lifetime.
141 if (pinfo
->vltime
!= DHCP6_DURATION_INFINITE
&&
142 (pinfo
->pltime
== DHCP6_DURATION_INFINITE
||
143 pinfo
->pltime
> pinfo
->vltime
)) {
144 dprintf(LOG_INFO
, FNAME
, "invalid prefix %s/%d: "
145 "pltime (%lu) is larger than vltime (%lu)",
146 in6addr2str(&pinfo
->addr
, 0), pinfo
->plen
,
147 pinfo
->pltime
, pinfo
->vltime
);
151 if (iac_pd
== NULL
) {
152 if ((iac_pd
= malloc(sizeof(*iac_pd
))) == NULL
) {
153 dprintf(LOG_NOTICE
, FNAME
, "memory allocation failed");
156 memset(iac_pd
, 0, sizeof(*iac_pd
));
157 iac_pd
->iacpd_ia
= ia
;
158 iac_pd
->iacpd_callback
= callback
;
159 iac_pd
->iacpd_isvalid
= isvalid
;
160 iac_pd
->iacpd_duration
= duration
;
161 iac_pd
->iacpd_cleanup
= cleanup
;
162 iac_pd
->iacpd_renew_data
=
163 iac_pd
->iacpd_rebind_data
=
164 iac_pd
->iacpd_release_data
=
165 iac_pd
->iacpd_reestablish_data
= renew_prefix
;
167 iac_pd
->pifc_head
= pifc
;
168 TAILQ_INIT(&iac_pd
->siteprefix_head
);
169 *ctlp
= (struct iactl
*)iac_pd
;
172 /* search for the given prefix, and make a new one if it fails */
173 if ((sp
= find_siteprefix(&iac_pd
->siteprefix_head
, pinfo
, 1)) == NULL
) {
174 if ((sp
= malloc(sizeof(*sp
))) == NULL
) {
175 dprintf(LOG_NOTICE
, FNAME
, "memory allocation failed");
178 memset(sp
, 0, sizeof(*sp
));
179 sp
->prefix
.addr
= pinfo
->addr
;
180 sp
->prefix
.plen
= pinfo
->plen
;
182 TAILQ_INIT(&sp
->ifprefix_list
);
184 TAILQ_INSERT_TAIL(&iac_pd
->siteprefix_head
, sp
, link
);
189 /* update the timestamp of update */
190 sp
->updatetime
= time(NULL
);
192 /* update the prefix according to pinfo */
193 sp
->prefix
.pltime
= pinfo
->pltime
;
194 sp
->prefix
.vltime
= pinfo
->vltime
;
195 dprintf(LOG_DEBUG
, FNAME
, "%s a prefix %s/%d pltime=%lu, vltime=%lu",
196 spcreate
? "create" : "update",
197 in6addr2str(&pinfo
->addr
, 0), pinfo
->plen
,
198 pinfo
->pltime
, pinfo
->vltime
);
200 /* update prefix interfaces if necessary */
201 if (sp
->prefix
.vltime
!= 0 && spcreate
) {
202 for (pif
= TAILQ_FIRST(iac_pd
->pifc_head
); pif
;
203 pif
= TAILQ_NEXT(pif
, link
)) {
205 * The requesting router MUST NOT assign any delegated
206 * prefixes or subnets from the delegated prefix(es) to
207 * the link through which it received the DHCP message
208 * from the delegating router.
209 * [RFC3633 Section 12.1]
211 if (strcmp(pif
->ifname
, dhcpifp
->ifname
) == 0) {
212 dprintf(LOG_INFO
, FNAME
,
213 "skip %s as a prefix interface",
218 add_ifprefix(sp
, pinfo
, pif
);
223 * If the new vltime is 0, this prefix immediately expires.
224 * Otherwise, set up or update the associated timer.
226 switch (sp
->prefix
.vltime
) {
228 remove_siteprefix(sp
);
230 case DHCP6_DURATION_INFINITE
:
232 dhcp6_remove_timer(&sp
->timer
);
235 if (sp
->timer
== NULL
) {
236 sp
->timer
= dhcp6_add_timer(siteprefix_timo
, sp
);
237 if (sp
->timer
== NULL
) {
238 dprintf(LOG_NOTICE
, FNAME
,
239 "failed to add prefix timer");
240 remove_siteprefix(sp
); /* XXX */
244 /* update the timer */
245 timo
.tv_sec
= sp
->prefix
.vltime
;
248 dhcp6_set_timer(&timo
, sp
->timer
);
255 static struct siteprefix
*
256 find_siteprefix(head
, prefix
, match_plen
)
257 struct siteprefix_list
*head
;
258 struct dhcp6_prefix
*prefix
;
261 struct siteprefix
*sp
;
263 for (sp
= TAILQ_FIRST(head
); sp
; sp
= TAILQ_NEXT(sp
, link
)) {
264 if (!IN6_ARE_ADDR_EQUAL(&sp
->prefix
.addr
, &prefix
->addr
))
266 if (match_plen
== 0 || sp
->prefix
.plen
== prefix
->plen
)
274 remove_siteprefix(sp
)
275 struct siteprefix
*sp
;
277 struct dhcp6_ifprefix
*ip
;
279 dprintf(LOG_DEBUG
, FNAME
, "remove a site prefix %s/%d",
280 in6addr2str(&sp
->prefix
.addr
, 0), sp
->prefix
.plen
);
283 dhcp6_remove_timer(&sp
->timer
);
285 /* remove all interface prefixes */
286 while ((ip
= TAILQ_FIRST(&sp
->ifprefix_list
)) != NULL
) {
287 TAILQ_REMOVE(&sp
->ifprefix_list
, ip
, plink
);
288 pd_ifaddrconf(IFADDRCONF_REMOVE
, ip
);
292 TAILQ_REMOVE(&sp
->ctl
->siteprefix_head
, sp
, link
);
300 struct iactl_pd
*iac_pd
= (struct iactl_pd
*)iac
;
302 if (TAILQ_EMPTY(&iac_pd
->siteprefix_head
))
303 return (0); /* this IA is invalid */
311 struct iactl_pd
*iac_pd
= (struct iactl_pd
*)iac
;
312 struct siteprefix
*sp
;
313 u_int32_t base
= DHCP6_DURATION_INFINITE
, pltime
, passed
;
316 /* Determine the smallest period until pltime expires. */
318 for (sp
= TAILQ_FIRST(&iac_pd
->siteprefix_head
); sp
;
319 sp
= TAILQ_NEXT(sp
, link
)) {
320 passed
= now
> sp
->updatetime
?
321 (u_int32_t
)(now
- sp
->updatetime
) : 0;
322 pltime
= sp
->prefix
.pltime
> passed
?
323 sp
->prefix
.pltime
- passed
: 0;
325 if (base
== DHCP6_DURATION_INFINITE
|| pltime
< base
)
336 struct iactl_pd
*iac_pd
= (struct iactl_pd
*)iac
;
337 struct siteprefix
*sp
;
339 while ((sp
= TAILQ_FIRST(&iac_pd
->siteprefix_head
)) != NULL
) {
340 TAILQ_REMOVE(&iac_pd
->siteprefix_head
, sp
, link
);
341 remove_siteprefix(sp
);
348 renew_prefix(iac
, iaparam
, evdp
, evd
)
350 struct dhcp6_ia
*iaparam
;
351 struct dhcp6_eventdata
**evdp
, *evd
;
353 struct iactl_pd
*iac_pd
= (struct iactl_pd
*)iac
;
354 struct siteprefix
*sp
;
355 struct dhcp6_list
*ial
= NULL
, pl
;
358 for (sp
= TAILQ_FIRST(&iac_pd
->siteprefix_head
); sp
;
359 sp
= TAILQ_NEXT(sp
, link
)) {
360 if (dhcp6_add_listval(&pl
, DHCP6_LISTVAL_PREFIX6
,
361 &sp
->prefix
, NULL
) == NULL
)
365 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
368 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IAPD
, iaparam
, &pl
) == NULL
)
370 dhcp6_clear_list(&pl
);
372 evd
->type
= DHCP6_EVDATA_IAPD
;
373 evd
->data
= (void *)ial
;
374 evd
->privdata
= (void *)evdp
;
375 evd
->destructor
= renew_data_free
;
380 dhcp6_clear_list(&pl
);
388 struct dhcp6_eventdata
*evd
;
390 struct dhcp6_list
*ial
;
392 if (evd
->type
!= DHCP6_EVDATA_IAPD
) {
393 dprintf(LOG_ERR
, FNAME
, "assumption failure");
398 *(struct dhcp6_eventdata
**)evd
->privdata
= NULL
;
399 ial
= (struct dhcp6_list
*)evd
->data
;
400 dhcp6_clear_list(ial
);
404 static struct dhcp6_timer
*
408 struct siteprefix
*sp
= (struct siteprefix
*)arg
;
410 void (*callback
)__P((struct ia
*));
412 dprintf(LOG_DEBUG
, FNAME
, "prefix timeout for %s/%d",
413 in6addr2str(&sp
->prefix
.addr
, 0), sp
->prefix
.plen
);
415 ia
= sp
->ctl
->iacpd_ia
;
416 callback
= sp
->ctl
->iacpd_callback
;
419 dhcp6_remove_timer(&sp
->timer
);
421 remove_siteprefix(sp
);
429 add_ifprefix(siteprefix
, prefix
, pconf
)
430 struct siteprefix
*siteprefix
;
431 struct dhcp6_prefix
*prefix
;
432 struct prefix_ifconf
*pconf
;
434 struct dhcp6_ifprefix
*ifpfx
= NULL
;
440 if ((ifpfx
= malloc(sizeof(*ifpfx
))) == NULL
) {
441 dprintf(LOG_NOTICE
, FNAME
,
442 "failed to allocate memory for ifprefix");
445 memset(ifpfx
, 0, sizeof(*ifpfx
));
447 ifpfx
->ifconf
= pconf
;
449 ifpfx
->paddr
.sin6_family
= AF_INET6
;
451 ifpfx
->paddr
.sin6_len
= sizeof(struct sockaddr_in6
);
453 ifpfx
->paddr
.sin6_addr
= prefix
->addr
;
454 ifpfx
->plen
= prefix
->plen
+ pconf
->sla_len
;
456 * XXX: our current implementation assumes ifid len is a multiple of 8
458 if ((pconf
->ifid_len
% 8) != 0) {
459 dprintf(LOG_ERR
, FNAME
,
460 "assumption failure on the length of interface ID");
463 if (ifpfx
->plen
+ pconf
->ifid_len
< 0 ||
464 ifpfx
->plen
+ pconf
->ifid_len
> 128) {
465 dprintf(LOG_INFO
, FNAME
,
466 "invalid prefix length %d + %d + %d",
467 prefix
->plen
, pconf
->sla_len
, pconf
->ifid_len
);
471 /* copy prefix and SLA ID */
472 a
= &ifpfx
->paddr
.sin6_addr
;
474 for (i
= 0, b
= prefix
->plen
; b
> 0; b
-= 8, i
++)
475 a
->s6_addr
[i
] = prefix
->addr
.s6_addr
[i
];
476 sla_id
= htonl(pconf
->sla_id
);
477 sp
= ((char *)&sla_id
+ 3);
478 i
= (128 - pconf
->ifid_len
) / 8;
479 for (b
= pconf
->sla_len
; b
> 7; b
-= 8, sp
--)
480 a
->s6_addr
[--i
] = *sp
;
482 a
->s6_addr
[--i
] |= *sp
;
484 /* configure the corresponding address */
485 ifpfx
->ifaddr
= ifpfx
->paddr
;
486 for (i
= 15; i
>= pconf
->ifid_len
/ 8; i
--)
487 ifpfx
->ifaddr
.sin6_addr
.s6_addr
[i
] = pconf
->ifid
[i
];
488 if (pd_ifaddrconf(IFADDRCONF_ADD
, ifpfx
))
491 /* TODO: send a control message for other processes */
493 TAILQ_INSERT_TAIL(&siteprefix
->ifprefix_list
, ifpfx
, plink
);
503 #ifndef ND6_INFINITE_LIFETIME
504 #define ND6_INFINITE_LIFETIME 0xffffffff
508 pd_ifaddrconf(cmd
, ifpfx
)
509 ifaddrconf_cmd_t cmd
;
510 struct dhcp6_ifprefix
*ifpfx
;
512 struct prefix_ifconf
*pconf
;
514 pconf
= ifpfx
->ifconf
;
515 return (ifaddrconf(cmd
, pconf
->ifname
, &ifpfx
->ifaddr
, ifpfx
->plen
,
516 ND6_INFINITE_LIFETIME
, ND6_INFINITE_LIFETIME
));