]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/mip6_ha.c
2ca6725d21303696cf3bbac83ca189046fdd7de7
[apple/xnu.git] / bsd / netinet6 / mip6_ha.c
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. */
75 struct 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 */
96 void
97 mip6_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 */
114 void
115 mip6_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 */
158 int
159 mip6_rec_raha(m, off)
160 struct mbuf *m; /* Mbuf containing the entire IPv6 packet */
161 int 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 */
257 int
258 mip6_ra_options(halp, icmp6msg, icmp6len)
259 struct mip6_ha_list *halp; /* Home Agent list entry */
260 caddr_t icmp6msg; /* icmp6 message */
261 int 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 */
372 struct mip6_subopt_hal *
373 mip6_hal_dynamic(own_addr)
374 struct 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 */
462 struct in6_addr *
463 mip6_global_addr(anycast_addr)
464 struct 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 */
524 void
525 mip6_icmp6_output(m)
526 struct 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 */
642 void
643 mip6_prefix_examine(halp, ifp, icmp6msg, icmp6len)
644 struct mip6_ha_list *halp; /* Home Agent list entry */
645 struct ifnet *ifp; /* Outgoing i/f for prefixes */
646 caddr_t icmp6msg; /* icmp6 message */
647 int 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 */
744 struct mip6_link_list *
745 mip6_ll_find(ifname)
746 char *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 */
767 struct mip6_link_list *
768 mip6_ll_create(ifname, ifp)
769 char *ifname;
770 struct 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_funneled, (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 */
811 struct mip6_link_list *
812 mip6_ll_delete(llp_del)
813 struct 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 */
873 struct mip6_ha_list *
874 mip6_hal_find(hal_start, ll_addr)
875 struct mip6_ha_list *hal_start; /* First entry in the Home Agents list */
876 struct 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 */
896 struct mip6_ha_list *
897 mip6_hal_create(hal_start, ll_addr, lifetime, pref)
898 struct mip6_ha_list **hal_start; /* First entry in the Home Agents list */
899 struct in6_addr *ll_addr; /* Link local address to search for */
900 u_int32_t lifetime; /* Node lifetime */
901 int16_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 */
934 void
935 mip6_hal_sort(ha_head)
936 struct 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 */
998 struct mip6_ha_list *
999 mip6_hal_delete(ha_start, ha_delete)
1000 struct mip6_ha_list **ha_start; /* First list entry of HAs for a link */
1001 struct 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 */
1063 void
1064 mip6_timer_ll_funneled(arg)
1065 void *arg; /* Not used */
1066 {
1067 #ifdef __APPLE__
1068 boolean_t funnel_state;
1069 funnel_state = thread_funnel_set(network_flock, TRUE);
1070 #endif
1071 mip6_timer_ll(arg);
1072 #ifdef __APPLE__
1073 (void) thread_funnel_set(network_flock, FALSE);
1074 #endif
1075 }
1076 void
1077 mip6_timer_ll(arg)
1078 void *arg; /* Not used */
1079 {
1080 struct mip6_link_list *llp; /* Current Link list entry */
1081 struct mip6_ha_list *halp; /* Current Home Agent list entry */
1082 int s;
1083
1084 /* Go through the entire Home Agent List and delete all entries
1085 for which the time has expired. */
1086 s = splnet();
1087 for (llp = mip6_llq; llp;) {
1088 for (halp = llp->ha_list; halp;) {
1089 halp->lifetime -= 1;
1090 if (halp->lifetime == 0)
1091 halp = mip6_hal_delete(&llp->ha_list, halp);
1092 else
1093 halp = halp->next;
1094 }
1095
1096 if (llp->ha_list == NULL)
1097 llp = mip6_ll_delete(llp);
1098 else
1099 llp = llp->next;
1100 }
1101 splx(s);
1102
1103 if (mip6_llq != NULL) {
1104 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
1105 mip6_timer_ll_handle =
1106 #endif
1107 timeout(mip6_timer_ll_funneled, (void *)0, hz);
1108 }
1109 }
1110
1111
1112
1113 /*
1114 ##############################################################################
1115 #
1116 # IOCTL FUNCTIONS
1117 # These functions are called from mip6_ioctl.
1118 #
1119 ##############################################################################
1120 */
1121
1122 /*
1123 ******************************************************************************
1124 * Function: mip6_write_config_data_ha
1125 * Description: This function is called to write certain config values for
1126 * MIPv6. The data is written into the global config structure.
1127 * Ret value: -
1128 ******************************************************************************
1129 */
1130 int mip6_write_config_data_ha(u_long cmd, void *arg)
1131 {
1132 int retval = 0;
1133
1134 switch (cmd) {
1135 case SIOCSHAPREF_MIP6:
1136 mip6_config.ha_pref = ((struct mip6_input_data *)arg)->value;
1137 break;
1138 }
1139 return retval;
1140 }
1141
1142
1143
1144 /*
1145 ******************************************************************************
1146 * Function: mip6_clear_config_data_ha
1147 * Description: This function is called to clear internal lists handled by
1148 * MIPv6.
1149 * Ret value: -
1150 ******************************************************************************
1151 */
1152 int mip6_clear_config_data_ha(u_long cmd, void *data)
1153 {
1154 int retval = 0;
1155 int s;
1156 struct mip6_link_list *llp;
1157
1158 s = splnet();
1159 switch (cmd) {
1160 case SIOCSHALISTFLUSH_MIP6:
1161 for (llp = mip6_llq; llp;)
1162 llp = mip6_ll_delete(llp);
1163 break;
1164 }
1165 splx(s);
1166 return retval;
1167 }
1168
1169
1170
1171 /*
1172 ******************************************************************************
1173 * Function: mip6_enable_func_ha
1174 * Description: This function is called to enable or disable certain functions
1175 * in mip6. The data is written into the global config struct.
1176 * Ret value: -
1177 ******************************************************************************
1178 */
1179 int mip6_enable_func_ha(u_long cmd, caddr_t data)
1180 {
1181 int enable;
1182 int retval = 0;
1183
1184 enable = ((struct mip6_input_data *)data)->value;
1185
1186 switch (cmd) {
1187 case SIOCSFWDSLUNICAST_MIP6:
1188 mip6_config.fwd_sl_unicast = enable;
1189 break;
1190
1191 case SIOCSFWDSLMULTICAST_MIP6:
1192 mip6_config.fwd_sl_multicast = enable;
1193 break;
1194 }
1195 return retval;
1196 }