Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netinet6/scope6.c
102424 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (C) 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
* $KAME: scope6.c,v 1.10 2000/07/24 13:29:31 itojun Exp $
32
*/
33
34
#include <sys/param.h>
35
#include <sys/malloc.h>
36
#include <sys/mbuf.h>
37
#include <sys/socket.h>
38
#include <sys/sockio.h>
39
#include <sys/systm.h>
40
#include <sys/queue.h>
41
#include <sys/sysctl.h>
42
#include <sys/syslog.h>
43
44
#include <net/if.h>
45
#include <net/if_var.h>
46
#include <net/if_private.h>
47
#include <net/vnet.h>
48
49
#include <netinet/in.h>
50
51
#include <netinet/ip6.h>
52
#include <netinet6/in6_var.h>
53
#include <netinet6/ip6_var.h>
54
#include <netinet6/scope6_var.h>
55
56
#ifdef ENABLE_DEFAULT_SCOPE
57
VNET_DEFINE(int, ip6_use_defzone) = 1;
58
#else
59
VNET_DEFINE(int, ip6_use_defzone) = 0;
60
#endif
61
SYSCTL_DECL(_net_inet6_ip6);
62
63
/*
64
* The scope6_lock protects the global sid default stored in
65
* sid_default below.
66
*/
67
static struct mtx scope6_lock;
68
#define SCOPE6_LOCK_INIT() mtx_init(&scope6_lock, "scope6_lock", NULL, MTX_DEF)
69
#define SCOPE6_LOCK() mtx_lock(&scope6_lock)
70
#define SCOPE6_UNLOCK() mtx_unlock(&scope6_lock)
71
#define SCOPE6_LOCK_ASSERT() mtx_assert(&scope6_lock, MA_OWNED)
72
73
VNET_DEFINE_STATIC(struct scope6_id, sid_default);
74
#define V_sid_default VNET(sid_default)
75
76
#define SID(ifp) (&(ifp)->if_inet6->scope6_id)
77
78
static int scope6_get(struct ifnet *, struct scope6_id *);
79
static int scope6_set(struct ifnet *, struct scope6_id *);
80
static int scope6_get_default(struct scope6_id *);
81
82
void
83
scope6_init(void)
84
{
85
86
bzero(&V_sid_default, sizeof(V_sid_default));
87
88
if (!IS_DEFAULT_VNET(curvnet))
89
return;
90
91
SCOPE6_LOCK_INIT();
92
}
93
94
void
95
scope6_ifattach(struct ifnet *ifp)
96
{
97
struct scope6_id *sid = &ifp->if_inet6->scope6_id;
98
99
/*
100
* XXX: IPV6_ADDR_SCOPE_xxx macros are not standard.
101
* Should we rather hardcode here?
102
*/
103
bzero(sid, sizeof(*sid));
104
sid->s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = ifp->if_index;
105
sid->s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = ifp->if_index;
106
}
107
108
int
109
scope6_ioctl(u_long cmd, caddr_t data, struct ifnet *ifp)
110
{
111
struct in6_ifreq *ifr;
112
113
if (ifp->if_inet6 == NULL)
114
return (EPFNOSUPPORT);
115
116
ifr = (struct in6_ifreq *)data;
117
switch (cmd) {
118
case SIOCSSCOPE6:
119
return (scope6_set(ifp,
120
(struct scope6_id *)ifr->ifr_ifru.ifru_scope_id));
121
case SIOCGSCOPE6:
122
return (scope6_get(ifp,
123
(struct scope6_id *)ifr->ifr_ifru.ifru_scope_id));
124
case SIOCGSCOPE6DEF:
125
return (scope6_get_default(
126
(struct scope6_id *)ifr->ifr_ifru.ifru_scope_id));
127
default:
128
return (EOPNOTSUPP);
129
}
130
}
131
132
/*
133
* XXXGL: The use of IF_ADDR_WLOCK (previously it was IF_AFDATA_LOCK) in this
134
* function is quite strange.
135
*/
136
static int
137
scope6_set(struct ifnet *ifp, struct scope6_id *idlist)
138
{
139
int i;
140
int error = 0;
141
struct scope6_id *sid = NULL;
142
143
IF_ADDR_WLOCK(ifp);
144
sid = SID(ifp);
145
146
if (!sid) { /* paranoid? */
147
IF_ADDR_WUNLOCK(ifp);
148
return (EINVAL);
149
}
150
151
/*
152
* XXX: We need more consistency checks of the relationship among
153
* scopes (e.g. an organization should be larger than a site).
154
*/
155
156
/*
157
* TODO(XXX): after setting, we should reflect the changes to
158
* interface addresses, routing table entries, PCB entries...
159
*/
160
161
for (i = 0; i < 16; i++) {
162
if (idlist->s6id_list[i] &&
163
idlist->s6id_list[i] != sid->s6id_list[i]) {
164
/*
165
* An interface zone ID must be the corresponding
166
* interface index by definition.
167
*/
168
if (i == IPV6_ADDR_SCOPE_INTFACELOCAL &&
169
idlist->s6id_list[i] != ifp->if_index) {
170
IF_ADDR_WUNLOCK(ifp);
171
return (EINVAL);
172
}
173
174
if (i == IPV6_ADDR_SCOPE_LINKLOCAL) {
175
struct epoch_tracker et;
176
177
NET_EPOCH_ENTER(et);
178
if (!ifnet_byindex(idlist->s6id_list[i])) {
179
/*
180
* XXX: theoretically, there should be
181
* no relationship between link IDs and
182
* interface IDs, but we check the
183
* consistency for safety in later use.
184
*/
185
NET_EPOCH_EXIT(et);
186
IF_ADDR_WUNLOCK(ifp);
187
return (EINVAL);
188
}
189
NET_EPOCH_EXIT(et);
190
}
191
192
/*
193
* XXX: we must need lots of work in this case,
194
* but we simply set the new value in this initial
195
* implementation.
196
*/
197
sid->s6id_list[i] = idlist->s6id_list[i];
198
}
199
}
200
IF_ADDR_WUNLOCK(ifp);
201
202
return (error);
203
}
204
205
static int
206
scope6_get(struct ifnet *ifp, struct scope6_id *idlist)
207
{
208
struct epoch_tracker et;
209
struct scope6_id *sid;
210
211
/* We only need to lock the interface's afdata for SID() to work. */
212
NET_EPOCH_ENTER(et);
213
sid = SID(ifp);
214
if (sid == NULL) { /* paranoid? */
215
NET_EPOCH_EXIT(et);
216
return (EINVAL);
217
}
218
219
*idlist = *sid;
220
221
NET_EPOCH_EXIT(et);
222
return (0);
223
}
224
225
/*
226
* Get a scope of the address. Node-local, link-local, site-local or global.
227
*/
228
int
229
in6_addrscope(const struct in6_addr *addr)
230
{
231
232
if (IN6_IS_ADDR_MULTICAST(addr)) {
233
/*
234
* Addresses with reserved value F must be treated as
235
* global multicast addresses.
236
*/
237
if (IPV6_ADDR_MC_SCOPE(addr) == 0x0f)
238
return (IPV6_ADDR_SCOPE_GLOBAL);
239
return (IPV6_ADDR_MC_SCOPE(addr));
240
}
241
if (IN6_IS_ADDR_LINKLOCAL(addr) ||
242
IN6_IS_ADDR_LOOPBACK(addr))
243
return (IPV6_ADDR_SCOPE_LINKLOCAL);
244
if (IN6_IS_ADDR_SITELOCAL(addr))
245
return (IPV6_ADDR_SCOPE_SITELOCAL);
246
return (IPV6_ADDR_SCOPE_GLOBAL);
247
}
248
249
/*
250
* ifp - note that this might be NULL
251
*/
252
253
void
254
scope6_setdefault(struct ifnet *ifp)
255
{
256
257
/*
258
* Currently, this function just sets the default "interfaces"
259
* and "links" according to the given interface.
260
* We might eventually have to separate the notion of "link" from
261
* "interface" and provide a user interface to set the default.
262
*/
263
SCOPE6_LOCK();
264
if (ifp) {
265
V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] =
266
ifp->if_index;
267
V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] =
268
ifp->if_index;
269
} else {
270
V_sid_default.s6id_list[IPV6_ADDR_SCOPE_INTFACELOCAL] = 0;
271
V_sid_default.s6id_list[IPV6_ADDR_SCOPE_LINKLOCAL] = 0;
272
}
273
SCOPE6_UNLOCK();
274
}
275
276
static int
277
scope6_get_default(struct scope6_id *idlist)
278
{
279
280
SCOPE6_LOCK();
281
*idlist = V_sid_default;
282
SCOPE6_UNLOCK();
283
284
return (0);
285
}
286
287
u_int32_t
288
scope6_addr2default(struct in6_addr *addr)
289
{
290
u_int32_t id;
291
292
/*
293
* special case: The loopback address should be considered as
294
* link-local, but there's no ambiguity in the syntax.
295
*/
296
if (IN6_IS_ADDR_LOOPBACK(addr))
297
return (0);
298
299
/*
300
* XXX: 32-bit read is atomic on all our platforms, is it OK
301
* not to lock here?
302
*/
303
SCOPE6_LOCK();
304
id = V_sid_default.s6id_list[in6_addrscope(addr)];
305
SCOPE6_UNLOCK();
306
return (id);
307
}
308
309
/*
310
* Validate the specified scope zone ID in the sin6_scope_id field. If the ID
311
* is unspecified (=0), needs to be specified, and the default zone ID can be
312
* used, the default value will be used.
313
* This routine then generates the kernel-internal form: if the address scope
314
* of is interface-local or link-local, embed the interface index in the
315
* address.
316
*/
317
int
318
sa6_embedscope(struct sockaddr_in6 *sin6, int defaultok)
319
{
320
u_int32_t zoneid;
321
322
if ((zoneid = sin6->sin6_scope_id) == 0 && defaultok)
323
zoneid = scope6_addr2default(&sin6->sin6_addr);
324
325
if (zoneid != 0 &&
326
(IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
327
IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))) {
328
struct epoch_tracker et;
329
330
/*
331
* At this moment, we only check interface-local and
332
* link-local scope IDs, and use interface indices as the
333
* zone IDs assuming a one-to-one mapping between interfaces
334
* and links.
335
*/
336
NET_EPOCH_ENTER(et);
337
if (ifnet_byindex(zoneid) == NULL) {
338
NET_EPOCH_EXIT(et);
339
return (ENXIO);
340
}
341
NET_EPOCH_EXIT(et);
342
343
/* XXX assignment to 16bit from 32bit variable */
344
sin6->sin6_addr.s6_addr16[1] = htons(zoneid & 0xffff);
345
sin6->sin6_scope_id = 0;
346
}
347
348
return 0;
349
}
350
351
/*
352
* generate standard sockaddr_in6 from embedded form.
353
*/
354
int
355
sa6_recoverscope(struct sockaddr_in6 *sin6)
356
{
357
char ip6buf[INET6_ADDRSTRLEN];
358
u_int32_t zoneid;
359
360
if (IN6_IS_SCOPE_LINKLOCAL(&sin6->sin6_addr) ||
361
IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr)) {
362
/*
363
* KAME assumption: link id == interface id
364
*/
365
zoneid = ntohs(sin6->sin6_addr.s6_addr16[1]);
366
if (zoneid) {
367
struct epoch_tracker et;
368
369
NET_EPOCH_ENTER(et);
370
/* sanity check */
371
if (!ifnet_byindex(zoneid)) {
372
NET_EPOCH_EXIT(et);
373
return (ENXIO);
374
}
375
NET_EPOCH_EXIT(et);
376
if (sin6->sin6_scope_id != 0 &&
377
zoneid != sin6->sin6_scope_id) {
378
log(LOG_NOTICE,
379
"%s: embedded scope mismatch: %s%%%d. "
380
"sin6_scope_id was overridden\n", __func__,
381
ip6_sprintf(ip6buf, &sin6->sin6_addr),
382
sin6->sin6_scope_id);
383
}
384
sin6->sin6_addr.s6_addr16[1] = 0;
385
sin6->sin6_scope_id = zoneid;
386
}
387
}
388
389
return 0;
390
}
391
392
/*
393
* Determine the appropriate scope zone ID for in6 and ifp. If ret_id is
394
* non NULL, it is set to the zone ID. If the zone ID needs to be embedded
395
* in the in6_addr structure, in6 will be modified.
396
*
397
* ret_id - unnecessary?
398
*/
399
int
400
in6_setscope(struct in6_addr *in6, struct ifnet *ifp, u_int32_t *ret_id)
401
{
402
int scope;
403
u_int32_t zoneid = 0;
404
struct scope6_id *sid;
405
406
/*
407
* special case: the loopback address can only belong to a loopback
408
* interface.
409
*/
410
if (IN6_IS_ADDR_LOOPBACK(in6)) {
411
if (!(ifp->if_flags & IFF_LOOPBACK))
412
return (EINVAL);
413
} else {
414
scope = in6_addrscope(in6);
415
if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL ||
416
scope == IPV6_ADDR_SCOPE_LINKLOCAL) {
417
/*
418
* Currently we use interface indices as the
419
* zone IDs for interface-local and link-local
420
* scopes.
421
*/
422
zoneid = ifp->if_index;
423
in6->s6_addr16[1] = htons(zoneid & 0xffff); /* XXX */
424
} else if (scope != IPV6_ADDR_SCOPE_GLOBAL) {
425
struct epoch_tracker et;
426
427
NET_EPOCH_ENTER(et);
428
/* XXXGL */
429
if (ifp->if_inet6 == NULL) {
430
NET_EPOCH_EXIT(et);
431
return (ENETDOWN);
432
}
433
sid = SID(ifp);
434
zoneid = sid->s6id_list[scope];
435
NET_EPOCH_EXIT(et);
436
}
437
}
438
439
if (ret_id != NULL)
440
*ret_id = zoneid;
441
442
return (0);
443
}
444
445
/*
446
* Just clear the embedded scope identifier. Return 0 if the original address
447
* is intact; return non 0 if the address is modified.
448
*/
449
int
450
in6_clearscope(struct in6_addr *in6)
451
{
452
int modified = 0;
453
454
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6)) {
455
if (in6->s6_addr16[1] != 0)
456
modified = 1;
457
in6->s6_addr16[1] = 0;
458
}
459
460
return (modified);
461
}
462
463
/*
464
* Return the scope identifier or zero.
465
*/
466
uint16_t
467
in6_getscope(const struct in6_addr *in6)
468
{
469
470
if (IN6_IS_SCOPE_LINKLOCAL(in6) || IN6_IS_ADDR_MC_INTFACELOCAL(in6))
471
return (in6->s6_addr16[1]);
472
473
return (0);
474
}
475
476
/*
477
* Returns scope zone id for the unicast address @in6.
478
*
479
* Returns 0 for global unicast and loopback addresses.
480
* Returns interface index for the link-local addresses.
481
*/
482
uint32_t
483
in6_get_unicast_scopeid(const struct in6_addr *in6, const struct ifnet *ifp)
484
{
485
486
if (IN6_IS_SCOPE_LINKLOCAL(in6))
487
return (ifp->if_index);
488
return (0);
489
}
490
491
void
492
in6_set_unicast_scopeid(struct in6_addr *in6, uint32_t scopeid)
493
{
494
495
in6->s6_addr16[1] = htons(scopeid & 0xffff);
496
}
497
498
/*
499
* Return pointer to ifnet structure, corresponding to the zone id of
500
* link-local scope.
501
*/
502
struct ifnet*
503
in6_getlinkifnet(uint32_t zoneid)
504
{
505
struct ifnet *ifp;
506
507
ifp = ifnet_byindex((u_short)zoneid);
508
509
if (ifp == NULL)
510
return (NULL);
511
512
/* An interface might not be IPv6 capable. */
513
if (ifp->if_inet6 == NULL) {
514
log(LOG_NOTICE,
515
"%s: embedded scope points to an interface without "
516
"IPv6: %s%%%d.\n", __func__,
517
if_name(ifp), zoneid);
518
return (NULL);
519
}
520
521
return (ifp);
522
}
523
524
/*
525
* Return zone id for the specified scope.
526
*/
527
uint32_t
528
in6_getscopezone(const struct ifnet *ifp, int scope)
529
{
530
531
if (scope == IPV6_ADDR_SCOPE_INTFACELOCAL ||
532
scope == IPV6_ADDR_SCOPE_LINKLOCAL)
533
return (ifp->if_index);
534
if (scope >= 0 && scope < IPV6_ADDR_SCOPES_COUNT)
535
return (SID(ifp)->s6id_list[scope]);
536
return (0);
537
}
538
539
/*
540
* Extracts scope from address @dst, stores cleared address
541
* inside @dst and zone inside @scopeid
542
*/
543
void
544
in6_splitscope(const struct in6_addr *src, struct in6_addr *dst,
545
uint32_t *scopeid)
546
{
547
uint32_t zoneid;
548
549
*dst = *src;
550
zoneid = ntohs(in6_getscope(dst));
551
in6_clearscope(dst);
552
*scopeid = zoneid;
553
}
554
555
/*
556
* This function is for checking sockaddr_in6 structure passed
557
* from the application level (usually).
558
*
559
* sin6_scope_id should be set for link-local unicast, link-local and
560
* interface-local multicast addresses.
561
*
562
* If it is zero, then look into default zone ids. If default zone id is
563
* not set or disabled, then return error.
564
*/
565
int
566
sa6_checkzone(struct sockaddr_in6 *sa6)
567
{
568
int scope;
569
570
scope = in6_addrscope(&sa6->sin6_addr);
571
if (scope == IPV6_ADDR_SCOPE_GLOBAL)
572
return (sa6->sin6_scope_id ? EINVAL: 0);
573
if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr) &&
574
scope != IPV6_ADDR_SCOPE_LINKLOCAL &&
575
scope != IPV6_ADDR_SCOPE_INTFACELOCAL) {
576
if (sa6->sin6_scope_id == 0 && V_ip6_use_defzone != 0)
577
sa6->sin6_scope_id = V_sid_default.s6id_list[scope];
578
return (0);
579
}
580
/*
581
* Since ::1 address always configured on the lo0, we can
582
* automatically set its zone id, when it is not specified.
583
* Return error, when specified zone id doesn't match with
584
* actual value.
585
*/
586
if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) {
587
if (sa6->sin6_scope_id == 0)
588
sa6->sin6_scope_id = in6_getscopezone(V_loif, scope);
589
else if (sa6->sin6_scope_id != in6_getscopezone(V_loif, scope))
590
return (EADDRNOTAVAIL);
591
}
592
/* XXX: we can validate sin6_scope_id here */
593
if (sa6->sin6_scope_id != 0)
594
return (0);
595
if (V_ip6_use_defzone != 0)
596
sa6->sin6_scope_id = V_sid_default.s6id_list[scope];
597
/* Return error if we can't determine zone id */
598
return (sa6->sin6_scope_id ? 0: EADDRNOTAVAIL);
599
}
600
601
/*
602
* This function is similar to sa6_checkzone, but it uses given ifp
603
* to initialize sin6_scope_id.
604
*/
605
int
606
sa6_checkzone_ifp(struct ifnet *ifp, struct sockaddr_in6 *sa6)
607
{
608
int scope;
609
610
scope = in6_addrscope(&sa6->sin6_addr);
611
if (scope == IPV6_ADDR_SCOPE_LINKLOCAL ||
612
scope == IPV6_ADDR_SCOPE_INTFACELOCAL) {
613
if (sa6->sin6_scope_id == 0) {
614
sa6->sin6_scope_id = in6_getscopezone(ifp, scope);
615
return (0);
616
} else if (sa6->sin6_scope_id != in6_getscopezone(ifp, scope))
617
return (EADDRNOTAVAIL);
618
}
619
return (sa6_checkzone(sa6));
620
}
621
622