sync with KAME regarding NDP

- introduced fine-grain-timer to manage ND-caches and IPv6 Multicast-Listeners
- supports Router-Preference <draft-ietf-ipv6-router-selection-07.txt>
- better prefix lifetime management
- more spec-comformant DAD advertisement
- updated RFC/internet-draft revisions

Obtained from: KAME
Reviewed by: ume, gnn
MFC after: 2 month
This commit is contained in:
SUZUKI Shinsuke 2005-10-21 16:23:01 +00:00
parent 9c8aab3e0b
commit 743eee666f
15 changed files with 1531 additions and 797 deletions

View File

@ -204,19 +204,16 @@ RFC3542: Advanced Sockets API for IPv6 (revised)
* For supported library functions/kernel APIs, see sys/netinet6/ADVAPI. * For supported library functions/kernel APIs, see sys/netinet6/ADVAPI.
* Some of the updates in the draft are not implemented yet. See * Some of the updates in the draft are not implemented yet. See
TODO.2292bis for more details. TODO.2292bis for more details.
draft-ietf-ipngwg-icmp-name-lookups-09: IPv6 Name Lookups Through ICMP RFC4007: IPv6 Scoped Address Architecture
draft-ietf-ngtrans-tcpudp-relay-04.txt:
An IPv6-to-IPv4 transport relay translator
* FAITH tcp relay translator (faithd) implements this. See 3.1 for more
details.
draft-ietf-ipngwg-router-selection-01.txt:
Default Router Preferences and More-Specific Routes
* router-side only.
draft-ietf-ipngwg-scoping-arch-02.txt:
The architecture, text representation, and usage of IPv6
scoped addresses.
* some part of the documentation (especially about the routing * some part of the documentation (especially about the routing
model) is not supported yet. model) is not supported yet.
* zone indices that contain scope types have not been supported yet.
draft-ietf-ipngwg-icmp-name-lookups-09: IPv6 Name Lookups Through ICMP
draft-ietf-ipv6-router-selection-07.txt:
Default Router Preferences and More-Specific Routes
* router-side: both router preference and specific routes are supported.
* host-side: only router preference is supported.
draft-ietf-pim-sm-v2-new-02.txt draft-ietf-pim-sm-v2-new-02.txt
A revised version of RFC2362, which includes the IPv6 specific A revised version of RFC2362, which includes the IPv6 specific
packet format and protocol descriptions. packet format and protocol descriptions.
@ -224,8 +221,12 @@ draft-ietf-dnsext-mdns-00.txt: Multicast DNS
* kame/mdnsd has test implementation, which will not be built in * kame/mdnsd has test implementation, which will not be built in
default compilation. The draft will experience a major change in the default compilation. The draft will experience a major change in the
near future, so don't rely upon it. near future, so don't rely upon it.
draft-ietf-ipngwg-icmp-v3-02.txt: ICMPv6 for IPv6 specification (revised)
* See 1.9 in this document for details.
draft-itojun-ipv6-tcp-to-anycast-01.txt: draft-itojun-ipv6-tcp-to-anycast-01.txt:
Disconnecting TCP connection toward IPv6 anycast address Disconnecting TCP connection toward IPv6 anycast address
draft-ietf-ipv6-rfc2462bis-06.txt: IPv6 Stateless Address
Autoconfiguration (revised)
draft-itojun-ipv6-transition-abuse-01.txt: draft-itojun-ipv6-transition-abuse-01.txt:
Possible abuse against IPv6 transition technologies (expired) Possible abuse against IPv6 transition technologies (expired)
* KAME does not implement RFC1933/2893 automatic tunnel. * KAME does not implement RFC1933/2893 automatic tunnel.
@ -240,10 +241,11 @@ draft-itojun-ipv6-flowlabel-api-01.txt: Socket API for IPv6 flow label field
1.2 Neighbor Discovery 1.2 Neighbor Discovery
Neighbor Discovery is fairly stable. Currently Address Resolution, Our implementation of Neighbor Discovery is fairly stable. Currently
Duplicated Address Detection, and Neighbor Unreachability Detection Address Resolution, Duplicated Address Detection, and Neighbor
are supported. In the near future we will be adding Unsolicited Neighbor Unreachability Detection are supported. In the near future we will be
Advertisement transmission command as admin tool. adding an Unsolicited Neighbor Advertisement transmission command as
an administration tool.
Duplicated Address Detection (DAD) will be performed when an IPv6 address Duplicated Address Detection (DAD) will be performed when an IPv6 address
is assigned to a network interface, or the network interface is enabled is assigned to a network interface, or the network interface is enabled
@ -253,6 +255,21 @@ generated to syslog (and usually to console). The "duplicated" mark
can be checked with ifconfig. It is administrators' responsibility to check can be checked with ifconfig. It is administrators' responsibility to check
for and recover from DAD failures. We may try to improve failure recovery for and recover from DAD failures. We may try to improve failure recovery
in future KAME code. in future KAME code.
A successor version of RFC2462 (called rfc2462bis) clarifies the
behavior when DAD fails (i.e., duplicate is detected): if the
duplicate address is a link-local address formed from an interface
identifier based on the hardware address which is supposed to be
uniquely assigned (e.g., EUI-64 for an Ethernet interface), IPv6
operation on the interface should be disabled. The KAME
implementation supports this as follows: if this type of duplicate is
detected, the kernel marks "disabled" in the ND specific data
structure for the interface. Every IPv6 I/O operation in the kernel
checks this mark, and the kernel will drop packets received on or
being sent to the "disabled" interface. Whether the IPv6 operation is
disabled or not can be confirmed by the ndp(8) command. See the man
page for more details.
DAD procedure may not be effective on certain network interfaces/drivers. DAD procedure may not be effective on certain network interfaces/drivers.
If a network driver needs long initialization time (with wireless network If a network driver needs long initialization time (with wireless network
interfaces this situation is popular), and the driver mistakingly raises interfaces this situation is popular), and the driver mistakingly raises
@ -261,15 +278,13 @@ DAD probes to not-really-ready network driver and the packet will not go out
from the interface. In such cases, network drivers should be corrected. from the interface. In such cases, network drivers should be corrected.
Some of network drivers loop multicast packets back to themselves, Some of network drivers loop multicast packets back to themselves,
even if instructed not to do so (especially in promiscuous mode). even if instructed not to do so (especially in promiscuous mode). In
In such cases DAD may fail, because DAD engine sees inbound NS packet such cases DAD may fail, because the DAD engine sees inbound NS packet
(actually from the node itself) and considers it as a sign of duplicate. (actually from the node itself) and considers it as a sign of
In this case, drivers should be corrected to honor IFF_SIMPLEX behavior. duplicate. In this case, drivers should be corrected to honor
For example, you may need to check source MAC address on an inbound packet, IFF_SIMPLEX behavior. For example, you may need to check source MAC
and reject it if it is from the node itself. address on an inbound packet, and reject it if it is from the node
You may also want to look at #if condition marked "heuristics" in itself.
sys/netinet6/nd6_nbr.c:nd6_dad_timer() as workaround (note that the code
fragment in "heuristics" section is not spec conformant).
Neighbor Discovery specification (RFC2461) does not talk about neighbor Neighbor Discovery specification (RFC2461) does not talk about neighbor
cache handling in the following cases: cache handling in the following cases:
@ -281,12 +296,35 @@ For (1), we implemented workaround based on discussions on IETF ipngwg mailing
list. For more details, see the comments in the source code and email list. For more details, see the comments in the source code and email
thread started from (IPng 7155), dated Feb 6 1999. thread started from (IPng 7155), dated Feb 6 1999.
IPv6 on-link determination rule (RFC2461) is quite different from assumptions IPv6 on-link determination rule (RFC2461) is quite different from
in BSD IPv4 network code. To implement behavior in RFC2461 section 5.2 assumptions in BSD IPv4 network code. To implement the behavior in
(when default router list is empty), the kernel needs to know the default RFC2461 section 6.3.6 (3), the kernel needs to know the default
outgoing interface. To configure the default outgoing interface, use outgoing interface. To configure the default outgoing interface, use
commands like "ndp -I de0" as root. Note that the spec misuse the word commands like "ndp -I de0" as root. Then the kernel will have a
"host" and "node" in several places in the section. "default" route to the interface with the cloning "C" bit being on.
This default route will cause to make a neighbor cache entry for every
destination that does not match an explicit route entry.
Note that we intentionally disable configuring the default interface
by default. This is because we found it sometimes caused inconvenient
situation while it was rarely useful in practical usage. For example,
consider a destination that has both IPv4 and IPv6 addresses but is
only reachable via IPv4. Since our getaddrinfo(3) prefers IPv6 by
default, an (TCP) application using the library with PF_UNSPEC first
tries to connect to the IPv6 address. If we turn on RFC 2461 6.3.6
(3), we have to wait for quite a long period before the first attempt
to make a connection fails. If we turn it off, the first attempt will
immediately fail with EHOSTUNREACH, and then the application can try
the next, reachable address.
The notion of the default interface is also disabled when the node is
acting as a router. The reason is that routers tend to control all
routes stored in the kernel and the default route automatically
installed would rather confuse the routers. Note that the spec misuse
the word "host" and "node" in several places in Section 5.2 of RFC
2461. We basically read the word "node" in this section as "host,"
and thus believe the implementation policy does not break the
specification.
To avoid possible DoS attacks and infinite loops, KAME stack will accept To avoid possible DoS attacks and infinite loops, KAME stack will accept
only 10 options on ND packet. Therefore, if you have 20 prefix options only 10 options on ND packet. Therefore, if you have 20 prefix options
@ -312,32 +350,37 @@ There are certain limitations, though:
We do not prohibit hosts from doing proxy ND, but there will be very limited We do not prohibit hosts from doing proxy ND, but there will be very limited
use in it. use in it.
Starting mid March 2000, we support Neighbor Unreachability Detection (NUD) Starting mid March 2000, we support Neighbor Unreachability Detection
on p2p interfaces, including tunnel interfaces (gif). NUD is turned on by (NUD) on p2p interfaces, including tunnel interfaces (gif). NUD is
default. Before March 2000 KAME stack did not perform NUD on p2p interfaces. turned on by default. Before March 2000 the KAME stack did not
If the change raises any interoperability issues, you can turn off/on NUD perform NUD on p2p interfaces. If the change raises any
by per-interface basis. Use "ndp -i interface -nud" to turn it off. interoperability issues, you can turn off/on NUD by per-interface
Consult ndp(8) for details. basis. Use "ndp -i interface -nud" to turn it off. Consult ndp(8)
for details.
RFC2461 specifies upper-layer reachability confirmation hint. Whenever RFC2461 specifies upper-layer reachability confirmation hint. Whenever
upper-layer reachability confirmation hint comes, ND process can use it upper-layer reachability confirmation hint comes, ND process can use it
to optimize neighbor discovery process - ND process can omit real ND exchange to optimize neighbor discovery process - ND process can omit real ND exchange
and keep the neighbor cache state in REACHABLE. and keep the neighbor cache state in REACHABLE.
We currently have two sources for hints: (1) setsockopt(IPV6_REACHCONF) We currently have two sources for hints: (1) setsockopt(IPV6_REACHCONF)
defined by 2292bis API, and (2) hints from tcp_input. defined by the RFC3542 API, and (2) hints from tcp(6)_input.
It is questionable if they are really trustworthy. For example, a rogue
userland program can use IPV6_REACHCONF to confuse ND process. Neighbor It is questionable if they are really trustworthy. For example, a
cache is a system-wide information pool, and it is bad to allow single process rogue userland program can use IPV6_REACHCONF to confuse the ND
to affect others. Also, tcp_input can be hosed by hijack attempts. It is process. Neighbor cache is a system-wide information pool, and it is
wrong to allow hijack attempts to affect ND process. bad to allow a single process to affect others. Also, tcp(6)_input
Starting June 2000, ND code has a protection mechanism against incorrect can be hosed by hijack attempts. It is wrong to allow hijack attempts
upper-layer reachability confirmation. ND code counts subsequent upper-layer to affect the ND process.
hints. If the number of hints reaches maximum, ND code will ignore further
upper-layer hints and run real ND process to confirm reachability to the peer. Starting June 2000, the ND code has a protection mechanism against
sysctl net.inet6.icmp6.nd6_maxnudhint defines maximum # of subsequent incorrect upper-layer reachability confirmation. The ND code counts
subsequent upper-layer hints. If the number of hints reaches the
maximum, the ND code will ignore further upper-layer hints and run
real ND process to confirm reachability to the peer. sysctl
net.inet6.icmp6.nd6_maxnudhint defines the maximum # of subsequent
upper-layer hints to be accepted. upper-layer hints to be accepted.
(from April 2000 to June 2000, we rejected setsockopt(IPV6_REACHCONF) from (from April 2000 to June 2000, we rejected setsockopt(IPV6_REACHCONF) from
non-root process - after local discussion, it looks that hints are not non-root process - after a local discussion, it looks that hints are not
that trustworthy even if they are from privileged processes) that trustworthy even if they are from privileged processes)
If inbound ND packets carry invalid values, the KAME kernel will If inbound ND packets carry invalid values, the KAME kernel will
@ -681,29 +724,34 @@ The first step in stateless address configuration is Duplicated Address
Detection (DAD). See 1.2 for more detail on DAD. Detection (DAD). See 1.2 for more detail on DAD.
When a host hears Router Advertisement from the router, a host may When a host hears Router Advertisement from the router, a host may
autoconfigure itself by stateless address autoconfiguration. autoconfigure itself by stateless address autoconfiguration. This
This behavior can be controlled by net.inet6.ip6.accept_rtadv behavior can be controlled by the net.inet6.ip6.accept_rtadv sysctl
(host autoconfigures itself if it is set to 1). variable and a per-interface flag managed in the kernel. The latter,
By autoconfiguration, network address prefix for the receiving interface which we call "if_accept_rtadv" here, can be changed by the ndp(8)
(usually global address prefix) is added. The default route is also command (see the manpage for more details). When the sysctl variable
configured. is set to 1, and the flag is set, the host autoconfigures itself. By
autoconfiguration, network address prefixes for the receiving
interface (usually global address prefix) are added. The default
route is also configured.
Routers periodically generate Router Advertisement packets. To Routers periodically generate Router Advertisement packets. To
request an adjacent router to generate RA packet, a host can transmit request an adjacent router to generate RA packet, a host can transmit
Router Solicitation. To generate an RS packet at any time, use the Router Solicitation. To generate an RS packet at any time, use the
"rtsol" command. The "rtsold" daemon is also available. "rtsold" "rtsol" command. The "rtsold" daemon is also available. "rtsold"
generates Router Solicitation whenever necessary, and it works great generates Router Solicitation whenever necessary, and it works greatly
for nomadic usage (notebooks/laptops). If one wishes to ignore Router for nomadic usage (notebooks/laptops). If one wishes to ignore Router
Advertisements, use sysctl to set net.inet6.ip6.accept_rtadv to 0. Advertisements, use sysctl to set net.inet6.ip6.accept_rtadv to 0.
Additionally, ndp(8) command can be used to control the behavior
per-interface basis.
To generate Router Advertisement from a router, use the "rtadvd" daemon. To generate Router Advertisement from a router, use the "rtadvd" daemon.
Note that the IPv6 specification assumes the following items and that Note that the IPv6 specification assumes the following items and that
nonconforming cases are left unspecified: nonconforming cases are left unspecified:
- Only hosts will listen to router advertisements - Only hosts will listen to router advertisements
- Hosts have single network interface (except loopback) - Hosts have a single network interface (except loopback)
This is therefore unwise to enable net.inet6.ip6.accept_rtadv on routers, This is therefore unwise to enable net.inet6.ip6.accept_rtadv on routers,
or multi-interface host. A misconfigured node can behave strange or multi-interface hosts. A misconfigured node can behave strange
(KAME code allows nonconforming configuration, for those who would like (KAME code allows nonconforming configuration, for those who would like
to do some experiments). to do some experiments).
@ -713,12 +761,17 @@ To summarize the sysctl knob:
0 0 host (to be manually configured) 0 0 host (to be manually configured)
0 1 router 0 1 router
1 0 autoconfigured host 1 0 autoconfigured host
(spec assumes that host has single (spec assumes that hosts have a single
interface only, autoconfigred host with interface only, autoconfigred hosts
multiple interface is out-of-scope) with multiple interfaces are
out-of-scope)
1 1 invalid, or experimental 1 1 invalid, or experimental
(out-of-scope of spec) (out-of-scope of spec)
The if_accept_rtadv flag is referred only when accept_rtadv is 1 (the
latter two cases). The flag does not have any effects when the sysctl
variable is 0.
See 1.2 in the document for relationship between DAD and autoconfiguration. See 1.2 in the document for relationship between DAD and autoconfiguration.
1.4.3 DHCPv6 1.4.3 DHCPv6
@ -792,6 +845,14 @@ sent from a user application as follows:
routers, since some routing daemons stop advertising prefixes routers, since some routing daemons stop advertising prefixes
(addresses) on interfaces that have become down. (addresses) on interfaces that have become down.
- prefer addresses on "preferred" interfaces. "Preferred"
interfaces can be specified by the ndp(8) command. By default,
no interface is preferred, that is, this rule does not apply.
Again, this rule is particularly useful for routers, since there
is a convention, among router administrators, of assigning
"stable" addresses on a particular interface (typically a
loopback interface).
In any case, addresses that break the scope zone of the In any case, addresses that break the scope zone of the
destination, or addresses whose zone do not contain the outgoing destination, or addresses whose zone do not contain the outgoing
interface are never chosen. interface are never chosen.
@ -1396,7 +1457,7 @@ Here are couple of comments:
The form can be used as a trigger for TCP DoS attack. KAME code already The form can be used as a trigger for TCP DoS attack. KAME code already
filters them out. filters them out.
- The following examples are seemingly illegal. It seems that there's general - The following examples are seemingly illegal. It seems that there's general
consensus among ipngwg for those. (1) mobile-ip6 home address option, consensus among ipngwg for those. (1) Mobile IPv6 home address option,
(2) offlink packets (so routers should not forward them). (2) offlink packets (so routers should not forward them).
KAME implmements (2) already. KAME implmements (2) already.
@ -1601,9 +1662,12 @@ The following table lists the network drivers we have tried so far.
bah zbus/amiga NG(*) bah zbus/amiga NG(*)
cnw pcmcia/i386 ok ok yes cnw pcmcia/i386 ok ok yes
ep pcmcia/i386 ok ok - ep pcmcia/i386 ok ok -
fxp pci/i386 ok(*2) ok -
tlp pci/i386 ok ok -
le sbus/sparc ok ok yes le sbus/sparc ok ok yes
ne pci/i386 ok ok yes ne pci/i386 ok ok yes
ne pcmcia/i386 ok ok yes ne pcmcia/i386 ok ok yes
rtk pci/i386 ok ok -
wi pcmcia/i386 ok ok yes wi pcmcia/i386 ok ok yes
(ATM) (ATM)
en pci/i386 ok ok - en pci/i386 ok ok -
@ -1629,7 +1693,7 @@ Here is a list of FreeBSD 3.x-RELEASE drivers and its conditions:
(*) These drivers are distributed with PAO as PAO3 (*) These drivers are distributed with PAO as PAO3
(http://www.jp.freebsd.org/PAO/). (http://www.jp.freebsd.org/PAO/).
(**) there are trouble reports with multicast filter initialization. (**) there were trouble reports with multicast filter initialization.
More drivers will just simply work on KAME FreeBSD 3.x-RELEASE but have not More drivers will just simply work on KAME FreeBSD 3.x-RELEASE but have not
been checked yet. been checked yet.
@ -1677,6 +1741,7 @@ You may want to use "@insert" directive in /etc/pccard.conf to invoke
(*) exp driver has serious conflict with KAME initialization sequence. (*) exp driver has serious conflict with KAME initialization sequence.
A workaround is committed into sys/i386/pci/if_exp.c, and should be okay by now. A workaround is committed into sys/i386/pci/if_exp.c, and should be okay by now.
3. Translator 3. Translator
We categorize IPv4/IPv6 translator into 4 types. We categorize IPv4/IPv6 translator into 4 types.
@ -1720,13 +1785,13 @@ the connection will be relayed toward IPv4 destination 163.221.202.12.
faithd must be invoked on FAITH-relay dual stack node. faithd must be invoked on FAITH-relay dual stack node.
For more details, consult kame/kame/faithd/README and For more details, consult kame/kame/faithd/README and RFC3142.
draft-ietf-ngtrans-tcpudp-relay-04.txt.
3.2 IPv6-to-IPv4 header translator 3.2 IPv6-to-IPv4 header translator
(to be written) (to be written)
4. IPsec 4. IPsec
IPsec is implemented as the following three components. IPsec is implemented as the following three components.
@ -1902,7 +1967,7 @@ Currently supported algorithms are:
keyed SHA1 with 96bit crypto checksum (no document) keyed SHA1 with 96bit crypto checksum (no document)
HMAC MD5 with 96bit crypto checksum (rfc2403.txt HMAC MD5 with 96bit crypto checksum (rfc2403.txt
HMAC SHA1 with 96bit crypto checksum (rfc2404.txt) HMAC SHA1 with 96bit crypto checksum (rfc2404.txt)
HMAC SHA2-256 with 96bit crypto checksum (no document) HMAC SHA2-256 with 96bit crypto checksum (draft-ietf-ipsec-ciph-sha-256-00.txt)
HMAC SHA2-384 with 96bit crypto checksum (no document) HMAC SHA2-384 with 96bit crypto checksum (no document)
HMAC SHA2-512 with 96bit crypto checksum (no document) HMAC SHA2-512 with 96bit crypto checksum (no document)
HMAC RIPEMD160 with 96bit crypto checksum (RFC2857) HMAC RIPEMD160 with 96bit crypto checksum (RFC2857)
@ -1916,11 +1981,10 @@ Currently supported algorithms are:
BLOWFISH CBC (rfc2451.txt) BLOWFISH CBC (rfc2451.txt)
CAST128 CBC (rfc2451.txt) CAST128 CBC (rfc2451.txt)
RIJNDAEL/AES CBC (rfc3602.txt) RIJNDAEL/AES CBC (rfc3602.txt)
AES counter mode (draft-ietf-ipsec-ciph-aes-ctr-03.txt) AES counter mode (rfc3686.txt)
each of the above can be combined with: each of the above can be combined with new IPsec AH schemes for
ESP authentication with HMAC-MD5(96bit) ESP authentication.
ESP authentication with HMAC-SHA1(96bit)
IPComp IPComp
RFC2394: IP Payload Compression Using DEFLATE RFC2394: IP Payload Compression Using DEFLATE
@ -2000,19 +2064,26 @@ Here are (some of) platforms we have tested IPsec/IKE interoperability
in the past, no particular order. Note that both ends (KAME and in the past, no particular order. Note that both ends (KAME and
others) may have modified their implementation, so use the following others) may have modified their implementation, so use the following
list just for reference purposes. list just for reference purposes.
ACC, allied-telesis, Altiga, Ashley-laurent (vpcom.com), BlueSteel, 6WIND, ACC, Allied-telesis, Altiga, Ashley-laurent (vpcom.com),
CISCO IOS, Cryptek, Checkpoint FW-1, Data Fellows (F-Secure), BlueSteel, CISCO IOS, Checkpoint FW-1, Compaq Tru54 UNIX
Ericsson, Fitel, FreeS/WAN, HiFn, HITACHI, IBM AIX, IIJ, Intel Canada, X5.1B-BL4, Cryptek, Data Fellows (F-Secure), Ericsson,
Intel Packet Protect, MEW NetCocoon, MGCS, Microsoft WinNT/2000, F-Secure VPN+ 5.40, Fitec, Fitel, FreeS/WAN, HITACHI, HiFn,
NAI PGPnet, NetLock, NIST (linux IPsec + plutoplus), NEC IX5000, IBM AIX 5.1, III, IIJ (fujie stack), Intel Canada, Intel
Netscreen, NxNetworks, OpenBSD isakmpd, Pivotal, Radguard, RapidStream, Packet Protect, MEW NetCocoon, MGCS, Microsoft WinNT/2000/XP,
RedCreek, Routerware, RSA, SSH (both IPv4/IPv6), Secure Computing, NAI PGPnet, NEC IX5000, NIST (linux IPsec + plutoplus),
Soliton, Sun Solaris8, TIS/NAI Gauntret, Toshiba, VPNet, NetLock, Netoctave, Netopia, Netscreen, Nokia EPOC, Nortel
Yamaha RT series GatewayController/CallServer 2000 (not released yet),
NxNetworks, OpenBSD isakmpd on OpenBSD, Oullim information
technologies SECUREWORKS VPN gateway 3.0, Pivotal, RSA,
Radguard, RapidStream, RedCreek, Routerware, SSH, SecGo
CryptoIP v3, Secure Computing, Soliton, Sun Solaris 8,
TIS/NAI Gauntret, Toshiba, Trilogy AdmitOne 2.6, Trustworks
TrustedClient v3.2, USAGI linux, VPNet, Yamaha RT series,
ZyXEL
Here are (some of) platforms we have tested IPComp/IKE interoperability Here are (some of) platforms we have tested IPComp/IKE interoperability
in the past, in no particular order. in the past, in no particular order.
IRE, SSH (both IPv4/IPv6), NetLock Compaq, IRE, SSH, NetLock, FreeS/WAN, F-Secure VPN+ 5.40
VPNC (vpnc.org) provides IPsec conformance tests, using KAME and OpenBSD VPNC (vpnc.org) provides IPsec conformance tests, using KAME and OpenBSD
IPsec/IKE implementations. Their test results are available at IPsec/IKE implementations. Their test results are available at
@ -2147,9 +2218,11 @@ interoperate.
5. ALTQ 5. ALTQ
KAME kit includes ALTQ 2.1 code, which supports FreeBSD2, FreeBSD3, KAME kit includes ALTQ, which supports FreeBSD3, FreeBSD4, FreeBSD5
NetBSD and OpenBSD. For BSD/OS, ALTQ does not work. NetBSD. OpenBSD has ALTQ merged into pf and its ALTQ code is not
ALTQ in KAME supports (or tries to support) IPv6. compatible with other platforms so that KAME's ALTQ is not used for
OpenBSD. For BSD/OS, ALTQ does not work.
ALTQ in KAME supports IPv6.
(actually, ALTQ is developed on KAME repository since ALTQ 2.1 - Jan 2000) (actually, ALTQ is developed on KAME repository since ALTQ 2.1 - Jan 2000)
ALTQ occupies single character device number. For FreeBSD, it is officially ALTQ occupies single character device number. For FreeBSD, it is officially
@ -2167,7 +2240,8 @@ compile ALTQ-ready kernel for other archititectures, take the following steps:
- before building userland, change netbsd/{lib,usr.sbin,usr.bin}/Makefile - before building userland, change netbsd/{lib,usr.sbin,usr.bin}/Makefile
(or openbsd/foobaa) so that it will visit altq-related sub directories. (or openbsd/foobaa) so that it will visit altq-related sub directories.
6. mobile-ip6
6. Mobile IPv6
6.1 KAME node as correspondent node 6.1 KAME node as correspondent node

View File

@ -619,7 +619,11 @@ struct icmp6stat {
#define ICMPV6CTL_ND6_DEBUG 18 #define ICMPV6CTL_ND6_DEBUG 18
#define ICMPV6CTL_ND6_DRLIST 19 #define ICMPV6CTL_ND6_DRLIST 19
#define ICMPV6CTL_ND6_PRLIST 20 #define ICMPV6CTL_ND6_PRLIST 20
#define ICMPV6CTL_MAXID 21 #define ICMPV6CTL_MLD_MAXSRCFILTER 21
#define ICMPV6CTL_MLD_SOMAXSRC 22
#define ICMPV6CTL_MLD_VERSION 23
#define ICMPV6CTL_ND6_MAXQLEN 24
#define ICMPV6CTL_MAXID 25
#define RTF_PROBEMTU RTF_PROTO1 #define RTF_PROBEMTU RTF_PROTO1

View File

@ -2178,7 +2178,7 @@ void
icmp6_fasttimo() icmp6_fasttimo()
{ {
mld6_fasttimeo(); return;
} }
static const char * static const char *
@ -2415,7 +2415,7 @@ icmp6_redirect_output(m0, rt)
icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0); icmp6_errcount(&icmp6stat.icp6s_outerrhist, ND_REDIRECT, 0);
/* if we are not router, we don't send icmp6 redirect */ /* if we are not router, we don't send icmp6 redirect */
if (!ip6_forwarding || ip6_accept_rtadv) if (!ip6_forwarding)
goto fail; goto fail;
/* sanity check */ /* sanity check */

View File

@ -328,6 +328,7 @@ in6_control(so, cmd, data, ifp, td)
struct in6_ifaddr *ia = NULL; struct in6_ifaddr *ia = NULL;
struct in6_aliasreq *ifra = (struct in6_aliasreq *)data; struct in6_aliasreq *ifra = (struct in6_aliasreq *)data;
int error, privileged; int error, privileged;
struct sockaddr_in6 *sa6;
privileged = 0; privileged = 0;
if (td == NULL || !suser(td)) if (td == NULL || !suser(td))
@ -408,19 +409,56 @@ in6_control(so, cmd, data, ifp, td)
/* /*
* Find address for this interface, if it exists. * Find address for this interface, if it exists.
*
* In netinet code, we have checked ifra_addr in SIOCSIF*ADDR operation
* only, and used the first interface address as the target of other
* operations (without checking ifra_addr). This was because netinet
* code/API assumed at most 1 interface address per interface.
* Since IPv6 allows a node to assign multiple addresses
* on a single interface, we almost always look and check the
* presence of ifra_addr, and reject invalid ones here.
* It also decreases duplicated code among SIOC*_IN6 operations.
*/ */
if (ifra->ifra_addr.sin6_family == AF_INET6) { /* XXX */ switch (cmd) {
case SIOCAIFADDR_IN6:
case SIOCSIFPHYADDR_IN6:
sa6 = &ifra->ifra_addr;
break;
case SIOCSIFADDR_IN6:
case SIOCGIFADDR_IN6:
case SIOCSIFDSTADDR_IN6:
case SIOCSIFNETMASK_IN6:
case SIOCGIFDSTADDR_IN6:
case SIOCGIFNETMASK_IN6:
case SIOCDIFADDR_IN6:
case SIOCGIFPSRCADDR_IN6:
case SIOCGIFPDSTADDR_IN6:
case SIOCGIFAFLAG_IN6:
case SIOCSNDFLUSH_IN6:
case SIOCSPFXFLUSH_IN6:
case SIOCSRTRFLUSH_IN6:
case SIOCGIFALIFETIME_IN6:
case SIOCSIFALIFETIME_IN6:
case SIOCGIFSTAT_IN6:
case SIOCGIFSTAT_ICMP6:
sa6 = &ifr->ifr_addr;
break;
default:
sa6 = NULL;
break;
}
if (sa6 && sa6->sin6_family == AF_INET6) {
int error = 0; int error = 0;
if (ifra->ifra_addr.sin6_scope_id != 0) if (sa6->sin6_scope_id != 0)
error = sa6_embedscope(&ifra->ifra_addr, 0); error = sa6_embedscope(sa6, 0);
else else
error = in6_setscope(&ifra->ifra_addr.sin6_addr, error = in6_setscope(&sa6->sin6_addr, ifp, NULL);
ifp, NULL);
if (error != 0) if (error != 0)
return (error); return (error);
ia = in6ifa_ifpwithaddr(ifp, &ifra->ifra_addr.sin6_addr); ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
} } else
ia = NULL;
switch (cmd) { switch (cmd) {
case SIOCSIFADDR_IN6: case SIOCSIFADDR_IN6:
@ -538,6 +576,42 @@ in6_control(so, cmd, data, ifp, td)
case SIOCGIFALIFETIME_IN6: case SIOCGIFALIFETIME_IN6:
ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime; ifr->ifr_ifru.ifru_lifetime = ia->ia6_lifetime;
if (ia->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME) {
time_t maxexpire;
struct in6_addrlifetime *retlt =
&ifr->ifr_ifru.ifru_lifetime;
/*
* XXX: adjust expiration time assuming time_t is
* signed.
*/
maxexpire = (-1) &
~(1 << ((sizeof(maxexpire) * 8) - 1));
if (ia->ia6_lifetime.ia6t_vltime <
maxexpire - ia->ia6_updatetime) {
retlt->ia6t_expire = ia->ia6_updatetime +
ia->ia6_lifetime.ia6t_vltime;
} else
retlt->ia6t_expire = maxexpire;
}
if (ia->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME) {
time_t maxexpire;
struct in6_addrlifetime *retlt =
&ifr->ifr_ifru.ifru_lifetime;
/*
* XXX: adjust expiration time assuming time_t is
* signed.
*/
maxexpire = (-1) &
~(1 << ((sizeof(maxexpire) * 8) - 1));
if (ia->ia6_lifetime.ia6t_pltime <
maxexpire - ia->ia6_updatetime) {
retlt->ia6t_preferred = ia->ia6_updatetime +
ia->ia6_lifetime.ia6t_pltime;
} else
retlt->ia6t_preferred = maxexpire;
}
break; break;
case SIOCSIFALIFETIME_IN6: case SIOCSIFALIFETIME_IN6:
@ -558,13 +632,14 @@ in6_control(so, cmd, data, ifp, td)
case SIOCAIFADDR_IN6: case SIOCAIFADDR_IN6:
{ {
int i, error = 0; int i, error = 0;
struct nd_prefix pr0, *pr; struct nd_prefixctl pr0;
struct nd_prefix *pr;
/* /*
* first, make or update the interface address structure, * first, make or update the interface address structure,
* and link it to the list. * and link it to the list.
*/ */
if ((error = in6_update_ifa(ifp, ifra, ia)) != 0) if ((error = in6_update_ifa(ifp, ifra, ia, 0)) != 0)
return (error); return (error);
/* /*
@ -586,7 +661,6 @@ in6_control(so, cmd, data, ifp, td)
break; /* we don't need to install a host route. */ break; /* we don't need to install a host route. */
} }
pr0.ndpr_prefix = ifra->ifra_addr; pr0.ndpr_prefix = ifra->ifra_addr;
pr0.ndpr_mask = ifra->ifra_prefixmask.sin6_addr;
/* apply the mask for safety. */ /* apply the mask for safety. */
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
@ -638,7 +712,7 @@ in6_control(so, cmd, data, ifp, td)
if (ip6_use_tempaddr && if (ip6_use_tempaddr &&
pr->ndpr_refcnt == 1) { pr->ndpr_refcnt == 1) {
int e; int e;
if ((e = in6_tmpifadd(ia, 1)) != 0) { if ((e = in6_tmpifadd(ia, 1, 0)) != 0) {
log(LOG_NOTICE, "in6_control: " log(LOG_NOTICE, "in6_control: "
"failed to create a " "failed to create a "
"temporary address, " "temporary address, "
@ -662,7 +736,8 @@ in6_control(so, cmd, data, ifp, td)
case SIOCDIFADDR_IN6: case SIOCDIFADDR_IN6:
{ {
int i = 0; int i = 0;
struct nd_prefix pr0, *pr; struct nd_prefixctl pr0;
struct nd_prefix *pr;
/* /*
* If the address being deleted is the only one that owns * If the address being deleted is the only one that owns
@ -680,10 +755,10 @@ in6_control(so, cmd, data, ifp, td)
if (pr0.ndpr_plen == 128) if (pr0.ndpr_plen == 128)
goto purgeaddr; goto purgeaddr;
pr0.ndpr_prefix = ia->ia_addr; pr0.ndpr_prefix = ia->ia_addr;
pr0.ndpr_mask = ia->ia_prefixmask.sin6_addr; /* apply the mask for safety. */
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &= pr0.ndpr_prefix.sin6_addr.s6_addr32[i] &=
ia->ia_prefixmask.sin6_addr.s6_addr32[i]; ifra->ifra_prefixmask.sin6_addr.s6_addr32[i];
} }
/* /*
* The logic of the following condition is a bit complicated. * The logic of the following condition is a bit complicated.
@ -723,16 +798,20 @@ in6_control(so, cmd, data, ifp, td)
* XXX: should this be performed under splnet()? * XXX: should this be performed under splnet()?
*/ */
int int
in6_update_ifa(ifp, ifra, ia) in6_update_ifa(ifp, ifra, ia, flags)
struct ifnet *ifp; struct ifnet *ifp;
struct in6_aliasreq *ifra; struct in6_aliasreq *ifra;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
int flags;
{ {
int error = 0, hostIsNew = 0, plen = -1; int error = 0, hostIsNew = 0, plen = -1;
struct in6_ifaddr *oia; struct in6_ifaddr *oia;
struct sockaddr_in6 dst6; struct sockaddr_in6 dst6;
struct in6_addrlifetime *lt; struct in6_addrlifetime *lt;
struct in6_multi_mship *imm;
struct in6_multi *in6m_sol;
struct rtentry *rt; struct rtentry *rt;
int delay;
/* Validate parameters */ /* Validate parameters */
if (ifp == NULL || ifra == NULL) /* this maybe redundant */ if (ifp == NULL || ifra == NULL) /* this maybe redundant */
@ -818,10 +897,8 @@ in6_update_ifa(ifp, ifra, ia)
} }
/* lifetime consistency check */ /* lifetime consistency check */
lt = &ifra->ifra_lifetime; lt = &ifra->ifra_lifetime;
if (lt->ia6t_vltime != ND6_INFINITE_LIFETIME if (lt->ia6t_pltime > lt->ia6t_vltime)
&& lt->ia6t_vltime + time_second < time_second) { return (EINVAL);
return EINVAL;
}
if (lt->ia6t_vltime == 0) { if (lt->ia6t_vltime == 0) {
/* /*
* the following log might be noisy, but this is a typical * the following log might be noisy, but this is a typical
@ -830,10 +907,9 @@ in6_update_ifa(ifp, ifra, ia)
nd6log((LOG_INFO, nd6log((LOG_INFO,
"in6_update_ifa: valid lifetime is 0 for %s\n", "in6_update_ifa: valid lifetime is 0 for %s\n",
ip6_sprintf(&ifra->ifra_addr.sin6_addr))); ip6_sprintf(&ifra->ifra_addr.sin6_addr)));
}
if (lt->ia6t_pltime != ND6_INFINITE_LIFETIME if (ia == NULL)
&& lt->ia6t_pltime + time_second < time_second) { return (0); /* there's nothing to do */
return EINVAL;
} }
/* /*
@ -852,11 +928,12 @@ in6_update_ifa(ifp, ifra, ia)
if (ia == NULL) if (ia == NULL)
return (ENOBUFS); return (ENOBUFS);
bzero((caddr_t)ia, sizeof(*ia)); bzero((caddr_t)ia, sizeof(*ia));
/* Initialize the address and masks */ /* Initialize the address and masks, and put time stamp */
IFA_LOCK_INIT(&ia->ia_ifa); IFA_LOCK_INIT(&ia->ia_ifa);
ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr;
ia->ia_addr.sin6_family = AF_INET6; ia->ia_addr.sin6_family = AF_INET6;
ia->ia_addr.sin6_len = sizeof(ia->ia_addr); ia->ia_addr.sin6_len = sizeof(ia->ia_addr);
ia->ia6_createtime = time_second;
if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) {
/* /*
* XXX: some functions expect that ifa_dstaddr is not * XXX: some functions expect that ifa_dstaddr is not
@ -881,6 +958,9 @@ in6_update_ifa(ifp, ifra, ia)
TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list); TAILQ_INSERT_TAIL(&ifp->if_addrlist, &ia->ia_ifa, ifa_list);
} }
/* update timestamp */
ia->ia6_updatetime = time_second;
/* set prefix mask */ /* set prefix mask */
if (ifra->ifra_prefixmask.sin6_len) { if (ifra->ifra_prefixmask.sin6_len) {
/* /*
@ -945,8 +1025,6 @@ in6_update_ifa(ifp, ifra, ia)
* configure address flags. * configure address flags.
*/ */
ia->ia6_flags = ifra->ifra_flags; ia->ia6_flags = ifra->ifra_flags;
ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */
ia->ia6_flags &= ~IN6_IFF_NODAD; /* Mobile IPv6 */
/* /*
* backward compatibility - if IN6_IFF_DEPRECATED is set from the * backward compatibility - if IN6_IFF_DEPRECATED is set from the
* userland, make it deprecated. * userland, make it deprecated.
@ -955,17 +1033,14 @@ in6_update_ifa(ifp, ifra, ia)
ia->ia6_lifetime.ia6t_pltime = 0; ia->ia6_lifetime.ia6t_pltime = 0;
ia->ia6_lifetime.ia6t_preferred = time_second; ia->ia6_lifetime.ia6t_preferred = time_second;
} }
/* /*
* Perform DAD, if needed. * Make the address tentative before joining multicast addresses,
* XXX It may be of use, if we can administratively * so that corresponding MLD responses would not have a tentative
* disable DAD. * source address.
*/ */
if (in6if_do_dad(ifp) && hostIsNew && ia->ia6_flags &= ~IN6_IFF_DUPLICATED; /* safety */
(ifra->ifra_flags & IN6_IFF_NODAD) == 0) { if (hostIsNew && in6if_do_dad(ifp))
ia->ia6_flags |= IN6_IFF_TENTATIVE; ia->ia6_flags |= IN6_IFF_TENTATIVE;
nd6_dad_start((struct ifaddr *)ia, NULL);
}
/* /*
* We are done if we have simply modified an existing address. * We are done if we have simply modified an existing address.
@ -979,9 +1054,9 @@ in6_update_ifa(ifp, ifra, ia)
*/ */
/* Join necessary multicast groups */ /* Join necessary multicast groups */
in6m_sol = NULL;
if ((ifp->if_flags & IFF_MULTICAST) != 0) { if ((ifp->if_flags & IFF_MULTICAST) != 0) {
struct sockaddr_in6 mltaddr, mltmask; struct sockaddr_in6 mltaddr, mltmask;
struct in6_multi *in6m;
struct in6_addr llsol; struct in6_addr llsol;
/* join solicited multicast addr for new host id */ /* join solicited multicast addr for new host id */
@ -997,15 +1072,29 @@ in6_update_ifa(ifp, ifra, ia)
"in6_setscope failed\n"); "in6_setscope failed\n");
goto cleanup; goto cleanup;
} }
(void)in6_addmulti(&llsol, ifp, &error); delay = 0;
if ((flags & IN6_IFAUPDATE_DADDELAY)) {
/*
* We need a random delay for DAD on the address
* being configured. It also means delaying
* transmission of the corresponding MLD report to
* avoid report collision.
* [draft-ietf-ipv6-rfc2462bis-02.txt]
*/
delay = arc4random() %
(MAX_RTR_SOLICITATION_DELAY * hz);
}
imm = in6_joingroup(ifp, &llsol, &error, delay);
if (error != 0) { if (error != 0) {
nd6log((LOG_WARNING, nd6log((LOG_WARNING,
"in6_update_ifa: addmulti failed for " "in6_update_ifa: addmulti failed for "
"%s on %s (errno=%d)\n", "%s on %s (errno=%d)\n",
ip6_sprintf(&llsol), if_name(ifp), ip6_sprintf(&llsol), if_name(ifp),
error)); error));
goto cleanup; in6_purgeaddr((struct ifaddr *)ia);
return (error);
} }
in6m_sol = imm->i6mm_maddr;
bzero(&mltmask, sizeof(mltmask)); bzero(&mltmask, sizeof(mltmask));
mltmask.sin6_len = sizeof(struct sockaddr_in6); mltmask.sin6_len = sizeof(struct sockaddr_in6);
@ -1050,10 +1139,35 @@ in6_update_ifa(ifp, ifra, ia)
} else } else
RTFREE_LOCKED(rt); RTFREE_LOCKED(rt);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); /*
if (in6m == NULL) { * XXX: do we really need this automatic routes?
(void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); * We should probably reconsider this stuff. Most applications
if (error != 0) { * actually do not need the routes, since they usually specify
* the outgoing interface.
*/
rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
if (rt) {
/* XXX: only works in !SCOPEDROUTING case. */
if (memcmp(&mltaddr.sin6_addr,
&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
MLTMASK_LEN)) {
RTFREE_LOCKED(rt);
rt = NULL;
}
}
if (!rt) {
error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
(struct sockaddr *)&ia->ia_addr,
(struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING,
(struct rtentry **)0);
if (error)
goto cleanup;
} else {
RTFREE_LOCKED(rt);
}
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0);
if (!imm) {
nd6log((LOG_WARNING, nd6log((LOG_WARNING,
"in6_update_ifa: addmulti failed for " "in6_update_ifa: addmulti failed for "
"%s on %s (errno=%d)\n", "%s on %s (errno=%d)\n",
@ -1061,26 +1175,31 @@ in6_update_ifa(ifp, ifra, ia)
if_name(ifp), error)); if_name(ifp), error));
goto cleanup; goto cleanup;
} }
}
/* /*
* join node information group address * join node information group address
*/ */
#define hostnamelen strlen(hostname) #define hostnamelen strlen(hostname)
delay = 0;
if ((flags & IN6_IFAUPDATE_DADDELAY)) {
/*
* The spec doesn't say anything about delay for this
* group, but the same logic should apply.
*/
delay = arc4random() %
(MAX_RTR_SOLICITATION_DELAY * hz);
}
if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr) if (in6_nigroup(ifp, hostname, hostnamelen, &mltaddr.sin6_addr)
== 0) { == 0) {
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error,
if (in6m == NULL) { delay); /* XXX jinmei */
(void)in6_addmulti(&mltaddr.sin6_addr, if (!imm) {
ifp, &error);
if (error != 0) {
nd6log((LOG_WARNING, "in6_update_ifa: " nd6log((LOG_WARNING, "in6_update_ifa: "
"addmulti failed for " "addmulti failed for %s on %s "
"%s on %s (errno=%d)\n", "(errno=%d)\n",
ip6_sprintf(&mltaddr.sin6_addr), ip6_sprintf(&mltaddr.sin6_addr),
if_name(ifp), error)); if_name(ifp), error));
goto cleanup; /* XXX not very fatal, go on... */
}
} }
} }
#undef hostnamelen #undef hostnamelen
@ -1113,10 +1232,29 @@ in6_update_ifa(ifp, ifra, ia)
} else } else
RTFREE_LOCKED(rt); RTFREE_LOCKED(rt);
IN6_LOOKUP_MULTI(mltaddr.sin6_addr, ifp, in6m); /* XXX: again, do we really need the route? */
if (in6m == NULL) { rt = rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL);
(void)in6_addmulti(&mltaddr.sin6_addr, ifp, &error); if (rt) {
if (error != 0) { if (memcmp(&mltaddr.sin6_addr,
&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
MLTMASK_LEN)) {
RTFREE_LOCKED(rt);
rt = NULL;
}
}
if (!rt) {
error = rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr,
(struct sockaddr *)&ia->ia_addr,
(struct sockaddr *)&mltmask, RTF_UP | RTF_CLONING,
(struct rtentry **)0);
if (error)
goto cleanup;
} else {
RTFREE_LOCKED(rt);
}
imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0);
if (!imm) {
nd6log((LOG_WARNING, "in6_update_ifa: " nd6log((LOG_WARNING, "in6_update_ifa: "
"addmulti failed for %s on %s " "addmulti failed for %s on %s "
"(errno=%d)\n", "(errno=%d)\n",
@ -1124,10 +1262,47 @@ in6_update_ifa(ifp, ifra, ia)
if_name(ifp), error)); if_name(ifp), error));
goto cleanup; goto cleanup;
} }
}
#undef MLTMASK_LEN #undef MLTMASK_LEN
} }
/*
* Perform DAD, if needed.
* XXX It may be of use, if we can administratively
* disable DAD.
*/
if (hostIsNew && in6if_do_dad(ifp) &&
((ifra->ifra_flags & IN6_IFF_NODAD) == 0) &&
(ia->ia6_flags & IN6_IFF_TENTATIVE))
{
int mindelay, maxdelay;
delay = 0;
if ((flags & IN6_IFAUPDATE_DADDELAY)) {
/*
* We need to impose a delay before sending an NS
* for DAD. Check if we also needed a delay for the
* corresponding MLD message. If we did, the delay
* should be larger than the MLD delay (this could be
* relaxed a bit, but this simple logic is at least
* safe).
*/
mindelay = 0;
if (in6m_sol != NULL &&
in6m_sol->in6m_state == MLD_REPORTPENDING) {
mindelay = in6m_sol->in6m_timer;
}
maxdelay = MAX_RTR_SOLICITATION_DELAY * hz;
if (maxdelay - mindelay == 0)
delay = 0;
else {
delay =
(arc4random() % (maxdelay - mindelay)) +
mindelay;
}
}
nd6_dad_start((struct ifaddr *)ia, delay);
}
return (error); return (error);
unlink: unlink:
@ -1603,10 +1778,11 @@ in6_ifinit(ifp, ia, sin6, newhost)
} }
struct in6_multi_mship * struct in6_multi_mship *
in6_joingroup(ifp, addr, errorp) in6_joingroup(ifp, addr, errorp, delay)
struct ifnet *ifp; struct ifnet *ifp;
struct in6_addr *addr; struct in6_addr *addr;
int *errorp; int *errorp;
int delay;
{ {
struct in6_multi_mship *imm; struct in6_multi_mship *imm;
@ -1615,7 +1791,7 @@ in6_joingroup(ifp, addr, errorp)
*errorp = ENOBUFS; *errorp = ENOBUFS;
return NULL; return NULL;
} }
imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp); imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, delay);
if (!imm->i6mm_maddr) { if (!imm->i6mm_maddr) {
/* *errorp is alrady set */ /* *errorp is alrady set */
free(imm, M_IP6MADDR); free(imm, M_IP6MADDR);
@ -1943,21 +2119,27 @@ in6_if_up(ifp)
{ {
struct ifaddr *ifa; struct ifaddr *ifa;
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
int dad_delay; /* delay ticks before DAD output */
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ia = (struct in6_ifaddr *)ifa;
if (ia->ia6_flags & IN6_IFF_TENTATIVE) {
/*
* The TENTATIVE flag was likely set by hand
* beforehand, implicitly indicating the need for DAD.
* We may be able to skip the random delay in this
* case, but we impose delays just in case.
*/
nd6_dad_start(ifa,
arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz));
}
}
/* /*
* special cases, like 6to4, are handled in in6_ifattach * special cases, like 6to4, are handled in in6_ifattach
*/ */
in6_ifattach(ifp, NULL); in6_ifattach(ifp, NULL);
dad_delay = 0;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
ia = (struct in6_ifaddr *)ifa;
if (ia->ia6_flags & IN6_IFF_TENTATIVE)
nd6_dad_start(ifa, &dad_delay);
}
} }
int int
@ -2021,6 +2203,66 @@ in6_setmaxmtu()
in6_maxmtu = maxmtu; in6_maxmtu = maxmtu;
} }
/*
* Provide the length of interface identifiers to be used for the link attached
* to the given interface. The length should be defined in "IPv6 over
* xxx-link" document. Note that address architecture might also define
* the length for a particular set of address prefixes, regardless of the
* link type. As clarified in rfc2462bis, those two definitions should be
* consistent, and those really are as of August 2004.
*/
int
in6_if2idlen(ifp)
struct ifnet *ifp;
{
switch (ifp->if_type) {
case IFT_ETHER: /* RFC2464 */
#ifdef IFT_PROPVIRTUAL
case IFT_PROPVIRTUAL: /* XXX: no RFC. treat it as ether */
#endif
#ifdef IFT_L2VLAN
case IFT_L2VLAN: /* ditto */
#endif
#ifdef IFT_IEEE80211
case IFT_IEEE80211: /* ditto */
#endif
#ifdef IFT_MIP
case IFT_MIP: /* ditto */
#endif
return (64);
case IFT_FDDI: /* RFC2467 */
return (64);
case IFT_ISO88025: /* RFC2470 (IPv6 over Token Ring) */
return (64);
case IFT_PPP: /* RFC2472 */
return (64);
case IFT_ARCNET: /* RFC2497 */
return (64);
case IFT_FRELAY: /* RFC2590 */
return (64);
case IFT_IEEE1394: /* RFC3146 */
return (64);
case IFT_GIF:
return (64); /* draft-ietf-v6ops-mech-v2-07 */
case IFT_LOOP:
return (64); /* XXX: is this really correct? */
default:
/*
* Unknown link type:
* It might be controversial to use the today's common constant
* of 64 for these cases unconditionally. For full compliance,
* we should return an error in this case. On the other hand,
* if we simply miss the standard for the link type or a new
* standard is defined for a new link type, the IFID length
* is very likely to be the common constant. As a compromise,
* we always use the constant, but make an explicit notice
* indicating the "unknown" case.
*/
printf("in6_if2idlen: unknown link type (%d)\n", ifp->if_type);
return (64);
}
}
void * void *
in6_domifattach(ifp) in6_domifattach(ifp)
struct ifnet *ifp; struct ifnet *ifp;

View File

@ -374,11 +374,13 @@ extern const struct in6_addr in6addr_linklocal_allrouters;
(IN6_IS_ADDR_MC_LINKLOCAL(a))) (IN6_IS_ADDR_MC_LINKLOCAL(a)))
#define IFA6_IS_DEPRECATED(a) \ #define IFA6_IS_DEPRECATED(a) \
((a)->ia6_lifetime.ia6t_preferred != 0 && \ ((a)->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME && \
(a)->ia6_lifetime.ia6t_preferred < time_second) (u_int32_t)((time_second - (a)->ia6_updatetime)) > \
(a)->ia6_lifetime.ia6t_pltime)
#define IFA6_IS_INVALID(a) \ #define IFA6_IS_INVALID(a) \
((a)->ia6_lifetime.ia6t_expire != 0 && \ ((a)->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME && \
(a)->ia6_lifetime.ia6t_expire < time_second) (u_int32_t)((time_second - (a)->ia6_updatetime)) > \
(a)->ia6_lifetime.ia6t_vltime)
#endif /* _KERNEL */ #endif /* _KERNEL */
/* /*

View File

@ -419,7 +419,7 @@ in6_ifattach_linklocal(ifp, altifp)
{ {
struct in6_ifaddr *ia; struct in6_ifaddr *ia;
struct in6_aliasreq ifra; struct in6_aliasreq ifra;
struct nd_prefix pr0; struct nd_prefixctl pr0;
int i, error; int i, error;
/* /*
@ -457,20 +457,14 @@ in6_ifattach_linklocal(ifp, altifp)
ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
/*
* Do not let in6_update_ifa() do DAD, since we need a random delay
* before sending an NS at the first time the interface becomes up.
* Instead, in6_if_up() will start DAD with a proper random delay.
*/
ifra.ifra_flags |= IN6_IFF_NODAD;
/* /*
* Now call in6_update_ifa() to do a bunch of procedures to configure * Now call in6_update_ifa() to do a bunch of procedures to configure
* a link-local address. We can set the 3rd argument to NULL, because * a link-local address. We can set the 3rd argument to NULL, because
* we know there's no other link-local address on the interface * we know there's no other link-local address on the interface
* and therefore we are adding one (instead of updating one). * and therefore we are adding one (instead of updating one).
*/ */
if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { if ((error = in6_update_ifa(ifp, &ifra, NULL,
IN6_IFAUPDATE_DADDELAY)) != 0) {
/* /*
* XXX: When the interface does not support IPv6, this call * XXX: When the interface does not support IPv6, this call
* would fail in the SIOCSIFADDR ioctl. I believe the * would fail in the SIOCSIFADDR ioctl. I believe the
@ -485,11 +479,6 @@ in6_ifattach_linklocal(ifp, altifp)
return (-1); return (-1);
} }
/*
* Adjust ia6_flags so that in6_if_up will perform DAD.
* XXX: Some P2P interfaces seem not to send packets just after
* becoming up, so we skip p2p interfaces for safety.
*/
ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */ ia = in6ifa_ifpforlinklocal(ifp, 0); /* ia must not be NULL */
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
if (!ia) { if (!ia) {
@ -497,10 +486,6 @@ in6_ifattach_linklocal(ifp, altifp)
/* NOTREACHED */ /* NOTREACHED */
} }
#endif #endif
if (in6if_do_dad(ifp) && (ifp->if_flags & IFF_POINTOPOINT) == 0) {
ia->ia6_flags &= ~IN6_IFF_NODAD;
ia->ia6_flags |= IN6_IFF_TENTATIVE;
}
/* /*
* Make the link-local prefix (fe80::%link/64) as on-link. * Make the link-local prefix (fe80::%link/64) as on-link.
@ -513,7 +498,6 @@ in6_ifattach_linklocal(ifp, altifp)
pr0.ndpr_ifp = ifp; pr0.ndpr_ifp = ifp;
/* this should be 64 at this moment. */ /* this should be 64 at this moment. */
pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL); pr0.ndpr_plen = in6_mask2len(&ifra.ifra_prefixmask.sin6_addr, NULL);
pr0.ndpr_mask = ifra.ifra_prefixmask.sin6_addr;
pr0.ndpr_prefix = ifra.ifra_addr; pr0.ndpr_prefix = ifra.ifra_addr;
/* apply the mask for safety. (nd6_prelist_add will apply it again) */ /* apply the mask for safety. (nd6_prelist_add will apply it again) */
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
@ -588,7 +572,7 @@ in6_ifattach_loopback(ifp)
* We are sure that this is a newly assigned address, so we can set * We are sure that this is a newly assigned address, so we can set
* NULL to the 3rd arg. * NULL to the 3rd arg.
*/ */
if ((error = in6_update_ifa(ifp, &ifra, NULL)) != 0) { if ((error = in6_update_ifa(ifp, &ifra, NULL, 0)) != 0) {
nd6log((LOG_ERR, "in6_ifattach_loopback: failed to configure " nd6log((LOG_ERR, "in6_ifattach_loopback: failed to configure "
"the loopback address on %s (errno=%d)\n", "the loopback address on %s (errno=%d)\n",
if_name(ifp), error)); if_name(ifp), error));
@ -854,7 +838,7 @@ in6_ifdetach(ifp)
} }
} }
void int
in6_get_tmpifid(ifp, retbuf, baseid, generate) in6_get_tmpifid(ifp, retbuf, baseid, generate)
struct ifnet *ifp; struct ifnet *ifp;
u_int8_t *retbuf; u_int8_t *retbuf;
@ -878,6 +862,8 @@ in6_get_tmpifid(ifp, retbuf, baseid, generate)
ndi->randomid); ndi->randomid);
} }
bcopy(ndi->randomid, retbuf, 8); bcopy(ndi->randomid, retbuf, 8);
return (0);
} }
void void

View File

@ -36,7 +36,7 @@
#ifdef _KERNEL #ifdef _KERNEL
void in6_ifattach __P((struct ifnet *, struct ifnet *)); void in6_ifattach __P((struct ifnet *, struct ifnet *));
void in6_ifdetach __P((struct ifnet *)); void in6_ifdetach __P((struct ifnet *));
void in6_get_tmpifid __P((struct ifnet *, u_int8_t *, const u_int8_t *, int)); int in6_get_tmpifid __P((struct ifnet *, u_int8_t *, const u_int8_t *, int));
void in6_tmpaddrtimer __P((void *)); void in6_tmpaddrtimer __P((void *));
int in6_get_hw_ifid __P((struct ifnet *, struct in6_addr *)); int in6_get_hw_ifid __P((struct ifnet *, struct in6_addr *));
int in6_nigroup __P((struct ifnet *, const char *, int, struct in6_addr *)); int in6_nigroup __P((struct ifnet *, const char *, int, struct in6_addr *));

View File

@ -108,7 +108,10 @@ struct in6_ifaddr {
int ia6_flags; int ia6_flags;
struct in6_addrlifetime ia6_lifetime; struct in6_addrlifetime ia6_lifetime;
struct ifprefix *ia6_ifpr; /* back pointer to ifprefix */ time_t ia6_createtime; /* the creation time of this address, which is
* currently used for temporary addresses only.
*/
time_t ia6_updatetime;
/* back pointer to the ND prefix (for autoconfigured addresses only) */ /* back pointer to the ND prefix (for autoconfigured addresses only) */
struct nd_prefix *ia6_ndpr; struct nd_prefix *ia6_ndpr;
@ -518,9 +521,16 @@ struct in6_multi {
u_int in6m_refcount; /* # membership claims by sockets */ u_int in6m_refcount; /* # membership claims by sockets */
u_int in6m_state; /* state of the membership */ u_int in6m_state; /* state of the membership */
u_int in6m_timer; /* MLD6 listener report timer */ u_int in6m_timer; /* MLD6 listener report timer */
struct timeval in6m_timer_expire; /* when the timer expires */
struct callout *in6m_timer_ch;
}; };
#define IN6M_TIMER_UNDEF -1
#ifdef _KERNEL #ifdef _KERNEL
/* flags to in6_update_ifa */
#define IN6_IFAUPDATE_DADDELAY 0x1 /* first time to configure an address */
extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead; extern LIST_HEAD(in6_multihead, in6_multi) in6_multihead;
/* /*
@ -579,15 +589,15 @@ do { \
} while(0) } while(0)
struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *, struct in6_multi *in6_addmulti __P((struct in6_addr *, struct ifnet *,
int *)); int *, int));
void in6_delmulti __P((struct in6_multi *)); void in6_delmulti __P((struct in6_multi *));
struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *); struct in6_multi_mship *in6_joingroup(struct ifnet *, struct in6_addr *, int *, int);
int in6_leavegroup(struct in6_multi_mship *); int in6_leavegroup(struct in6_multi_mship *);
int in6_mask2len __P((struct in6_addr *, u_char *)); int in6_mask2len __P((struct in6_addr *, u_char *));
int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *, int in6_control __P((struct socket *, u_long, caddr_t, struct ifnet *,
struct thread *)); struct thread *));
int in6_update_ifa __P((struct ifnet *, struct in6_aliasreq *, int in6_update_ifa __P((struct ifnet *, struct in6_aliasreq *,
struct in6_ifaddr *)); struct in6_ifaddr *, int));
void in6_purgeaddr __P((struct ifaddr *)); void in6_purgeaddr __P((struct ifaddr *));
int in6if_do_dad __P((struct ifnet *)); int in6if_do_dad __P((struct ifnet *));
void in6_purgeif __P((struct ifnet *)); void in6_purgeif __P((struct ifnet *));
@ -595,6 +605,7 @@ void in6_savemkludge __P((struct in6_ifaddr *));
void *in6_domifattach __P((struct ifnet *)); void *in6_domifattach __P((struct ifnet *));
void in6_domifdetach __P((struct ifnet *, void *)); void in6_domifdetach __P((struct ifnet *, void *));
void in6_setmaxmtu __P((void)); void in6_setmaxmtu __P((void));
int in6_if2idlen __P((struct ifnet *));
void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *)); void in6_restoremkludge __P((struct in6_ifaddr *, struct ifnet *));
void in6_purgemkludge __P((struct ifnet *)); void in6_purgemkludge __P((struct ifnet *));
struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int)); struct in6_ifaddr *in6ifa_ifpforlinklocal __P((struct ifnet *, int));

View File

@ -2764,16 +2764,9 @@ ip6_setmoptions(optname, im6op, m)
* Everything looks good; add a new record to the multicast * Everything looks good; add a new record to the multicast
* address list for the given interface. * address list for the given interface.
*/ */
imm = malloc(sizeof(*imm), M_IP6MADDR, M_WAITOK); imm = in6_joingroup(ifp, &mreq->ipv6mr_multiaddr, &error, 0);
if (imm == NULL) { if (imm == NULL)
error = ENOBUFS;
break; break;
}
if ((imm->i6mm_maddr =
in6_addmulti(&mreq->ipv6mr_multiaddr, ifp, &error)) == NULL) {
free(imm, M_IP6MADDR);
break;
}
LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain); LIST_INSERT_HEAD(&im6o->im6o_memberships, imm, i6mm_chain);
break; break;
@ -3206,7 +3199,9 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
/* turn off the previous option, then set the new option. */ /* turn off the previous option, then set the new option. */
ip6_clearpktopts(opt, IPV6_NEXTHOP); ip6_clearpktopts(opt, IPV6_NEXTHOP);
opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_WAITOK); opt->ip6po_nexthop = malloc(*buf, M_IP6OPT, M_NOWAIT);
if (opt->ip6po_nexthop == NULL)
return (ENOBUFS);
bcopy(buf, opt->ip6po_nexthop, *buf); bcopy(buf, opt->ip6po_nexthop, *buf);
break; break;
@ -3239,7 +3234,9 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
/* turn off the previous option, then set the new option. */ /* turn off the previous option, then set the new option. */
ip6_clearpktopts(opt, IPV6_HOPOPTS); ip6_clearpktopts(opt, IPV6_HOPOPTS);
opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_WAITOK); opt->ip6po_hbh = malloc(hbhlen, M_IP6OPT, M_NOWAIT);
if (opt->ip6po_hbh == NULL)
return (ENOBUFS);
bcopy(hbh, opt->ip6po_hbh, hbhlen); bcopy(hbh, opt->ip6po_hbh, hbhlen);
break; break;
@ -3301,7 +3298,9 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
/* turn off the previous option, then set the new option. */ /* turn off the previous option, then set the new option. */
ip6_clearpktopts(opt, optname); ip6_clearpktopts(opt, optname);
*newdest = malloc(destlen, M_IP6OPT, M_WAITOK); *newdest = malloc(destlen, M_IP6OPT, M_NOWAIT);
if (newdest == NULL)
return (ENOBUFS);
bcopy(dest, *newdest, destlen); bcopy(dest, *newdest, destlen);
break; break;
@ -3341,7 +3340,9 @@ ip6_setpktopt(optname, buf, len, opt, priv, sticky, cmsg, uproto)
/* turn off the previous option */ /* turn off the previous option */
ip6_clearpktopts(opt, IPV6_RTHDR); ip6_clearpktopts(opt, IPV6_RTHDR);
opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_WAITOK); opt->ip6po_rthdr = malloc(rthlen, M_IP6OPT, M_NOWAIT);
if (opt->ip6po_rthdr == NULL)
return (ENOBUFS);
bcopy(rth, opt->ip6po_rthdr, rthlen); bcopy(rth, opt->ip6po_rthdr, rthlen);
break; break;

View File

@ -74,12 +74,15 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/protosw.h> #include <sys/protosw.h>
#include <sys/syslog.h> #include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/callout.h>
#include <sys/malloc.h> #include <sys/malloc.h>
#include <net/if.h> #include <net/if.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/in_var.h> #include <netinet/in_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet6/ip6_var.h> #include <netinet6/ip6_var.h>
#include <netinet6/scope6_var.h> #include <netinet6/scope6_var.h>
@ -101,9 +104,12 @@
#define MLD_UNSOLICITED_REPORT_INTERVAL 10 #define MLD_UNSOLICITED_REPORT_INTERVAL 10
static struct ip6_pktopts ip6_opts; static struct ip6_pktopts ip6_opts;
static int mld6_timers_are_running;
static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *); static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
static void mld_starttimer(struct in6_multi *);
static void mld_stoptimer(struct in6_multi *);
static void mld_timeo(struct in6_multi *);
static u_long mld_timerresid(struct in6_multi *);
void void
mld6_init() mld6_init()
@ -112,8 +118,6 @@ mld6_init()
struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf; struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD); u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
mld6_timers_are_running = 0;
/* ip6h_nxt will be fill in later */ /* ip6h_nxt will be fill in later */
hbh->ip6h_len = 0; /* (8 >> 3) - 1 */ hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
@ -128,6 +132,84 @@ mld6_init()
ip6_opts.ip6po_hbh = hbh; ip6_opts.ip6po_hbh = hbh;
} }
static void
mld_starttimer(in6m)
struct in6_multi *in6m;
{
struct timeval now;
microtime(&now);
in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
in6m->in6m_timer_expire.tv_usec = now.tv_usec +
(in6m->in6m_timer % hz) * (1000000 / hz);
if (in6m->in6m_timer_expire.tv_usec > 1000000) {
in6m->in6m_timer_expire.tv_sec++;
in6m->in6m_timer_expire.tv_usec -= 1000000;
}
/* start or restart the timer */
callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer,
(void (*) __P((void *)))mld_timeo, in6m);
}
static void
mld_stoptimer(in6m)
struct in6_multi *in6m;
{
if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
return;
callout_stop(in6m->in6m_timer_ch);
in6m->in6m_timer = IN6M_TIMER_UNDEF;
}
static void
mld_timeo(in6m)
struct in6_multi *in6m;
{
int s = splnet();
in6m->in6m_timer = IN6M_TIMER_UNDEF;
callout_stop(in6m->in6m_timer_ch);
switch (in6m->in6m_state) {
case MLD_REPORTPENDING:
mld6_start_listening(in6m);
break;
default:
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
break;
}
splx(s);
}
static u_long
mld_timerresid(in6m)
struct in6_multi *in6m;
{
struct timeval now, diff;
microtime(&now);
if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
(now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
return (0);
}
diff = in6m->in6m_timer_expire;
diff.tv_sec -= now.tv_sec;
diff.tv_usec -= now.tv_usec;
if (diff.tv_usec < 0) {
diff.tv_sec--;
diff.tv_usec += 1000000;
}
/* return the remaining time in milliseconds */
return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000);
}
void void
mld6_start_listening(in6m) mld6_start_listening(in6m)
struct in6_multi *in6m; struct in6_multi *in6m;
@ -155,11 +237,11 @@ mld6_start_listening(in6m)
in6m->in6m_state = MLD_OTHERLISTENER; in6m->in6m_state = MLD_OTHERLISTENER;
} else { } else {
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
in6m->in6m_timer = in6m->in6m_timer = arc4random() %
MLD_RANDOM_DELAY(MLD_UNSOLICITED_REPORT_INTERVAL * MLD_UNSOLICITED_REPORT_INTERVAL * hz;
PR_FASTHZ);
in6m->in6m_state = MLD_IREPORTEDLAST; in6m->in6m_state = MLD_IREPORTEDLAST;
mld6_timers_are_running = 1;
mld_starttimer(in6m);
} }
splx(s); splx(s);
} }
@ -276,6 +358,8 @@ mld6_input(m, off)
* - Use the value specified in the query message as * - Use the value specified in the query message as
* the maximum timeout. * the maximum timeout.
*/ */
timer = ntohs(mldh->mld_maxdelay);
IFP_TO_IA6(ifp, ia); IFP_TO_IA6(ifp, ia);
if (ia == NULL) if (ia == NULL)
break; break;
@ -305,16 +389,17 @@ mld6_input(m, off)
IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) { IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) {
if (timer == 0) { if (timer == 0) {
/* send a report immediately */ /* send a report immediately */
mld_stoptimer(in6m);
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
NULL); NULL);
in6m->in6m_timer = 0; /* reset timer */ in6m->in6m_timer = 0; /* reset timer */
in6m->in6m_state = MLD_IREPORTEDLAST; in6m->in6m_state = MLD_IREPORTEDLAST;
} }
else if (in6m->in6m_timer == 0 || /*idle state*/ else if (in6m->in6m_timer == 0 || /*idle state*/
in6m->in6m_timer > timer) { mld_timerresid(in6m) > (u_long)timer) {
in6m->in6m_timer = in6m->in6m_timer = arc4random() %
MLD_RANDOM_DELAY(timer); (int)((long)(timer * hz) / 1000);
mld6_timers_are_running = 1; mld_starttimer(in6m);
} }
} }
} }
@ -355,39 +440,6 @@ mld6_input(m, off)
m_freem(m); m_freem(m);
} }
void
mld6_fasttimeo()
{
struct in6_multi *in6m;
struct in6_multistep step;
int s;
/*
* Quick check to see if any work needs to be done, in order
* to minimize the overhead of fasttimo processing.
*/
if (!mld6_timers_are_running)
return;
s = splnet();
mld6_timers_are_running = 0;
IN6_FIRST_MULTI(step, in6m);
while (in6m != NULL) {
if (in6m->in6m_timer == 0) {
/* do nothing */
} else if (--in6m->in6m_timer == 0) {
mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
in6m->in6m_state = MLD_IREPORTEDLAST;
} else {
mld6_timers_are_running = 1;
}
IN6_NEXT_MULTI(step, in6m);
}
splx(s);
}
static void static void
mld6_sendpkt(in6m, type, dst) mld6_sendpkt(in6m, type, dst)
struct in6_multi *in6m; struct in6_multi *in6m;
@ -492,10 +544,10 @@ mld6_sendpkt(in6m, type, dst)
* and the number of source is not 0. * and the number of source is not 0.
*/ */
struct in6_multi * struct in6_multi *
in6_addmulti(maddr6, ifp, errorp) in6_addmulti(maddr6, ifp, errorp, delay)
struct in6_addr *maddr6; struct in6_addr *maddr6;
struct ifnet *ifp; struct ifnet *ifp;
int *errorp; int *errorp, delay;
{ {
struct in6_multi *in6m; struct in6_multi *in6m;
struct ifmultiaddr *ifma; struct ifmultiaddr *ifma;
@ -542,8 +594,25 @@ in6_addmulti(maddr6, ifp, errorp)
in6m->in6m_refcount = 1; in6m->in6m_refcount = 1;
in6m->in6m_ifma = ifma; in6m->in6m_ifma = ifma;
ifma->ifma_protospec = in6m; ifma->ifma_protospec = in6m;
in6m->in6m_timer_ch = malloc(sizeof(*in6m->in6m_timer_ch), M_IP6MADDR,
M_NOWAIT);
if (in6m->in6m_timer_ch == NULL) {
free(in6m, M_IP6MADDR);
splx(s);
return (NULL);
}
LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry); LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
callout_init(in6m->in6m_timer_ch, 0);
in6m->in6m_timer = delay;
if (in6m->in6m_timer > 0) {
in6m->in6m_state = MLD_REPORTPENDING;
mld_starttimer(in6m);
splx(s);
return (in6m);
}
/* /*
* Let MLD6 know that we have joined a new IPv6 multicast * Let MLD6 know that we have joined a new IPv6 multicast
* group. * group.
@ -571,6 +640,7 @@ in6_delmulti(in6m)
mld6_stop_listening(in6m); mld6_stop_listening(in6m);
ifma->ifma_protospec = NULL; ifma->ifma_protospec = NULL;
LIST_REMOVE(in6m, in6m_entry); LIST_REMOVE(in6m, in6m_entry);
free(in6m->in6m_timer_ch, M_IP6MADDR);
free(in6m, M_IP6MADDR); free(in6m, M_IP6MADDR);
} }
/* XXX - should be separate API for when we have an ifma? */ /* XXX - should be separate API for when we have an ifma? */

View File

@ -42,6 +42,7 @@
*/ */
#define MLD_OTHERLISTENER 0 #define MLD_OTHERLISTENER 0
#define MLD_IREPORTEDLAST 1 #define MLD_IREPORTEDLAST 1
#define MLD_REPORTPENDING 2 /* implementation specific */
void mld6_init(void); void mld6_init(void);
void mld6_input(struct mbuf *, int); void mld6_input(struct mbuf *, int);

View File

@ -67,6 +67,8 @@
#include <netinet6/nd6.h> #include <netinet6/nd6.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
#include <sys/limits.h>
#include <net/net_osdep.h> #include <net/net_osdep.h>
#define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */ #define ND6_SLOWTIMER_INTERVAL (60 * 60) /* 1 hour */
@ -87,6 +89,7 @@ int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */
int nd6_maxndopt = 10; /* max # of ND options allowed */ int nd6_maxndopt = 10; /* max # of ND options allowed */
int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */
int nd6_maxqueuelen = 1; /* max # of packets cached in unresolved ND entries */
#ifdef ND6_DEBUG #ifdef ND6_DEBUG
int nd6_debug = 1; int nd6_debug = 1;
@ -109,6 +112,8 @@ static int nd6_is_new_addr_neighbor __P((struct sockaddr_in6 *,
static void nd6_setmtu0 __P((struct ifnet *, struct nd_ifinfo *)); static void nd6_setmtu0 __P((struct ifnet *, struct nd_ifinfo *));
static void nd6_slowtimo __P((void *)); static void nd6_slowtimo __P((void *));
static int regen_tmpaddr __P((struct in6_ifaddr *)); static int regen_tmpaddr __P((struct in6_ifaddr *));
static struct llinfo_nd6 *nd6_free __P((struct rtentry *, int));
static void nd6_llinfo_timer __P((void *));
struct callout nd6_slowtimo_ch; struct callout nd6_slowtimo_ch;
struct callout nd6_timer_ch; struct callout nd6_timer_ch;
@ -383,91 +388,104 @@ nd6_options(ndopts)
} }
/* /*
* ND6 timer routine to expire default route list and prefix list * ND6 timer routine to handle ND6 entries
*/ */
void void
nd6_timer(ignored_arg) nd6_llinfo_settimer(ln, tick)
void *ignored_arg;
{
int s;
struct llinfo_nd6 *ln; struct llinfo_nd6 *ln;
struct nd_defrouter *dr; long tick;
struct nd_prefix *pr; {
struct ifnet *ifp; if (tick < 0) {
struct in6_ifaddr *ia6, *nia6; ln->ln_expire = 0;
struct in6_addrlifetime *lt6; ln->ln_ntick = 0;
callout_stop(&ln->ln_timer_ch);
} else {
ln->ln_expire = time_second + tick / hz;
if (tick > INT_MAX) {
ln->ln_ntick = tick - INT_MAX;
callout_reset(&ln->ln_timer_ch, INT_MAX,
nd6_llinfo_timer, ln);
} else {
ln->ln_ntick = 0;
callout_reset(&ln->ln_timer_ch, tick,
nd6_llinfo_timer, ln);
}
}
}
s = splnet(); static void
callout_reset(&nd6_timer_ch, nd6_prune * hz, nd6_llinfo_timer(arg)
nd6_timer, NULL); void *arg;
{
ln = llinfo_nd6.ln_next; struct llinfo_nd6 *ln;
while (ln && ln != &llinfo_nd6) {
struct rtentry *rt; struct rtentry *rt;
struct sockaddr_in6 *dst; struct in6_addr *dst;
struct llinfo_nd6 *next = ln->ln_next; struct ifnet *ifp;
/* XXX: used for the DELAY case only: */
struct nd_ifinfo *ndi = NULL; struct nd_ifinfo *ndi = NULL;
if ((rt = ln->ln_rt) == NULL) { ln = (struct llinfo_nd6 *)arg;
ln = next;
continue;
}
if ((ifp = rt->rt_ifp) == NULL) {
ln = next;
continue;
}
ndi = ND_IFINFO(ifp);
dst = (struct sockaddr_in6 *)rt_key(rt);
if (ln->ln_expire > time_second) { if (ln->ln_ntick > 0) {
ln = next; if (ln->ln_ntick > INT_MAX) {
continue; ln->ln_ntick -= INT_MAX;
nd6_llinfo_settimer(ln, INT_MAX);
} else {
ln->ln_ntick = 0;
nd6_llinfo_settimer(ln, ln->ln_ntick);
} }
return;
}
if ((rt = ln->ln_rt) == NULL)
panic("ln->ln_rt == NULL");
if ((ifp = rt->rt_ifp) == NULL)
panic("ln->ln_rt->rt_ifp == NULL");
ndi = ND_IFINFO(ifp);
/* sanity check */ /* sanity check */
if (!rt)
panic("rt=0 in nd6_timer(ln=%p)", ln);
if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
panic("rt_llinfo(%p) is not equal to ln(%p)", panic("rt_llinfo(%p) is not equal to ln(%p)",
rt->rt_llinfo, ln); rt->rt_llinfo, ln);
if (!dst) if (rt_key(rt) == NULL)
panic("dst=0 in nd6_timer(ln=%p)", ln); panic("rt key is NULL in nd6_timer(ln=%p)", ln);
dst = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
switch (ln->ln_state) { switch (ln->ln_state) {
case ND6_LLINFO_INCOMPLETE: case ND6_LLINFO_INCOMPLETE:
if (ln->ln_asked < nd6_mmaxtries) { if (ln->ln_asked < nd6_mmaxtries) {
ln->ln_asked++; ln->ln_asked++;
ln->ln_expire = time_second + nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
ND_IFINFO(ifp)->retrans / 1000; nd6_ns_output(ifp, NULL, dst, ln, 0);
nd6_ns_output(ifp, NULL, &dst->sin6_addr,
ln, 0);
} else { } else {
struct mbuf *m = ln->ln_hold; struct mbuf *m = ln->ln_hold;
if (m) { if (m) {
/* /*
* assuming every packet in ln_hold has * assuming every packet in ln_hold has the
* the same IP header * same IP header
*/ */
ln->ln_hold = NULL; ln->ln_hold = NULL;
icmp6_error2(m, ICMP6_DST_UNREACH, icmp6_error2(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0, ICMP6_DST_UNREACH_ADDR, 0, rt->rt_ifp);
rt->rt_ifp);
} }
next = nd6_free(rt); if (rt)
(void)nd6_free(rt, 0);
ln = NULL;
} }
break; break;
case ND6_LLINFO_REACHABLE: case ND6_LLINFO_REACHABLE:
if (ln->ln_expire) { if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_state = ND6_LLINFO_STALE; ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
} }
break; break;
case ND6_LLINFO_STALE: case ND6_LLINFO_STALE:
/* Garbage Collection(RFC 2461 5.3) */ /* Garbage Collection(RFC 2461 5.3) */
if (ln->ln_expire) if (!ND6_LLINFO_PERMANENT(ln)) {
next = nd6_free(rt); (void)nd6_free(rt, 1);
ln = NULL;
}
break; break;
case ND6_LLINFO_DELAY: case ND6_LLINFO_DELAY:
@ -475,32 +493,45 @@ nd6_timer(ignored_arg)
/* We need NUD */ /* We need NUD */
ln->ln_asked = 1; ln->ln_asked = 1;
ln->ln_state = ND6_LLINFO_PROBE; ln->ln_state = ND6_LLINFO_PROBE;
ln->ln_expire = time_second + nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
ndi->retrans / 1000; nd6_ns_output(ifp, dst, dst, ln, 0);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr,
ln, 0);
} else { } else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */ ln->ln_state = ND6_LLINFO_STALE; /* XXX */
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
} }
break; break;
case ND6_LLINFO_PROBE: case ND6_LLINFO_PROBE:
if (ln->ln_asked < nd6_umaxtries) { if (ln->ln_asked < nd6_umaxtries) {
ln->ln_asked++; ln->ln_asked++;
ln->ln_expire = time_second + nd6_llinfo_settimer(ln, (long)ndi->retrans * hz / 1000);
ND_IFINFO(ifp)->retrans / 1000; nd6_ns_output(ifp, dst, dst, ln, 0);
nd6_ns_output(ifp, &dst->sin6_addr,
&dst->sin6_addr, ln, 0);
} else { } else {
next = nd6_free(rt); (void)nd6_free(rt, 0);
ln = NULL;
} }
break; break;
} }
ln = next;
} }
/*
* ND6 timer routine to expire default route list and prefix list
*/
void
nd6_timer(ignored_arg)
void *ignored_arg;
{
int s;
struct nd_defrouter *dr;
struct nd_prefix *pr;
struct in6_ifaddr *ia6, *nia6;
struct in6_addrlifetime *lt6;
callout_reset(&nd6_timer_ch, nd6_prune * hz,
nd6_timer, NULL);
/* expire default router list */ /* expire default router list */
s = splnet();
dr = TAILQ_FIRST(&nd_defrouter); dr = TAILQ_FIRST(&nd_defrouter);
while (dr) { while (dr) {
if (dr->expire && dr->expire < time_second) { if (dr->expire && dr->expire < time_second) {
@ -594,7 +625,8 @@ nd6_timer(ignored_arg)
* since pltime is just for autoconf, pltime processing for * since pltime is just for autoconf, pltime processing for
* prefix is not necessary. * prefix is not necessary.
*/ */
if (pr->ndpr_expire && pr->ndpr_expire < time_second) { if (pr->ndpr_vltime != ND6_INFINITE_LIFETIME &&
time_second - pr->ndpr_lastupdate > pr->ndpr_vltime) {
struct nd_prefix *t; struct nd_prefix *t;
t = pr->ndpr_next; t = pr->ndpr_next;
@ -663,7 +695,7 @@ regen_tmpaddr(ia6)
if (public_ifa6 != NULL) { if (public_ifa6 != NULL) {
int e; int e;
if ((e = in6_tmpifadd(public_ifa6, 0)) != 0) { if ((e = in6_tmpifadd(public_ifa6, 0, 0)) != 0) {
log(LOG_NOTICE, "regen_tmpaddr: failed to create a new" log(LOG_NOTICE, "regen_tmpaddr: failed to create a new"
" tmp addr,errno=%d\n", e); " tmp addr,errno=%d\n", e);
return (-1); return (-1);
@ -683,21 +715,29 @@ nd6_purge(ifp)
struct ifnet *ifp; struct ifnet *ifp;
{ {
struct llinfo_nd6 *ln, *nln; struct llinfo_nd6 *ln, *nln;
struct nd_defrouter *dr, *ndr, drany; struct nd_defrouter *dr, *ndr;
struct nd_prefix *pr, *npr; struct nd_prefix *pr, *npr;
/* Nuke default router list entries toward ifp */
if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) {
/* /*
* The first entry of the list may be stored in * Nuke default router list entries toward ifp.
* the routing table, so we'll delete it later. * We defer removal of default router list entries that is installed
* in the routing table, in order to keep additional side effects as
* small as possible.
*/ */
for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = ndr) { for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
ndr = TAILQ_NEXT(dr, dr_entry); ndr = TAILQ_NEXT(dr, dr_entry);
if (dr->installed)
continue;
if (dr->ifp == ifp) if (dr->ifp == ifp)
defrtrlist_del(dr); defrtrlist_del(dr);
} }
dr = TAILQ_FIRST(&nd_defrouter);
for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = ndr) {
ndr = TAILQ_NEXT(dr, dr_entry);
if (!dr->installed)
continue;
if (dr->ifp == ifp) if (dr->ifp == ifp)
defrtrlist_del(dr); defrtrlist_del(dr);
} }
@ -706,6 +746,14 @@ nd6_purge(ifp)
for (pr = nd_prefix.lh_first; pr; pr = npr) { for (pr = nd_prefix.lh_first; pr; pr = npr) {
npr = pr->ndpr_next; npr = pr->ndpr_next;
if (pr->ndpr_ifp == ifp) { if (pr->ndpr_ifp == ifp) {
/*
* Because if_detach() does *not* release prefixes
* while purging addresses the reference count will
* still be above zero. We therefore reset it to
* make sure that the prefix really gets purged.
*/
pr->ndpr_refcnt = 0;
/* /*
* Previously, pr->ndpr_addr is removed as well, * Previously, pr->ndpr_addr is removed as well,
* but I strongly believe we don't have to do it. * but I strongly believe we don't have to do it.
@ -724,8 +772,6 @@ nd6_purge(ifp)
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */
/* refresh default router list */ /* refresh default router list */
bzero(&drany, sizeof(drany));
defrouter_delreq(&drany, 0);
defrouter_select(); defrouter_select();
} }
@ -746,7 +792,7 @@ nd6_purge(ifp)
rt->rt_gateway->sa_family == AF_LINK) { rt->rt_gateway->sa_family == AF_LINK) {
sdl = (struct sockaddr_dl *)rt->rt_gateway; sdl = (struct sockaddr_dl *)rt->rt_gateway;
if (sdl->sdl_index == ifp->if_index) if (sdl->sdl_index == ifp->if_index)
nln = nd6_free(rt); nln = nd6_free(rt, 0);
} }
ln = nln; ln = nln;
} }
@ -833,6 +879,10 @@ nd6_lookup(addr6, create, ifp)
* own address on a non-loopback interface. Instead, we should * own address on a non-loopback interface. Instead, we should
* use rt->rt_ifa->ifa_ifp, which would specify the REAL * use rt->rt_ifa->ifa_ifp, which would specify the REAL
* interface. * interface.
* Note also that ifa_ifp and ifp may differ when we connect two
* interfaces to a same link, install a link prefix to an interface,
* and try to install a neighbor cache on an interface that does not
* have a route to the prefix.
*/ */
if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 || if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL || rt->rt_gateway->sa_family != AF_LINK || rt->rt_llinfo == NULL ||
@ -861,6 +911,7 @@ nd6_is_new_addr_neighbor(addr, ifp)
struct ifnet *ifp; struct ifnet *ifp;
{ {
struct nd_prefix *pr; struct nd_prefix *pr;
struct ifaddr *dstaddr;
/* /*
* A link-local address is always a neighbor. * A link-local address is always a neighbor.
@ -903,6 +954,14 @@ nd6_is_new_addr_neighbor(addr, ifp)
return (1); return (1);
} }
/*
* If the address is assigned on the node of the other side of
* a p2p interface, the address should be a neighbor.
*/
dstaddr = ifa_ifwithdstaddr((struct sockaddr *)addr);
if ((dstaddr != NULL) && (dstaddr->ifa_ifp == ifp))
return (1);
/* /*
* If the default router list is empty, all addresses are regarded * If the default router list is empty, all addresses are regarded
* as on-link, and thus, as a neighbor. * as on-link, and thus, as a neighbor.
@ -943,10 +1002,14 @@ nd6_is_addr_neighbor(addr, ifp)
/* /*
* Free an nd6 llinfo entry. * Free an nd6 llinfo entry.
* Since the function would cause significant changes in the kernel, DO NOT
* make it global, unless you have a strong reason for the change, and are sure
* that the change is safe.
*/ */
struct llinfo_nd6 * static struct llinfo_nd6 *
nd6_free(rt) nd6_free(rt, gc)
struct rtentry *rt; struct rtentry *rt;
int gc;
{ {
struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next;
struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
@ -957,12 +1020,38 @@ nd6_free(rt)
* even though it is not harmful, it was not really necessary. * even though it is not harmful, it was not really necessary.
*/ */
if (!ip6_forwarding && ip6_accept_rtadv) { /* XXX: too restrictive? */ /* cancel timer */
nd6_llinfo_settimer(ln, -1);
if (!ip6_forwarding) {
int s; int s;
s = splnet(); s = splnet();
dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, dr = defrouter_lookup(&((struct sockaddr_in6 *)rt_key(rt))->sin6_addr,
rt->rt_ifp); rt->rt_ifp);
if (dr != NULL && dr->expire &&
ln->ln_state == ND6_LLINFO_STALE && gc) {
/*
* If the reason for the deletion is just garbage
* collection, and the neighbor is an active default
* router, do not delete it. Instead, reset the GC
* timer using the router's lifetime.
* Simply deleting the entry would affect default
* router selection, which is not necessarily a good
* thing, especially when we're using router preference
* values.
* XXX: the check for ln_state would be redundant,
* but we intentionally keep it just in case.
*/
if (dr->expire > time_second)
nd6_llinfo_settimer(ln,
(dr->expire - time_second) * hz);
else
nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
splx(s);
return (ln->ln_next);
}
if (ln->ln_router || dr) { if (ln->ln_router || dr) {
/* /*
* rt6_flush must be called whether or not the neighbor * rt6_flush must be called whether or not the neighbor
@ -996,20 +1085,11 @@ nd6_free(rt)
*/ */
pfxlist_onlink_check(); pfxlist_onlink_check();
if (dr == TAILQ_FIRST(&nd_defrouter)) {
/* /*
* It is used as the current default router, * refresh default router list
* so we have to move it to the end of the
* list and choose a new one.
* XXX: it is not very efficient if this is
* the only router.
*/ */
TAILQ_REMOVE(&nd_defrouter, dr, dr_entry);
TAILQ_INSERT_TAIL(&nd_defrouter, dr, dr_entry);
defrouter_select(); defrouter_select();
} }
}
splx(s); splx(s);
} }
@ -1079,9 +1159,10 @@ nd6_nud_hint(rt, dst6, force)
} }
ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_state = ND6_LLINFO_REACHABLE;
if (ln->ln_expire) if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_expire = time_second + nd6_llinfo_settimer(ln,
ND_IFINFO(rt->rt_ifp)->reachable; (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
}
} }
void void
@ -1143,12 +1224,13 @@ nd6_rtrequest(req, rt, info)
* SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff) * SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
* rt->rt_flags |= RTF_CLONING; * rt->rt_flags |= RTF_CLONING;
*/ */
if (rt->rt_flags & (RTF_CLONING | RTF_LLINFO)) { if ((rt->rt_flags & RTF_CLONING) ||
((rt->rt_flags & RTF_LLINFO) && ln == NULL)) {
/* /*
* Case 1: This route should come from * Case 1: This route should come from a route to
* a route to interface. RTF_LLINFO flag is set * interface (RTF_CLONING case) or the route should be
* for a host route whose destination should be * treated as on-link but is currently not
* treated as on-link. * (RTF_LLINFO && ln == NULL case).
*/ */
rt_setgate(rt, rt_key(rt), rt_setgate(rt, rt_key(rt),
(struct sockaddr *)&null_sdl); (struct sockaddr *)&null_sdl);
@ -1156,11 +1238,7 @@ nd6_rtrequest(req, rt, info)
SDL(gate)->sdl_type = ifp->if_type; SDL(gate)->sdl_type = ifp->if_type;
SDL(gate)->sdl_index = ifp->if_index; SDL(gate)->sdl_index = ifp->if_index;
if (ln) if (ln)
ln->ln_expire = time_second; nd6_llinfo_settimer(ln, 0);
if (ln && ln->ln_expire == 0) {
/* kludge for desktops */
ln->ln_expire = 1;
}
if ((rt->rt_flags & RTF_CLONING) != 0) if ((rt->rt_flags & RTF_CLONING) != 0)
break; break;
} }
@ -1215,6 +1293,8 @@ nd6_rtrequest(req, rt, info)
nd6_allocated++; nd6_allocated++;
bzero(ln, sizeof(*ln)); bzero(ln, sizeof(*ln));
ln->ln_rt = rt; ln->ln_rt = rt;
callout_init(&ln->ln_timer_ch, 0);
/* this is required for "ndp" command. - shin */ /* this is required for "ndp" command. - shin */
if (req == RTM_ADD) { if (req == RTM_ADD) {
/* /*
@ -1230,7 +1310,7 @@ nd6_rtrequest(req, rt, info)
* initialized in rtrequest(), so rt_expire is 0. * initialized in rtrequest(), so rt_expire is 0.
*/ */
ln->ln_state = ND6_LLINFO_NOSTATE; ln->ln_state = ND6_LLINFO_NOSTATE;
ln->ln_expire = time_second; nd6_llinfo_settimer(ln, 0);
} }
rt->rt_flags |= RTF_LLINFO; rt->rt_flags |= RTF_LLINFO;
ln->ln_next = llinfo_nd6.ln_next; ln->ln_next = llinfo_nd6.ln_next;
@ -1246,7 +1326,7 @@ nd6_rtrequest(req, rt, info)
&SIN6(rt_key(rt))->sin6_addr); &SIN6(rt_key(rt))->sin6_addr);
if (ifa) { if (ifa) {
caddr_t macp = nd6_ifptomac(ifp); caddr_t macp = nd6_ifptomac(ifp);
ln->ln_expire = 0; nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0; ln->ln_byhint = 0;
if (macp) { if (macp) {
@ -1270,7 +1350,7 @@ nd6_rtrequest(req, rt, info)
} }
} }
} else if (rt->rt_flags & RTF_ANNOUNCE) { } else if (rt->rt_flags & RTF_ANNOUNCE) {
ln->ln_expire = 0; nd6_llinfo_settimer(ln, -1);
ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0; ln->ln_byhint = 0;
@ -1286,7 +1366,8 @@ nd6_rtrequest(req, rt, info)
llsol.s6_addr8[12] = 0xff; llsol.s6_addr8[12] = 0xff;
if (in6_setscope(&llsol, ifp, NULL)) if (in6_setscope(&llsol, ifp, NULL))
break; break;
if (!in6_addmulti(&llsol, ifp, &error)) { if (in6_addmulti(&llsol, ifp,
&error, 0) == NULL) {
nd6log((LOG_ERR, "%s: failed to join " nd6log((LOG_ERR, "%s: failed to join "
"%s (errno=%d)\n", if_name(ifp), "%s (errno=%d)\n", if_name(ifp),
ip6_sprintf(&llsol), error)); ip6_sprintf(&llsol), error));
@ -1320,6 +1401,7 @@ nd6_rtrequest(req, rt, info)
ln->ln_next->ln_prev = ln->ln_prev; ln->ln_next->ln_prev = ln->ln_prev;
ln->ln_prev->ln_next = ln->ln_next; ln->ln_prev->ln_next = ln->ln_next;
ln->ln_prev = NULL; ln->ln_prev = NULL;
nd6_llinfo_settimer(ln, -1);
rt->rt_llinfo = 0; rt->rt_llinfo = 0;
rt->rt_flags &= ~RTF_LLINFO; rt->rt_flags &= ~RTF_LLINFO;
if (ln->ln_hold) if (ln->ln_hold)
@ -1339,7 +1421,7 @@ nd6_ioctl(cmd, data, ifp)
struct in6_ndireq *ndi = (struct in6_ndireq *)data; struct in6_ndireq *ndi = (struct in6_ndireq *)data;
struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data; struct in6_nbrinfo *nbi = (struct in6_nbrinfo *)data;
struct in6_ndifreq *ndif = (struct in6_ndifreq *)data; struct in6_ndifreq *ndif = (struct in6_ndifreq *)data;
struct nd_defrouter *dr, any; struct nd_defrouter *dr;
struct nd_prefix *pr; struct nd_prefix *pr;
struct rtentry *rt; struct rtentry *rt;
int i = 0, error = 0; int i = 0, error = 0;
@ -1392,7 +1474,22 @@ nd6_ioctl(cmd, data, ifp)
oprl->prefix[i].vltime = pr->ndpr_vltime; oprl->prefix[i].vltime = pr->ndpr_vltime;
oprl->prefix[i].pltime = pr->ndpr_pltime; oprl->prefix[i].pltime = pr->ndpr_pltime;
oprl->prefix[i].if_index = pr->ndpr_ifp->if_index; oprl->prefix[i].if_index = pr->ndpr_ifp->if_index;
oprl->prefix[i].expire = pr->ndpr_expire; if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
oprl->prefix[i].expire = 0;
else {
time_t maxexpire;
/* XXX: we assume time_t is signed. */
maxexpire = (-1) &
~(1 << ((sizeof(maxexpire) * 8) - 1));
if (pr->ndpr_vltime <
maxexpire - pr->ndpr_lastupdate) {
oprl->prefix[i].expire =
pr->ndpr_lastupdate +
pr->ndpr_vltime;
} else
oprl->prefix[i].expire = maxexpire;
}
pfr = pr->ndpr_advrtrs.lh_first; pfr = pr->ndpr_advrtrs.lh_first;
j = 0; j = 0;
@ -1430,7 +1527,6 @@ nd6_ioctl(cmd, data, ifp)
break; break;
case SIOCGIFINFO_IN6: case SIOCGIFINFO_IN6:
ND = *ND_IFINFO(ifp); ND = *ND_IFINFO(ifp);
ND.linkmtu = IN6_LINKMTU(ifp);
break; break;
case SIOCSIFINFO_IN6: case SIOCSIFINFO_IN6:
/* /*
@ -1465,15 +1561,9 @@ nd6_ioctl(cmd, data, ifp)
break; break;
#undef ND #undef ND
case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */ case SIOCSNDFLUSH_IN6: /* XXX: the ioctl name is confusing... */
/* flush default router list */ /* sync kernel routing table with the default router list */
/* defrouter_reset();
* xxx sumikawa: should not delete route if default
* route equals to the top of default router list
*/
bzero(&any, sizeof(any));
defrouter_delreq(&any, 0);
defrouter_select(); defrouter_select();
/* xxx sumikawa: flush prefix list */
break; break;
case SIOCSPFXFLUSH_IN6: case SIOCSPFXFLUSH_IN6:
{ {
@ -1511,17 +1601,12 @@ nd6_ioctl(cmd, data, ifp)
struct nd_defrouter *dr, *next; struct nd_defrouter *dr, *next;
s = splnet(); s = splnet();
if ((dr = TAILQ_FIRST(&nd_defrouter)) != NULL) { defrouter_reset();
/* for (dr = TAILQ_FIRST(&nd_defrouter); dr; dr = next) {
* The first entry of the list may be stored in
* the routing table, so we'll delete it later.
*/
for (dr = TAILQ_NEXT(dr, dr_entry); dr; dr = next) {
next = TAILQ_NEXT(dr, dr_entry); next = TAILQ_NEXT(dr, dr_entry);
defrtrlist_del(dr); defrtrlist_del(dr);
} }
defrtrlist_del(TAILQ_FIRST(&nd_defrouter)); defrouter_select();
}
splx(s); splx(s);
break; break;
} }
@ -1613,7 +1698,7 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
return NULL; return NULL;
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) { if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail: fail:
(void)nd6_free(rt); (void)nd6_free(rt, 0);
return NULL; return NULL;
} }
ln = (struct llinfo_nd6 *)rt->rt_llinfo; ln = (struct llinfo_nd6 *)rt->rt_llinfo;
@ -1682,20 +1767,36 @@ nd6_cache_lladdr(ifp, from, lladdr, lladdrlen, type, code)
* we must set the timer now, although it is actually * we must set the timer now, although it is actually
* meaningless. * meaningless.
*/ */
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
if (ln->ln_hold) { if (ln->ln_hold) {
struct mbuf *m_hold, *m_hold_next;
for (m_hold = ln->ln_hold; m_hold;
m_hold = m_hold_next) {
struct mbuf *mpkt = NULL;
m_hold_next = m_hold->m_nextpkt;
mpkt = m_copym(m_hold, 0, M_COPYALL, M_DONTWAIT);
if (mpkt == NULL) {
m_freem(m_hold);
break;
}
mpkt->m_nextpkt = NULL;
/* /*
* we assume ifp is not a p2p here, so just * we assume ifp is not a p2p here, so
* set the 2nd argument as the 1st one. * just set the 2nd argument as the
* 1st one.
*/ */
nd6_output(ifp, ifp, ln->ln_hold, nd6_output(ifp, ifp, mpkt,
(struct sockaddr_in6 *)rt_key(rt), rt); (struct sockaddr_in6 *)rt_key(rt),
rt);
}
ln->ln_hold = NULL; ln->ln_hold = NULL;
} }
} else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {
/* probe right away */ /* probe right away */
ln->ln_expire = time_second; nd6_llinfo_settimer((void *)ln, 0);
} }
} }
@ -1789,7 +1890,6 @@ static void
nd6_slowtimo(ignored_arg) nd6_slowtimo(ignored_arg)
void *ignored_arg; void *ignored_arg;
{ {
int s = splnet();
struct nd_ifinfo *nd6if; struct nd_ifinfo *nd6if;
struct ifnet *ifp; struct ifnet *ifp;
@ -1811,7 +1911,6 @@ nd6_slowtimo(ignored_arg)
} }
} }
IFNET_RUNLOCK(); IFNET_RUNLOCK();
splx(s);
} }
#define senderr(e) { error = (e); goto bad;} #define senderr(e) { error = (e); goto bad;}
@ -1931,7 +2030,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && if ((ifp->if_flags & IFF_POINTOPOINT) != 0 &&
ln->ln_state < ND6_LLINFO_REACHABLE) { ln->ln_state < ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE; ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
} }
/* /*
@ -1944,7 +2043,7 @@ nd6_output(ifp, origifp, m0, dst, rt0)
if (ln->ln_state == ND6_LLINFO_STALE) { if (ln->ln_state == ND6_LLINFO_STALE) {
ln->ln_asked = 0; ln->ln_asked = 0;
ln->ln_state = ND6_LLINFO_DELAY; ln->ln_state = ND6_LLINFO_DELAY;
ln->ln_expire = time_second + nd6_delay; nd6_llinfo_settimer(ln, (long)nd6_delay * hz);
} }
/* /*
@ -1957,27 +2056,45 @@ nd6_output(ifp, origifp, m0, dst, rt0)
/* /*
* There is a neighbor cache entry, but no ethernet address * There is a neighbor cache entry, but no ethernet address
* response yet. Replace the held mbuf (if any) with this * response yet. Append this latest packet to the end of the
* latest one. * packet queue in the mbuf, unless the number of the packet
* * does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen,
* This code conforms to the rate-limiting rule described in Section * the oldest packet in the queue will be removed.
* 7.2.2 of RFC 2461, because the timer is set correctly after sending
* an NS below.
*/ */
if (ln->ln_state == ND6_LLINFO_NOSTATE) if (ln->ln_state == ND6_LLINFO_NOSTATE)
ln->ln_state = ND6_LLINFO_INCOMPLETE; ln->ln_state = ND6_LLINFO_INCOMPLETE;
if (ln->ln_hold) if (ln->ln_hold) {
m_freem(ln->ln_hold); struct mbuf *m_hold;
ln->ln_hold = m; int i;
if (ln->ln_expire) {
if (ln->ln_asked < nd6_mmaxtries && i = 0;
ln->ln_expire < time_second) { for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold->m_nextpkt) {
ln->ln_asked++; i++;
ln->ln_expire = time_second + if (m_hold->m_nextpkt == NULL) {
ND_IFINFO(ifp)->retrans / 1000; m_hold->m_nextpkt = m;
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0); break;
} }
} }
while (i >= nd6_maxqueuelen) {
m_hold = ln->ln_hold;
ln->ln_hold = ln->ln_hold->m_nextpkt;
m_free(m_hold);
i--;
}
} else {
ln->ln_hold = m;
}
/*
* If there has been no NS for the neighbor after entering the
* INCOMPLETE state, send the first solicitation.
*/
if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) {
ln->ln_asked++;
nd6_llinfo_settimer(ln,
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
}
return (0); return (0);
sendpkt: sendpkt:
@ -2128,6 +2245,8 @@ SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_DRLIST, nd6_drlist,
CTLFLAG_RD, nd6_sysctl_drlist, ""); CTLFLAG_RD, nd6_sysctl_drlist, "");
SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist, SYSCTL_NODE(_net_inet6_icmp6, ICMPV6CTL_ND6_PRLIST, nd6_prlist,
CTLFLAG_RD, nd6_sysctl_prlist, ""); CTLFLAG_RD, nd6_sysctl_prlist, "");
SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXQLEN, nd6_maxqueuelen,
CTLFLAG_RW, &nd6_maxqueuelen, 1, "");
static int static int
nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS) nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
@ -2151,12 +2270,7 @@ nd6_sysctl_drlist(SYSCTL_HANDLER_ARGS)
d->rtaddr.sin6_family = AF_INET6; d->rtaddr.sin6_family = AF_INET6;
d->rtaddr.sin6_len = sizeof(d->rtaddr); d->rtaddr.sin6_len = sizeof(d->rtaddr);
d->rtaddr.sin6_addr = dr->rtaddr; d->rtaddr.sin6_addr = dr->rtaddr;
if (sa6_recoverscope(&d->rtaddr)) { sa6_recoverscope(&d->rtaddr);
log(LOG_ERR,
"scope error in router list (%s)\n",
ip6_sprintf(&d->rtaddr.sin6_addr));
/* XXX: press on... */
}
d->flags = dr->flags; d->flags = dr->flags;
d->rtlifetime = dr->rtlifetime; d->rtlifetime = dr->rtlifetime;
d->expire = dr->expire; d->expire = dr->expire;
@ -2209,7 +2323,21 @@ nd6_sysctl_prlist(SYSCTL_HANDLER_ARGS)
p->vltime = pr->ndpr_vltime; p->vltime = pr->ndpr_vltime;
p->pltime = pr->ndpr_pltime; p->pltime = pr->ndpr_pltime;
p->if_index = pr->ndpr_ifp->if_index; p->if_index = pr->ndpr_ifp->if_index;
p->expire = pr->ndpr_expire; if (pr->ndpr_vltime == ND6_INFINITE_LIFETIME)
p->expire = 0;
else {
time_t maxexpire;
/* XXX: we assume time_t is signed. */
maxexpire = (-1) &
~(1 << ((sizeof(maxexpire) * 8) - 1));
if (pr->ndpr_vltime <
maxexpire - pr->ndpr_lastupdate) {
p->expire = pr->ndpr_lastupdate +
pr->ndpr_vltime;
} else
p->expire = maxexpire;
}
p->refcnt = pr->ndpr_refcnt; p->refcnt = pr->ndpr_refcnt;
p->flags = pr->ndpr_stateflags; p->flags = pr->ndpr_stateflags;
p->origin = PR_ORIG_RA; p->origin = PR_ORIG_RA;

View File

@ -51,6 +51,9 @@ struct llinfo_nd6 {
short ln_state; /* reachability state */ short ln_state; /* reachability state */
short ln_router; /* 2^0: ND6 router bit */ short ln_router; /* 2^0: ND6 router bit */
int ln_byhint; /* # of times we made it reachable by UL hint */ int ln_byhint; /* # of times we made it reachable by UL hint */
long ln_ntick;
struct callout ln_timer_ch;
}; };
#define ND6_LLINFO_NOSTATE -2 #define ND6_LLINFO_NOSTATE -2
@ -69,6 +72,7 @@ struct llinfo_nd6 {
#define ND6_LLINFO_PROBE 4 #define ND6_LLINFO_PROBE 4
#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) #define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE)
#define ND6_LLINFO_PERMANENT(n) (((n)->ln_expire == 0) && ((n)->ln_state > ND6_LLINFO_INCOMPLETE))
struct nd_ifinfo { struct nd_ifinfo {
u_int32_t linkmtu; /* LinkMTU */ u_int32_t linkmtu; /* LinkMTU */
@ -92,6 +96,7 @@ struct nd_ifinfo {
#define ND6_IFF_IFDISABLED 0x8 /* IPv6 operation is disabled due to #define ND6_IFF_IFDISABLED 0x8 /* IPv6 operation is disabled due to
* DAD failure. (XXX: not ND-specific) * DAD failure. (XXX: not ND-specific)
*/ */
#define ND6_IFF_DONT_SET_IFROUTE 0x10
#ifdef _KERNEL #ifdef _KERNEL
#define ND_IFINFO(ifp) \ #define ND_IFINFO(ifp) \
@ -243,18 +248,36 @@ struct nd_defrouter {
u_short rtlifetime; u_short rtlifetime;
u_long expire; u_long expire;
struct ifnet *ifp; struct ifnet *ifp;
int installed; /* is installed into kernel routing table */
}; };
struct nd_prefixctl {
struct ifnet *ndpr_ifp;
/* prefix */
struct sockaddr_in6 ndpr_prefix;
u_char ndpr_plen;
u_int32_t ndpr_vltime; /* advertised valid lifetime */
u_int32_t ndpr_pltime; /* advertised preferred lifetime */
struct prf_ra ndpr_flags;
};
struct nd_prefix { struct nd_prefix {
struct ifnet *ndpr_ifp; struct ifnet *ndpr_ifp;
LIST_ENTRY(nd_prefix) ndpr_entry; LIST_ENTRY(nd_prefix) ndpr_entry;
struct sockaddr_in6 ndpr_prefix; /* prefix */ struct sockaddr_in6 ndpr_prefix; /* prefix */
struct in6_addr ndpr_mask; /* netmask derived from the prefix */ struct in6_addr ndpr_mask; /* netmask derived from the prefix */
struct in6_addr ndpr_addr; /* address that is derived from the prefix */
u_int32_t ndpr_vltime; /* advertised valid lifetime */ u_int32_t ndpr_vltime; /* advertised valid lifetime */
u_int32_t ndpr_pltime; /* advertised preferred lifetime */ u_int32_t ndpr_pltime; /* advertised preferred lifetime */
time_t ndpr_expire; /* expiration time of the prefix */ time_t ndpr_expire; /* expiration time of the prefix */
time_t ndpr_preferred; /* preferred time of the prefix */ time_t ndpr_preferred; /* preferred time of the prefix */
time_t ndpr_lastupdate; /* reception time of last advertisement */
struct prf_ra ndpr_flags; struct prf_ra ndpr_flags;
u_int32_t ndpr_stateflags; /* actual state flags */ u_int32_t ndpr_stateflags; /* actual state flags */
/* list of routers that advertise the prefix: */ /* list of routers that advertise the prefix: */
@ -268,12 +291,7 @@ struct nd_prefix {
#define ndpr_raf ndpr_flags #define ndpr_raf ndpr_flags
#define ndpr_raf_onlink ndpr_flags.onlink #define ndpr_raf_onlink ndpr_flags.onlink
#define ndpr_raf_auto ndpr_flags.autonomous #define ndpr_raf_auto ndpr_flags.autonomous
#define ndpr_raf_router ndpr_flags.router
/*
* We keep expired prefix for certain amount of time, for validation purposes.
* 1800s = MaxRtrAdvInterval
*/
#define NDPR_KEEP_EXPIRED (1800 * 2)
/* /*
* Message format for use in obtaining information about prefixes * Message format for use in obtaining information about prefixes
@ -301,9 +319,6 @@ struct inet6_ndpr_msghdr {
#define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid #define prm_rrf_decrvalid prm_flags.prf_rr.decrvalid
#define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd #define prm_rrf_decrprefd prm_flags.prf_rr.decrprefd
#define ifpr2ndpr(ifpr) ((struct nd_prefix *)(ifpr))
#define ndpr2ifpr(ndpr) ((struct ifprefix *)(ndpr))
struct nd_pfxrouter { struct nd_pfxrouter {
LIST_ENTRY(nd_pfxrouter) pfr_entry; LIST_ENTRY(nd_pfxrouter) pfr_entry;
#define pfr_next pfr_entry.le_next #define pfr_next pfr_entry.le_next
@ -321,7 +336,6 @@ extern int nd6_useloopback;
extern int nd6_maxnudhint; extern int nd6_maxnudhint;
extern int nd6_gctimer; extern int nd6_gctimer;
extern struct llinfo_nd6 llinfo_nd6; extern struct llinfo_nd6 llinfo_nd6;
extern struct nd_ifinfo *nd_ifinfo;
extern struct nd_drhead nd_defrouter; extern struct nd_drhead nd_defrouter;
extern struct nd_prhead nd_prefix; extern struct nd_prhead nd_prefix;
extern int nd6_debug; extern int nd6_debug;
@ -373,9 +387,9 @@ struct nd_opt_hdr *nd6_option __P((union nd_opts *));
int nd6_options __P((union nd_opts *)); int nd6_options __P((union nd_opts *));
struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *)); struct rtentry *nd6_lookup __P((struct in6_addr *, int, struct ifnet *));
void nd6_setmtu __P((struct ifnet *)); void nd6_setmtu __P((struct ifnet *));
void nd6_llinfo_settimer __P((struct llinfo_nd6 *, long));
void nd6_timer __P((void *)); void nd6_timer __P((void *));
void nd6_purge __P((struct ifnet *)); void nd6_purge __P((struct ifnet *));
struct llinfo_nd6 *nd6_free __P((struct rtentry *));
void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int)); void nd6_nud_hint __P((struct rtentry *, struct in6_addr *, int));
int nd6_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *, int nd6_resolve __P((struct ifnet *, struct rtentry *, struct mbuf *,
struct sockaddr *, u_char *)); struct sockaddr *, u_char *));
@ -385,9 +399,9 @@ struct rtentry *nd6_cache_lladdr __P((struct ifnet *, struct in6_addr *,
char *, int, int, int)); char *, int, int, int));
int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *, int nd6_output __P((struct ifnet *, struct ifnet *, struct mbuf *,
struct sockaddr_in6 *, struct rtentry *)); struct sockaddr_in6 *, struct rtentry *));
int nd6_need_cache __P((struct ifnet *));
int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *, int nd6_storelladdr __P((struct ifnet *, struct rtentry *, struct mbuf *,
struct sockaddr *, u_char *)); struct sockaddr *, u_char *));
int nd6_need_cache __P((struct ifnet *));
/* nd6_nbr.c */ /* nd6_nbr.c */
void nd6_na_input __P((struct mbuf *, int, int)); void nd6_na_input __P((struct mbuf *, int, int));
@ -397,7 +411,7 @@ void nd6_ns_input __P((struct mbuf *, int, int));
void nd6_ns_output __P((struct ifnet *, const struct in6_addr *, void nd6_ns_output __P((struct ifnet *, const struct in6_addr *,
const struct in6_addr *, struct llinfo_nd6 *, int)); const struct in6_addr *, struct llinfo_nd6 *, int));
caddr_t nd6_ifptomac __P((struct ifnet *)); caddr_t nd6_ifptomac __P((struct ifnet *));
void nd6_dad_start __P((struct ifaddr *, int *)); void nd6_dad_start __P((struct ifaddr *, int));
void nd6_dad_stop __P((struct ifaddr *)); void nd6_dad_stop __P((struct ifaddr *));
void nd6_dad_duplicated __P((struct ifaddr *)); void nd6_dad_duplicated __P((struct ifaddr *));
@ -406,23 +420,20 @@ void nd6_rs_input __P((struct mbuf *, int, int));
void nd6_ra_input __P((struct mbuf *, int, int)); void nd6_ra_input __P((struct mbuf *, int, int));
void prelist_del __P((struct nd_prefix *)); void prelist_del __P((struct nd_prefix *));
void defrouter_addreq __P((struct nd_defrouter *)); void defrouter_addreq __P((struct nd_defrouter *));
void defrouter_delreq __P((struct nd_defrouter *, int)); void defrouter_reset __P((void));
void defrouter_select __P((void)); void defrouter_select __P((void));
void defrtrlist_del __P((struct nd_defrouter *)); void defrtrlist_del __P((struct nd_defrouter *));
void prelist_remove __P((struct nd_prefix *)); void prelist_remove __P((struct nd_prefix *));
int prelist_update __P((struct nd_prefix *, struct nd_defrouter *, int nd6_prelist_add __P((struct nd_prefixctl *, struct nd_defrouter *,
struct mbuf *));
int nd6_prelist_add __P((struct nd_prefix *, struct nd_defrouter *,
struct nd_prefix **)); struct nd_prefix **));
int nd6_prefix_onlink __P((struct nd_prefix *)); int nd6_prefix_onlink __P((struct nd_prefix *));
int nd6_prefix_offlink __P((struct nd_prefix *)); int nd6_prefix_offlink __P((struct nd_prefix *));
void pfxlist_onlink_check __P((void)); void pfxlist_onlink_check __P((void));
struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, struct ifnet *)); struct nd_defrouter *defrouter_lookup __P((struct in6_addr *, struct ifnet *));
struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefix *)); struct nd_prefix *nd6_prefix_lookup __P((struct nd_prefixctl *));
int in6_init_prefix_ltimes __P((struct nd_prefix *));
void rt6_flush __P((struct in6_addr *, struct ifnet *)); void rt6_flush __P((struct in6_addr *, struct ifnet *));
int nd6_setdefaultiface __P((int)); int nd6_setdefaultiface __P((int));
int in6_tmpifadd __P((const struct in6_ifaddr *, int)); int in6_tmpifadd __P((const struct in6_ifaddr *, int, int));
#endif /* _KERNEL */ #endif /* _KERNEL */

View File

@ -677,13 +677,13 @@ nd6_na_input(m, off, icmp6len)
if (is_solicited) { if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0; ln->ln_byhint = 0;
if (ln->ln_expire) { if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_expire = time_second + nd6_llinfo_settimer(ln,
ND_IFINFO(rt->rt_ifp)->reachable; (long)ND_IFINFO(rt->rt_ifp)->reachable * hz);
} }
} else { } else {
ln->ln_state = ND6_LLINFO_STALE; ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
} }
if ((ln->ln_router = is_router) != 0) { if ((ln->ln_router = is_router) != 0) {
/* /*
@ -737,7 +737,7 @@ nd6_na_input(m, off, icmp6len)
*/ */
if (ln->ln_state == ND6_LLINFO_REACHABLE) { if (ln->ln_state == ND6_LLINFO_REACHABLE) {
ln->ln_state = ND6_LLINFO_STALE; ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln, (long)nd6_gctimer * hz);
} }
goto freeit; goto freeit;
} else if (is_override /* (2a) */ } else if (is_override /* (2a) */
@ -759,14 +759,15 @@ nd6_na_input(m, off, icmp6len)
if (is_solicited) { if (is_solicited) {
ln->ln_state = ND6_LLINFO_REACHABLE; ln->ln_state = ND6_LLINFO_REACHABLE;
ln->ln_byhint = 0; ln->ln_byhint = 0;
if (ln->ln_expire) { if (!ND6_LLINFO_PERMANENT(ln)) {
ln->ln_expire = time_second + nd6_llinfo_settimer(ln,
ND_IFINFO(ifp)->reachable; (long)ND_IFINFO(ifp)->reachable * hz);
} }
} else { } else {
if (lladdr != NULL && llchange) { if (lladdr != NULL && llchange) {
ln->ln_state = ND6_LLINFO_STALE; ln->ln_state = ND6_LLINFO_STALE;
ln->ln_expire = time_second + nd6_gctimer; nd6_llinfo_settimer(ln,
(long)nd6_gctimer * hz);
} }
} }
} }
@ -793,7 +794,7 @@ nd6_na_input(m, off, icmp6len)
dr = defrouter_lookup(in6, ifp); dr = defrouter_lookup(in6, ifp);
if (dr) if (dr)
defrtrlist_del(dr); defrtrlist_del(dr);
else if (!ip6_forwarding && ip6_accept_rtadv) { else if (!ip6_forwarding) {
/* /*
* Even if the neighbor is not in the default * Even if the neighbor is not in the default
* router list, the neighbor may be used * router list, the neighbor may be used
@ -810,12 +811,25 @@ nd6_na_input(m, off, icmp6len)
rt->rt_flags &= ~RTF_REJECT; rt->rt_flags &= ~RTF_REJECT;
ln->ln_asked = 0; ln->ln_asked = 0;
if (ln->ln_hold) { if (ln->ln_hold) {
struct mbuf *m_hold, *m_hold_next;
for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold_next) {
struct mbuf *mpkt = NULL;
m_hold_next = m_hold->m_nextpkt;
mpkt = m_copym(m_hold, 0, M_COPYALL, M_DONTWAIT);
if (mpkt == NULL) {
m_freem(m_hold);
break;
}
mpkt->m_nextpkt = NULL;
/* /*
* we assume ifp is not a loopback here, so just set the 2nd * we assume ifp is not a loopback here, so just set
* argument as the 1st one. * the 2nd argument as the 1st one.
*/ */
nd6_output(ifp, ifp, ln->ln_hold, nd6_output(ifp, ifp, mpkt,
(struct sockaddr_in6 *)rt_key(rt), rt); (struct sockaddr_in6 *)rt_key(rt), rt);
}
ln->ln_hold = NULL; ln->ln_hold = NULL;
} }
@ -1081,9 +1095,9 @@ nd6_dad_stoptimer(dp)
* Start Duplicate Address Detection (DAD) for specified interface address. * Start Duplicate Address Detection (DAD) for specified interface address.
*/ */
void void
nd6_dad_start(ifa, tick) nd6_dad_start(ifa, delay)
struct ifaddr *ifa; struct ifaddr *ifa;
int *tick; /* minimum delay ticks for IFF_UP event */ int delay;
{ {
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp; struct dadq *dp;
@ -1151,19 +1165,12 @@ nd6_dad_start(ifa, tick)
dp->dad_count = ip6_dad_count; dp->dad_count = ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0; dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0; dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
if (tick == NULL) { if (delay == 0) {
nd6_dad_ns_output(dp, ifa); nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp, nd6_dad_starttimer(dp,
ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
} else { } else {
int ntick; nd6_dad_starttimer(dp, delay);
if (*tick == 0)
ntick = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz);
else
ntick = *tick + arc4random() % (hz / 2);
*tick = ntick;
nd6_dad_starttimer(dp, ntick);
} }
} }
@ -1246,7 +1253,7 @@ nd6_dad_timer(ifa)
*/ */
nd6_dad_ns_output(dp, ifa); nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp, nd6_dad_starttimer(dp,
ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000);
} else { } else {
/* /*
* We have transmitted sufficient number of DAD packets. * We have transmitted sufficient number of DAD packets.

File diff suppressed because it is too large Load Diff