diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2006-11-06 19:05:06 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2006-11-06 19:05:06 +0000 |
commit | 4f4d9f7a0e48ee9caa58a9e6ec62485a917a3924 (patch) | |
tree | cf694ee1ec25dda600bb3b238d159261aaadccde | |
parent | 77383bab0a15747e1f55fedf8db732840fcc953f (diff) | |
download | vyos-strongswan-4f4d9f7a0e48ee9caa58a9e6ec62485a917a3924.tar.gz vyos-strongswan-4f4d9f7a0e48ee9caa58a9e6ec62485a917a3924.zip |
- New upstream release.
111 files changed, 21845 insertions, 11596 deletions
@@ -1,3 +1,13 @@ +strongswan-2.8.0 +---------------- + +- Implementation of ModeConfig push mode via the new connection keyword + modeconfig=push allows interoperability with Cisco VPN gateways. + +- The command ipsec statusall now shows "DPD active" for all ISAKMP SAs + that are under active Dead Peer Detection control. + + strongswan-2.7.3 ---------------- @@ -11,7 +11,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: Makefile,v 1.4 2004/11/14 21:50:59 as Exp $ +# RCSID $Id: Makefile,v 1.5 2006/08/28 11:12:36 as Exp $ FREESWANSRCDIR=$(shell pwd) @@ -244,7 +244,7 @@ programs install clean checkprograms:: @for d in $(SUBDIRS) ; \ do \ (cd $$d && $(MAKE) FREESWANSRCDIR=.. $@ ) || exit 1; \ - done; \ + done; clean:: rm -rf $(RPMTMPDIR) $(RPMDEST) diff --git a/Makefile.ver b/Makefile.ver index b8f0d8ffd..c13d3a0da 100644 --- a/Makefile.ver +++ b/Makefile.ver @@ -1 +1 @@ -IPSECVERSION=2.7.3 +IPSECVERSION=2.8.0 @@ -41,7 +41,7 @@ Contents 6.1 Loading private key files in PKCS#1 format 6.2 Entering passphrases interactively 6.3 Multiple private keys - 7. Configuring CA properties - ipsec.conf + 7. Configuring CA properties - ipsec.onf 8. Smartcard support 8.1 Configuring a smartcard-based connection 8.2 Entering the PIN code @@ -69,7 +69,8 @@ Contents 14.1 Authentication and encryption algorithms 14.2 NAT traversal 14.3 Dead peer detection - 14.4 IKE Mode Config + 14.4 IKE Mode Config Pull Mode + 14.5 IKE Mode Config Push Mode 15. Copyright statement and acknowledgements @@ -2918,8 +2919,8 @@ even if they might be supported by the responder. Currently please refer to README.NAT-Traversal document in the strongSwan distribution. - - + + 14.3 Dead peer detection -------------------- @@ -2969,14 +2970,15 @@ dpdaction=clear for dynamic roadwarrior connections. The default value is dpdaction=none, which disables DPD. -14.4 IKE Mode Config - --------------- - +14.4 IKE Mode Config Pull Mode + ------------------------- + The IKE Mode Config protocol <draft-ietf-ipsec-isakmp-mode-cfg-04.txt> allows the dynamic assignment of virtual IP addresses and optional DNS and WINS server -information to IPsec clients. Currently only "Mode Config Pull Mode" is -implemented where the client actively sends a Mode Config request to the server -in order to obtain a virtual IP. +information to IPsec clients. As a default the "Mode Config Pull Mode" is +used where the client actively sends a Mode Config request to the server +in order to obtain a virtual IP. The server answers with a Mode Config reply +message containing the requested information. Client side configuration (carol): @@ -3008,6 +3010,22 @@ the virtual IP address defined by the rightsourceip parameter. In the future an LDAP-based lookup mechanism will be supported. +14.5 IKE Mode Config Push Mode + ------------------------- + +Cisco VPN equipment uses the alternative "Mode Config Push Mode" where the +initiating clients waits for the server to push down a virtual address via +a Mode Config set message. The receipt is acknowledged by the client with a +Mode Config ack message. + +Mode Config Push Mode is activated by the parameter + + modeconfig=push + +as part of the connection definition in ipsec.conf. The default value is +modeconfig=pull. + + 15. Copyright statement and acknowledgements ---------------------------------------- @@ -3058,7 +3076,7 @@ an LDAP-based lookup mechanism will be supported. Copyright (c) 2000, Kai Martius X.509, OCSP and smartcard functionality: - +° Copyright (c) 2000, Andreas Hess, Patric Lichtsteiner, Roger Wegmann Copyright (c) 2001, Marco Bertossa, Andreas Schleiss Copyright (c) 2002, Uli Galizzi, Ariane Seiler, Mario Strasser @@ -3087,5 +3105,5 @@ an LDAP-based lookup mechanism will be supported. for more details. ----------------------------------------------------------------------------- -This file is RCSID $Id: README,v 1.34 2006/04/26 18:19:34 as Exp $ +This file is RCSID $Id: README,v 1.36 2006/10/20 15:43:51 as Exp $ diff --git a/debian/changelog b/debian/changelog index 1329b6f5c..15abbffd6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +strongswan (2.8.0+dfsg-1) UNRELEASED; urgency=low + + * (NOT RELEASED YET) New upstream release + + -- Rene Mayrhofer <rmayr@debian.org> Mon, 6 Nov 2006 19:01:58 +0000 + strongswan (2.7.3+dfsg-1) unstable; urgency=low * New upstream release. Another try on getting it into unstable. diff --git a/doc/src/draft-richardson-ipsec-opportunistic.html b/doc/src/draft-richardson-ipsec-opportunistic.html deleted file mode 100644 index 87a13365a..000000000 --- a/doc/src/draft-richardson-ipsec-opportunistic.html +++ /dev/null @@ -1,2456 +0,0 @@ -<html><head><title>Opportunistic Encryption using The Internet Key Exchange (IKE)</title> -<STYLE type='text/css'> - .title { color: #990000; font-size: 22px; line-height: 22px; font-weight: bold; text-align: right; - font-family: helvetica, arial, sans-serif } - .filename { color: #666666; font-size: 18px; line-height: 28px; font-weight: bold; text-align: right; - font-family: helvetica, arial, sans-serif } - p.copyright { color: #000000; font-size: 10px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - p { margin-left: 2em; margin-right: 2em; } - li { margin-left: 3em; } - ol { margin-left: 2em; margin-right: 2em; } - ul.text { margin-left: 2em; margin-right: 2em; } - pre { margin-left: 3em; color: #333333 } - ul.toc { color: #000000; line-height: 16px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - H3 { color: #333333; font-size: 16px; line-height: 16px; font-family: helvetica, arial, sans-serif } - H4 { color: #000000; font-size: 14px; font-family: helvetica, arial, sans-serif } - TD.header { color: #ffffff; font-size: 10px; font-family: arial, helvetica, san-serif; valign: top } - TD.author-text { color: #000000; font-size: 10px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - TD.author { color: #000000; font-weight: bold; margin-left: 4em; font-size: 10px; font-family: verdana, charcoal, helvetica, arial, sans-serif } - A:link { color: #990000; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - A:visited { color: #333333; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - A:name { color: #333333; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - .link2 { color:#ffffff; font-weight: bold; text-decoration: none; - font-family: monaco, charcoal, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } - .RFC { color:#666666; font-weight: bold; text-decoration: none; - font-family: monaco, charcoal, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } - .hotText { color:#ffffff; font-weight: normal; text-decoration: none; - font-family: charcoal, monaco, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } -</style> -</head> -<body bgcolor="#ffffff" text="#000000" alink="#000000" vlink="#666666" link="#990000"> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<table width="66%" border="0" cellpadding="0" cellspacing="0"><tr><td><table width="100%" border="0" cellpadding="2" cellspacing="1"> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">Independent submission</td><td width="33%" bgcolor="#666666" class="header">M. Richardson</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">Internet-Draft</td><td width="33%" bgcolor="#666666" class="header">SSW</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">Expires: November 19, 2003</td><td width="33%" bgcolor="#666666" class="header">D. Redelmeier</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header"> </td><td width="33%" bgcolor="#666666" class="header">Mimosa</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header"> </td><td width="33%" bgcolor="#666666" class="header">May 21, 2003</td></tr> -</table></td></tr></table> -<div align="right"><font face="monaco, MS Sans Serif" color="#990000" size="+3"><b><br><span class="title">Opportunistic Encryption using The Internet Key Exchange (IKE)</span></b></font></div> -<div align="right"><font face="monaco, MS Sans Serif" color="#666666" size="+2"><b><span class="filename">draft-richardson-ipsec-opportunistic-11.txt</span></b></font></div> -<font face="verdana, helvetica, arial, sans-serif" size="2"> - -<h3>Status of this Memo</h3> -<p> -This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026.</p> -<p> -Internet-Drafts are working documents of the Internet Engineering -Task Force (IETF), its areas, and its working groups. -Note that other groups may also distribute working documents as -Internet-Drafts.</p> -<p> -Internet-Drafts are draft documents valid for a maximum of six months -and may be updated, replaced, or obsoleted by other documents at any time. -It is inappropriate to use Internet-Drafts as reference material or to cite -them other than as "work in progress."</p> -<p> -The list of current Internet-Drafts can be accessed at -<a href='http://www.ietf.org/ietf/1id-abstracts.txt'>http://www.ietf.org/ietf/1id-abstracts.txt</a>.</p> -<p> -The list of Internet-Draft Shadow Directories can be accessed at -<a href='http://www.ietf.org/shadow.html'>http://www.ietf.org/shadow.html</a>.</p> -<p> -This Internet-Draft will expire on November 19, 2003.</p> - -<h3>Copyright Notice</h3> -<p> -Copyright (C) The Internet Society (2003). All Rights Reserved.</p> - -<h3>Abstract</h3> - -<p> -This document describes opportunistic encryption (OE) using the Internet Key -Exchange (IKE) and IPsec. -Each system administrator adds new -resource records to his or her Domain Name System (DNS) to support -opportunistic encryption. The objective is to allow encryption for secure communication without -any pre-arrangement specific to the pair of systems involved. - -</p> -<p> -DNS is used to distribute the public keys of each -system involved. This is resistant to passive attacks. The use of DNS -Security (DNSSEC) secures this system against active attackers as well. - -</p> -<p> -As a result, the administrative overhead is reduced -from the square of the number of systems to a linear dependence, and it becomes -possible to make secure communication the default even -when the partner is not known in advance. - -</p> -<p> -This document is offered up as an Informational RFC. - -</p><a name="toc"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Table of Contents</h3> -<ul compact class="toc"> -<b><a href="#anchor1">1.</a> -Introduction<br></b> -<b><a href="#anchor6">2.</a> -Overview<br></b> -<b><a href="#anchor13">3.</a> -Specification<br></b> -<b><a href="#anchor31">4.</a> -Impacts on IKE<br></b> -<b><a href="#anchor38">5.</a> -DNS issues<br></b> -<b><a href="#anchor42">6.</a> -Network address translation interaction<br></b> -<b><a href="#anchor46">7.</a> -Host implementations<br></b> -<b><a href="#anchor47">8.</a> -Multi-homing<br></b> -<b><a href="#anchor48">9.</a> -Failure modes<br></b> -<b><a href="#anchor52">10.</a> -Unresolved issues<br></b> -<b><a href="#anchor54">11.</a> -Examples<br></b> -<b><a href="#securityconsiderations">12.</a> -Security considerations<br></b> -<b><a href="#anchor79">13.</a> -IANA Considerations<br></b> -<b><a href="#anchor80">14.</a> -Acknowledgments<br></b> -<b><a href="#rfc.references1">§</a> -Normative references<br></b> -<b><a href="#rfc.authors">§</a> -Authors' Addresses<br></b> -<b><a href="#rfc.copyright">§</a> -Full Copyright Statement<br></b> -</ul> -<br clear="all"> - -<a name="anchor1"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.1"></a><h3>1. Introduction</h3> - -<a name="rfc.section.1.1"></a><h4><a name="anchor2">1.1</a> Motivation</h4> - -<p> -The objective of opportunistic encryption is to allow encryption without -any pre-arrangement specific to the pair of systems involved. Each -system administrator adds -public key information to DNS records to support opportunistic -encryption and then enables this feature in the nodes' IPsec stack. -Once this is done, any two such nodes can communicate securely. - -</p> -<p> -This document describes opportunistic encryption as designed and -mostly implemented by the Linux FreeS/WAN project. -For project information, see http://www.freeswan.org. - -</p> -<p> -The Internet Architecture Board (IAB) and Internet Engineering -Steering Group (IESG) have taken a strong stand that the Internet -should use powerful encryption to provide security and -privacy <a href="#RFC1984">[4]</a>. -The Linux FreeS/WAN project attempts to provide a practical means to implement this policy. - -</p> -<p> -The project uses the IPsec, ISAKMP/IKE, DNS and DNSSEC -protocols because they are -standardized, widely available and can often be deployed very easily -without changing hardware or software or retraining users. - -</p> -<p> -The extensions to support opportunistic encryption are simple. No -changes to any on-the-wire formats are needed. The only changes are to -the policy decision making system. This means that opportunistic -encryption can be implemented with very minimal changes to an existing -IPsec implementation. - -</p> -<p> -Opportunistic encryption creates a "fax effect". The proliferation -of the fax machine was possible because it did not require that everyone -buy one overnight. Instead, as each person installed one, the value -of having one increased - as there were more people that could receive faxes. -Once opportunistic encryption is installed it -automatically recognizes -other boxes using opportunistic encryption, without any further configuration -by the network -administrator. So, as opportunistic encryption software is installed on more -boxes, its value -as a tool increases. - -</p> -<p> -This document describes the infrastructure to permit deployment of -Opportunistic Encryption. - -</p> -<p> -The term S/WAN is a trademark of RSA Data Systems, and is used with permission -by this project. - -</p> -<a name="rfc.section.1.2"></a><h4><a name="anchor3">1.2</a> Types of network traffic</h4> - -<p> - To aid in understanding the relationship between security processing and IPsec - we divide network traffic into four categories: - -<blockquote class="text"><dl> -<dt>* Deny:</dt> -<dd> networks to which traffic is always forbidden. -</dd> -<dt>* Permit:</dt> -<dd> networks to which traffic in the clear is permitted. -</dd> -<dt>* Opportunistic tunnel:</dt> -<dd> networks to which traffic is encrypted if possible, but otherwise is in the clear - or fails depending on the default policy in place. - -</dd> -<dt>* Configured tunnel:</dt> -<dd> networks to which traffic must be encrypted, and traffic in the clear is never permitted. -</dd> -</dl></blockquote><p> -</p> -<p> -Traditional firewall devices handle the first two categories. No authentication is required. -The permit policy is currently the default on the Internet. - -</p> -<p> -This document describes the third category - opportunistic tunnel, which is -proposed as the new default for the Internet. - -</p> -<p> - Category four, encrypt traffic or drop it, requires authentication of the - end points. As the number of end points is typically bounded and is typically - under a single authority, arranging for distribution of - authentication material, while difficult, does not require any new - technology. The mechanism described here provides an additional way to - distribute the authentication materials, that of a public key method that does not - require deployment of an X.509 based infrastructure. - -</p> -<p> -Current Virtual Private Networks can often be replaced by an "OE paranoid" -policy as described herein. - -</p> -<a name="rfc.section.1.3"></a><h4><a name="anchor4">1.3</a> Peer authentication in opportunistic encryption</h4> - -<p> - Opportunistic encryption creates tunnels between nodes that - are essentially strangers. This is done without any prior bilateral - arrangement. - There is, therefore, the difficult question of how one knows to whom one is - talking. - -</p> -<p> - One possible answer is that since no useful - authentication can be done, none should be tried. This mode of operation is - named "anonymous encryption". An active man-in-the-middle attack can be - used to thwart the privacy of this type of communication. - Without peer authentication, there is no way to prevent this kind of attack. - -</p> -<p> -Although a useful mode, anonymous encryption is not the goal of this -project. Simpler methods are available that can achieve anonymous -encryption only, but authentication of the peer is a desireable goal. -The latter is achieved through key distribution in DNS, leveraging upon -the authentication of the DNS in DNSSEC. - -</p> -<p> - Peers are, therefore, authenticated with DNSSEC when available. Local policy -determines how much trust to extend when DNSSEC is not available. - -</p> -<p> - However, an essential premise of building private connections with - strangers is that datagrams received through opportunistic tunnels - are no more special than datagrams that arrive in the clear. - Unlike in a VPN, these datagrams should not be given any special - exceptions when it comes to auditing, further authentication or - firewalling. - -</p> -<p> - When initiating outbound opportunistic encryption, local - configuration determines what happens if tunnel setup fails. It may be that - the packet goes out in the clear, or it may be dropped. - -</p> -<a name="rfc.section.1.4"></a><h4><a name="anchor5">1.4</a> Use of RFC2119 terms</h4> - -<p> - The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, - SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this - document, are to be interpreted as described in <a href="#RFC2119">[5]</a> -</p> -<a name="anchor6"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.2"></a><h3>2. Overview</h3> - -<a name="rfc.section.2.1"></a><h4><a name="anchor7">2.1</a> Reference diagram</h4> -<br><hr size="1" shade="0"> -<a name="networkdiagram"></a> - -<p>The following network diagram is used in the rest of - this document as the canonical diagram: -</p></font><pre> - [Q] [R] - . . AS2 - [A]----+----[SG-A].......+....+.......[SG-B]-------[B] - | ...... - AS1 | ..PI.. - | ...... - [D]----+----[SG-D].......+....+.......[C] AS3 - - - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> - -<p> -</p><table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Reference Network Diagram </b></font><br></td></tr></table><hr size="1" shade="0"> - -<p> - In this diagram, there are four end-nodes: A, B, C and D. - There are three gateways, SG-A, SG-B, SG-D. A, D, SG-A and SG-D are part - of the same administrative authority, AS1. SG-A and SG-D are on two different exit - paths from organization 1. SG-B/B is an independent organization, AS2. - Nodes Q and R are nodes on the Internet. PI is the Public - Internet ("The Wild"). - -</p> -<a name="rfc.section.2.2"></a><h4><a name="anchor8">2.2</a> Terminology</h4> - -<p> - The following terminology is used in this document: - -</p> -<blockquote class="text"><dl> -<dt>Security gateway:</dt> -<dd> a system that performs IPsec tunnel - mode encapsulation/decapsulation. [SG-x] in the diagram. -</dd> -<dt>Alice:</dt> -<dd> node [A] in the diagram. When an IP address is needed, this is 192.1.0.65. -</dd> -<dt>Bob:</dt> -<dd> node [B] in the diagram. When an IP address is needed, this is 192.2.0.66. -</dd> -<dt>Carol:</dt> -<dd> node [C] in the diagram. When an IP address is needed, this is 192.1.1.67. -</dd> -<dt>Dave:</dt> -<dd> node [D] in the diagram. When an IP address is needed, this is 192.3.0.68. -</dd> -<dt>SG-A:</dt> -<dd> Alice's security gateway. Internally it is 192.1.0.1, externally it is 192.1.1.4. -</dd> -<dt>SG-B:</dt> -<dd> Bob's security gateway. Internally it is 192.2.0.1, externally it is 192.1.1.5. -</dd> -<dt>SG-D:</dt> -<dd> Dave's security gateway. Also Alice's backup security gateway. Internally it is 192.3.0.1, externally it is 192.1.1.6. -</dd> -<dt>-</dt> -<dd> A single dash represents clear-text datagrams. -</dd> -<dt>=</dt> -<dd> An equals sign represents phase 2 (IPsec) cipher-text - datagrams. -</dd> -<dt>~</dt> -<dd> A single tilde represents clear-text phase 1 datagrams. -</dd> -<dt>#</dt> -<dd> A hash sign represents phase 1 (IKE) cipher-text - datagrams. -</dd> -<dt>.</dt> -<dd> A period represents an untrusted network of unknown - type. -</dd> -<dt>Configured tunnel:</dt> -<dd> a tunnel that - is directly and deliberately hand configured on participating gateways. - Configured tunnels are typically given a higher level of - trust than opportunistic tunnels. -</dd> -<dt>Road warrior tunnel:</dt> -<dd> a configured tunnel connecting one - node with a fixed IP address and one node with a variable IP address. - A road warrior (RW) connection must be initiated by the - variable node, since the fixed node cannot know the - current address for the road warrior. -</dd> -<dt>Anonymous encryption:</dt> -<dd> - the process of encrypting a session without any knowledge of who the - other parties are. No authentication of identities is done. -</dd> -<dt>Opportunistic encryption:</dt> -<dd> - the process of encrypting a session with authenticated knowledge of - who the other parties are. -</dd> -<dt>Lifetime:</dt> -<dd> - the period in seconds (bytes or datagrams) for which a security - association will remain alive before needing to be re-keyed. -</dd> -<dt>Lifespan:</dt> -<dd> - the effective time for which a security association remains useful. A - security association with a lifespan shorter than its lifetime would - be removed when no longer needed. A security association with a - lifespan longer than its lifetime would need to be re-keyed one or - more times. -</dd> -<dt>Phase 1 SA:</dt> -<dd> an ISAKMP/IKE security association sometimes - referred to as a keying channel. -</dd> -<dt>Phase 2 SA:</dt> -<dd> an IPsec security association. -</dd> -<dt>Tunnel:</dt> -<dd> another term for a set of phase 2 SA (one in each direction). -</dd> -<dt>NAT:</dt> -<dd> Network Address Translation - (see <a href="#RFC2663">[20]</a>). -</dd> -<dt>NAPT:</dt> -<dd> Network Address and Port Translation - (see <a href="#RFC2663">[20]</a>). -</dd> -<dt>AS:</dt> -<dd> an autonomous system (AS) is a group of systems (a network) that - are under the administrative control of a single organization. -</dd> -<dt>Default-free zone:</dt> -<dd> - a set of routers that maintain a complete set of routes to - all currently reachable destinations. Having such a list, these routers - never make use of a default route. A datagram with a destination address - not matching any route will be dropped by such a router. - -</dd> -</dl></blockquote><p> -<a name="rfc.section.2.3"></a><h4><a name="anchor9">2.3</a> Model of operation</h4> - -<p> -The opportunistic encryption security gateway (OE gateway) is a regular -gateway node as described in <a href="#RFC0791">[2]</a> section 2.4 and -<a href="#RFC1009">[3]</a> with the additional capabilities described here and -in <a href="#RFC2401">[7]</a>. -The algorithm described here provides a way to determine, for each datagram, -whether or not to encrypt and tunnel the datagram. Two important things -that must be determined are whether or not to encrypt and tunnel and, if -so, the destination address or name of the tunnel end point which should be used. - -</p> -<a name="rfc.section.2.3.1"></a><h4><a name="anchor10">2.3.1</a> Tunnel authorization</h4> - -<p> -The OE gateway determines whether or not to create a tunnel based on -the destination address of each packet. Upon receiving a packet with a destination -address not recently seen, the OE gateway performs a lookup in DNS for an -authorization resource record (see <a href="#TXT">Use of TXT delegation record</a>). The record is located using -the IP address to perform a search in the in-addr.arpa (IPv4) or ip6.arpa -(IPv6) maps. If an authorization record is found, the OE gateway -interprets this as a request for a tunnel to be formed. - -</p> -<a name="rfc.section.2.3.2"></a><h4><a name="anchor11">2.3.2</a> Tunnel end-point discovery</h4> - -<p> -The authorization resource record also provides the address or name of the tunnel -end point which should be used. - -</p> -<p> -The record may also provide the public RSA key of the tunnel end point -itself. This is provided for efficiency only. If the public RSA key is not -present, the OE gateway performs a second lookup to find a KEY -resource record for the end point address or name. - -</p> -<p> -Origin and integrity protection of the resource records is provided by -DNSSEC (<a href="#RFC2535">[16]</a>). <a href="#nodnssec">Restriction on unauthenticated TXT delegation records</a> -documents an optional restriction on the tunnel end point if DNSSEC signatures -are not available for the relevant records. - -</p> -<a name="rfc.section.2.3.3"></a><h4><a name="anchor12">2.3.3</a> Caching of authorization results</h4> - -<p> -The OE gateway maintains a cache, in the forwarding plane, of -source/destination pairs for which opportunistic encryption has been -attempted. This cache maintains a record of whether or not OE was -successful so that subsequent datagrams can be forwarded properly -without additional delay. - -</p> -<p> -Successful negotiation of OE instantiates a new security association. -Failure to negotiate OE results in creation of a -forwarding policy entry either to drop or transmit in the clear future -datagrams. This negative cache is necessary to avoid the possibly lengthy process of repeatedly looking -up the same information. - -</p> -<p> -The cache is timed out periodically, as described in <a href="#teardown">Renewal and teardown</a>. -This removes entries that are no longer -being used and permits the discovery of changes in authorization policy. - -</p> -<a name="anchor13"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.3"></a><h3>3. Specification</h3> - -<p> -The OE gateway is modeled to have a forwarding plane and a control -plane. A control channel, such as PF_KEY, connects the two planes. -(See <a href="#RFC2367">[6]</a>.) -The forwarding plane performs per datagram operations. The control plane -contains a keying -daemon, such as ISAKMP/IKE, and performs all authorization, peer authentication and -key derivation functions. - -</p> -<a name="rfc.section.3.1"></a><h4><a name="anchor14">3.1</a> Datagram state machine</h4> - -<p> -Let the OE gateway maintain a collection of objects -- a superset of the -security policy database (SPD) specified in <a href="#RFC2401">[7]</a>. For -each combination of source and destination address, an SPD -object exists in one of five following states. -Prior to forwarding each datagram, the -responder uses the source and destination addresses to pick an entry from the SPD. -The SPD then determines if and how the packet is forwarded. - -</p> -<a name="rfc.section.3.1.1"></a><h4><a name="anchor15">3.1.1</a> Non-existent policy</h4> - -<p> -If the responder does not find an entry, then this policy applies. -The responder creates an entry with an initial state of "hold policy" and requests -keying material from the keying daemon. The responder does not forward the datagram, -rather it attaches the datagram to the SPD entry as the "first" datagram and retains it -for eventual transmission in a new state. - - -</p> -<a name="rfc.section.3.1.2"></a><h4><a name="anchor16">3.1.2</a> Hold policy</h4> - -<p> -The responder requests keying material. If the interface to the keying -system is lossy (PF_KEY, for instance, can be), the implementation -SHOULD include a mechanism to retransmit the -keying request at a rate limited to less than 1 request per second. -The responder does not forward the datagram. It attaches the -datagram to the SPD entry as the "last" datagram where it is retained -for eventual transmission. If there is -a datagram already so stored, then that already stored datagram is discarded. - -</p> -<p> -Because the "first" datagram is probably a TCP SYN packet, the -responder retains the "first" datagram in an attempt to avoid waiting for a -TCP retransmit. The responder retains the "last" -datagram in deference to streaming protocols that find it useful to know -how much data has been lost. These are recommendations to -decrease latency. There are no operational requirements for this. - -</p> -<a name="rfc.section.3.1.3"></a><h4><a name="anchor17">3.1.3</a> Pass-through policy</h4> - -<p> -The responder forwards the datagram using the normal forwarding table. -The responder enters this state only by command from the keying daemon, -and upon entering this state, also forwards the "first" and "last" datagrams. - -</p> -<a name="rfc.section.3.1.4"></a><h4><a name="anchor18">3.1.4</a> Deny policy</h4> - -<p> -The responder discards the datagram. The responder enters this state only by -command -from the keying daemon, and upon entering this state, discards the "first" -and "last" datagrams. -Local administration decides if further datagrams cause ICMP messages -to be generated (i.e. ICMP Destination Unreachable, Communication -Administratively Prohibited. type=3, code=13). - -</p> -<a name="rfc.section.3.1.5"></a><h4><a name="anchor19">3.1.5</a> Encrypt policy</h4> - -<p> -The responder encrypts the datagram using the indicated security association database -(SAD) entry. The responder enters this state only by command from the keying daemon, and upon entering -this state, releases and forwards the "first" and "last" datagrams using the -new encrypt policy. - -</p> -<p> -If the associated SAD entry expires because of byte, packet or time limits, then -the entry returns to the Hold policy, and an expire message is sent to the keying daemon. - -</p> -<p> -All states may be created directly by the keying daemon while acting as a -responder. - -</p> -<a name="rfc.section.3.2"></a><h4><a name="initclasses">3.2</a> Keying state machine - initiator</h4> - -<p> -Let the keying daemon maintain a collection of objects. Let them be -called "connections" or "conn"s. There are two categories of -connection objects: classes and instances. A class represents an -abstract policy - what could be. An instance represents an actual connection - -what is implemented at the time. - -</p> -<p> -Let there be two further subtypes of connections: keying channels (Phase -1 SAs) and data channels (Phase 2 SAs). Each data channel object may have -a corresponding SPD and SAD entry maintained by the datagram state machine. - -</p> -<p> -For the purposes of opportunistic encryption, there MUST, at least, be -connection classes known as "deny", "always-clear-text", "OE-permissive", and -"OE-paranoid". -The latter two connection classes define a set of source and/or destination -addresses for which opportunistic encryption will be attempted. The administrator MAY set policy -options in a number of additional places. An implementation MAY create additional connection classes to further refine -these policies. - -</p> -<p> -The simplest system may need only the "OE-permissive" connection, and would -list its own (single) IP address as the source address of this policy and -the wild-card address 0.0.0.0/0 as the destination IPv4 address. That is, the -simplest policy is to try opportunistic encryption with all destinations. - -</p> -<p> -The distinction between permissive and paranoid OE use will become clear -in the state transition differences. In general a permissive OE will, on -failure, install a pass-through policy, while a paranoid OE will, on failure, -install a drop policy. - -</p> -<p> -In this description of the keying machine's state transitions, the states -associated with the keying system itself are omitted because they are best documented in the keying system -(<a href="#RFC2407">[8]</a>, -<a href="#RFC2408">[9]</a> and <a href="#RFC2409">[10]</a> for ISAKMP/IKE), -and the details are keying system specific. Opportunistic encryption is not -dependent upon any specific keying protocol, but this document does provide -requirements for those using ISAKMP/IKE to assure that implementations inter-operate. - -</p> -<p> -The state transitions that may be involved in communicating with the -forwarding plane are omitted. PF_KEY and similar protocols have their own -set of states required for message sends and completion notifications. - -</p> -<p> -Finally, the retransmits and recursive lookups that are normal for DNS are -not included in this description of the state machine. - -</p> -<a name="rfc.section.3.2.1"></a><h4><a name="anchor20">3.2.1</a> Nonexistent connection</h4> - -<p> -There is no connection instance for a given source/destination address pair. -Upon receipt of a request for keying material for this -source/destination pair, the initiator searches through the connection classes to -determine the most appropriate policy. Upon determining an appropriate -connection class, an instance object is created of that type. -Both of the OE types result in a potential OE connection. - -</p> -<p>Failure to find an appropriate connection class results in an -administrator defined default. - -</p> -<p> -In each case, when the initiator finds an appropriate class for the new flow, -an instance connection is made of the class which matched. - -</p> -<a name="rfc.section.3.2.2"></a><h4><a name="anchor21">3.2.2</a> Clear-text connection</h4> - -<p> -The non-existent connection makes a transition to this state when an -always-clear-text class is instantiated, or when an OE-permissive -connection fails. During the transition, the initiator creates a pass-through -policy object in the forwarding plane for the appropriate flow. - -</p> -<p> -Timing out is the only way to leave this state -(see <a href="#expiring">Expiring connection</a>). - -</p> -<a name="rfc.section.3.2.3"></a><h4><a name="anchor22">3.2.3</a> Deny connection</h4> - -<p> -The empty connection makes a transition to this state when a -deny class is instantiated, or when an OE-paranoid connection fails. -During the transition, the initiator creates a deny policy object in the forwarding plane -for the appropriate flow. - -</p> -<p> -Timing out is the only way to leave this state -(see <a href="#expiring">Expiring connection</a>). - -</p> -<a name="rfc.section.3.2.4"></a><h4><a name="anchor23">3.2.4</a> Potential OE connection</h4> - -<p> -The empty connection makes a transition to this state when one of either OE class is instantiated. -During the transition to this state, the initiator creates a hold policy object in the -forwarding plane for the appropriate flow. - -</p> -<p> -In addition, when making a transition into this state, DNS lookup is done in -the reverse-map for a TXT delegation resource record (see <a href="#TXT">Use of TXT delegation record</a>). -The lookup key is the destination address of the flow. - -</p> -<p> -There are three ways to exit this state: - -<ol class="text"> -<li>DNS lookup finds a TXT delegation resource record. -</li> -<li>DNS lookup does not find a TXT delegation resource record. -</li> -<li>DNS lookup times out. -</li> -</ol><p> -</p> -<p> -Based upon the results of the DNS lookup, the potential OE connection makes a -transition to the pending OE connection state. The conditions for a -successful DNS look are: - -<ol class="text"> -<li>DNS finds an appropriate resource record -</li> -<li>It is properly formatted according to <a href="#TXT">Use of TXT delegation record</a> -</li> -<li> if DNSSEC is enabled, then the signature has been vouched for. -</li> -</ol><p> - -Note that if the initiator does not find the public key -present in the TXT delegation record, then the public key must -be looked up as a sub-state. Only successful completion of all the -DNS lookups is considered a success. - -</p> -<p> -If DNS lookup does not find a resource record or DNS times out, then the -initiator considers the receiver not OE capable. If this is an OE-paranoid instance, -then the potential OE connection makes a transition to the deny connection state. -If this is an OE-permissive instance, then the potential OE connection makes a transition to the -clear-text connection state. - -</p> -<p> -If the initiator finds a resource record but it is not properly formatted, or -if DNSSEC is -enabled and reports a failure to authenticate, then the potential OE -connection should make a -transition to the deny connection state. This action SHOULD be logged. If the -administrator wishes to override this transition between states, then an -always-clear class can be installed for this flow. An implementation MAY make -this situation a new class. - -</p> -<a name="rfc.section.3.2.4.1"></a><h4><a name="nodnssec">3.2.4.1</a> Restriction on unauthenticated TXT delegation records</h4> - -<p> -An implementation SHOULD also provide an additional administrative control -on delegation records and DNSSEC. This control would apply to delegation -records (the TXT records in the reverse-map) that are not protected by -DNSSEC. -Records of this type are only permitted to delegate to their own address as -a gateway. When this option is enabled, an active attack on DNS will be -unable to redirect packets to other than the original destination. - -</p> -<a name="rfc.section.3.2.5"></a><h4><a name="anchor24">3.2.5</a> Pending OE connection</h4> - -<p> -The potential OE connection makes a transition to this state when -the initiator determines that all the information required from the DNS lookup is present. -Upon entering this state, the initiator attempts to initiate keying to the gateway -provided. - -</p> -<p> -Exit from this state occurs either with a successfully created IPsec SA, or -with a failure of some kind. Successful SA creation results in a transition -to the key connection state. - -</p> -<p> -Three failures have caused significant problems. They are clearly not the -only possible failures from keying. - -</p> -<p> -Note that if there are multiple gateways available in the TXT delegation -records, then a failure can only be declared after all have been -tried. Further, creation of a phase 1 SA does not constitute success. A set -of phase 2 SAs (a tunnel) is considered success. - -</p> -<p> -The first failure occurs when an ICMP port unreachable is consistently received -without any other communication, or when there is silence from the remote -end. This usually means that either the gateway is not alive, or the -keying daemon is not functional. For an OE-permissive connection, the initiator makes a transition -to the clear-text connection but with a low lifespan. For an OE-pessimistic connection, -the initiator makes a transition to the deny connection again with a low lifespan. The lifespan in both -cases is kept low because the remote gateway may -be in the process of rebooting or be otherwise temporarily unavailable. - -</p> -<p> -The length of time to wait for the remote keying daemon to wake up is -a matter of some debate. If there is a routing failure, 5 minutes is usually long enough for the network to -re-converge. Many systems can reboot in that amount of -time as well. However, 5 minutes is far too long for most users to wait to -hear that they can not connect using OE. Implementations SHOULD make this a -tunable parameter. - -</p> -<p> -The second failure occurs after a phase 1 SA has been created, but there is -either no response to the phase 2 proposal, or the initiator receives a -negative notify (the notify must be -authenticated). The remote gateway is not prepared to do OE at this time. -As before, the initiator makes a transition to the clear-text or the deny -connection based upon connection class, but this -time with a normal lifespan. - -</p> -<p> -The third failure occurs when there is signature failure while authenticating -the remote gateway. This can occur when there has been a -key roll-over, but DNS has not caught up. In this case again, the initiator makes a -transition to the clear-text or the deny connection based -upon the connection class. However, the lifespan depends upon the remaining -time to live in the DNS. (Note that DNSSEC signed resource records have a different -expiry time than non-signed records.) - -</p> -<a name="rfc.section.3.2.6"></a><h4><a name="keyed">3.2.6</a> Keyed connection</h4> - -<p> -The pending OE connection makes a transition to this state when -session keying material (the phase 2 SAs) is derived. The initiator creates an encrypt -policy in the forwarding plane for this flow. - -</p> -<p> -There are three ways to exit this state. The first is by receipt of an -authenticated delete message (via the keying channel) from the peer. This is -normal teardown and results in a transition to the expired connection state. - -</p> -<p> -The second exit is by expiry of the forwarding plane keying material. This -starts a re-key operation with a transition back to pending OE -connection. In general, the soft expiry occurs with sufficient time left -to continue to use the keys. A re-key can fail, which may -result in the connection failing to clear-text or deny as -appropriate. In the event of a failure, the forwarding plane -policy does not change until the phase 2 SA (IPsec SA) reaches its -hard expiry. - -</p> -<p> -The third exit is in response to a negotiation from a remote -gateway. If the forwarding plane signals the control plane that it has received an -unknown SPI from the remote gateway, or an ICMP is received from the remote gateway -indicating an unknown SPI, the initiator should consider that -the remote gateway has rebooted or restarted. Since these -indications are easily forged, the implementation must -exercise care. The initiator should make a cautious -(rate-limited) attempt to re-key the connection. - -</p> -<a name="rfc.section.3.2.7"></a><h4><a name="expiring">3.2.7</a> Expiring connection</h4> - -<p> -The initiator will periodically place each of the deny, clear-text, and keyed -connections into this -sub-state. See <a href="#teardown">Renewal and teardown</a> for more details of how often this -occurs. -The initiator queries the forwarding plane for last use time of the -appropriate -policy. If the last use time is relatively recent, then the connection -returns to the -previous deny, clear-text or keyed connection state. If not, then the -connection enters -the expired connection state. - -</p> -<p> -The DNS query and answer that lead to the expiring connection state are also -examined. The DNS query may become stale. (A negative, i.e. no such record, answer -is valid for the period of time given by the MINIMUM field in an attached SOA -record. See <a href="#RFC1034">[12]</a> section 4.3.4.) -If the DNS query is stale, then a new query is made. If the results change, then the connection -makes a transition to a new state as described in potential OE connection state. - -</p> -<p> -Note that when considering how stale a connection is, both outgoing SPD and -incoming SAD must be queried as some flows may be unidirectional for some time. - -</p> -<p> -Also note that the policy at the forwarding plane is not updated unless there -is a conclusion that there should be a change. - -</p> -<a name="rfc.section.3.2.8"></a><h4><a name="anchor25">3.2.8</a> Expired connection</h4> - -<p> -Entry to this state occurs when no datagrams have been forwarded recently via the -appropriate SPD and SAD objects. The objects in the forwarding plane are -removed (logging any final byte and packet counts if appropriate) and the -connection instance in the keying plane is deleted. - -</p> -<p> -The initiator sends an ISAKMP/IKE delete to clean up the phase 2 SAs as described in -<a href="#teardown">Renewal and teardown</a>. - -</p> -<p> -Whether or not to delete the phase 1 SAs -at this time is left as a local implementation issue. Implementations -that do delete the phase 1 SAs MUST send authenticated delete messages to -indicate that they are doing so. There is an advantage to keeping -the phase 1 SAs until they expire - they may prove useful again in the -near future. - -</p> -<a name="rfc.section.3.3"></a><h4><a name="anchor26">3.3</a> Keying state machine - responder</h4> - -<p> -The responder has a set of objects identical to those of the initiator. - -</p> -<p> -The responder receives an invitation to create a keying channel from an initiator. - -</p> -<a name="rfc.section.3.3.1"></a><h4><a name="anchor27">3.3.1</a> Unauthenticated OE peer</h4> - -<p> -Upon entering this state, the responder starts a DNS lookup for a KEY record for the -initiator. -The responder looks in the reverse-map for a KEY record for the initiator if the -initiator has offered an ID_IPV4_ADDR, and in the forward map if the -initiator has offered an ID_FQDN type. (See <a href="#RFC2407">[8]</a> section -4.6.2.1.) - -</p> -<p> -The responder exits this state upon successful receipt of a KEY from DNS, and use of the key -to verify the signature of the initiator. - -</p> -<p> -Successful authentication of the peer results in a transition to the -authenticated OE Peer state. - -</p> -<p> -Note that the unauthenticated OE peer state generally occurs in the middle of the key negotiation -protocol. It is really a form of pseudo-state. - -</p> -<a name="rfc.section.3.3.2"></a><h4><a name="anchor28">3.3.2</a> Authenticated OE Peer</h4> - -<p> -The peer will eventually propose one or more phase 2 SAs. The responder uses the source and -destination address in the proposal to -finish instantiating the connection state -using the connection class table. -The responder MUST search for an identical connection object at this point. - -</p> -<p> -If an identical connection is found, then the responder deletes the old instance, -and the new object makes a transition to the pending OE connection state. This means -that new ISAKMP connections with a given peer will always use the latest -instance, which is the correct one if the peer has rebooted in the interim. - -</p> -<p> -If an identical connection is not found, then the responder makes the transition according to the -rules given for the initiator. - -</p> -<p> -Note that if the initiator is in OE-paranoid mode and the responder is in -either always-clear-text or deny, then no communication is possible according -to policy. An implementation is permitted to create new types of policies -such as "accept OE but do not initiate it". This is a local matter. - -</p> -<a name="rfc.section.3.4"></a><h4><a name="teardown">3.4</a> Renewal and teardown</h4> - -<a name="rfc.section.3.4.1"></a><h4><a name="anchor29">3.4.1</a> Aging</h4> - -<p> -A potentially unlimited number of tunnels may exist. In practice, only a few -tunnels are used during a period of time. Unused tunnels MUST, therefore, be -torn down. Detecting when tunnels are no longer in use is the subject of this section. - -</p> -<p> -There are two methods for removing tunnels: explicit deletion or expiry. - -</p> -<p> -Explicit deletion requires an IKE delete message. As the deletes -MUST be authenticated, both ends of the tunnel must maintain the -key channel (phase 1 ISAKMP SA). An implementation which refuses to either maintain or -recreate the keying channel SA will be unable to use this method. - -</p> -<p> -The tunnel expiry method, simply allows the IKE daemon to -expire normally without attempting to re-key it. - -</p> -<p> -Regardless of which method is used to remove tunnels, the implementation requires -a method to determine if the tunnel is still in use. The specifics are a -local matter, but the FreeS/WAN project uses the following criteria. These -criteria are currently implemented in the key management daemon, but could -also be implemented at the SPD layer using an idle timer. - -</p> -<p> -Set a short initial (soft) lifespan of 1 minute since many net flows last -only a few seconds. - -</p> -<p> -At the end of the lifespan, check to see if the tunnel was used by -traffic in either direction during the last 30 seconds. If so, assign a -longer tentative lifespan of 20 minutes after which, look again. If the -tunnel is not in use, then close the tunnel. - -</p> -<p> -The expiring state in the key management -system (see <a href="#expiring">Expiring connection</a>) implements these timeouts. -The timer above may be in the forwarding plane, -but then it must be re-settable. - -</p> -<p> -The tentative lifespan is independent of re-keying; it is just the time when -the tunnel's future is next considered. -(The term lifespan is used here rather than lifetime for this reason.) -Unlike re-keying, this tunnel use check is not costly and should happen -reasonably frequently. - -</p> -<p> -A multi-step back-off algorithm is not considered worth the effort here. - -</p> -<p> -If the security gateway and the client host are the -same and not a Bump-in-the-Stack or Bump-in-the-Wire implementation, tunnel -teardown decisions MAY pay attention to TCP connection status as reported -by the local TCP layer. A still-open TCP connection is almost a guarantee that more traffic is -expected. Closing of the only TCP connection through a tunnel is a -strong hint that no more traffic is expected. - -</p> -<a name="rfc.section.3.4.2"></a><h4><a name="anchor30">3.4.2</a> Teardown and cleanup</h4> - -<p> -Teardown should always be coordinated between the two ends of the tunnel by -interpreting and sending delete notifications. There is a -detailed sub-state in the expired connection state of the key manager that -relates to retransmits of the delete notifications, but this is considered to -be a keying system detail. - -</p> -<p> -On receiving a delete for the outbound SAs of a tunnel (or some subset of -them), tear down the inbound ones also and notify the remote end with a -delete. If the local system receives a delete for a tunnel which is no longer in -existence, then two delete messages have crossed paths. Ignore the delete. -The operation has already been completed. Do not generate any messages in this -situation. - -</p> -<p> -Tunnels are to be considered as bidirectional entities, even though the -low-level protocols don't treat them this way. - -</p> -<p> -When the deletion is initiated locally, rather than as a -response to a received delete, send a delete for (all) the -inbound SAs of a tunnel. If the local system does not receive a responding delete -for the outbound SAs, try re-sending the original -delete. Three tries spaced 10 seconds apart seems a reasonable -level of effort. A failure of the other end to respond after 3 attempts, -indicates that the possibility of further communication is unlikely. Remove the outgoing SAs. -(The remote system may be a mobile node that is no longer present or powered on.) - -</p> -<p> -After re-keying, transmission should switch to using the new -outgoing SAs (ISAKMP or IPsec) immediately, and the old leftover -outgoing SAs should be cleared out promptly (delete should be sent -for the outgoing SAs) rather than waiting for them to expire. This -reduces clutter and minimizes confusion for the operator doing diagnostics. - -</p> -<a name="anchor31"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.4"></a><h3>4. Impacts on IKE</h3> - -<a name="rfc.section.4.1"></a><h4><a name="anchor32">4.1</a> ISAKMP/IKE protocol</h4> - -<p> - The IKE wire protocol needs no modifications. The major changes are - implementation issues relating to how the proposals are interpreted, and from - whom they may come. - -</p> -<p> - As opportunistic encryption is designed to be useful between peers without - prior operator configuration, an IKE daemon must be prepared to negotiate - phase 1 SAs with any node. This may require a large amount of resources to - maintain cookie state, as well as large amounts of entropy for nonces, - cookies and so on. - -</p> -<p> - The major changes to support opportunistic encryption are at the IKE daemon - level. These changes relate to handling of key acquisition requests, lookup - of public keys and TXT records, and interactions with firewalls and other - security facilities that may be co-resident on the same gateway. - -</p> -<a name="rfc.section.4.2"></a><h4><a name="anchor33">4.2</a> Gateway discovery process</h4> - -<p> - In a typical configured tunnel, the address of SG-B is provided - via configuration. Furthermore, the mapping of an SPD entry to a gateway is - typically a 1:1 mapping. When the 0.0.0.0/0 SPD entry technique is used, then - the mapping to a gateway is determined by the reverse DNS records. - -</p> -<p> - The need to do a DNS lookup and wait for a reply will typically introduce a - new state and a new event source (DNS replies) to IKE. Although a -synchronous DNS request can be implemented for proof of concept, experience -is that it can cause very high latencies when a queue of queries must -all timeout in series. - -</p> -<p> - Use of an asynchronous DNS lookup will also permit overlap of DNS lookups with - some of the protocol steps. - -</p> -<a name="rfc.section.4.3"></a><h4><a name="anchor34">4.3</a> Self identification</h4> - -<p> - SG-A will have to establish its identity. Use an - IPv4 ID in phase 1. - -</p> -<p> There are many situations where the administrator of SG-A may not be - able to control the reverse DNS records for SG-A's public IP address. - Typical situations include dialup connections and most residential-type broadband Internet access - (ADSL, cable-modem) connections. In these situations, a fully qualified domain - name that is under the control of SG-A's administrator may be used - when acting as an initiator only. - The FQDN ID should be used in phase 1. See <a href="#fqdn">Use of FQDN IDs</a> - for more details and restrictions. - -</p> -<a name="rfc.section.4.4"></a><h4><a name="anchor35">4.4</a> Public key retrieval process</h4> - -<p> - Upon receipt of a phase 1 SA proposal with either an IPv4 (IPv6) ID or - an FQDN ID, an IKE daemon needs to examine local caches and - configuration files to determine if this is part of a configured tunnel. - If no configured tunnels are found, then the implementation should attempt to retrieve - a KEY record from the reverse DNS in the case of an IPv4/IPv6 ID, or - from the forward DNS in the case of FQDN ID. - -</p> -<p> - It is reasonable that if other non-local sources of policy are used - (COPS, LDAP), they be consulted concurrently but some - clear ordering of policy be provided. Note that due to variances in - latency, implementations must wait for positive or negative replies from all sources - of policy before making any decisions. - -</p> -<a name="rfc.section.4.5"></a><h4><a name="anchor36">4.5</a> Interactions with DNSSEC</h4> - -<p> - The implementation described (1.98) neither uses DNSSEC directly to - explicitly verify the authenticity of zone information, nor uses the NXT - records to provide authentication of the absence of a TXT or KEY - record. Rather, this implementation uses a trusted path to a DNSSEC - capable caching resolver. - -</p> -<p> - To distinguish between an authenticated and an unauthenticated DNS - resource record, a stub resolver capable of returning DNSSEC - information MUST be used. - -</p> -<a name="rfc.section.4.6"></a><h4><a name="anchor37">4.6</a> Required proposal types</h4> - -<a name="rfc.section.4.6.1"></a><h4><a name="phase1id">4.6.1</a> Phase 1 parameters</h4> - -<p> - Main mode MUST be used. - -</p> -<p> - The initiator MUST offer at least one proposal using some combination - of: 3DES, HMAC-MD5 or HMAC-SHA1, DH group 2 or 5. Group 5 SHOULD be - proposed first. - <a href="#RFC3526">[11]</a> -</p> -<p> - The initiator MAY offer additional proposals, but the cipher MUST not - be weaker than 3DES. The initiator SHOULD limit the number of proposals - such that the IKE datagrams do not need to be fragmented. - -</p> -<p> - The responder MUST accept one of the proposals. If any configuration - of the responder is required then the responder is not acting in an - opportunistic way. - -</p> -<p> - SG-A SHOULD use an ID_IPV4_ADDR (ID_IPV6_ADDR for IPv6) of the external - interface of SG-A for phase 1. (There is an exception, see <a href="#fqdn">Use of FQDN IDs</a>.) The authentication method MUST be RSA public key signatures. - The RSA key for SG-A SHOULD be placed into a DNS KEY record in - the reverse space of SG-A (i.e. using in-addr.arpa). - -</p> -<a name="rfc.section.4.6.2"></a><h4><a name="phase2id">4.6.2</a> Phase 2 parameters</h4> - -<p> - SG-A MUST propose a tunnel between Alice and Bob, using 3DES-CBC - mode, MD5 or SHA1 authentication. Perfect Forward Secrecy MUST be specified. - -</p> -<p> - Tunnel mode MUST be used. - -</p> -<p> - Identities MUST be ID_IPV4_ADDR_SUBNET with the mask being /32. - -</p> -<p> - Authorization for SG-A to act on Alice's behalf is determined by - looking for a TXT record in the reverse-map at Alice's address. - -</p> -<p> - Compression SHOULD NOT be mandatory. It may be offered as an option. - -</p> -<a name="anchor38"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.5"></a><h3>5. DNS issues</h3> - -<a name="rfc.section.5.1"></a><h4><a name="KEY">5.1</a> Use of KEY record</h4> - -<p> - In order to establish their own identities, SG-A and SG-B SHOULD publish - their public keys in their reverse DNS via - DNSSEC's KEY record. - See section 3 of <a href="#RFC2535">RFC 2535</a>[16]. - -</p> -<p> -<p>For example: -</p></font><pre> -KEY 0x4200 4 1 AQNJjkKlIk9...nYyUkKK8 -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> - -<blockquote class="text"><dl> -<dt>0x4200:</dt> -<dd> The flag bits, indicating that this key is prohibited - for confidentiality use (it authenticates the peer only, a separate - Diffie-Hellman exchange is used for - confidentiality), and that this key is associated with the non-zone entity - whose name is the RR owner name. No other flags are set. -</dd> -<dt>4:</dt> -<dd>This indicates that this key is for use by IPsec. -</dd> -<dt>1:</dt> -<dd>An RSA key is present. -</dd> -<dt>AQNJjkKlIk9...nYyUkKK8:</dt> -<dd>The public key of the host as described in <a href="#RFC3110">[17]</a>. -</dd> -</dl></blockquote><p> -</p> -<p>Use of several KEY records allows for key rollover. The SIG Payload in - IKE phase 1 SHOULD be accepted if the public key given by any KEY RR - validates it. - -</p> -<a name="rfc.section.5.2"></a><h4><a name="TXT">5.2</a> Use of TXT delegation record</h4> - -<p> -Alice publishes a TXT record to provide authorization for SG-A to act on -Alice's behalf. - -Bob publishes a TXT record to provide authorization for SG-B to act on Bob's -behalf. - -These records are located in the reverse DNS (in-addr.arpa) for their -respective IP addresses. The reverse DNS SHOULD be secured by DNSSEC, when -it is deployed. DNSSEC is required to defend against active attacks. - -</p> -<p> - If Alice's address is P.Q.R.S, then she can authorize another node to - act on her behalf by publishing records at: - </p> -</font><pre> -S.R.Q.P.in-addr.arpa - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<p> - The contents of the resource record are expected to be a string that - uses the following syntax, as suggested in <a href="#RFC1464">[15]</a>. - (Note that the reply to query may include other TXT resource - records used by other applications.) - - <br><hr size="1" shade="0"> -<a name="txtformat"></a> -</p> -</font><pre> -X-IPsec-Server(P)=A.B.C.D KEY - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Format of reverse delegation record </b></font><br></td></tr></table><hr size="1" shade="0"> - -</p> -<blockquote class="text"><dl> -<dt>P:</dt> -<dd> Specifies a precedence for this record. This is - similar to MX record preferences. Lower numbers have stronger - preference. - -</dd> -<dt>A.B.C.D:</dt> -<dd> Specifies the IP address of the Security Gateway - for this client machine. - -</dd> -<dt>KEY:</dt> -<dd> Is the encoded RSA Public key of the Security - Gateway. The key is provided here to avoid a second DNS lookup. If this - field is absent, then a KEY resource record should be looked up in the - reverse-map of A.B.C.D. The key is transmitted in base64 format. - -</dd> -</dl></blockquote><p> -<p> - The pieces of the record are separated by any whitespace - (space, tab, newline, carriage return). An ASCII space SHOULD - be used. - -</p> -<p> - In the case where Alice is located at a public address behind a - security gateway that has no fixed address (or no control over its - reverse-map), then Alice may delegate to a public key by domain name. - - <br><hr size="1" shade="0"> -<a name="txtfqdnformat"></a> -</p> -</font><pre> -X-IPsec-Server(P)=@FQDN KEY - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Format of reverse delegation record (FQDN version) </b></font><br></td></tr></table><hr size="1" shade="0"> - -</p> -<blockquote class="text"><dl> -<dt>P:</dt> -<dd> Is as above. - -</dd> -<dt>FQDN:</dt> -<dd> Specifies the FQDN that the Security Gateway - will identify itself with. - -</dd> -<dt>KEY:</dt> -<dd> Is the encoded RSA Public key of the Security - Gateway. -</dd> -</dl></blockquote><p> -<p> - If there is more than one such TXT record with strongest (lowest - numbered) precedence, one Security Gateway is picked arbitrarily from - those specified in the strongest-preference records. - -</p> -<a name="rfc.section.5.2.1"></a><h4><a name="anchor39">5.2.1</a> Long TXT records</h4> - -<p> - When packed into transport format, TXT records which are longer than 255 - characters are divided into smaller <character-strings>. - (See <a href="#RFC1035">[13]</a> section 3.3 and 3.3.14.) These MUST - be reassembled into a single string for processing. - Whitespace characters in the base64 encoding are to be ignored. - -</p> -<a name="rfc.section.5.2.2"></a><h4><a name="anchor40">5.2.2</a> Choice of TXT record</h4> - -<p> - It has been suggested to use the KEY, OPT, CERT, or KX records - instead of a TXT record. None is satisfactory. - -</p> -<p> The KEY RR has a protocol field which could be used to indicate a new protocol, -and an algorithm field which could be used to - indicate different contents in the key data. However, the KEY record - is clearly not intended for storing what are really authorizations, - it is just for identities. Other uses have been discouraged. - -</p> -<p> OPT resource records, as defined in <a href="#RFC2671">[14]</a> are not - intended to be used for storage of information. They are not to be loaded, - cached or forwarded. They are, therefore, inappropriate for use here. - -</p> -<p> - CERT records <a href="#RFC2538">[18]</a> can encode almost any set of - information. A custom type code could be used permitting any suitable - encoding to be stored, not just X.509. According to - the RFC, the certificate RRs are to be signed internally which may add undesirable -and unnecessary bulk. Larger DNS records may require TCP instead of UDP transfers. - -</p> -<p> - At the time of protocol design, the CERT RR was not widely deployed and - could not be counted upon. Use of CERT records will be investigated, - and may be proposed in a future revision of this document. - -</p> -<p> - KX records are ideally suited for use instead of TXT records, but had not been deployed at - the time of implementation. - -</p> -<a name="rfc.section.5.3"></a><h4><a name="fqdn">5.3</a> Use of FQDN IDs</h4> - -<p> - Unfortunately, not every administrator has control over the contents - of the reverse-map. Where the initiator (SG-A) has no suitable reverse-map, the - authorization record present in the reverse-map of Alice may refer to a - FQDN instead of an IP address. - -</p> -<p> - In this case, the client's TXT record gives the fully qualified domain - name (FQDN) in place of its security gateway's IP address. - The initiator should use the ID_FQDN ID-payload in phase 1. - A forward lookup for a KEY record on the FQDN must yield the - initiator's public key. - -</p> -<p> - This method can also be used when the external address of SG-A is - dynamic. - -</p> -<p> - If SG-A is acting on behalf of Alice, then Alice must still delegate - authority for SG-A to do so in her reverse-map. When Alice and SG-A - are one and the same (i.e. Alice is acting as an end-node) then there - is no need for this when initiating only. -</p> -<p>However, Alice must still delegate to herself if she wishes others to - initiate OE to her. See <a href="#txtfqdnformat">Format of reverse delegation record (FQDN version)</a>. - -</p> -<a name="rfc.section.5.4"></a><h4><a name="anchor41">5.4</a> Key roll-over</h4> - -<p> -Good cryptographic hygiene says that one should replace public/private key pairs -periodically. Some administrators may wish to do this as often as daily. Typical DNS -propagation delays are determined by the SOA Resource Record MINIMUM -parameter, which controls how long DNS replies may be cached. For reasonable -operation of DNS servers, administrators usually want this value to be at least several -hours, sometimes as a long as a day. This presents a problem - a new key MUST -not be used prior to it propagating through DNS. - -</p> -<p> -This problem is dealt with by having the Security Gateway generate a new -public/private key pair at least MINIMUM seconds in advance of using it. It -then adds this key to the DNS (both as a second KEY record and in additional TXT -delegation records) at key generation time. Note: only one key is allowed in -each TXT record. - -</p> -<p> -When authenticating, all gateways MUST have available all public keys -that are found in DNS for this entity. This permits the authenticating end -to check both the key for "today" and the key for "tomorrow". Note that it is -the end which is creating the signature (possesses the private key) that -determines which key is to be used. - -</p> -<a name="anchor42"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.6"></a><h3>6. Network address translation interaction</h3> - -<p> - There are no fundamentally new issues for implementing opportunistic encryption - in the presence of network address translation. Rather there are - only the regular IPsec issues with NAT traversal. - -</p> -<p> - There are several situations to consider for NAT. - -</p> -<a name="rfc.section.6.1"></a><h4><a name="anchor43">6.1</a> Co-located NAT/NAPT</h4> - -<p> - If SG-A is also performing network address translation on - behalf of Alice, then the packet should be translated prior to - being subjected to opportunistic encryption. This is in contrast to - typically configured tunnels which often exist to bridge islands of - private network address space. SG-A will use the translated source - address for phase 2, and so SG-B will look up that address to - confirm SG-A's authorization. - -</p> -<p> In the case of NAT (1:1), the address space into which the - translation is done MUST be globally unique, and control over the - reverse-map is assumed. - Placing of TXT records is possible. - -</p> -<p> In the case of NAPT (m:1), the address will be SG-A. The ability to get - KEY and TXT records in place will again depend upon whether or not - there is administrative control over the reverse-map. This is - identical to situations involving a single host acting on behalf of - itself. - - FQDN style can be used to get around a lack of a reverse-map for - initiators only. - -</p> -<a name="rfc.section.6.2"></a><h4><a name="anchor44">6.2</a> SG-A behind NAT/NAPT</h4> - -<p> - If there is a NAT or NAPT between SG-A and SG-B, then normal IPsec - NAT traversal rules apply. In addition to the transport problem - which may be solved by other mechanisms, there - is the issue of what phase 1 and phase 2 IDs to use. While FQDN could - be used during phase 1 for SG-A, there is no appropriate ID for phase 2 - that permits SG-B to determine that SG-A is in fact authorized to speak for Alice. - -</p> -<a name="rfc.section.6.3"></a><h4><a name="anchor45">6.3</a> Bob is behind a NAT/NAPT</h4> - -<p> - If Bob is behind a NAT (perhaps SG-B), then there is, in fact, no way for - Alice to address a packet to Bob. Not only is opportunistic encryption - impossible, but it is also impossible for Alice to initiate any - communication to Bob. It may be possible for Bob to initiate in such - a situation. This creates an asymmetry, but this is common for - NAPT. - -</p> -<a name="anchor46"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.7"></a><h3>7. Host implementations</h3> - -<p> - When Alice and SG-A are components of the same system, they are - considered to be a host implementation. The packet sequence scenario remains unchanged. - -</p> -<p> - Components marked Alice are the upper layers (TCP, UDP, the - application), and SG-A is the IP layer. - -</p> -<p> - Note that tunnel mode is still required. - -</p> -<p> - As Alice and SG-A are acting on behalf of themselves, no TXT based delegation - record is necessary for Alice to initiate. She can rely on FQDN in a - forward map. This is particularly attractive to mobile nodes such as - notebook computers at conferences. - To respond, Alice/SG-A will still need an entry in Alice's reverse-map. - -</p> -<a name="anchor47"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.8"></a><h3>8. Multi-homing</h3> - -<p> -If there are multiple paths between Alice and Bob (as illustrated in -the diagram with SG-D), then additional DNS records are required to establish -authorization. - -</p> -<p> -In <a href="#networkdiagram">Reference Network Diagram</a>, Alice has two ways to -exit her network: SG-A and SG-D. Previously SG-D has been ignored. Postulate -that there are routers between Alice and her set of security gateways -(denoted by the + signs and the marking of an autonomous system number for -Alice's network). Datagrams may, therefore, travel to either SG-A or SG-D en -route to Bob. - -</p> -<p> -As long as all network connections are in good order, it does not matter how -datagrams exit Alice's network. When they reach either security gateway, the -security gateway will find the TXT delegation record in Bob's reverse-map, -and establish an SA with SG-B. - -</p> -<p> -SG-B has no problem establishing that either of SG-A or SG-D may speak for -Alice, because Alice has published two equally weighted TXT delegation records: - <br><hr size="1" shade="0"> -<a name="txtmultiexample"></a> -</p> -</font><pre> -X-IPsec-Server(10)=192.1.1.5 AQMM...3s1Q== -X-IPsec-Server(10)=192.1.1.6 AAJN...j8r9== - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Multiple gateway delegation example for Alice </b></font><br></td></tr></table><hr size="1" shade="0"> - -</p> -<p> -Alice's routers can now do any kind of load sharing needed. Both SG-A and SG-D send datagrams addressed to Bob through -their tunnel to SG-B. - -</p> -<p> -Alice's use of non-equal weight delegation records to show preference of one gateway over another, has relevance only when SG-B -is initiating to Alice. - -</p> -<p> -If the precedences are the same, then SG-B has a more difficult time. It -must decide which of the two tunnels to use. SG-B has no information about -which link is less loaded, nor which security gateway has more cryptographic -resources available. SG-B, in fact, has no knowledge of whether both gateways -are even reachable. - -</p> -<p> -The Public Internet's default-free zone may well know a good route to Alice, -but the datagrams that SG-B creates must be addressed to either SG-A or SG-D; -they can not be addressed to Alice directly. - -</p> -<p> -SG-B may make a number of choices: - -<ol class="text"> -<li>It can ignore the problem and round robin among the tunnels. This - causes losses during times when one or the other security gateway is - unreachable. If this worries Alice, she can change the weights in her - TXT delegation records. -</li> -<li>It can send to the gateway from which it most recently received datagrams. - This assumes that routing and reachability are symmetrical. -</li> -<li>It can listen to BGP information from the Internet to decide which - system is currently up. This is clearly much more complicated, but if SG-B is already participating - in the BGP peering system to announce Bob, the results data may already - be available to it. -</li> -<li>It can refuse to negotiate the second tunnel. (It is unclear whether or -not this is even an option.) -</li> -<li>It can silently replace the outgoing portion of the first tunnel with the -second one while still retaining the incoming portions of both. SG-B can, -thus, accept datagrams from either SG-A or SG-D, but -send only to the gateway that most recently re-keyed with it. -</li> -</ol><p> -</p> -<p> -Local policy determines which choice SG-B makes. Note that even if SG-B has perfect -knowledge about the reachability of SG-A and SG-D, Alice may not be reachable -from either of these security gateways because of internal reachability -issues. - -</p> -<p> -FreeS/WAN implements option 5. Implementing a different option is -being considered. The multi-homing aspects of OE are not well developed and may -be the subject of a future document. - -</p> -<a name="anchor48"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.9"></a><h3>9. Failure modes</h3> - -<a name="rfc.section.9.1"></a><h4><a name="anchor49">9.1</a> DNS failures</h4> - -<p> - If a DNS server fails to respond, local policy decides - whether or not to permit communication in the clear as embodied in - the connection classes in <a href="#initclasses">Keying state machine - initiator</a>. - It is easy to mount a denial of service attack on the DNS server - responsible for a particular network's reverse-map. - Such an attack may cause all communication with that network to go in - the clear if the policy is permissive, or fail completely - if the policy is paranoid. Please note that this is an active attack. - -</p> -<p> - There are still many networks - that do not have properly configured reverse-maps. Further, if the policy is not to communicate, - the above denial of service attack isolates the target network. Therefore, the decision of whether -or not to permit communication in the clear MUST be a matter of local policy. - -</p> -<a name="rfc.section.9.2"></a><h4><a name="anchor50">9.2</a> DNS configured, IKE failures</h4> - -<p> - DNS records claim that opportunistic encryption should - occur, but the target gateway either does not respond on port 500, or - refuses the proposal. This may be because of a crash or reboot, a - faulty configuration, or a firewall filtering port 500. - -</p> -<p> - The receipt of ICMP port, host or network unreachable - messages indicates a potential problem, but MUST NOT cause communication - to fail - immediately. ICMP messages are easily forged by attackers. If such a - forgery caused immediate failure, then an active attacker could easily - prevent any - encryption from ever occurring, possibly preventing all communication. - -</p> -<p> - In these situations a clear log should be produced - and local policy should dictate if communication is then - permitted in the clear. - -</p> -<a name="rfc.section.9.3"></a><h4><a name="anchor51">9.3</a> System reboots</h4> - -<p> -Tunnels sometimes go down because the remote end crashes, -disconnects, or has a network link break. In general there is no -notification of this. Even in the event of a crash and successful reboot, -other SGs don't hear about it unless the rebooted SG has specific -reason to talk to them immediately. Over-quick response to temporary -network outages is undesirable. Note that a tunnel can be torn -down and then re-established without any effect visible to the user -except a pause in traffic. On the other hand, if one end reboots, -the other end can't get datagrams to it at all (except via -IKE) until the situation is noticed. So a bias toward quick -response is appropriate even at the cost of occasional -false alarms. - -</p> -<p> -A mechanism for recovery after reboot is a topic of current research and is not specified in this -document. - -</p> -<p> -A deliberate shutdown should include an attempt, using deletes, to notify all other SGs -currently connected by phase 1 SAs that communication is -about to fail. Again, a remote SG will assume this is a teardown. Attempts by the -remote SGs to negotiate new tunnels as replacements should be ignored. When possible, -SGs should attempt to preserve information about currently-connected SGs in non-volatile storage, so -that after a crash, an Initial-Contact can be sent to previous partners to -indicate loss of all previously established connections. - -</p> -<a name="anchor52"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.10"></a><h3>10. Unresolved issues</h3> - -<a name="rfc.section.10.1"></a><h4><a name="anchor53">10.1</a> Control of reverse DNS</h4> - -<p> - The method of obtaining information by reverse DNS lookup causes - problems for people who cannot control their reverse DNS - bindings. This is an unresolved problem in this version, and is out - of scope. - -</p> -<a name="anchor54"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.11"></a><h3>11. Examples</h3> - -<a name="rfc.section.11.1"></a><h4><a name="anchor55">11.1</a> Clear-text usage (permit policy)</h4> - -<p> -Two example scenarios follow. In the first example GW-A -(Gateway A) and GW-B (Gateway B) have always-clear-text policies, and in the second example they have an OE -policy. - -</p><br><hr size="1" shade="0"> -<a name="regulartiming"></a> -</font><pre> - Alice SG-A DNS SG-B Bob - (1) - ------(2)--------------> - <-----(3)--------------- - (4)----(5)-----> - ----------(6)------> - ------(7)-----> - <------(8)------ - <----------(9)------ - <----(10)----- - (11)-----------> - ----------(12)-----> - --------------> - <--------------- - <------------------- - <------------- - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Timing of regular transaction </b></font><br></td></tr></table><hr size="1" shade="0"> - -<p> -Alice wants to communicate with Bob. Perhaps she wants to retrieve a -web page from Bob's web server. In the absence of opportunistic -encryptors, the following events occur: - -<blockquote class="text"><dl> -<dt>(1)</dt> -<dd>Human or application 'clicks' with a name. -</dd> -<dt>(2)</dt> -<dd>Application looks up name in DNS to get IP address. -</dd> -<dt>(3)</dt> -<dd>Resolver returns A record to application. -</dd> -<dt>(4)</dt> -<dd>Application starts a TCP session or UDP session and OS sends datagram. -</dd> -<dt>(5)</dt> -<dd>Datagram is seen at first gateway from Alice (SG-A). (SG-A -makes a transition through Empty connection to always-clear connection and -instantiates a pass-through policy at the forwarding plane.) -</dd> -<dt>(6)</dt> -<dd>Datagram is seen at last gateway before Bob (SG-B). -</dd> -<dt>(7)</dt> -<dd>First datagram from Alice is seen by Bob. -</dd> -<dt>(8)</dt> -<dd>First return datagram is sent by Bob. -</dd> -<dt>(9)</dt> -<dd>Datagram is seen at Bob's gateway. (SG-B makes a transition through -Empty connection to always-clear connection and instantiates a pass-through -policy at the forwarding plane.) -</dd> -<dt>(10)</dt> -<dd>Datagram is seen at Alice's gateway. -</dd> -<dt>(11)</dt> -<dd>OS hands datagram to application. Alice sends another datagram. -</dd> -<dt>(12)</dt> -<dd>A second datagram traverses the Internet. -</dd> -</dl></blockquote><p> -</p> -<a name="rfc.section.11.2"></a><h4><a name="anchor56">11.2</a> Opportunistic encryption</h4> - -<p> -In the presence of properly configured opportunistic encryptors, the -event list is extended. - -<br><hr size="1" shade="0"> -<a name="opportunistictiming"></a> -</p> -</font><pre> - Alice SG-A DNS SG-B Bob - (1) - ------(2)--------------> - <-----(3)--------------- - (4)----(5)----->+ - ----(5B)-> - <---(5C)-- - ~~~~~~~~~~~~~(5D)~~~> - <~~~~~~~~~~~~(5E1)~~~ - ~~~~~~~~~~~~~(5E2)~~> - <~~~~~~~~~~~~(5E3)~~~ - #############(5E4)##> - <############(5E5)### - <----(5F1)-- - -----(5F2)-> - #############(5G1)##> - <----(5H1)-- - -----(5H2)-> - <############(5G2)### - #############(5G3)##> - ============(6)====> - ------(7)-----> - <------(8)------ - <==========(9)====== - <-----(10)---- - (11)-----------> - ==========(12)=====> - --------------> - <--------------- - <=================== - <------------- - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Timing of opportunistic encryption transaction </b></font><br></td></tr></table><hr size="1" shade="0"> - -<blockquote class="text"><dl> -<dt>(1)</dt> -<dd>Human or application clicks with a name. -</dd> -<dt>(2)</dt> -<dd>Application initiates DNS mapping. -</dd> -<dt>(3)</dt> -<dd>Resolver returns A record to application. -</dd> -<dt>(4)</dt> -<dd>Application starts a TCP session or UDP. -</dd> -<dt>(5)</dt> -<dd>SG-A (host or SG) sees datagram to target, and buffers it. -</dd> -<dt>(5B)</dt> -<dd>SG-A asks DNS for TXT record. -</dd> -<dt>(5C)</dt> -<dd>DNS returns TXT record(s). -</dd> -<dt>(5D)</dt> -<dd>Initial IKE Main Mode Packet goes out. -</dd> -<dt>(5E)</dt> -<dd>IKE ISAKMP phase 1 succeeds. -</dd> -<dt>(5F)</dt> -<dd>SG-B asks DNS for TXT record to prove SG-A is an agent for Alice. -</dd> -<dt>(5G)</dt> -<dd>IKE phase 2 negotiation. -</dd> -<dt>(5H)</dt> -<dd>DNS lookup by responder (SG-B). -</dd> -<dt>(6)</dt> -<dd>Buffered datagram is sent by SG-A. -</dd> -<dt>(7)</dt> -<dd>Datagram is received by SG-B, decrypted, and sent to Bob. -</dd> -<dt>(8)</dt> -<dd>Bob replies, and datagram is seen by SG-B. -</dd> -<dt>(9)</dt> -<dd>SG-B already has tunnel up with SG-A, and uses it. -</dd> -<dt>(10)</dt> -<dd>SG-A decrypts datagram and gives it to Alice. -</dd> -<dt>(11)</dt> -<dd>Alice receives datagram. Sends new packet to Bob. -</dd> -<dt>(12)</dt> -<dd>SG-A gets second datagram, sees that tunnel is up, and uses it. -</dd> -</dl></blockquote><p> -</p> -<p> - For the purposes of this section, we will describe only the changes that - occur between <a href="#regulartiming">Timing of regular transaction</a> and - <a href="#opportunistictiming">Timing of opportunistic encryption transaction</a>. This corresponds to time points 5, 6, 7, 9 and 10 on the list above. - -</p> -<a name="rfc.section.11.2.1"></a><h4><a name="anchor57">11.2.1</a> (5) IPsec datagram interception</h4> - -<p> - At point (5), SG-A intercepts the datagram because this source/destination pair lacks a policy -(the non-existent policy state). SG-A creates a hold policy, and buffers the datagram. SG-A requests keys from the keying daemon. - -</p> -<a name="rfc.section.11.2.2"></a><h4><a name="anchor58">11.2.2</a> (5B) DNS lookup for TXT record</h4> - -<p> - SG-A's IKE daemon, having looked up the source/destination pair in the connection - class list, creates a new Potential OE connection instance. SG-A starts DNS - queries. - -</p> -<a name="rfc.section.11.2.3"></a><h4><a name="anchor59">11.2.3</a> (5C) DNS returns TXT record(s)</h4> - -<p> - DNS returns properly formed TXT delegation records, and SG-A's IKE daemon - causes this instance to make a transition from Potential OE connection to Pending OE - connection. - -</p> -<p> - Using the example above, the returned record might contain: - - <br><hr size="1" shade="0"> -<a name="txtexample"></a> -</p> -</font><pre> -X-IPsec-Server(10)=192.1.1.5 AQMM...3s1Q== - </pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> -<table border="0" cellpadding="0" cellspacing="2" align="center"><tr><td align="center"><font face="monaco, MS Sans Serif" size="1"><b> Example of reverse delegation record for Bob </b></font><br></td></tr></table><hr size="1" shade="0"> - - with SG-B's IP address and public key listed. - -</p> -<a name="rfc.section.11.2.4"></a><h4><a name="anchor60">11.2.4</a> (5D) Initial IKE main mode packet goes out</h4> - -<p>Upon entering Pending OE connection, SG-A sends the initial ISAKMP - message with proposals. See <a href="#phase1id">Phase 1 parameters</a>. - -</p> -<a name="rfc.section.11.2.5"></a><h4><a name="anchor61">11.2.5</a> (5E1) Message 2 of phase 1 exchange</h4> - -<p> - SG-B receives the message. A new connection instance is created in the - unauthenticated OE peer state. - -</p> -<a name="rfc.section.11.2.6"></a><h4><a name="anchor62">11.2.6</a> (5E2) Message 3 of phase 1 exchange</h4> - -<p> - SG-A sends a Diffie-Hellman exponent. This is an internal state of the - keying daemon. - -</p> -<a name="rfc.section.11.2.7"></a><h4><a name="anchor63">11.2.7</a> (5E3) Message 4 of phase 1 exchange</h4> - -<p> - SG-B responds with a Diffie-Hellman exponent. This is an internal state of the - keying protocol. - -</p> -<a name="rfc.section.11.2.8"></a><h4><a name="anchor64">11.2.8</a> (5E4) Message 5 of phase 1 exchange</h4> - -<p> - SG-A uses the phase 1 SA to send its identity under encryption. - The choice of identity is discussed in <a href="#phase1id">Phase 1 parameters</a>. - This is an internal state of the keying protocol. - -</p> -<a name="rfc.section.11.2.9"></a><h4><a name="anchor65">11.2.9</a> (5F1) Responder lookup of initiator key</h4> - -<p> - SG-B asks DNS for the public key of the initiator. - DNS looks for a KEY record by IP address in the reverse-map. - That is, a KEY resource record is queried for 4.1.1.192.in-addr.arpa - (recall that SG-A's external address is 192.1.1.4). - SG-B uses the resulting public key to authenticate the initiator. See <a href="#KEY">Use of KEY record</a> for further details. - -</p> -<a name="rfc.section.11.2.10"></a><h4><a name="anchor66">11.2.10</a> (5F2) DNS replies with public key of initiator</h4> - -<p> -Upon successfully authenticating the peer, the connection instance makes a -transition to authenticated OE peer on SG-B. - -</p> -<p> -The format of the TXT record returned is described in -<a href="#TXT">Use of TXT delegation record</a>. - -</p> -<a name="rfc.section.11.2.11"></a><h4><a name="anchor67">11.2.11</a> (5E5) Responder replies with ID and authentication</h4> - -<p> - SG-B sends its ID along with authentication material. This is an internal - state for the keying protocol. - -</p> -<a name="rfc.section.11.2.12"></a><h4><a name="anchor68">11.2.12</a> (5G) IKE phase 2</h4> - -<a name="rfc.section.11.2.12.1"></a><h4><a name="anchor69">11.2.12.1</a> (5G1) Initiator proposes tunnel</h4> - -<p> - Having established mutually agreeable authentications (via KEY) and - authorizations (via TXT), SG-A proposes to create an IPsec tunnel for - datagrams transiting from Alice to Bob. This tunnel is established only for - the Alice/Bob combination, not for any subnets that may be behind SG-A and SG-B. - -</p> -<a name="rfc.section.11.2.12.2"></a><h4><a name="anchor70">11.2.12.2</a> (5H1) Responder determines initiator's authority</h4> - -<p> - While the identity of SG-A has been established, its authority to - speak for Alice has not yet been confirmed. SG-B does a reverse - lookup on Alice's address for a TXT record. - -</p> -<p>Upon receiving this specific proposal, SG-B's connection instance - makes a transition into the potential OE connection state. SG-B may already have an - instance, and the check is made as described above. -</p> -<a name="rfc.section.11.2.12.3"></a><h4><a name="anchor71">11.2.12.3</a> (5H2) DNS replies with TXT record(s)</h4> - -<p> - The returned key and IP address should match that of SG-A. - -</p> -<a name="rfc.section.11.2.12.4"></a><h4><a name="anchor72">11.2.12.4</a> (5G2) Responder agrees to proposal</h4> - -<p> - Should additional communication occur between, for instance, Dave and Bob using - SG-A and SG-B, a new tunnel (phase 2 SA) would be established. The phase 1 SA - may be reusable. - -</p> -<p>SG-A, having successfully keyed the tunnel, now makes a transition from - Pending OE connection to Keyed OE connection. - -</p> -<p>The responder MUST setup the inbound IPsec SAs before sending its reply. -</p> -<a name="rfc.section.11.2.12.5"></a><h4><a name="anchor73">11.2.12.5</a> (5G3) Final acknowledgment from initiator</h4> - -<p> - The initiator agrees with the responder's choice and sets up the tunnel. - The initiator sets up the inbound and outbound IPsec SAs. - -</p> -<p> - The proper authorization returned with keys prompts SG-B to make a transition - to the keyed OE connection state. - -</p> -<p>Upon receipt of this message, the responder may now setup the outbound - IPsec SAs. -</p> -<a name="rfc.section.11.2.13"></a><h4><a name="anchor74">11.2.13</a> (6) IPsec succeeds, and sets up tunnel for communication between Alice and Bob</h4> - -<p> - SG-A sends the datagram saved at step (5) through the newly created - tunnel to SG-B, where it gets decrypted and forwarded. - Bob receives it at (7) and replies at (8). - -</p> -<a name="rfc.section.11.2.14"></a><h4><a name="anchor75">11.2.14</a> (9) SG-B already has tunnel up with G1 and uses it</h4> - -<p> - At (9), SG-B has already established an SPD entry mapping Bob->Alice via a - tunnel, so this tunnel is simply applied. The datagram is encrypted to SG-A, - decrypted by SG-A and passed to Alice at (10). - -</p> -<a name="securityconsiderations"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.12"></a><h3>12. Security considerations</h3> - -<a name="rfc.section.12.1"></a><h4><a name="anchor76">12.1</a> Configured vs opportunistic tunnels</h4> - -<p> - Configured tunnels are those which are setup using bilateral mechanisms: exchanging -public keys (raw RSA, DSA, PKIX), pre-shared secrets, or by referencing keys that -are in known places (distinguished name from LDAP, DNS). These keys are then used to -configure a specific tunnel. - -</p> -<p> -A pre-configured tunnel may be on all the time, or may be keyed only when needed. -The end points of the tunnel are not necessarily static: many mobile -applications (road warrior) are considered to be configured tunnels. - -</p> -<p> -The primary characteristic is that configured tunnels are assigned specific -security properties. They may be trusted in different ways relating to exceptions to -firewall rules, exceptions to NAT processing, and to bandwidth or other quality of service restrictions. - -</p> -<p> -Opportunistic tunnels are not inherently trusted in any strong way. They are -created without prior arrangement. As the two parties are strangers, there -MUST be no confusion of datagrams that arrive from opportunistic peers and -those that arrive from configured tunnels. A security gateway MUST take care -that an opportunistic peer can not impersonate a configured peer. - -</p> -<p> -Ingress filtering MUST be used to make sure that only datagrams authorized by -negotiation (and the concomitant authentication and authorization) are -accepted from a tunnel. This is to prevent one peer from impersonating another. - -</p> -<p> -An implementation suggestion is to treat opportunistic tunnel -datagrams as if they arrive on a logical interface distinct from other -configured tunnels. As the number of opportunistic tunnels that may be -created automatically on a system is potentially very high, careful attention -to scaling should be taken into account. - -</p> -<p> -As with any IKE negotiation, opportunistic encryption cannot be secure -without authentication. Opportunistic encryption relies on DNS for its -authentication information and, therefore, cannot be fully secure without -a secure DNS. Without secure DNS, opportunistic encryption can protect against passive -eavesdropping but not against active man-in-the-middle attacks. - -</p> -<a name="rfc.section.12.2"></a><h4><a name="anchor77">12.2</a> Firewalls versus Opportunistic Tunnels</h4> - -<p> - Typical usage of per datagram access control lists is to implement various -kinds of security gateways. These are typically called "firewalls". - -</p> -<p> - Typical usage of a virtual private network (VPN) within a firewall is to -bypass all or part of the access controls between two networks. Additional -trust (as outlined in the previous section) is given to datagrams that arrive -in the VPN. - -</p> -<p> - Datagrams that arrive via opportunistically configured tunnels MUST not be -trusted. Any security policy that would apply to a datagram arriving in the -clear SHOULD also be applied to datagrams arriving opportunistically. - -</p> -<a name="rfc.section.12.3"></a><h4><a name="anchor78">12.3</a> Denial of service</h4> - -<p> - There are several different forms of denial of service that an implementor - should concern themselves with. Most of these problems are shared with - security gateways that have large numbers of mobile peers (road warriors). - -</p> -<p> - The design of ISAKMP/IKE, and its use of cookies, defend against many kinds - of denial of service. Opportunism changes the assumption that if the phase 1 (ISAKMP) - SA is authenticated, that it was worthwhile creating. Because the gateway will communicate with any machine, it is - possible to form phase 1 SAs with any machine on the Internet. - -</p> -<a name="anchor79"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.13"></a><h3>13. IANA Considerations</h3> - -<p> - There are no known numbers which IANA will need to manage. - -</p> -<a name="anchor80"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.14"></a><h3>14. Acknowledgments</h3> - -<p> - Substantive portions of this document are based upon previous work by - Henry Spencer. - -</p> -<p> - Thanks to Tero Kivinen, Sandy Harris, Wes Hardarker, Robert Moskowitz, - Jakob Schlyter, Bill Sommerfeld, John Gilmore and John Denker for their - comments and constructive criticism. - -</p> -<p> - Sandra Hoffman and Bill Dickie did the detailed proof reading and editing. - -</p> -<a name="rfc.references1"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Normative references</h3> -<table width="99%" border="0"> -<tr><td class="author-text" valign="top"><b><a name="OEspec">[1]</a></b></td> -<td class="author-text"><a href="mailto:hugh@mimosa.com">Redelmeier, D.</a> and <a href="mailto:henry@spsystems.net">H. Spencer</a>, "Opportunistic Encryption", paper http://www.freeswan.org/freeswan_trees/freeswan-1.91/doc/opportunism.spec, May 2001.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC0791">[2]</a></b></td> -<td class="author-text">Defense Advanced Research Projects Agency (DARPA), Information Processing Techniques Office and University of Southern California (USC)/Information Sciences Institute, "<a href="ftp://ftp.isi.edu/in-notes/rfc791.txt">Internet Protocol</a>", STD 5, RFC 791, September 1981.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1009">[3]</a></b></td> -<td class="author-text"><a href="mailto:">Braden, R.</a> and <a href="mailto:">J. Postel</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1009.txt">Requirements for Internet gateways</a>", RFC 1009, June 1987.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1984">[4]</a></b></td> -<td class="author-text">IAB, IESG, <a href="mailto:brian@dxcoms.cern.ch">Carpenter, B.</a> and <a href="mailto:fred@cisco.com">F. Baker</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1984.txt">IAB and IESG Statement on Cryptographic Technology and the Internet</a>", RFC 1984, August 1996.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2119">[5]</a></b></td> -<td class="author-text"><a href="mailto:-">Bradner, S.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2119.txt">Key words for use in RFCs to Indicate Requirement Levels</a>", BCP 14, RFC 2119, March 1997.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2367">[6]</a></b></td> -<td class="author-text"><a href="mailto:danmcd@eng.sun.com">McDonald, D.</a>, <a href="mailto:cmetz@inner.net">Metz, C.</a> and <a href="mailto:phan@itd.nrl.navy.mil">B. Phan</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2367.txt">PF_KEY Key Management API, Version 2</a>", RFC 2367, July 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2401">[7]</a></b></td> -<td class="author-text"><a href="mailto:kent@bbn.com">Kent, S.</a> and <a href="mailto:rja@corp.home.net">R. Atkinson</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2401.txt">Security Architecture for the Internet Protocol</a>", RFC 2401, November 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2407">[8]</a></b></td> -<td class="author-text"><a href="mailto:ddp@network-alchemy.com">Piper, D.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2407.txt">The Internet IP Security Domain of Interpretation for ISAKMP</a>", RFC 2407, November 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2408">[9]</a></b></td> -<td class="author-text"><a href="mailto:wdm@tycho.ncsc.mil">Maughan, D.</a>, <a href="mailto:mss@tycho.ncsc.mil">Schneider, M.</a> and <a href="er@raba.com">M. Schertler</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2408.txt">Internet Security Association and Key Management Protocol (ISAKMP)</a>", RFC 2408, November 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2409">[10]</a></b></td> -<td class="author-text"><a href="mailto:dharkins@cisco.com">Harkins, D.</a> and <a href="mailto:carrel@ipsec.org">D. Carrel</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2409.txt">The Internet Key Exchange (IKE)</a>", RFC 2409, November 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC3526">[11]</a></b></td> -<td class="author-text"><a href="mailto:kivinen@ssh.fi">Kivinen, T.</a> and <a href="mailto:mrskojo@cc.helsinki.fi">M. Kojo</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc3526.txt">More MODP Diffie-Hellman groups for IKE</a>", RFC 3526, March 2003.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1034">[12]</a></b></td> -<td class="author-text">Mockapetris, P., "<a href="ftp://ftp.isi.edu/in-notes/rfc1034.txt">Domain names - concepts and facilities</a>", STD 13, RFC 1034, November 1987.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1035">[13]</a></b></td> -<td class="author-text"><a href="mailto:">Mockapetris, P.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1035.txt">Domain names - implementation and specification</a>", STD 13, RFC 1035, November 1987.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2671">[14]</a></b></td> -<td class="author-text"><a href="mailto:vixie@isc.org">Vixie, P.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2671.txt">Extension Mechanisms for DNS (EDNS0)</a>", RFC 2671, August 1999.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1464">[15]</a></b></td> -<td class="author-text"><a href="mailto:rosenbaum@lkg.dec.com">Rosenbaum, R.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1464.txt">Using the Domain Name System To Store Arbitrary String Attributes</a>", RFC 1464, May 1993.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2535">[16]</a></b></td> -<td class="author-text"><a href="mailto:dee3@us.ibm.com">Eastlake, D.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2535.txt">Domain Name System Security Extensions</a>", RFC 2535, March 1999.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC3110">[17]</a></b></td> -<td class="author-text">Eastlake, D., "<a href="ftp://ftp.isi.edu/in-notes/rfc3110.txt">RSA/SHA-1 SIGs and RSA KEYs in the Domain Name System (DNS)</a>", RFC 3110, May 2001.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2538">[18]</a></b></td> -<td class="author-text"><a href="mailto:dee3@us.ibm.com">Eastlake, D.</a> and <a href="mailto:ogud@tislabs.com">O. Gudmundsson</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2538.txt">Storing Certificates in the Domain Name System (DNS)</a>", RFC 2538, March 1999.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2748">[19]</a></b></td> -<td class="author-text"><a href="mailto:David.Durham@intel.com">Durham, D.</a>, <a href="mailto:jboyle@Level3.net">Boyle, J.</a>, <a href="mailto:ronc@cisco.com">Cohen, R.</a>, <a href="mailto:herzog@iphighway.com">Herzog, S.</a>, <a href="mailto:rajan@research.att.com">Rajan, R.</a> and <a href="mailto:asastry@cisco.com">A. Sastry</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2748.txt">The COPS (Common Open Policy Service) Protocol</a>", RFC 2748, January 2000.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2663">[20]</a></b></td> -<td class="author-text"><a href="mailto:srisuresh@lucent.com">Srisuresh, P.</a> and <a href="mailto:holdrege@lucent.com">M. Holdrege</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2663.txt">IP Network Address Translator (NAT) Terminology and Considerations</a>", RFC 2663, August 1999.</td></tr> -</table> - -<a name="rfc.authors"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Authors' Addresses</h3> -<table width="99%" border="0" cellpadding="0" cellspacing="0"> -<tr><td class="author-text"> </td> -<td class="author-text">Michael C. Richardson</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Sandelman Software Works</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">470 Dawson Avenue</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Ottawa, ON K1Z 5V7</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">CA</td></tr> -<tr><td class="author" align="right">EMail: </td> -<td class="author-text"><a href="mailto:mcr@sandelman.ottawa.on.ca">mcr@sandelman.ottawa.on.ca</a></td></tr> -<tr><td class="author" align="right">URI: </td> -<td class="author-text"><a href="http://www.sandelman.ottawa.on.ca/">http://www.sandelman.ottawa.on.ca/</a></td></tr> -<tr cellpadding="3"><td> </td><td> </td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">D. Hugh Redelmeier</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Mimosa</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Toronto, ON</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">CA</td></tr> -<tr><td class="author" align="right">EMail: </td> -<td class="author-text"><a href="mailto:hugh@mimosa.com">hugh@mimosa.com</a></td></tr> -</table> -<a name="rfc.copyright"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Full Copyright Statement</h3> -<p class='copyright'> -Copyright (C) The Internet Society (2003). All Rights Reserved.</p> -<p class='copyright'> -This document and translations of it may be copied and furnished to -others, and derivative works that comment on or otherwise explain it -or assist in its implementation may be prepared, copied, published and -distributed, in whole or in part, without restriction of any kind, -provided that the above copyright notice and this paragraph are -included on all such copies and derivative works. However, this -document itself may not be modified in any way, such as by removing -the copyright notice or references to the Internet Society or other -Internet organizations, except as needed for the purpose of -developing Internet standards in which case the procedures for -copyrights defined in the Internet Standards process must be -followed, or as required to translate it into languages other than -English.</p> -<p class='copyright'> -The limited permissions granted above are perpetual and will not be -revoked by the Internet Society or its successors or assigns.</p> -<p class='copyright'> -This document and the information contained herein is provided on an -"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING -TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION -HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF -MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.</p> -<h3>Acknowledgement</h3> -<p class='copyright'> -Funding for the RFC Editor function is currently provided by the -Internet Society.</p> -</font></body></html> diff --git a/doc/src/draft-richardson-ipsec-opportunistic.xml b/doc/src/draft-richardson-ipsec-opportunistic.xml deleted file mode 100644 index d587df693..000000000 --- a/doc/src/draft-richardson-ipsec-opportunistic.xml +++ /dev/null @@ -1,2519 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE rfc SYSTEM "rfc2629.dtd"> -<?rfc toc="yes"?> -<?rfc tocdepth='2' ?> - -<rfc ipr="full2026" docName="draft-richardson-ipsec-opportunistic-12.txt"> - -<front> - <area>Security</area> - <workgroup>Independent submission</workgroup> - <title abbrev="opportunistic"> - Opportunistic Encryption using The Internet Key Exchange (IKE) - </title> - - <author initials="M." surname="Richardson" fullname="Michael C. Richardson"> - <organization abbrev="SSW">Sandelman Software Works</organization> - <address> - <postal> - <street>470 Dawson Avenue</street> - <city>Ottawa</city> - <region>ON</region> - <code>K1Z 5V7</code> - <country>CA</country> - </postal> - <email>mcr@sandelman.ottawa.on.ca</email> - <uri>http://www.sandelman.ottawa.on.ca/</uri> - </address> - </author> - - <author initials="D.H." surname="Redelmeier" - fullname="D. Hugh Redelmeier"> - <organization abbrev="Mimosa">Mimosa</organization> - <address> - <postal> - <city>Toronto</city> - <region>ON</region> - <country>CA</country> - </postal> - <email>hugh@mimosa.com</email> - </address> - </author> - - <date month="June" year="2003"></date> - -<abstract> - <t> -This document describes opportunistic encryption (OE) using the Internet Key -Exchange (IKE) and IPsec. -Each system administrator adds new -resource records to his or her Domain Name System (DNS) to support -opportunistic encryption. The objective is to allow encryption for secure communication without -any pre-arrangement specific to the pair of systems involved. - </t> - <t> -DNS is used to distribute the public keys of each -system involved. This is resistant to passive attacks. The use of DNS -Security (DNSSEC) secures this system against active attackers as well. - </t> - <t> -As a result, the administrative overhead is reduced -from the square of the number of systems to a linear dependence, and it becomes -possible to make secure communication the default even -when the partner is not known in advance. - </t> - <t> -This document is offered up as an Informational RFC. - </t> -</abstract> - -</front> - -<middle> - -<section title="Introduction"> - -<section title="Motivation"> - -<t> -The objective of opportunistic encryption is to allow encryption without -any pre-arrangement specific to the pair of systems involved. Each -system administrator adds -public key information to DNS records to support opportunistic -encryption and then enables this feature in the nodes' IPsec stack. -Once this is done, any two such nodes can communicate securely. -</t> - -<t> -This document describes opportunistic encryption as designed and -implemented by the Linux FreeS/WAN project in revisions up and including 2.00. -Note that 2.01 and beyond implements RFC3445, in a backward compatible way. -For project information, see http://www.freeswan.org. -</t> - - <t> -The Internet Architecture Board (IAB) and Internet Engineering -Steering Group (IESG) have taken a strong stand that the Internet -should use powerful encryption to provide security and -privacy <xref target="RFC1984" />. -The Linux FreeS/WAN project attempts to provide a practical means to implement this policy. - </t> - - <t> -The project uses the IPsec, ISAKMP/IKE, DNS and DNSSEC -protocols because they are -standardized, widely available and can often be deployed very easily -without changing hardware or software or retraining users. - </t> - - <t> -The extensions to support opportunistic encryption are simple. No -changes to any on-the-wire formats are needed. The only changes are to -the policy decision making system. This means that opportunistic -encryption can be implemented with very minimal changes to an existing -IPsec implementation. - </t> - - <t> -Opportunistic encryption creates a "fax effect". The proliferation -of the fax machine was possible because it did not require that everyone -buy one overnight. Instead, as each person installed one, the value -of having one increased - as there were more people that could receive faxes. -Once opportunistic encryption is installed it -automatically recognizes -other boxes using opportunistic encryption, without any further configuration -by the network -administrator. So, as opportunistic encryption software is installed on more -boxes, its value -as a tool increases. -</t> - - <t> -This document describes the infrastructure to permit deployment of -Opportunistic Encryption. -</t> - - <t> -The term S/WAN is a trademark of RSA Data Systems, and is used with permission -by this project. - </t> - -</section> - -<section title="Types of network traffic"> - <t> - To aid in understanding the relationship between security processing and IPsec - we divide network traffic into four categories: - <list style="hanging"> - <t hangText="* Deny:"> networks to which traffic is always forbidden.</t> - <t hangText="* Permit:"> networks to which traffic in the clear is permitted.</t> - <t hangText="* Opportunistic tunnel:"> networks to which traffic is encrypted if possible, but otherwise is in the clear - or fails depending on the default policy in place. - </t> - <t hangText="* Configured tunnel:"> networks to which traffic -must be encrypted, and traffic in the clear is never permitted. -A Virtual Private Network (VPN) is a form of configured tunnel. -</t> - </list> - </t> - -<t> -Traditional firewall devices handle the first two categories. -No authentication is required. -The permit policy is currently the default on the Internet. -</t> - -<t> -This document describes the third category - opportunistic tunnel, which is -proposed as the new default for the Internet. -</t> - -<t> - Category four, encrypt traffic or drop it, requires authentication of the - end points. As the number of end points is typically bounded and is typically - under a single authority, arranging for distribution of - authentication material, while difficult, does not require any new - technology. The mechanism described here provides an additional way to - distribute the authentication materials, that of a public key method that does not - require deployment of an X.509 based infrastructure. -</t> -<t> -Current Virtual Private Networks can often be replaced by an "OE paranoid" -policy as described herein. -</t> -</section> - -<section title="Peer authentication in opportunistic encryption"> - - <t> - Opportunistic encryption creates tunnels between nodes that - are essentially strangers. This is done without any prior bilateral - arrangement. - There is, therefore, the difficult question of how one knows to whom one is - talking. - </t> - - <t> - One possible answer is that since no useful - authentication can be done, none should be tried. This mode of operation is - named "anonymous encryption". An active man-in-the-middle attack can be - used to thwart the privacy of this type of communication. - Without peer authentication, there is no way to prevent this kind of attack. - </t> - - <t> -Although a useful mode, anonymous encryption is not the goal of this -project. Simpler methods are available that can achieve anonymous -encryption only, but authentication of the peer is a desireable goal. -The latter is achieved through key distribution in DNS, leveraging upon -the authentication of the DNS in DNSSEC. -</t> - - <t> - Peers are, therefore, authenticated with DNSSEC when available. Local policy -determines how much trust to extend when DNSSEC is not available. - </t> - - <t> - However, an essential premise of building private connections with - strangers is that datagrams received through opportunistic tunnels - are no more special than datagrams that arrive in the clear. - Unlike in a VPN, these datagrams should not be given any special - exceptions when it comes to auditing, further authentication or - firewalling. - </t> - - <t> - When initiating outbound opportunistic encryption, local - configuration determines what happens if tunnel setup fails. It may be that - the packet goes out in the clear, or it may be dropped. - </t> - - </section> - -<section title="Use of RFC2119 terms"> -<t> - The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, - SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL, when they appear in this - document, are to be interpreted as described in <xref target="RFC2119" /> -</t> -</section> - -</section> - -<section title="Overview"> - - <section title="Reference diagram"> - - <figure anchor="networkdiagram" title="Reference Network Diagram"> - <preamble>The following network diagram is used in the rest of - this document as the canonical diagram:</preamble> - <artwork> - [Q] [R] - . . AS2 - [A]----+----[SG-A].......+....+.......[SG-B]-------[B] - | ...... - AS1 | ..PI.. - | ...... - [D]----+----[SG-D].......+....+.......[C] AS3 - - - </artwork> - <postamble></postamble> - - </figure> - - <t> - In this diagram, there are four end-nodes: A, B, C and D. - There are three security gateways, SG-A, SG-B, SG-D. A, D, SG-A and - SG-D are part - of the same administrative authority, AS1. SG-A and SG-D are on two - different exit - paths from organization 1. SG-B/B is an independent organization, AS2. - Nodes Q and R are nodes on the Internet. PI is the Public - Internet ("The Wild"). - </t> - - </section> - - <section title="Terminology"> - - <t> - The following terminology is used in this document: - </t> - - <list style="hanging"> - <t hangText="Security gateway (or simply gateway):"> a system that performs IPsec tunnel - mode encapsulation/decapsulation. [SG-x] in the diagram.</t> - <t hangText="Alice:"> node [A] in the diagram. When an IP address is needed, this is 192.1.0.65.</t> - <t hangText="Bob:"> node [B] in the diagram. When an IP address is needed, this is 192.2.0.66.</t> - <t hangText="Carol:"> node [C] in the diagram. When an IP address is needed, this is 192.1.1.67.</t> - <t hangText="Dave:"> node [D] in the diagram. When an IP address is needed, this is 192.3.0.68.</t> - <t hangText="SG-A:"> Alice's security gateway. Internally it is 192.1.0.1, externally it is 192.1.1.4.</t> - <t hangText="SG-B:"> Bob's security gateway. Internally it is 192.2.0.1, externally it is 192.1.1.5.</t> - <t hangText="SG-D:"> Dave's security gateway. Also Alice's backup security gateway. Internally it is 192.3.0.1, externally it is 192.1.1.6.</t> - <t hangText="."> A period represents an untrusted network of unknown - type.</t> - <t hangText="Configured tunnel:"> a tunnel that - is directly and deliberately hand configured on participating gateways. - Configured tunnels are typically given a higher level of - trust than opportunistic tunnels.</t> - - <t hangText="Road warrior tunnel:"> a configured tunnel connecting one - node with a fixed IP address and one node with a variable IP address. - A road warrior (RW) connection must be initiated by the - variable node, since the fixed node cannot know the - current address for the road warrior. </t> - - <t hangText="Anonymous encryption:"> - the process of encrypting a session without any knowledge of who the - other parties are. No authentication of identities is done.</t> - - <t hangText="Opportunistic encryption:"> - the process of encrypting a session with authenticated knowledge of - who the other party is.</t> - - <t hangText="Lifetime:"> - the period in seconds (bytes or datagrams) for which a security - association will remain alive before needing to be re-keyed.</t> - - <t hangText="Lifespan:"> - the effective time for which a security association remains useful. A - security association with a lifespan shorter than its lifetime would - be removed when no longer needed. A security association with a - lifespan longer than its lifetime would need to be re-keyed one or - more times.</t> - - <t hangText="Phase 1 SA:"> an ISAKMP/IKE security association sometimes - referred to as a keying channel.</t> - - <t hangText="Phase 2 SA:"> an IPsec security association.</t> - - <t hangText="Tunnel:"> another term for a set of phase 2 SA (one in each direction).</t> - - <t hangText="NAT:"> Network Address Translation - (see <xref target="RFC2663" />).</t> - - <t hangText="NAPT:"> Network Address and Port Translation - (see <xref target="RFC2663" />).</t> - - <t hangText="AS:"> an autonomous system </t> - - <t hangText="FQDN:"> Fully-Qualified Domain Name </t> - - <t hangText="Default-free zone:"> - a set of routers that maintain a complete set of routes to - all currently reachable destinations. Having such a list, these routers - never make use of a default route. A datagram with a destination address - not matching any route will be dropped by such a router. - </t> - - </list> - </section> - -<section title="Model of operation"> - -<t> -The opportunistic encryption security gateway (OE gateway) is a regular -gateway node as described in <xref target="RFC0791" /> section 2.4 and -<xref target="RFC1009" /> with the additional capabilities described here and -in <xref target="RFC2401" />. -The algorithm described here provides a way to determine, for each datagram, -whether or not to encrypt and tunnel the datagram. Two important things -that must be determined are whether or not to encrypt and tunnel and, if -so, the destination address or name of the tunnel end point which should be used. -</t> - -<section title="Tunnel authorization"> -<t> -The OE gateway determines whether or not to create a tunnel based on -the destination address of each packet. Upon receiving a packet with a destination -address not recently seen, the OE gateway performs a lookup in DNS for an -authorization resource record (see <xref target="TXT"/>). The record is located using -the IP address to perform a search in the in-addr.arpa (IPv4) or ip6.arpa -(IPv6) maps. If an authorization record is found, the OE gateway -interprets this as a request for a tunnel to be formed. -</t> -</section> - -<section title="Tunnel end-point discovery"> - -<t> -The authorization resource record also provides the address or name of the tunnel -end point which should be used. -</t> -<t> -The record may also provide the public RSA key of the tunnel end point -itself. This is provided for efficiency only. If the public RSA key is not -present, the OE gateway performs a second lookup to find a KEY -resource record for the end point address or name. -</t> -<t> -Origin and integrity protection of the resource records is provided by -DNSSEC (<xref target="RFC2535"/>). <xref target="nodnssec"/> -documents an optional restriction on the tunnel end point if DNSSEC signatures -are not available for the relevant records. -</t> - -</section> - -<section title="Caching of authorization results"> -<t> -The OE gateway maintains a cache, in the forwarding plane, of -source/destination pairs for which opportunistic encryption has been -attempted. This cache maintains a record of whether or not OE was -successful so that subsequent datagrams can be forwarded properly -without additional delay. -</t> - -<t> -Successful negotiation of OE instantiates a new security association. -Failure to negotiate OE results in creation of a -forwarding policy entry either to drop or transmit in the clear future -datagrams. This negative cache is necessary to avoid the possibly lengthy process of repeatedly looking -up the same information. -</t> - -<t> -The cache is timed out periodically, as described in <xref target="teardown" />. -This removes entries that are no longer -being used and permits the discovery of changes in authorization policy. -</t> -</section> - -</section> <!-- "Model of operation" --> - -</section> <!-- "Overview" --> - -<section title="Protocol Specification"> - -<t> -The OE gateway is modeled to have a forwarding plane and a control -plane. A control channel, such as PF_KEY, connects the two planes. -(See <xref target="RFC2367" />.) -The forwarding plane performs per datagram operations. The control plane -contains a keying daemon, such as ISAKMP/IKE, and performs all -authorization, peer authentication and key derivation functions. -</t> - -<section title="Forwarding plane state machine"> - -<t> -Let the OE gateway maintain a collection of objects -- a superset of the -security policy database (SPD) specified in <xref target="RFC2401" />. For -each combination of source and destination address, an SPD -object exists in one of five following states. -Prior to forwarding each datagram, the responder uses the source and -destination addresses to pick an entry from the SPD. -The SPD then determines if and how the packet is forwarded. -</t> - -<!-- from file forwardingstate.txt --> -<artwork><![CDATA[ - .--------------. - | non-existant | - | policy | - `--------------' - | - | PF_ACQUIRE - | - |<---------. - V | new packet - .--------------. | (maybe resend PF_ACQUIRE) - | hold policy |--' - | |--. - `--------------' \ pass - | | \ msg .---------. - | | \ V | forward - | | .-------------. | packet - create | | | pass policy |--' - IPsec | | `-------------' - SA | | - | \ - | \ - V \ deny - .---------. \ msg - | encrypt | \ - | policy | \ ,---------. - `---------' \ | | discard - \ V | packet - .-------------. | - | deny policy |--' - '-------------' -]]></artwork> - - -<section title="Non-existent policy"> -<t> -If the gateway does not find an entry, then this policy applies. -The gateway creates an entry with an initial state of "hold policy" and requests -keying material from the keying daemon. The gateway does not forward the datagram, -rather it SHOULD attach the datagram to the SPD entry as the "first" datagram and retain it -for eventual transmission in a new state. - -</t> -</section> - -<section title="Hold policy"> -<t> -The gateway requests keying material. If the interface to the keying -system is lossy (PF_KEY, for instance, can be), the implementation -SHOULD include a mechanism to retransmit the -keying request at a rate limited to less than 1 request per second. -The gateway does not forward the datagram. The gateway SHOULD attach the -datagram to the SPD entry as the "last" datagram where it is retained -for eventual transmission. -If there is a datagram already so stored, then that already stored datagram is discarded. -</t> -<t> -The rational behind saving the the "first" and "last" datagrams are as follows: -The "first" datagram is probably a TCP SYN packet. Once there is keying -established, the gateway will release this datagram, avoiding the need to -for the end-point to retransmit the datagram. In the case where the connection -was not a TCP connection, buyt was instead a streaming protocol or a DNS request, -the "last" datagram that was retained is likely the most recent data. The difference -between "first" and "last" may also help the end-points determine -which data awas dropped while negotiation took place. -</t> -</section> - -<section title="Pass-through policy"> -<t> -The gateway forwards the datagram using the normal forwarding table. -The gateway enters this state only by command from the keying daemon, -and upon entering this state, also forwards the "first" and "last" datagrams. -</t> -</section> - -<section title="Deny policy"> -<t> -The gateway discards the datagram. The gateway enters this state only by -command -from the keying daemon, and upon entering this state, discards the "first" -and "last" datagrams. -An implementation MAY provide the administator with a control to determine -if further datagrams cause ICMP messages -to be generated (i.e. ICMP Destination Unreachable, Communication -Administratively Prohibited. type=3, code=13). -</t> -</section> - -<section title="Encrypt policy"> -<t> -The gateway encrypts the datagram using the indicated security association database -(SAD) entry. The gateway enters this state only by command from the keying daemon, and upon entering -this state, releases and forwards the "first" and "last" datagrams using the -new encrypt policy. -</t> -<t> -If the associated SAD entry expires because of byte, packet or time limits, then -the entry returns to the Hold policy, and an expire message is sent to the keying daemon. -</t> -</section> - -<t> -All states may be created directly by the keying daemon while acting as a -gateway. -</t> - -</section> <!-- "Datagram state machine" --> - - -<section anchor="initclasses" title="Keying Daemon -- initiator"> -<t> -Let the keying daemon maintain a collection of objects. Let them be -called "connections" or "conn"s. There are two categories of -connection objects: classes and instances. A class represents an -abstract policy - what could be. An instance represents an actual connection - -what is implemented at the time. -</t> - -<t> -Let there be two further subtypes of connections: keying channels (Phase -1 SAs) and data channels (Phase 2 SAs). Each data channel object may have -a corresponding SPD and SAD entry maintained by the datagram state machine. -</t> - -<t> -For the purposes of opportunistic encryption, there MUST, at least, be -connection classes known as "deny", "always-clear-text", "OE-permissive", and -"OE-paranoid". -The latter two connection classes define a set of source and/or destination -addresses for which opportunistic encryption will be attempted. -The administrator MAY set policy options in a number of additional places. -An implementation MAY create additional connection classes to further refine -these policies. -</t> - -<t> -The simplest system may need only the "OE-permissive" connection, and would -list its own (single) IP address as the source address of this policy and -the wild-card address 0.0.0.0/0 as the destination IPv4 address. That is, the -simplest policy is to try opportunistic encryption with all destinations. -</t> - -<t> -The distinction between permissive and paranoid OE use will become clear -in the state transition differences. In general a permissive OE will, on -failure, install a pass-through policy, while a paranoid OE will, on failure, -install a drop policy. -</t> - -<t> -In this description of the keying machine's state transitions, the states -associated with the keying system itself are omitted because they are best documented in the keying system -(<xref target="RFC2407" />, -<xref target="RFC2408" /> and <xref target="RFC2409" /> for ISAKMP/IKE), -and the details are keying system specific. Opportunistic encryption is not -dependent upon any specific keying protocol, but this document does provide -requirements for those using ISAKMP/IKE to assure that implementations inter-operate. -</t> -<t> -The state transitions that may be involved in communicating with the -forwarding plane are omitted. PF_KEY and similar protocols have their own -set of states required for message sends and completion notifications. -</t> -<t> -Finally, the retransmits and recursive lookups that are normal for DNS are -not included in this description of the state machine. -</t> - -<!-- from file initiatorstate.txt --> -<artwork><![CDATA[ - - | - | PF_ACQUIRE - | - V - .---------------. - | non-existant | - | connection | - `---------------' - | | | - send , | \ -expired pass / | \ send -conn. msg / | \ deny - ^ / | \ msg - | V | do \ -.---------------. | DNS \ .---------------. -| clear-text | | lookup `->| deny |---> expired -| connection | | for | connection | connection -`---------------' | destination `---------------' - ^ ^ | ^ - | | no record | | - | | OE-permissive V | no record - | | .---------------. | OE-paranoid - | `------------| potential OE |---------' - | | connection | ^ - | `---------------' | - | | | - | | got TXT record | DNSSEC failure - | | reply | - | V | wrong - | .---------------. | failure - | | authenticate |---------' - | | & parse TXT RR| ^ - | repeated `---------------' | - | ICMP | | - | failures | initiate IKE to | - | (short-timeout) | responder | - | V | - | phase-2 .---------------. | failure - | failure | pending |---------' - | (normal | OE | ^ - | timeout) | |invalid | phase-2 failure (short-timeout) - | | |<--.SPI | ICMP failures (normal timeout) - | | | | | - | | +=======+ |---' | - | | | IKE | | ^ | - `--------------| | states|---------------' - | +=======+ | | - `---------------' | - | IPsec SA | invalid SPI - | established | - V | rekey time - .--------------. | - | keyed |<---|-------------------------------. - | connection |----' | - `--------------' | - | timer | - | | - V | - .--------------. connection still active | - clear-text----->| expired |------------------------------------' - deny----->| connection | - `--------------' - | dead connected - deleted - V -]]></artwork> - - -<section title="Nonexistent connection"> -<t> -There is no connection instance for a given source/destination address pair. -Upon receipt of a request for keying material for this -source/destination pair, the initiator searches through the connection classes to -determine the most appropriate policy. Upon determining an appropriate -connection class, an instance object is created of that type. -Both of the OE types result in a potential OE connection. -</t> -<t>Failure to find an appropriate connection class results in an -administrator defined default. -</t> -<t> -In each case, when the initiator finds an appropriate class for the new flow, -an instance connection is made of the class which matched. -</t> -</section> - -<section title="Clear-text connection"> -<t> -The non-existent connection makes a transition to this state when an -always-clear-text class is instantiated, or when an OE-permissive -connection fails. During the transition, the initiator creates a pass-through -policy object in the forwarding plane for the appropriate flow. -</t> -<t> -Timing out is the only way to leave this state -(see <xref target="expiring" />). -</t> -</section> - -<section title="Deny connection"> -<t> -The empty connection makes a transition to this state when a -deny class is instantiated, or when an OE-paranoid connection fails. -During the transition, the initiator creates a deny policy object in the forwarding plane -for the appropriate flow. -</t> -<t> -Timing out is the only way to leave this state -(see <xref target="expiring" />). -</t> -</section> - -<section title="Potential OE connection"> -<t> -The empty connection makes a transition to this state when one of either OE class is instantiated. -During the transition to this state, the initiator creates a hold policy object in the -forwarding plane for the appropriate flow. -</t> -<t> -In addition, when making a transition into this state, DNS lookup is done in -the reverse-map for a TXT delegation resource record (see <xref target="TXT" />). -The lookup key is the destination address of the flow. -</t> -<t> -There are three ways to exit this state: -<list style="numbers"> -<t>DNS lookup finds a TXT delegation resource record.</t> -<t>DNS lookup does not find a TXT delegation resource record.</t> -<t>DNS lookup times out.</t> -</list> -</t> - -<t> -Based upon the results of the DNS lookup, the potential OE connection makes a -transition to the pending OE connection state. The conditions for a -successful DNS look are: -<list style="numbers"> -<t>DNS finds an appropriate resource record</t> -<t>It is properly formatted according to <xref target="TXT" /></t> -<t> if DNSSEC is enabled, then the signature has been vouched for.</t> -</list> - -Note that if the initiator does not find the public key -present in the TXT delegation record, then the public key must -be looked up as a sub-state. Only successful completion of all the -DNS lookups is considered a success. -</t> -<t> -If DNS lookup does not find a resource record or DNS times out, then the -initiator considers the receiver not OE capable. If this is an OE-paranoid instance, -then the potential OE connection makes a transition to the deny connection state. -If this is an OE-permissive instance, then the potential OE connection makes a transition to the -clear-text connection state. -</t> -<t> -If the initiator finds a resource record but it is not properly formatted, or -if DNSSEC is -enabled and reports a failure to authenticate, then the potential OE -connection makes a -transition to the deny connection state. This action SHOULD be logged. If the -administrator wishes to override this transition between states, then an -always-clear class can be installed for this flow. An implementation MAY make -this situation a new class. -</t> - -<section anchor="nodnssec" title="Restriction on unauthenticated TXT delegation records"> -<t> -An implementation SHOULD also provide an additional administrative control -on delegation records and DNSSEC. This control would apply to delegation -records (the TXT records in the reverse-map) that are not protected by -DNSSEC. -Records of this type are only permitted to delegate to their own address as -a gateway. When this option is enabled, an active attack on DNS will be -unable to redirect packets to other than the original destination. -<!-- This was asked for by Bill Sommerfeld --> -</t> -</section> -</section> - -<section title="Pending OE connection"> -<t> -The potential OE connection makes a transition to this state when -the initiator determines that all the information required from the DNS lookup is present. -Upon entering this state, the initiator attempts to initiate keying to the gateway -provided. -</t> -<t> -Exit from this state occurs either with a successfully created IPsec SA, or -with a failure of some kind. Successful SA creation results in a transition -to the key connection state. -</t> -<t> -Three failures have caused significant problems. They are clearly not the -only possible failures from keying. -</t> -<t> -Note that if there are multiple gateways available in the TXT delegation -records, then a failure can only be declared after all have been -tried. Further, creation of a phase 1 SA does not constitute success. A set -of phase 2 SAs (a tunnel) is considered success. -</t> -<t> -The first failure occurs when an ICMP port unreachable is consistently received -without any other communication, or when there is silence from the remote -end. This usually means that either the gateway is not alive, or the -keying daemon is not functional. For an OE-permissive connection, the initiator makes a transition -to the clear-text connection but with a low lifespan. For an OE-pessimistic connection, -the initiator makes a transition to the deny connection again with a low lifespan. The -lifespan in both -cases is kept low because the remote gateway may -be in the process of rebooting or be otherwise temporarily unavailable. -</t> -<t> -The length of time to wait for the remote keying daemon to wake up is -a matter of some debate. If there is a routing failure, 5 minutes is usually long -enough for the network to -re-converge. Many systems can reboot in that amount of -time as well. However, 5 minutes is far too long for most users to wait to -hear that they can not connect using OE. Implementations SHOULD make this a -tunable parameter. -</t> -<t> -The second failure occurs after a phase 1 SA has been created, but there is -either no response to the phase 2 proposal, or the initiator receives a -negative notify (the notify must be -authenticated). The remote gateway is not prepared to do OE at this time. -As before, the initiator makes a transition to the clear-text or the deny -connection based upon connection class, but this -time with a normal lifespan. -</t> -<t> -The third failure occurs when there is signature failure while authenticating -the remote gateway. This can occur when there has been a -key roll-over, but DNS has not caught up. In this case again, the initiator makes a -transition to the clear-text or the deny connection based -upon the connection class. However, the lifespan depends upon the remaining -time to live in the DNS. (Note that DNSSEC signed resource records have a different -expiry time than non-signed records.) -<!-- dig @gateway would also work here --> -</t> - -</section> - -<section anchor="keyed" title="Keyed connection"> -<t> -The pending OE connection makes a transition to this state when -session keying material (the phase 2 SAs) is derived. The initiator creates an encrypt -policy in the forwarding plane for this flow. -</t> -<t> -There are three ways to exit this state. The first is by receipt of an -authenticated delete message (via the keying channel) from the peer. This is -normal teardown and results in a transition to the expired connection state. -</t> -<t> -The second exit is by expiry of the forwarding plane keying material. This -starts a re-key operation with a transition back to pending OE -connection. In general, the soft expiry occurs with sufficient time left -to continue to use the keys. A re-key can fail, which may -result in the connection failing to clear-text or deny as -appropriate. In the event of a failure, the forwarding plane -policy does not change until the phase 2 SA (IPsec SA) reaches its -hard expiry. -</t> -<t> -The third exit is in response to a negotiation from a remote -gateway. If the forwarding plane signals the control plane that it has received an -unknown SPI from the remote gateway, or an ICMP is received from the remote gateway -indicating an unknown SPI, the initiator should consider that -the remote gateway has rebooted or restarted. Since these -indications are easily forged, the implementation must -exercise care. The initiator should make a cautious -(rate-limited) attempt to re-key the connection. -</t> -</section> - -<section anchor="expiring" title="Expiring connection"> -<t> -The initiator will periodically place each of the deny, clear-text, and keyed -connections into this -sub-state. See <xref target="teardown" /> for more details of how often this -occurs. -The initiator queries the forwarding plane for last use time of the -appropriate -policy. If the last use time is relatively recent, then the connection -returns to the -previous deny, clear-text or keyed connection state. If not, then the -connection enters -the expired connection state. -</t> -<t> -The DNS query and answer that lead to the expiring connection state are also -examined. The DNS query may become stale. (A negative, i.e. no such record, answer -is valid for the period of time given by the MINIMUM field in an attached SOA -record. See <xref target="RFC1034" /> section 4.3.4.) -If the DNS query is stale, then a new query is made. If the results change, then the connection -makes a transition to a new state as described in potential OE connection state. -</t> -<t> -Note that when considering how stale a connection is, both outgoing SPD and -incoming SAD must be queried as some flows may be unidirectional for some time. -</t> -<t> -Also note that the policy at the forwarding plane is not updated unless there -is a conclusion that there should be a change. -</t> - -</section> -<section title="Expired connection"> -<t> -Entry to this state occurs when no datagrams have been forwarded recently via the -appropriate SPD and SAD objects. The objects in the forwarding plane are -removed (logging any final byte and packet counts if appropriate) and the -connection instance in the keying plane is deleted. -</t> -<t> -The initiator sends an ISAKMP/IKE delete to clean up the phase 2 SAs as described in -<xref target="teardown" />. -</t> -<t> -Whether or not to delete the phase 1 SAs -at this time is left as a local implementation issue. Implementations -that do delete the phase 1 SAs MUST send authenticated delete messages to -indicate that they are doing so. There is an advantage to keeping -the phase 1 SAs until they expire - they may prove useful again in the -near future. -</t> -</section> - -</section> <!-- "Keying state machine - initiator" --> - -<section title="Keying Daemon - responder"> -<t> -The responder has a set of objects identical to those of the initiator. -</t> -<t> -The responder receives an invitation to create a keying channel from an initiator. -</t> - -<!-- from file responderstate.txt --> -<artwork><![CDATA[ - | - | IKE main mode - | phase 1 - V - .-----------------. - | unauthenticated | - | OE peer | - `-----------------' - | - | lookup KEY RR in in-addr.arpa - | (if ID_IPV4_ADDR) - | lookup KEY RR in forward - | (if ID_FQDN) - V - .-----------------. RR not found - | received DNS |---------------> log failure - | reply | - `----+--------+---' - phase 2 | \ misformatted - proposal | `------------------> log failure - V - .----------------. - | authenticated | identical initiator - | OE peer |--------------------> initiator - `----------------' connection found state machine - | - | look for TXT record for initiator - | - V - .---------------. - | authorized |---------------------> log failure - | OE peer | - `---------------' - | - | - V - potential OE - connection in - initiator state - machine - - -$Id: draft-richardson-ipsec-opportunistic.xml,v 1.1 2004/03/15 20:35:24 as Exp $ -]]></artwork> - - -<section title="Unauthenticated OE peer"> -<t> -Upon entering this state, the responder starts a DNS lookup for a KEY record for the -initiator. -The responder looks in the reverse-map for a KEY record for the initiator if the -initiator has offered an ID_IPV4_ADDR, and in the forward map if the -initiator has offered an ID_FQDN type. (See <xref target="RFC2407" /> section -4.6.2.1.) -</t> -<t> -The responder exits this state upon successful receipt of a KEY from DNS, and use of the key -to verify the signature of the initiator. -</t> - -<!-- -<t> -The public key that is retrieved should be stored in stable storage for an -administratively defined period of time, (typically several months if -possible). If a key has previously been stored on disk, then the returned key -should be compared to what has been received, and the key considered valid -only if they match. -</t> ---> - -<t> -Successful authentication of the peer results in a transition to the -authenticated OE Peer state. -</t> -<t> -Note that the unauthenticated OE peer state generally occurs in the middle of the key negotiation -protocol. It is really a form of pseudo-state. -</t> -</section> - -<section title="Authenticated OE Peer"> -<t> -The peer will eventually propose one or more phase 2 SAs. The responder uses the source and -destination address in the proposal to -finish instantiating the connection state -using the connection class table. -The responder MUST search for an identical connection object at this point. -</t> -<t> -If an identical connection is found, then the responder deletes the old instance, -and the new object makes a transition to the pending OE connection state. This means -that new ISAKMP connections with a given peer will always use the latest -instance, which is the correct one if the peer has rebooted in the interim. -</t> -<t> -If an identical connection is not found, then the responder makes the transition according to the -rules given for the initiator. -</t> -<t> -Note that if the initiator is in OE-paranoid mode and the responder is in -either always-clear-text or deny, then no communication is possible according -to policy. An implementation is permitted to create new types of policies -such as "accept OE but do not initiate it". This is a local matter. - </t> -</section> - -</section> <!-- "Keying state machine - responder" --> - -<section anchor="teardown" title="Renewal and teardown"> - <section title="Aging"> -<t> -A potentially unlimited number of tunnels may exist. In practice, only a few -tunnels are used during a period of time. Unused tunnels MUST, therefore, be -torn down. Detecting when tunnels are no longer in use is the subject of this section. -</t> - -<t> -There are two methods for removing tunnels: explicit deletion or expiry. -</t> - -<t> -Explicit deletion requires an IKE delete message. As the deletes -MUST be authenticated, both ends of the tunnel must maintain the -key channel (phase 1 ISAKMP SA). An implementation which refuses to either maintain or -recreate the keying channel SA will be unable to use this method. -</t> - -<t> -The tunnel expiry method simply allows the IKE daemon to -expire normally without attempting to re-key it. -</t> - -<t> -Regardless of which method is used to remove tunnels, the implementation MUST -a method to determine if the tunnel is still in use. The specifics are a -local matter, but the FreeS/WAN project uses the following criteria. These -criteria are currently implemented in the key management daemon, but could -also be implemented at the SPD layer using an idle timer. -</t> - -<t> -Set a short initial (soft) lifespan of 1 minute since many net flows last -only a few seconds. -</t> - -<t> -At the end of the lifespan, check to see if the tunnel was used by -traffic in either direction during the last 30 seconds. If so, assign a -longer tentative lifespan of 20 minutes after which, look again. If the -tunnel is not in use, then close the tunnel. -</t> - -<t> -The expiring state in the key management -system (see <xref target="expiring" />) implements these timeouts. -The timer above may be in the forwarding plane, -but then it must be re-settable. -</t> - -<t> -The tentative lifespan is independent of re-keying; it is just the time when -the tunnel's future is next considered. -(The term lifespan is used here rather than lifetime for this reason.) -Unlike re-keying, this tunnel use check is not costly and should happen -reasonably frequently. -</t> - -<t> -A multi-step back-off algorithm is not considered worth the effort here. -</t> - -<t> -If the security gateway and the client host are the -same and not a Bump-in-the-Stack or Bump-in-the-Wire implementation, tunnel -teardown decisions MAY pay attention to TCP connection status as reported -by the local TCP layer. A still-open TCP connection is almost a guarantee that more traffic is -expected. Closing of the only TCP connection through a tunnel is a -strong hint that no more traffic is expected. -</t> - -</section> <!-- "Aging" --> - -<section title="Teardown and cleanup"> - -<t> -Teardown should always be coordinated between the two ends of the tunnel by -interpreting and sending delete notifications. There is a -detailed sub-state in the expired connection state of the key manager that -relates to retransmits of the delete notifications, but this is considered to -be a keying system detail. -</t> - -<t> -On receiving a delete for the outbound SAs of a tunnel (or some subset of -them), tear down the inbound ones also and notify the remote end with a -delete. If the local system receives a delete for a tunnel which is no longer in -existence, then two delete messages have crossed paths. Ignore the delete. -The operation has already been completed. Do not generate any messages in this -situation. -</t> -<t> -Tunnels are to be considered as bidirectional entities, even though the -low-level protocols don't treat them this way. -</t> - -<t> -When the deletion is initiated locally, rather than as a -response to a received delete, send a delete for (all) the -inbound SAs of a tunnel. If the local system does not receive a responding delete -for the outbound SAs, try re-sending the original -delete. Three tries spaced 10 seconds apart seems a reasonable -level of effort. A failure of the other end to respond after 3 attempts, -indicates that the possibility of further communication is unlikely. Remove the outgoing SAs. -(The remote system may be a mobile node that is no longer present or powered on.) -</t> - -<t> -After re-keying, transmission should switch to using the new -outgoing SAs (ISAKMP or IPsec) immediately, and the old leftover -outgoing SAs should be cleared out promptly (delete should be sent -for the outgoing SAs) rather than waiting for them to expire. This -reduces clutter and minimizes confusion for the operator doing diagnostics. -</t> - -</section> - -</section> - -</section> <!-- "Specification" --> - -<section title="Impacts on IKE"> - - <section title="ISAKMP/IKE protocol"> - <t> - The IKE wire protocol needs no modifications. The major changes are - implementation issues relating to how the proposals are interpreted, and from - whom they may come. - </t> - <t> - As opportunistic encryption is designed to be useful between peers without - prior operator configuration, an IKE daemon must be prepared to negotiate - phase 1 SAs with any node. This may require a large amount of resources to - maintain cookie state, as well as large amounts of entropy for nonces, - cookies and so on. - </t> - <t> - The major changes to support opportunistic encryption are at the IKE daemon - level. These changes relate to handling of key acquisition requests, lookup - of public keys and TXT records, and interactions with firewalls and other - security facilities that may be co-resident on the same gateway. - </t> - </section> - - <section title="Gateway discovery process"> - <t> - In a typical configured tunnel, the address of SG-B is provided - via configuration. Furthermore, the mapping of an SPD entry to a gateway is - typically a 1:1 mapping. When the 0.0.0.0/0 SPD entry technique is used, then - the mapping to a gateway is determined by the reverse DNS records. - </t> - <t> - The need to do a DNS lookup and wait for a reply will typically introduce a - new state and a new event source (DNS replies) to IKE. Although a -synchronous DNS request can be implemented for proof of concept, experience -is that it can cause very high latencies when a queue of queries must -all timeout in series. - </t> - <t> - Use of an asynchronous DNS lookup will also permit overlap of DNS lookups with - some of the protocol steps. - </t> - </section> - - <section title="Self identification"> - <t> - SG-A will have to establish its identity. Use an - IPv4 ID in phase 1. - </t> - <t> There are many situations where the administrator of SG-A may not be - able to control the reverse DNS records for SG-A's public IP address. - Typical situations include dialup connections and most residential-type broadband Internet access - (ADSL, cable-modem) connections. In these situations, a fully qualified domain - name that is under the control of SG-A's administrator may be used - when acting as an initiator only. - The FQDN ID should be used in phase 1. See <xref target="fqdn" /> - for more details and restrictions. - </t> - </section> - - <section title="Public key retrieval process"> - <t> - Upon receipt of a phase 1 SA proposal with either an IPv4 (IPv6) ID or - an FQDN ID, an IKE daemon needs to examine local caches and - configuration files to determine if this is part of a configured tunnel. - If no configured tunnels are found, then the implementation should attempt to retrieve - a KEY record from the reverse DNS in the case of an IPv4/IPv6 ID, or - from the forward DNS in the case of FQDN ID. - </t> - <t> - It is reasonable that if other non-local sources of policy are used - (COPS, LDAP), they be consulted concurrently but some - clear ordering of policy be provided. Note that due to variances in - latency, implementations must wait for positive or negative replies from all sources - of policy before making any decisions. - </t> - </section> - - <section title="Interactions with DNSSEC"> - <t> - The implementation described (1.98) neither uses DNSSEC directly to - explicitly verify the authenticity of zone information, nor uses the NXT - records to provide authentication of the absence of a TXT or KEY - record. Rather, this implementation uses a trusted path to a DNSSEC - capable caching resolver. - </t> - <t> - To distinguish between an authenticated and an unauthenticated DNS - resource record, a stub resolver capable of returning DNSSEC - information MUST be used. - </t> - - </section> - -<!-- - <section title="Interactions with COPS"> - <t> - At this time there is no experience with implementations that interact - with COPS Policy Decision Points (PDP) <xref target="RFC2748" />. It is - suggested that it may be - appropriate for many of - the policy and discovery mechanisms outlined here to be done by a PDP. - In this context, the IKE daemon present in the Policy Enforcement Point - (PEP) may not need any modifications. - </t> - </section> ---> - - <section title="Required proposal types"> - - <section anchor="phase1id" title="Phase 1 parameters"> - <t> - Main mode MUST be used. - </t> - <t> - The initiator MUST offer at least one proposal using some combination - of: 3DES, HMAC-MD5 or HMAC-SHA1, DH group 2 or 5. Group 5 SHOULD be - proposed first. - <xref target="RFC3526" /> - </t> - <t> - The initiator MAY offer additional proposals, but the cipher MUST not - be weaker than 3DES. The initiator SHOULD limit the number of proposals - such that the IKE datagrams do not need to be fragmented. - </t> - <t> - The responder MUST accept one of the proposals. If any configuration - of the responder is required then the responder is not acting in an - opportunistic way. - </t> - <t> - The initiator SHOULD use an ID_IPV4_ADDR (ID_IPV6_ADDR for IPv6) of the external - interface of the initiator for phase 1. (There is an exception, see <xref - target="fqdn" />.) The authentication method MUST be RSA public key signatures. - The RSA key for the initiator SHOULD be placed into a DNS KEY record in - the reverse space of the initiator (i.e. using in-addr.arpa or - ip6.arpa). - </t> - </section> - - <section anchor="phase2id" title="Phase 2 parameters"> - <t> - The initiator MUST propose a tunnel between the ultimate - sender ("Alice" or "A") and ultimate recipient ("Bob" or "B") - using 3DES-CBC - mode, MD5 or SHA1 authentication. Perfect Forward Secrecy MUST be specified. - </t> - <t> - Tunnel mode MUST be used. - </t> - <t> - Identities MUST be ID_IPV4_ADDR_SUBNET with the mask being /32. - </t> - <t> - Authorization for the initiator to act on Alice's behalf is determined by - looking for a TXT record in the reverse-map at Alice's IP address. - </t> - <t> - Compression SHOULD NOT be mandatory. It MAY be offered as an option. - </t> - </section> - </section> - -</section> - -<section title="DNS issues"> - <section anchor="KEY" title="Use of KEY record"> - <t> - In order to establish their own identities, security gateways SHOULD publish - their public keys in their reverse DNS via - DNSSEC's KEY record. - See section 3 of <xref target="RFC2535">RFC 2535</xref>. - </t> - <t> - <preamble>For example:</preamble> - <artwork><![CDATA[ -KEY 0x4200 4 1 AQNJjkKlIk9...nYyUkKK8 -]]></artwork> - - <list style="hanging"> - <t hangText="0x4200:"> The flag bits, indicating that this key is prohibited - for confidentiality use (it authenticates the peer only, a separate - Diffie-Hellman exchange is used for - confidentiality), and that this key is associated with the non-zone entity - whose name is the RR owner name. No other flags are set.</t> - <t hangText="4:">This indicates that this key is for use by IPsec.</t> - <t hangText="1:">An RSA key is present.</t> - <t hangText="AQNJjkKlIk9...nYyUkKK8:">The public key of the host as described in <xref target="RFC3110" />.</t> - </list> - </t> - <t>Use of several KEY records allows for key rollover. The SIG Payload in - IKE phase 1 SHOULD be accepted if the public key given by any KEY RR - validates it. - </t> - </section> - - <section anchor="TXT" title="Use of TXT delegation record"> - <t> -If, for example, machine Alice wishes SG-A to act on her behalf, then -she publishes a TXT record to provide authorization for SG-A to act on -Alice's behalf. Similarly for Bob and SG-B. -</t> - -<t> -These records are located in the reverse DNS (in-addr.arpa or ip6.arpa) for their -respective IP addresses. The reverse DNS SHOULD be secured by DNSSEC. -DNSSEC is required to defend against active attacks. - </t> - <t> - If Alice's address is P.Q.R.S, then she can authorize another node to - act on her behalf by publishing records at: - <artwork><![CDATA[ -S.R.Q.P.in-addr.arpa - ]]></artwork> - </t> - - <t> - The contents of the resource record are expected to be a string that - uses the following syntax, as suggested in <xref target="RFC1464">RFC1464</xref>. - (Note that the reply to query may include other TXT resource - records used by other applications.) - - <figure anchor="txtformat" title="Format of reverse delegation record"> - <artwork><![CDATA[ -X-IPsec-Server(P)=A.B.C.D KEY - ]]></artwork> - </figure> - </t> - - where the record is formed by the following fields: - - <list style="hanging"> - <t hangText="P:"> Specifies a precedence for this record. This is - similar to MX record preferences. Lower numbers have stronger - preference. - </t> - - <t hangText="A.B.C.D:"> Specifies the IP address of the Security Gateway - for this client machine. - </t> - - <t hangText="KEY:"> Is the encoded RSA Public key of the Security - Gateway. The key is provided here to avoid a second DNS lookup. If this - field is absent, then a KEY resource record should be looked up in the - reverse-map of A.B.C.D. The key is transmitted in base64 format. - </t> - </list> - - <t> - The fields of the record MUST be separated by whitespace. This - MAY be: space, tab, newline, or carriage return. A space is preferred. - </t> - - <t> - In the case where Alice is located at a public address behind a - security gateway that has no fixed address (or no control over its - reverse-map), then Alice may delegate to a public key by domain name. - - <figure anchor="txtfqdnformat" - title="Format of reverse delegation record (FQDN version)"> - <artwork><![CDATA[ -X-IPsec-Server(P)=@FQDN KEY - ]]></artwork> - </figure> - </t> - - <list style="hanging"> - <t hangText="P:"> Is as above. - </t> - - <t hangText="FQDN:"> Specifies the FQDN that the Security Gateway - will identify itself with. - </t> - - <t hangText="KEY:"> Is the encoded RSA Public key of the Security - Gateway. </t> - </list> - - <t> - If there is more than one such TXT record with strongest (lowest - numbered) precedence, one Security Gateway is picked arbitrarily from - those specified in the strongest-preference records. - </t> - - <section title="Long TXT records"> - <t> - When packed into transport format, TXT records which are longer than 255 - characters are divided into smaller <character-strings>. - (See <xref target="RFC1035" /> section 3.3 and 3.3.14.) These MUST - be reassembled into a single string for processing. - Whitespace characters in the base64 encoding are to be ignored. - </t> - </section> - - <section title="Choice of TXT record"> - <t> - It has been suggested to use the KEY, OPT, CERT, or KX records - instead of a TXT record. None is satisfactory. - </t> - <t> The KEY RR has a protocol field which could be used to indicate a new protocol, -and an algorithm field which could be used to - indicate different contents in the key data. However, the KEY record - is clearly not intended for storing what are really authorizations, - it is just for identities. Other uses have been discouraged. - </t> - <t> OPT resource records, as defined in <xref target="RFC2671" /> are not - intended to be used for storage of information. They are not to be loaded, - cached or forwarded. They are, therefore, inappropriate for use here. - </t> - <t> - CERT records <xref target="RFC2538" /> can encode almost any set of - information. A custom type code could be used permitting any suitable - encoding to be stored, not just X.509. According to - the RFC, the certificate RRs are to be signed internally which may add undesirable -and unnecessary bulk. Larger DNS records may require TCP instead of UDP transfers. - </t> - <t> - At the time of protocol design, the CERT RR was not widely deployed and - could not be counted upon. Use of CERT records will be investigated, - and may be proposed in a future revision of this document. - </t> - <t> - KX records are ideally suited for use instead of TXT records, but had not been deployed at - the time of implementation. -<!-- Jakob Schlyter <j@crt.se> confirmed --> - </t> - </section> - </section> - - <section anchor="fqdn" title="Use of FQDN IDs"> - <t> - Unfortunately, not every administrator has control over the contents - of the reverse-map. Where the initiator (SG-A) has no suitable reverse-map, the - authorization record present in the reverse-map of Alice may refer to a - FQDN instead of an IP address. - </t> - <t> - In this case, the client's TXT record gives the fully qualified domain - name (FQDN) in place of its security gateway's IP address. - The initiator should use the ID_FQDN ID-payload in phase 1. - A forward lookup for a KEY record on the FQDN must yield the - initiator's public key. - </t> - <t> - This method can also be used when the external address of SG-A is - dynamic. - </t> - <t> - If SG-A is acting on behalf of Alice, then Alice must still delegate - authority for SG-A to do so in her reverse-map. When Alice and SG-A - are one and the same (i.e. Alice is acting as an end-node) then there - is no need for this when initiating only. </t> - <t>However, Alice must still delegate to herself if she wishes others to - initiate OE to her. See <xref target="txtfqdnformat" />. - </t> - < - </section> - -<section title="Key roll-over"> -<t> -Good cryptographic hygiene says that one should replace public/private key pairs -periodically. Some administrators may wish to do this as often as daily. Typical DNS -propagation delays are determined by the SOA Resource Record MINIMUM -parameter, which controls how long DNS replies may be cached. For reasonable -operation of DNS servers, administrators usually want this value to be at least several -hours, sometimes as a long as a day. This presents a problem - a new key MUST -not be used prior to it propagating through DNS. -</t> -<t> -This problem is dealt with by having the Security Gateway generate a new -public/private key pair at least MINIMUM seconds in advance of using it. It -then adds this key to the DNS (both as a second KEY record and in additional TXT -delegation records) at key generation time. Note: only one key is allowed in -each TXT record. -</t> -<t> -When authenticating, all gateways MUST have available all public keys -that are found in DNS for this entity. This permits the authenticating end -to check both the key for "today" and the key for "tomorrow". Note that it is -the end which is creating the signature (possesses the private key) that -determines which key is to be used. -</t> - - </section> -</section> - - -<section title="Network address translation interaction"> - <t> - There are no fundamentally new issues for implementing opportunistic encryption - in the presence of network address translation. Rather there are - only the regular IPsec issues with NAT traversal. - </t> - <t> - There are several situations to consider for NAT. - </t> - <section title="Co-located NAT/NAPT"> - <t> - If a security gateway is also performing network address translation on - behalf of an end-system, then the packet should be translated prior to - being subjected to opportunistic encryption. This is in contrast to - typically configured tunnels which often exist to bridge islands of - private network address space. The security gateway will use the translated source - address for phase 2, and so the responding security gateway will look up that address to - confirm SG-A's authorization. - </t> - <t> In the case of NAT (1:1), the address space into which the - translation is done MUST be globally unique, and control over the - reverse-map is assumed. - Placing of TXT records is possible. - </t> - <t> In the case of NAPT (m:1), the address will be the security - gateway itself. The ability to get - KEY and TXT records in place will again depend upon whether or not - there is administrative control over the reverse-map. This is - identical to situations involving a single host acting on behalf of - itself. - - FQDN style can be used to get around a lack of a reverse-map for - initiators only. - </t> - </section> - - <section title="Security Gateway behind NAT/NAPT"> - <t> - If there is a NAT or NAPT between the security gateways, then normal IPsec - NAT traversal problems occur. In addition to the transport problem - which may be solved by other mechanisms, there is the issue of - what phase 1 and phase 2 IDs to use. While FQDN could - be used during phase 1 for the security gateway, there is no appropriate ID for phase 2. - Due to the NAT, the end systems live in different IP address spaces. - </t> - </section> - - <section title="End System is behind a NAT/NAPT"> - <t> - If the end system is behind a NAT (perhaps SG-B), then there is, in fact, no way for - another end system to address a packet to this end system. - Not only is opportunistic encryption - impossible, but it is also impossible for any communication to - be initiate to the end system. It may be possible for this end - system to initiate in such communication. This creates an asymmetry, but this is common for - NAPT. - </t> - </section> -</section> - -<section title="Host implementations"> -<t> - When Alice and SG-A are components of the same system, they are - considered to be a host implementation. The packet sequence scenario remains unchanged. -</t> -<t> - Components marked Alice are the upper layers (TCP, UDP, the - application), and SG-A is the IP layer. -</t> -<t> - Note that tunnel mode is still required. -</t> -<t> - As Alice and SG-A are acting on behalf of themselves, no TXT based delegation - record is necessary for Alice to initiate. She can rely on FQDN in a - forward map. This is particularly attractive to mobile nodes such as - notebook computers at conferences. - To respond, Alice/SG-A will still need an entry in Alice's reverse-map. -</t> -</section> - -<section title="Multi-homing"> -<t> -If there are multiple paths between Alice and Bob (as illustrated in -the diagram with SG-D), then additional DNS records are required to establish -authorization. -</t> -<t> -In <xref target="networkdiagram" />, Alice has two ways to -exit her network: SG-A and SG-D. Previously SG-D has been ignored. Postulate -that there are routers between Alice and her set of security gateways -(denoted by the + signs and the marking of an autonomous system number for -Alice's network). Datagrams may, therefore, travel to either SG-A or SG-D en -route to Bob. -</t> -<t> -As long as all network connections are in good order, it does not matter how -datagrams exit Alice's network. When they reach either security gateway, the -security gateway will find the TXT delegation record in Bob's reverse-map, -and establish an SA with SG-B. -</t> -<t> -SG-B has no problem establishing that either of SG-A or SG-D may speak for -Alice, because Alice has published two equally weighted TXT delegation records: - <figure anchor="txtmultiexample" - title="Multiple gateway delegation example for Alice"> - <artwork><![CDATA[ -X-IPsec-Server(10)=192.1.1.5 AQMM...3s1Q== -X-IPsec-Server(10)=192.1.1.6 AAJN...j8r9== - ]]></artwork> - </figure> -</t> -<t> -Alice's routers can now do any kind of load sharing needed. Both SG-A and SG-D send datagrams addressed to Bob through -their tunnel to SG-B. -</t> -<t> -Alice's use of non-equal weight delegation records to show preference of one gateway over another, has relevance only when SG-B -is initiating to Alice. -</t> -<t> -If the precedences are the same, then SG-B has a more difficult time. It -must decide which of the two tunnels to use. SG-B has no information about -which link is less loaded, nor which security gateway has more cryptographic -resources available. SG-B, in fact, has no knowledge of whether both gateways -are even reachable. -</t> -<t> -The Public Internet's default-free zone may well know a good route to Alice, -but the datagrams that SG-B creates must be addressed to either SG-A or SG-D; -they can not be addressed to Alice directly. -</t> -<t> -SG-B may make a number of choices: -<list style="numbers"> -<t>It can ignore the problem and round robin among the tunnels. This - causes losses during times when one or the other security gateway is - unreachable. If this worries Alice, she can change the weights in her - TXT delegation records.</t> - -<t>It can send to the gateway from which it most recently received datagrams. - This assumes that routing and reachability are symmetrical.</t> - -<t>It can listen to BGP information from the Internet to decide which - system is currently up. This is clearly much more complicated, but if SG-B is already participating - in the BGP peering system to announce Bob, the results data may already - be available to it. </t> - -<t>It can refuse to negotiate the second tunnel. (It is unclear whether or -not this is even an option.)</t> - -<t>It can silently replace the outgoing portion of the first tunnel with the -second one while still retaining the incoming portions of both. SG-B can, -thus, accept datagrams from either SG-A or SG-D, but -send only to the gateway that most recently re-keyed with it.</t> -</list> -</t> - -<t> -Local policy determines which choice SG-B makes. Note that even if SG-B has perfect -knowledge about the reachability of SG-A and SG-D, Alice may not be reachable -from either of these security gateways because of internal reachability -issues. -</t> - -<t> -FreeS/WAN implements option 5. Implementing a different option is -being considered. The multi-homing aspects of OE are not well developed and may -be the subject of a future document. -</t> - -</section> - -<section title="Failure modes"> - <section title="DNS failures"> - <t> - If a DNS server fails to respond, local policy decides - whether or not to permit communication in the clear as embodied in - the connection classes in <xref target="initclasses" />. - It is easy to mount a denial of service attack on the DNS server - responsible for a particular network's reverse-map. - Such an attack may cause all communication with that network to go in - the clear if the policy is permissive, or fail completely - if the policy is paranoid. Please note that this is an active attack. - </t> - <t> - There are still many networks - that do not have properly configured reverse-maps. Further, if the policy is not to communicate, - the above denial of service attack isolates the target network. Therefore, the decision of whether -or not to permit communication in the clear MUST be a matter of local policy. - </t> - </section> - - <section title="DNS configured, IKE failures"> - <t> - DNS records claim that opportunistic encryption should - occur, but the target gateway either does not respond on port 500, or - refuses the proposal. This may be because of a crash or reboot, a - faulty configuration, or a firewall filtering port 500. - </t> - <t> - The receipt of ICMP port, host or network unreachable - messages indicates a potential problem, but MUST NOT cause communication - to fail - immediately. ICMP messages are easily forged by attackers. If such a - forgery caused immediate failure, then an active attacker could easily - prevent any - encryption from ever occurring, possibly preventing all communication. - </t> - <t> - In these situations a clear log should be produced - and local policy should dictate if communication is then - permitted in the clear. - </t> - </section> - - <section title="System reboots"> -<t> -Tunnels sometimes go down because the remote end crashes, -disconnects, or has a network link break. In general there is no -notification of this. Even in the event of a crash and successful reboot, -other SGs don't hear about it unless the rebooted SG has specific -reason to talk to them immediately. Over-quick response to temporary -network outages is undesirable. Note that a tunnel can be torn -down and then re-established without any effect visible to the user -except a pause in traffic. On the other hand, if one end reboots, -the other end can't get datagrams to it at all (except via -IKE) until the situation is noticed. So a bias toward quick -response is appropriate even at the cost of occasional -false alarms. -</t> - -<t> -A mechanism for recovery after reboot is a topic of current research and is not specified in this -document. -</t> - -<t> -A deliberate shutdown should include an attempt, using deletes, to notify all other SGs -currently connected by phase 1 SAs that communication is -about to fail. Again, a remote SG will assume this is a teardown. Attempts by the -remote SGs to negotiate new tunnels as replacements should be ignored. When possible, -SGs should attempt to preserve information about currently-connected SGs in non-volatile storage, so -that after a crash, an Initial-Contact can be sent to previous partners to -indicate loss of all previously established connections. -</t> - - </section> -</section> - -<!-- -<section title="Performance experiences"> - - Claudia> Is it useful to point out (or to clarify for our own discussion) any of the - Claudia> following: - - Claudia> * how much time this is likely to take on typical current hardware? - Claudia> * what steps are likely to be time consuming - Claudia> * how any added time could affect a typical transaction, such as hitting - Claudia> a web site - Claudia> * any ways to minimize such time delays - - <section title="Introduced latency"> - </section> - - <section title="Cryptographic performance"> - </section> - - <section title="Phase 1 SA performance"> - </section> - -</section> ---> - -<section title="Unresolved issues"> - <section title="Control of reverse DNS"> - <t> - The method of obtaining information by reverse DNS lookup causes - problems for people who cannot control their reverse DNS - bindings. This is an unresolved problem in this version, and is out - of scope. - </t> - </section> -</section> - -<section title="Examples"> - -<section title="Clear-text usage (permit policy)"> - -<t> -Two example scenarios follow. In the first example GW-A -(Gateway A) and GW-B (Gateway B) have always-clear-text policies, and in the second example they have an OE -policy. The clear-text policy serves as a reference for what occurs in -TCP/IP in the absence of Opportunistic Encryption. - -<t> -Alice wants to communicate with Bob. Perhaps she wants to retrieve a -web page from Bob's web server. In the absence of opportunistic -encryptors, the following events occur: -</t> - - <figure anchor="regulartiming" title="Timing of regular transaction"> - <artwork><![CDATA[ - Alice SG-A DNS SG-B Bob - Human or application - 'clicks' with a name. - (1) - - ------(2)--------------> - Application looks up - name in DNS to get - IP address. - - <-----(3)--------------- - Resolver returns "A" RR - to application with IP - address. - - (4) - Application starts a TCP session - or UDP session and OS sends - first datagram - - ----(5)-----> - Datagram is seen at first gateway - from Alice (SG-A). - - ----------(6)------> - Datagram traverses - network. - - ------(7)-----> - Datagram arrives - at Bob, is provided - to TCP. - - <------(8)------ - A reply is sent. - - <----------(9)------ - Datagram traverses - network. - <----(10)----- - Alice receives - answer. - - (11)-----------> - A second exchange - occurs. - ----------(12)-----> - --------------> - <--------------- - <------------------- - <------------- - ]]></artwork> -</figure> - -</t> -</section> - -<section title="Opportunistic encryption"> - -<t> -In the presence of properly configured opportunistic encryptors, the -event list is extended. Only changes are annotated. -</t> - -<t>The following symbols are used in the time-sequence diagram</t> - -<t> -<list style="hanging"> - <t hangText="-"> A single dash represents clear-text datagrams.</t> - <t hangText="="> An equals sign represents phase 2 (IPsec) cipher-text - datagrams.</t> - <t hangText="~"> A single tilde represents clear-text phase 1 datagrams.</t> - <t hangText="#"> A hash sign represents phase 1 (IKE) cipher-text - datagrams.</t> -</list> -</t> - -<t> -<figure anchor="opportunistictiming" title="Timing of opportunistic encryption transaction"> - <artwork><![CDATA[ - Alice SG-A DNS SG-B Bob - (1) - ------(2)--------------> - <-----(3)--------------- - (4)----(5)----->+ - SG-A sees datagram - to new target and - saves it as "first" - - ----(5B)-> - SG-A asks DNS - for TXT RR. - - <---(5C)-- - DNS returns TXT RR. - - ~~~~~~~~~~~~~(5D)~~~> - initial IKE main mode - packet is sent. - - <~~~~~~~~~~~~(5E1)~~~ - ~~~~~~~~~~~~~(5E2)~~> - <~~~~~~~~~~~~(5E3)~~~ - IKE phase 1 - privacy. - - #############(5E4)##> - SG-A sends ID to SG-B - <----(5F1)-- - SG-B asks DNS - for SG-A's public - KEY - -----(5F2)-> - DNS provides KEY RR. - SG-B authenticates SG-A - - <############(5E5)### - IKE phase 1 - complete - - #############(5G1)##> - IKE phase 2 - Alice<->Bob - tunnel is proposed. - - <----(5H1)-- - SG-B asks DNS for - Alice's TXT record. - -----(5H2)-> - DNS replies with TXT - record. SG-B checks - SG-A's authorization. - - <############(5G2)### - SG-B accepts proposal. - - #############(5G3)##> - SG-A confirms. - - ============(6)====> - SG-A sends "first" - packet in new IPsec - SA. - ------(7)-----> - packet is decrypted - and forward to Bob. - <------(8)------ - <==========(9)====== - return packet also - encrypted. - <-----(10)---- - - (11)-----------> - a second packet - is sent by Alice - ==========(12)=====> - existing tunnel is used - --------------> - <--------------- - <=================== - <------------- - ]]></artwork> -</figure> - -</t> - - <t> - For the purposes of this section, we will describe only the changes that - occur between <xref target="regulartiming" /> and - <xref target="opportunistictiming" />. This corresponds to time points 5, 6, 7, 9 and 10 on the list above. - </t> - -<list style="symbols"> - <t> - At point (5), SG-A intercepts the datagram because this source/destination pair lacks a policy -(the non-existent policy state). SG-A creates a hold policy, and buffers the datagram. SG-A requests keys from the keying daemon. - </t> - - <t> - SG-A's IKE daemon, having looked up the source/destination pair in the connection - class list, creates a new Potential OE connection instance. SG-A starts DNS - queries. - </t> - </section> - - <section title="(5C) DNS returns TXT record(s)"> - - <t> - DNS returns properly formed TXT delegation records, and SG-A's IKE daemon - causes this instance to make a transition from Potential OE connection to Pending OE - connection. - </t> - - <t> - Using the example above, the returned record might contain: - - <figure anchor="txtexample" - title="Example of reverse delegation record for Bob"> - <artwork><![CDATA[ -X-IPsec-Server(10)=192.1.1.5 AQMM...3s1Q== - ]]></artwork> - </figure> - with SG-B's IP address and public key listed. - </t> - - </section> - - <section title="(5D) Initial IKE main mode packet goes out"> - <t>Upon entering Pending OE connection, SG-A sends the initial ISAKMP - message with proposals. See <xref target="phase1id" />. - </t> - </section> - - <section title="(5E1) Message 2 of phase 1 exchange"> - <t> - SG-B receives the message. A new connection instance is created in the - unauthenticated OE peer state. - </t> - </section> - - <section title="(5E2) Message 3 of phase 1 exchange"> - <t> - SG-A sends a Diffie-Hellman exponent. This is an internal state of the - keying daemon. - </t> - </section> - - <section title="(5E3) Message 4 of phase 1 exchange"> - <t> - SG-B responds with a Diffie-Hellman exponent. This is an internal state of the - keying protocol. - </t> - </section> - - <section title="(5E4) Message 5 of phase 1 exchange"> - <t> - SG-A uses the phase 1 SA to send its identity under encryption. - The choice of identity is discussed in <xref target="phase1id" />. - This is an internal state of the keying protocol. - </t> - </section> - - <section title="(5F1) Responder lookup of initiator key"> - <t> - SG-B asks DNS for the public key of the initiator. - DNS looks for a KEY record by IP address in the reverse-map. - That is, a KEY resource record is queried for 4.1.1.192.in-addr.arpa - (recall that SG-A's external address is 192.1.1.4). - SG-B uses the resulting public key to authenticate the initiator. See <xref - target="KEY" /> for further details. - </t> - </section> - -<section title="(5F2) DNS replies with public key of initiator"> -<t> -Upon successfully authenticating the peer, the connection instance makes a -transition to authenticated OE peer on SG-B. -</t> -<t> -The format of the TXT record returned is described in -<xref target="TXT" />. -</t> -</section> - - <section title="(5E5) Responder replies with ID and authentication"> - <t> - SG-B sends its ID along with authentication material. This is an internal - state for the keying protocol. - </t> - </section> - - <section title="(5G) IKE phase 2"> - <section title="(5G1) Initiator proposes tunnel"> - <t> - Having established mutually agreeable authentications (via KEY) and - authorizations (via TXT), SG-A proposes to create an IPsec tunnel for - datagrams transiting from Alice to Bob. This tunnel is established only for - the Alice/Bob combination, not for any subnets that may be behind SG-A and SG-B. - </t> - </section> - - <section title="(5H1) Responder determines initiator's authority"> - <t> - While the identity of SG-A has been established, its authority to - speak for Alice has not yet been confirmed. SG-B does a reverse - lookup on Alice's address for a TXT record. - </t> - <t>Upon receiving this specific proposal, SG-B's connection instance - makes a transition into the potential OE connection state. SG-B may already have an - instance, and the check is made as described above.</t> - </section> - - <section title="(5H2) DNS replies with TXT record(s)"> - <t> - The returned key and IP address should match that of SG-A. - </t> - </section> - - <section title="(5G2) Responder agrees to proposal"> - <t> - Should additional communication occur between, for instance, Dave and Bob using - SG-A and SG-B, a new tunnel (phase 2 SA) would be established. The phase 1 SA - may be reusable. - </t> - <t>SG-A, having successfully keyed the tunnel, now makes a transition from - Pending OE connection to Keyed OE connection. - </t> - <t>The responder MUST setup the inbound IPsec SAs before sending its reply.</t> - </section> - - <section title="(5G3) Final acknowledgment from initiator"> - <t> - The initiator agrees with the responder's choice and sets up the tunnel. - The initiator sets up the inbound and outbound IPsec SAs. - </t> - <t> - The proper authorization returned with keys prompts SG-B to make a transition - to the keyed OE connection state. - </t> - <t>Upon receipt of this message, the responder may now setup the outbound - IPsec SAs.</t> - </section> - </section> - - <section title="(6) IPsec succeeds, and sets up tunnel for communication between Alice and Bob"> - <t> - SG-A sends the datagram saved at step (5) through the newly created - tunnel to SG-B, where it gets decrypted and forwarded. - Bob receives it at (7) and replies at (8). - </t> - </section> - - <section title="(9) SG-B already has tunnel up with G1 and uses it"> - <t> - At (9), SG-B has already established an SPD entry mapping Bob->Alice via a - tunnel, so this tunnel is simply applied. The datagram is encrypted to SG-A, - decrypted by SG-A and passed to Alice at (10). - </t> - - </section> -</section> <!-- OE example --> - -</section> <!-- Examples --> - -<section anchor="securityconsiderations" title="Security considerations"> - - <section title="Configured vs opportunistic tunnels"> -<t> - Configured tunnels are those which are setup using bilateral mechanisms: exchanging -public keys (raw RSA, DSA, PKIX), pre-shared secrets, or by referencing keys that -are in known places (distinguished name from LDAP, DNS). These keys are then used to -configure a specific tunnel. -</t> -<t> -A pre-configured tunnel may be on all the time, or may be keyed only when needed. -The end points of the tunnel are not necessarily static: many mobile -applications (road warrior) are considered to be configured tunnels. -</t> -<t> -The primary characteristic is that configured tunnels are assigned specific -security properties. They may be trusted in different ways relating to exceptions to -firewall rules, exceptions to NAT processing, and to bandwidth or other quality of service restrictions. -</t> -<t> -Opportunistic tunnels are not inherently trusted in any strong way. They are -created without prior arrangement. As the two parties are strangers, there -MUST be no confusion of datagrams that arrive from opportunistic peers and -those that arrive from configured tunnels. A security gateway MUST take care -that an opportunistic peer can not impersonate a configured peer. -</t> -<t> -Ingress filtering MUST be used to make sure that only datagrams authorized by -negotiation (and the concomitant authentication and authorization) are -accepted from a tunnel. This is to prevent one peer from impersonating another. -</t> -<t> -An implementation suggestion is to treat opportunistic tunnel -datagrams as if they arrive on a logical interface distinct from other -configured tunnels. As the number of opportunistic tunnels that may be -created automatically on a system is potentially very high, careful attention -to scaling should be taken into account. -</t> -<t> -As with any IKE negotiation, opportunistic encryption cannot be secure -without authentication. Opportunistic encryption relies on DNS for its -authentication information and, therefore, cannot be fully secure without -a secure DNS. Without secure DNS, opportunistic encryption can protect against passive -eavesdropping but not against active man-in-the-middle attacks. -</t> - </section> - - <section title="Firewalls versus Opportunistic Tunnels"> -<t> - Typical usage of per datagram access control lists is to implement various -kinds of security gateways. These are typically called "firewalls". -</t> -<t> - Typical usage of a virtual private network (VPN) within a firewall is to -bypass all or part of the access controls between two networks. Additional -trust (as outlined in the previous section) is given to datagrams that arrive -in the VPN. -</t> -<t> - Datagrams that arrive via opportunistically configured tunnels MUST not be -trusted. Any security policy that would apply to a datagram arriving in the -clear SHOULD also be applied to datagrams arriving opportunistically. -</t> - </section> - - <section title="Denial of service"> -<t> - There are several different forms of denial of service that an implementor - should concern themselves with. Most of these problems are shared with - security gateways that have large numbers of mobile peers (road warriors). -</t> -<t> - The design of ISAKMP/IKE, and its use of cookies, defend against many kinds - of denial of service. Opportunism changes the assumption that if the phase 1 (ISAKMP) - SA is authenticated, that it was worthwhile creating. Because the gateway will communicate with any machine, it is - possible to form phase 1 SAs with any machine on the Internet. -</t> - -</section> -</section> - -<section title="IANA Considerations"> -<t> - There are no known numbers which IANA will need to manage. -</t> -</section> - -<section title="Acknowledgments"> -<t> - Substantive portions of this document are based upon previous work by - Henry Spencer. -</t> -<t> - Thanks to Tero Kivinen, Sandy Harris, Wes Hardarker, Robert Moskowitz, - Jakob Schlyter, Bill Sommerfeld, John Gilmore and John Denker for their - comments and constructive criticism. -</t> -<t> - Sandra Hoffman and Bill Dickie did the detailed proof reading and editing. -</t> -</section> - -</middle> - -<back> -<references title="Normative references"> -<?rfc include="reference.OEspec" ?> -<!-- renumber according to reference order --> -<?rfc include="reference.RFC.0791" ?> -<?rfc include="reference.RFC.1009" ?> -<?rfc include="reference.RFC.1984" ?> -<?rfc include="reference.RFC.2119" ?> -<!-- IPsec --> -<?rfc include="reference.RFC.2367" ?> -<?rfc include="reference.RFC.2401" ?> -<?rfc include="reference.RFC.2407" ?> -<?rfc include="reference.RFC.2408" ?> -<?rfc include="reference.RFC.2409" ?> -<!-- MODPGROUPS --> -<?rfc include="reference.RFC.3526" ?> -<!-- DNSSEC --> -<?rfc include="reference.RFC.1034" ?> -<?rfc include="reference.RFC.1035" ?> -<?rfc include="reference.RFC.2671" ?> -<?rfc include="reference.RFC.1464" ?> -<?rfc include="reference.RFC.2535" ?> -<?rfc include="reference.RFC.3110" ?> -<?rfc include="reference.RFC.2538" ?> -<!-- COPS --> -<?rfc include="reference.RFC.2748" ?> -<!-- NAT --> -<?rfc include="reference.RFC.2663" ?> -</references> -<!-- <references title="Non-normative references"> --> -<!-- ESPUDP --> -<!-- <?rfc include="reference.ESPUDP" ?> --> -<!-- </references> --> -</back> -</rfc> -<!-- - $Id: draft-richardson-ipsec-opportunistic.xml,v 1.1 2004/03/15 20:35:24 as Exp $ - - $Log: draft-richardson-ipsec-opportunistic.xml,v $ - Revision 1.1 2004/03/15 20:35:24 as - added files from freeswan-2.04-x509-1.5.3 - - Revision 1.33 2003/06/30 03:19:59 mcr - timing-diagram with inline explanation. - - Revision 1.32 2003/06/30 01:57:44 mcr - initial edits per-Bob Braden. - - Revision 1.31 2003/05/26 19:31:23 mcr - updates to drafts - IPSEC RR - SC versions, and RFC3526 - reference in OE draft. - - Revision 1.30 2003/05/21 15:42:34 mcr - updates due to publication of RFC 3526. - - Revision 1.29 2003/01/17 16:22:55 mcr - rev 11 of OE draft. - - Revision 1.28 2002/07/25 19:27:31 mcr - added DHR's minor edits. - - Revision 1.27 2002/07/21 16:26:26 mcr - slides from presentation at OLS - draft-10 of OE draft. - - Revision 1.26 2002/07/16 03:46:53 mcr - second edits from Sandra. - - Revision 1.25 2002/07/16 03:36:14 mcr - removed HS from authors list - updated reference inclusion to use <?rfc-include directive. - Revision 1.24 2002/07/11 02:08:21 mcr - updated XML file from Sandra - - Revision 1.23 2002/06/06 17:18:53 mcr - spellcheck. - - Revision 1.22 2002/06/06 17:14:19 mcr - results of hand-editing session from May 28th. - This is FINAL OE draft. - - Revision 1.21 2002/06/06 02:25:44 mcr - results of hand-editing session from May 28th. - This is FINAL OE draft. - - Revision 1.20 2002/05/24 03:28:37 mcr - changes as requested by RFC editor. - - Revision 1.19 2002/04/09 16:01:05 mcr - comments from PHB. - - Revision 1.18 2002/04/08 02:14:34 mcr - RGBs changes to rev6. - - Revision 1.17 2002/03/12 21:23:55 mcr - adjusted definition of default-free zone. - moved text on key rollover from format description to new - section. - - Revision 1.16 2002/02/22 01:23:21 mcr - revisions from MCR (2002/2/18) and net. - - Revision 1.15 2002/02/21 20:44:12 mcr - extensive from DHR. - - Revision 1.14 2002/02/10 16:20:39 mcr - -05 draft. Many revisions to do "OE system in world of OE systems" - view of the universe. - - Revision 1.13 2001/12/20 04:35:22 mcr - fixed reference to rfc1984. - - Revision 1.12 2001/12/20 03:35:19 mcr - comments from Henry, Tero, and Sandy. - - Revision 1.11 2001/12/19 07:26:22 mcr - added comment about KX records. - - Revision 1.10 2001/11/09 04:28:10 mcr - fixed some typos with XML, and one s/SG-B/SG-D/. - - Revision 1.9 2001/11/09 04:07:13 mcr - expanded section 10: multihoming, with an example. - - Revision 1.8 2001/11/09 02:16:51 mcr - added lifetime/lifespan definitions. - moved example from 5B to 5C. - added reference to phase 1 IDs to 5D. - cleared up text in aging section. - added text about delegation of DNSSEC activity to a DNS server. - spelt out DH group names. - added text about ignoring TXT records unless DNSSEC is deployed (somerfeld) - added example of TXT delegation using FQDN. - clarified some text in NAT interaction section. - clarified absense of TXT record need for host implementation - - Revision 1.7 2001/11/08 23:09:37 mcr - changed revision of draft to 03. - - Revision 1.6 2001/11/08 19:37:14 mcr - fixed some formatting of Aging section. - - Revision 1.5 2001/11/08 19:19:30 mcr - fixed address for DHR, updated address for MCR, - added reference to original HS/DHR OE specification paper. - - Revision 1.4 2001/11/08 19:08:24 mcr - section 10, "Renewal and Teardown" added moved between 4/5, and - slightly rewritten. - - Revision 1.3 2001/11/08 18:56:34 mcr - sections 4.2, 5.6, 5.7.1 and 6.2 edited as per HS. - section 10, "Renewal and Teardown" added. - section 11, "Failure modes" completed. - - Revision 1.2 2001/11/05 20:31:31 mcr - added section from OE spec on aging and teardown. - - Revision 1.1 2001/11/05 04:27:58 mcr - OE draft added to documentation. - - Revision 1.12 2001/10/10 01:12:31 mcr - removed impact on DNS servers section. - removed nested comments. - adjusted data of issue - - Revision 1.11 2001/09/17 02:55:50 mcr - outline is now stable. - - Revision 1.5 2001/08/19 02:53:32 mcr - version 00d formatted. - - Revision 1.10 2001/08/19 02:34:04 mcr - version 00d formatted. - - Revision 1.9 2001/08/19 02:21:54 mcr - version 00d - - Revision 1.8 2001/07/20 19:07:06 mcr - commented out section 1.1 - - Revision 1.7 2001/07/20 14:14:22 mcr - HS and HD comments. - - Revision 1.6 2001/07/19 00:56:50 mcr - version 00b. - - Revision 1.5 2001/07/12 23:57:07 mcr - OE ID, 00. - - -!> diff --git a/doc/src/draft-richardson-ipsec-rr.html b/doc/src/draft-richardson-ipsec-rr.html deleted file mode 100644 index 08473104f..000000000 --- a/doc/src/draft-richardson-ipsec-rr.html +++ /dev/null @@ -1,659 +0,0 @@ -<html><head><title>A method for storing IPsec keying material in DNS.</title> -<STYLE type='text/css'> - .title { color: #990000; font-size: 22px; line-height: 22px; font-weight: bold; text-align: right; - font-family: helvetica, arial, sans-serif } - .filename { color: #666666; font-size: 18px; line-height: 28px; font-weight: bold; text-align: right; - font-family: helvetica, arial, sans-serif } - p.copyright { color: #000000; font-size: 10px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - p { margin-left: 2em; margin-right: 2em; } - li { margin-left: 3em; } - ol { margin-left: 2em; margin-right: 2em; } - ul.text { margin-left: 2em; margin-right: 2em; } - pre { margin-left: 3em; color: #333333 } - ul.toc { color: #000000; line-height: 16px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - H3 { color: #333333; font-size: 16px; line-height: 16px; font-family: helvetica, arial, sans-serif } - H4 { color: #000000; font-size: 14px; font-family: helvetica, arial, sans-serif } - TD.header { color: #ffffff; font-size: 10px; font-family: arial, helvetica, san-serif; valign: top } - TD.author-text { color: #000000; font-size: 10px; - font-family: verdana, charcoal, helvetica, arial, sans-serif } - TD.author { color: #000000; font-weight: bold; margin-left: 4em; font-size: 10px; font-family: verdana, charcoal, helvetica, arial, sans-serif } - A:link { color: #990000; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - A:visited { color: #333333; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - A:name { color: #333333; font-weight: bold; - font-family: MS Sans Serif, verdana, charcoal, helvetica, arial, sans-serif } - .link2 { color:#ffffff; font-weight: bold; text-decoration: none; - font-family: monaco, charcoal, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } - .RFC { color:#666666; font-weight: bold; text-decoration: none; - font-family: monaco, charcoal, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } - .hotText { color:#ffffff; font-weight: normal; text-decoration: none; - font-family: charcoal, monaco, geneva, MS Sans Serif, helvetica, monotype, verdana, sans-serif; - font-size: 9px } -</style> -</head> -<body bgcolor="#ffffff" text="#000000" alink="#000000" vlink="#666666" link="#990000"> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<table width="66%" border="0" cellpadding="0" cellspacing="0"><tr><td><table width="100%" border="0" cellpadding="2" cellspacing="1"> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">IPSECKEY WG</td><td width="33%" bgcolor="#666666" class="header">M. Richardson</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">Internet-Draft</td><td width="33%" bgcolor="#666666" class="header">SSW</td></tr> -<tr valign="top"><td width="33%" bgcolor="#666666" class="header">Expires: March 4, 2004</td><td width="33%" bgcolor="#666666" class="header">September 4, 2003</td></tr> -</table></td></tr></table> -<div align="right"><font face="monaco, MS Sans Serif" color="#990000" size="+3"><b><br><span class="title">A method for storing IPsec keying material in DNS.</span></b></font></div> -<div align="right"><font face="monaco, MS Sans Serif" color="#666666" size="+2"><b><span class="filename">draft-ietf-ipseckey-rr-07.txt</span></b></font></div> -<font face="verdana, helvetica, arial, sans-serif" size="2"> - -<h3>Status of this Memo</h3> -<p> -This document is an Internet-Draft and is in full conformance with all provisions of Section 10 of RFC2026.</p> -<p> -Internet-Drafts are working documents of the Internet Engineering -Task Force (IETF), its areas, and its working groups. -Note that other groups may also distribute working documents as -Internet-Drafts.</p> -<p> -Internet-Drafts are draft documents valid for a maximum of six months -and may be updated, replaced, or obsoleted by other documents at any time. -It is inappropriate to use Internet-Drafts as reference material or to cite -them other than as "work in progress."</p> -<p> -The list of current Internet-Drafts can be accessed at -<a href='http://www.ietf.org/ietf/1id-abstracts.txt'>http://www.ietf.org/ietf/1id-abstracts.txt</a>.</p> -<p> -The list of Internet-Draft Shadow Directories can be accessed at -<a href='http://www.ietf.org/shadow.html'>http://www.ietf.org/shadow.html</a>.</p> -<p> -This Internet-Draft will expire on March 4, 2004.</p> - -<h3>Copyright Notice</h3> -<p> -Copyright (C) The Internet Society (2003). All Rights Reserved.</p> - -<h3>Abstract</h3> - -<p> -This document describes a new resource record for DNS. This record may be -used to store public keys for use in IPsec systems. - -</p> -<p> -This record replaces the functionality of the sub-type #1 of the KEY Resource -Record, which has been obsoleted by RFC3445. - -</p><a name="toc"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Table of Contents</h3> -<ul compact class="toc"> -<b><a href="#anchor1">1.</a> -Introduction<br></b> -<b><a href="#anchor2">1.1</a> -Overview<br></b> -<b><a href="#anchor3">1.2</a> -Usage Criteria<br></b> -<b><a href="#anchor4">2.</a> -Storage formats<br></b> -<b><a href="#anchor5">2.1</a> -IPSECKEY RDATA format<br></b> -<b><a href="#anchor6">2.2</a> -RDATA format - precedence<br></b> -<b><a href="#algotype">2.3</a> -RDATA format - algorithm type<br></b> -<b><a href="#gatewaytype">2.4</a> -RDATA format - gateway type<br></b> -<b><a href="#anchor7">2.5</a> -RDATA format - gateway<br></b> -<b><a href="#anchor8">2.6</a> -RDATA format - public keys<br></b> -<b><a href="#anchor9">3.</a> -Presentation formats<br></b> -<b><a href="#anchor10">3.1</a> -Representation of IPSECKEY RRs<br></b> -<b><a href="#anchor11">3.2</a> -Examples<br></b> -<b><a href="#anchor12">4.</a> -Security Considerations<br></b> -<b><a href="#anchor13">4.1</a> -Active attacks against unsecured IPSECKEY resource records<br></b> -<b><a href="#anchor14">5.</a> -IANA Considerations<br></b> -<b><a href="#anchor15">6.</a> -Acknowledgments<br></b> -<b><a href="#rfc.references1">§</a> -Normative references<br></b> -<b><a href="#rfc.references2">§</a> -Non-normative references<br></b> -<b><a href="#rfc.authors">§</a> -Author's Address<br></b> -<b><a href="#rfc.copyright">§</a> -Full Copyright Statement<br></b> -</ul> -<br clear="all"> - -<a name="anchor1"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.1"></a><h3>1. Introduction</h3> - -<p> - The type number for the IPSECKEY RR is TBD. - -</p> -<a name="rfc.section.1.1"></a><h4><a name="anchor2">1.1</a> Overview</h4> - -<p> - The IPSECKEY resource record (RR) is used to publish a public key that is - to be associated with a Domain Name System (DNS) name for use with the - IPsec protocol suite. This can be the public key of a host, - network, or application (in the case of per-port keying). - -</p> -<p> - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL - NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and - "OPTIONAL" in this document are to be interpreted as described in - RFC2119 <a href="#RFC2119">[8]</a>. - -</p> -<a name="rfc.section.1.2"></a><h4><a name="anchor3">1.2</a> Usage Criteria</h4> - -<p> - An IPSECKEY resource record SHOULD be used in combination with DNSSEC -unless some other means of authenticating the IPSECKEY resource record -is available. - -</p> -<p> - It is expected that there will often be multiple IPSECKEY resource - records at the same name. This will be due to the presence - of multiple gateways and the need to rollover keys. - - -</p> -<p> - This resource record is class independent. - -</p> -<a name="anchor4"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.2"></a><h3>2. Storage formats</h3> - -<a name="rfc.section.2.1"></a><h4><a name="anchor5">2.1</a> IPSECKEY RDATA format</h4> - -<p> - The RDATA for an IPSECKEY RR consists of a precedence value, a public key, - algorithm type, and an optional gateway address. - -</p></font><pre> - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | precedence | gateway type | algorithm | gateway | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------+ + - ~ gateway ~ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | / - / public key / - / / - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> - -<a name="rfc.section.2.2"></a><h4><a name="anchor6">2.2</a> RDATA format - precedence</h4> - -<p> -This is an 8-bit precedence for this record. This is interpreted in -the same way as the PREFERENCE field described in section -3.3.9 of RFC1035 <a href="#RFC1035">[2]</a>. - -</p> -<p> -Gateways listed in IPSECKEY records with lower precedence are -to be attempted first. Where there is a tie in precedence, the order -should be non-deterministic. - -</p> -<a name="rfc.section.2.3"></a><h4><a name="algotype">2.3</a> RDATA format - algorithm type</h4> - -<p> -The algorithm type field identifies the public key's cryptographic -algorithm and determines the format of the public key field. - -</p> -<p> -A value of 0 indicates that no key is present. - -</p> -<p> -The following values are defined: - -<blockquote class="text"><dl> -<dt>1</dt> -<dd>A DSA key is present, in the format defined in RFC2536 <a href="#RFC2536">[11]</a> -</dd> -<dt>2</dt> -<dd>A RSA key is present, in the format defined in RFC3110 <a href="#RFC3110">[12]</a> -</dd> -</dl></blockquote><p> -</p> -<a name="rfc.section.2.4"></a><h4><a name="gatewaytype">2.4</a> RDATA format - gateway type</h4> - -<p> -The gateway type field indicates the format of the information that -is stored in the gateway field. - -</p> -<p> -The following values are defined: - -<blockquote class="text"><dl> -<dt>0</dt> -<dd>No gateway is present -</dd> -<dt>1</dt> -<dd>A 4-byte IPv4 address is present -</dd> -<dt>2</dt> -<dd>A 16-byte IPv6 address is present -</dd> -<dt>3</dt> -<dd>A wire-encoded domain name is present. The wire-encoded -format is self-describing, so the length is implicit. The domain name -MUST NOT be compressed. -</dd> -</dl></blockquote><p> -</p> -<a name="rfc.section.2.5"></a><h4><a name="anchor7">2.5</a> RDATA format - gateway</h4> - -<p> -The gateway field indicates a gateway to which an IPsec tunnel may be -created in order to reach the entity named by this resource record. - -</p> -<p> -There are three formats: - -</p> -<p> -A 32-bit IPv4 address is present in the gateway field. The data -portion is an IPv4 address as described in section 3.4.1 of -<a href="#RFC1035">RFC1035</a>[2]. This is a 32-bit number in network byte order. - -</p> -<p>A 128-bit IPv6 address is present in the gateway field. -The data portion is an IPv6 address as described in section 2.2 of -<a href="#RFC1886">RFC1886</a>[7]. This is a 128-bit number in network byte order. - -</p> -<p> -The gateway field is a normal wire-encoded domain name, as described -in section 3.3 of RFC1035 <a href="#RFC1035">[2]</a>. Compression MUST NOT be used. - -</p> -<a name="rfc.section.2.6"></a><h4><a name="anchor8">2.6</a> RDATA format - public keys</h4> - -<p> -Both of the public key types defined in this document (RSA and DSA) -inherit their public key formats from the corresponding KEY RR formats. -Specifically, the public key field contains the algorithm-specific -portion of the KEY RR RDATA, which is all of the KEY RR DATA after the -first four octets. This is the same portion of the KEY RR that must be -specified by documents that define a DNSSEC algorithm. -Those documents also specify a message digest to be used for generation -of SIG RRs; that specification is not relevant for IPSECKEY RR. - -</p> -<p> -Future algorithms, if they are to be used by both DNSSEC (in the KEY -RR) and IPSECKEY, are likely to use the same public key encodings in -both records. Unless otherwise specified, the IPSECKEY public key -field will contain the algorithm-specific portion of the KEY RR RDATA -for the corresponding algorithm. The algorithm must still be -designated for use by IPSECKEY, and an IPSECKEY algorithm type number -(which might be different than the DNSSEC algorithm number) must be -assigned to it. - -</p> -<p>The DSA key format is defined in RFC2536 <a href="#RFC2536">[11]</a> -</p> -<p>The RSA key format is defined in RFC3110 <a href="#RFC3110">[12]</a>, -with the following changes: -</p> -<p> -The earlier definition of RSA/MD5 in RFC2065 limited the exponent and -modulus to 2552 bits in length. RFC3110 extended that limit to 4096 -bits for RSA/SHA1 keys. The IPSECKEY RR imposes no length limit on -RSA public keys, other than the 65535 octet limit imposed by the -two-octet length encoding. This length extension is applicable only -to IPSECKEY and not to KEY RRs. - -</p> -<a name="anchor9"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.3"></a><h3>3. Presentation formats</h3> - -<a name="rfc.section.3.1"></a><h4><a name="anchor10">3.1</a> Representation of IPSECKEY RRs</h4> - -<p> - IPSECKEY RRs may appear in a zone data master file. - The precedence, gateway type and algorithm and gateway fields are REQUIRED. - The base64 encoded public key block is OPTIONAL; if not present, - then the public key field of the resource record MUST be construed - as being zero octets in length. - -</p> -<p> - The algorithm field is an unsigned integer. No mnemonics are defined. - -</p> -<p> - If no gateway is to be indicated, then the gateway type field MUST - be zero, and the gateway field MUST be "." - -</p> -<p> - The Public Key field is represented as a Base64 encoding of the - Public Key. Whitespace is allowed within the Base64 text. For a - definition of Base64 encoding, see -<a href="#RFC1521">RFC1521</a>[3] Section 5.2. - -</p> -<p> - The general presentation for the record as as follows: -</p> -</font><pre> -IN IPSECKEY ( precedence gateway-type algorithm - gateway base64-encoded-public-key ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<a name="rfc.section.3.2"></a><h4><a name="anchor11">3.2</a> Examples</h4> - -<p> -An example of a node 192.0.2.38 that will accept IPsec tunnels on its -own behalf. -</p> -</font><pre> -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 - 192.0.2.38 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<p> -An example of a node, 192.0.2.38 that has published its key only. -</p> -</font><pre> -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 - . - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<p> -An example of a node, 192.0.2.38 that has delegated authority to the node -192.0.2.3. -</p> -</font><pre> -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 - 192.0.2.3 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<p> -An example of a node, 192.0.1.38 that has delegated authority to the node -with the identity "mygateway.example.com". -</p> -</font><pre> -38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 - mygateway.example.com. - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<p> -An example of a node, 2001:0DB8:0200:1:210:f3ff:fe03:4d0 that has -delegated authority to the node 2001:0DB8:c000:0200:2::1 -</p> -</font><pre> -$ORIGIN 1.0.0.0.0.0.2.8.B.D.0.1.0.0.2.ip6.int. -0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2 - 2001:0DB8:0:8002::2000:1 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -</pre><font face="verdana, helvetica, arial, sans-serif" size="2"> -<p> - -</p> -<a name="anchor12"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.4"></a><h3>4. Security Considerations</h3> - -<p> - This entire memo pertains to the provision of public keying material - for use by key management protocols such as ISAKMP/IKE (RFC2407) - <a href="#RFC2407">[9]</a>. - -</p> -<p> -The IPSECKEY resource record contains information that SHOULD be -communicated to the end client in an integral fashion - i.e. free from -modification. The form of this channel is up to the consumer of the -data - there must be a trust relationship between the end consumer of this -resource record and the server. This relationship may be end-to-end -DNSSEC validation, a TSIG or SIG(0) channel to another secure source, -a secure local channel on the host, or some combination of the above. - -</p> -<p> -The keying material provided by the IPSECKEY resource record is not -sensitive to passive attacks. The keying material may be freely -disclosed to any party without any impact on the security properties -of the resulting IPsec session: IPsec and IKE provide for defense -against both active and passive attacks. - -</p> -<p> - Any user of this resource record MUST carefully document their trust - model, and why the trust model of DNSSEC is appropriate, if that is - the secure channel used. - -</p> -<a name="rfc.section.4.1"></a><h4><a name="anchor13">4.1</a> Active attacks against unsecured IPSECKEY resource records</h4> - -<p> -This section deals with active attacks against the DNS. These attacks -require that DNS requests and responses be intercepted and changed. -DNSSEC is designed to defend against attacks of this kind. - -</p> -<p> -The first kind of active attack is when the attacker replaces the -keying material with either a key under its control or with garbage. - -</p> -<p> -If the attacker is not able to mount a subsequent -man-in-the-middle attack on the IKE negotiation after replacing the -public key, then this will result in a denial of service, as the -authenticator used by IKE would fail. - -</p> -<p> -If the attacker is able to both to mount active attacks against DNS -and is also in a position to perform a man-in-the-middle attack on IKE and -IPsec negotiations, then the attacker will be in a position to compromise -the resulting IPsec channel. Note that an attacker must be able to -perform active DNS attacks on both sides of the IKE negotiation in -order for this to succeed. - -</p> -<p> -The second kind of active attack is one in which the attacker replaces -the the gateway address to point to a node under the attacker's -control. The attacker can then either replace the public key or remove -it, thus providing an IPSECKEY record of its own to match the -gateway address. - -</p> -<p> -This later form creates a simple man-in-the-middle since the attacker -can then create a second tunnel to the real destination. Note that, as before, -this requires that the attacker also mount an active attack against -the responder. - -</p> -<p> -Note that the man-in-the-middle can not just forward cleartext -packets to the original destination. While the destination may be -willing to speak in the clear, replying to the original sender, -the sender will have already created a policy expecting ciphertext. -Thus, the attacker will need to intercept traffic from both sides. In some -cases, the attacker may be able to accomplish the full intercept by use -of Network Addresss/Port Translation (NAT/NAPT) technology. - -</p> -<p> -Note that the danger here only applies to cases where the gateway -field of the IPSECKEY RR indicates a different entity than the owner -name of the IPSECKEY RR. In cases where the end-to-end integrity of -the IPSECKEY RR is suspect, the end client MUST restrict its use -of the IPSECKEY RR to cases where the RR owner name matches the -content of the gateway field. - -</p> -<a name="anchor14"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.5"></a><h3>5. IANA Considerations</h3> - -<p> -This document updates the IANA Registry for DNS Resource Record Types -by assigning type X to the IPSECKEY record. - -</p> -<p> -This document creates an IANA registry for the algorithm type field. - -</p> -<p> -Values 0, 1 and 2 are defined in <a href="#algotype">RDATA format - algorithm type</a>. Algorithm numbers -3 through 255 can be assigned by IETF Consensus (<a href="#RFC2434">see RFC2434</a>[6]). - -</p> -<p> -This document creates an IANA registry for the gateway type field. - -</p> -<p> -Values 0, 1, 2 and 3 are defined in <a href="#gatewaytype">RDATA format - gateway type</a>. -Algorithm numbers 4 through 255 can be assigned by -Standards Action (<a href="#RFC2434">see RFC2434</a>[6]). - -</p> -<a name="anchor15"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<a name="rfc.section.6"></a><h3>6. Acknowledgments</h3> - -<p> -My thanks to Paul Hoffman, Sam Weiler, Jean-Jacques Puig, Rob Austein, -and Olafur Gurmundsson who reviewed this document carefully. -Additional thanks to Olafur Gurmundsson for a reference implementation. - -</p> -<a name="rfc.references1"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Normative references</h3> -<table width="99%" border="0"> -<tr><td class="author-text" valign="top"><b><a name="RFC1034">[1]</a></b></td> -<td class="author-text">Mockapetris, P., "<a href="ftp://ftp.isi.edu/in-notes/rfc1034.txt">Domain names - concepts and facilities</a>", STD 13, RFC 1034, November 1987.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1035">[2]</a></b></td> -<td class="author-text"><a href="mailto:">Mockapetris, P.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1035.txt">Domain names - implementation and specification</a>", STD 13, RFC 1035, November 1987.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC1521">[3]</a></b></td> -<td class="author-text"><a href="mailto:nsb@bellcore.com">Borenstein, N.</a> and <a href="mailto:">N. Freed</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1521.txt">MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies</a>", RFC 1521, September 1993.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2026">[4]</a></b></td> -<td class="author-text"><a href="mailto:sob@harvard.edu">Bradner, S.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2026.txt">The Internet Standards Process -- Revision 3</a>", BCP 9, RFC 2026, October 1996.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2065">[5]</a></b></td> -<td class="author-text"><a href="mailto:dee@cybercash.com">Eastlake, D.</a> and <a href="mailto:charlie_kaufman@iris.com">C. Kaufman</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2065.txt">Domain Name System Security Extensions</a>", RFC 2065, January 1997.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2434">[6]</a></b></td> -<td class="author-text"><a href="mailto:narten@raleigh.ibm.com">Narten, T.</a> and <a href="mailto:Harald@Alvestrand.no">H. Alvestrand</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2434.txt">Guidelines for Writing an IANA Considerations Section in RFCs</a>", BCP 26, RFC 2434, October 1998.</td></tr> -</table> - -<a name="rfc.references2"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Non-normative references</h3> -<table width="99%" border="0"> -<tr><td class="author-text" valign="top"><b><a name="RFC1886">[7]</a></b></td> -<td class="author-text"><a href="mailto:set@thumper.bellcore.com">Thomson, S.</a> and <a href="mailto:Christian.Huitema@MIRSA.INRIA.FR">C. Huitema</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc1886.txt">DNS Extensions to support IP version 6</a>", RFC 1886, December 1995.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2119">[8]</a></b></td> -<td class="author-text"><a href="mailto:-">Bradner, S.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2119.txt">Key words for use in RFCs to Indicate Requirement Levels</a>", BCP 14, RFC 2119, March 1997.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2407">[9]</a></b></td> -<td class="author-text"><a href="mailto:ddp@network-alchemy.com">Piper, D.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2407.txt">The Internet IP Security Domain of Interpretation for ISAKMP</a>", RFC 2407, November 1998.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2535">[10]</a></b></td> -<td class="author-text"><a href="mailto:dee3@us.ibm.com">Eastlake, D.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2535.txt">Domain Name System Security Extensions</a>", RFC 2535, March 1999.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC2536">[11]</a></b></td> -<td class="author-text"><a href="mailto:dee3@us.ibm.com">Eastlake, D.</a>, "<a href="ftp://ftp.isi.edu/in-notes/rfc2536.txt">DSA KEYs and SIGs in the Domain Name System (DNS)</a>", RFC 2536, March 1999.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC3110">[12]</a></b></td> -<td class="author-text">Eastlake, D., "<a href="ftp://ftp.isi.edu/in-notes/rfc3110.txt">RSA/SHA-1 SIGs and RSA KEYs in the Domain Name System (DNS)</a>", RFC 3110, May 2001.</td></tr> -<tr><td class="author-text" valign="top"><b><a name="RFC3445">[13]</a></b></td> -<td class="author-text">Massey, D. and S. Rose, "<a href="ftp://ftp.isi.edu/in-notes/rfc3445.txt">Limiting the Scope of the KEY Resource Record (RR)</a>", RFC 3445, December 2002.</td></tr> -</table> - -<a name="rfc.authors"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Author's Address</h3> -<table width="99%" border="0" cellpadding="0" cellspacing="0"> -<tr><td class="author-text"> </td> -<td class="author-text">Michael C. Richardson</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Sandelman Software Works</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">470 Dawson Avenue</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">Ottawa, ON K1Z 5V7</td></tr> -<tr><td class="author-text"> </td> -<td class="author-text">CA</td></tr> -<tr><td class="author" align="right">EMail: </td> -<td class="author-text"><a href="mailto:mcr@sandelman.ottawa.on.ca">mcr@sandelman.ottawa.on.ca</a></td></tr> -<tr><td class="author" align="right">URI: </td> -<td class="author-text"><a href="http://www.sandelman.ottawa.on.ca/">http://www.sandelman.ottawa.on.ca/</a></td></tr> -</table> -<a name="rfc.copyright"><br><hr size="1" shade="0"></a> -<table border="0" cellpadding="0" cellspacing="2" width="30" height="15" align="right"><tr><td bgcolor="#990000" align="center" width="30" height="15"><a href="#toc" CLASS="link2"><font face="monaco, MS Sans Serif" color="#ffffff" size="1"><b> TOC </b></font></a><br></td></tr></table> -<h3>Full Copyright Statement</h3> -<p class='copyright'> -Copyright (C) The Internet Society (2003). All Rights Reserved.</p> -<p class='copyright'> -This document and translations of it may be copied and furnished to -others, and derivative works that comment on or otherwise explain it -or assist in its implementation may be prepared, copied, published and -distributed, in whole or in part, without restriction of any kind, -provided that the above copyright notice and this paragraph are -included on all such copies and derivative works. However, this -document itself may not be modified in any way, such as by removing -the copyright notice or references to the Internet Society or other -Internet organizations, except as needed for the purpose of -developing Internet standards in which case the procedures for -copyrights defined in the Internet Standards process must be -followed, or as required to translate it into languages other than -English.</p> -<p class='copyright'> -The limited permissions granted above are perpetual and will not be -revoked by the Internet Society or its successors or assigns.</p> -<p class='copyright'> -This document and the information contained herein is provided on an -"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING -TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING -BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION -HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF -MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.</p> -<h3>Acknowledgement</h3> -<p class='copyright'> -Funding for the RFC Editor function is currently provided by the -Internet Society.</p> -</font></body></html> diff --git a/doc/src/draft-richardson-ipsec-rr.xml b/doc/src/draft-richardson-ipsec-rr.xml deleted file mode 100644 index e51b32615..000000000 --- a/doc/src/draft-richardson-ipsec-rr.xml +++ /dev/null @@ -1,560 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE rfc SYSTEM "rfc2629.dtd"> -<?rfc toc="yes"?> - -<rfc ipr="full2026" docName="draft-ietf-ipseckey-rr-07.txt"> - -<front> - <area>Security</area> - <workgroup>IPSECKEY WG</workgroup> - <title abbrev="ipsecrr"> - A method for storing IPsec keying material in DNS. - </title> - - <author initials="M." surname="Richardson" fullname="Michael C. Richardson"> - <organization abbrev="SSW">Sandelman Software Works</organization> - <address> - <postal> - <street>470 Dawson Avenue</street> - <city>Ottawa</city> - <region>ON</region> - <code>K1Z 5V7</code> - <country>CA</country> - </postal> - <email>mcr@sandelman.ottawa.on.ca</email> - <uri>http://www.sandelman.ottawa.on.ca/</uri> - </address> - </author> - - <date month="September" year="2003" /> - -<abstract> - <t> -This document describes a new resource record for DNS. This record may be -used to store public keys for use in IPsec systems. -</t> - -<t> -This record replaces the functionality of the sub-type #1 of the KEY Resource -Record, which has been obsoleted by RFC3445. -</t> -</abstract> - -</front> - -<middle> - -<section title="Introduction"> -<t> - The type number for the IPSECKEY RR is TBD. -</t> - -<section title="Overview"> -<t> - The IPSECKEY resource record (RR) is used to publish a public key that is - to be associated with a Domain Name System (DNS) name for use with the - IPsec protocol suite. This can be the public key of a host, - network, or application (in the case of per-port keying). -</t> - -<t> - The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL - NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and - "OPTIONAL" in this document are to be interpreted as described in - RFC2119 <xref target="RFC2119" />. -</t> -</section> - -<section title="Usage Criteria"> -<t> - An IPSECKEY resource record SHOULD be used in combination with DNSSEC -unless some other means of authenticating the IPSECKEY resource record -is available. -</t> - -<t> - It is expected that there will often be multiple IPSECKEY resource - records at the same name. This will be due to the presence - of multiple gateways and the need to rollover keys. - -</t> - -<t> - This resource record is class independent. -</t> -</section> -</section> - -<section title="Storage formats"> - -<section title="IPSECKEY RDATA format"> - -<t> - The RDATA for an IPSECKEY RR consists of a precedence value, a public key, - algorithm type, and an optional gateway address. -</t> - -<artwork><![CDATA[ - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | precedence | gateway type | algorithm | gateway | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-------------+ + - ~ gateway ~ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | / - / public key / - / / - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| -]]></artwork> -</section> - -<section title="RDATA format - precedence"> -<t> -This is an 8-bit precedence for this record. This is interpreted in -the same way as the PREFERENCE field described in section -3.3.9 of RFC1035 <xref target="RFC1035" />. -</t> -<t> -Gateways listed in IPSECKEY records with lower precedence are -to be attempted first. Where there is a tie in precedence, the order -should be non-deterministic. -</t> -</section> - -<section anchor="algotype" title="RDATA format - algorithm type"> -<t> -The algorithm type field identifies the public key's cryptographic -algorithm and determines the format of the public key field. -</t> - -<t> -A value of 0 indicates that no key is present. -</t> - -<t> -The following values are defined: - <list style="hanging"> - <t hangText="1">A DSA key is present, in the format defined in RFC2536 <xref target="RFC2536" /></t> - <t hangText="2">A RSA key is present, in the format defined in RFC3110 <xref target="RFC3110" /></t> - </list> -</t> - -</section> - -<section anchor="gatewaytype" title="RDATA format - gateway type"> -<t> -The gateway type field indicates the format of the information that -is stored in the gateway field. -</t> - -<t> -The following values are defined: - <list style="hanging"> - <t hangText="0">No gateway is present</t> - <t hangText="1">A 4-byte IPv4 address is present</t> - <t hangText="2">A 16-byte IPv6 address is present</t> - <t hangText="3">A wire-encoded domain name is present. The wire-encoded -format is self-describing, so the length is implicit. The domain name -MUST NOT be compressed.</t> - </list> -</t> - -</section> - -<section title="RDATA format - gateway"> -<t> -The gateway field indicates a gateway to which an IPsec tunnel may be -created in order to reach the entity named by this resource record. -</t> -<t> -There are three formats: -</t> - -<t> -A 32-bit IPv4 address is present in the gateway field. The data -portion is an IPv4 address as described in section 3.4.1 of -<xref target="RFC1035">RFC1035</xref>. This is a 32-bit number in network byte order. -</t> - -<t>A 128-bit IPv6 address is present in the gateway field. -The data portion is an IPv6 address as described in section 2.2 of -<xref target="RFC1886">RFC1886</xref>. This is a 128-bit number in network byte order. -</t> - -<t> -The gateway field is a normal wire-encoded domain name, as described -in section 3.3 of RFC1035 <xref target="RFC1035" />. Compression MUST NOT be used. -</t> - -</section> - -<section title="RDATA format - public keys"> -<t> -Both of the public key types defined in this document (RSA and DSA) -inherit their public key formats from the corresponding KEY RR formats. -Specifically, the public key field contains the algorithm-specific -portion of the KEY RR RDATA, which is all of the KEY RR DATA after the -first four octets. This is the same portion of the KEY RR that must be -specified by documents that define a DNSSEC algorithm. -Those documents also specify a message digest to be used for generation -of SIG RRs; that specification is not relevant for IPSECKEY RR. -</t> - -<t> -Future algorithms, if they are to be used by both DNSSEC (in the KEY -RR) and IPSECKEY, are likely to use the same public key encodings in -both records. Unless otherwise specified, the IPSECKEY public key -field will contain the algorithm-specific portion of the KEY RR RDATA -for the corresponding algorithm. The algorithm must still be -designated for use by IPSECKEY, and an IPSECKEY algorithm type number -(which might be different than the DNSSEC algorithm number) must be -assigned to it. -</t> - -<t>The DSA key format is defined in RFC2536 <xref target="RFC2536" /></t>. - -<t>The RSA key format is defined in RFC3110 <xref target="RFC3110" />, -with the following changes:</t> - -<t> -The earlier definition of RSA/MD5 in RFC2065 limited the exponent and -modulus to 2552 bits in length. RFC3110 extended that limit to 4096 -bits for RSA/SHA1 keys. The IPSECKEY RR imposes no length limit on -RSA public keys, other than the 65535 octet limit imposed by the -two-octet length encoding. This length extension is applicable only -to IPSECKEY and not to KEY RRs. -</t> - -</section> - -</section> - - - -<section title="Presentation formats"> - -<section title="Representation of IPSECKEY RRs"> -<t> - IPSECKEY RRs may appear in a zone data master file. - The precedence, gateway type and algorithm and gateway fields are REQUIRED. - The base64 encoded public key block is OPTIONAL; if not present, - then the public key field of the resource record MUST be construed - as being zero octets in length. -</t> -<t> - The algorithm field is an unsigned integer. No mnemonics are defined. -</t> -<t> - If no gateway is to be indicated, then the gateway type field MUST - be zero, and the gateway field MUST be "." -</t> - -<t> - The Public Key field is represented as a Base64 encoding of the - Public Key. Whitespace is allowed within the Base64 text. For a - definition of Base64 encoding, see -<xref target="RFC1521">RFC1521</xref> Section 5.2. -</t> - - -<t> - The general presentation for the record as as follows: -<artwork><![CDATA[ -IN IPSECKEY ( precedence gateway-type algorithm - gateway base64-encoded-public-key ) -]]></artwork> -</t> -</section> - - -<section title="Examples"> -<t> -An example of a node 192.0.2.38 that will accept IPsec tunnels on its -own behalf. -<artwork><![CDATA[ -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 - 192.0.2.38 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -]]></artwork> -</t> - -<t> -An example of a node, 192.0.2.38 that has published its key only. -<artwork><![CDATA[ -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 - . - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -]]></artwork> -</t> - -<t> -An example of a node, 192.0.2.38 that has delegated authority to the node -192.0.2.3. -<artwork><![CDATA[ -38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 - 192.0.2.3 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -]]></artwork> -</t> - -<t> -An example of a node, 192.0.1.38 that has delegated authority to the node -with the identity "mygateway.example.com". -<artwork><![CDATA[ -38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 - mygateway.example.com. - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -]]></artwork> -</t> - -<t> -An example of a node, 2001:0DB8:0200:1:210:f3ff:fe03:4d0 that has -delegated authority to the node 2001:0DB8:c000:0200:2::1 -<artwork><![CDATA[ -$ORIGIN 1.0.0.0.0.0.2.8.B.D.0.1.0.0.2.ip6.int. -0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2 - 2001:0DB8:0:8002::2000:1 - AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== ) -]]></artwork> -</t> - -</section> -</section> - -<section title="Security Considerations"> -<t> - This entire memo pertains to the provision of public keying material - for use by key management protocols such as ISAKMP/IKE (RFC2407) - <xref target="RFC2407" />. -</t> - -<t> -The IPSECKEY resource record contains information that SHOULD be -communicated to the end client in an integral fashion - i.e. free from -modification. The form of this channel is up to the consumer of the -data - there must be a trust relationship between the end consumer of this -resource record and the server. This relationship may be end-to-end -DNSSEC validation, a TSIG or SIG(0) channel to another secure source, -a secure local channel on the host, or some combination of the above. -</t> - -<t> -The keying material provided by the IPSECKEY resource record is not -sensitive to passive attacks. The keying material may be freely -disclosed to any party without any impact on the security properties -of the resulting IPsec session: IPsec and IKE provide for defense -against both active and passive attacks. -</t> - -<t> - Any user of this resource record MUST carefully document their trust - model, and why the trust model of DNSSEC is appropriate, if that is - the secure channel used. -</t> - -<section title="Active attacks against unsecured IPSECKEY resource records"> -<t> -This section deals with active attacks against the DNS. These attacks -require that DNS requests and responses be intercepted and changed. -DNSSEC is designed to defend against attacks of this kind. -</t> - -<t> -The first kind of active attack is when the attacker replaces the -keying material with either a key under its control or with garbage. -</t> - -<t> -If the attacker is not able to mount a subsequent -man-in-the-middle attack on the IKE negotiation after replacing the -public key, then this will result in a denial of service, as the -authenticator used by IKE would fail. -</t> - -<t> -If the attacker is able to both to mount active attacks against DNS -and is also in a position to perform a man-in-the-middle attack on IKE and -IPsec negotiations, then the attacker will be in a position to compromise -the resulting IPsec channel. Note that an attacker must be able to -perform active DNS attacks on both sides of the IKE negotiation in -order for this to succeed. -</t> - -<t> -The second kind of active attack is one in which the attacker replaces -the the gateway address to point to a node under the attacker's -control. The attacker can then either replace the public key or remove -it, thus providing an IPSECKEY record of its own to match the -gateway address. -</t> - -<t> -This later form creates a simple man-in-the-middle since the attacker -can then create a second tunnel to the real destination. Note that, as before, -this requires that the attacker also mount an active attack against -the responder. -</t> - -<t> -Note that the man-in-the-middle can not just forward cleartext -packets to the original destination. While the destination may be -willing to speak in the clear, replying to the original sender, -the sender will have already created a policy expecting ciphertext. -Thus, the attacker will need to intercept traffic from both sides. In some -cases, the attacker may be able to accomplish the full intercept by use -of Network Addresss/Port Translation (NAT/NAPT) technology. -</t> - -<t> -Note that the danger here only applies to cases where the gateway -field of the IPSECKEY RR indicates a different entity than the owner -name of the IPSECKEY RR. In cases where the end-to-end integrity of -the IPSECKEY RR is suspect, the end client MUST restrict its use -of the IPSECKEY RR to cases where the RR owner name matches the -content of the gateway field. -</t> -</section> - -</section> - -<section title="IANA Considerations"> -<t> -This document updates the IANA Registry for DNS Resource Record Types -by assigning type X to the IPSECKEY record. -</t> - -<t> -This document creates an IANA registry for the algorithm type field. -</t> -<t> -Values 0, 1 and 2 are defined in <xref target="algotype" />. Algorithm numbers -3 through 255 can be assigned by IETF Consensus (<xref target="RFC2434">see RFC2434</xref>). -</t> - -<t> -This document creates an IANA registry for the gateway type field. -</t> -<t> -Values 0, 1, 2 and 3 are defined in <xref target="gatewaytype" />. -Algorithm numbers 4 through 255 can be assigned by -Standards Action (<xref target="RFC2434">see RFC2434</xref>). -</t> - - - -</section> - -<section title="Acknowledgments"> -<t> -My thanks to Paul Hoffman, Sam Weiler, Jean-Jacques Puig, Rob Austein, -and Olafur Gurmundsson who reviewed this document carefully. -Additional thanks to Olafur Gurmundsson for a reference implementation. -</t> -</section> - -</middle> - -<back> -<references title="Normative references"> -<!-- DNSSEC --> -<?rfc include="reference.RFC.1034" ?> -<?rfc include="reference.RFC.1035" ?> -<?rfc include="reference.RFC.1521" ?> -<?rfc include="reference.RFC.2026" ?> -<?rfc include="reference.RFC.2065" ?> -<?rfc include="reference.RFC.2434" ?> -</references> - -<references title="Non-normative references"> -<?rfc include="reference.RFC.1886" ?> -<?rfc include="reference.RFC.2119" ?> -<?rfc include="reference.RFC.2407" ?> -<?rfc include="reference.RFC.2535" ?> -<?rfc include="reference.RFC.2536" ?> -<?rfc include="reference.RFC.3110" ?> -<?rfc include="reference.RFC.3445" ?> -</references> -</back> -</rfc> -<!-- - $Id: draft-richardson-ipsec-rr.xml,v 1.1 2004/03/15 20:35:24 as Exp $ - - $Log: draft-richardson-ipsec-rr.xml,v $ - Revision 1.1 2004/03/15 20:35:24 as - added files from freeswan-2.04-x509-1.5.3 - - Revision 1.23 2003/09/04 23:26:09 mcr - more nits. - - Revision 1.22 2003/08/16 15:55:35 mcr - fixed version to -06. - - Revision 1.21 2003/08/16 15:52:32 mcr - Sam's comments on IANA considerations. - - Revision 1.20 2003/07/27 22:57:54 mcr - updated document with new text about a seperate registry - for the algorithm type. - - Revision 1.19 2003/06/30 01:51:50 mcr - minor typo fixes. - - Revision 1.18 2003/06/16 17:45:00 mcr - adjusted date on rev-04. - - Revision 1.17 2003/06/16 17:41:30 mcr - revision -04 - - Revision 1.16 2003/06/16 17:39:20 mcr - adjusted typos, and adjusted IANA considerations. - - Revision 1.15 2003/05/26 19:31:23 mcr - updates to drafts - IPSEC RR - SC versions, and RFC3526 - reference in OE draft. - - Revision 1.14 2003/05/23 13:57:40 mcr - updated draft ##. - - Revision 1.13 2003/05/23 13:54:45 mcr - updated month on draft. - - Revision 1.12 2003/05/21 15:42:49 mcr - new SC section with comments from Rob Austein. - - Revision 1.11 2003/05/20 20:52:22 mcr - new security considerations section. - - Revision 1.10 2003/05/20 19:07:47 mcr - rewrote Security Considerations. - - Revision 1.9 2003/05/20 18:17:09 mcr - nits from Rob Austein. - - Revision 1.8 2003/04/29 00:44:59 mcr - updates according to WG consensus: restored three-way - gateway field type. - - Revision 1.7 2003/03/30 17:00:29 mcr - updates according to community feedback. - - Revision 1.6 2003/03/19 02:20:24 mcr - updated draft based upon comments from working group - - Revision 1.5 2003/02/23 22:39:22 mcr - updates to IPSECKEY draft. - - Revision 1.4 2003/02/21 04:39:04 mcr - updated drafts, and added crosscompile.html - - Revision 1.3 2003/01/17 16:26:34 mcr - updated IPSEC KEY draft with restrictions. - - Revision 1.2 2002/08/26 18:20:54 mcr - updated documents - - Revision 1.1 2002/08/10 20:05:33 mcr - document proposing IPSECKEY Resource Record - - -!> diff --git a/doc/utils/rfc_pg.c b/doc/utils/rfc_pg.c index 448cc1a36..e2484959e 100644 --- a/doc/utils/rfc_pg.c +++ b/doc/utils/rfc_pg.c @@ -1,5 +1,5 @@ /* - * $Header: /var/cvsroot/strongswan/doc/utils/rfc_pg.c,v 1.1 2004/03/15 20:35:24 as Exp $ + * $Header: /root/strongswan/doc/utils/rfc_pg.c,v 1.1 2004/03/15 20:35:24 as Exp $ * * from 2-nroff.template file. * diff --git a/lib/Makefile b/lib/Makefile index fa2b27bef..8f0b6ec24 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -12,7 +12,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: Makefile,v 1.1 2004/03/15 20:35:24 as Exp $ +# RCSID $Id: Makefile,v 1.2 2006/10/19 18:12:45 as Exp $ FREESWANSRCDIR=.. include ${FREESWANSRCDIR}/Makefile.inc @@ -37,5 +37,4 @@ cleanall distclean mostlyclean realclean install programs checkprograms check cl @for d in $(SUBDIRS) ; \ do \ (cd $$d && $(MAKE) FREESWANSRCDIR=$(FREESWANSRCDIR)/.. $@ ) || exit 1; \ - done; \ - + done; diff --git a/programs/Makefile b/programs/Makefile index 03c9d582a..dbc03f416 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -12,7 +12,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: Makefile,v 1.8 2006/04/17 11:04:45 as Exp $ +# RCSID $Id: Makefile,v 1.9 2006/08/28 11:12:36 as Exp $ FREESWANSRCDIR=.. include ${FREESWANSRCDIR}/Makefile.inc @@ -42,5 +42,5 @@ cleanall distclean mostlyclean realclean install programs checkprograms check cl @for d in $(SUBDIRS) ; \ do \ (cd $$d && $(MAKE) FREESWANSRCDIR=$(FREESWANSRCDIR)/.. $@ ) || exit 1;\ - done; \ + done; diff --git a/programs/pluto/connections.c b/programs/pluto/connections.c index 8bd3ed49b..1ea6ac5fa 100644 --- a/programs/pluto/connections.c +++ b/programs/pluto/connections.c @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: connections.c,v 1.44 2006/07/06 19:20:09 as Exp $ + * RCSID $Id: connections.c,v 1.46 2006/10/19 15:40:52 as Exp $ */ #include <string.h> @@ -862,6 +862,7 @@ extract_end(struct end *dst, const whack_end_t *src, const char *which) dst->host_addr = src->host_addr; dst->host_nexthop = src->host_nexthop; dst->host_srcip = src->host_srcip; + dst->has_natip = src->has_natip; dst->client = src->client; dst->protocol = src->protocol; dst->port = src->port; @@ -880,6 +881,7 @@ extract_end(struct end *dst, const whack_end_t *src, const char *which) */ if (addrbytesptr(&dst->host_srcip, NULL) && !isanyaddr(&dst->host_srcip) + && !dst->has_natip && !dst->has_client) { err_t ugh = addrtosubnet(&dst->host_srcip, &dst->client); @@ -2024,7 +2026,8 @@ cannot_oppo(struct connection *c char state_buf2[LOG_WIDTH]; time_t n = now(); - fmt_state(st, n, state_buf, sizeof(state_buf) + fmt_state(FALSE, st, n + , state_buf, sizeof(state_buf) , state_buf2, sizeof(state_buf2)); DBG_log("cannot_oppo, failure SA1: %s", state_buf); DBG_log("cannot_oppo, failure SA2: %s", state_buf2); @@ -3065,7 +3068,7 @@ ISAKMP_SA_established(struct connection *c, so_serial_t serial) /* the connection is now oriented so that we are able to determine * whether we are a mode config server with a virtual IP to send. */ - if (!isanyaddr(&c->spd.that.host_srcip)) + if (!isanyaddr(&c->spd.that.host_srcip) && !c->spd.that.has_natip) c->spd.that.modecfg = TRUE; if (uniqueIDs) diff --git a/programs/pluto/connections.h b/programs/pluto/connections.h index 6dfddbe22..33fbc3fea 100644 --- a/programs/pluto/connections.h +++ b/programs/pluto/connections.h @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: connections.h,v 1.18 2006/04/22 21:59:20 as Exp $ + * RCSID $Id: connections.h,v 1.19 2006/10/19 15:38:27 as Exp $ */ #ifndef _CONNECTIONS_H @@ -143,6 +143,7 @@ struct end { bool has_client_wildcard; bool has_port_wildcard; bool has_id_wildcards; + bool has_natip; char *updown; u_int16_t host_port; /* host order */ u_int16_t port; /* host order */ diff --git a/programs/pluto/constants.c b/programs/pluto/constants.c index 27e4db1e0..5ca7b65ce 100644 --- a/programs/pluto/constants.c +++ b/programs/pluto/constants.c @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: constants.c,v 1.21 2006/03/27 07:38:59 as Exp $ + * RCSID $Id: constants.c,v 1.22 2006/10/19 21:07:40 as Exp $ */ /* @@ -188,6 +188,7 @@ static const char *const state_name[] = { "STATE_MODE_CFG_R2", "STATE_MODE_CFG_I1", "STATE_MODE_CFG_I2", + "STATE_MODE_CFG_I3", "STATE_IKE_ROOF" }; @@ -218,9 +219,10 @@ const char *const state_story[] = { "sent ModeCfg reply", /* STATE_MODE_CFG_R0 */ "sent ModeCfg reply", /* STATE_MODE_CFG_R1 */ - "ModeCfg R2", /* STATE_MODE_CFG_R2 */ + "received ModeCfg ack", /* STATE_MODE_CFG_R2 */ "sent ModeCfg request, expecting reply", /* STATE_MODE_CFG_I1 */ "received ModeCfg reply", /* STATE_MODE_CFG_I2 */ + "received ModeCfg set, sent ack", /* STATE_MODE_CFG_I3 */ }; /* kind of struct connection */ @@ -484,6 +486,7 @@ const char *const sa_policy_bit_names[] = { "GROUP", "GROUTED", "UP", + "MODECFGPUSH", NULL }; diff --git a/programs/pluto/constants.h b/programs/pluto/constants.h index b66d002ee..bad162898 100644 --- a/programs/pluto/constants.h +++ b/programs/pluto/constants.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: constants.h,v 1.20 2006/02/28 19:13:33 as Exp $ + * RCSID $Id: constants.h,v 1.22 2006/10/19 21:07:40 as Exp $ */ #ifndef _CONSTANTS_H @@ -510,6 +510,7 @@ enum state_kind { STATE_MODE_CFG_I1, /* this is used on the initiator */ STATE_MODE_CFG_I2, + STATE_MODE_CFG_I3, STATE_IKE_ROOF }; @@ -519,17 +520,21 @@ enum state_kind { #define PHASE1_INITIATOR_STATES (LELEM(STATE_MAIN_I1) | LELEM(STATE_MAIN_I2) \ | LELEM(STATE_MAIN_I3) | LELEM(STATE_MAIN_I4)) #define ISAKMP_SA_ESTABLISHED_STATES (LELEM(STATE_MAIN_R3) | LELEM(STATE_MAIN_I4) \ - | LELEM(STATE_MODE_CFG_R1) | LELEM(STATE_MODE_CFG_I2)) + | LELEM(STATE_MODE_CFG_R1) | LELEM(STATE_MODE_CFG_R2) \ + | LELEM(STATE_MODE_CFG_I2) | LELEM(STATE_MODE_CFG_I3)) #define IS_PHASE1(s) ((STATE_MAIN_R0 <= (s) && (s) <= STATE_MAIN_I4) \ - || (STATE_MODE_CFG_R0 <= (s) && (s) <= STATE_MODE_CFG_I2)) + || (STATE_MODE_CFG_R0 <= (s) && (s) <= STATE_MODE_CFG_I3)) #define IS_QUICK(s) (STATE_QUICK_R0 <= (s) && (s) <= STATE_QUICK_R2) #define IS_ISAKMP_ENCRYPTED(s) (STATE_MAIN_I2 <= (s)) -#define IS_ISAKMP_SA_ESTABLISHED(s) ((s) == STATE_MAIN_R3 \ - || (s) == STATE_MAIN_I4 \ +#define IS_ISAKMP_SA_ESTABLISHED(s) ( \ + (s) == STATE_MAIN_R3 \ + || (s) == STATE_MAIN_I4 \ || (s) == STATE_MODE_CFG_R0 \ || (s) == STATE_MODE_CFG_R1 \ - || (s) == STATE_MODE_CFG_I2) + || (s) == STATE_MODE_CFG_R2 \ + || (s) == STATE_MODE_CFG_I2 \ + || (s) == STATE_MODE_CFG_I3) #define IS_IPSEC_SA_ESTABLISHED(s) ((s) == STATE_QUICK_I2 || (s) == STATE_QUICK_R2) #define IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(s) ((s) == STATE_QUICK_R1) @@ -783,11 +788,12 @@ extern const char *prettypolicy(lset_t policy); /* connection policy * Other policies could vary per state object. These live in connection. */ -#define POLICY_DONT_REKEY LELEM(12) /* don't rekey state either Phase */ -#define POLICY_OPPO LELEM(13) /* is this opportunistic? */ -#define POLICY_GROUP LELEM(14) /* is this a group template? */ -#define POLICY_GROUTED LELEM(15) /* do we want this group routed? */ -#define POLICY_UP LELEM(16) /* do we want this up? */ +#define POLICY_DONT_REKEY LELEM(12) /* don't rekey state either Phase */ +#define POLICY_OPPO LELEM(13) /* is this opportunistic? */ +#define POLICY_GROUP LELEM(14) /* is this a group template? */ +#define POLICY_GROUTED LELEM(15) /* do we want this group routed? */ +#define POLICY_UP LELEM(16) /* do we want this up? */ +#define POLICY_MODECFG_PUSH LELEM(17) /* is modecfg pushed by server? */ /* Any IPsec policy? If not, a connection description diff --git a/programs/pluto/demux.c b/programs/pluto/demux.c index db7f1c4a6..3146b3d40 100644 --- a/programs/pluto/demux.c +++ b/programs/pluto/demux.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: demux.c,v 1.14 2006/06/22 11:58:25 as Exp $ + * RCSID $Id: demux.c,v 1.16 2006/10/19 21:07:40 as Exp $ */ /* Ordering Constraints on Payloads @@ -481,7 +481,17 @@ static const struct state_microcode state_microcode_table[] = { { STATE_MODE_CFG_I1, STATE_MODE_CFG_I2 , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RELEASE_PENDING_P2 , P(ATTR) | P(HASH), P(VID), PT(HASH) - , EVENT_SA_REPLACE, modecfg_inR1 }, + , EVENT_SA_REPLACE, modecfg_inI1 }, + + { STATE_MODE_CFG_I2, STATE_MODE_CFG_I3 + , SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_REPLY | SMF_RELEASE_PENDING_P2 + , P(ATTR) | P(HASH), P(VID), PT(HASH) + , EVENT_SA_REPLACE, modecfg_inI2 }, + + { STATE_MODE_CFG_I3, STATE_UNDEFINED + , SMF_ALL_AUTH | SMF_ENCRYPTED + , LEMPTY, LEMPTY, PT(NONE) + , EVENT_NULL, unexpected }, #undef P #undef PT @@ -1441,7 +1451,7 @@ process_packet(struct msg_digest **mdp) { st->st_state = STATE_MAIN_R3; /* ISAKMP is up... */ } - + set_cur_state(st); if (!IS_ISAKMP_SA_ESTABLISHED(st->st_state)) @@ -1471,7 +1481,7 @@ process_packet(struct msg_digest **mdp) } else { - set_cur_state(st); + set_cur_state(st); from_state = st->st_state; } @@ -1563,7 +1573,7 @@ process_packet(struct msg_digest **mdp) else if (st->st_connection->spd.this.modecfg && IS_PHASE1(st->st_state)) { - from_state = STATE_MODE_CFG_R1; + from_state = STATE_MODE_CFG_I2; } else { @@ -2323,38 +2333,39 @@ complete_state_transition(struct msg_digest **mdp, stf_status result) , story, sadetails); } - /* Should we start Mode Config as a client */ + /* Should we start ModeConfig as a client? */ if (st->st_connection->spd.this.modecfg && IS_ISAKMP_SA_ESTABLISHED(st->st_state) + && !(st->st_connection->policy & POLICY_MODECFG_PUSH) && !st->st_modecfg.started) { DBG(DBG_CONTROL, - DBG_log("modecfg client is starting") + DBG_log("starting ModeCfg client in pull mode") ) modecfg_send_request(st); break; } - /* Should we set the peer's IP address regardless? */ -/* if (st->st_connection->spd.that.modecfg + /* Should we start ModeConfig as a server? */ + if (st->st_connection->spd.that.modecfg && IS_ISAKMP_SA_ESTABLISHED(st->st_state) - && !st->st_modecfg.vars_set - && !(st->st_connection->policy & POLICY_MODECFG_PULL)) + && !st->st_modecfg.started + && (st->st_connection->policy & POLICY_MODECFG_PUSH)) { - st->st_state = STATE_MODE_CFG_R1; - set_cur_state(st); - plog("Sending MODE CONFIG set"); - modecfg_start_set(st); + DBG(DBG_CONTROL, + DBG_log("starting ModeCfg server in push mode") + ) + modecfg_send_set(st); break; } -*/ - /* wait for modecfg_set */ + + /* Wait for ModeConfig set from server */ if (st->st_connection->spd.this.modecfg && IS_ISAKMP_SA_ESTABLISHED(st->st_state) && !st->st_modecfg.vars_set) { DBG(DBG_CONTROL, - DBG_log("waiting for modecfg set from server") + DBG_log("waiting for ModeCfg set from server") ) break; } diff --git a/programs/pluto/log.c b/programs/pluto/log.c index 73ffceccd..aef93ff3c 100644 --- a/programs/pluto/log.c +++ b/programs/pluto/log.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: log.c,v 1.8 2006/04/29 18:16:02 as Exp $ + * RCSID $Id: log.c,v 1.9 2006/10/17 10:30:54 as Exp $ */ #include <stdio.h> @@ -773,7 +773,7 @@ show_status(bool all, const char *name) whack_log(RC_COMMENT, BLANK_FORMAT); /* spacer */ } show_connections_status(all, name); - show_states_status(name); + show_states_status(all, name); #ifdef KLIPS show_shunt_status(); #endif diff --git a/programs/pluto/modecfg.c b/programs/pluto/modecfg.c index 1c22845a5..ec334aaf5 100644 --- a/programs/pluto/modecfg.c +++ b/programs/pluto/modecfg.c @@ -2,6 +2,7 @@ * Copyright (C) 2001-2002 Colubris Networks * Copyright (C) 2003 Sean Mathews - Nu Tech Software Solutions, inc. * Copyright (C) 2003-2004 Xelerance Corporation + * Copyright (C) 2006 Andreas Steffen - Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -13,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: modecfg.c,v 1.6 2006/04/24 20:44:57 as Exp $ + * RCSID $Id: modecfg.c,v 1.9 2006/10/21 17:11:13 as Exp $ * * This code originally written by Colubris Networks, Inc. * Extraction of patch and porting to 1.99 codebases by Xelerance Corporation @@ -39,23 +40,46 @@ #include "modecfg.h" #include "whack.h" +#define SUPPORTED_ATTR_SET ( LELEM(INTERNAL_IP4_ADDRESS) \ + | LELEM(INTERNAL_IP4_NETMASK) \ + | LELEM(INTERNAL_IP4_DNS) \ + | LELEM(INTERNAL_IP4_NBNS) \ + ) + /* - * Addresses assigned (usually via MODE_CONFIG) to the Initiator + * Addresses assigned (usually via ModeCfg) to the Initiator */ +typedef struct internal_addr internal_addr_t; + struct internal_addr { + lset_t attr_set; ip_address ipaddr; ip_address dns[2]; - ip_address wins[2]; + ip_address wins[2]; }; /* - * Get inside IP address for a connection + * Initialize an internal_addr struct */ static void -get_internal_addresses(struct connection *c, struct internal_addr *ia) +init_internal_addr(internal_addr_t *ia) { - zero(ia); + ia->attr_set = LEMPTY; + anyaddr(AF_INET, &ia->ipaddr); + anyaddr(AF_INET, &ia->dns[0]); + anyaddr(AF_INET, &ia->dns[1]); + anyaddr(AF_INET, &ia->wins[0]); + anyaddr(AF_INET, &ia->wins[1]); +} + +/* + * get internal IP address for a connection + */ +static void +get_internal_addr(struct connection *c, internal_addr_t *ia) +{ + init_internal_addr(ia); if (isanyaddr(&c->spd.that.host_srcip)) { @@ -63,16 +87,78 @@ get_internal_addresses(struct connection *c, struct internal_addr *ia) } else { + char srcip[ADDRTOT_BUF]; + ia->ipaddr = c->spd.that.host_srcip; + + addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip)); + plog("assigning virtual IP source address %s", srcip); + } + + if (!isanyaddr(&ia->ipaddr)) /* We got an IP address, send it */ + { + c->spd.that.client.addr = ia->ipaddr; + c->spd.that.client.maskbits = 32; + c->spd.that.has_client = TRUE; + + ia->attr_set |= LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_NETMASK); + } + + + if (!isanyaddr(&ia->dns[0])) /* We got DNS addresses, send them */ + ia->attr_set |= LELEM(INTERNAL_IP4_DNS); + + if (!isanyaddr(&ia->wins[0])) /* We got WINS addresses, send them */ + ia->attr_set |= LELEM(INTERNAL_IP4_NBNS); +} + +/* + * Set srcip and client subnet to internal IP address + */ +static bool +set_internal_addr(struct connection *c, internal_addr_t *ia) +{ + if (ia->attr_set & LELEM(INTERNAL_IP4_ADDRESS) + && !isanyaddr(&ia->ipaddr)) + { + if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 + || isanyaddr(&c->spd.this.host_srcip) + || sameaddr(&c->spd.this.host_srcip, &ia->ipaddr)) + { + char srcip[ADDRTOT_BUF]; + + addrtot(&ia->ipaddr, 0, srcip, sizeof(srcip)); + plog("setting virtual IP source address to %s", srcip); + } + else + { + char old_srcip[ADDRTOT_BUF]; + char new_srcip[ADDRTOT_BUF]; + + addrtot(&c->spd.this.host_srcip, 0, old_srcip, sizeof(old_srcip)); + addrtot(&ia->ipaddr, 0, new_srcip, sizeof(new_srcip)); + plog("replacing virtual IP source address %s by %s" + , old_srcip, new_srcip); + } + + /* setting srcip */ + c->spd.this.host_srcip = ia->ipaddr; + + /* setting client subnet to srcip/32 */ + addrtosubnet(&ia->ipaddr, &c->spd.this.client); + setportof(0, &c->spd.this.client.addr); + c->spd.this.has_client = TRUE; + return TRUE; } + return FALSE; } /* * Compute HASH of Mode Config. */ static size_t -mode_cfg_hash(u_char *dest, const u_char *start, const u_char *roof - , const struct state *st) +modecfg_hash(u_char *dest, const u_char *start, const u_char *roof + , const struct state *st) { struct hmac_ctx ctx; @@ -82,92 +168,51 @@ mode_cfg_hash(u_char *dest, const u_char *start, const u_char *roof hmac_final(dest, &ctx); DBG(DBG_CRYPT, - DBG_log("MODE CFG: HASH computed:"); + DBG_log("ModeCfg HASH computed:"); DBG_dump("", dest, ctx.hmac_digest_size) ) return ctx.hmac_digest_size; } -/* Mode Config Reply - * Generates a reply stream containing Mode Config information (eg: IP, DNS, WINS) +/* + * Generate an IKE message containing ModeCfg information (eg: IP, DNS, WINS) */ -stf_status modecfg_resp(struct state *st - , u_int resp - , pb_stream *rbody - , u_int16_t replytype - , bool hackthat - , u_int16_t ap_id) +static stf_status +modecfg_build_msg(struct state *st, pb_stream *rbody + , u_int16_t msg_type + , internal_addr_t *ia + , u_int16_t ap_id) { - u_char *r_hash_start,*r_hashval; - - /* START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); */ + u_char *r_hash_start, *r_hashval; - { - pb_stream hash_pbs; - int np = ISAKMP_NEXT_ATTR; - - if (!out_generic(np, &isakmp_hash_desc, rbody, &hash_pbs)) - return STF_INTERNAL_ERROR; - r_hashval = hash_pbs.cur; /* remember where to plant value */ - if (!out_zero(st->st_oakley.hasher->hash_digest_size, &hash_pbs, "HASH")) - return STF_INTERNAL_ERROR; - close_output_pbs(&hash_pbs); - r_hash_start = (rbody)->cur; /* hash from after HASH payload */ - } + START_HASH_PAYLOAD(*rbody, ISAKMP_NEXT_ATTR); /* ATTR out */ { - struct isakmp_mode_attr attrh; + struct isakmp_mode_attr attrh; struct isakmp_attribute attr; pb_stream strattr,attrval; int attr_type; - struct internal_addr ia; int dns_idx, wins_idx; bool dont_advance; + lset_t attr_set = ia->attr_set; - attrh.isama_np = ISAKMP_NEXT_NONE; - attrh.isama_type = replytype; - + attrh.isama_np = ISAKMP_NEXT_NONE; + attrh.isama_type = msg_type; attrh.isama_identifier = ap_id; + if (!out_struct(&attrh, &isakmp_attr_desc, rbody, &strattr)) return STF_INTERNAL_ERROR; - get_internal_addresses(st->st_connection, &ia); - - if (!isanyaddr(&ia.dns[0])) /* We got DNS addresses, answer with those */ - resp |= LELEM(INTERNAL_IP4_DNS); - else - resp &= ~LELEM(INTERNAL_IP4_DNS); - - if (!isanyaddr(&ia.wins[0])) /* We got WINS addresses, answer with those */ - resp |= LELEM(INTERNAL_IP4_NBNS); - else - resp &= ~LELEM(INTERNAL_IP4_NBNS); - - if (hackthat) - { - if (memcmp(&st->st_connection->spd.that.client.addr - ,&ia.ipaddr - ,sizeof(ia.ipaddr)) != 0) - { - /* Make the Internal IP address and Netmask - * as that client address - */ - st->st_connection->spd.that.client.addr = ia.ipaddr; - st->st_connection->spd.that.client.maskbits = 32; - st->st_connection->spd.that.has_client = TRUE; - } - } - attr_type = 0; dns_idx = 0; wins_idx = 0; - while (resp != 0) + while (attr_set != 0) { dont_advance = FALSE; - if (resp & 1) + if (attr_set & 1) { const u_char *byte_ptr; u_int len; @@ -179,14 +224,11 @@ stf_status modecfg_resp(struct state *st switch (attr_type) { case INTERNAL_IP4_ADDRESS: + if (!isanyaddr(&ia->ipaddr)) { - char srcip[ADDRTOT_BUF]; - - addrtot(&ia.ipaddr, 0, srcip, sizeof(srcip)); - plog("assigning virtual IP source address %s", srcip); - len = addrbytesptr(&ia.ipaddr, &byte_ptr); - out_raw(byte_ptr,len,&attrval,"IP4_addr"); - } + len = addrbytesptr(&ia->ipaddr, &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_addr"); + } break; case INTERNAL_IP4_NETMASK: { @@ -207,7 +249,7 @@ stf_status modecfg_resp(struct state *st mask = 0; else mask = 0xffffffff * 1; - out_raw(&mask,4,&attrval,"IP4_mask"); + out_raw(&mask, 4, &attrval, "IP4_mask"); } break; case INTERNAL_IP4_SUBNET: @@ -228,22 +270,28 @@ stf_status modecfg_resp(struct state *st m = 0; } len = addrbytesptr(&st->st_connection->spd.this.client.addr, &byte_ptr); - out_raw(byte_ptr,len,&attrval,"IP4_subnet"); - out_raw(mask,sizeof(mask),&attrval,"IP4_submsk"); + out_raw(byte_ptr, len, &attrval, "IP4_subnet"); + out_raw(mask, sizeof(mask), &attrval, "IP4_submsk"); } break; case INTERNAL_IP4_DNS: - len = addrbytesptr(&ia.dns[dns_idx++], &byte_ptr); - out_raw(byte_ptr,len,&attrval,"IP4_dns"); - if (dns_idx < 2 && !isanyaddr(&ia.dns[dns_idx])) + if (!isanyaddr(&ia->dns[dns_idx])) + { + len = addrbytesptr(&ia->dns[dns_idx++], &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_dns"); + } + if (dns_idx < 2 && !isanyaddr(&ia->dns[dns_idx])) { dont_advance = TRUE; } break; case INTERNAL_IP4_NBNS: - len = addrbytesptr(&ia.wins[wins_idx++], &byte_ptr); - out_raw(byte_ptr,len,&attrval,"IP4_wins"); - if (wins_idx < 2 && !isanyaddr(&ia.wins[wins_idx])) + if (!isanyaddr(&ia->wins[wins_idx])) + { + len = addrbytesptr(&ia->wins[wins_idx++], &byte_ptr); + out_raw(byte_ptr, len, &attrval, "IP4_wins"); + } + if (wins_idx < 2 && !isanyaddr(&ia->wins[wins_idx])) { dont_advance = TRUE; } @@ -254,35 +302,39 @@ stf_status modecfg_resp(struct state *st break; } close_output_pbs(&attrval); - } if (!dont_advance) { attr_type++; - resp >>= 1; + attr_set >>= 1; } } close_message(&strattr); } - mode_cfg_hash(r_hashval,r_hash_start,rbody->cur,st); + modecfg_hash(r_hashval, r_hash_start, rbody->cur,st); close_message(rbody); encrypt_message(rbody, st); return STF_OK; } -/* Set MODE_CONFIG data to client. - * Pack IP Addresses, DNS, etc... and ship +/* + * Send ModeCfg message */ -stf_status modecfg_send_set(struct state *st) +static stf_status +modecfg_send_msg(struct state *st, int isama_type, internal_addr_t *ia) { - pb_stream reply,rbody; - char buf[256]; + pb_stream msg; + pb_stream rbody; + char buf[BUF_LEN]; - /* set up reply */ - init_pbs(&reply, buf, sizeof(buf), "ModecfgR1"); + /* set up attr */ + init_pbs(&msg, buf, sizeof(buf), "ModeCfg msg buffer"); + + /* this is the beginning of a new exchange */ + st->st_msgid = generate_msgid(st); + init_phase2_iv(st, &st->st_msgid); - st->st_state = STATE_MODE_CFG_R1; /* HDR out */ { struct isakmp_hdr hdr; @@ -296,503 +348,280 @@ stf_status modecfg_send_set(struct state *st) memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); hdr.isa_msgid = st->st_msgid; - if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) + if (!out_struct(&hdr, &isakmp_hdr_desc, &msg, &rbody)) { return STF_INTERNAL_ERROR; } } -#define MODECFG_SET_ITEM ( LELEM(INTERNAL_IP4_ADDRESS) | LELEM(INTERNAL_IP4_SUBNET) | LELEM(INTERNAL_IP4_NBNS) | LELEM(INTERNAL_IP4_DNS) ) - - modecfg_resp(st, MODECFG_SET_ITEM - , &rbody - , ISAKMP_CFG_SET - , TRUE - , 0/* XXX ID */); -#undef MODECFG_SET_ITEM + /* ATTR out */ + modecfg_build_msg(st, &rbody + , isama_type + , ia + , 0 /* XXX isama_id */ + ); - clonetochunk(st->st_tpacket, reply.start - , pbs_offset(&reply), "ModeCfg set"); + clonetochunk(st->st_tpacket, msg.start, pbs_offset(&msg), "ModeCfg msg"); /* Transmit */ - send_packet(st, "ModeCfg set"); + send_packet(st, "ModeCfg msg"); - /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ - if (st->st_event->ev_type != EVENT_RETRANSMIT - && st->st_event->ev_type != EVENT_NULL) + if (st->st_event->ev_type != EVENT_RETRANSMIT) { delete_event(st); event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0, st); } - + st->st_modecfg.started = TRUE; return STF_OK; } -/* Set MODE_CONFIG data to client. - * Pack IP Addresses, DNS, etc... and ship - */ -stf_status -modecfg_start_set(struct state *st) -{ - if (st->st_msgid == 0) - { - /* pick a new message id */ - st->st_msgid = generate_msgid(st); - } - st->st_modecfg.vars_set = TRUE; - - return modecfg_send_set(st); -} - /* - * Send modecfg IP address request (IP4 address) + * Send ModeCfg request message from client to server in pull mode */ stf_status modecfg_send_request(struct state *st) { - pb_stream reply; - pb_stream rbody; - char buf[256]; - u_char *r_hash_start,*r_hashval; + internal_addr_t ia; - /* set up reply */ - init_pbs(&reply, buf, sizeof(buf), "modecfg_buf"); + init_internal_addr(&ia); + ia.attr_set = LELEM(INTERNAL_IP4_ADDRESS) + | LELEM(INTERNAL_IP4_NETMASK); plog("sending ModeCfg request"); - - /* this is the beginning of a new exchange */ - st->st_msgid = generate_msgid(st); st->st_state = STATE_MODE_CFG_I1; + return modecfg_send_msg(st, ISAKMP_CFG_REQUEST, &ia); +} - /* HDR out */ - { - struct isakmp_hdr hdr; - - zero(&hdr); /* default to 0 */ - hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; - hdr.isa_np = ISAKMP_NEXT_HASH; - hdr.isa_xchg = ISAKMP_XCHG_MODE_CFG; - hdr.isa_flags = ISAKMP_FLAG_ENCRYPTION; - memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE); - memcpy(hdr.isa_rcookie, st->st_rcookie, COOKIE_SIZE); - hdr.isa_msgid = st->st_msgid; - - if (!out_struct(&hdr, &isakmp_hdr_desc, &reply, &rbody)) - { - return STF_INTERNAL_ERROR; - } - } - - START_HASH_PAYLOAD(rbody, ISAKMP_NEXT_ATTR); - - /* ATTR out */ - { - struct isakmp_mode_attr attrh; - struct isakmp_attribute attr; - pb_stream strattr; - - attrh.isama_np = ISAKMP_NEXT_NONE; - attrh.isama_type = ISAKMP_CFG_REQUEST; - attrh.isama_identifier = 0; - if (!out_struct(&attrh, &isakmp_attr_desc, &rbody, &strattr)) - return STF_INTERNAL_ERROR; - /* ISAKMP attr out (ipv4) */ - attr.isaat_af_type = INTERNAL_IP4_ADDRESS; - attr.isaat_lv = 0; - out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); - - /* ISAKMP attr out (netmask) */ - attr.isaat_af_type = INTERNAL_IP4_NETMASK; - attr.isaat_lv = 0; - out_struct(&attr, &isakmp_modecfg_attribute_desc, &strattr, NULL); - - close_message(&strattr); - } - - mode_cfg_hash(r_hashval,r_hash_start,rbody.cur,st); - - close_message(&rbody); - close_output_pbs(&reply); - - init_phase2_iv(st, &st->st_msgid); - encrypt_message(&rbody, st); - - clonetochunk(st->st_tpacket, reply.start, pbs_offset(&reply) - , "modecfg: req"); - - /* Transmit */ - send_packet(st, "modecfg: req"); +/* + * Send ModeCfg set message from server to client in push mode + */ +stf_status +modecfg_send_set(struct state *st) +{ + internal_addr_t ia; - /* RETRANSMIT if Main, SA_REPLACE if Aggressive */ - if (st->st_event->ev_type != EVENT_RETRANSMIT) - { - delete_event(st); - event_schedule(EVENT_RETRANSMIT, EVENT_RETRANSMIT_DELAY_0 * 3, st); - } - st->st_modecfg.started = TRUE; + get_internal_addr(st->st_connection, &ia); - return STF_OK; + plog("sending ModeCfg set"); + st->st_state = STATE_MODE_CFG_R1; + return modecfg_send_msg(st, ISAKMP_CFG_SET, &ia); } /* - * parse a modecfg attribute payload + * Parse a ModeCfg attribute payload */ static stf_status -modecfg_parse_attributes(pb_stream *attrs, u_int *set) +modecfg_parse_attributes(pb_stream *attrs, internal_addr_t *ia) { struct isakmp_attribute attr; pb_stream strattr; - while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) + while (pbs_left(attrs) >= sizeof(struct isakmp_attribute)) { + u_int16_t attr_type; + u_int16_t attr_len; + if (!in_struct(&attr, &isakmp_modecfg_attribute_desc, attrs, &strattr)) { - int len = (attr.isaat_af_type & 0x8000)? 4 : attr.isaat_lv; - - if (len < 4) - { - plog("Attribute was too short: %d", len); - return STF_FAIL; - } - - attrs->cur += len; + return STF_FAIL; } + attr_type = attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK; + attr_len = attr.isaat_lv; - switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) + switch (attr_type) { case INTERNAL_IP4_ADDRESS: + if (attr_len == 4) + { + initaddr((char *)(strattr.cur), 4, AF_INET, &ia->ipaddr); + } + /* fall through to set attribute flags */ case INTERNAL_IP4_NETMASK: case INTERNAL_IP4_DNS: case INTERNAL_IP4_SUBNET: case INTERNAL_IP4_NBNS: - *set |= LELEM(attr.isaat_af_type); + ia->attr_set |= LELEM(attr_type); break; default: - plog("unsupported mode cfg attribute %s received." - , enum_show(&modecfg_attr_names - , attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK )); + plog("unsupported ModeCfg attribute %s received." + , enum_show(&modecfg_attr_names, attr_type)); break; } } return STF_OK; } -/* STATE_MODE_CFG_R0: - * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP) - * - * This state occurs both in the responder and in the initiator. - * - * In the responding server, it occurs when the client *asks* for an IP - * address or other information. - * - * Otherwise, it occurs in the initiator when the server sends a challenge - * a set, or has a reply to our request. +/* + * Parse a ModeCfg message */ -stf_status -modecfg_inR0(struct msg_digest *md) +static stf_status +modecfg_parse_msg(struct msg_digest *md, int isama_type, u_int16_t *isama_id + , internal_addr_t *ia) { struct state *const st = md->st; struct payload_digest *p; stf_status stat; - plog("received ModeCfg request"); - st->st_msgid = md->hdr.isa_msgid; - CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val - ,hash_pbs->roof - , md->message_pbs.roof, st) - , "MODECFG-HASH", "MODE R0"); - /* process the MODECFG payloads therein */ + CHECK_QUICK_HASH(md, modecfg_hash(hash_val + , hash_pbs->roof + , md->message_pbs.roof, st) + , "MODECFG-HASH", "ISAKMP_CFG_MSG"); + + /* process the ModeCfg payloads received */ for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) { - u_int set_modecfg_attrs = LEMPTY; - - switch (p->payload.attribute.isama_type) - { - default: - plog("Expecting ISAKMP_CFG_REQUEST, got %s instead (ignored)." - , enum_name(&attr_msg_type_names - , p->payload.attribute.isama_type)); - - stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); - if (stat != STF_OK) - return stat; - break; + internal_addr_t ia_candidate; - case ISAKMP_CFG_REQUEST: - stat = modecfg_parse_attributes(&p->pbs, &set_modecfg_attrs); - if (stat != STF_OK) - return stat; + init_internal_addr(&ia_candidate); - stat = modecfg_resp(st, set_modecfg_attrs - ,&md->rbody - ,ISAKMP_CFG_REPLY - ,TRUE - ,p->payload.attribute.isama_identifier); + if (p->payload.attribute.isama_type == isama_type) + { + *isama_id = p->payload.attribute.isama_identifier; - if (stat != STF_OK) + stat = modecfg_parse_attributes(&p->pbs, &ia_candidate); + if (stat == STF_OK) { - /* notification payload - not exactly the right choice, but okay */ - md->note = CERTIFICATE_UNAVAILABLE; - return stat; + /* retrun with a valid set of attributes */ + *ia = ia_candidate; + return STF_OK; } + } + else + { + plog("expected %s, got %s instead (ignored)" + , enum_name(&attr_msg_type_names, isama_type) + , enum_name(&attr_msg_type_names, p->payload.attribute.isama_type)); - /* they asked us, we responded, msgid is done */ - st->st_msgid = 0; + stat = modecfg_parse_attributes(&p->pbs, &ia_candidate); } + if (stat != STF_OK) + return stat; } - return STF_OK; + return STF_IGNORE; } -/* STATE_MODE_CFG_R2: - * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) +/* STATE_MODE_CFG_R0: + * HDR*, HASH, ATTR(REQ=IP) --> HDR*, HASH, ATTR(REPLY=IP) * - * used in server push mode, on the client (initiator). + * used in ModeCfg pull mode, on the server (responder) */ -static stf_status -modecfg_inI2(struct msg_digest *md) +stf_status +modecfg_inR0(struct msg_digest *md) { struct state *const st = md->st; - pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; - int resp = LEMPTY; + u_int16_t isama_id; + internal_addr_t ia; stf_status stat; - struct payload_digest *p; - u_int16_t isama_id = 0; - - st->st_msgid = md->hdr.isa_msgid; - CHECK_QUICK_HASH(md - , mode_cfg_hash(hash_val - ,hash_pbs->roof - , md->message_pbs.roof - , st) - , "MODECFG-HASH", "MODE R1"); - - for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) - { - struct isakmp_attribute attr; - pb_stream strattr; - isama_id = p->payload.attribute.isama_identifier; - - if (p->payload.attribute.isama_type != ISAKMP_CFG_SET) - { - plog("Expecting MODE_CFG_SET, got %x instead." - ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); - return STF_IGNORE; - } - - /* CHECK that SET has been received. */ - - while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) - { - if (!in_struct(&attr, &isakmp_modecfg_attribute_desc - , attrs, &strattr)) - { - int len; - - /* Skip unknown */ - if (attr.isaat_af_type & 0x8000) - len = 4; - else - len = attr.isaat_lv; - - if (len < 4) - { - plog("Attribute was too short: %d", len); - return STF_FAIL; - } - - attrs->cur += len; - } - - switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) - { - case INTERNAL_IP4_ADDRESS: - { - struct connection *c = st->st_connection; - ip_address a; - u_int32_t *ap = (u_int32_t *)(strattr.cur); - a.u.v4.sin_family = AF_INET; - - memcpy(&a.u.v4.sin_addr.s_addr, ap - , sizeof(a.u.v4.sin_addr.s_addr)); - - if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 - || isanyaddr(&c->spd.this.host_srcip)) - { - char srcip[ADDRTOT_BUF]; - - c->spd.this.host_srcip = a; - addrtot(&a, 0, srcip, sizeof(srcip)); - plog("setting virtual IP source address to %s", srcip); - } - - /* setting client subnet as srcip/32 */ - addrtosubnet(&a, &c->spd.this.client); - c->spd.this.has_client = TRUE; - } - resp |= LELEM(attr.isaat_af_type); - break; - case INTERNAL_IP4_NETMASK: - case INTERNAL_IP4_DNS: - case INTERNAL_IP4_SUBNET: - case INTERNAL_IP4_NBNS: - resp |= LELEM(attr.isaat_af_type); - break; - default: - plog("unsupported mode cfg attribute %s received." - , enum_show(&modecfg_attr_names, (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); - break; - } - } - } + stat = modecfg_parse_msg(md, ISAKMP_CFG_REQUEST, &isama_id, &ia); + if (stat != STF_OK) + return stat; - /* ack things */ - stat = modecfg_resp(st, resp - ,&md->rbody - ,ISAKMP_CFG_ACK - ,FALSE - ,isama_id); + get_internal_addr(st->st_connection, &ia); + /* build ISAKMP_CFG_REPLY */ + stat = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_REPLY + , &ia + , isama_id); if (stat != STF_OK) { /* notification payload - not exactly the right choice, but okay */ - md->note = CERTIFICATE_UNAVAILABLE; + md->note = ATTRIBUTES_NOT_SUPPORTED; return stat; } - /* - * we are done with this exchange, clear things so - * that we can start phase 2 properly - */ st->st_msgid = 0; - - if (resp) - { - st->st_modecfg.vars_set = TRUE; - } return STF_OK; } /* STATE_MODE_CFG_R1: - * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + * HDR*, HASH, ATTR(ACK,OK) + * + * used in ModeCfg push mode, on the server (responder) */ stf_status modecfg_inR1(struct msg_digest *md) { struct state *const st = md->st; - pb_stream *attrs = &md->chain[ISAKMP_NEXT_ATTR]->pbs; - int set_modecfg_attrs = LEMPTY; + u_int16_t isama_id; + internal_addr_t ia; stf_status stat; - struct payload_digest *p; - plog("parsing ModeCfg reply"); - - st->st_msgid = md->hdr.isa_msgid; - CHECK_QUICK_HASH(md, mode_cfg_hash(hash_val,hash_pbs->roof, md->message_pbs.roof, st) - , "MODECFG-HASH", "MODE R1"); + plog("parsing ModeCfg ack"); + stat = modecfg_parse_msg(md, ISAKMP_CFG_ACK, &isama_id, &ia); + if (stat != STF_OK) + return stat; - /* process the MODECFG payloads therein */ - for (p = md->chain[ISAKMP_NEXT_ATTR]; p != NULL; p = p->next) - { - struct isakmp_attribute attr; - pb_stream strattr; - - attrs = &p->pbs; - - switch (p->payload.attribute.isama_type) - { - default: - { - plog("Expecting MODE_CFG_ACK, got %x instead." - ,md->chain[ISAKMP_NEXT_ATTR]->payload.attribute.isama_type); - return STF_IGNORE; - } - break; - - case ISAKMP_CFG_ACK: - /* CHECK that ACK has been received. */ - stat = modecfg_parse_attributes(attrs, &set_modecfg_attrs); - if (stat != STF_OK) - return stat; - break; + st->st_msgid = 0; + return STF_OK; +} - case ISAKMP_CFG_REPLY: - while (pbs_left(attrs) > sizeof(struct isakmp_attribute)) - { - if (!in_struct(&attr, &isakmp_modecfg_attribute_desc - , attrs, &strattr)) - { - /* Skip unknown */ - int len; - if (attr.isaat_af_type & 0x8000) - len = 4; - else - len = attr.isaat_lv; - - if (len < 4) - { - plog("Attribute was too short: %d", len); - return STF_FAIL; - } +/* STATE_MODE_CFG_I1: + * HDR*, HASH, ATTR(REPLY=IP) + * + * used in ModeCfg pull mode, on the client (initiator) + */ +stf_status +modecfg_inI1(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + stf_status stat; - attrs->cur += len; - } + plog("parsing ModeCfg reply"); - switch (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ) - { - case INTERNAL_IP4_ADDRESS: - { - struct connection *c = st->st_connection; - ip_address a; - u_int32_t *ap = (u_int32_t *)(strattr.cur); - a.u.v4.sin_family = AF_INET; + stat = modecfg_parse_msg(md, ISAKMP_CFG_REPLY, &isama_id, &ia); + if (stat != STF_OK) + return stat; - memcpy(&a.u.v4.sin_addr.s_addr, ap - , sizeof(a.u.v4.sin_addr.s_addr)); + st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia); + st->st_msgid = 0; + return STF_OK; +} - if (addrbytesptr(&c->spd.this.host_srcip, NULL) == 0 - || isanyaddr(&c->spd.this.host_srcip)) - { - char srcip[ADDRTOT_BUF]; +/* STATE_MODE_CFG_I2: + * HDR*, HASH, ATTR(SET=IP) --> HDR*, HASH, ATTR(ACK,OK) + * + * used in ModeCfg push mode, on the client (initiator). + */ +stf_status +modecfg_inI2(struct msg_digest *md) +{ + struct state *const st = md->st; + u_int16_t isama_id; + internal_addr_t ia; + lset_t attr_set; + stf_status stat; - c->spd.this.host_srcip = a; - addrtot(&a, 0, srcip, sizeof(srcip)); - plog("setting virtual IP source address to %s", srcip); - } + plog("parsing ModeCfg set"); - /* setting client subnet as srcip/32 */ - addrtosubnet(&a, &c->spd.this.client); - setportof(0, &c->spd.this.client.addr); - c->spd.this.has_client = TRUE; - } - /* fall through to set attribute flage */ + stat = modecfg_parse_msg(md, ISAKMP_CFG_SET, &isama_id, &ia); + if (stat != STF_OK) + return stat; - case INTERNAL_IP4_NETMASK: - case INTERNAL_IP4_DNS: - case INTERNAL_IP4_SUBNET: - case INTERNAL_IP4_NBNS: - set_modecfg_attrs |= LELEM(attr.isaat_af_type); - break; - default: - plog("unsupported mode cfg attribute %s received." - , enum_show(&modecfg_attr_names - , (attr.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK ))); - break; - } - } - break; - } - } + st->st_modecfg.vars_set = set_internal_addr(st->st_connection, &ia); - /* we are done with this exchange, clear things so that we can start phase 2 properly */ - st->st_msgid = 0; + /* prepare ModeCfg ack which sends zero length attributes */ + attr_set = ia.attr_set; + init_internal_addr(&ia); + ia.attr_set = attr_set & SUPPORTED_ATTR_SET; - if (set_modecfg_attrs) + stat = modecfg_build_msg(st, &md->rbody + , ISAKMP_CFG_ACK + , &ia + , isama_id); + if (stat != STF_OK) { - st->st_modecfg.vars_set = TRUE; + /* notification payload - not exactly the right choice, but okay */ + md->note = ATTRIBUTES_NOT_SUPPORTED; + return stat; } + + st->st_msgid = 0; return STF_OK; } diff --git a/programs/pluto/modecfg.h b/programs/pluto/modecfg.h index f6856d263..1f8259ca8 100644 --- a/programs/pluto/modecfg.h +++ b/programs/pluto/modecfg.h @@ -12,22 +12,17 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: modecfg.h,v 1.1 2005/01/06 22:10:15 as Exp $ + * RCSID $Id: modecfg.h,v 1.3 2006/10/19 21:07:40 as Exp $ */ struct state; -stf_status modecfg_resp(struct state *st - , u_int resp - , pb_stream *s, u_int16_t cmd - , bool hackthat, u_int16_t id); - -stf_status modecfg_send_set(struct state *st); - -extern stf_status modecfg_start_set(struct state *st); - -/* Mode Config States */ +/* ModeConfig starting functions */ +extern stf_status modecfg_send_request(struct state *st); +extern stf_status modecfg_send_set(struct state *st); +/* ModeConfig state transition functions */ extern stf_status modecfg_inR0(struct msg_digest *md); extern stf_status modecfg_inR1(struct msg_digest *md); -extern stf_status modecfg_send_request(struct state *st); +extern stf_status modecfg_inI1(struct msg_digest *md); +extern stf_status modecfg_inI2(struct msg_digest *md); diff --git a/programs/pluto/state.c b/programs/pluto/state.c index 0781d2eb3..8181c34b4 100644 --- a/programs/pluto/state.c +++ b/programs/pluto/state.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: state.c,v 1.13 2006/04/29 18:16:02 as Exp $ + * RCSID $Id: state.c,v 1.15 2006/10/20 15:02:23 as Exp $ */ #include <stdio.h> @@ -716,7 +716,7 @@ state_eroute_usage(ip_subnet *ours, ip_subnet *his }); } -void fmt_state(struct state *st, time_t n +void fmt_state(bool all, struct state *st, time_t n , char *state_buf, size_t state_buf_len , char *state_buf2, size_t state_buf2_len) { @@ -735,20 +735,22 @@ void fmt_state(struct state *st, time_t n /* XXX spd-enum */ const char *eo = c->spd.eroute_owner == st->st_serialno ? "; eroute owner" : ""; - + const char *dpd = (all && st->st_dpd && c->dpd_action != DPD_ACTION_NONE) + ? "; DPD active" : ""; + passert(st->st_event != 0); fmt_conn_instance(c, inst); snprintf(state_buf, state_buf_len - , "#%lu: \"%s\"%s %s (%s); %s in %lds%s%s%s" + , "#%lu: \"%s\"%s %s (%s); %s in %lds%s%s%s%s" , st->st_serialno , c->name, inst , enum_name(&state_names, st->st_state) , state_story[st->st_state - STATE_MAIN_R0] , enum_name(&timer_event_names, st->st_event->ev_type) , delta - , np1, np2, eo); + , np1, np2, eo, dpd); /* print out SPIs if SAs are established */ if (state_buf2_len != 0) @@ -846,7 +848,7 @@ state_compare(const void *a, const void *b) } void -show_states_status(const char *name) +show_states_status(bool all, const char *name) { time_t n = now(); int i; @@ -892,7 +894,8 @@ show_states_status(const char *name) st = array[i]; - fmt_state(st, n, state_buf, sizeof(state_buf) + fmt_state(all, st, n + , state_buf, sizeof(state_buf) , state_buf2, sizeof(state_buf2)); whack_log(RC_COMMENT, state_buf); if (state_buf2[0] != '\0') diff --git a/programs/pluto/state.h b/programs/pluto/state.h index 2f30d77f1..c7212fd1a 100644 --- a/programs/pluto/state.h +++ b/programs/pluto/state.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: state.h,v 1.11 2006/03/08 22:12:37 as Exp $ + * RCSID $Id: state.h,v 1.12 2006/10/17 10:30:54 as Exp $ */ #include <sys/types.h> @@ -259,11 +259,11 @@ extern struct state *find_phase1_state(const struct connection *c, lset_t ok_states), *find_sender(size_t packet_len, u_char *packet); -extern void show_states_status(const char *name); +extern void show_states_status(bool all, const char *name); extern void for_each_state(void *(f)(struct state *, void *data), void *data); extern void find_my_cpi_gap(cpi_t *latest_cpi, cpi_t *first_busy_cpi); extern ipsec_spi_t uniquify_his_cpi(ipsec_spi_t cpi, struct state *st); -extern void fmt_state(struct state *st, time_t n +extern void fmt_state(bool all, struct state *st, time_t n , char *state_buf, size_t state_buf_len , char *state_buf2, size_t state_buf_len2); extern void delete_states_by_peer(ip_address *peer); diff --git a/programs/pluto/vendor.c b/programs/pluto/vendor.c index a51971cde..cbb26a5ef 100644 --- a/programs/pluto/vendor.c +++ b/programs/pluto/vendor.c @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: vendor.c,v 1.39 2006/07/06 12:32:41 as Exp $ + * RCSID $Id: vendor.c,v 1.41 2006/10/19 15:21:08 as Exp $ */ #include <stdlib.h> @@ -200,9 +200,13 @@ static struct vid_struct _vid_tab[] = { */ DEC_MD5_VID(STRONGSWAN_4_0_0, "strongSwan 4.0.0") DEC_MD5_VID(STRONGSWAN_4_0_1, "strongSwan 4.0.1") - DEC_MD5_VID(STRONGSWAN_4_0_1, "strongSwan 4.0.2") + DEC_MD5_VID(STRONGSWAN_4_0_2, "strongSwan 4.0.2") + DEC_MD5_VID(STRONGSWAN_4_0_3, "strongSwan 4.0.3") + DEC_MD5_VID(STRONGSWAN_4_0_4, "strongSwan 4.0.4") + DEC_MD5_VID(STRONGSWAN_4_0_5, "strongSwan 4.0.5") - DEC_MD5_VID(STRONGSWAN, "strongSwan 2.7.3") + DEC_MD5_VID(STRONGSWAN, "strongSwan 2.8.0") + DEC_MD5_VID(STRONGSWAN_2_7_3, "strongSwan 2.7.3") DEC_MD5_VID(STRONGSWAN_2_7_2, "strongSwan 2.7.2") DEC_MD5_VID(STRONGSWAN_2_7_1, "strongSwan 2.7.1") DEC_MD5_VID(STRONGSWAN_2_7_0, "strongSwan 2.7.0") diff --git a/programs/pluto/vendor.h b/programs/pluto/vendor.h index c4ed6d294..c7f70a480 100644 --- a/programs/pluto/vendor.h +++ b/programs/pluto/vendor.h @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: vendor.h,v 1.34 2006/07/06 12:32:41 as Exp $ + * RCSID $Id: vendor.h,v 1.36 2006/10/19 15:21:08 as Exp $ */ #ifndef _VENDOR_H_ @@ -79,10 +79,14 @@ enum known_vendorid { VID_STRONGSWAN_2_7_0 = 58, VID_STRONGSWAN_2_7_1 = 59, VID_STRONGSWAN_2_7_2 = 60, + VID_STRONGSWAN_2_7_3 = 61, VID_STRONGSWAN_4_0_0 = 70, VID_STRONGSWAN_4_0_1 = 71, VID_STRONGSWAN_4_0_2 = 72, + VID_STRONGSWAN_4_0_3 = 73, + VID_STRONGSWAN_4_0_4 = 74, + VID_STRONGSWAN_4_0_5 = 75, /* 101 - 200 : NAT-Traversal */ VID_NATT_STENBERG_01 =101, diff --git a/programs/pluto/whack.h b/programs/pluto/whack.h index 3086f1543..755918a2c 100644 --- a/programs/pluto/whack.h +++ b/programs/pluto/whack.h @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: whack.h,v 1.16 2006/04/17 10:39:14 as Exp $ + * RCSID $Id: whack.h,v 1.17 2006/10/19 15:18:43 as Exp $ */ #ifndef _WHACK_H @@ -62,6 +62,7 @@ struct whack_end { bool has_client_wildcard; bool has_port_wildcard; bool has_srcip; + bool has_natip; bool modecfg; bool hostaccess; certpolicy_t sendcert; diff --git a/programs/starter/args.c b/programs/starter/args.c index 6f3da63eb..2b2853a20 100644 --- a/programs/starter/args.c +++ b/programs/starter/args.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: args.c,v 1.9 2006/04/17 10:32:36 as Exp $ + * RCSID $Id: args.c,v 1.10 2006/10/19 14:58:30 as Exp $ */ #include <stddef.h> @@ -191,6 +191,7 @@ static const token_info_t token_info[] = { ARG_TIME, offsetof(starter_conn_t, dpd_delay), NULL }, { ARG_TIME, offsetof(starter_conn_t, dpd_timeout), NULL }, { ARG_ENUM, offsetof(starter_conn_t, dpd_action), LST_dpd_action }, + { ARG_MISC, 0, NULL /* KW_MODECONFIG */ }, /* ca section keywords */ { ARG_STR, offsetof(starter_ca_t, name), NULL }, @@ -209,6 +210,7 @@ static const token_info_t token_info[] = { ARG_MISC, 0, NULL /* KW_SUBNETWITHIN */ }, { ARG_MISC, 0, NULL /* KW_PROTOPORT */ }, { ARG_MISC, 0, NULL /* KW_SOURCEIP */ }, + { ARG_MISC, 0, NULL /* KW_NATIP */ }, { ARG_ENUM, offsetof(starter_end_t, firewall), LST_bool }, { ARG_ENUM, offsetof(starter_end_t, hostaccess), LST_bool }, { ARG_STR, offsetof(starter_end_t, updown), NULL }, diff --git a/programs/starter/confread.c b/programs/starter/confread.c index af0f00877..edd041ab4 100644 --- a/programs/starter/confread.c +++ b/programs/starter/confread.c @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: confread.c,v 1.38 2006/06/20 21:52:53 as Exp $ + * RCSID $Id: confread.c,v 1.39 2006/10/19 14:58:30 as Exp $ */ #include <stddef.h> @@ -255,6 +255,11 @@ kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token end->has_port_wildcard = has_port_wildcard; break; case KW_SOURCEIP: + if (end->has_natip) + { + plog("# natip and sourceip cannot be defined at the same time"); + goto err; + } if (streq(value, "%modeconfig") || streq(value, "%modecfg")) { end->modecfg = TRUE; @@ -272,6 +277,22 @@ kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token } conn->policy |= POLICY_TUNNEL; break; + case KW_NATIP: + if (end->has_srcip) + { + plog("# natip and sourceip cannot be defined at the same time"); + goto err; + } + conn->tunnel_addr_family = ip_version(value); + ugh = ttoaddr(value, 0, conn->tunnel_addr_family, &end->srcip); + if (ugh != NULL) + { + plog("# bad addr: %s=%s [%s]", name, value, ugh); + goto err; + } + end->has_natip = TRUE; + conn->policy |= POLICY_TUNNEL; + break; default: break; } @@ -430,6 +451,9 @@ load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg) case KW_REKEY: KW_POLICY_FLAG("no", "yes", POLICY_DONT_REKEY) break; + case KW_MODECONFIG: + KW_POLICY_FLAG("push", "pull", POLICY_MODECFG_PUSH) + break; default: break; } diff --git a/programs/starter/confread.h b/programs/starter/confread.h index a3b1b7379..052f5d527 100644 --- a/programs/starter/confread.h +++ b/programs/starter/confread.h @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: confread.h,v 1.23 2006/04/17 10:32:36 as Exp $ + * RCSID $Id: confread.h,v 1.24 2006/10/19 15:01:05 as Exp $ */ #ifndef _IPSEC_CONFREAD_H_ @@ -49,15 +49,16 @@ struct starter_end { char *cert; char *ca; char *groups; - char *iface; + char *iface; ip_address addr; ip_address nexthop; ip_address srcip; - ip_subnet subnet; + ip_subnet subnet; bool has_client; bool has_client_wildcard; - bool has_port_wildcard; + bool has_port_wildcard; bool has_srcip; + bool has_natip; bool modecfg; certpolicy_t sendcert; bool firewall; diff --git a/programs/starter/keywords.c b/programs/starter/keywords.c index 4cc5c03e8..75be0a542 100644 --- a/programs/starter/keywords.c +++ b/programs/starter/keywords.c @@ -44,7 +44,7 @@ error "gperf generated tables don't work with this execution character set. Plea * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: keywords.c,v 1.7 2006/04/17 10:32:48 as Exp $ + * RCSID $Id: keywords.c,v 1.8 2006/10/19 14:58:30 as Exp $ */ #include <string.h> @@ -56,12 +56,12 @@ struct kw_entry { kw_token_t token; }; -#define TOTAL_KEYWORDS 77 +#define TOTAL_KEYWORDS 80 #define MIN_WORD_LENGTH 3 #define MAX_WORD_LENGTH 17 #define MIN_HASH_VALUE 9 -#define MAX_HASH_VALUE 146 -/* maximum key range = 138, duplicates = 0 */ +#define MAX_HASH_VALUE 156 +/* maximum key range = 148, duplicates = 0 */ #ifdef __GNUC__ __inline @@ -77,32 +77,32 @@ hash (str, len) { static const unsigned char asso_values[] = { - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 15, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 85, 147, 40, - 25, 25, 0, 10, 5, 80, 147, 35, 60, 35, - 60, 55, 10, 147, 15, 20, 5, 65, 147, 147, - 147, 35, 0, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147, 147, 147, 147, 147, - 147, 147, 147, 147, 147, 147 + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 25, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 90, 157, 60, + 50, 25, 0, 10, 5, 65, 157, 65, 70, 5, + 0, 75, 35, 157, 10, 20, 5, 70, 157, 157, + 157, 55, 0, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157 }; return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[len - 1]]; } @@ -111,104 +111,113 @@ static const struct kw_entry wordlist[] = { {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {"left", KW_LEFT}, - {""}, {""}, {""}, + {"leftupdown", KW_LEFTUPDOWN}, + {""}, {""}, {"leftcert", KW_LEFTCERT,}, {"auth", KW_AUTH}, {"leftsubnet", KW_LEFTSUBNET}, - {""}, + {"leftsubnetwithin", KW_LEFTSUBNETWITHIN}, {"leftsendcert", KW_LEFTSENDCERT}, {"leftprotoport", KW_LEFTPROTOPORT}, {""}, {"right", KW_RIGHT}, - {"leftnexthop", KW_LEFTNEXTHOP}, - {"leftsourceip", KW_LEFTSOURCEIP}, - {"esp", KW_ESP}, + {"rightupdown", KW_RIGHTUPDOWN}, + {"dumpdir", KW_DUMPDIR}, + {""}, {"rightcert", KW_RIGHTCERT}, {""}, {"rightsubnet", KW_RIGHTSUBNET}, - {""}, + {"rightsubnetwithin", KW_RIGHTSUBNETWITHIN}, {"rightsendcert", KW_RIGHTSENDCERT}, {"rightprotoport", KW_RIGHTPROTOPORT}, {"leftgroups", KW_LEFTGROUPS}, - {"leftid", KW_LEFTID}, - {"rightnexthop", KW_RIGHTNEXTHOP}, - {"rightsourceip", KW_RIGHTSOURCEIP}, + {""}, {""}, + {"compress", KW_COMPRESS}, {"lefthostaccess", KW_LEFTHOSTACCESS}, {"interfaces", KW_INTERFACES}, + {""}, {""}, {""}, {""}, {""}, + {"rightgroups", KW_RIGHTGROUPS}, + {""}, + {"pfs", KW_PFS}, + {"leftnatip", KW_LEFTNATIP}, + {"righthostaccess", KW_RIGHTHOSTACCESS}, + {"leftnexthop", KW_LEFTNEXTHOP}, + {"leftsourceip", KW_LEFTSOURCEIP}, {""}, {""}, + {"virtual_private", KW_VIRTUAL_PRIVATE}, + {""}, {""}, + {"ike", KW_IKE}, + {""}, + {"rightnatip", KW_RIGHTNATIP}, + {"leftid", KW_LEFTID}, + {"rightnexthop", KW_RIGHTNEXTHOP}, + {"rightsourceip", KW_RIGHTSOURCEIP}, + {"dpdaction", KW_DPDACTION}, + {"keep_alive", KW_KEEP_ALIVE}, + {"ikelifetime", KW_IKELIFETIME}, + {""}, {"pfsgroup", KW_PFSGROUP}, {"type", KW_TYPE}, {"dpdtimeout", KW_DPDTIMEOUT}, - {"rightgroups", KW_RIGHTGROUPS}, - {"rightid", KW_RIGHTID}, - {"pfs", KW_PFS}, - {"rekeyfuzz", KW_REKEYFUZZ}, - {"righthostaccess", KW_RIGHTHOSTACCESS}, {"authby", KW_AUTHBY}, - {""}, + {"rightid", KW_RIGHTID}, {"leftrsasigkey", KW_LEFTRSASIGKEY}, - {""}, {""}, + {""}, + {"modeconfig", KW_MODECONFIG}, {"cacert", KW_CACERT}, - {"hidetos", KW_HIDETOS}, - {"ike", KW_IKE}, {""}, - {"virtual_private", KW_VIRTUAL_PRIVATE}, + {"esp", KW_ESP}, + {"rekeyfuzz", KW_REKEYFUZZ}, {""}, - {"dumpdir", KW_DUMPDIR}, + {"rekeymargin", KW_REKEYMARGIN}, + {"hidetos", KW_HIDETOS}, {"packetdefault", KW_PACKETDEFAULT}, {"rightrsasigkey", KW_RIGHTRSASIGKEY}, - {"keep_alive", KW_KEEP_ALIVE}, - {"ikelifetime", KW_IKELIFETIME}, + {"strictcrlpolicy", KW_STRICTCRLPOLICY}, + {""}, + {"leftfirewall", KW_LEFTFIREWALL}, {""}, - {"compress", KW_COMPRESS}, {"auto", KW_AUTO}, - {"strictcrlpolicy", KW_STRICTCRLPOLICY}, + {"klipsdebug", KW_KLIPSDEBUG}, {"keyingtries", KW_KEYINGTRIES}, {"keylife", KW_KEYLIFE}, - {"dpddelay", KW_DPDDELAY}, + {"nat_traversal", KW_NAT_TRAVERSAL}, {"cachecrls", KW_CACHECRLS}, - {"leftupdown", KW_LEFTUPDOWN}, + {"plutodebug", KW_PLUTODEBUG}, {"keyexchange", KW_KEYEXCHANGE}, - {"leftfirewall", KW_LEFTFIREWALL}, - {"nocrsend", KW_NOCRSEND}, + {"ocspuri", KW_OCSPURI}, + {"rightfirewall", KW_RIGHTFIREWALL}, + {"uniqueids", KW_UNIQUEIDS}, {""}, - {"rekey", KW_REKEY}, - {"leftsubnetwithin", KW_LEFTSUBNETWITHIN}, + {"leftca", KW_LEFTCA}, {"pkcs11module", KW_PKCS11MODULE}, - {"nat_traversal", KW_NAT_TRAVERSAL}, + {""}, {"also", KW_ALSO}, {"pkcs11keepstate", KW_PKCS11KEEPSTATE}, - {"rightupdown", KW_RIGHTUPDOWN}, + {""}, {"crluri2", KW_CRLURI2}, - {"rightfirewall", KW_RIGHTFIREWALL}, - {"postpluto", KW_POSTPLUTO}, - {"plutodebug", KW_PLUTODEBUG}, - {"pkcs11proxy", KW_PKCS11PROXY}, - {"rightsubnetwithin", KW_RIGHTSUBNETWITHIN}, - {"prepluto", KW_PREPLUTO}, - {""}, {""}, - {"leftca", KW_LEFTCA}, - {""}, {""}, - {"dpdaction", KW_DPDACTION}, - {""}, {""}, {""}, {"ldaphost", KW_LDAPHOST}, + {"postpluto", KW_POSTPLUTO}, {""}, - {"klipsdebug", KW_KLIPSDEBUG}, {"overridemtu", KW_OVERRIDEMTU}, {"rightca", KW_RIGHTCA}, - {"fragicmp", KW_FRAGICMP}, - {""}, {""}, - {"rekeymargin", KW_REKEYMARGIN}, - {"ocspuri", KW_OCSPURI}, - {""}, - {"uniqueids", KW_UNIQUEIDS}, - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {"prepluto", KW_PREPLUTO}, + {""}, {""}, {""}, {""}, + {"dpddelay", KW_DPDDELAY}, + {""}, {""}, {""}, {""}, + {"nocrsend", KW_NOCRSEND}, + {""}, {""}, {""}, {""}, {"ldapbase", KW_LDAPBASE}, + {""}, + {"rekey", KW_REKEY}, + {"pkcs11proxy", KW_PKCS11PROXY}, + {""}, {""}, {""}, {""}, {""}, {""}, + {"fragicmp", KW_FRAGICMP}, + {""}, {""}, {""}, {""}, {""}, {""}, {""}, + {"crluri", KW_CRLURI}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, - {"crlcheckinterval", KW_CRLCHECKINTERVAL}, - {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, - {"crluri", KW_CRLURI} + {""}, {""}, {""}, {""}, {""}, + {"crlcheckinterval", KW_CRLCHECKINTERVAL} }; #ifdef __GNUC__ diff --git a/programs/starter/keywords.h b/programs/starter/keywords.h index 6542ae1be..be3aabf3b 100644 --- a/programs/starter/keywords.h +++ b/programs/starter/keywords.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: keywords.h,v 1.8 2006/04/17 10:30:27 as Exp $ + * RCSID $Id: keywords.h,v 1.9 2006/10/19 14:57:56 as Exp $ */ #ifndef _KEYWORDS_H_ @@ -76,9 +76,10 @@ typedef enum { KW_DPDDELAY, KW_DPDTIMEOUT, KW_DPDACTION, + KW_MODECONFIG, #define KW_CONN_FIRST KW_CONN_SETUP -#define KW_CONN_LAST KW_DPDACTION +#define KW_CONN_LAST KW_MODECONFIG /* ca section keywords */ KW_CA_NAME, @@ -100,6 +101,7 @@ typedef enum { KW_SUBNETWITHIN, KW_PROTOPORT, KW_SOURCEIP, + KW_NATIP, KW_FIREWALL, KW_HOSTACCESS, KW_UPDOWN, @@ -121,6 +123,7 @@ typedef enum { KW_LEFTSUBNETWITHIN, KW_LEFTPROTOPORT, KW_LEFTSOURCEIP, + KW_LEFTNATIP, KW_LEFTFIREWALL, KW_LEFTHOSTACCESS, KW_LEFTUPDOWN, @@ -141,6 +144,7 @@ typedef enum { KW_RIGHTSUBNETWITHIN, KW_RIGHTPROTOPORT, KW_RIGHTSOURCEIP, + KW_RIGHTNATIP, KW_RIGHTFIREWALL, KW_RIGHTHOSTACCESS, KW_RIGHTUPDOWN, diff --git a/programs/starter/keywords.txt b/programs/starter/keywords.txt index dcfdafc98..fc9e49e47 100644 --- a/programs/starter/keywords.txt +++ b/programs/starter/keywords.txt @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: keywords.txt,v 1.6 2006/04/17 10:30:27 as Exp $ + * RCSID $Id: keywords.txt,v 1.7 2006/10/19 14:57:56 as Exp $ */ #include <string.h> @@ -65,6 +65,7 @@ pfsgroup, KW_PFSGROUP dpddelay, KW_DPDDELAY dpdtimeout, KW_DPDTIMEOUT dpdaction, KW_DPDACTION +modeconfig, KW_MODECONFIG cacert, KW_CACERT ldaphost, KW_LDAPHOST ldapbase, KW_LDAPBASE @@ -77,6 +78,7 @@ leftsubnet, KW_LEFTSUBNET leftsubnetwithin, KW_LEFTSUBNETWITHIN leftprotoport, KW_LEFTPROTOPORT leftsourceip, KW_LEFTSOURCEIP +leftnatip, KW_LEFTNATIP leftfirewall, KW_LEFTFIREWALL lefthostaccess, KW_LEFTHOSTACCESS leftupdown, KW_LEFTUPDOWN @@ -92,6 +94,7 @@ rightsubnet, KW_RIGHTSUBNET rightsubnetwithin, KW_RIGHTSUBNETWITHIN rightprotoport, KW_RIGHTPROTOPORT rightsourceip, KW_RIGHTSOURCEIP +rightnatip, KW_RIGHTNATIP rightfirewall, KW_RIGHTFIREWALL righthostaccess, KW_RIGHTHOSTACCESS rightupdown, KW_RIGHTUPDOWN diff --git a/programs/starter/starterwhack.c b/programs/starter/starterwhack.c index 0d7a3715e..b4bf2fb9d 100644 --- a/programs/starter/starterwhack.c +++ b/programs/starter/starterwhack.c @@ -11,7 +11,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * RCSID $Id: starterwhack.c,v 1.18 2006/06/20 21:52:53 as Exp $ + * RCSID $Id: starterwhack.c,v 1.19 2006/10/19 15:02:46 as Exp $ */ #include <sys/types.h> @@ -171,6 +171,7 @@ set_whack_end(whack_end_t *w, starter_end_t *end) w->has_client_wildcard = end->has_client_wildcard; w->has_port_wildcard = end->has_port_wildcard; w->has_srcip = end->has_srcip; + w->has_natip = end->has_natip; w->modecfg = end->modecfg; w->hostaccess = end->hostaccess; w->sendcert = end->sendcert; diff --git a/testing/INSTALL b/testing/INSTALL index 055219ae6..cfb9f1806 100644 --- a/testing/INSTALL +++ b/testing/INSTALL @@ -53,7 +53,7 @@ are required for the strongSwan testing environment: * A vanilla Linux kernel on which the UML kernel will be based on. We recommend the use of - http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.17.7.tar.bz2 + http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.18.1.tar.bz2 * Starting with Linux kernel 2.6.9 no patch must be applied any more in order to make the vanilla kernel UML-capable. For older kernels you'll find @@ -63,15 +63,15 @@ are required for the strongSwan testing environment: * The matching .config file required to compile the UML kernel: - http://download.strongswan.org/uml/.config-2.6.17 + http://download.strongswan.org/uml/.config-2.6.18 * A gentoo-based UML file system (compressed size 130 MBytes) found at - http://download.strongswan.org/uml/gentoo-fs-20060330.tar.bz2 + http://download.strongswan.org/uml/gentoo-fs-20061006.tar.bz2 * The latest strongSwan distribution - http://download.strongswan.org/strongswan-2.7.3.tar.gz + http://download.strongswan.org/strongswan-2.8.0.tar.gz 3. Creating the environment @@ -146,5 +146,5 @@ README document. ----------------------------------------------------------------------------- -This file is RCSID $Id: INSTALL,v 1.43 2006/08/03 10:20:54 as Exp $ +This file is RCSID $Id: INSTALL,v 1.43 2006/10/19 15:36:10 as Exp $ diff --git a/testing/do-tests b/testing/do-tests index ceddd72d8..6119d37d4 100755 --- a/testing/do-tests +++ b/testing/do-tests @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: do-tests,v 1.20 2006/02/08 21:27:59 as Exp $ +# RCSID $Id: do-tests,v 1.21 2006/10/19 21:12:43 as Exp $ DIR=`dirname $0` @@ -68,15 +68,16 @@ cp -rfp $DEFAULTTESTSDIR/* $TESTSDIR for host in $STRONGSWANHOSTS do - eval ip_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval ip_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" + case $host in moon) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" + eval ip1_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" searchandreplace PH_IP_MOON $ip_moon $TESTSDIR searchandreplace PH_IP1_MOON $ip1_moon $TESTSDIR ;; sun) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" + eval ip1_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" searchandreplace PH_IP_SUN $ip_sun $TESTSDIR searchandreplace PH_IP1_SUN $ip1_sun $TESTSDIR ;; @@ -90,12 +91,12 @@ do searchandreplace PH_IP_BOB $ip_bob $TESTSDIR ;; carol) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" + eval ip1_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" searchandreplace PH_IP_CAROL $ip_carol $TESTSDIR searchandreplace PH_IP1_CAROL $ip1_carol $TESTSDIR ;; dave) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" + eval ip1_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" searchandreplace PH_IP_DAVE $ip_dave $TESTSDIR searchandreplace PH_IP1_DAVE $ip1_dave $TESTSDIR ;; @@ -105,7 +106,6 @@ do esac done - ############################################################################## # create header for the results html file # diff --git a/testing/hosts/alice/etc/conf.d/net b/testing/hosts/alice/etc/conf.d/net index 3070a46b1..02494db97 100644 --- a/testing/hosts/alice/etc/conf.d/net +++ b/testing/hosts/alice/etc/conf.d/net @@ -2,10 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_ALICE broadcast 10.1.255.255 netmask 255.255.0.0" +config_eth0=( "PH_IP_ALICE broadcast 10.1.255.255 netmask 255.255.0.0" + "PH_IP6_ALICE/16" ) # For setting the default gateway # -gateway="eth0/PH_IP1_MOON" - +routes_eth0=( "default via PH_IP_MOON1" ) diff --git a/testing/hosts/alice/etc/init.d/net.eth0 b/testing/hosts/alice/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/alice/etc/init.d/net.eth0 +++ b/testing/hosts/alice/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/alice/etc/ipsec.conf b/testing/hosts/alice/etc/ipsec.conf index d6cdbba7b..4e525d929 100755 --- a/testing/hosts/alice/etc/ipsec.conf +++ b/testing/hosts/alice/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/alice/etc/runlevels/default/net.eth0 b/testing/hosts/alice/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/alice/etc/runlevels/default/net.eth0 +++ b/testing/hosts/alice/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/bob/etc/conf.d/net b/testing/hosts/bob/etc/conf.d/net index 09133acad..bd0b3a5ce 100644 --- a/testing/hosts/bob/etc/conf.d/net +++ b/testing/hosts/bob/etc/conf.d/net @@ -2,9 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_BOB broadcast 10.2.255.255 netmask 255.255.0.0" +config_eth0=( "PH_IP_BOB broadcast 10.2.255.255 netmask 255.255.0.0" + "PH_IP6_BOB/16" ) # For setting the default gateway # -gateway="eth0/PH_IP1_SUN" +routes_eth0=( "default via PH_IP_SUN1" ) diff --git a/testing/hosts/bob/etc/init.d/net.eth0 b/testing/hosts/bob/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/bob/etc/init.d/net.eth0 +++ b/testing/hosts/bob/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/bob/etc/ipsec.conf b/testing/hosts/bob/etc/ipsec.conf index cdef4e042..9040fc25d 100755 --- a/testing/hosts/bob/etc/ipsec.conf +++ b/testing/hosts/bob/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/bob/etc/runlevels/default/net.eth0 b/testing/hosts/bob/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/bob/etc/runlevels/default/net.eth0 +++ b/testing/hosts/bob/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/carol/etc/conf.d/net b/testing/hosts/carol/etc/conf.d/net index 39470ad14..f7f685942 100644 --- a/testing/hosts/carol/etc/conf.d/net +++ b/testing/hosts/carol/etc/conf.d/net @@ -2,9 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_CAROL broadcast 192.168.0.255 netmask 255.255.255.0" +config_eth0=( "PH_IP_CAROL broadcast 192.168.0.255 netmask 255.255.255.0" + "PH_IP6_CAROL/16" ) # For setting the default gateway # -gateway="eth0/192.168.0.254" +routes_eth0=( "default via 192.168.0.254" ) diff --git a/testing/hosts/carol/etc/init.d/net.eth0 b/testing/hosts/carol/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/carol/etc/init.d/net.eth0 +++ b/testing/hosts/carol/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/carol/etc/ipsec.conf b/testing/hosts/carol/etc/ipsec.conf index 3228f4e16..43deae00f 100755 --- a/testing/hosts/carol/etc/ipsec.conf +++ b/testing/hosts/carol/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/carol/etc/ipsec.d/private/carolKey.pem b/testing/hosts/carol/etc/ipsec.d/private/carolKey.pem index 0522355ce..5a41744f6 100644 --- a/testing/hosts/carol/etc/ipsec.d/private/carolKey.pem +++ b/testing/hosts/carol/etc/ipsec.d/private/carolKey.pem @@ -1,27 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAuBuEkgQI4IbI0njTrS6f/AG4noxCO2ErkIAQ+BdP+W9nKehL -GF+5bmB4MILFB6zp1k95vrAlLgXl8feomgsz5nifXetmUITLIwGRwWW8rRo0Vj4B -GzSbtqtRfwHs9+L03pYdNiA7hel4EcsmtlDP0BTRXdLStx79ZW5WOKJL9wmGuBKL -uuXztCjWNg4D0/ToFlAuPR0U1xZasakqn+Fe8EXU5I/1vXmOyAyUIJYsmpeYtUoO -0qAM8sllHX2YguGBwe9rHEPtytov0ZHhCZYtvSbzigAgjBrD7SelkkxgMwx5h461 -x6kJYKZHL5eaypxa7iu00K7TlbVGxeNhkQoGNwIDAQABAoIBAQCnhwq8H4XAYYWN -17quJPYZR6uqQfDmvYX5yD8osXXpgNC8Fo92z2wZnxje86+8S0DA7bLXrMs4NM/H -vVcjTTxd5LcHrHN+o0eBRCVQeXYVgfnL3EH/coCa2Qugaa0q589wV+Ke5Pek5AyJ -DHXegmyHaNoW6Qcq8L0dtigpAq3jTLG5w/3QCo8DMDKOGNR3AJrFpDlS/gW3JXuL -Tn+PUojoWO9W6joDGYDTi4eeYyfZSajzwZvzecEpez3vwKN0pupvHA6BJCR8Mzkq -5EYIH0hvWLtBy8uiWRXTfu/ggoQI9/Z5hBWIQk71m23uMIBbkm+oDn/1fLSBcWfK -cr9RyM+RAoGBANpWewy7xCbkRV/90bgBNkTZ9HeFsFsWOgFVyB1XwM2E/nOqdRJc -rxFt5Qt63DaXB+2REn231EIrsIz/Xd+R9KDl+yZOCY/m/mL4oquTPurEGJPzZdgW -X3kUOFW1pbfcMcmzSp1FPufI17JPiePtUb/q3C4RAjCKlnskHftEyK1pAoGBANfd -eEN8UzoV/R3WsGNDNOecMdIVAX9aiGnkLLraP7CRZ1heCH5y+RV1RB98yppkJG31 -fw5F8a6GoomkMHWGqhzDzQacZQV8w7C6rDY0Bk5TF5vemvJTGlrydwodfMvcJj8Y -KZrS7A0iS3GAAmM6VGr3THyscszfJTq0NwE3v4KfAoGAY7L1wVzENxYpb6nRZ/p1 -s37rGODdJNrDZfSrympVygMexeZiSx4zevv5iQJzKCJTJnIGRY35yLV2iwvY68wU -LpyV0Gn2B9Xs93idn0c/hahBqN2N9dxRgFJxXwHxSEGuInJScfo6vVCC3hNf3cpy -d/Zg0FBH9a5zBIv7fM9t63ECgYAosrSt5I68cNDcA1IWJOGgmS47cYJqxGLbtA1K -3UMMwx08592qGXsktIs3dIuuOBs2MAbYZg9+3Btg3/fS8KS576CEEpBpTHCIrWky -fvSBZ+EXngyQi2J4qyYOXijdNpBvbNrLOeEPSNv4di39D05DLITbLJgoUBnwy3Fj -ZWNR+QKBgEKn19f5QWs/O1DPWBXfvCSc01cuSUx20a6Z3B6Lxj+7MuXBjVxPkGge -kEaZnNzwgm+QQ1C51nIn9gY4LZx7OxMy0idEss76aRq4Lb7I6XakBrCQLQ9IiTkv -gNOeJYGsQv7tkIWWRlBIXjSBGwT6HzTEfN5cvdHSDzARGFGjwYfK +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,1E1991A43D0778B7 + +MAsd1YBlHz54KjvBvhpwDBewinBkxBo/NmdsMetLIcV8Ag87YcKtTXYju+fbW21y +DI12iPDQeS9tk17tS8qE5ubWmx/8n0fa5VCdLZ06JK6eeASXNoomXZh5rGsd42It +sj0irWAnbIA3nFFWQl+Uz5pGZMse7aDSNyk1zs3xtywFIaditYIBsRhrTVmJ/bCK +waVr++S2pwUHJ/phKoZQ8pwgF5KtYOZxdNtYIzfOZNMoplESR3+WYBYSuW8BKuOc +QAign/BL2JVJLD4OpHQ68D8Su2sbh6ZYA5jslZLDgG9O7eiMbkCE+N8DmKO6wNAr +zB5ILb4u5dIyTqun32tOENEhpZqDdMQtZZ34fRBze4IoMx9LrEOAHdZAQyyERP80 +iJCnH8BNf6FerA+XeDs4LVd1yrCklXKFINatqSRP/tNY3kruKw2Q7cAi2AFf+Rv6 +1lrvwK4MiLSHFtzcgEJuxm2bxeceIwXLJ2AVlfLBJvK/yJlq0MPedFbl6E6UwKfw +cMLokF3sa1XrfwpJ93enGLqdpJrkR3dTzrsshjIhjQqfc8lqLwRlbMGc9u+V0ZsK +OJ8e26wc/4l5D7CQ1vmgT/R/tuydBtUskgH96anhNJj1M95odkoh4Zicmm5iLgy2 +kluVYiEk0Fs7hc5Qtv8ZLN7ZoBRvZfJZWhXHDXmh71g1aoVYacIkFwiTMX4NoDy5 +QVq9tFUZ1TW4VrNIzfq++rLoz4XlgVy0Yz8jNWKuB0KRuHPNSsQUY2NHkDX+wOjq +MP1SfNDxqPoqrmCqbgMw/9DmeOj9gyiTyjZhPZTxFOp67FYEYzYtR6bLQKEhdgf6 +iOVROZyrFHMZdBiUgV8GECds1th6ZYWmNRGdvxYjSjExIYgkDrcWbowTqD0bFC9b +zClaSqrxR6GHUzbUVOBuCP+RmUx4j6gPvMRLUcIn5RmpbGtPE0ixeB5sFB0IuRRW +6u2YToCiuq3EG1iJRmxjnBa/zj1aBO6OlsE/aPc0Sx+Jhm+MUbDioxUAriX96bJ+ +DEB4zgDhC0vIvkkUVAzQMkWPX479nPDmiZLpMqUIfqUh75WDpHbCladyGMgSkEo0 +IKq96oAWHJC8WLH0UMxMNuf8Ut+TsSpIO6G0RPl/cx3+hQqSUC5oUB7R3ZAWYx+6 +mawjkNJEx72yeJmQtGiZYEfeMt0Svm10PypMXFu0+2JjiS2eRj2K1yqrUnuL6AnY +GYYmTmR74dnVAd35bRYJjY1XHGC9MyqBn4jLqKZm1BKO3sFsctGDy6vybnvAgPD7 +LioGQHPiOZmQe9Q5mMLedE9NAUCzlR8BHRbWtlnajQWcC0JcVu/mBQsjOt/KHh/V +CY4aFXE56lRH2OpqZQxFpBFOSFDcuVX+zcEBGmKfk65n2MFL4McAJUhVRZL561Zx +r9BvILv1Ld6/hECbodq0sUqvbDYHzv25zxAVKSIk1xy85mP5aNbk8xuGHmm860wg +YOqdePwBEcDHoio+ov/uFYB7+4gt40vV90EzSiyfdq8x9RFMViJU430IkIBcvByo +tFFcbN8ucBozxtl4AX495GVSRI7V0XXBtEdOIwJIzPBylZOHxCuTnA== -----END RSA PRIVATE KEY----- diff --git a/testing/hosts/carol/etc/ipsec.secrets b/testing/hosts/carol/etc/ipsec.secrets index 797f18a01..6a2aea811 100644 --- a/testing/hosts/carol/etc/ipsec.secrets +++ b/testing/hosts/carol/etc/ipsec.secrets @@ -1,7 +1,3 @@ # /etc/ipsec.secrets - strongSwan IPsec secrets file -: RSA carolKey.pem - - - - +: RSA carolKey.pem "nH5ZQEWtku0RJEZ6" diff --git a/testing/hosts/carol/etc/runlevels/default/net.eth0 b/testing/hosts/carol/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/carol/etc/runlevels/default/net.eth0 +++ b/testing/hosts/carol/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/dave/etc/conf.d/net b/testing/hosts/dave/etc/conf.d/net index db3753fb0..2b902525a 100644 --- a/testing/hosts/dave/etc/conf.d/net +++ b/testing/hosts/dave/etc/conf.d/net @@ -2,9 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_DAVE broadcast 192.168.0.255 netmask 255.255.255.0" +config_eth0=( "PH_IP_DAVE broadcast 192.168.0.255 netmask 255.255.255.0" + "PH_IP6_DAVE/16" ) # For setting the default gateway # -gateway="eth0/192.168.0.254" +routes_eth0=( "default via 192.168.0.254" ) diff --git a/testing/hosts/dave/etc/init.d/net.eth0 b/testing/hosts/dave/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/dave/etc/init.d/net.eth0 +++ b/testing/hosts/dave/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/dave/etc/ipsec.conf b/testing/hosts/dave/etc/ipsec.conf index 76623491c..5fc5eef46 100755 --- a/testing/hosts/dave/etc/ipsec.conf +++ b/testing/hosts/dave/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/dave/etc/runlevels/default/net.eth0 b/testing/hosts/dave/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/dave/etc/runlevels/default/net.eth0 +++ b/testing/hosts/dave/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/default/etc/hosts b/testing/hosts/default/etc/hosts index b8bc8da66..25c18ad5e 100644 --- a/testing/hosts/default/etc/hosts +++ b/testing/hosts/default/etc/hosts @@ -12,23 +12,56 @@ 10.1.0.254 uml1.strongswan.org uml1 10.2.0.254 uml1.strongswan.org uml2 -PH_IP_ALICE alice.strongswan.org alice -PH_IP_VENUS venus.strongswan.org venus -PH_IP1_MOON moon1.strongswan.org moon1 -PH_IP_MOON moon.strongswan.org moon -PH_IP_CAROL carol.strongswan.org carol -PH_IP1_CAROL carol1.strongswan.org carol1 -PH_IP_WINNETOU winnetou.strongswan.org winnetou crl.strongswan.org ocsp.strongswan.org ldap.strongswan.org -PH_IP_DAVE dave.strongswan.org dave -PH_IP1_DAVE dave1.strongswan.org dave1 -PH_IP_SUN sun.strongswan.org sun -PH_IP1_SUN sun1.strongswan.org sun1 -PH_IP_BOB bob.strongswan.org bob +10.1.0.10 alice.strongswan.org alice +10.1.0.20 venus.strongswan.org venus +10.1.0.1 moon1.strongswan.org moon1 +192.168.0.1 moon.strongswan.org moon +192.168.0.100 carol.strongswan.org carol +10.3.0.1 carol1.strongswan.org carol1 +192.168.0.150 winnetou.strongswan.org winnetou crl.strongswan.org ocsp.strongswan.org ldap.strongswan.org +192.168.0.200 dave.strongswan.org dave +10.3.0.2 dave1.strongswan.org dave1 +192.168.0.2 sun.strongswan.org sun +10.2.0.1 sun1.strongswan.org sun1 +10.2.0.10 bob.strongswan.org bob -# IPV6 versions of localhost and co +# IPv6 versions of localhost and co ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters ff02::3 ip6-allhosts + +# IPv6 solicited-node multicast addresses +ff02::1:ff00:1 ip6-mcast-1 +ff02::1:ff00:2 ip6-mcast-2 +ff02::1:ff00:10 ip6-mcast-10 +ff02::1:ff00:15 ip6-mcast-15 +ff02::1:ff00:20 ip6-mcast-20 + +# IPv6 site-local addresses +fec1::10 ip6-alice.strongswan.org ip6-alice +fec1::20 ip6-venus.strongswan.org ip6-venus +fec1::1 ip6-moon1.strongswan.org ip6-moon1 +fec0::1 ip6-moon.strongswan.org ip6-moon +fec0::10 ip6-carol.strongswan.org ip6-carol +fec3::1 ip6-carol1.strongswan.org ip6-carol1 +fec0::15 ip6-winnetou.strongswan.org ip6-winnetou +fec0::20 ip6-dave.strongswan.org ip6-dave +fec3::2 ip6-dave1.strongswan.org ip6-dave1 +fec0::2 ip6-sun.strongswan.org ip6-sun +fec2::1 ip6-sun1.strongswan.org ip6-sun1 +fec2::10 ip6-bob.strongswan.org ip6-bob + +# IPv6 link-local HW derived addresses +fe80::fcfd:0aff:fe01:14 ip6-hw-venus.strongswan.org ip6-hw-venus +fe80::fcfd:0aff:fe01:0a ip6-hw-alice.strongswan.org ip6-hw-alice +fe80::fcfd:0aff:fe01:01 ip6-hw-moon1.strongswan.org ip6-hw-moon1 +fe80::fcfd:c0ff:fea8:01 ip6-hw-moon.strongswan.org ip6-hw-moon +fe80::fcfd:c0ff:fea8:64 ip6-hw-carol.strongswan.org ip6-hw-carol +fe80::fcfd:c0ff:fea8:96 ip6-hw-winnetou.strongswan.org ip6-hw-winnetou +fe80::fcfd:c0ff:fea8:c8 ip6-hw-dave.strongswan.org ip6-hw-dave +fe80::fcfd:c0ff:fea8:02 ip6-hw-sun.strongswan.org ip6-hw-sun +fe80::fcfd:0aff:fe02:01 ip6-hw-sun1.strongswan.org ip6-hw-sun1 +fe80::fcfd:0aff:fe02:0a ip6-hw-bob.strongswan.org ip6-hw-bob diff --git a/testing/hosts/moon/etc/conf.d/net b/testing/hosts/moon/etc/conf.d/net index 7dec60ba5..7f09fd8a5 100644 --- a/testing/hosts/moon/etc/conf.d/net +++ b/testing/hosts/moon/etc/conf.d/net @@ -2,10 +2,11 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_MOON broadcast 192.168.0.255 netmask 255.255.255.0" -iface_eth1="PH_IP1_MOON broadcast 10.1.255.255 netmask 255.255.0.0" +config_eth0=( "PH_IP_MOON broadcast 192.168.0.255 netmask 255.255.255.0" + "PH_IP6_MOON/16" ) +config_eth1=( "PH_IP_MOON1 broadcast 10.1.255.255 netmask 255.255.0.0" + "PH_IP6_MOON1/16" ) # For setting the default gateway # -gateway="eth0/192.168.0.254" +routes_eth0=( "default via 192.168.0.254" ) diff --git a/testing/hosts/moon/etc/init.d/net.eth0 b/testing/hosts/moon/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/moon/etc/init.d/net.eth0 +++ b/testing/hosts/moon/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/moon/etc/init.d/net.eth1 b/testing/hosts/moon/etc/init.d/net.eth1 index fa1200242..92b3851cf 100755 --- a/testing/hosts/moon/etc/init.d/net.eth1 +++ b/testing/hosts/moon/etc/init.d/net.eth1 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/moon/etc/ipsec.conf b/testing/hosts/moon/etc/ipsec.conf index a0e97e057..c7d7dc2ed 100755 --- a/testing/hosts/moon/etc/ipsec.conf +++ b/testing/hosts/moon/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 @@ -12,7 +10,7 @@ conn %default keylife=20m rekeymargin=3m keyingtries=1 - left=192.168.0.1 + left=PH_IP_MOON leftnexthop=%direct leftcert=moonCert.pem leftid=@moon.strongswan.org @@ -20,13 +18,13 @@ conn %default conn net-net leftsubnet=10.1.0.0/16 - right=192.168.0.2 + right=PH_IP_SUN rightsubnet=10.2.0.0/16 rightid=@sun.strongswan.org auto=add conn host-host - right=192.168.0.2 + right=PH_IP_SUN rightid=@sun.strongswan.org auto=add diff --git a/testing/hosts/moon/etc/runlevels/default/net.eth0 b/testing/hosts/moon/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/moon/etc/runlevels/default/net.eth0 +++ b/testing/hosts/moon/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/moon/etc/runlevels/default/net.eth1 b/testing/hosts/moon/etc/runlevels/default/net.eth1 index fa1200242..92b3851cf 100755 --- a/testing/hosts/moon/etc/runlevels/default/net.eth1 +++ b/testing/hosts/moon/etc/runlevels/default/net.eth1 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/sun/etc/conf.d/net b/testing/hosts/sun/etc/conf.d/net index 0f8dc57b1..4a6370ab7 100644 --- a/testing/hosts/sun/etc/conf.d/net +++ b/testing/hosts/sun/etc/conf.d/net @@ -2,12 +2,13 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_SUN broadcast 192.168.0.255 netmask 255.255.255.0" -iface_eth1="PH_IP1_SUN broadcast 10.2.255.255 netmask 255.255.0.0" +config_eth0=( "PH_IP_SUN broadcast 192.168.0.255 netmask 255.255.255.0" + "PH_IP6_SUN/16" ) +config_eth1=( "PH_IP_SUN1 broadcast 10.2.255.255 netmask 255.255.0.0" + "PH_IP6_SUN1/16" ) # For setting the default gateway # -gateway="eth0/192.168.0.254" +routes_eth0=( "default via 192.168.0.254" ) diff --git a/testing/hosts/sun/etc/init.d/net.eth0 b/testing/hosts/sun/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/sun/etc/init.d/net.eth0 +++ b/testing/hosts/sun/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/sun/etc/init.d/net.eth1 b/testing/hosts/sun/etc/init.d/net.eth1 index fa1200242..92b3851cf 100755 --- a/testing/hosts/sun/etc/init.d/net.eth1 +++ b/testing/hosts/sun/etc/init.d/net.eth1 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/sun/etc/ipsec.conf b/testing/hosts/sun/etc/ipsec.conf index 4d0299a08..1106ded6f 100755 --- a/testing/hosts/sun/etc/ipsec.conf +++ b/testing/hosts/sun/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/sun/etc/runlevels/default/net.eth0 b/testing/hosts/sun/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/sun/etc/runlevels/default/net.eth0 +++ b/testing/hosts/sun/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/sun/etc/runlevels/default/net.eth1 b/testing/hosts/sun/etc/runlevels/default/net.eth1 index fa1200242..92b3851cf 100755 --- a/testing/hosts/sun/etc/runlevels/default/net.eth1 +++ b/testing/hosts/sun/etc/runlevels/default/net.eth1 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/venus/etc/conf.d/net b/testing/hosts/venus/etc/conf.d/net index 2c55c2c20..43ec97807 100644 --- a/testing/hosts/venus/etc/conf.d/net +++ b/testing/hosts/venus/etc/conf.d/net @@ -2,10 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_VENUS broadcast 10.1.255.255 netmask 255.255.0.0" +config_eth0=( "PH_IP_VENUS broadcast 10.1.255.255 netmask 255.255.0.0" + "PH_IP6_VENUS/16" ) # For setting the default gateway # -gateway="eth0/PH_IP1_MOON" - +routes_eth0=( "default via PH_IP_MOON1" ) diff --git a/testing/hosts/venus/etc/init.d/net.eth0 b/testing/hosts/venus/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/venus/etc/init.d/net.eth0 +++ b/testing/hosts/venus/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/venus/etc/ipsec.conf b/testing/hosts/venus/etc/ipsec.conf index 35f264f82..8e4e47459 100755 --- a/testing/hosts/venus/etc/ipsec.conf +++ b/testing/hosts/venus/etc/ipsec.conf @@ -1,7 +1,5 @@ # /etc/ipsec.conf - strongSwan IPsec configuration file -version 2.0 # conforms to second version of ipsec.conf specification - config setup plutodebug=control crlcheckinterval=180 diff --git a/testing/hosts/venus/etc/runlevels/default/net.eth0 b/testing/hosts/venus/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/venus/etc/runlevels/default/net.eth0 +++ b/testing/hosts/venus/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/winnetou/etc/conf.d/apache2 b/testing/hosts/winnetou/etc/conf.d/apache2 index cfb80a7d9..0c96fe77f 100644 --- a/testing/hosts/winnetou/etc/conf.d/apache2 +++ b/testing/hosts/winnetou/etc/conf.d/apache2 @@ -1,6 +1,6 @@ # Copyright 1999-2004 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/strongswan/testing/hosts/winnetou/etc/conf.d/apache2,v 1.2 2006/01/06 12:21:21 as Exp $ +# $Header: /root/strongswan/testing/hosts/winnetou/etc/conf.d/apache2,v 1.2 2006/01/06 12:21:21 as Exp $ # Config file for /etc/init.d/apache2 diff --git a/testing/hosts/winnetou/etc/conf.d/net b/testing/hosts/winnetou/etc/conf.d/net index 1a32153f3..7fbc37014 100644 --- a/testing/hosts/winnetou/etc/conf.d/net +++ b/testing/hosts/winnetou/etc/conf.d/net @@ -2,9 +2,9 @@ # This is basically the ifconfig argument without the ifconfig $iface # -iface_lo="127.0.0.1 netmask 255.0.0.0" -iface_eth0="PH_IP_WINNETOU broadcast 192.168.0.255 netmask 255.255.255.0" +config_eth0=( "PH_IP_WINNETOU broadcast 192.168.0.255 netmask 255.255.255.0" + "PH_IP6_WINNETOU/16" ) # For setting the default gateway # -gateway="eth0/192.168.0.254" +routes_eth0=( "default via 192.168.0.254" ) diff --git a/testing/hosts/winnetou/etc/init.d/net.eth0 b/testing/hosts/winnetou/etc/init.d/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/winnetou/etc/init.d/net.eth0 +++ b/testing/hosts/winnetou/etc/init.d/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/hosts/winnetou/etc/init.d/slapd b/testing/hosts/winnetou/etc/init.d/slapd index d4c070b33..6e3130e90 100755 --- a/testing/hosts/winnetou/etc/init.d/slapd +++ b/testing/hosts/winnetou/etc/init.d/slapd @@ -1,7 +1,7 @@ #!/sbin/runscript # Copyright 1999-2004 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/strongswan/testing/hosts/winnetou/etc/init.d/slapd,v 1.2 2005/05/31 14:04:43 as Exp $ +# $Header: /root/strongswan/testing/hosts/winnetou/etc/init.d/slapd,v 1.2 2005/05/31 14:04:43 as Exp $ depend() { need net diff --git a/testing/hosts/winnetou/etc/openldap/slapd.conf b/testing/hosts/winnetou/etc/openldap/slapd.conf index 4558ee2e2..5a99f955d 100644 --- a/testing/hosts/winnetou/etc/openldap/slapd.conf +++ b/testing/hosts/winnetou/etc/openldap/slapd.conf @@ -53,9 +53,9 @@ argsfile /var/run/openldap/slapd.args ####################################################################### database bdb -checkpoint 32 30 # <kbyte> <min> suffix "o=Linux strongSwan,c=CH" rootdn "cn=Manager,o=Linux strongSwan,c=CH" +checkpoint 32 30 # <kbyte> <min> # Cleartext passwords, especially for the rootdn, should # be avoid. See slappasswd(8) and slapd.conf(5) for details. # Use of strong authentication encouraged. diff --git a/testing/hosts/winnetou/etc/openssl/index.txt b/testing/hosts/winnetou/etc/openssl/index.txt index 4db6c2924..9e744674d 100644 --- a/testing/hosts/winnetou/etc/openssl/index.txt +++ b/testing/hosts/winnetou/etc/openssl/index.txt @@ -13,3 +13,6 @@ R 140321062536Z 050621195214Z 0C unknown /C=CH/O=Linux strongSwan/OU=Research/CN V 140321062916Z 0D unknown /C=CH/O=Linux strongSwan/OU=Sales/CN=Sales CA V 100607191714Z 0E unknown /C=CH/O=Linux strongSwan/CN=winnetou.strongswan.org V 100620195806Z 0F unknown /C=CH/O=Linux strongSwan/OU=Research/CN=Research CA +V 111007105811Z 10 unknown /C=CH/O=Linux strongSwan/OU=SHA-256/CN=moon.strongswan.org +V 111007121250Z 11 unknown /C=CH/O=Linux strongSwan/OU=SHA-384/CN=carol@strongswan.org +V 111007122112Z 12 unknown /C=CH/O=Linux strongSwan/OU=SHA-512/CN=dave@strongswan.org diff --git a/testing/hosts/winnetou/etc/openssl/index.txt.attr b/testing/hosts/winnetou/etc/openssl/index.txt.attr index 8f7e63a34..3a7e39e6e 100644 --- a/testing/hosts/winnetou/etc/openssl/index.txt.attr +++ b/testing/hosts/winnetou/etc/openssl/index.txt.attr @@ -1 +1 @@ -unique_subject = yes +unique_subject = no diff --git a/testing/hosts/winnetou/etc/openssl/index.txt.attr.old b/testing/hosts/winnetou/etc/openssl/index.txt.attr.old index 8f7e63a34..3a7e39e6e 100644 --- a/testing/hosts/winnetou/etc/openssl/index.txt.attr.old +++ b/testing/hosts/winnetou/etc/openssl/index.txt.attr.old @@ -1 +1 @@ -unique_subject = yes +unique_subject = no diff --git a/testing/hosts/winnetou/etc/openssl/index.txt.old b/testing/hosts/winnetou/etc/openssl/index.txt.old index 669702b0c..4d7201a35 100644 --- a/testing/hosts/winnetou/etc/openssl/index.txt.old +++ b/testing/hosts/winnetou/etc/openssl/index.txt.old @@ -12,3 +12,6 @@ V 100216084430Z 0B unknown /C=CH/O=Linux strongSwan/OU=Authorization Authority/ R 140321062536Z 050621195214Z 0C unknown /C=CH/O=Linux strongSwan/OU=Research/CN=Research CA V 140321062916Z 0D unknown /C=CH/O=Linux strongSwan/OU=Sales/CN=Sales CA V 100607191714Z 0E unknown /C=CH/O=Linux strongSwan/CN=winnetou.strongswan.org +V 100620195806Z 0F unknown /C=CH/O=Linux strongSwan/OU=Research/CN=Research CA +V 111007105811Z 10 unknown /C=CH/O=Linux strongSwan/OU=SHA-256/CN=moon.strongswan.org +V 111007121250Z 11 unknown /C=CH/O=Linux strongSwan/OU=SHA-384/CN=carol@strongswan.org diff --git a/testing/hosts/winnetou/etc/openssl/newcerts/10.pem b/testing/hosts/winnetou/etc/openssl/newcerts/10.pem new file mode 100644 index 000000000..307f4953e --- /dev/null +++ b/testing/hosts/winnetou/etc/openssl/newcerts/10.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHzCCAwegAwIBAgIBEDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ +MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS +b290IENBMB4XDTA2MTAwODEwNTgxMVoXDTExMTAwNzEwNTgxMVowWDELMAkGA1UE +BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xEDAOBgNVBAsTB1NIQS0y +NTYxHDAaBgNVBAMTE21vb24uc3Ryb25nc3dhbi5vcmcwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDzXHm8D8sY1lmX7o1KK0jt/M+UzAI2Ifpx7nAqoviH +XQIPe56BOAm4zHhEIlojEMFd1nncplXvDDGjuV/2F0KK1bFxbNtom88Ix1jrRWtk +FLopYwj3ERC2970OhNO3nuPLrnEAzj6k3XPGMTA3drGnpRf162f7mHAdmYIRXtWm +mfaecs4wGFs8BFGdeDfo6SPhQXZSBwZqjzQxvk1PA7E1qifgR5IGNZkNQRQ9IZD0 +86xzjmZgg5DaJcQKw45elpiVKQN6OkdWTngR3uUBfseWNeRGP5UxCUbDnPijWUbA +6ZAdEfFXLgSpSoXHLNttvGg+SWm0kgKTpHYWYhvpflKNAgMBAAGjggEFMIIBATAJ +BgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQU0gL3aEo/H8c/Ld/GkBTb +W9Ma+nUwbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJ +BgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJz +dHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYITbW9vbi5zdHJvbmdzd2Fu +Lm9yZzA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3Jn +L3N0cm9uZ3N3YW4uY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQCItzRn3TNWUzczBd8z +MtdPEsRl5Oi4fV3UecQxhjxAmJDLsEZT5I4uNa1XoLkJm6jVdSL7k+bjzjmpNJ1H +uL49cqia2yTdGP4IU0K8dTGaflg3ccaLLGGXTWU/NtgdI1o6yuZTwb6a9ZL7wWZT +x21BAsvyPTzCpUS1yCK4bFeYOxOYDphUGcwb0JTuRxx2/710b+p64BYiCfVkQJxT +eF1ZtjSW6nJgzMRg5n2zNpdrdXMMCPI6Nl7V6wxbs3Cphmz5qx3lijwi7nZt+jE5 +qK5gphph1MkKIhnA7MF66KEcx5Rknao68yLBBDIA/AISZ3bCIj8R1SGgl/tMYfep +sbRF +-----END CERTIFICATE----- diff --git a/testing/hosts/winnetou/etc/openssl/newcerts/11.pem b/testing/hosts/winnetou/etc/openssl/newcerts/11.pem new file mode 100644 index 000000000..d4b532323 --- /dev/null +++ b/testing/hosts/winnetou/etc/openssl/newcerts/11.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEITCCAwmgAwIBAgIBETANBgkqhkiG9w0BAQwFADBFMQswCQYDVQQGEwJDSDEZ +MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS +b290IENBMB4XDTA2MTAwODEyMTI1MFoXDTExMTAwNzEyMTI1MFowWTELMAkGA1UE +BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xEDAOBgNVBAsTB1NIQS0z +ODQxHTAbBgNVBAMUFGNhcm9sQHN0cm9uZ3N3YW4ub3JnMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAtCwjB6Yni4jSTbPJ4GX0kM06nr2tDBdU0PH6dZra +IXNaNiBthBNPNDeCYAQDG/ouwuywAJ6L2Lt0GYEhJSwfXMm87fYSG8qRP+C/nlKz +3fCfsuZ8yOAo5NAp2kgvbFVdB5cMeOtid21UqUvDxkncjFRDgpERtrjSthalUFYu +ObIcSMPdlcDho73jzq6zVK5XDJ4l1LHUQLbS4SzyrphCYKekTIoDy3YwRUys6Pdm +4QlFBIXuBwOYHjclvVu0HQVNSM4nWAJd+204KUm/+8neO0kn1Yakv9yoa47o3KGP +3XjtmcgY9SqBbuF+8yDcZQ7+5zUBjc0J+d8txdPoIjLi7wIDAQABo4IBBjCCAQIw +CQYDVR0TBAIwADALBgNVHQ8EBAMCA6gwHQYDVR0OBBYEFIUlEfDm3V0eDmRrpIvj +4FiPpGlpMG0GA1UdIwRmMGSAFF2n3XAGUTJ+57Zts7Xl4GDqLk3voUmkRzBFMQsw +CQYDVQQGEwJDSDEZMBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMS +c3Ryb25nU3dhbiBSb290IENBggEAMB8GA1UdEQQYMBaBFGNhcm9sQHN0cm9uZ3N3 +YW4ub3JnMDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwuc3Ryb25nc3dhbi5v +cmcvc3Ryb25nc3dhbi5jcmwwDQYJKoZIhvcNAQEMBQADggEBAL5ZmFmy8lW4Vdwq +hWB6qTtLLa1wwCvTXwbV9V+F8dK39AvHj6CHFqTiFhAbGIq/Ryt9cg2XGy1TDjVj +hQEua7mjp8XH2j2NLY2SiFTMjchbHmMylFk2FrHy2ZnmlRCiH83TAw+EnUWsQKj+ +gL+7Of9SpiaaIblrl+aCiBVktRuXcFSaxjYWTVXOeTCwnxQdF2SNtUKDoCuVPk1J +XCrs86mj575xL/FGjyN4SVbjTEZ4lm1emxrf/RblZOhCKp7mUic8KyP0kf7o6X8E +MXXjq9fDQVrSDG/q62uhZu7CyInnBpWnoUKiMImSxRn/cs0r7RUspC5DtJyhE33Y +DW2BzIc= +-----END CERTIFICATE----- diff --git a/testing/hosts/winnetou/etc/openssl/newcerts/12.pem b/testing/hosts/winnetou/etc/openssl/newcerts/12.pem new file mode 100644 index 000000000..73088cd1d --- /dev/null +++ b/testing/hosts/winnetou/etc/openssl/newcerts/12.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEHzCCAwegAwIBAgIBEjANBgkqhkiG9w0BAQ0FADBFMQswCQYDVQQGEwJDSDEZ +MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS +b290IENBMB4XDTA2MTAwODEyMjExMloXDTExMTAwNzEyMjExMlowWDELMAkGA1UE +BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xEDAOBgNVBAsTB1NIQS01 +MTIxHDAaBgNVBAMUE2RhdmVAc3Ryb25nc3dhbi5vcmcwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDL4+PsltDM0QCCS08tkefhll5Q0nb2VEdRZotBIdt6 +XEY1kmDlw0yQOp0XUznnIhcrxXpKeWpLqJdbo56jSxMaUB3Mod1u+aKvVhCgkOT8 +uQa7gIdcNMuXnfnch7yYYS6YxVfzdr/qXBxmVYNbR9sXy48vAD6glZLEVjDITHJO +a6tEVSrAOMyeuA9XTYJiGw5loj63YbUr6Ikp6W9SncPCtfX6G2Amk38MTuITu93W +Pd/bGB06ra6gmMQGAhXuGs14n3QZfQz9PWTp9TPsQNqQZdEjQyNdfeAKtPuz5jnO +cnZuhvVR0q4sxWuy64vkyZ57luTZAXyxdInBeBOp7sC3AgMBAAGjggEFMIIBATAJ +BgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQU0wvMMeoe59mocM/RiYnD +iw9NUm0wbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJ +BgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJz +dHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYETZGF2ZUBzdHJvbmdzd2Fu +Lm9yZzA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3Jn +L3N0cm9uZ3N3YW4uY3JsMA0GCSqGSIb3DQEBDQUAA4IBAQC/uKe2O9elbSFgpKP5 +7ZjJrCkYu493iH/PDm5G4D76q6WkRvZDqTgGDSIrXrt1xRLIsVJES+HERxfED0DB +yXNe22p1jR8iZdCesZxmEsKYyLh9XmeixKCfnLvStWCVs0+vqwhJlIkyEAveZ4HR +Yq121khdmCDDUugpjEl/nU7CLvCRVgFrlhDm1QLs2rYqxwQrJ2SH4/1W0YRdkY2R +vKZ2ngjLBNjBfXWNXSOpEAG367nVam5lFAepUC0wZTshyCUXt1NzClTnxWABm6M6 +x2Qwg4D6Qt5iXSjR8+DGVh+LaBL/alQi1YYcjkxufdFHnko294c0HsZcTZ3KRghk +ue1F +-----END CERTIFICATE----- diff --git a/testing/hosts/winnetou/etc/openssl/serial b/testing/hosts/winnetou/etc/openssl/serial index f599e28b8..b1bd38b62 100644 --- a/testing/hosts/winnetou/etc/openssl/serial +++ b/testing/hosts/winnetou/etc/openssl/serial @@ -1 +1 @@ -10 +13 diff --git a/testing/hosts/winnetou/etc/openssl/serial.old b/testing/hosts/winnetou/etc/openssl/serial.old index 0ced2f35e..48082f72f 100644 --- a/testing/hosts/winnetou/etc/openssl/serial.old +++ b/testing/hosts/winnetou/etc/openssl/serial.old @@ -1 +1 @@ -0F +12 diff --git a/testing/hosts/winnetou/etc/runlevels/default/net.eth0 b/testing/hosts/winnetou/etc/runlevels/default/net.eth0 index fa1200242..92b3851cf 100755 --- a/testing/hosts/winnetou/etc/runlevels/default/net.eth0 +++ b/testing/hosts/winnetou/etc/runlevels/default/net.eth0 @@ -1,314 +1,1124 @@ #!/sbin/runscript -# Copyright 1999-2004 Gentoo Technologies, Inc. +# Copyright (c) 2004-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -#NB: Config is in /etc/conf.d/net +# Contributed by Roy Marples (uberlord@gentoo.org) +# Many thanks to Aron Griffis (agriffis@gentoo.org) +# for help, ideas and patches -if [[ -n $NET_DEBUG ]]; then - set -x - devnull=/dev/stderr -else - devnull=/dev/null -fi +#NB: Config is in /etc/conf.d/net # For pcmcia users. note that pcmcia must be added to the same # runlevel as the net.* script that needs it. depend() { - use hotplug pcmcia -} + need localmount + after bootmisc hostname + use isapnp isdn pcmcia usb wlan -checkconfig() { - if [[ -z "${ifconfig_IFACE}" ]]; then - eerror "Please make sure that /etc/conf.d/net has \$ifconfig_$IFACE set" - eerror "(or \$iface_$IFACE for old-style configuration)" - return 1 + # Load any custom depend functions for the given interface + # For example, br0 may need eth0 and eth1 + local iface="${SVCNAME#*.}" + [[ $(type -t "depend_${iface}") == "function" ]] && depend_${iface} + + if [[ ${iface} != "lo" && ${iface} != "lo0" ]] ; then + after net.lo net.lo0 + + # Support new style RC_NEED and RC_USE in one net file + local x="RC_NEED_${iface}" + [[ -n ${!x} ]] && need ${!x} + x="RC_USE_${iface}" + [[ -n ${!x} ]] && use ${!x} fi - if [[ -n "${vlans_IFACE}" && ! -x /sbin/vconfig ]]; then - eerror "For VLAN (802.1q) support, emerge net-misc/vconfig" - return 1 + + return 0 +} + +# Define where our modules are +MODULES_DIR="${svclib}/net" + +# Make some wrappers to fudge after/before/need/use depend flags. +# These are callbacks so MODULE will be set. +after() { + eval "${MODULE}_after() { echo \"$*\"; }" +} +before() { + eval "${MODULE}_before() { echo \"$*\"; }" +} +need() { + eval "${MODULE}_need() { echo \"$*\"; }" +} +installed() { + # We deliberately misspell this as _installed will probably be used + # at some point + eval "${MODULE}_instlled() { echo \"$*\"; }" +} +provide() { + eval "${MODULE}_provide() { echo \"$*\"; }" +} +functions() { + eval "${MODULE}_functions() { echo \"$*\"; }" +} +variables() { + eval "${MODULE}_variables() { echo \"$*\"; }" +} + +is_loopback() { + [[ $1 == "lo" || $1 == "lo0" ]] +} + +# char* interface_device(char *iface) +# +# Gets the base device of the interface +# Can handle eth0:1 and eth0.1 +# Which returns eth0 in this case +interface_device() { + local dev="${1%%.*}" + [[ ${dev} == "$1" ]] && dev="${1%%:*}" + echo "${dev}" +} + +# char* interface_type(char* iface) +# +# Returns the base type of the interface +# eth, ippp, etc +interface_type() { + echo "${1%%[0-9]*}" +} + +# int calculate_metric(char *interface, int base) +# +# Calculates the best metric for the interface +# We use this when we add routes so we can prefer interfaces over each other +calculate_metric() { + local iface="$1" metric="$2" + + # Have we already got a metric? + local m=$(awk '$1=="'${iface}'" && $2=="00000000" { print $7 }' \ + /proc/net/route) + if [[ -n ${m} ]] ; then + echo "${m}" + return 0 fi + + local i= dest= gw= flags= ref= u= m= mtu= metrics= + while read i dest gw flags ref u m mtu ; do + # Ignore lo + is_loopback "${i}" && continue + # We work out metrics from default routes only + [[ ${dest} != "00000000" || ${gw} == "00000000" ]] && continue + metrics="${metrics}\n${m}" + done < /proc/net/route + + # Now, sort our metrics + metrics=$(echo -e "${metrics}" | sort -n) + + # Now, find the lowest we can use + local gotbase=false + for m in ${metrics} ; do + [[ ${m} -lt ${metric} ]] && continue + [[ ${m} == ${metric} ]] && ((metric++)) + [[ ${m} -gt ${metric} ]] && break + done + + echo "${metric}" } -# Fix bug 50039 (init.d/net.eth0 localization) -# Some other commands in this script might need to be wrapped, but -# we'll get them one-by-one. Note that LC_ALL trumps LC_anything_else -# according to locale(7) -ifconfig() { - LC_ALL=C /sbin/ifconfig "$@" +# int netmask2cidr(char *netmask) +# +# Returns the CIDR of a given netmask +netmask2cidr() { + local binary= i= bin= + + for i in ${1//./ }; do + bin="" + while [[ ${i} != "0" ]] ; do + bin=$[${i}%2]${bin} + (( i=i>>1 )) + done + binary="${binary}${bin}" + done + binary="${binary%%0*}" + echo "${#binary}" } -# setup_vars: setup variables based on $1 and content of /etc/conf.d/net -# The following variables are set, which should be declared local by -# the calling routine. -# status_IFACE (up or '') -# vlans_IFACE (space-separated list) -# ifconfig_IFACE (array of ifconfig lines, replaces iface_IFACE) -# dhcpcd_IFACE (command-line args for dhcpcd) -# routes_IFACE (array of route lines) -# inet6_IFACE (array of inet6 lines) -# ifconfig_fallback_IFACE (fallback ifconfig if dhcp fails) -setup_vars() { - local i iface="${1//\./_}" - - status_IFACE="$(ifconfig ${1} 2>${devnull} | gawk '$1 == "UP" {print "up"}')" - eval vlans_IFACE=\"\$\{iface_${iface}_vlans\}\" - eval ifconfig_IFACE=( \"\$\{ifconfig_$iface\[@\]\}\" ) - eval dhcpcd_IFACE=\"\$\{dhcpcd_$iface\}\" - eval routes_IFACE=( \"\$\{routes_$iface\[@\]\}\" ) - eval inet6_IFACE=( \"\$\{inet6_$iface\[@\]\}\" ) - eval ifconfig_fallback_IFACE=( \"\$\{ifconfig_fallback_$iface\[@\]\}\" ) - - # BACKWARD COMPATIBILITY: populate the ifconfig_IFACE array - # if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0) - eval local iface_IFACE=\"\$\{iface_$iface\}\" - if [[ -n ${iface_IFACE} && -z ${ifconfig_IFACE} ]]; then - # Make sure these get evaluated as arrays - local -a aliases broadcasts netmasks - - # Start with the primary interface - ifconfig_IFACE=( "${iface_IFACE}" ) - - # ..then add aliases - eval aliases=( \$\{alias_$iface\} ) - eval broadcasts=( \$\{broadcast_$iface\} ) - eval netmasks=( \$\{netmask_$iface\} ) - for ((i = 0; i < ${#aliases[@]}; i = i + 1)); do - ifconfig_IFACE[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}" + +# bool is_function(char* name) +# +# Returns 0 if the given name is a shell function, otherwise 1 +is_function() { + [[ -z $1 ]] && return 1 + [[ $(type -t "$1") == "function" ]] +} + +# void function_wrap(char* source, char* target) +# +# wraps function calls - for example function_wrap(this, that) +# maps function names this_* to that_* +function_wrap() { + local i= + + is_function "${2}_depend" && return + + for i in $(typeset -f | grep -o '^'"${1}"'_[^ ]*'); do + eval "${2}${i#${1}}() { ${i} \"\$@\"; }" + done +} + +# char[] * expand_parameters(char *cmd) +# +# Returns an array after expanding parameters. For example +# "192.168.{1..3}.{1..3}/24 brd +" +# will return +# "192.168.1.1/24 brd +" +# "192.168.1.2/24 brd +" +# "192.168.1.3/24 brd +" +# "192.168.2.1/24 brd +" +# "192.168.2.2/24 brd +" +# "192.168.2.3/24 brd +" +# "192.168.3.1/24 brd +" +# "192.168.3.2/24 brd +" +# "192.168.3.3/24 brd +" +expand_parameters() { + local x=$(eval echo ${@// /_}) + local -a a=( ${x} ) + + a=( "${a[@]/#/\"}" ) + a=( "${a[@]/%/\"}" ) + echo "${a[*]//_/ }" +} + +# void configure_variables(char *interface, char *option1, [char *option2]) +# +# Maps configuration options from <variable>_<option> to <variable>_<iface> +# option2 takes precedence over option1 +configure_variables() { + local iface="$1" option1="$2" option2="$3" + + local mod= func= x= i= + local -a ivars=() ovars1=() ovars2=() + local ifvar=$(bash_variable "${iface}") + + for mod in ${MODULES[@]}; do + is_function ${mod}_variables || continue + for v in $(${mod}_variables) ; do + x= + [[ -n ${option2} ]] && x="${v}_${option2}[@]" + [[ -z ${!x} ]] && x="${v}_${option1}[@]" + [[ -n ${!x} ]] && eval "${v}_${ifvar}=( \"\${!x}\" )" done + done + + return 0 +} +# bool module_load_minimum(char *module) +# +# Does the minimum checking on a module - even when forcing +module_load_minimum() { + local f="$1.sh" MODULE="${1##*/}" + + if [[ ! -f ${f} ]] ; then + eerror "${f} does not exist" + return 1 fi - # BACKWARD COMPATIBILITY: check for space-separated inet6 addresses - if [[ ${#inet6_IFACE[@]} == 1 && ${inet6_IFACE} == *' '* ]]; then - inet6_IFACE=( ${inet6_IFACE} ) + if ! source "${f}" ; then + eerror "${MODULE} failed a sanity check" + return 1 fi + + for f in depend; do + is_function "${MODULE}_${f}" && continue + eerror "${MODULE}.sh does not support the required function ${f}" + return 1 + done + + return 0 } -iface_start() { - local IFACE=${1} i x retval - checkconfig || return 1 - - if [[ ${ifconfig_IFACE} != dhcp ]]; then - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Bringing ${IFACE} up (${i})" - else - ebegin "Bringing ${IFACE} up" +# bool modules_load_auto() +# +# Load and check each module for sanity +# If the module is not installed, the functions are to be removed +modules_load_auto() { + local i j inst + + # Populate the MODULES array + # Basically we treat evey file in ${MODULES_DIR} as a module + MODULES=( $( cd "${MODULES_DIR}" ; ls *.sh ) ) + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES_DIR}/${MODULES[i]}" + [[ ! -f ${MODULES[i]} ]] && unset MODULES[i] + done + MODULES=( "${MODULES[@]}" ) + + # Each of these sources into the global namespace, so it's + # important that module functions and variables are prefixed with + # the module name, for example iproute2_ + + j="${#MODULES[@]}" + loaded_interface=false + for (( i=0; i<j; i++ )); do + MODULES[i]="${MODULES[i]%.sh*}" + if [[ ${MODULES[i]##*/} == "interface" ]] ; then + eerror "interface is a reserved name - cannot load a module called interface" + return 1 fi - # ifconfig does not always return failure .. - ifconfig ${IFACE} ${ifconfig_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? - else - # Check that eth0 was not brought up by the kernel ... - if [[ ${status_IFACE} == up ]]; then - einfo "Keeping kernel configuration for ${IFACE}" + + ( + u=0; + module_load_minimum "${MODULES[i]}" || u=1; + if [[ ${u} == 0 ]] ; then + inst="${MODULES[i]##*/}_check_installed"; + if is_function "${inst}" ; then + ${inst} false || u=1; + fi + fi + exit "${u}"; + ) + + if [[ $? == 0 ]] ; then + source "${MODULES[i]}.sh" + MODULES[i]="${MODULES[i]##*/}" else - ebegin "Bringing ${IFACE} up via DHCP" - /sbin/dhcpcd ${dhcpcd_IFACE} ${IFACE} - retval=$? - eend $retval - if [[ $retval == 0 ]]; then - # DHCP succeeded, show address retrieved - i=$(ifconfig ${IFACE} | grep -m1 -o 'inet addr:[^ ]*' | - cut -d: -f2) - [[ -n ${i} ]] && einfo " ${IFACE} received address ${i}" - elif [[ -n "${ifconfig_fallback_IFACE}" ]]; then - # DHCP failed, try fallback. - # Show the address, but catch if this interface will be inet6 only - i=${ifconfig_fallback_IFACE%% *} - if [[ ${i} == *.*.*.* ]]; then - ebegin "Using fallback configuration (${i}) for ${IFACE}" - else - ebegin "Using fallback configuration for ${IFACE}" + unset MODULES[i] + fi + done + + MODULES=( "${MODULES[@]}" ) + return 0 +} + +# bool modules_check_installed(void) +# +# Ensure that all modules have the required modules loaded +# This enables us to remove modules from the MODULES array +# Whilst other modules can still explicitly call them +# One example of this is essidnet which configures network +# settings for the specific ESSID connected to as the user +# may be using a daemon to configure wireless instead of our +# iwconfig module +modules_check_installed() { + local i j missingdeps nmods="${#MODULES[@]}" + + for (( i=0; i<nmods; i++ )); do + is_function "${MODULES[i]}_instlled" || continue + for j in $( ${MODULES[i]}_instlled ); do + missingdeps=true + if is_function "${j}_check_installed" ; then + ${j}_check_installed && missingdeps=false + elif is_function "${j}_depend" ; then + missingdeps=false + fi + ${missingdeps} && unset MODULES[i] && unset PROVIDES[i] && break + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) +} + +# bool modules_check_user(void) +modules_check_user() { + local iface="$1" ifvar=$(bash_variable "${IFACE}") + local i= j= k= l= nmods="${#MODULES[@]}" + local -a umods=() + + # Has the interface got any specific modules? + umods="modules_${ifvar}[@]" + umods=( "${!umods}" ) + + # Global setting follows interface-specific setting + umods=( "${umods[@]}" "${modules[@]}" ) + + # Add our preferred modules + local -a pmods=( "iproute2" "dhcpcd" "iwconfig" "netplugd" ) + umods=( "${umods[@]}" "${pmods[@]}" ) + + # First we strip any modules that conflict from user settings + # So if the user specifies pump then we don't use dhcpcd + for (( i=0; i<${#umods[@]}; i++ )); do + # Some users will inevitably put "dhcp" in their modules + # list. To keep users from screwing up their system this + # way, ignore this setting so that the default dhcp + # module will be used. + [[ ${umods[i]} == "dhcp" ]] && continue + + # We remove any modules we explicitly don't want + if [[ ${umods[i]} == "!"* ]] ; then + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${umods[i]:1} == "${MODULES[j]}" \ + || ${umods[i]:1} == "${PROVIDES[j]}" ]] ; then + # We may need to setup a class wrapper for it even though + # we don't use it directly + # However, we put it into an array and wrap later as + # another module may provide the same thing + ${MODULES[j]}_check_installed \ + && WRAP_MODULES=( + "${WRAP_MODULES[@]}" + "${MODULES[j]} ${PROVIDES[j]}" + ) + unset MODULES[j] + unset PROVIDES[j] fi - ifconfig ${IFACE} ${ifconfig_fallback_IFACE} >${devnull} && \ - ifconfig ${IFACE} up &>${devnull} - eend $? || return $? + done + continue + fi + + if ! is_function "${umods[i]}_depend" ; then + # If the module is one of our preferred modules, then + # ignore this error; whatever is available will be + # used instead. + (( i < ${#umods[@]} - ${#pmods[@]} )) || continue + + # The function may not exist because the modules software is + # not installed. Load the module and report its error + if [[ -e "${MODULES_DIR}/${umods[i]}.sh" ]] ; then + source "${MODULES_DIR}/${umods[i]}.sh" + is_function "${umods[i]}_check_installed" \ + && ${umods[i]}_check_installed true else - return $retval + eerror "The module \"${umods[i]}\" does not exist" fi + return 1 fi - fi - if [[ ${#ifconfig_IFACE[@]} -gt 1 ]]; then - einfo " Adding aliases" - for ((i = 1; i < ${#ifconfig_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE}:${i} (${ifconfig_IFACE[i]%% *})" - ifconfig ${IFACE}:${i} ${ifconfig_IFACE[i]} - eend $? + if is_function "${umods[i]}_provide" ; then + mod=$(${umods[i]}_provide) + else + mod="${umods[i]}" + fi + for (( j=0; j<nmods; j++ )); do + [[ -z ${MODULES[j]} ]] && continue + if [[ ${PROVIDES[j]} == "${mod}" && ${umods[i]} != "${MODULES[j]}" ]] ; then + # We don't have a match - now ensure that we still provide an + # alternative. This is to handle our preferred modules. + for (( l=0; l<nmods; l++ )); do + [[ ${l} == "${j}" || -z ${MODULES[l]} ]] && continue + if [[ ${PROVIDES[l]} == "${mod}" ]] ; then + unset MODULES[j] + unset PROVIDES[j] + break + fi + done + fi + done + done + + # Then we strip conflicting modules. + # We only need to do this for 3rd party modules that conflict with + # our own modules and the preferred list AND the user modules + # list doesn't specify a preference. + for (( i=0; i<nmods-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( j=i+1; j<nmods; j++)); do + [[ -z ${MODULES[j]} ]] && continue + [[ ${PROVIDES[i]} == "${PROVIDES[j]}" ]] \ + && unset MODULES[j] && unset PROVIDES[j] + done + done + + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + return 0 +} + +# void modules_sort(void) +# +# Sort our modules +modules_sort() { + local i= j= nmods=${#MODULES[@]} m= + local -a provide=() provide_list=() after=() dead=() sorted=() sortedp=() + + # Make our provide list + for ((i=0; i<nmods; i++)); do + dead[i]="false" + if [[ ${MODULES[i]} != "${PROVIDES[i]}" ]] ; then + local provided=false + for ((j=0; j<${#provide[@]}; j++)); do + if [[ ${provide[j]} == "${PROVIDES[i]}" ]] ; then + provide_list[j]="${provide_list[j]} ${MODULES[i]}" + provided=true + fi + done + if ! ${provided}; then + provide[j]="${PROVIDES[i]}" + provide_list[j]="${MODULES[i]}" + fi + fi + done + + # Create an after array, which holds which modules the module at + # index i must be after + for ((i=0; i<nmods; i++)); do + if is_function "${MODULES[i]}_after" ; then + after[i]=" ${after[i]} $(${MODULES[i]}_after) " + fi + if is_function "${MODULES[i]}_before" ; then + for m in $(${MODULES[i]}_before); do + for ((j=0; j<nmods; j++)) ; do + if [[ ${PROVIDES[j]} == "${m}" ]] ; then + after[j]=" ${after[j]} ${MODULES[i]} " + break + fi + done + done + fi + done + + # Replace the after list modules with real modules + for ((i=0; i<nmods; i++)); do + if [[ -n ${after[i]} ]] ; then + for ((j=0; j<${#provide[@]}; j++)); do + after[i]="${after[i]// ${provide[j]} / ${provide_list[j]} }" + done + fi + done + + # We then use the below code to provide a topologial sort + module_after_visit() { + local name="$1" i= x= + + for ((i=0; i<nmods; i++)); do + [[ ${MODULES[i]} == "$1" ]] && break done + + ${dead[i]} && return + dead[i]="true" + + for x in ${after[i]} ; do + module_after_visit "${x}" + done + + sorted=( "${sorted[@]}" "${MODULES[i]}" ) + sortedp=( "${sortedp[@]}" "${PROVIDES[i]}" ) + } + + for x in ${MODULES[@]}; do + module_after_visit "${x}" + done + + MODULES=( "${sorted[@]}" ) + PROVIDES=( "${sortedp[@]}" ) +} + +# bool modules_check_depends(bool showprovides) +modules_check_depends() { + local showprovides="${1:-false}" nmods="${#MODULES[@]}" i= j= needmod= + local missingdeps= p= interface=false + + for (( i=0; i<nmods; i++ )); do + if is_function "${MODULES[i]}_need" ; then + for needmod in $(${MODULES[i]}_need); do + missingdeps=true + for (( j=0; j<nmods; j++ )); do + if [[ ${needmod} == "${MODULES[j]}" \ + || ${needmod} == "${PROVIDES[j]}" ]] ; then + missingdeps=false + break + fi + done + if ${missingdeps} ; then + eerror "${MODULES[i]} needs ${needmod} (dependency failure)" + return 1 + fi + done + fi + + if is_function "${MODULES[i]}_functions" ; then + for f in $(${MODULES[i]}_functions); do + if ! is_function "${f}" ; then + eerror "${MODULES[i]}: missing required function \"${f}\"" + return 1 + fi + done + fi + + [[ ${PROVIDES[i]} == "interface" ]] && interface=true + + if ${showprovides} ; then + [[ ${PROVIDES[i]} != "${MODULES[i]}" ]] \ + && veinfo "${MODULES[i]} provides ${PROVIDES[i]}" + fi + done + + if ! ${interface} ; then + eerror "no interface module has been loaded" + return 1 fi - if [[ -n ${inet6_IFACE} ]]; then - einfo " Adding inet6 addresses" - for ((i = 0; i < ${#inet6_IFACE[@]}; i = i + 1)); do - ebegin " ${IFACE} inet6 add ${inet6_IFACE[i]}" - ifconfig ${IFACE} inet6 add ${inet6_IFACE[i]} >${devnull} - eend $? + return 0 +} + +# bool modules_load(char *iface, bool starting) +# +# Loads the defined handler and modules for the interface +# Returns 0 on success, otherwise 1 +modules_load() { + local iface="$1" starting="${2:-true}" MODULE= p=false i= j= k= + local -a x=() + local RC_INDENTATION="${RC_INDENTATION}" + local -a PROVIDES=() WRAP_MODULES=() + + if ! is_loopback "${iface}" ; then + x="modules_force_${iface}[@]" + [[ -n ${!x} ]] && modules_force=( "${!x}" ) + if [[ -n ${modules_force} ]] ; then + ewarn "WARNING: You are forcing modules!" + ewarn "Do not complain or file bugs if things start breaking" + report=true + fi + fi + + veinfo "Loading networking modules for ${iface}" + eindent + + if [[ -z ${modules_force} ]] ; then + modules_load_auto || return 1 + else + j="${#modules_force[@]}" + for (( i=0; i<j; i++ )); do + module_load_minimum "${MODULES_DIR}/${modules_force[i]}" || return 1 + if is_function "${modules_force[i]}_check_installed" ; then + ${modules_force[i]}_check_installed || unset modules_force[i] + fi done + MODULES=( "${modules_force[@]}" ) fi - # Set static routes - if [[ -n ${routes_IFACE} ]]; then - einfo " Adding routes" - for ((i = 0; i < ${#routes_IFACE[@]}; i = i + 1)); do - ebegin " ${routes_IFACE[i]}" - /sbin/route add ${routes_IFACE[i]} - eend $? + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + # Now load our dependencies - we need to use the MODULE variable + # here as the after/before/need functions use it + MODULE="${MODULES[i]}" + ${MODULE}_depend + + # expose does exactly the same thing as depend + # However it is more "correct" as it exposes things to other modules + # instead of depending on them ;) + is_function "${MODULES[i]}_expose" && ${MODULES[i]}_expose + + # If no provide is given, assume module name + if is_function "${MODULES[i]}_provide" ; then + PROVIDES[i]=$(${MODULES[i]}_provide) + else + PROVIDES[i]="${MODULES[i]}" + fi + done + + if [[ -n ${modules_force[@]} ]] ; then + # Strip any duplicate modules providing the same thing + j="${#MODULES[@]}" + for (( i=0; i<j-1; i++ )); do + [[ -z ${MODULES[i]} ]] && continue + for (( k=i+1; k<j; k++ )); do + if [[ ${PROVIDES[i]} == ${PROVIDES[k]} ]] ; then + unset MODULES[k] + unset PROVIDES[k] + fi + done done + MODULES=( "${MODULES[@]}" ) + PROVIDES=( "${PROVIDES[@]}" ) + else + if ${starting}; then + modules_check_user "${iface}" || return 1 + else + # Always prefer iproute2 for taking down interfaces + if is_function iproute2_provide ; then + function_wrap iproute2 "$(iproute2_provide)" + fi + fi fi + + # Wrap our modules + j="${#MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap "${MODULES[i]}" "${PROVIDES[i]}" + done + j="${#WRAP_MODULES[@]}" + for (( i=0; i<j; i++ )); do + function_wrap ${WRAP_MODULES[i]} + done + + if [[ -z ${modules_force[@]} ]] ; then + modules_check_installed || return 1 + modules_sort || return 1 + fi + + veinfo "modules: ${MODULES[@]}" + eindent + + ${starting} && p=true + modules_check_depends "${p}" || return 1 + return 0 +} + +# bool iface_start(char *interface) +# +# iface_start is called from start. It's expected to start the base +# interface (for example "eth0"), aliases (for example "eth0:1") and to start +# VLAN interfaces (for example eth0.0, eth0.1). VLAN setup is accomplished by +# calling itself recursively. +iface_start() { + local iface="$1" mod config_counter="-1" x config_worked=false + local RC_INDENTATION="${RC_INDENTATION}" + local -a config=() fallback=() fallback_route=() conf=() a=() b=() + local ifvar=$(bash_variable "$1") i= j= metric=0 - # Set default route if applicable to this interface - if [[ ${gateway} == ${IFACE}/* ]]; then - local ogw=$(/bin/netstat -rn | awk '$1 == "0.0.0.0" {print $2}') - local gw=${gateway#*/} - if [[ ${ogw} != ${gw} ]]; then - ebegin " Setting default gateway ($gw)" - - # First delete any existing route if it was setup by kernel... - /sbin/route del default dev ${IFACE} &>${devnull} - - # Second delete old gateway if it was set... - /sbin/route del default gw ${ogw} &>${devnull} - - # Third add our new default gateway - /sbin/route add default gw ${gw} >${devnull} - eend $? || { - true # need to have some command in here - # Note: This originally called stop, which is obviously - # wrong since it's calling with a local version of IFACE. - # The below code works correctly to abort configuration of - # the interface, but is commented because we're assuming - # that default route failure should not cause the interface - # to be unconfigured. - #local error=$? - #ewarn "Aborting configuration of ${IFACE}" - #iface_stop ${IFACE} - #return ${error} - } + # pre Start any modules with + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_start" ; then + ${mod}_pre_start "${iface}" || { eend 1; return 1; } fi + done + + x="metric_${ifvar}" + # If we don't have a metric then calculate one + # Our modules will set the metric variable to a suitable base + # in their pre starts. + if [[ -z ${!x} ]] ; then + eval "metric_${ifvar}=\"$(calculate_metric "${iface}" "${metric}")\"" fi - # Enabling rp_filter causes wacky packets to be auto-dropped by - # the kernel. Note that we only do this if it is not set via - # /etc/sysctl.conf ... - if [[ -e /proc/sys/net/ipv4/conf/${IFACE}/rp_filter && \ - -z "$(grep -s '^[^#]*rp_filter' /etc/sysctl.conf)" ]]; then - echo -n 1 > /proc/sys/net/ipv4/conf/${IFACE}/rp_filter + # We now expand the configuration parameters and pray that the + # fallbacks expand to the same number as config or there will be + # trouble! + a="config_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + config=( "${config[@]}" "${b[@]}" ) + done + + a="fallback_${ifvar}[@]" + a=( "${!a}" ) + for (( i=0; i<${#a[@]}; i++ )); do + eval b=( $(expand_parameters "${a[i]}") ) + fallback=( "${fallback[@]}" "${b[@]}" ) + done + + # We don't expand routes + fallback_route="fallback_route_${ifvar}[@]" + fallback_route=( "${!fallback_route}" ) + + # We must support old configs + if [[ -z ${config} ]] ; then + interface_get_old_config "${iface}" || return 1 + if [[ -n ${config} ]] ; then + ewarn "You are using a deprecated configuration syntax for ${iface}" + ewarn "You are advised to read /etc/conf.d/net.example and upgrade it accordingly" + fi + fi + + # Handle "noop" correctly + if [[ ${config[0]} == "noop" ]] ; then + if interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + return 0 + fi + + # Remove noop from the config var + config=( "${config[@]:1}" ) + fi + + # Provide a default of DHCP if no configuration is set and we're auto + # Otherwise a default of NULL + if [[ -z ${config} ]] ; then + ewarn "Configuration not set for ${iface} - assuming DHCP" + if is_function "dhcp_start" ; then + config=( "dhcp" ) + else + eerror "No DHCP client installed" + return 1 + fi fi + + einfo "Bringing up ${iface}" + eindent + for (( config_counter=0; config_counter<${#config[@]}; config_counter++ )); do + # Handle null and noop correctly + if [[ ${config[config_counter]} == "null" \ + || ${config[config_counter]} == "noop" ]] ; then + eend 0 + config_worked=true + continue + fi + + # We convert it to an array - this has the added + # bonus of trimming spaces! + conf=( ${config[config_counter]} ) + einfo "${conf[0]}" + + # Do we have a function for our config? + if is_function "${conf[0]}_start" ; then + eindent + ${conf[0]}_start "${iface}" ; x=$? + eoutdent + [[ ${x} == 0 ]] && config_worked=true && continue + # We need to test to see if it's an IP address or a function + # We do this by testing if the 1st character is a digit + elif [[ ${conf[0]:0:1} == [[:digit:]] || ${conf[0]} == *:* ]] ; then + x="0" + if ! is_loopback "${iface}" ; then + if [[ " ${MODULES[@]} " == *" arping "* ]] ; then + if arping_address_exists "${iface}" "${conf[0]}" ; then + eerror "${conf[0]%%/*} already taken on ${iface}" + x="1" + fi + fi + fi + [[ ${x} == "0" ]] && interface_add_address "${iface}" ${conf[@]}; x="$?" + eend "${x}" && config_worked=true && continue + else + if [[ ${conf[0]} == "dhcp" ]] ; then + eerror "No DHCP client installed" + else + eerror "No loaded modules provide \"${conf[0]}\" (${conf[0]}_start)" + fi + fi + + if [[ -n ${fallback[config_counter]} ]] ; then + einfo "Trying fallback configuration" + config[config_counter]="${fallback[config_counter]}" + fallback[config_counter]="" + + # Do we have a fallback route? + if [[ -n ${fallback_route[config_counter]} ]] ; then + x="fallback_route[config_counter]" + eval "routes_${ifvar}=( \"\${!x}\" )" + fallback_route[config_counter]="" + fi + + (( config_counter-- )) # since the loop will increment it + continue + fi + done + eoutdent + + # We return failure if no configuration parameters worked + ${config_worked} || return 1 + + # Start any modules with _post_start + for mod in ${MODULES[@]}; do + if is_function "${mod}_post_start" ; then + ${mod}_post_start "${iface}" || return 1 + fi + done + + return 0 } +# bool iface_stop(char *interface) +# # iface_stop: bring down an interface. Don't trust information in # /etc/conf.d/net since the configuration might have changed since # iface_start ran. Instead query for current configuration and bring # down the interface. iface_stop() { - local IFACE=${1} i x aliases inet6 count - - # Try to do a simple down (no aliases, no inet6, no dhcp) - aliases="$(ifconfig | grep -o "^$IFACE:[0-9]*" | tac)" - inet6="$(ifconfig ${IFACE} | awk '$1 == "inet6" {print $2}')" - if [[ -z ${aliases} && -z ${inet6} && ! -e /var/run/dhcpcd-${IFACE}.pid ]]; then - ebegin "Bringing ${IFACE} down" - ifconfig ${IFACE} down &>/dev/null - eend 0 - return 0 - fi + local iface="$1" i= aliases= need_begin=false mod= + local RC_INDENTATION="${RC_INDENTATION}" - einfo "Bringing ${IFACE} down" + # pre Stop any modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_pre_stop" ; then + ${mod}_pre_stop "${iface}" || return 1 + fi + done + + einfo "Bringing down ${iface}" + eindent + + # Collect list of aliases for this interface. + # List will be in reverse order. + if interface_exists "${iface}" ; then + aliases=$(interface_get_aliases_rev "${iface}") + fi # Stop aliases before primary interface. # Note this must be done in reverse order, since ifconfig eth0:1 # will remove eth0:2, etc. It might be sufficient to simply remove # the base interface but we're being safe here. - for i in ${aliases} ${IFACE}; do - - # Delete all the inet6 addresses for this interface - inet6="$(ifconfig ${i} | awk '$1 == "inet6" {print $3}')" - if [[ -n ${inet6} ]]; then - einfo " Removing inet6 addresses" - for x in ${inet6}; do - ebegin " ${IFACE} inet6 del ${x}" - ifconfig ${i} inet6 del ${x} - eend $? - done + for i in ${aliases} ${iface}; do + # Stop all our modules + for mod in ${MODULES[@]}; do + if is_function "${mod}_stop" ; then + ${mod}_stop "${i}" || return 1 + fi + done + + # A module may have removed the interface + if ! interface_exists "${iface}" ; then + eend 0 + continue fi - # Stop DHCP (should be N/A for aliases) - # Don't trust current configuration... investigate ourselves - if /sbin/dhcpcd -z ${i} &>${devnull}; then - ebegin " Releasing DHCP lease for ${IFACE}" - for ((count = 0; count < 9; count = count + 1)); do - /sbin/dhcpcd -z ${i} &>${devnull} || break - sleep 1 - done - [[ ${count} -lt 9 ]] - eend $? "Timed out" + # We don't delete ppp assigned addresses + if ! is_function pppd_exists || ! pppd_exists "${i}" ; then + # Delete all the addresses for this alias + interface_del_addresses "${i}" fi - ebegin " Stopping ${i}" - ifconfig ${i} down &>${devnull} - eend 0 + + # Do final shut down of this alias + if [[ ${IN_BACKGROUND} != "true" \ + && ${RC_DOWN_INTERFACE} == "yes" ]] ; then + ebegin "Shutting down ${i}" + interface_iface_stop "${i}" + eend "$?" + fi + done + + # post Stop any modules + for mod in ${MODULES[@]}; do + # We have already taken down the interface, so no need to error + is_function "${mod}_post_stop" && ${mod}_post_stop "${iface}" done return 0 } -start() { - # These variables are set by setup_vars - local status_IFACE vlans_IFACE dhcpcd_IFACE - local -a ifconfig_IFACE routes_IFACE inet6_IFACE +# bool run_start(char *iface) +# +# Brings up ${IFACE}. Calls preup, iface_start, then postup. +# Returns 0 (success) unless preup or iface_start returns 1 (failure). +# Ignores the return value from postup. +# We cannot check that the device exists ourselves as modules like +# tuntap make create it. +run_start() { + local iface="$1" IFVAR=$(bash_variable "$1") + + # We do this so users can specify additional addresses for lo if they + # need too - additional routes too + # However, no extra modules are loaded as they are just not needed + if [[ ${iface} == "lo" ]] ; then + metric_lo="0" + config_lo=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo=( "127.0.0.0/8" "${routes_lo[@]}" ) + elif [[ ${iface} == "lo0" ]] ; then + metric_lo0="0" + config_lo0=( "127.0.0.1/8 brd 127.255.255.255" "${config_lo[@]}" ) + routes_lo0=( "127.0.0.0/8" "${routes_lo[@]}" ) + fi + + # We may not have a loaded module for ${iface} + # Some users may have "alias natsemi eth0" in /etc/modules.d/foo + # so we can work with this + # However, if they do the same with eth1 and try to start it + # but eth0 has not been loaded then the module gets loaded as + # eth0. + # Not much we can do about this :( + # Also, we cannot error here as some modules - such as bridge + # create interfaces + if ! interface_exists "${iface}" ; then + /sbin/modprobe "${iface}" &>/dev/null + fi # Call user-defined preup function if it exists - if [[ $(type -t preup) == function ]]; then + if is_function preup ; then einfo "Running preup function" - preup ${IFACE} || { - eerror "preup ${IFACE} failed" - return 1 - } + eindent + ( preup "${iface}" ) + eend "$?" "preup ${iface} failed" || return 1 + eoutdent fi - # Start the primary interface and aliases - setup_vars ${IFACE} - iface_start ${IFACE} || return 1 + # If config is set to noop and the interface is up with an address + # then we don't start it + local config= + config="config_${IFVAR}[@]" + config=( "${!config}" ) + if [[ ${config[0]} == "noop" ]] && interface_is_up "${iface}" true ; then + einfo "Keeping current configuration for ${iface}" + eend 0 + else + # Remove noop from the config var + [[ ${config[0]} == "noop" ]] \ + && eval "config_${IFVAR}=( "\"\$\{config\[@\]:1\}\"" )" - # Start vlans - local vlan - for vlan in ${vlans_IFACE}; do - /sbin/vconfig add ${IFACE} ${vlan} >${devnull} - setup_vars ${IFACE}.${vlan} - iface_start ${IFACE}.${vlan} - done + # There may be existing ip address info - so we strip it + if [[ ${RC_INTERFACE_KEEP_CONFIG} != "yes" \ + && ${IN_BACKGROUND} != "true" ]] ; then + interface_del_addresses "${iface}" + fi + + # Start the interface + if ! iface_start "${iface}" ; then + if [[ ${IN_BACKGROUND} != "true" ]] ; then + interface_exists "${iface}" && interface_down "${iface}" + fi + eend 1 + return 1 + fi + fi # Call user-defined postup function if it exists - if [[ $(type -t postup) == function ]]; then + if is_function postup ; then + # We need to mark the service as started incase a + # postdown function wants to restart services that depend on us + mark_service_started "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postup function" - postup ${IFACE} + eindent + ( postup "${iface}" ) + eoutdent fi + + return 0 } -stop() { +# bool run_stop(char *iface) { +# +# Brings down ${iface}. If predown call returns non-zero, then +# stop returns non-zero to indicate failure bringing down device. +# In all other cases stop returns 0 to indicate success. +run_stop() { + local iface="$1" IFVAR=$(bash_variable "$1") x + + # Load our ESSID variable so users can use it in predown() instead + # of having to write code. + local ESSID=$(get_options ESSID) ESSIDVAR= + [[ -n ${ESSID} ]] && ESSIDVAR=$(bash_variable "${ESSID}") + # Call user-defined predown function if it exists - if [[ $(type -t predown) == function ]]; then + if is_function predown ; then einfo "Running predown function" - predown ${IFACE} + eindent + ( predown "${iface}" ) + eend $? "predown ${iface} failed" || return 1 + eoutdent + elif is_net_fs / ; then + eerror "root filesystem is network mounted -- can't stop ${iface}" + return 1 + elif is_union_fs / ; then + for x in $(unionctl "${dir}" --list \ + | sed -e 's/^\(.*\) .*/\1/') ; do + if is_net_fs "${x}" ; then + eerror "Part of the root filesystem is network mounted - cannot stop ${iface}" + return 1 + fi + done fi - # Don't depend on setup_vars since configuration might have changed. - # Investigate current configuration instead. - local vlan - for vlan in $(ifconfig | grep -o "^${IFACE}\.[^ ]*"); do - iface_stop ${vlan} - /sbin/vconfig rem ${vlan} >${devnull} - done + iface_stop "${iface}" || return 1 # always succeeds, btw - iface_stop ${IFACE} || return 1 # always succeeds, btw + # Release resolv.conf information. + [[ -x /sbin/resolvconf ]] && resolvconf -d "${iface}" + + # Mark us as inactive if called from the background + [[ ${IN_BACKGROUND} == "true" ]] && mark_service_inactive "net.${iface}" # Call user-defined postdown function if it exists - if [[ $(type -t postdown) == function ]]; then + if is_function postdown ; then + # We need to mark the service as stopped incase a + # postdown function wants to restart services that depend on us + [[ ${IN_BACKGROUND} != "true" ]] && mark_service_stopped "net.${iface}" + end_service "net.${iface}" 0 einfo "Running postdown function" - postdown ${IFACE} + eindent + ( postdown "${iface}" ) + eoutdent + fi + + + return 0 +} + +# bool run(char *iface, char *cmd) +# +# Main start/stop entry point +# We load modules here and remove any functions that they +# added as we may be called inside the same shell scope for another interface +run() { + local iface="$1" cmd="$2" r=1 RC_INDENTATION="${RC_INDENTATION}" + local starting=true + local -a MODULES=() mods=() + local IN_BACKGROUND="${IN_BACKGROUND}" + + if [[ ${IN_BACKGROUND} == "true" || ${IN_BACKGROUND} == "1" ]] ; then + IN_BACKGROUND=true + else + IN_BACKGROUND=false + fi + + # We need to override the exit function as runscript.sh now checks + # for it. We need it so we can mark the service as inactive ourselves. + unset -f exit + + eindent + [[ ${cmd} == "stop" ]] && starting=false + + # We force lo to only use these modules for a major speed boost + if is_loopback "${iface}" ; then + modules_force=( "iproute2" "ifconfig" "system" ) + fi + + if modules_load "${iface}" "${starting}" ; then + if [[ ${cmd} == "stop" ]] ; then + # Reverse the module list for stopping + mods=( "${MODULES[@]}" ) + for ((i = 0; i < ${#mods[@]}; i++)); do + MODULES[i]=${mods[((${#mods[@]} - i - 1))]} + done + + run_stop "${iface}" && r=0 + else + # Only hotplug on ethernet interfaces + if [[ ${IN_HOTPLUG} == 1 ]] ; then + if ! interface_is_ethernet "${iface}" ; then + eerror "We only hotplug for ethernet interfaces" + return 1 + fi + fi + + run_start "${iface}" && r=0 + fi + fi + + if [[ ${r} != "0" ]] ; then + if [[ ${cmd} == "start" ]] ; then + # Call user-defined failup if it exists + if is_function failup ; then + einfo "Running failup function" + eindent + ( failup "${iface}" ) + eoutdent + fi + else + # Call user-defined faildown if it exists + if is_function faildown ; then + einfo "Running faildown function" + eindent + ( faildown "${iface}" ) + eoutdent + fi + fi + [[ ${IN_BACKGROUND} == "true" ]] \ + && mark_service_inactive "net.${iface}" fi + + return "${r}" +} + +# bool start(void) +# +# Start entry point so that we only have one function +# which localises variables and unsets functions +start() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Starting ${IFACE}" + run "${IFACE}" start +} + +# bool stop(void) +# +# Stop entry point so that we only have one function +# which localises variables and unsets functions +stop() { + declare -r IFACE="${SVCNAME#*.}" + einfo "Stopping ${IFACE}" + run "${IFACE}" stop } # vim:ts=4 diff --git a/testing/scripts/build-hostconfig b/testing/scripts/build-hostconfig index 0df8861c8..28b321a70 100755 --- a/testing/scripts/build-hostconfig +++ b/testing/scripts/build-hostconfig @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: build-hostconfig,v 1.3 2005/02/08 10:40:48 as Exp $ +# RCSID $Id: build-hostconfig,v 1.4 2006/10/19 21:38:45 as Exp $ DIR=`dirname $0` @@ -58,44 +58,61 @@ HOSTIP=`ifconfig eth0 |grep inet |sed -e "s/.*inet addr://" -e "s/ Bcast.*//"` for host in $STRONGSWANHOSTS do cecho-n "${host}.." - eval ip_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval ipv4_${host}="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" + eval ipv6_${host}="`echo $HOSTNAMEIPV6 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" - [ "`eval echo \\\$ip_${host}`" != "$HOSTIP" ] || die "$host has the same IP as eth0 (Host)! Please change that." + [ "`eval echo \\\$ipv4_${host}`" != "$HOSTIP" ] || die "$host has the same IP as eth0 (Host)! Please change that." case $host in moon) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" - [ "`eval echo \\\$ip1_${host}`" != "$HOSTIP" ] || die "eth1 of $host has the same IP as eth0 (Host)! Please change that." - searchandreplace PH_IP_MOON $ip_moon $HOSTCONFIGDIR - searchandreplace PH_IP1_MOON $ip1_moon $HOSTCONFIGDIR + eval ipv4_moon1="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + [ "`eval echo \\\$ipv4_moon1`" != "$HOSTIP" ] || die "eth1 of $host has the same IP as eth0 (Host)! Please change that." + searchandreplace PH_IP_MOON1 $ipv4_moon1 $HOSTCONFIGDIR + searchandreplace PH_IP_MOON $ipv4_moon $HOSTCONFIGDIR + eval ipv6_moon1="`echo $HOSTNAMEIPV6 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP6_MOON1 $ipv6_moon1 $HOSTCONFIGDIR + searchandreplace PH_IP6_MOON $ipv6_moon $HOSTCONFIGDIR ;; sun) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" - [ "`eval echo \\\$ip1_${host}`" != "$HOSTIP" ] || die "eth1 of $host has the same IP as eth0 (Host)! Please change that." - searchandreplace PH_IP_SUN $ip_sun $HOSTCONFIGDIR - searchandreplace PH_IP1_SUN $ip1_sun $HOSTCONFIGDIR + eval ipv4_sun1="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + [ "`eval echo \\\$ipv4_sun1`" != "$HOSTIP" ] || die "eth1 of $host has the same IP as eth0 (Host)! Please change that." + searchandreplace PH_IP_SUN1 $ipv4_sun1 $HOSTCONFIGDIR + searchandreplace PH_IP_SUN $ipv4_sun $HOSTCONFIGDIR + eval ipv6_sun1="`echo $HOSTNAMEIPV6 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP6_SUN1 $ipv6_sun1 $HOSTCONFIGDIR + searchandreplace PH_IP6_SUN $ipv6_sun $HOSTCONFIGDIR ;; alice) - searchandreplace PH_IP_ALICE $ip_alice $HOSTCONFIGDIR + searchandreplace PH_IP_ALICE $ipv4_alice $HOSTCONFIGDIR + searchandreplace PH_IP6_ALICE $ipv6_alice $HOSTCONFIGDIR ;; venus) - searchandreplace PH_IP_VENUS $ip_venus $HOSTCONFIGDIR + searchandreplace PH_IP_VENUS $ipv4_venus $HOSTCONFIGDIR + searchandreplace PH_IP6_VENUS $ipv6_venus $HOSTCONFIGDIR ;; bob) - searchandreplace PH_IP_BOB $ip_bob $HOSTCONFIGDIR + searchandreplace PH_IP_BOB $ipv4_bob $HOSTCONFIGDIR + searchandreplace PH_IP6_BOB $ipv6_bob $HOSTCONFIGDIR ;; carol) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" - searchandreplace PH_IP_CAROL $ip_carol $HOSTCONFIGDIR - searchandreplace PH_IP1_CAROL $ip1_carol $HOSTCONFIGDIR + eval ipv4_carol1="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP_CAROL1 $ipv4_carol1 $HOSTCONFIGDIR + searchandreplace PH_IP_CAROL $ipv4_carol $HOSTCONFIGDIR + eval ipv6_carol1="`echo $HOSTNAMEIPV6 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP6_CAROL1 $ipv6_carol1 $HOSTCONFIGDIR + searchandreplace PH_IP6_CAROL $ipv6_carol $HOSTCONFIGDIR ;; dave) - eval ip1_${host}="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $2 }' | awk '{ print $1 }'`" - searchandreplace PH_IP_DAVE $ip_dave $HOSTCONFIGDIR - searchandreplace PH_IP1_DAVE $ip1_dave $HOSTCONFIGDIR + eval ipv4_dave1="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP_DAVE1 $ipv4_dave1 $HOSTCONFIGDIR + searchandreplace PH_IP_DAVE $ipv4_dave $HOSTCONFIGDIR + eval ipv6_dave1="`echo $HOSTNAMEIPV6 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $2 }' | awk '{ print $1 }'`" + searchandreplace PH_IP6_DAVE1 $ipv6_dave1 $HOSTCONFIGDIR + searchandreplace PH_IP6_DAVE $ipv6_dave $HOSTCONFIGDIR ;; winnetou) - searchandreplace PH_IP_WINNETOU $ip_winnetou $HOSTCONFIGDIR + searchandreplace PH_IP_WINNETOU $ipv4_winnetou $HOSTCONFIGDIR + searchandreplace PH_IP6_WINNETOU $ipv6_winnetou $HOSTCONFIGDIR ;; esac done diff --git a/testing/scripts/build-sshkeys b/testing/scripts/build-sshkeys index f4d584d6b..2faa3963d 100755 --- a/testing/scripts/build-sshkeys +++ b/testing/scripts/build-sshkeys @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: build-sshkeys,v 1.2 2005/02/15 14:12:16 as Exp $ +# RCSID $Id: build-sshkeys,v 1.3 2006/10/19 21:38:45 as Exp $ DIR=`dirname $0` @@ -58,10 +58,10 @@ else cecho "done" fi -for host in $HOSTNAMEIPS +for host in $HOSTNAMEIPV4 do - HOSTNAME=`echo $host | awk -F : '{ print $1 }'` - IP=`echo $host | awk -F : '{ print $2 }'` + HOSTNAME=`echo $host | awk -F, '{ print $1 }'` + IP=`echo $host | awk -F, '{ print $2 }'` if [ `grep "$IP " ~/.ssh/known_hosts | wc -l` != "0" ] then cecho "!! Warning: An entry exists for the following IP address: $IP" diff --git a/testing/scripts/build-umlrootfs b/testing/scripts/build-umlrootfs index ba103838f..1d534c81b 100755 --- a/testing/scripts/build-umlrootfs +++ b/testing/scripts/build-umlrootfs @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: build-umlrootfs,v 1.11 2006/01/08 22:29:56 as Exp $ +# RCSID $Id: build-umlrootfs,v 1.12 2006/10/20 14:26:05 as Exp $ DIR=`dirname $0` @@ -91,6 +91,12 @@ cecho "done" ###################################################### +# remove /etc/resolv.conf +# +cecho " * Removing /etc/resolv.conf" +rm -f $LOOPDIR/etc/resolv.conf + +###################################################### # copying default /etc/hosts to the root filesystem # cecho " * Copying '$HOSTCONFIGDIR/default/etc/hosts' to the root filesystem" @@ -153,7 +159,7 @@ cp $LOOPDIR/etc/ssh/ssh_host_rsa_key $LOOPDIR/root/.ssh/id_rsa for host in $STRONGSWANHOSTS do - eval ip="`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval ip="`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" echo "$host,$ip `cat $HOSTCONFIGDIR/ssh_host_rsa_key.pub`" >> $LOOPDIR/root/.ssh/known_hosts echo "`cat $HOSTCONFIGDIR/ssh_host_rsa_key.pub` root@$host" >> $LOOPDIR/root/.ssh/authorized_keys done diff --git a/testing/scripts/load-testconfig b/testing/scripts/load-testconfig index 89da17e72..9c0477e54 100755 --- a/testing/scripts/load-testconfig +++ b/testing/scripts/load-testconfig @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: load-testconfig,v 1.2 2004/12/13 21:02:42 as Exp $ +# RCSID $Id: load-testconfig,v 1.3 2006/10/19 21:38:45 as Exp $ DIR=`dirname $0` @@ -46,7 +46,7 @@ if [ -d $TESTSDIR/$testname/hosts ] then for host in `ls $TESTSDIR/$testname/hosts` do - eval HOSTLOGIN="root@`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval HOSTLOGIN="root@`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" scp -rp $TESTSDIR/$testname/hosts/$host/etc $HOSTLOGIN:/ > /dev/null 2>&1 done fi @@ -58,7 +58,7 @@ fi for host in $IPSECHOSTS do - eval HOSTLOGIN="root@`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval HOSTLOGIN="root@`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" ssh $HOSTLOGIN 'rm -f /var/log/auth.log; \ kill -SIGHUP `cat /var/run/syslogd.pid`' > /dev/null 2>&1 done diff --git a/testing/scripts/restore-defaults b/testing/scripts/restore-defaults index 129e46f56..03f723e82 100755 --- a/testing/scripts/restore-defaults +++ b/testing/scripts/restore-defaults @@ -14,7 +14,7 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: restore-defaults,v 1.2 2004/12/20 07:56:33 as Exp $ +# RCSID $Id: restore-defaults,v 1.3 2006/10/19 21:40:27 as Exp $ DIR=`dirname $0` @@ -47,7 +47,7 @@ if [ -d $TESTSDIR/${testname}/hosts ] then for host in `ls $TESTSDIR/${testname}/hosts` do - eval HOSTLOGIN="root@`echo $HOSTNAMEIPS | sed -n -e "s/^.*${host}://gp" | awk -F : '{ print $1 }' | awk '{ print $1 }'`" + eval HOSTLOGIN="root@`echo $HOSTNAMEIPV4 | sed -n -e "s/^.*${host},//gp" | awk -F, '{ print $1 }' | awk '{ print $1 }'`" scp -rp $HOSTCONFIGDIR/${host}/etc $HOSTLOGIN:/ > /dev/null 2>&1 done fi diff --git a/testing/testing.conf b/testing/testing.conf index 4f323d354..6a2db67fd 100755 --- a/testing/testing.conf +++ b/testing/testing.conf @@ -14,34 +14,34 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # -# RCSID $Id: testing.conf,v 1.56 2006/08/03 10:20:54 as Exp $ +# RCSID $Id: testing.conf,v 1.57 2006/10/19 21:38:44 as Exp $ # Root directory of testing UMLTESTDIR=~/strongswan-testing # Bzipped kernel sources # (file extension .tar.bz2 required) -KERNEL=$UMLTESTDIR/linux-2.6.17.7.tar.bz2 +KERNEL=$UMLTESTDIR/linux-2.6.18.1.tar.bz2 # Extract kernel version KERNELVERSION=`basename $KERNEL .tar.bz2 | sed -e 's/linux-//'` # Kernel configuration file -KERNELCONFIG=$UMLTESTDIR/.config-2.6.17 +KERNELCONFIG=$UMLTESTDIR/.config-2.6.18 # Bzipped uml patch for kernel # (not needed anymore for 2.6.9 kernel or higher) -UMLPATCH= +UMLPATCH=$UMLTESTDIR/uml_jmpbuf-2.6.18.patch.bz2 # Bzipped source of strongSwan -STRONGSWAN=$UMLTESTDIR/strongswan-2.7.3.tar.bz2 +STRONGSWAN=$UMLTESTDIR/strongswan-2.8.0.tar.bz2 # strongSwan compile options (use "yes" or "no") USE_LIBCURL="yes" USE_LDAP="yes" # Gentoo linux root filesystem -ROOTFS=$UMLTESTDIR/gentoo-fs-20060330.tar.bz2 +ROOTFS=$UMLTESTDIR/gentoo-fs-20061006.tar.bz2 # Size of the finished root filesystem in MB ROOTFSSIZE=544 @@ -98,20 +98,30 @@ SELECTEDTESTSONLY="no" SELECTEDTESTS="net2net-cert" ############################################################## -# hostname and according IP(s) -# You may change the IPs but keep them in the same net, +# hostname and corresponding IPv4 and IPv6 addresses +# You may change the IPs but keep them in the same subnet, # this means retain the netmasks! # Also don't use IPs ending with 254, they are reserved! # -HOSTNAMEIPS="\ -alice:10.1.0.10 \ -venus:10.1.0.20 \ -moon:192.168.0.1:10.1.0.1 \ -carol:192.168.0.100:10.3.0.1 \ -winnetou:192.168.0.150 \ -dave:192.168.0.200:10.3.0.2 \ -sun:192.168.0.2:10.2.0.1 \ -bob:10.2.0.10" +HOSTNAMEIPV4="\ +alice,10.1.0.10 \ +venus,10.1.0.20 \ +moon,192.168.0.1,10.1.0.1 \ +carol,192.168.0.100,10.3.0.1 \ +winnetou,192.168.0.150 \ +dave,192.168.0.200,10.3.0.2 \ +sun,192.168.0.2,10.2.0.1 \ +bob,10.2.0.10" + +HOSTNAMEIPV6="\ +alice,fec1::10 \ +venus,fec1::20 \ +moon,fec0::1,fec1::1 \ +carol,fec0::10,fec3::1 \ +winnetou,fec0::15 \ +dave,fec0::20,fec3::2 \ +sun,fec0::2,fec2::1 \ +bob,fec2::10" ############################################################## # VPN gateways / clients @@ -138,17 +148,13 @@ IFCONFIG_2="10.2.0.254 netmask 255.255.0.0" ############################################################## # Network interfaces of the UML instances # -SWITCH_alice="eth0=daemon,,unix,/tmp/umlswitch1" -SWITCH_venus="eth0=daemon,,unix,/tmp/umlswitch1" -SWITCH_moon="eth0=daemon,,unix,/tmp/umlswitch0 \ - eth1=daemon,,unix,/tmp/umlswitch1" -SWITCH_carol="eth0=daemon,,unix,/tmp/umlswitch0" -SWITCH_winnetou="eth0=daemon,,unix,/tmp/umlswitch0" -SWITCH_dave="eth0=daemon,,unix,/tmp/umlswitch0" -SWITCH_sun="eth0=daemon,,unix,/tmp/umlswitch0 \ - eth1=daemon,,unix,/tmp/umlswitch2" -SWITCH_bob="eth0=daemon,,unix,/tmp/umlswitch2" - - - - +SWITCH_alice="eth0=daemon,fe:fd:0a:01:00:0a,unix,/tmp/umlswitch1" +SWITCH_venus="eth0=daemon,fe:fd:0a:01:00:14,unix,/tmp/umlswitch1" +SWITCH_moon="eth0=daemon,fe:fd:c0:a8:00:01,unix,/tmp/umlswitch0 \ + eth1=daemon,fe:fd:0a:01:00:01,unix,/tmp/umlswitch1" +SWITCH_carol="eth0=daemon,fe:fd:c0:a8:00:64,unix,/tmp/umlswitch0" +SWITCH_winnetou="eth0=daemon,fe:fd:c0:a8:00:96,unix,/tmp/umlswitch0" +SWITCH_dave="eth0=daemon,fe:fd:c0:a8:00:c8,unix,/tmp/umlswitch0" +SWITCH_sun="eth0=daemon,fe:fd:c0:a8:00:02,unix,/tmp/umlswitch0 \ + eth1=daemon,fe:fd:0a:02:00:01,unix,/tmp/umlswitch2" +SWITCH_bob="eth0=daemon,fe:fd:0a:02:00:0a,unix,/tmp/umlswitch2" diff --git a/testing/tests/crl-ldap/pretest.dat b/testing/tests/crl-ldap/pretest.dat index 64fae2a16..0097e78f4 100644 --- a/testing/tests/crl-ldap/pretest.dat +++ b/testing/tests/crl-ldap/pretest.dat @@ -5,3 +5,4 @@ moon::ipsec start carol::ipsec start carol::sleep 2 carol::ipsec up home +carol::sleep 2 diff --git a/testing/tests/dpd-clear/evaltest.dat b/testing/tests/dpd-clear/evaltest.dat index 98d5b146b..da3567d3e 100644 --- a/testing/tests/dpd-clear/evaltest.dat +++ b/testing/tests/dpd-clear/evaltest.dat @@ -1,5 +1,6 @@ carol::ipsec status::STATE_MAIN_I4 (ISAKMP SA established)::YES carol::iptables -A INPUT -i eth0 -s PH_IP_MOON -j DROP::no output expected::NO +moon::ipsec statusall::DPD active::YES moon::sleep 50::no output expected::NO moon::cat /var/log/auth.log::inserting event EVENT_DPD::YES moon::cat /var/log/auth.log::DPD: No response from peer - declaring peer dead::YES diff --git a/testing/tests/mode-config-push/description.txt b/testing/tests/mode-config-push/description.txt new file mode 100644 index 000000000..387c3b409 --- /dev/null +++ b/testing/tests/mode-config-push/description.txt @@ -0,0 +1,10 @@ +The roadwarriors <b>carol</b> and <b>dave</b> set up a connection each to gateway <b>moon</b>. +Both <b>carol</b> and <b>dave</b> request a <b>virtual IP</b> via the IKE Mode Config protocol +by using the <b>leftsourceip=%modeconfig</b> parameter. By setting the option <b>modeconfig=push</b> +on both the roadwarriors and the gateway, the Mode Config server <b>moon</b> will actively push +the configuration down to <b>carol</b> and <b>dave</b>. +<p> +<b>leftfirewall=yes</b> automatically inserts iptables-based firewall rules that let pass the +tunneled traffic. In order to test the tunnels, <b>carol</b> and <b>dave</b> then ping the client +<b>alice</b> behind the gateway <b>moon</b>. The source IP addresses of the two pings will +be the virtual IPs <b>carol1</b> and <b>dave1</b>, respectively. diff --git a/testing/tests/mode-config-push/evaltest.dat b/testing/tests/mode-config-push/evaltest.dat new file mode 100644 index 000000000..7de32d681 --- /dev/null +++ b/testing/tests/mode-config-push/evaltest.dat @@ -0,0 +1,16 @@ +carol::cat /var/log/auth.log::setting virtual IP source address to 10.3.0.1::YES +carol::ipsec status::home.*STATE_QUICK_I2.*IPsec SA established::YES +carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_seq=1::YES +dave::cat /var/log/auth.log::setting virtual IP source address to 10.3.0.2::YES +dave::ipsec status::home.*STATE_QUICK_I2.*IPsec SA established::YES +dave::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_seq=1::YES +moon::ipsec status::rw-carol.*STATE_QUICK_R2.*IPsec SA established::YES +moon::ipsec status::rw-dave.*STATE_QUICK_R2.*IPsec SA established::YES +moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES +moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES +alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/mode-config-push/hosts/carol/etc/ipsec.conf b/testing/tests/mode-config-push/hosts/carol/etc/ipsec.conf new file mode 100755 index 000000000..d66c4d329 --- /dev/null +++ b/testing/tests/mode-config-push/hosts/carol/etc/ipsec.conf @@ -0,0 +1,31 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +version 2.0 # conforms to second version of ipsec.conf specification + +config setup + plutodebug=control + crlcheckinterval=180 + strictcrlpolicy=no + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + modeconfig=push + +conn home + left=PH_IP_CAROL + leftsourceip=%modeconfig + leftnexthop=%direct + leftcert=carolCert.pem + leftid=carol@strongswan.org + leftfirewall=yes + right=PH_IP_MOON + rightsubnet=10.1.0.0/16 + rightid=@moon.strongswan.org + auto=add + + + + diff --git a/testing/tests/mode-config-push/hosts/dave/etc/ipsec.conf b/testing/tests/mode-config-push/hosts/dave/etc/ipsec.conf new file mode 100755 index 000000000..bf2625148 --- /dev/null +++ b/testing/tests/mode-config-push/hosts/dave/etc/ipsec.conf @@ -0,0 +1,31 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +version 2.0 # conforms to second version of ipsec.conf specification + +config setup + plutodebug=control + crlcheckinterval=180 + strictcrlpolicy=no + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + modeconfig=push + +conn home + left=PH_IP_DAVE + leftsourceip=%modeconfig + leftnexthop=%direct + leftcert=daveCert.pem + leftid=dave@strongswan.org + leftfirewall=yes + right=PH_IP_MOON + rightsubnet=10.1.0.0/16 + rightid=@moon.strongswan.org + auto=add + + + + diff --git a/testing/tests/mode-config-push/hosts/moon/etc/ipsec.conf b/testing/tests/mode-config-push/hosts/moon/etc/ipsec.conf new file mode 100755 index 000000000..3416c5d68 --- /dev/null +++ b/testing/tests/mode-config-push/hosts/moon/etc/ipsec.conf @@ -0,0 +1,34 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +version 2.0 # conforms to second version of ipsec.conf specification + +config setup + plutodebug=control + crlcheckinterval=180 + strictcrlpolicy=no + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + modeconfig=push + left=PH_IP_MOON + leftsubnet=10.1.0.0/16 + leftsourceip=PH_IP1_MOON + leftnexthop=%direct + leftcert=moonCert.pem + leftid=@moon.strongswan.org + leftfirewall=yes + +conn rw-carol + right=%any + rightid=carol@strongswan.org + rightsourceip=PH_IP1_CAROL + auto=add + +conn rw-dave + right=%any + rightid=dave@strongswan.org + rightsourceip=PH_IP1_DAVE + auto=add diff --git a/testing/tests/mode-config-push/posttest.dat b/testing/tests/mode-config-push/posttest.dat new file mode 100644 index 000000000..932b319a7 --- /dev/null +++ b/testing/tests/mode-config-push/posttest.dat @@ -0,0 +1,11 @@ +moon::iptables -v -n -L +carol::iptables -v -n -L +dave::iptables -v -n -L +moon::ipsec stop +carol::ipsec stop +dave::ipsec stop +moon::/etc/init.d/iptables stop 2> /dev/null +carol::/etc/init.d/iptables stop 2> /dev/null +dave::/etc/init.d/iptables stop 2> /dev/null +carol::ip addr del PH_IP1_CAROL/32 dev eth0 +dave::ip addr del PH_IP1_DAVE/32 dev eth0 diff --git a/testing/tests/mode-config-push/pretest.dat b/testing/tests/mode-config-push/pretest.dat new file mode 100644 index 000000000..1e45f00fd --- /dev/null +++ b/testing/tests/mode-config-push/pretest.dat @@ -0,0 +1,9 @@ +moon::/etc/init.d/iptables start 2> /dev/null +carol::/etc/init.d/iptables start 2> /dev/null +dave::/etc/init.d/iptables start 2> /dev/null +carol::ipsec start +dave::ipsec start +moon::ipsec start +carol::sleep 2 +carol::ipsec up home +dave::ipsec up home diff --git a/testing/tests/mode-config-push/test.conf b/testing/tests/mode-config-push/test.conf new file mode 100644 index 000000000..1a8f2a4e0 --- /dev/null +++ b/testing/tests/mode-config-push/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# UML instances used for this test + +# All UML instances that are required for this test +# +UMLHOSTS="alice moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-m-c-w-d.png" + +# UML instances on which tcpdump is to be started +# +TCPDUMPHOSTS="moon alice" + +# UML instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon carol dave" diff --git a/testing/tests/mode-config-swapped/evaltest.dat b/testing/tests/mode-config-swapped/evaltest.dat index be8ca6ef5..7de32d681 100644 --- a/testing/tests/mode-config-swapped/evaltest.dat +++ b/testing/tests/mode-config-swapped/evaltest.dat @@ -10,7 +10,7 @@ moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES -alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: icmp::YES -alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: icmp::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES +alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/mode-config/evaltest.dat b/testing/tests/mode-config/evaltest.dat index be8ca6ef5..7de32d681 100644 --- a/testing/tests/mode-config/evaltest.dat +++ b/testing/tests/mode-config/evaltest.dat @@ -10,7 +10,7 @@ moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES -alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: icmp::YES -alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: icmp::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES +alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/multi-level-ca-ldap/hosts/carol/etc/ipsec.secrets b/testing/tests/multi-level-ca-ldap/hosts/carol/etc/ipsec.secrets new file mode 100644 index 000000000..fac55d63b --- /dev/null +++ b/testing/tests/multi-level-ca-ldap/hosts/carol/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA carolKey.pem diff --git a/testing/tests/multi-level-ca-loop/hosts/carol/etc/ipsec.secrets b/testing/tests/multi-level-ca-loop/hosts/carol/etc/ipsec.secrets new file mode 100644 index 000000000..fac55d63b --- /dev/null +++ b/testing/tests/multi-level-ca-loop/hosts/carol/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA carolKey.pem diff --git a/testing/tests/multi-level-ca-strict/hosts/carol/etc/ipsec.secrets b/testing/tests/multi-level-ca-strict/hosts/carol/etc/ipsec.secrets new file mode 100644 index 000000000..fac55d63b --- /dev/null +++ b/testing/tests/multi-level-ca-strict/hosts/carol/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA carolKey.pem diff --git a/testing/tests/multi-level-ca/hosts/carol/etc/ipsec.secrets b/testing/tests/multi-level-ca/hosts/carol/etc/ipsec.secrets new file mode 100644 index 000000000..fac55d63b --- /dev/null +++ b/testing/tests/multi-level-ca/hosts/carol/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA carolKey.pem diff --git a/testing/tests/starter-also-loop/pretest.dat b/testing/tests/starter-also-loop/pretest.dat index aa46124dc..b135b12c3 100644 --- a/testing/tests/starter-also-loop/pretest.dat +++ b/testing/tests/starter-also-loop/pretest.dat @@ -1 +1,2 @@ moon::ipsec start --debug-all +moon::sleep 1 diff --git a/testing/tests/starter-includes/evaltest.dat b/testing/tests/starter-includes/evaltest.dat index be8ca6ef5..7de32d681 100644 --- a/testing/tests/starter-includes/evaltest.dat +++ b/testing/tests/starter-includes/evaltest.dat @@ -10,7 +10,7 @@ moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES moon::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES -alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: icmp::YES -alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: icmp::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES +alice::tcpdump::IP dave1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > dave1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/virtual-ip-swapped/evaltest.dat b/testing/tests/virtual-ip-swapped/evaltest.dat index 5160a340f..bf3965727 100644 --- a/testing/tests/virtual-ip-swapped/evaltest.dat +++ b/testing/tests/virtual-ip-swapped/evaltest.dat @@ -5,5 +5,5 @@ carol::ping -c 1 PH_IP1_MOON::64 bytes from PH_IP1_MOON: icmp_seq=1::YES moon::ping -c 1 PH_IP1_CAROL::64 bytes from PH_IP1_CAROL: icmp_seq=1::YES moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES -alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: icmp::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/virtual-ip/evaltest.dat b/testing/tests/virtual-ip/evaltest.dat index 5160a340f..bf3965727 100644 --- a/testing/tests/virtual-ip/evaltest.dat +++ b/testing/tests/virtual-ip/evaltest.dat @@ -5,5 +5,5 @@ carol::ping -c 1 PH_IP1_MOON::64 bytes from PH_IP1_MOON: icmp_seq=1::YES moon::ping -c 1 PH_IP1_CAROL::64 bytes from PH_IP1_CAROL: icmp_seq=1::YES moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES -alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: icmp::YES -alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: icmp::YES +alice::tcpdump::IP carol1.strongswan.org > alice.strongswan.org: ICMP echo request::YES +alice::tcpdump::IP alice.strongswan.org > carol1.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/wlan/evaltest.dat b/testing/tests/wlan/evaltest.dat index ccf5d0c8a..1936c93a3 100644 --- a/testing/tests/wlan/evaltest.dat +++ b/testing/tests/wlan/evaltest.dat @@ -8,4 +8,4 @@ alice::ping -c 1 PH_IP_MOON::64 bytes from PH_IP_MOON: icmp_seq=1::YES alice::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_seq=1::YES venus::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_seq=1::YES moon::tcpdump::ESP::YES -sun::tcpdump::icmp::YES +sun::tcpdump::ICMP::YES |