]> git.saurik.com Git - apple/xnu.git/blame - bsd/netinet6/mip6_ha.c
xnu-123.5.tar.gz
[apple/xnu.git] / bsd / netinet6 / mip6_ha.c
CommitLineData
1c79356b
A
1/* $KAME: mip6_ha.c,v 1.8 2000/03/18 03:05:40 itojun Exp $ */
2
3/*
4 * Copyright (C) 1995, 1996, 1997, 1998, 1999 and 2000 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
18 *
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
29 * SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 1999 and 2000 Ericsson Radio Systems AB
34 * All rights reserved.
35 *
36 * Author: Conny Larsson <conny.larsson@era.ericsson.se>
37 *
38 */
39
40#if (defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__NetBSD__)
41#include "opt_inet.h"
42#endif
43
44/*
45 * Mobile IPv6 Home Agent
46 */
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/malloc.h>
50#include <sys/mbuf.h>
51#include <sys/domain.h>
52#include <sys/protosw.h>
53#include <sys/socket.h>
54#include <sys/errno.h>
55#include <sys/time.h>
56#include <sys/kernel.h>
57#include <sys/syslog.h>
58#include <sys/ioccom.h>
59#include <net/if.h>
60#include <net/route.h>
61#include <netinet/in.h>
62#include <netinet/in_var.h>
63#include <netinet/ip6.h>
64#include <netinet6/nd6.h>
65#include <netinet6/ip6_var.h>
66#include <netinet/icmp6.h>
67#include <netinet6/mip6.h>
68#include <netinet6/mip6_common.h>
69#include <machine/limits.h>
70
71#include <net/net_osdep.h>
72
73#if defined(__FreeBSD__) && __FreeBSD__ >= 3
74/* Declaration of Global variables. */
75struct callout_handle mip6_timer_ll_handle;
76#endif
77
78
79/*
80 ##############################################################################
81 #
82 # INITIALIZATION AND EXIT FUNCTIONS
83 # These functions are executed when the MIPv6 code is activated and de-
84 # activated respectively.
85 #
86 ##############################################################################
87 */
88
89/*
90 ******************************************************************************
91 * Function: mip6_ha_init
92 * Description: Initialization of MIPv6 variables that must be initialized
93 * before the HA code is executed.
94 ******************************************************************************
95 */
96void
97mip6_ha_init(void)
98{
99#if defined(__FreeBSD__) && __FreeBSD__ >= 3
100 /* Initialize handle for timer functions. */
101 callout_handle_init(&mip6_timer_ll_handle);
102#endif
103}
104
105
106
107/*
108 ******************************************************************************
109 * Function: mip6_ha_exit
110 * Description: This function is called when the HA module is unloaded
111 * (relesed) from the kernel.
112 ******************************************************************************
113 */
114void
115mip6_ha_exit()
116{
117 struct mip6_link_list *llp;
118 int s;
119
120 /* Cancel outstanding timeout function calls. */
121#if defined(__FreeBSD__) && __FreeBSD__ >= 3
122 untimeout(mip6_timer_ll, (void *)NULL, mip6_timer_ll_handle);
123#else
124 untimeout(mip6_timer_ll, (void *)NULL);
125#endif
126
127 /* Remove each entry in every queue. */
128 s = splnet();
129 for (llp = mip6_llq; llp;)
130 llp = mip6_ll_delete(llp);
131 mip6_llq = NULL;
132 splx(s);
133}
134
135
136
137/*
138 ##############################################################################
139 #
140 # RECEIVING FUNCTIONS
141 # These functions receives the incoming IPv6 packet and further processing of
142 # the packet depends on the content in the packet.
143 #
144 ##############################################################################
145 */
146
147/*
148 ******************************************************************************
149 * Function: mip6_rec_raha
150 * Description: Processed by a Home Agent. Includes a Router Advertisement
151 * with a H-bit set in the flags variable (checked by the calling
152 * function).
153 * A link list entry and a Home Agent List entry are created or
154 * modified if needed.
155 * Ret value: 0 Everything is OK. Otherwise appropriate error code.
156 ******************************************************************************
157 */
158int
159mip6_rec_raha(m, off)
160struct mbuf *m; /* Mbuf containing the entire IPv6 packet */
161int off; /* Offset from start of mbuf to start of RA */
162{
163 struct ifnet *ifp; /* Receiving interface */
164 struct ip6_hdr *ip6; /* IPv6 header */
165 struct nd_router_advert *ra; /* Router Advertisement */
166 struct mip6_link_list *llp; /* Link list entry */
167 struct mip6_ha_list *halp; /* Home Agent list entry */
168 caddr_t icmp6msg; /* Copy of mbuf (consequtively) */
169 char ifname[IFNAMSIZ+1]; /* Interface name */
170 int res, s, icmp6len;
171
172 /* Find out if the RA can be processed */
173 ip6 = mtod(m, struct ip6_hdr *);
174 if (ip6->ip6_hlim != 255) {
175 log(LOG_INFO,
176 "%s: Invalid hlim %d in Router Advertisement\n",
177 __FUNCTION__, ip6->ip6_hlim);
178 return 0;
179 }
180
181 if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
182 log(LOG_INFO,
183 "%s: Source Address %s is not link-local\n",
184 __FUNCTION__, ip6_sprintf(&ip6->ip6_src));
185 return 0;
186 }
187
188 /* Find out which interface the RA arrived at */
189 ifp = m->m_pkthdr.rcvif;
190 sprintf(ifname, "%s", if_name(ifp));
191
192 llp = mip6_ll_find(ifname);
193 if (llp == NULL) {
194 llp = mip6_ll_create(ifname, ifp);
195 if (llp == NULL)
196 return ENOBUFS;
197 }
198
199 /* The mbuf data must be stored consequtively to be able to
200 cast data from it. */
201 icmp6len = m->m_pkthdr.len - off;
202 icmp6msg = (caddr_t)MALLOC(icmp6len, M_TEMP, M_NOWAIT);
203 if (icmp6msg == NULL)
204 return IPPROTO_DONE;
205
206 m_copydata(m, off, icmp6len, icmp6msg);
207 ra = (struct nd_router_advert *)icmp6msg;
208
209 /* Find the Home Agent sending the RA and read its options.
210 This section must have high priority since the Home Agent
211 list entry lifetime is initialized to 0 and could be
212 removed by the timer function before the RA options have
213 been evaluated. */
214 s = splnet();
215 halp = mip6_hal_find(llp->ha_list, &ip6->ip6_src);
216 if (halp == NULL) {
217 halp = mip6_hal_create(&llp->ha_list, &ip6->ip6_src,
218 ntohl(ra->nd_ra_router_lifetime), 0);
219 if (halp == NULL) {
220 splx(s);
221 return ENOBUFS;
222 }
223 } else {
224 halp->lifetime = ntohl(ra->nd_ra_router_lifetime);
225 halp->pref = 0;
226 }
227
228 res = mip6_ra_options(halp, icmp6msg, icmp6len);
229 if (res) {
230 splx(s);
231 return res;
232 }
233 splx(s);
234 return 0;
235}
236
237
238
239/*
240 ##############################################################################
241 #
242 # UTILITY FUNCTIONS
243 # Miscellaneous functions needed for the internal processing of incoming and
244 # outgoing control signals.
245 #
246 ##############################################################################
247 */
248
249/*
250 ******************************************************************************
251 * Function: mip6_ra_options
252 * Description: Search through all the options in the Router Advertisement
253 * and store them in the Home Agent List.
254 * Ret value: 0 Everything is OK. Otherwise appropriate error code.
255 ******************************************************************************
256 */
257int
258mip6_ra_options(halp, icmp6msg, icmp6len)
259struct mip6_ha_list *halp; /* Home Agent list entry */
260caddr_t icmp6msg; /* icmp6 message */
261int icmp6len; /* Length of icmp6 message */
262{
263 struct mip6_addr_list *ap; /* Address list entry */
264 struct nd_opt_hai *hai; /* Home Agent information option */
265 struct nd_opt_advint *ai; /* Advertisement Interval option */
266 struct nd_opt_prefix_info *pi; /* Ptr to prefix information */
267 u_int8_t *optp; /* Ptr to current option in RA */
268 int cur_off; /* Cur offset from start of RA */
269
270 /* Process each option in the RA */
271 cur_off = sizeof(struct nd_router_advert);
272 while (cur_off < icmp6len) {
273 optp = ((caddr_t)icmp6msg + cur_off);
274 if (*optp == ND_OPT_PREFIX_INFORMATION) {
275 /* Check the prefix information option */
276 pi = (struct nd_opt_prefix_info *)optp;
277 if (pi->nd_opt_pi_len != 4) {
278 ip6stat.ip6s_badoptions++;
279 return IPPROTO_DONE;
280 }
281
282 if (!(pi->nd_opt_pi_flags_reserved &
283 ND_OPT_PI_FLAG_RTADDR)) {
284 cur_off += 4 * 8;
285 continue;
286 }
287
288 if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
289 IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
290 cur_off += 4 * 8;
291 continue;
292 }
293
294 /* Aggregatable unicast address, rfc2374 */
295 if (((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) > 0x10)
296 && (pi->nd_opt_pi_prefix_len != 64)) {
297 cur_off += 4 * 8;
298 continue;
299 }
300
301 /* Store the address if not already present */
302 for (ap = halp->addr_list; ap; ap = ap->next) {
303 if (IN6_ARE_ADDR_EQUAL(&ap->ip6_addr,
304 &pi->nd_opt_pi_prefix))
305 break;
306 }
307
308 if (ap == NULL) {
309 /* Create a new address list entry. */
310 ap = (struct mip6_addr_list *)
311 MALLOC(sizeof(struct mip6_addr_list),
312 M_TEMP, M_WAITOK);
313 if (ap == NULL)
314 return ENOBUFS;
315 bzero(ap, sizeof(struct mip6_addr_list));
316
317 ap->next = halp->addr_list;
318 ap->ip6_addr = pi->nd_opt_pi_prefix;
319 ap->prefix_len = pi->nd_opt_pi_prefix_len;
320 halp->addr_list = ap;
321 }
322 cur_off += 4 * 8;
323 continue;
324 } else if (*optp == ND_OPT_ADV_INTERVAL) {
325 /* Check the advertisement interval option */
326 ai = (struct nd_opt_advint *)optp;
327 if (ai->nd_opt_int_len != 1) {
328 ip6stat.ip6s_badoptions++;
329 return IPPROTO_DONE;
330 }
331
332 /* XXX. Function call to move detection */
333 cur_off += 8;
334 continue;
335 } else if (*optp == ND_OPT_HA_INFORMATION) {
336 /* Check the home agent information option */
337 hai = (struct nd_opt_hai *)optp;
338 if (hai->nd_opt_hai_len != 1) {
339 ip6stat.ip6s_badoptions++;
340 return IPPROTO_DONE;
341 }
342
343 halp->pref = ntohs(hai->nd_opt_hai_pref);
344 halp->lifetime = ntohs(hai->nd_opt_hai_lifetime);
345 cur_off += 8;
346 continue;
347 } else {
348 if (*(optp + 1) == 0) {
349 ip6stat.ip6s_badoptions++;
350 return IPPROTO_DONE;
351 }
352 cur_off += *(optp + 1) * 8;
353 }
354 }
355 return 0;
356}
357
358
359
360/*
361 ******************************************************************************
362 * Function: mip6_hal_dynamic
363 * Description: Search through all the link lists and home agents list and
364 * create a Home Agents List sub-option to be used in dynamic
365 * home agent address discovery.
366 * If my own global source address is included in the first
367 * home agents list entry, leave it. It will be in the source
368 * address of the outgoing packet anyway.
369 * Ret value: Ptr to the sub-option or NULL.
370 ******************************************************************************
371 */
372struct mip6_subopt_hal *
373mip6_hal_dynamic(own_addr)
374struct in6_addr *own_addr; /* Own global unicast source address used */
375{
376 struct mip6_link_list *llp; /* Link list entry */
377 struct mip6_ha_list *halp; /* Home Agent list entry */
378 struct mip6_subopt_hal *opt; /* Home Agents list sub-option */
379 struct mip6_addr_list *addrp; /* Address list entry */
380 struct mip6_addr_list *tmp_addrp; /* Temporary address list entry */
381 struct ifaddr *if_addr; /* Interface data */
382 struct sockaddr_in6 sin6;
383 char ifname[IFNAMSIZ+1]; /* Interface name */
384 int ii, len, found;
385
386 /* Find the interface */
387 bzero(&sin6, sizeof(struct sockaddr_in6));
388 sin6.sin6_len = sizeof(struct sockaddr_in6);
389 sin6.sin6_family = AF_INET6;
390 sin6.sin6_addr = *own_addr;
391
392 if_addr = ifa_ifwithaddr((struct sockaddr *)&sin6);
393 if (if_addr == NULL)
394 return NULL;
395
396 sprintf(ifname, "%s", if_name(if_addr->ifa_ifp));
397
398 llp = mip6_ll_find(ifname);
399 if (llp == NULL)
400 return NULL;
401
402 /* Allocate memory for home agent list sub option */
403 opt = (struct mip6_subopt_hal *)MALLOC(sizeof(struct mip6_subopt_hal) +
404 31 * sizeof(struct in6_addr),
405 M_TEMP, M_WAITOK);
406 if (opt == NULL)
407 return NULL;
408
409 opt->type = IP6SUBOPT_HALIST;
410 opt->len = 0;
411
412 /* Search the home agents list for the specific link. */
413 /* First, sort the Home Agent list in decending order */
414 mip6_hal_sort(&llp->ha_list);
415 ii = 0;
416 for (halp = llp->ha_list; halp; halp = halp->next) {
417 tmp_addrp = NULL;
418 found = 0;
419 for (addrp = halp->addr_list; addrp; addrp = addrp->next) {
420 len = addrp->prefix_len;
421 if (in6_are_prefix_equal(own_addr, &addrp->ip6_addr, len)) {
422 if (IN6_ARE_ADDR_EQUAL(own_addr, &addrp->ip6_addr)) {
423 found = 1;
424 break;
425 } else if (tmp_addrp == NULL)
426 tmp_addrp = addrp;
427 }
428 }
429
430 if (found && (ii != 0)) {
431 opt->halist[ii] = addrp->ip6_addr;
432 opt->len += IP6OPT_HALISTLEN;
433 ii += 1;
434 } else if (tmp_addrp != NULL) {
435 opt->halist[ii] = tmp_addrp->ip6_addr;
436 opt->len += IP6OPT_HALISTLEN;
437 ii += 1;
438 }
439 }
440
441 if (opt->len != 0)
442 return opt;
443 else {
444 _FREE(opt, M_TEMP);
445 return NULL;
446 }
447}
448
449
450
451/*
452 ******************************************************************************
453 * Function: mip6_global_addr
454 * Description: Search the list of IP addresses and find the interface for
455 * the anycast address. Find a link local address and use this
456 * address while searching through the list of home agents.
457 * When my own home agent is found, pick the first global address
458 * which matches the aycast prefix.
459 * Ret value: Ptr to the global unicast address or NULL.
460 ******************************************************************************
461 */
462struct in6_addr *
463mip6_global_addr(anycast_addr)
464struct in6_addr *anycast_addr; /* Home Agents anycast address */
465{
466 struct in6_ifaddr *ia; /* I/f address for anycast address */
467 struct in6_ifaddr *ia_ll; /* I/f address for link local address */
468 struct ifnet *ifp; /* Interface */
469 struct mip6_ha_list *halp; /* Home Agent list entry */
470 struct mip6_addr_list *addrp; /* Address list entry */
471 struct mip6_link_list *llp; /* Link list entry for anycast address */
472 char ifname[IFNAMSIZ+1]; /* Interface name */
473
474 /* Find out the interface for the anycast address */
475 for (ia = in6_ifaddr; ia; ia = ia->ia_next)
476 {
477 if (ia->ia_addr.sin6_family != AF_INET6)
478 continue;
479 if ((ia->ia6_flags & IN6_IFF_ANYCAST) &&
480 IN6_ARE_ADDR_EQUAL(anycast_addr, &ia->ia_addr.sin6_addr))
481 break;
482 }
483
484 if (ia == NULL)
485 return NULL;
486
487 ifp = ia->ia_ifa.ifa_ifp;
488 sprintf(ifname, "%s", if_name(ifp));
489 llp = mip6_ll_find(ifname);
490 if (llp == NULL)
491 return NULL;
492
493 /* Use link local address to identify my own home agent list entry */
494 /* XXX: I'm not sure if the 2nd arg is OK(jinmei@kame) */
495 ia_ll = in6ifa_ifpforlinklocal(ifp, 0);
496 if (ia_ll == NULL)
497 return NULL;
498 halp = mip6_hal_find(llp->ha_list, &ia_ll->ia_addr.sin6_addr);
499 if (halp == NULL)
500 return NULL;
501
502 /* Find my global address */
503 for (addrp = halp->addr_list; addrp; addrp = addrp->next) {
504 if (in6_are_prefix_equal(anycast_addr, &addrp->ip6_addr,
505 addrp->prefix_len))
506 return &addrp->ip6_addr;
507 }
508 return NULL;
509}
510
511
512
513/*
514 ******************************************************************************
515 * Function: mip6_icmp6_output
516 * Description: Takes care of an outgoing Router Advertisement. It finds the
517 * outgoing interface and add each prefix to the home agents list.
518 * Each prefix is also added to the internal prefix list used
519 * when a BU is received to decide whether the MN is on-link or
520 * not.
521 * Ret value: -
522 ******************************************************************************
523 */
524void
525mip6_icmp6_output(m)
526struct mbuf *m; /* Mbuf chain with IPv6 packet */
527{
528 struct ip6_hdr *ip6; /* IPv6 header */
529 struct icmp6_hdr *icmp6; /* ICMP6 header */
530 struct nd_router_advert *ra; /* Router Advertisement */
531 struct ifaddr *if_addr; /* Interface address */
532 struct mip6_link_list *llp; /* Link list entry */
533 struct mip6_ha_list *halp; /* Home Agent list entry */
534 struct sockaddr_in6 sin6;
535 caddr_t icmp6msg; /* Copy of mbuf (consequtively) */
536 char ifname[IFNAMSIZ+1]; /* Interface name */
537 int icmp6len, s, res;
538
539 /* Check if the packet shall be processed */
540 if (!MIP6_IS_HA_ACTIVE)
541 return;
542
543 ip6 = mtod(m, struct ip6_hdr *);
544 if (ip6->ip6_nxt != IPPROTO_ICMPV6)
545 return;
546
547 /* The mbuf data must be stored consequtively to be able to cast data
548 from it. */
549 icmp6len = m->m_pkthdr.len - sizeof(struct ip6_hdr);
550 icmp6msg = (caddr_t)MALLOC(icmp6len, M_TEMP, M_WAITOK);
551 if (icmp6msg == NULL)
552 return;
553
554 m_copydata(m, sizeof(struct ip6_hdr), icmp6len, icmp6msg);
555 icmp6 = (struct icmp6_hdr *)icmp6msg;
556
557 /* Check if the packet shall be processed */
558 if (icmp6->icmp6_type != ND_ROUTER_ADVERT) {
559 _FREE(icmp6msg, M_TEMP);
560 return;
561 }
562
563 if (icmp6->icmp6_code != 0) {
564 _FREE(icmp6msg, M_TEMP);
565 return;
566 }
567
568 if (icmp6len < sizeof(struct nd_router_advert)) {
569 _FREE(icmp6msg, M_TEMP);
570 return;
571 }
572
573 /* Find the outgoing interface */
574 bzero(&sin6, sizeof(struct sockaddr_in6));
575 sin6.sin6_len = sizeof(struct sockaddr_in6);
576 sin6.sin6_family = AF_INET6;
577 sin6.sin6_addr = ip6->ip6_src;
578
579 if_addr = ifa_ifwithaddr((struct sockaddr *)&sin6);
580 if (if_addr == NULL) {
581 _FREE(icmp6msg, M_TEMP);
582 return;
583 }
584
585 sprintf(ifname, "%s", if_name(if_addr->ifa_ifp));
586
587 llp = mip6_ll_find(ifname);
588 if (llp == NULL) {
589 llp = mip6_ll_create(ifname, if_addr->ifa_ifp);
590 if (llp == NULL) {
591 _FREE(icmp6msg, M_TEMP);
592 return;
593 }
594 }
595
596 /* Find the Home Agent sending the RA and read its options.
597 This section must have high priority since the Home Agent list
598 entry lifetime is initialized to 0 and could be removed by the
599 timer function before the RA options have been evaluated. */
600 s = splnet();
601 ra = (struct nd_router_advert *)icmp6;
602 halp = mip6_hal_find(llp->ha_list, &ip6->ip6_src);
603 if (halp == NULL) {
604 halp = mip6_hal_create(&llp->ha_list, &ip6->ip6_src,
605 ntohl(ra->nd_ra_router_lifetime), 0);
606 if (halp == NULL) {
607 _FREE(icmp6msg, M_TEMP);
608 splx(s);
609 return;
610 }
611 } else {
612 halp->lifetime = ntohl(ra->nd_ra_router_lifetime);
613 halp->pref = 0;
614 }
615
616 res = mip6_ra_options(halp, icmp6msg, icmp6len);
617 if (res) {
618 _FREE(icmp6msg, M_TEMP);
619 splx(s);
620 return;
621 }
622 splx(s);
623
624 /* Add the prefix to prefix list and the anycast address to the
625 interface. */
626 mip6_prefix_examine(halp, if_addr->ifa_ifp, icmp6msg, icmp6len);
627 _FREE(icmp6msg, M_TEMP);
628 return;
629}
630
631
632
633/*
634 ******************************************************************************
635 * Function: mip6_prefix_examine
636 * Description: Add each prefix in a RA to the internal prefix list. Make
637 * sure that the Home-Agents anycast address for the prefix
638 * has been assigned to the interface.
639 * Ret value: -
640 ******************************************************************************
641 */
642void
643mip6_prefix_examine(halp, ifp, icmp6msg, icmp6len)
644struct mip6_ha_list *halp; /* Home Agent list entry */
645struct ifnet *ifp; /* Outgoing i/f for prefixes */
646caddr_t icmp6msg; /* icmp6 message */
647int icmp6len; /* Length of icmp6 message */
648{
649 struct nd_opt_prefix_info *pi; /* Ptr to prefix information */
650 struct mip6_prefix *pq; /* Prefix queue entry */
651 struct in6_addr anycast_addr;
652 int cur_off; /* Cur offset from start of mbuf */
653 u_int8_t *opt_ptr; /* Ptr to current option in RA */
654
655 /* Process each option in the RA */
656 cur_off = sizeof(struct nd_router_advert);
657 while (cur_off < icmp6len) {
658 opt_ptr = ((caddr_t)icmp6msg + cur_off);
659 if (*opt_ptr == ND_OPT_PREFIX_INFORMATION) {
660 /* Check the prefix information option */
661 pi = (struct nd_opt_prefix_info *)opt_ptr;
662 if (pi->nd_opt_pi_len != 4)
663 return;
664
665 if (!(pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) {
666 cur_off += 4 * 8;
667 continue;
668 }
669
670 if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
671 IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix)) {
672 cur_off += 4 * 8;
673 continue;
674 }
675
676 /* Aggregatable unicast address, rfc2374 */
677 if (((pi->nd_opt_pi_prefix.s6_addr8[0] & 0xe0) > 0x10) &&
678 (pi->nd_opt_pi_prefix_len != 64)) {
679 cur_off += 4 * 8;
680 continue;
681 }
682
683 /* Store the prefix if not already present */
684 pq = mip6_prefix_find(&pi->nd_opt_pi_prefix,
685 pi->nd_opt_pi_prefix_len);
686 if (pq == NULL) {
687 int error;
688 pq = mip6_prefix_create(ifp, &pi->nd_opt_pi_prefix,
689 pi->nd_opt_pi_prefix_len,
690 pi->nd_opt_pi_valid_time);
691 if (pq == NULL)
692 return;
693
694 /* Create an Home Agent anycast address, add it to the
695 interface */
696 mip6_build_ha_anycast(&anycast_addr,
697 &pi->nd_opt_pi_prefix,
698 pi->nd_opt_pi_prefix_len);
699 error = mip6_add_ifaddr(&anycast_addr, ifp,
700 pi->nd_opt_pi_prefix_len,
701 IN6_IFF_ANYCAST);
702 if (error)
703 printf("%s: address assignment error (errno = %d).\n",
704 __FUNCTION__, error);
705
706 } else
707 pq->valid_time = ntohl(pi->nd_opt_pi_valid_time);
708
709 cur_off += 4 * 8;
710 continue;
711 } else {
712 if (*(opt_ptr + 1) == 0) {
713 return;
714 }
715 cur_off += *(opt_ptr + 1) * 8;
716 }
717 }
718}
719
720
721
722/*
723 ##############################################################################
724 #
725 # LIST FUNCTIONS
726 # The Home Agent maintains three lists (link list, home agent list and global
727 # address list) which are integrated into each other. Besides from this an
728 # internal prefix list is maintained in order to know which prefixes it is
729 # supposed to be home network for. The functions in this section are used for
730 # maintenance (create, find, delete and update entries) of these lists.
731 #
732 ##############################################################################
733 */
734
735/*
736 ******************************************************************************
737 * Function: mip6_ll_find
738 * Description: For each physical interface, i.e. link, that a Home Agent
739 * send and receive Router Advertisements at, a link list entry
740 * is maintained.
741 * Ret value: Pointer to found link list entry or NULL.
742 ******************************************************************************
743 */
744struct mip6_link_list *
745mip6_ll_find(ifname)
746char *ifname;
747{
748 struct mip6_link_list *llp;
749
750 for (llp = mip6_llq; llp; llp = llp->next) {
751 if (strcmp(ifname, llp->ifname) == 0)
752 return llp;
753 }
754 return NULL;
755}
756
757
758
759/*
760 ******************************************************************************
761 * Function: mip6_ll_create
762 * Description: Create a new link list entry and add it first to the link
763 * list. Start the timer if not already started.
764 * Ret value: Pointer to created link list entry or NULL.
765 ******************************************************************************
766 */
767struct mip6_link_list *
768mip6_ll_create(ifname, ifp)
769char *ifname;
770struct ifnet *ifp;
771{
772 struct mip6_link_list *llp;
773 int s, start_timer = 0;
774
775 if (mip6_llq == NULL)
776 start_timer = 1;
777
778 llp = (struct mip6_link_list *)MALLOC(sizeof(struct mip6_link_list),
779 M_TEMP, M_WAITOK);
780 if (llp == NULL)
781 return NULL;
782 bzero(llp, sizeof(struct mip6_link_list));
783
784 /* Add the new link list entry first to the list. */
785 s = splnet();
786 llp->next = mip6_llq;
787 strcpy(llp->ifname, ifname);
788 llp->ifp = ifp;
789 llp->ha_list = NULL;
790 mip6_llq = llp;
791 splx(s);
792
793 if (start_timer) {
794#if defined(__FreeBSD__) && __FreeBSD__ >= 3
795 mip6_timer_ll_handle =
796#endif
797 timeout(mip6_timer_ll, (void *)0, hz);
798 }
799 return llp;
800}
801
802
803
804/*
805 ******************************************************************************
806 * Function: mip6_ll_delete
807 * Description: Delete the requested link list entry.
808 * Ret value: Ptr to next entry in list or NULL if last entry removed.
809 ******************************************************************************
810 */
811struct mip6_link_list *
812mip6_ll_delete(llp_del)
813struct mip6_link_list *llp_del; /* Link list entry to be deleted */
814{
815 struct mip6_link_list *llp; /* Current entry in the list */
816 struct mip6_link_list *llp_prev; /* Previous entry in the list */
817 struct mip6_link_list *llp_next; /* Next entry in the list */
818 struct mip6_ha_list *halp; /* Home Agents list */
819 int s;
820
821 /* Find the requested entry in the link list. */
822 s = splnet();
823 llp_next = NULL;
824 llp_prev = NULL;
825 for (llp = mip6_llq; llp; llp = llp->next) {
826 llp_next = llp->next;
827 if (llp == llp_del) {
828 if (llp_prev == NULL)
829 mip6_llq = llp->next;
830 else
831 llp_prev->next = llp->next;
832
833 if (llp->ha_list) {
834 for (halp = llp->ha_list; halp;)
835 halp = mip6_hal_delete(&llp->ha_list, halp);
836 }
837
838#if MIP6_DEBUG
839 mip6_debug("\nLink List entry deleted (0x%x)\n", llp);
840#endif
841 _FREE(llp, M_TEMP);
842
843 /* Remove the timer if the BC queue is empty */
844 if (mip6_llq == NULL) {
845#if defined(__FreeBSD__) && __FreeBSD__ >= 3
846 untimeout(mip6_timer_ll, (void *)NULL, mip6_timer_ll_handle);
847 callout_handle_init(&mip6_timer_ll_handle);
848#else
849 untimeout(mip6_timer_ll, (void *)NULL);
850#endif
851 }
852 break;
853 }
854 llp_prev = llp;
855 }
856 splx(s);
857 return llp_next;
858}
859
860
861
862/*
863 ******************************************************************************
864 * Function: mip6_hal_find
865 * Description: Find a Home Agent list entry at a specific link. There will
866 * be one entry for each node sending a Router Advertisement
867 * with the H-bit set including a Prefix Information option
868 * with the R-bit set, for which the Router lifetime or the
869 * Home Agent lifetime (included in a separate option) is not 0.
870 * Ret value: Pointer to found Home Agent list entry or NULL.
871 ******************************************************************************
872 */
873struct mip6_ha_list *
874mip6_hal_find(hal_start, ll_addr)
875struct mip6_ha_list *hal_start; /* First entry in the Home Agents list */
876struct in6_addr *ll_addr; /* Link local address to search for */
877{
878 struct mip6_ha_list *halp;
879
880 for (halp = hal_start; halp; halp = halp->next) {
881 if (IN6_ARE_ADDR_EQUAL(&halp->ll_addr, ll_addr))
882 return halp;
883 }
884 return NULL;
885}
886
887
888
889/*
890 ******************************************************************************
891 * Function: mip6_hal_create
892 * Description: Create a Home Agent list entry for a specific link.
893 * Ret value: Pointer to created Home Agent list entry or NULL.
894 ******************************************************************************
895 */
896struct mip6_ha_list *
897mip6_hal_create(hal_start, ll_addr, lifetime, pref)
898struct mip6_ha_list **hal_start; /* First entry in the Home Agents list */
899struct in6_addr *ll_addr; /* Link local address to search for */
900u_int32_t lifetime; /* Node lifetime */
901int16_t pref; /* Node preference */
902{
903 struct mip6_ha_list *halp;
904 int s;
905
906 halp = (struct mip6_ha_list *)MALLOC(sizeof(struct mip6_ha_list),
907 M_TEMP, M_WAITOK);
908 if (halp == NULL)
909 return NULL;
910 bzero(halp, sizeof(struct mip6_ha_list));
911
912 /* Add the new home agent list entry first to the list. */
913 s = splnet();
914 halp->next = *hal_start;
915 halp->ll_addr = *ll_addr;
916 halp->lifetime = lifetime;
917 halp->pref = pref;
918 halp->addr_list = NULL;
919 *hal_start = halp;
920 splx(s);
921 return halp;
922}
923
924
925
926/*
927 ******************************************************************************
928 * Function: mip6_hal_sort
929 * Description: Sort the Home Agent list in decending order. Uses a temporary
930 * list where all the existing elements are moved.
931 * Ret value: -
932 ******************************************************************************
933 */
934void
935mip6_hal_sort(ha_head)
936struct mip6_ha_list **ha_head; /* Start of Home Agent list */
937{
938 struct mip6_ha_list *start, *halp;
939 struct mip6_ha_list *halp_prev, *halp_before, *halp_move;
940 struct mip6_ha_list *local_start, *local_last;
941 int16_t last_pref;
942 int s;
943
944 if (*ha_head == NULL)
945 return;
946
947 s = splnet();
948 start = *ha_head;
949 local_start = NULL;
950 local_last = NULL;
951
952 while (1) {
953 /* Find entry with highest preference */
954 last_pref = SHRT_MIN;
955 halp_prev = NULL;
956 for (halp = start; halp; halp = halp->next) {
957 if (halp->pref > last_pref) {
958 last_pref = halp->pref;
959 halp_move = halp;
960 halp_before = halp_prev;
961 }
962 halp_prev = halp;
963 }
964
965 /* Move it to the new list */
966 if (local_start == NULL)
967 local_start = halp_move;
968 else
969 local_last->next = halp_move;
970 local_last = halp_move;
971
972 /* Update the existing list */
973 if (halp_before == NULL)
974 start = halp_move->next;
975 else
976 halp_before->next = halp_move->next;
977
978 if (start == NULL)
979 break;
980 }
981 *ha_head = local_start;
982 splx(s);
983 return;
984}
985
986
987
988/*
989 ******************************************************************************
990 * Function: mip6_hal_delete
991 * Description: Delete a Home Agent list entry. If there are any address list
992 * entries associated with the Home Agent entry they are deleted
993 * as well.
994 * Ret value: Pointer to the next Home Agent list entry.
995 * NULL if the remaining list is empty or end of list reached.
996 ******************************************************************************
997 */
998struct mip6_ha_list *
999mip6_hal_delete(ha_start, ha_delete)
1000struct mip6_ha_list **ha_start; /* First list entry of HAs for a link */
1001struct mip6_ha_list *ha_delete; /* Home Agent entry to delete */
1002{
1003 struct mip6_ha_list *halp; /* Current HA list entry */
1004 struct mip6_ha_list *halp_prev; /* Previous HA list entry */
1005 struct mip6_addr_list *addrp; /* Address list entry */
1006 struct mip6_addr_list *addr_delete; /* Address list entry to delete */
1007 int s;
1008
1009 s = splnet();
1010 halp_prev = NULL;
1011 for (halp = *ha_start; halp; halp = halp->next) {
1012 if (halp != ha_delete) {
1013 halp_prev = halp;
1014 continue;
1015 }
1016
1017 /* Search the address list and remove each entry */
1018 for (addrp = halp->addr_list; addrp;) {
1019 addr_delete = addrp;
1020 addrp = addrp->next;
1021 _FREE(addr_delete, M_TEMP);
1022 }
1023
1024 /* Make sure that the pointer to the first entry is correct */
1025 if (halp == *ha_start) {
1026 *ha_start = halp->next;
1027 _FREE(halp, M_TEMP);
1028 splx(s);
1029 return *ha_start;
1030 } else {
1031 halp_prev->next = halp->next;
1032 _FREE(halp, M_TEMP);
1033 splx(s);
1034 return halp_prev->next;
1035 }
1036 }
1037 splx(s);
1038 return NULL;
1039}
1040
1041
1042
1043/*
1044 ##############################################################################
1045 #
1046 # TIMER FUNCTIONS
1047 # These functions are called at regular basis. They operate on the lists,
1048 # e.g. reducing timer counters and removing entries from the list if needed.
1049 #
1050 ##############################################################################
1051 */
1052
1053/*
1054 ******************************************************************************
1055 * Function: mip6_timer_ll
1056 * Description: Search the Home Agent list for each link and delete entries for
1057 * which the timer has expired.
1058 * If there are more entries left in the Home Agent list, call
1059 * this fuction again once every second until the list is empty.
1060 * Ret value: -
1061 ******************************************************************************
1062 */
1063void
1064mip6_timer_ll(arg)
1065void *arg; /* Not used */
1066{
1067 struct mip6_link_list *llp; /* Current Link list entry */
1068 struct mip6_ha_list *halp; /* Current Home Agent list entry */
1069 int s;
1070#ifdef __APPLE__
1071 boolean_t funnel_state;
1072 funnel_state = thread_set_funneled(TRUE);
1073#endif
1074
1075 /* Go through the entire Home Agent List and delete all entries
1076 for which the time has expired. */
1077 s = splnet();
1078 for (llp = mip6_llq; llp;) {
1079 for (halp = llp->ha_list; halp;) {
1080 halp->lifetime -= 1;
1081 if (halp->lifetime == 0)
1082 halp = mip6_hal_delete(&llp->ha_list, halp);
1083 else
1084 halp = halp->next;
1085 }
1086
1087 if (llp->ha_list == NULL)
1088 llp = mip6_ll_delete(llp);
1089 else
1090 llp = llp->next;
1091 }
1092 splx(s);
1093
1094 if (mip6_llq != NULL) {
1095#if defined(__FreeBSD__) && __FreeBSD__ >= 3
1096 mip6_timer_ll_handle =
1097#endif
1098 timeout(mip6_timer_ll, (void *)0, hz);
1099 }
1100#ifdef __APPLE__
1101 (void) thread_set_funneled(funnel_state);
1102#endif
1103}
1104
1105
1106
1107/*
1108 ##############################################################################
1109 #
1110 # IOCTL FUNCTIONS
1111 # These functions are called from mip6_ioctl.
1112 #
1113 ##############################################################################
1114 */
1115
1116/*
1117 ******************************************************************************
1118 * Function: mip6_write_config_data_ha
1119 * Description: This function is called to write certain config values for
1120 * MIPv6. The data is written into the global config structure.
1121 * Ret value: -
1122 ******************************************************************************
1123 */
1124int mip6_write_config_data_ha(u_long cmd, void *arg)
1125{
1126 int retval = 0;
1127
1128 switch (cmd) {
1129 case SIOCSHAPREF_MIP6:
1130 mip6_config.ha_pref = ((struct mip6_input_data *)arg)->value;
1131 break;
1132 }
1133 return retval;
1134}
1135
1136
1137
1138/*
1139 ******************************************************************************
1140 * Function: mip6_clear_config_data_ha
1141 * Description: This function is called to clear internal lists handled by
1142 * MIPv6.
1143 * Ret value: -
1144 ******************************************************************************
1145 */
1146int mip6_clear_config_data_ha(u_long cmd, void *data)
1147{
1148 int retval = 0;
1149 int s;
1150 struct mip6_link_list *llp;
1151
1152 s = splnet();
1153 switch (cmd) {
1154 case SIOCSHALISTFLUSH_MIP6:
1155 for (llp = mip6_llq; llp;)
1156 llp = mip6_ll_delete(llp);
1157 break;
1158 }
1159 splx(s);
1160 return retval;
1161}
1162
1163
1164
1165/*
1166 ******************************************************************************
1167 * Function: mip6_enable_func_ha
1168 * Description: This function is called to enable or disable certain functions
1169 * in mip6. The data is written into the global config struct.
1170 * Ret value: -
1171 ******************************************************************************
1172 */
1173int mip6_enable_func_ha(u_long cmd, caddr_t data)
1174{
1175 int enable;
1176 int retval = 0;
1177
1178 enable = ((struct mip6_input_data *)data)->value;
1179
1180 switch (cmd) {
1181 case SIOCSFWDSLUNICAST_MIP6:
1182 mip6_config.fwd_sl_unicast = enable;
1183 break;
1184
1185 case SIOCSFWDSLMULTICAST_MIP6:
1186 mip6_config.fwd_sl_multicast = enable;
1187 break;
1188 }
1189 return retval;
1190}