Network Working Group H. Balakrishnan Request for Comments: 3124 MIT LCS Category: Standards Track S. Seshan CMU June 2001 The Congestion Manager Status of this Memo This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited. Copyright Notice Copyright (C) The Internet Society (2001). All Rights Reserved.
AbstractThis document describes the Congestion Manager (CM), an end-system module that: (i) Enables an ensemble of multiple concurrent streams from a sender destined to the same receiver and sharing the same congestion properties to perform proper congestion avoidance and control, and (ii) Allows applications to easily adapt to network congestion. RFC-2119 [Bradner97]. STREAM A group of packets that all share the same source and destination IP address, IP type-of-service, transport protocol, and source and destination transport-layer port numbers.
MACROFLOW A group of CM-enabled streams that all use the same congestion management and scheduling algorithms, and share congestion state information. Currently, streams destined to different receivers belong to different macroflows. Streams destined to the same receiver MAY belong to different macroflows. When the Congestion Manager is in use, streams that experience identical congestion behavior and use the same congestion control algorithm SHOULD belong to the same macroflow. APPLICATION Any software module that uses the CM. This includes user-level applications such as Web servers or audio/video servers, as well as in-kernel protocols such as TCP [Postel81] that use the CM for congestion control. WELL-BEHAVED APPLICATION An application that only transmits when allowed by the CM and accurately accounts for all data that it has sent to the receiver by informing the CM using the CM API. PATH MAXIMUM TRANSMISSION UNIT (PMTU) The size of the largest packet that the sender can transmit without it being fragmented en route to the receiver. It includes the sizes of all headers and data except the IP header. CONGESTION WINDOW (cwnd) A CM state variable that modulates the amount of outstanding data between sender and receiver. OUTSTANDING WINDOW (ownd) The number of bytes that has been transmitted by the source, but not known to have been either received by the destination or lost in the network. INITIAL WINDOW (IW) The size of the sender's congestion window at the beginning of a macroflow.
DATA TYPE SYNTAX We use "u64" for unsigned 64-bit, "u32" for unsigned 32-bit, "u16" for unsigned 16-bit, "u8" for unsigned 8-bit, "i32" for signed 32-bit, "i16" for signed 16-bit quantities, "float" for IEEE floating point values. The type "void" is used to indicate that no return value is expected from a call. Pointers are referred to using "*" syntax, following C language convention. We emphasize that all the API functions described in this document are "abstract" calls and that conformant CM implementations may differ in specific implementation details.
1. Applications are well-behaved with their own independent per-byte or per-packet sequence number information, and use the CM API to update internal state in the CM. 2. Networks are best-effort without service discrimination or reservations. In particular, it does not address situations where different streams between the same pair of hosts traverse paths with differing characteristics. The Congestion Manager framework can be extended to support applications that do not provide their own feedback and to differentially-served networks. These extensions will be addressed in later documents. The CM is motivated by two main goals: (i) Enable efficient multiplexing. Increasingly, the trend on the Internet is for unicast data senders (e.g., Web servers) to transmit heterogeneous types of data to receivers, ranging from unreliable real-time streaming content to reliable Web pages and applets. As a result, many logically different streams share the same path between sender and receiver. For the Internet to remain stable, each of these streams must incorporate control protocols that safely probe for spare bandwidth and react to congestion. Unfortunately, these concurrent streams typically compete with each other for network resources, rather than share them effectively. Furthermore, they do not learn from each other about the state of the network. Even if they each independently implement congestion control (e.g., a group of TCP connections each implementing the algorithms in [Jacobson88, Allman99]), the ensemble of streams tends to be more aggressive in the face of congestion than a single TCP connection implementing standard TCP congestion control and avoidance [Balakrishnan98]. (ii) Enable application adaptation to congestion. Increasingly, popular real-time streaming applications run over UDP using their own user-level transport protocols for good application performance, but in most cases today do not adapt or react properly to network congestion. By implementing a stable control algorithm and exposing an adaptation API, the CM enables easy application adaptation to congestion. Applications adapt the data they transmit to the current network conditions. The CM framework builds on recent work on TCP control block sharing [Touch97], integrated TCP congestion control (TCP-Int) [Balakrishnan98] and TCP sessions [Padmanabhan98]. [Touch97] advocates the sharing of some of the state in the TCP control block to improve transient transport performance and describes sharing across an ensemble of TCP connections. [Balakrishnan98],
[Padmanabhan98], and [Eggert00] describe several experiments that quantify the benefits of sharing congestion state, including improved stability in the face of congestion and better loss recovery. Integrating loss recovery across concurrent connections significantly improves performance because losses on one connection can be detected by noticing that later data sent on another connection has been received and acknowledged. The CM framework extends these ideas in two significant ways: (i) it extends congestion management to non-TCP streams, which are becoming increasingly common and often do not implement proper congestion management, and (ii) it provides an API for applications to adapt their transmissions to current network conditions. For an extended discussion of the motivation for the CM, its architecture, API, and algorithms, see [Balakrishnan99]; for a description of an implementation and performance results, see [Andersen00]. The resulting end-host protocol architecture at the sender is shown in Figure 1. The CM helps achieve network stability by implementing stable congestion avoidance and control algorithms that are "TCP- friendly" [Mahdavi98] based on algorithms described in [Allman99]. However, it does not attempt to enforce proper congestion behavior for all applications (but it does not preclude a policer on the host that performs this task). Note that while the policer at the end- host can use CM, the network has to be protected against compromises to the CM and the policer at the end hosts, a task that requires router machinery [Floyd99a]. We do not address this issue further in this document.
|--------| |--------| |--------| |--------| |--------------| | HTTP | | FTP | | RTP 1 | | RTP 2 | | | |--------| |--------| |--------| |--------| | | | | | ^ | ^ | | | | | | | | | Scheduler | | | | | | | |---| | | | | | |-------|--+->| | | | | | | | | |<--| | v v v v | | |--------------| |--------| |--------| |-------------| | | ^ | TCP 1 | | TCP 2 | | UDP 1 | | A | | |--------| |--------| |-------------| | | | ^ | ^ | | | | |--------------| | | | | | | P |-->| | | | | | | | | | | |---|------+---|--------------|------->| | | Congestion | | | | | I | | | v v v | | | Controller | |-----------------------------------| | | | | | IP |-->| | | | |-----------------------------------| | | |--------------| |---| Figure 1 The key components of the CM framework are (i) the API, (ii) the congestion controller, and (iii) the scheduler. The API is (in part) motivated by the requirements of application-level framing (ALF) [Clark90], and is described in Section 4. The CM internals (Section 5) include a congestion controller (Section 5.1) and a scheduler to orchestrate data transmissions between concurrent streams in a macroflow (Section 5.2). The congestion controller adjusts the aggregate transmission rate between sender and receiver based on its estimate of congestion in the network. It obtains feedback about its past transmissions from applications themselves via the API. The scheduler apportions available bandwidth amongst the different streams within each macroflow and notifies applications when they are permitted to send data. This document focuses on well-behaved applications; a future one will describe the sender-receiver protocol and header formats that will handle applications that do not incorporate their own feedback to the CM.
SHOULD, etc., but the terms are meant to apply within the context of an implementation of the CM API. The section does not apply to congestion control implementations in general, only to those implementations offering the CM API. Using the CM API, streams can determine their share of the available bandwidth, request and have their data transmissions scheduled, inform the CM about successful transmissions, and be informed when the CM's estimate of path bandwidth changes. Thus, the CM frees applications from having to maintain information about the state of congestion and available bandwidth along any path. The function prototypes below follow standard C language convention. We emphasize that these API functions are abstract calls and conformant CM implementations may differ in specific details, as long as equivalent functionality is provided. When a new stream is created by an application, it passes some information to the CM via the cm_open(stream_info) API call. Currently, stream_info consists of the following information: (i) the source IP address, (ii) the source port, (iii) the destination IP address, (iv) the destination port, and (v) the IP protocol number. Mogul90]. It MAY be statically configured in the absence of such a mechanism.
Section 4.3 discusses the time duration for which the transmission grant is valid, while Section 5.2 describes how these requests are scheduled and callbacks made. 2. Synchronous-style. The above callback-based API accommodates a class of ALF streams that are "asynchronous." Asynchronous transmitters do not transmit based on a periodic clock, but do so triggered by asynchronous events like file reads or captured frames. On the other hand, there are many streams that are "synchronous" transmitters, which transmit periodically based on their own internal timers (e.g., an audio senders that sends at a constant sampling rate). While CM callbacks could be configured to periodically interrupt such transmitters, the transmit loop of such applications is less affected if they retain their original timer-based loop. In addition, it complicates the CM API to have a stream express the periodicity and granularity of its callbacks. Thus, the CM MUST export an API that allows such streams to be informed of changes in rates using the cmapp_update(u64 newrate, u32 srtt, u32 rttdev) callback function, where newrate is the new rate in bits per second for this stream, srtt is the current smoothed round trip time estimate in microseconds, and rttdev is the smoothed linear deviation in the round-trip time estimate calculated using the same algorithm as in TCP [Paxson00]. The newrate value reports an instantaneous rate calculated, for example, by taking the ratio of cwnd and srtt, and dividing by the fraction of that ratio allocated to the stream.
In response, the stream MUST adapt its packet size or change its timer interval to conform to (i.e., not exceed) the allowed rate. Of course, it may choose not to use all of this rate. Note that the CM is not on the data path of the actual transmission. To avoid unnecessary cmapp_update() callbacks that the application will only ignore, the CM MUST provide a cm_thresh(float rate_downthresh, float rate_upthresh, float rtt_downthresh, float rtt_upthresh) function that a stream can use at any stage in its execution. In response, the CM SHOULD invoke the callback only when the rate decreases to less than (rate_downthresh * lastrate) or increases to more than (rate_upthresh * lastrate), where lastrate is the rate last notified to the stream, or when the round-trip time changes correspondingly by the requisite thresholds. This information is used as a hint by the CM, in the sense the cmapp_update() can be called even if these conditions are not met. The CM MUST implement a cm_query(i32 cm_streamid, u64* rate, u32* srtt, u32* rttdev) to allow an application to query the current CM state. This sets the rate variable to the current rate estimate in bits per second, the srtt variable to the current smoothed round-trip time estimate in microseconds, and rttdev to the mean linear deviation. If the CM does not have valid estimates for the macroflow, it fills in negative values for the rate, srtt, and rttdev. Note that a stream can use more than one of the above transmission APIs at the same time. In particular, the knowledge of sustainable rate is useful for asynchronous streams as well as synchronous ones; e.g., an asynchronous Web server disseminating images using TCP may use cmapp_send() to schedule its transmissions and cmapp_update() to decide whether to send a low-resolution or high-resolution image. A TCP implementation using the CM is described in Section 6.1.1, where the benefit of the cm_request() callback API for TCP will become apparent. The reader will notice that the basic CM API does not provide an interface for buffered congestion-controlled transmissions. This is intentional, since this transmission mode can be implemented using the callback-based primitive. Section 6.1.2 describes how congestion-controlled UDP sockets may be implemented using the CM API.
successful receptions, type of loss (timeout event, Explicit Congestion Notification [Ramakrishnan99], etc.) and round-trip time samples. The nrecd parameter indicates how many bytes were successfully received by the receiver since the last cm_update call, while the nrecd parameter identifies how many bytes were received were lost during the same time period. The rtt value indicates the round-trip time measured during the transmission of these bytes. The rtt value must be set to -1 if no valid round-trip sample was obtained by the application. The lossmode parameter provides an indicator of how a loss was detected. A value of CM_NO_FEEDBACK indicates that the application has received no feedback for all its outstanding data, and is reporting this to the CM. For example, a TCP that has experienced a timeout would use this parameter to inform the CM of this. A value of CM_LOSS_FEEDBACK indicates that the application has experienced some loss, which it believes to be due to congestion, but not all outstanding data has been lost. For example, a TCP segment loss detected using duplicate (selective) acknowledgments or other data-driven techniques fits this category. A value of CM_EXPLICIT_CONGESTION indicates that the receiver echoed an explicit congestion notification message. Finally, a value of CM_NO_CONGESTION indicates that no congestion-related loss has occurred. The lossmode parameter MUST be reported as a bit-vector where the bits correspond to CM_NO_FEEDBACK, CM_LOSS_FEEDBACK, CM_EXPLICIT_CONGESTION, and CM_NO_CONGESTION. Note that over links (paths) that experience losses for reasons other than congestion, an application SHOULD inform the CM of losses, with the CM_NO_CONGESTION field set. cm_notify(i32 cm_streamid, u32 nsent) MUST be called when data is transmitted from the host (e.g., in the IP output routine) to inform the CM that nsent bytes were just transmitted on a given stream. This allows the CM to update its estimate of the number of outstanding bytes for the macroflow and for the stream. A cmapp_send() grant from the CM to an application is valid only for an expiration time, equal to the larger of the round-trip time and an implementation-dependent threshold communicated as an argument to the cmapp_send() callback function. The application MUST NOT send data based on this callback after this time has expired. Furthermore, if the application decides not to send data after receiving this callback, it SHOULD call cm_notify(stream_info, 0) to allow the CM to permit other streams in the macroflow to transmit data. The CM congestion controller MUST be robust to applications forgetting to invoke cm_notify(stream_info, 0) correctly, or applications that crash or disappear after having made a cm_request() call.
Section 5.2). A later guideline document is expected to describe a few simple schedulers (e.g., weighted round-robin, hierarchical scheduling) and the API they export to provide relative prioritization. Section 4.3) from concurrent streams on the same macroflow to build up information about the congestion state of the network path used by the macroflow. The congestion controller MUST implement a "TCP-friendly" [Mahdavi98] congestion control algorithm. Several macroflows MAY (and indeed, often will) use the same congestion control algorithm but each macroflow maintains state about the network used by its streams. The congestion control module MUST implement the following abstract interfaces. We emphasize that these are not directly visible to applications; they are within the context of a macroflow, and are different from the CM API functions of Section 4. - void query(u64 *rate, u32 *srtt, u32 *rttdev): This function returns the estimated rate (in bits per second) and smoothed round trip time (in microseconds) for the macroflow. - void notify(u32 nsent): This function MUST be used to notify the congestion control module whenever data is sent by an application. The nsent parameter indicates the number of bytes just sent by the application. - void update(u32 nsent, u32 nrecd, u32 rtt, u32 lossmode): This function is called whenever any of the CM streams associated with a macroflow identifies that data has reached the receiver or has been lost en route. The nrecd parameter indicates the number of bytes that have just arrived at the receiver. The nsent parameter is the sum of the number of bytes just received and the
number of bytes identified as lost en route. The rtt parameter is the estimated round trip time in microseconds during the transfer. The lossmode parameter provides an indicator of how a loss was detected (section 4.3). Although these interfaces are not visible to applications, the congestion controller MUST implement these abstract interfaces to provide for modular inter-operability with different separately- developed schedulers. The congestion control module MUST also call the associated scheduler's schedule function (section 5.2) when it believes that the current congestion state allows an MTU-sized packet to be sent.
Andersen00]. All applications that use the CM MUST incorporate feedback from the receiver. For example, it must periodically (typically once or twice per round trip time) determine how many of its packets arrived at the receiver. When the source gets this feedback, it MUST use cm_update() to inform the CM of this new information. This results in the CM updating ownd and may result in the CM changing its estimates and calling cmapp_update() of the streams of the macroflow. The protocols in this section are examples and suggestions for implementation, rather than requirements for any conformant implementation.
to help estimate the amount of outstanding data. Note that this is not needed if the SACK option is used on the connection, since this information is explicitly available. The TCP output routines are modified as follows: 1. All congestion window (cwnd) checks are removed. 2. When application data is available. The TCP output routines perform all non-congestion checks (Nagle algorithm, receiver- advertised window check, etc). If these checks pass, the output routine queues the data and calls cm_request() for the stream. 3. If incoming data or timers result in a loss being detected, the retransmission is also placed in a queue and cm_request() is called for the stream. 4. The cmapp_send() callback for TCP is set to an output routine. If any retransmission is enqueued, the routine outputs the retransmission. Otherwise, the routine outputs as much new data as the TCP connection state allows. However, the cmapp_send() never sends more than a single segment per call. This routine arranges for the other output computations to be done, such as header and options computations. The IP output routine on the host calls cm_notify() when the packets are actually sent out. Because it does not know which cm_streamid is responsible for the packet, cm_notify() takes the stream_info as argument (see Section 4 for what the stream_info should contain). Because cm_notify() reports the IP payload size, TCP keeps track of the total header size and incorporates these updates. The TCP input routines are modified as follows: 1. RTT estimation is done as normal using either timestamps or Karn's algorithm. Any rtt estimate that is generated is passed to CM via the cm_update call. 2. All cwnd and slow start threshold (ssthresh) updates are removed. 3. Upon the arrival of an ack for new data, TCP computes the value of in_flight (the amount of data in flight) as snd_max-ack-1 (i.e., MAX Sequence Sent - Current Ack - 1). TCP then calls cm_update(streamid, tcp_ownd - in_flight, 0, CM_NO_CONGESTION, rtt).
4. Upon the arrival of a duplicate acknowledgement, TCP must check its dupack count (dup_acks) to determine its action. If dup_acks < 3, the TCP does nothing. If dup_acks == 3, TCP assumes that a packet was lost and that at least 3 packets arrived to generate these duplicate acks. Therefore, it calls cm_update(streamid, 4 * avg_pkt_size, 3 * avg_pkt_size, CM_LOSS_FEEDBACK, rtt). The average packet size is used since the acknowledgments do not indicate exactly how much data has reached the other end. Most TCP implementations interpret a duplicate ACK as an indication that a full MSS has reached its destination. Once a new ACK is received, these TCP sender implementations may resynchronize with TCP receiver. The CM API does not provide a mechanism for TCP to pass information from this resynchronization. Therefore, TCP can only infer the arrival of an avg_pkt_size amount of data from each duplicate ack. TCP also enqueues a retransmission of the lost segment and calls cm_request(). If dup_acks > 3, TCP assumes that a packet has reached the other end and caused this ack to be sent. As a result, it calls cm_update(streamid, avg_pkt_size, avg_pkt_size, CM_NO_CONGESTION, rtt). 5. Upon the arrival of a partial acknowledgment (one that does not exceed the highest segment transmitted at the time the loss occurred, as defined in [Floyd99b]), TCP assumes that a packet was lost and that the retransmitted packet has reached the recipient. Therefore, it calls cm_update(streamid, 2 * avg_pkt_size, avg_pkt_size, CM_NO_CONGESTION, rtt). CM_NO_CONGESTION is used since the loss period has already been reported. TCP also enqueues a retransmission of the lost segment and calls cm_request(). When the TCP retransmission timer expires, the sender identifies that a segment has been lost and calls cm_update(streamid, avg_pkt_size, 0, CM_NO_FEEDBACK, 0) to signify that no feedback has been received from the receiver and that one segment is sure to have "left the pipe." TCP also enqueues a retransmission of the lost segment and calls cm_request(). Stevens94]. They provide the same functionality as standard Berkeley UDP sockets, but instead of immediately sending the data from the kernel packet queue to lower layers for transmission, the buffered socket implementation makes calls to the API exported by the CM inside the kernel and gets callbacks from the CM. When a CM UDP socket is created, it is bound to a particular stream. Later, when data is added to the packet queue, cm_request() is called on the stream associated with the
socket. When the CM schedules this stream for transmission, it calls udp_ccappsend() in the UDP module. This function transmits one MTU from the packet queue, and schedules the transmission of any remaining packets. The in-kernel implementation of the CM UDP API should not require any additional data copies and should support all standard UDP options. Modifying existing applications to use congestion-controlled UDP requires the implementation of a new socket option on the socket. To work correctly, the sender must obtain feedback about congestion. This can be done in at least two ways: (i) the UDP receiver application can provide feedback to the sender application, which will inform the CM of network conditions using cm_update(); (ii) the UDP receiver implementation can provide feedback to the sending UDP. Note that this latter alternative requires changes to the receiver's network stack and the sender UDP cannot assume that all receivers support this option without explicit negotiation. Section 4.2). When the source first starts, it uses the cm_query() call to get an initial estimate of network bandwidth and delay. If some other streams on that macroflow have already been active, then it gets an initial estimate that is valid; otherwise, it gets negative values, which it ignores. It then chooses an encoding that does not exceed these estimates (or, in the case of an invalid estimate, uses application-specific initial values) and begins transmitting data. The application also implements the cmapp_update() callback. When the CM determines that network characteristics have changed, it calls the application's cmapp_update() function and passes it a new rate and round-trip time estimate. The application must change its choice of audio encoding to ensure that it does not exceed these new estimates.
transmissions will give the CM an inaccurate picture of the network's congestion state. By giving CM a high estimate of congestion, an attacker can degrade the performance observed by applications. For example, a stream on a host can arbitrarily slow down any other stream on the same macroflow, a form of denial of service. The more dangerous form of attack occurs when an application gives the CM a low estimate of congestion. This would cause CM to be overly aggressive and allow data to be sent much more quickly than sound congestion control policies would allow. [Touch97] describes a number of the security problems that arise with congestion information sharing. An additional vulnerability (not covered by [Touch97])) occurs because applications have access through the CM API to control shared state that will affect other applications on the same computer. For instance, a poorly designed, possibly a compromised, or intentionally malicious UDP application could misuse cm_update() to cause starvation and/or too-aggressive behavior of others in the macroflow. [Allman99] Allman, M. and Paxson, V., "TCP Congestion Control", RFC 2581, April 1999. [Andersen00] Balakrishnan, H., System Support for Bandwidth Management and Content Adaptation in Internet Applications, Proc. 4th Symp. on Operating Systems Design and Implementation, San Diego, CA, October 2000. Available from http://nms.lcs.mit.edu/papers/cm-osdi2000.html [Balakrishnan98] Balakrishnan, H., Padmanabhan, V., Seshan, S., Stemm, M., and Katz, R., "TCP Behavior of a Busy Web Server: Analysis and Improvements," Proc. IEEE INFOCOM, San Francisco, CA, March 1998. [Balakrishnan99] Balakrishnan, H., Rahul, H., and Seshan, S., "An Integrated Congestion Management Architecture for Internet Hosts," Proc. ACM SIGCOMM, Cambridge, MA, September 1999. [Bradner96] Bradner, S., "The Internet Standards Process --- Revision 3", BCP 9, RFC 2026, October 1996. [Bradner97] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[Clark90] Clark, D. and Tennenhouse, D., "Architectural Consideration for a New Generation of Protocols", Proc. ACM SIGCOMM, Philadelphia, PA, September 1990. [Eggert00] Eggert, L., Heidemann, J., and Touch, J., "Effects of Ensemble TCP," ACM Computer Comm. Review, January 2000. [Floyd99a] Floyd, S. and Fall, K.," Promoting the Use of End- to-End Congestion Control in the Internet," IEEE/ACM Trans. on Networking, 7(4), August 1999, pp. 458-472. [Floyd99b] Floyd, S. and T. Henderson,"The New Reno Modification to TCP's Fast Recovery Algorithm," RFC 2582, April 1999. [Jacobson88] Jacobson, V., "Congestion Avoidance and Control," Proc. ACM SIGCOMM, Stanford, CA, August 1988. [Mahdavi98] Mahdavi, J. and Floyd, S., "The TCP Friendly Website," http://www.psc.edu/networking/tcp_friendly.html [Mogul90] Mogul, J. and S. Deering, "Path MTU Discovery," RFC 1191, November 1990. [Padmanabhan98] Padmanabhan, V., "Addressing the Challenges of Web Data Transport," PhD thesis, Univ. of California, Berkeley, December 1998. [Paxson00] Paxson, V. and M. Allman, "Computing TCP's Retransmission Timer", RFC 2988, November 2000. [Postel81] Postel, J., Editor, "Transmission Control Protocol", STD 7, RFC 793, September 1981. [Ramakrishnan99] Ramakrishnan, K. and Floyd, S., "A Proposal to Add Explicit Congestion Notification (ECN) to IP," RFC 2481, January 1999. [Stevens94] Stevens, W., TCP/IP Illustrated, Volume 1. Addison-Wesley, Reading, MA, 1994. [Touch97] Touch, J., "TCP Control Block Interdependence", RFC 2140, April 1997.
http://nms.lcs.mit.edu/~hari/ Srinivasan Seshan School of Computer Science Carnegie Mellon University 5000 Forbes Ave. Pittsburgh, PA 15213 EMail: email@example.com Web: http://www.cs.cmu.edu/~srini/
Full Copyright Statement Copyright (C) The Internet Society (2001). All Rights Reserved. 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. The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns. 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. Acknowledgement Funding for the RFC Editor function is currently provided by the Internet Society.