OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [net/] [bsd_tcpip/] [v2_0/] [src/] [sys/] [netinet6/] [in6_src.c] - Blame information for rev 27

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 27 unneback
//==========================================================================
2
//
3
//      src/sys/netinet6/in6_src.c
4
//
5
//==========================================================================
6
//####BSDCOPYRIGHTBEGIN####
7
//
8
// -------------------------------------------
9
//
10
// Portions of this software may have been derived from OpenBSD, 
11
// FreeBSD or other sources, and are covered by the appropriate
12
// copyright disclaimers included herein.
13
//
14
// Portions created by Red Hat are
15
// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
16
//
17
// -------------------------------------------
18
//
19
//####BSDCOPYRIGHTEND####
20
//==========================================================================
21
 
22
/*      $KAME: in6_src.c,v 1.96 2001/12/24 10:39:29 jinmei Exp $        */
23
 
24
/*
25
 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
26
 * All rights reserved.
27
 *
28
 * Redistribution and use in source and binary forms, with or without
29
 * modification, are permitted provided that the following conditions
30
 * are met:
31
 * 1. Redistributions of source code must retain the above copyright
32
 *    notice, this list of conditions and the following disclaimer.
33
 * 2. Redistributions in binary form must reproduce the above copyright
34
 *    notice, this list of conditions and the following disclaimer in the
35
 *    documentation and/or other materials provided with the distribution.
36
 * 3. Neither the name of the project nor the names of its contributors
37
 *    may be used to endorse or promote products derived from this software
38
 *    without specific prior written permission.
39
 *
40
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
41
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
44
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50
 * SUCH DAMAGE.
51
 */
52
 
53
/*
54
 * Copyright (c) 1982, 1986, 1991, 1993
55
 *      The Regents of the University of California.  All rights reserved.
56
 *
57
 * Redistribution and use in source and binary forms, with or without
58
 * modification, are permitted provided that the following conditions
59
 * are met:
60
 * 1. Redistributions of source code must retain the above copyright
61
 *    notice, this list of conditions and the following disclaimer.
62
 * 2. Redistributions in binary form must reproduce the above copyright
63
 *    notice, this list of conditions and the following disclaimer in the
64
 *    documentation and/or other materials provided with the distribution.
65
 * 3. All advertising materials mentioning features or use of this software
66
 *    must display the following acknowledgement:
67
 *      This product includes software developed by the University of
68
 *      California, Berkeley and its contributors.
69
 * 4. Neither the name of the University nor the names of its contributors
70
 *    may be used to endorse or promote products derived from this software
71
 *    without specific prior written permission.
72
 *
73
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83
 * SUCH DAMAGE.
84
 *
85
 *      @(#)in_pcb.c    8.2 (Berkeley) 1/4/94
86
 */
87
 
88
#include <sys/param.h>
89
#include <sys/malloc.h>
90
#include <sys/mbuf.h>
91
#include <sys/protosw.h>
92
#include <sys/socket.h>
93
#include <sys/socketvar.h>
94
#include <sys/sockio.h>
95
#include <sys/errno.h>
96
 
97
#include <net/if.h>
98
#include <net/route.h>
99
 
100
#include <netinet/in.h>
101
#include <netinet/in_var.h>
102
#include <netinet/in_systm.h>
103
#include <netinet/ip.h>
104
#include <netinet/in_pcb.h>
105
#include <netinet6/in6_var.h>
106
#include <netinet/ip6.h>
107
#include <netinet6/ip6_var.h>
108
#include <netinet6/in6_pcb.h>
109
#include <netinet6/nd6.h>
110
#include <netinet6/scope6_var.h> 
111
 
112
#ifdef MIP6
113
#include <netinet6/mip6.h>
114
#endif /* MIP6 */
115
 
116
#if defined(__NetBSD__)
117
extern struct ifnet loif[NLOOP];
118
#endif
119
 
120
#define ADDR_LABEL_NOTAPP (-1)
121
struct in6_addrpolicy defaultaddrpolicy;
122
 
123
int ip6_prefer_tempaddr = 0;
124
 
125
#ifdef NEW_STRUCT_ROUTE
126
static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
127
                             struct ip6_moptions *,
128
                             struct route *ro,
129
                             struct ifnet **));
130
#else
131
static int in6_selectif __P((struct sockaddr_in6 *, struct ip6_pktopts *,
132
                             struct ip6_moptions *,
133
                             struct route_in6 *ro,
134
                             struct ifnet **));
135
#endif
136
 
137
static struct in6_addrpolicy *lookup_addrsel_policy __P((struct sockaddr_in6 *));
138
 
139
static void init_policy_queue __P((void));
140
static int add_addrsel_policyent __P((struct in6_addrpolicy *));
141
static int delete_addrsel_policyent __P((struct in6_addrpolicy *));
142
static struct in6_addrpolicy *match_addrsel_policy __P((struct sockaddr_in6 *));
143
 
144
/*
145
 * Return an IPv6 address, which is the most appropriate for a given
146
 * destination and user specified options.
147
 * If necessary, this function lookups the routing table and returns
148
 * an entry to the caller for later use.
149
 */
150
#define REPLACE(r) do {\
151
        if ((r) < sizeof(ip6stat.ip6s_sources_rule) / \
152
                sizeof(ip6stat.ip6s_sources_rule[0])) /* check for safety */ \
153
                ip6stat.ip6s_sources_rule[(r)]++; \
154
        /* printf("in6_selectsrc: replace %s with %s by %d\n", ia_best ? ip6_sprintf(&ia_best->ia_addr.sin6_addr) : "none", ip6_sprintf(&ia->ia_addr.sin6_addr), (r)); */ \
155
        goto replace; \
156
} while(0)
157
#define NEXT(r) do {\
158
        if ((r) < sizeof(ip6stat.ip6s_sources_rule) / \
159
                sizeof(ip6stat.ip6s_sources_rule[0])) /* check for safety */ \
160
                ip6stat.ip6s_sources_rule[(r)]++; \
161
        /* printf("in6_selectsrc: keep %s against %s by %d\n", ia_best ? ip6_sprintf(&ia_best->ia_addr.sin6_addr) : "none", ip6_sprintf(&ia->ia_addr.sin6_addr), (r)); */ \
162
        goto next;              /* XXX: we can't use 'continue' here */ \
163
} while(0)
164
#define BREAK(r) do { \
165
        if ((r) < sizeof(ip6stat.ip6s_sources_rule) / \
166
                sizeof(ip6stat.ip6s_sources_rule[0])) /* check for safety */ \
167
                ip6stat.ip6s_sources_rule[(r)]++; \
168
        goto out;               /* XXX: we can't use 'break' here */ \
169
} while(0)
170
 
171
struct in6_addr *
172
in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
173
              struct ip6_moptions *mopts, struct route *ro,
174
              struct ifnet **ifpp, struct in6_addr *laddr,
175
              int *errorp)
176
{
177
        struct in6_addr *dst;
178
        struct ifnet *ifp = NULL;
179
        struct in6_ifaddr *ia = NULL, *ia_best = NULL;
180
        struct in6_pktinfo *pi = NULL;
181
        int dst_scope = -1, best_scope = -1, best_matchlen = -1;
182
        struct in6_addrpolicy *dst_policy = NULL, *best_policy = NULL;
183
#ifdef MIP6
184
        struct hif_softc *sc;
185
#ifdef MIP6_ALLOW_COA_FALLBACK
186
        struct mip6_bu *mbu_dst;
187
        u_int8_t coafallback = 0;
188
#endif
189
#endif
190
 
191
        dst = &dstsock->sin6_addr;
192
        *errorp = 0;
193
        if (ifpp)
194
                *ifpp = NULL;
195
 
196
        /*
197
         * If the source address is explicitly specified by the caller,
198
         * check if the requested source address is indeed a unicast address
199
         * assigned to the node, and can be used as the packet's source
200
         * address.  If everything is okay, use the address as source.
201
         */
202
        if (opts && (pi = opts->ip6po_pktinfo) &&
203
            !IN6_IS_ADDR_UNSPECIFIED(&pi->ipi6_addr)) {
204
                struct sockaddr_in6 srcsock;
205
                struct in6_ifaddr *ia6;
206
 
207
                /* get the outgoing interface */
208
                if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, &ifp))
209
                    != 0) {
210
                        return(NULL);
211
                }
212
 
213
                /*
214
                 * determine the appropriate zone id of the source based on
215
                 * the zone of the destination and the outgoing interface.
216
                 */
217
                bzero(&srcsock, sizeof(srcsock));
218
                srcsock.sin6_family = AF_INET6;
219
                srcsock.sin6_len = sizeof(srcsock);
220
                srcsock.sin6_addr = pi->ipi6_addr;
221
                if (ifp) {
222
                        int64_t zone;
223
 
224
                        zone = in6_addr2zoneid(ifp, &pi->ipi6_addr);
225
                        if (zone < 0) { /* XXX: this should not happen */
226
                                *errorp = EINVAL;
227
                                return(NULL);
228
                        }
229
                        srcsock.sin6_scope_id = zone;
230
                }
231
                if ((*errorp = in6_embedscope(&srcsock.sin6_addr, &srcsock))
232
                    != 0) {
233
                        return(NULL);
234
                }
235
#ifndef SCOPEDROUTING
236
                srcsock.sin6_scope_id = 0; /* XXX: ifa_ifwithaddr expects 0 */
237
#endif
238
                ia6 = (struct in6_ifaddr *)ifa_ifwithaddr((struct sockaddr *)(&srcsock));
239
                if (ia6 == NULL ||
240
                    (ia6->ia6_flags & (IN6_IFF_ANYCAST | IN6_IFF_NOTREADY))) {
241
                        *errorp = EADDRNOTAVAIL;
242
                        return(NULL);
243
                }
244
                pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
245
                if (*ifpp)
246
                        *ifpp = ifp;
247
                return(&pi->ipi6_addr);
248
        }
249
 
250
        /*
251
         * Otherwise, if the socket has already bound the source, just use it.
252
         */
253
        if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr))
254
                return(laddr);
255
 
256
        /*
257
         * If the address is not specified, choose the best one based on
258
         * the outgoing interface and the destination address.
259
         */
260
        /* get the outgoing interface */
261
        if ((*errorp = in6_selectif(dstsock, opts, mopts, ro, &ifp)) != 0)
262
                return(NULL);
263
 
264
#ifdef MIP6
265
#ifdef MIP6_ALLOW_COA_FALLBACK
266
        for (sc = TAILQ_FIRST(&hif_softc_list);
267
             sc;
268
             sc = TAILQ_NEXT(sc, hif_entry)) {
269
                mbu_dst = mip6_bu_list_find_withpaddr(&sc->hif_bu_list, dst);
270
                if (mbu_dst != NULL)
271
                        coafallback = mbu_dst->mbu_coafallback;
272
        }
273
#endif /* MIP6_ALLOW_COA_FALLBACK */
274
#endif /* MIP6 */
275
 
276
#ifdef DIAGNOSTIC
277
        if (ifp == NULL)        /* this should not happen */
278
                panic("in6_selectsrc: NULL ifp");
279
#endif
280
        for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
281
                int new_scope = -1, new_matchlen = -1;
282
                struct in6_addrpolicy *new_policy = NULL;
283
                int64_t srczone, dstzone;
284
                struct ifnet *ifp1 = ia->ia_ifp;
285
 
286
                /*
287
                 * We'll never take an address that breaks the scope zone
288
                 * of the destination.  We also skip an address if its zone
289
                 * does not contain the outgoing interface.
290
                 * XXX: we should probably use sin6_scope_id here.
291
                 */
292
                if ((dstzone = in6_addr2zoneid(ifp1, dst)) < 0 ||
293
                    dstzone != in6_addr2zoneid(ifp, dst)) {
294
                        continue;
295
                }
296
                if ((srczone = in6_addr2zoneid(ifp1, &ia->ia_addr.sin6_addr))
297
                    < 0 ||
298
                    srczone != in6_addr2zoneid(ifp, &ia->ia_addr.sin6_addr)) {
299
                        continue;
300
                }
301
 
302
                /* avoid unusable addresses */
303
                if ((ia->ia6_flags &
304
                     (IN6_IFF_NOTREADY | IN6_IFF_ANYCAST | IN6_IFF_DETACHED))) {
305
                                continue;
306
                }
307
                if (!ip6_use_deprecated && IFA6_IS_DEPRECATED(ia))
308
                        continue;
309
 
310
                /* Rule 1: Prefer same address */
311
                if (IN6_ARE_ADDR_EQUAL(dst, &ia->ia_addr.sin6_addr)) {
312
                        ia_best = ia;
313
                        BREAK(1); /* there should be no better candidate */
314
                }
315
 
316
                if (ia_best == NULL)
317
                        REPLACE(0);
318
 
319
                /* Rule 2: Prefer appropriate scope */
320
                if (dst_scope < 0)
321
                        dst_scope = in6_addrscope(dst);
322
                new_scope = in6_addrscope(&ia->ia_addr.sin6_addr);
323
                if (IN6_ARE_SCOPE_CMP(best_scope, new_scope) < 0) {
324
                        if (IN6_ARE_SCOPE_CMP(best_scope, dst_scope) < 0)
325
                                REPLACE(2);
326
                        NEXT(2);
327
                } else if (IN6_ARE_SCOPE_CMP(new_scope, best_scope) < 0) {
328
                        if (IN6_ARE_SCOPE_CMP(new_scope, dst_scope) < 0)
329
                                NEXT(2);
330
                        REPLACE(2);
331
                }
332
 
333
                /*
334
                 * Rule 3: Avoid deprecated addresses.  Note that the case of
335
                 * !ip6_use_deprecated is already rejected above.
336
                 */
337
                if (!IFA6_IS_DEPRECATED(ia_best) && IFA6_IS_DEPRECATED(ia))
338
                        NEXT(3);
339
                if (IFA6_IS_DEPRECATED(ia_best) && !IFA6_IS_DEPRECATED(ia))
340
                        REPLACE(3);
341
 
342
                /* Rule 4: Prefer home addresses */
343
                /*
344
                 * XXX: This is a TODO.  We should probably merge the MIP6
345
                 * case above.
346
                 */
347
#ifdef MIP6
348
                /*
349
                 * If SA is simultaneously a home address and care-of address
350
                 * and SB is not, then prefer SA. Similarly, if SB is
351
                 * simultaneously a home address and care-of address and SA is
352
                 * not, then prefer SB.
353
                 */
354
                {
355
                        struct mip6_bu *mbu_ia_best = NULL, *mbu_ia = NULL;
356
 
357
                        if (ia_best->ia6_flags & IN6_IFF_HOME) {
358
                                /*
359
                                 * find a binding update entry for ia_best.
360
                                 */
361
                                for (sc = TAILQ_FIRST(&hif_softc_list);
362
                                     sc;
363
                                     sc = TAILQ_NEXT(sc, hif_entry)) {
364
                                        mbu_ia_best = mip6_bu_list_find_home_registration(
365
                                                &sc->hif_bu_list,
366
                                                &ia->ia_addr.sin6_addr);
367
                                        if (mbu_ia_best)
368
                                                break;
369
                                }
370
                        }
371
                        if (ia->ia6_flags & IN6_IFF_HOME) {
372
                                /*
373
                                 * find a binding update entry for ia.
374
                                 */
375
                                for (sc = TAILQ_FIRST(&hif_softc_list);
376
                                     sc;
377
                                     sc = TAILQ_NEXT(sc, hif_entry)) {
378
                                        mbu_ia = mip6_bu_list_find_home_registration(
379
                                                &sc->hif_bu_list,
380
                                                &ia->ia_addr.sin6_addr);
381
                                        if (mbu_ia)
382
                                                break;
383
                                }
384
                        }
385
                        /*
386
                         * if the binding update entry for a certain address
387
                         * exists and its registration status is
388
                         * MIP6_BU_REG_STATE_NOTREG, the address is a home
389
                         * address and a care of addres simultaneously.
390
                         */
391
                        if ((mbu_ia_best &&
392
                             (mbu_ia_best->mbu_reg_state
393
                              == MIP6_BU_REG_STATE_NOTREG))
394
                            &&
395
                            !(mbu_ia &&
396
                              (mbu_ia->mbu_reg_state
397
                               == MIP6_BU_REG_STATE_NOTREG))) {
398
                                NEXT(4);
399
                        }
400
                        if (!(mbu_ia_best &&
401
                              (mbu_ia_best->mbu_reg_state
402
                               == MIP6_BU_REG_STATE_NOTREG))
403
                            &&
404
                            (mbu_ia &&
405
                             (mbu_ia->mbu_reg_state
406
                              == MIP6_BU_REG_STATE_NOTREG))) {
407
                                REPLACE(4);
408
                        }
409
                }
410
#ifdef MIP6_ALLOW_COA_FALLBACK
411
                if (coafallback) {
412
                        /*
413
                         * if the peer doesn't recognize a home
414
                         * address destination option, we will use a
415
                         * CoA as a source address instead of a home
416
                         * address we have registered before.  Though
417
                         * this behavior may arouse a mip6 beleiver's
418
                         * anger, is very useful in the current
419
                         * transition period that many hosts don't
420
                         * recognize a home address destination
421
                         * option...
422
                         */
423
                        if ((ia_best->ia6_flags & IN6_IFF_HOME) == 0 &&
424
                            (ia->ia6_flags & IN6_IFF_HOME) != 0) {
425
                                /* XXX will break stat! */
426
                                NEXT(0);
427
                        }
428
                        if ((ia_best->ia6_flags & IN6_IFF_HOME) != 0 &&
429
                            (ia->ia6_flags & IN6_IFF_HOME) == 0) {
430
                                /* XXX will break stat! */
431
                                REPLACE(0);
432
                        }
433
                } else
434
#endif
435
                {
436
                        /*
437
                         * If SA is just a home address and SB is just
438
                         * a care-of address, then prefer
439
                         * SA. Similarly, if SB is just a home address
440
                         * and SA is just a care-of address, then
441
                         * prefer SB.
442
                         */
443
                        if ((ia_best->ia6_flags & IN6_IFF_HOME) != 0 &&
444
                            (ia->ia6_flags & IN6_IFF_HOME) == 0) {
445
                                NEXT(4);
446
                        }
447
                        if ((ia_best->ia6_flags & IN6_IFF_HOME) == 0 &&
448
                            (ia->ia6_flags & IN6_IFF_HOME) != 0) {
449
                                REPLACE(4);
450
                        }
451
                }
452
#endif /* MIP6 */
453
 
454
                /* Rule 5: Prefer outgoing interface */
455
                if (ia_best->ia_ifp == ifp && ia->ia_ifp != ifp)
456
                        NEXT(5);
457
                if (ia_best->ia_ifp != ifp && ia->ia_ifp == ifp)
458
                        REPLACE(5);
459
 
460
                /*
461
                 * Rule 6: Prefer matching label
462
                 * Note that best_policy should be non-NULL here.
463
                 */
464
                if (dst_policy == NULL)
465
                        dst_policy = lookup_addrsel_policy(dstsock);
466
                if (dst_policy->label != ADDR_LABEL_NOTAPP) {
467
                        new_policy = lookup_addrsel_policy(&ia->ia_addr);
468
                        if (dst_policy->label == best_policy->label &&
469
                            dst_policy->label != new_policy->label)
470
                                NEXT(6);
471
                        if (dst_policy->label != best_policy->label &&
472
                            dst_policy->label == new_policy->label)
473
                                REPLACE(6);
474
                }
475
 
476
                /*
477
                 * Rule 7: Prefer public addresses.
478
                 * We allow users to reverse the logic by configuring
479
                 * a sysctl variable, so that privacy conscious users can
480
                 * always prefer temporary addresses.
481
                 */
482
                if (!(ia_best->ia6_flags & IN6_IFF_TEMPORARY) &&
483
                    (ia->ia6_flags & IN6_IFF_TEMPORARY)) {
484
                        if (ip6_prefer_tempaddr)
485
                                REPLACE(7);
486
                        else
487
                                NEXT(7);
488
                }
489
                if ((ia_best->ia6_flags & IN6_IFF_TEMPORARY) &&
490
                    !(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
491
                        if (ip6_prefer_tempaddr)
492
                                NEXT(7);
493
                        else
494
                                REPLACE(7);
495
                }
496
 
497
                /*
498
                 * Rule 8: prefer addresses on alive interfaces.
499
                 * This is a KAME specific rule.
500
                 */
501
                if ((ia_best->ia_ifp->if_flags & IFF_UP) &&
502
                    !(ia->ia_ifp->if_flags & IFF_UP))
503
                        NEXT(8);
504
                if (!(ia_best->ia_ifp->if_flags & IFF_UP) &&
505
                    (ia->ia_ifp->if_flags & IFF_UP))
506
                        REPLACE(8);
507
 
508
                /*
509
                 * Rule 9: prefer addresses on "preferred" interfaces.
510
                 * This is a KAME specific rule.
511
                 */
512
#define NDI_BEST (nd_ifinfo[ia_best->ia_ifp->if_index])
513
#define NDI_NEW  (nd_ifinfo[ia->ia_ifp->if_index])
514
                if ((NDI_BEST.flags & ND6_IFF_PREFER_SOURCE) &&
515
                    !(NDI_NEW.flags & ND6_IFF_PREFER_SOURCE))
516
                        NEXT(9);
517
                if (!(NDI_BEST.flags & ND6_IFF_PREFER_SOURCE) &&
518
                    (NDI_NEW.flags & ND6_IFF_PREFER_SOURCE))
519
                        REPLACE(9);
520
#undef NDI_BEST
521
#undef NDI_NEW
522
 
523
                /*
524
                 * Rule 14: Use longest matching prefix.
525
                 * Note: in the address selection draft, this rule is
526
                 * documented as "Rule 8".  However, since it is also
527
                 * documented that this rule can be overridden, we assign
528
                 * a large number so that it is easy to assign smaller numbers
529
                 * to more preferred rules.
530
                 */
531
                new_matchlen = in6_matchlen(&ia->ia_addr.sin6_addr, dst);
532
                if (best_matchlen < new_matchlen)
533
                        REPLACE(14);
534
                if (new_matchlen < best_matchlen)
535
                        NEXT(14);
536
 
537
                /* Rule 15 is reserved. */
538
 
539
                /*
540
                 * Last resort: just keep the current candidate.
541
                 * Or, do we need more rules?
542
                 */
543
                continue;
544
 
545
          replace:
546
                ia_best = ia;
547
                best_scope = (new_scope >= 0 ? new_scope :
548
                              in6_addrscope(&ia_best->ia_addr.sin6_addr));
549
                best_policy = (new_policy ? new_policy :
550
                               lookup_addrsel_policy(&ia_best->ia_addr));
551
                best_matchlen = (new_matchlen >= 0 ? new_matchlen :
552
                                 in6_matchlen(&ia_best->ia_addr.sin6_addr,
553
                                              dst));
554
 
555
          next:
556
                continue;
557
 
558
          out:
559
                break;
560
        }
561
 
562
        if ((ia = ia_best) == NULL) {
563
                *errorp = EADDRNOTAVAIL;
564
                return(NULL);
565
        }
566
 
567
        if (ifpp)
568
                *ifpp = ifp;
569
        return(&ia->ia_addr.sin6_addr);
570
}
571
#undef REPLACE
572
#undef BREAK
573
#undef NEXT
574
 
575
static int
576
in6_selectif(dstsock, opts, mopts, ro, retifp)
577
        struct sockaddr_in6 *dstsock;
578
        struct ip6_pktopts *opts;
579
        struct ip6_moptions *mopts;
580
#ifdef NEW_STRUCT_ROUTE
581
        struct route *ro;
582
#else
583
        struct route_in6 *ro;
584
#endif
585
        struct ifnet **retifp;
586
{
587
        int error, clone;
588
        struct rtentry *rt = NULL;
589
 
590
        clone = IN6_IS_ADDR_MULTICAST(&dstsock->sin6_addr) ? 0 : 1;
591
        if ((error = in6_selectroute(dstsock, opts, mopts, ro, retifp,
592
                                     &rt, clone)) != 0) {
593
                return(error);
594
        }
595
        /*
596
         * Adjust the "outgoing" interface.  If we're going to loop the packet
597
         * back to ourselves, the ifp would be the loopback interface.
598
         * However, we'd rather know the interface associated to the
599
         * destination address (which should probably be one of our own
600
         * addresses.)
601
         */
602
        if (rt && rt->rt_ifa && rt->rt_ifa->ifa_ifp)
603
                *retifp = rt->rt_ifa->ifa_ifp;
604
 
605
        return(0);
606
}
607
 
608
int
609
in6_selectroute(dstsock, opts, mopts, ro, retifp, retrt, clone)
610
        struct sockaddr_in6 *dstsock;
611
        struct ip6_pktopts *opts;
612
        struct ip6_moptions *mopts;
613
#ifdef NEW_STRUCT_ROUTE
614
        struct route *ro;
615
#else
616
        struct route_in6 *ro;
617
#endif
618
        struct ifnet **retifp;
619
        struct rtentry **retrt;
620
        int clone;              /* meaningful only for bsdi and freebsd. */
621
{
622
        int error = 0;
623
        struct ifnet *ifp = NULL;
624
        struct rtentry *rt = NULL;
625
        struct sockaddr_in6 *sin6_next;
626
        struct in6_pktinfo *pi = NULL;
627
        struct in6_addr *dst = &dstsock->sin6_addr;
628
 
629
        /* If the caller specify the outgoing interface explicitly, use it. */
630
        if (opts && (pi = opts->ip6po_pktinfo) != NULL && pi->ipi6_ifindex) {
631
                /* XXX boundary check is assumed to be already done. */
632
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
633
                ifp = ifnet_byindex(pi->ipi6_ifindex);
634
#else
635
                ifp = ifindex2ifnet[pi->ipi6_ifindex];
636
#endif
637
                if (ifp != NULL &&
638
                    (retrt == NULL || IN6_IS_ADDR_MULTICAST(dst))) {
639
                        /*
640
                         * we do not have to check nor get the route for
641
                         * multicast.
642
                         */
643
                        goto done;
644
                } else
645
                        goto getroute;
646
        }
647
 
648
        /*
649
         * If the destination address is a multicast address and the outgoing
650
         * interface for the address is specified by the caller, use it.
651
         */
652
        if (IN6_IS_ADDR_MULTICAST(dst) &&
653
            mopts != NULL && (ifp = mopts->im6o_multicast_ifp) != NULL) {
654
                goto done; /* we do not need a route for multicast. */
655
        }
656
 
657
  getroute:
658
        /*
659
         * If the next hop address for the packet is specified by the caller,
660
         * use it as the gateway.
661
         */
662
        if (opts && opts->ip6po_nexthop) {
663
#ifdef NEW_STRUCT_ROUTE
664
                struct route *ron;
665
#else
666
                struct route_in6 *ron;
667
#endif
668
 
669
                sin6_next = satosin6(opts->ip6po_nexthop);
670
 
671
                /* at this moment, we only support AF_INET6 next hops */
672
                if (sin6_next->sin6_family != AF_INET6) {
673
                        error = EAFNOSUPPORT; /* or should we proceed? */
674
                        goto done;
675
                }
676
 
677
                /*
678
                 * If the next hop is an IPv6 address, then the node identified
679
                 * by that address must be a neighbor of the sending host.
680
                 */
681
                ron = &opts->ip6po_nextroute;
682
                if ((ron->ro_rt &&
683
                     (ron->ro_rt->rt_flags & (RTF_UP | RTF_LLINFO)) !=
684
                     (RTF_UP | RTF_LLINFO)) ||
685
                    !SA6_ARE_ADDR_EQUAL(satosin6(&ron->ro_dst), sin6_next)) {
686
                        if (ron->ro_rt) {
687
                                RTFREE(ron->ro_rt);
688
                                ron->ro_rt = NULL;
689
                        }
690
                        *satosin6(&ron->ro_dst) = *sin6_next;
691
                }
692
                if (ron->ro_rt == NULL) {
693
                        rtalloc((struct route *)ron); /* multi path case? */
694
                        if (ron->ro_rt == NULL ||
695
                            !(ron->ro_rt->rt_flags & RTF_LLINFO)) {
696
                                if (ron->ro_rt) {
697
                                        RTFREE(ron->ro_rt);
698
                                        ron->ro_rt = NULL;
699
                                }
700
                                error = EHOSTUNREACH;
701
                                goto done;
702
                        }
703
                }
704
                rt = ron->ro_rt;
705
                ifp = rt->rt_ifp;
706
 
707
                /*
708
                 * When cloning is required, try to allocate a route to the
709
                 * destination so that the caller can store path MTU
710
                 * information.
711
                 */
712
                if (!clone)
713
                        goto done;
714
        }
715
 
716
        /*
717
         * Use a cached route if it exists and is valid, else try to allocate
718
         * a new one.
719
         */
720
        if (ro) {
721
                int newroute = 0;
722
 
723
                if (ro->ro_rt &&
724
                    (!(ro->ro_rt->rt_flags & RTF_UP) ||
725
                     !IN6_ARE_ADDR_EQUAL(&satosin6(&ro->ro_dst)->sin6_addr,
726
                                        dst))) {
727
                        RTFREE(ro->ro_rt);
728
                        ro->ro_rt = (struct rtentry *)NULL;
729
                }
730
                if (ro->ro_rt == (struct rtentry *)NULL) {
731
                        struct sockaddr_in6 *sa6;
732
 
733
                        /* No route yet, so try to acquire one */
734
                        newroute = 1;
735
                        bzero(&ro->ro_dst, sizeof(struct sockaddr_in6));
736
                        sa6 = (struct sockaddr_in6 *)&ro->ro_dst;
737
                        sa6->sin6_family = AF_INET6;
738
                        sa6->sin6_len = sizeof(struct sockaddr_in6);
739
                        sa6->sin6_addr = *dst;
740
#ifdef SCOPEDROUTING
741
                        sa6->sin6_scope_id = dstsock->sin6_scope_id;
742
#endif
743
                        if (clone) {
744
#ifdef __bsdi__
745
                                rtcalloc((struct route *)ro);
746
#else  /* !bsdi */
747
#ifdef RADIX_MPATH
748
                                rtalloc_mpath((struct route *)ro,
749
                                    ntohl(dstsock->sin6_addr.s6_addr32[3]));
750
#else
751
                                rtalloc((struct route *)ro);
752
#endif /* RADIX_MPATH */
753
#endif /* bsdi */
754
                        } else {
755
#ifdef __FreeBSD__
756
                                ro->ro_rt = rtalloc1(&((struct route *)ro)
757
                                                     ->ro_dst, (int)NULL, 0UL);
758
#else
759
#ifdef RADIX_MPATH
760
                                rtalloc_mpath((struct route *)ro,
761
                                    ntohl(dstsock->sin6_addr.s6_addr32[3]));
762
#else
763
                                ro->ro_rt = rtalloc1(&((struct route *)ro)
764
                                                     ->ro_dst, NULL);
765
#endif /* RADIX_MPATH */
766
#endif /* __FreeBSD__ */
767
                        }
768
                }
769
 
770
                /*
771
                 * do not care about the result if we have the nexthop
772
                 * explicitly specified.
773
                 */
774
                if (opts && opts->ip6po_nexthop)
775
                        goto done;
776
 
777
                if (ro->ro_rt) {
778
                        ifp = ro->ro_rt->rt_ifp;
779
 
780
                        if (ifp == NULL) { /* can this really happen? */
781
                                RTFREE(ro->ro_rt);
782
                                ro->ro_rt = NULL;
783
                        }
784
                }
785
                if (ro->ro_rt == NULL)
786
                        error = EHOSTUNREACH;
787
                rt = ro->ro_rt;
788
 
789
                /*
790
                 * Check if the outgoing interface conflicts with
791
                 * the interface specified by ipi6_ifindex (if specified).
792
                 * Note that loopback interface is always okay.
793
                 * (this may happen when we are sending a packet to one of
794
                 *  our own addresses.)
795
                 */
796
                if (opts && opts->ip6po_pktinfo
797
                    && opts->ip6po_pktinfo->ipi6_ifindex) {
798
                        if (!(ifp->if_flags & IFF_LOOPBACK) &&
799
                            ifp->if_index !=
800
                            opts->ip6po_pktinfo->ipi6_ifindex) {
801
                                error = EHOSTUNREACH;
802
                                goto done;
803
                        }
804
                }
805
        }
806
 
807
  done:
808
        if (error == EHOSTUNREACH)
809
                ip6stat.ip6s_noroute++;
810
 
811
        if (retifp != NULL)
812
                *retifp = ifp;
813
        if (retrt != NULL)
814
                *retrt = rt;    /* rt may be NULL */
815
 
816
        return(error);
817
}
818
 
819
/*
820
 * Default hop limit selection. The precedence is as follows:
821
 * 1. Hoplimit value specified via ioctl.
822
 * 2. (If the outgoing interface is detected) the current
823
 *     hop limit of the interface specified by router advertisement.
824
 * 3. The system default hoplimit.
825
*/
826
#ifdef HAVE_NRL_INPCB
827
#define in6pcb          inpcb
828
#define in6p_hops       inp_hops        
829
#endif
830
int
831
in6_selecthlim(struct in6pcb *in6p, struct ifnet *ifp)
832
{
833
        if (in6p && in6p->in6p_hops >= 0)
834
                return(in6p->in6p_hops);
835
        else if (ifp)
836
                return(nd_ifinfo[ifp->if_index].chlim);
837
        else
838
                return(ip6_defhlim);
839
}
840
#ifdef HAVE_NRL_INPCB
841
#undef in6pcb
842
#undef in6p_hops
843
#endif
844
 
845
#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3) && !defined(__OpenBSD__)
846
/*
847
 * Find an empty port and set it to the specified PCB.
848
 */
849
#ifdef HAVE_NRL_INPCB   /* XXX: I really hate such ugly macros...(jinmei) */
850
#define in6pcb          inpcb
851
#define in6p_socket     inp_socket
852
#define in6p_lport      inp_lport
853
#define in6p_head       inp_head
854
#define in6p_flags      inp_flags
855
#define IN6PLOOKUP_WILDCARD INPLOOKUP_WILDCARD
856
#endif
857
int
858
in6_pcbsetport(struct in6_addr *laddr, struct in6pcb *in6p)
859
{
860
        struct socket *so = in6p->in6p_socket;
861
        struct in6pcb *head = in6p->in6p_head;
862
        u_int16_t last_port, lport = 0;
863
        int wild = 0;
864
        void *t;
865
        u_int16_t min, max;
866
 
867
        /* XXX: this is redundant when called from in6_pcbbind */
868
        if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
869
           ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
870
            (so->so_options & SO_ACCEPTCONN) == 0))
871
                wild = IN6PLOOKUP_WILDCARD;
872
 
873
        if (in6p->in6p_flags & IN6P_LOWPORT) {
874
                min = ip6_lowportmin;
875
                max = ip6_lowportmax;
876
        } else {
877
                min = ip6_anonportmin;
878
                max = ip6_anonportmax;
879
        }
880
 
881
        /* value out of range */
882
        if (head->in6p_lport < min)
883
                head->in6p_lport = min;
884
        else if (head->in6p_lport > max)
885
                head->in6p_lport = min;
886
        last_port = head->in6p_lport;
887
        goto startover; /*to randomize*/
888
        for (;;) {
889
                lport = htons(head->in6p_lport);
890
                if (IN6_IS_ADDR_V4MAPPED(laddr)) {
891
#ifdef HAVE_NRL_INPCB
892
#ifdef INPLOOKUP_WILDCARD6
893
                        wild &= ~INPLOOKUP_WILDCARD6;
894
#endif
895
#endif
896
#if 0
897
                        t = in_pcblookup_bind(&tcbtable,
898
                                              (struct in_addr *)&in6p->in6p_laddr.s6_addr32[3],
899
                                              lport);
900
#else
901
                        t = NULL;
902
#endif
903
                } else {
904
#ifdef HAVE_NRL_INPCB
905
#ifdef INPLOOKUP_WILDCARD4
906
                        wild &= ~INPLOOKUP_WILDCARD4;
907
#endif
908
                        /* XXX: ugly cast... */
909
                        t = in_pcblookup(head, (struct in_addr *)&zeroin6_addr,
910
                                         0, (struct in_addr *)laddr,
911
                                         lport, wild | INPLOOKUP_IPV6);
912
#else
913
                        t = in6_pcblookup(head, &zeroin6_addr, 0, laddr,
914
                                          lport, wild);
915
#endif
916
                }
917
                if (t == 0)
918
                        break;
919
          startover:
920
                if (head->in6p_lport >= max)
921
                        head->in6p_lport = min;
922
                else
923
                        head->in6p_lport++;
924
                if (head->in6p_lport == last_port)
925
                        return (EADDRINUSE);
926
        }
927
 
928
        in6p->in6p_lport = lport;
929
        return(0);               /* success */
930
}
931
#ifdef HAVE_NRL_INPCB
932
#undef in6pcb
933
#undef in6p_socket
934
#undef in6p_lport
935
#undef in6p_head
936
#undef in6p_flags
937
#undef IN6PLOOKUP_WILDCARD
938
#endif
939
#endif /* !FreeBSD3 && !OpenBSD*/
940
 
941
#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
942
/*
943
 * XXX: this is borrowed from in6_pcbbind(). If possible, we should
944
 * share this function by all *bsd*...
945
 */
946
int
947
in6_pcbsetport(struct in6_addr *laddr, struct inpcb *inp, struct proc *p)
948
{
949
        struct socket *so = inp->inp_socket;
950
        u_int16_t lport = 0, first, last, *lastport;
951
        int count, wild = 0;
952
        struct inpcbinfo *pcbinfo = inp->inp_pcbinfo;
953
 
954
        /* XXX: this is redundant when called from in6_pcbbind */
955
        if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
956
                wild = INPLOOKUP_WILDCARD;
957
 
958
        inp->inp_flags |= INP_ANONPORT;
959
 
960
        if (inp->inp_flags & INP_HIGHPORT) {
961
                first = ipport_hifirstauto;     /* sysctl */
962
                last  = ipport_hilastauto;
963
                lastport = &pcbinfo->lasthi;
964
        } else if (inp->inp_flags & INP_LOWPORT) {
965
                first = ipport_lowfirstauto;    /* 1023 */
966
                last  = ipport_lowlastauto;     /* 600 */
967
                lastport = &pcbinfo->lastlow;
968
        } else {
969
                first = ipport_firstauto;       /* sysctl */
970
                last  = ipport_lastauto;
971
                lastport = &pcbinfo->lastport;
972
        }
973
        /*
974
         * Simple check to ensure all ports are not used up causing
975
         * a deadlock here.
976
         *
977
         * We split the two cases (up and down) so that the direction
978
         * is not being tested on each round of the loop.
979
         */
980
        if (first > last) {
981
                /*
982
                 * counting down
983
                 */
984
                count = first - last;
985
 
986
                do {
987
                        if (count-- < 0) {       /* completely used? */
988
                                /*
989
                                 * Undo any address bind that may have
990
                                 * occurred above.
991
                                 */
992
                                inp->in6p_laddr = in6addr_any;
993
                                return (EAGAIN);
994
                        }
995
                        --*lastport;
996
                        if (*lastport > first || *lastport < last)
997
                                *lastport = first;
998
                        lport = htons(*lastport);
999
                } while (in6_pcblookup_local(pcbinfo,
1000
                                             &inp->in6p_laddr, lport, wild));
1001
        } else {
1002
                /*
1003
                         * counting up
1004
                         */
1005
                count = last - first;
1006
 
1007
                do {
1008
                        if (count-- < 0) {       /* completely used? */
1009
                                /*
1010
                                 * Undo any address bind that may have
1011
                                 * occurred above.
1012
                                 */
1013
                                inp->in6p_laddr = in6addr_any;
1014
                                return (EAGAIN);
1015
                        }
1016
                        ++*lastport;
1017
                        if (*lastport < first || *lastport > last)
1018
                                *lastport = first;
1019
                        lport = htons(*lastport);
1020
                } while (in6_pcblookup_local(pcbinfo,
1021
                                             &inp->in6p_laddr, lport, wild));
1022
        }
1023
 
1024
        inp->inp_lport = lport;
1025
        if (in_pcbinshash(inp) != 0) {
1026
                inp->in6p_laddr = in6addr_any;
1027
                inp->inp_lport = 0;
1028
                return (EAGAIN);
1029
        }
1030
 
1031
        return(0);
1032
}
1033
#endif
1034
 
1035
/*
1036
 * Generate kernel-internal form (scopeid embedded into s6_addr16[1]).
1037
 * If the address scope of is interface-local or link-local, embed the
1038
 * interface index in the address.
1039
 *
1040
 * This function should be nuked in the future, when we get rid of embedded
1041
 * scopeid thing.
1042
 */
1043
int
1044
in6_embedscope(in6, sin6)
1045
        struct in6_addr *in6;
1046
        const struct sockaddr_in6 *sin6;
1047
{
1048
#ifdef SCOPEDROUTING
1049
        /*
1050
         * XXX: the SCOPEDROUTING code path is NOT expected to work at this
1051
         * moment (20011112).  We added this just in case.
1052
         */
1053
        return(0);               /* do nothing */
1054
#else
1055
        struct ifnet *ifp;
1056
        u_int32_t zoneid = sin6->sin6_scope_id;
1057
 
1058
        *in6 = sin6->sin6_addr;
1059
 
1060
        /*
1061
         * don't try to read sin6->sin6_addr beyond here, since the caller may
1062
         * ask us to overwrite existing sockaddr_in6
1063
         */
1064
 
1065
        if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
1066
                /* KAME assumption: link id == interface id */
1067
                if (zoneid) {
1068
                        if (if_index < zoneid)
1069
                                return(ENXIO);  /* XXX EINVAL? */
1070
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
1071
                        ifp = ifnet_byindex(zoneid);
1072
#else
1073
                        ifp = ifindex2ifnet[zoneid];
1074
#endif
1075
                        if (ifp == NULL) /* XXX: this can happen for some OS */
1076
                                return(ENXIO);
1077
 
1078
                        /* XXX assignment to 16bit from 32bit variable */
1079
                        in6->s6_addr16[1] = htons(zoneid & 0xffff);
1080
                }
1081
        }
1082
 
1083
        return 0;
1084
#endif
1085
}
1086
 
1087
/*
1088
 * generate standard sockaddr_in6 from embedded form.
1089
 * touches sin6_addr and sin6_scope_id only.
1090
 *
1091
 * this function should be nuked in the future, when we get rid of
1092
 * embedded scopeid thing.
1093
 */
1094
int
1095
in6_recoverscope(sin6, in6, ifp)
1096
        struct sockaddr_in6 *sin6;
1097
        const struct in6_addr *in6;
1098
        struct ifnet *ifp;
1099
{
1100
        u_int32_t zoneid;
1101
 
1102
        sin6->sin6_addr = *in6;
1103
 
1104
        /*
1105
         * don't try to read *in6 beyond here, since the caller may
1106
         * ask us to overwrite existing sockaddr_in6
1107
         */
1108
 
1109
        sin6->sin6_scope_id = 0;
1110
        if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
1111
                /*
1112
                 * KAME assumption: link id == interface id
1113
                 */
1114
                zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
1115
                if (zoneid) {
1116
                        /* sanity check */
1117
                        if (zoneid < 0 || if_index < zoneid)
1118
                                return ENXIO;
1119
                        if (ifp && ifp->if_index != zoneid)
1120
                                return ENXIO;
1121
                        sin6->sin6_addr.s6_addr16[1] = 0;
1122
                        sin6->sin6_scope_id = zoneid;
1123
                }
1124
        }
1125
 
1126
        return 0;
1127
}
1128
 
1129
/*
1130
 * just clear the embedded scope identifer.
1131
 * XXX: currently used for bsdi4 only as a supplement function.
1132
 */
1133
void
1134
in6_clearscope(addr)
1135
        struct in6_addr *addr;
1136
{
1137
        if (IN6_IS_SCOPE_LINKLOCAL(addr) || IN6_IS_ADDR_MC_INTFACELOCAL(addr))
1138
                addr->s6_addr16[1] = 0;
1139
}
1140
 
1141
void
1142
addrsel_policy_init()
1143
{
1144
        init_policy_queue();
1145
 
1146
        /* initialize the "last resort" policy */
1147
        bzero(&defaultaddrpolicy, sizeof(defaultaddrpolicy));
1148
        defaultaddrpolicy.label = ADDR_LABEL_NOTAPP;
1149
}
1150
 
1151
static struct in6_addrpolicy *
1152
lookup_addrsel_policy(key)
1153
        struct sockaddr_in6 *key;
1154
{
1155
        struct in6_addrpolicy *match = NULL;
1156
 
1157
        match = match_addrsel_policy(key);
1158
 
1159
        if (match == NULL)
1160
                match = &defaultaddrpolicy;
1161
        else
1162
                match->use++;
1163
 
1164
        return(match);
1165
}
1166
 
1167
 
1168
int
1169
in6_src_ioctl(cmd, data)
1170
        u_long cmd;
1171
        caddr_t data;
1172
{
1173
        int i;
1174
        struct in6_addrpolicy ent0;
1175
 
1176
        if (cmd != SIOCAADDRCTL_POLICY && cmd != SIOCDADDRCTL_POLICY)
1177
                return(EOPNOTSUPP); /* check for safety */
1178
 
1179
        ent0 = *(struct in6_addrpolicy *)data;
1180
 
1181
        if (ent0.label == ADDR_LABEL_NOTAPP)
1182
                return(EINVAL);
1183
        /* check if the prefix mask is consecutive. */
1184
        if (in6_mask2len(&ent0.addrmask.sin6_addr, NULL) < 0)
1185
                return(EINVAL);
1186
        /* clear trailing garbages (if any) of the prefix address. */
1187
        for (i = 0; i < 4; i++) {
1188
                ent0.addr.sin6_addr.s6_addr32[i] &=
1189
                        ent0.addrmask.sin6_addr.s6_addr32[i];
1190
        }
1191
        ent0.use = 0;
1192
 
1193
        switch (cmd) {
1194
        case SIOCAADDRCTL_POLICY:
1195
                return(add_addrsel_policyent(&ent0));
1196
        case SIOCDADDRCTL_POLICY:
1197
                return(delete_addrsel_policyent(&ent0));
1198
        }
1199
 
1200
        return(0);               /* XXX: compromise compilers */
1201
}
1202
 
1203
/*
1204
 * The followings are implementation of the policy table using a
1205
 * simple tail queue.
1206
 * XXX such details should be hidden.
1207
 * XXX implementation using binary tree should be more efficient.
1208
 */
1209
struct addrsel_policyent {
1210
        TAILQ_ENTRY(addrsel_policyent) ape_entry;
1211
        struct in6_addrpolicy ape_policy;
1212
};
1213
 
1214
TAILQ_HEAD(addrsel_policyhead, addrsel_policyent);
1215
 
1216
struct addrsel_policyhead addrsel_policytab;
1217
 
1218
static void
1219
init_policy_queue()
1220
{
1221
        TAILQ_INIT(&addrsel_policytab);
1222
}
1223
 
1224
static int
1225
add_addrsel_policyent(newpolicy)
1226
        struct in6_addrpolicy *newpolicy;
1227
{
1228
        struct addrsel_policyent *new, *pol;
1229
 
1230
        /* duplication check */
1231
        for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
1232
             pol = TAILQ_NEXT(pol, ape_entry)) {
1233
                if (SA6_ARE_ADDR_EQUAL(&newpolicy->addr,
1234
                                       &pol->ape_policy.addr) &&
1235
                    SA6_ARE_ADDR_EQUAL(&newpolicy->addrmask,
1236
                                       &pol->ape_policy.addrmask)) {
1237
                        return(EEXIST); /* or override it? */
1238
                }
1239
        }
1240
 
1241
        MALLOC(new, struct addrsel_policyent *, sizeof(*new), M_IFADDR,
1242
               M_WAITOK);
1243
        bzero(new, sizeof(*new));
1244
 
1245
        /* XXX: should validate entry */
1246
        new->ape_policy = *newpolicy;
1247
 
1248
        TAILQ_INSERT_TAIL(&addrsel_policytab, new, ape_entry);
1249
 
1250
        return(0);
1251
}
1252
 
1253
static int
1254
delete_addrsel_policyent(key)
1255
        struct in6_addrpolicy *key;
1256
{
1257
        struct addrsel_policyent *pol;
1258
 
1259
        /* search for the entry in the table */
1260
        for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
1261
             pol = TAILQ_NEXT(pol, ape_entry)) {
1262
                if (SA6_ARE_ADDR_EQUAL(&key->addr, &pol->ape_policy.addr) &&
1263
                    SA6_ARE_ADDR_EQUAL(&key->addrmask,
1264
                                       &pol->ape_policy.addrmask)) {
1265
                        break;
1266
                }
1267
        }
1268
        if (pol == NULL)
1269
                return(ESRCH);
1270
 
1271
        TAILQ_REMOVE(&addrsel_policytab, pol, ape_entry);
1272
 
1273
        return(0);
1274
}
1275
 
1276
void
1277
_show_addrsel_policy(void)
1278
{
1279
        struct addrsel_policyent *pol;
1280
 
1281
        log(LOG_ADDR, "IPv6 address policy table\n");
1282
        for (pol = TAILQ_FIRST(&addrsel_policytab); pol;
1283
             pol = TAILQ_NEXT(pol, ape_entry)) {
1284
            log(LOG_ADDR, "Addr: \n");
1285
            log_dump(LOG_ADDR, &pol->ape_policy.addr, 16);
1286
            log(LOG_ADDR, "Mask:\n");
1287
            log_dump(LOG_ADDR, &pol->ape_policy.addrmask, 16);
1288
        }
1289
}
1290
 
1291
static struct in6_addrpolicy *
1292
match_addrsel_policy(key)
1293
        struct sockaddr_in6 *key;
1294
{
1295
        struct addrsel_policyent *pent;
1296
        struct in6_addrpolicy *bestpol = NULL, *pol;
1297
        int matchlen, bestmatchlen = -1;
1298
        u_char *mp, *ep, *k, *p, m;
1299
 
1300
        for (pent = TAILQ_FIRST(&addrsel_policytab); pent;
1301
             pent = TAILQ_NEXT(pent, ape_entry)) {
1302
                matchlen = 0;
1303
 
1304
                pol = &pent->ape_policy;
1305
                mp = (u_char *)&pol->addrmask.sin6_addr;
1306
                ep = mp + 16;   /* XXX: scope field? */
1307
                k = (u_char *)&key->sin6_addr;
1308
                p = (u_char *)&pol->addr.sin6_addr;
1309
                for (; mp < ep && *mp; mp++, k++, p++) {
1310
                        m = *mp;
1311
                        if ((*k & m) != *p)
1312
                                goto next; /* not match */
1313
                        if (m == 0xff) /* short cut for a typical case */
1314
                                matchlen += 8;
1315
                        else {
1316
                                while(m >= 0x80) {
1317
                                        matchlen++;
1318
                                        m <<= 1;
1319
                                }
1320
                        }
1321
                }
1322
 
1323
                /* matched.  check if this is better than the current best. */
1324
                if (bestpol == NULL ||
1325
                    matchlen > bestmatchlen) {
1326
                        bestpol = pol;
1327
                        bestmatchlen = matchlen;
1328
                }
1329
 
1330
          next:
1331
                continue;
1332
        }
1333
 
1334
        return(bestpol);
1335
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.