ecn(9): Update ecn tunneling functions to RFC 6040
Update ECN tunneling functions from obsolete RFC 3168 to newer RFC 6040. Also, add ECN_COMPLETE to support dangerous packet reporting without causing extra costs to existing caller functions. Finally, return values are specified as macro to reduce confusion, considering extra return values for ECN_WARN and ECN_ALARM were added. Reviewed By: glebius, tuexen Differential Revision: https://reviews.freebsd.org/D53516
This commit is contained in:
+98
-55
@@ -31,10 +31,6 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* ECN consideration on tunnel ingress/egress operation.
|
||||
* http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
|
||||
*/
|
||||
|
||||
#include "opt_inet.h"
|
||||
#include "opt_inet6.h"
|
||||
@@ -58,7 +54,7 @@
|
||||
|
||||
/*
|
||||
* ECN and TOS (or TCLASS) processing rules at tunnel encapsulation and
|
||||
* decapsulation from RFC3168:
|
||||
* decapsulation from RFC6040:
|
||||
*
|
||||
* Outer Hdr at Inner Hdr at
|
||||
* Encapsulator Decapsulator
|
||||
@@ -66,18 +62,42 @@
|
||||
* DS Field copied from inner hdr no change
|
||||
* ECN Field constructed by (I) constructed by (E)
|
||||
*
|
||||
* ECN_ALLOWED (full functionality):
|
||||
* (I) if the ECN field in the inner header is set to CE, then set the
|
||||
* ECN field in the outer header to ECT(0).
|
||||
* otherwise, copy the ECN field to the outer header.
|
||||
* ECN_ALLOWED (normal mode):
|
||||
* (I) copy the ECN field to the outer header.
|
||||
*
|
||||
* (E) if the ECN field in the outer header is set to CE and the ECN
|
||||
* field of the inner header is not-ECT, drop the packet.
|
||||
* if the ECN field in the inner header is set to ECT(0) or ECT(1)
|
||||
* and the ECN field in the outer header is set to CE, then copy CE to
|
||||
* the inner header. otherwise, make no change to the inner header.
|
||||
* If the ECN field in the inner header is set to ECT(0) and the ECN
|
||||
* field in the outer header is set to ECT(1), copy ECT(1) to
|
||||
* the inner header. If the ECN field in the inner header is set
|
||||
* to ECT(0) or ECT(1) and the ECN field in the outer header is set to
|
||||
* CE, copy CE to the inner header.
|
||||
* Otherwise, make no change to the inner header. This behaviour can be
|
||||
* summarized in the table below:
|
||||
*
|
||||
* ECN_FORBIDDEN (limited functionality):
|
||||
* Outer Header at Decapsulator
|
||||
* +---------+------------+------------+------------+
|
||||
* | Not-ECT | ECT(0) | ECT(1) | CE |
|
||||
* Inner Hdr: +---------+------------+------------+------------+
|
||||
* Not-ECT | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)|
|
||||
* ECT(0) | ECT(0) | ECT(0) | ECT(1) | CE |
|
||||
* ECT(1) | ECT(1) | ECT(1) (!) | ECT(1) | CE |
|
||||
* CE | CE | CE | CE(!!!)| CE |
|
||||
* +---------+------------+------------+------------+
|
||||
*
|
||||
* ECN_COMPLETE (normal mode with security log):
|
||||
* certain combinations indicated in table by '(!!!)' or '(!)',
|
||||
* where '(!!!)' means the combination always potentially dangerous which
|
||||
* returns 3, while '(!)' means possibly dangerous in which returns 2.
|
||||
* These combinations are unsed by previous ECN tunneling specifications
|
||||
* and could be logged. Also, in case of more dangerous ones, the
|
||||
* decapsulator SHOULD log the event and MAY also raise an alarm.
|
||||
*
|
||||
* Note: Caller SHOULD use rate-limited alarms so that the anomalous
|
||||
* combinations will not amplify into a flood of alarm messages.
|
||||
* Also, it MUST be possible to suppress alarms or logging.
|
||||
*
|
||||
* ECN_FORBIDDEN (compatibility mode):
|
||||
* (I) set the ECN field to not-ECT in the outer header.
|
||||
*
|
||||
* (E) if the ECN field in the outer header is set to CE, drop the packet.
|
||||
@@ -94,26 +114,22 @@ void
|
||||
ip_ecn_ingress(int mode, uint8_t *outer, const uint8_t *inner)
|
||||
{
|
||||
|
||||
if (!outer || !inner)
|
||||
panic("NULL pointer passed to ip_ecn_ingress");
|
||||
KASSERT(outer != NULL && inner != NULL,
|
||||
("NULL pointer passed to %s", __func__));
|
||||
|
||||
*outer = *inner;
|
||||
switch (mode) {
|
||||
case ECN_ALLOWED: /* ECN allowed */
|
||||
/*
|
||||
* full-functionality: if the inner is CE, set ECT(0)
|
||||
* to the outer. otherwise, copy the ECN field.
|
||||
*/
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
|
||||
*outer &= ~IPTOS_ECN_ECT1;
|
||||
case ECN_COMPLETE:
|
||||
case ECN_ALLOWED:
|
||||
/* normal mode: always copy the ECN field. */
|
||||
break;
|
||||
case ECN_FORBIDDEN: /* ECN forbidden */
|
||||
/*
|
||||
* limited-functionality: set not-ECT to the outer
|
||||
*/
|
||||
|
||||
case ECN_FORBIDDEN:
|
||||
/* compatibility mode: set not-ECT to the outer */
|
||||
*outer &= ~IPTOS_ECN_MASK;
|
||||
break;
|
||||
case ECN_NOCARE: /* no consideration to ECN */
|
||||
|
||||
case ECN_NOCARE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -126,33 +142,57 @@ int
|
||||
ip_ecn_egress(int mode, const uint8_t *outer, uint8_t *inner)
|
||||
{
|
||||
|
||||
if (!outer || !inner)
|
||||
panic("NULL pointer passed to ip_ecn_egress");
|
||||
KASSERT(outer != NULL && inner != NULL,
|
||||
("NULL pointer passed to %s", __func__));
|
||||
|
||||
switch (mode) {
|
||||
case ECN_ALLOWED:
|
||||
/*
|
||||
* full-functionality: if the outer is CE and the inner is
|
||||
* not-ECT, should drop it. otherwise, copy CE.
|
||||
*/
|
||||
if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
|
||||
case ECN_COMPLETE:
|
||||
if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0) {
|
||||
/* if the outer is ECT(0) and inner is ECT(1) raise a warning */
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1)
|
||||
return (ECN_WARN);
|
||||
/* if the inner is not-ECT and outer is ECT(0) raise an alarm */
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
|
||||
return (0);
|
||||
return (ECN_ALARM);
|
||||
return (ECN_SUCCESS);
|
||||
} else if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1) {
|
||||
/* if the outer is ECT(1) and inner is CE or ECT(1), raise an alarm */
|
||||
if (((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_CE) ||
|
||||
((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT))
|
||||
return (ECN_ALARM);
|
||||
/* if the outer is ECT(1) and inner is ECT(0), copy ECT(1) */
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0)
|
||||
*inner = IPTOS_ECN_ECT1;
|
||||
return (ECN_SUCCESS);
|
||||
}
|
||||
/* fallthrough */
|
||||
case ECN_ALLOWED:
|
||||
if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
|
||||
/* if the outer is CE and the inner is not-ECT, drop it. */
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
|
||||
return (ECN_DROP);
|
||||
/* otherwise, copy CE */
|
||||
*inner |= IPTOS_ECN_CE;
|
||||
} else if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1) {
|
||||
/* if the outer is ECT(1) and inner is ECT(0), copy ECT(1) */
|
||||
if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0)
|
||||
*inner = IPTOS_ECN_ECT1;
|
||||
}
|
||||
break;
|
||||
case ECN_FORBIDDEN: /* ECN forbidden */
|
||||
|
||||
case ECN_FORBIDDEN:
|
||||
/*
|
||||
* limited-functionality: if the outer is CE, should drop it.
|
||||
* compatibility mode: if the outer is CE, should drop it.
|
||||
* otherwise, leave the inner.
|
||||
*/
|
||||
if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
|
||||
return (0);
|
||||
return (ECN_DROP);
|
||||
break;
|
||||
case ECN_NOCARE: /* no consideration to ECN */
|
||||
|
||||
case ECN_NOCARE:
|
||||
break;
|
||||
}
|
||||
return (1);
|
||||
return (ECN_SUCCESS);
|
||||
}
|
||||
|
||||
#ifdef INET6
|
||||
@@ -161,31 +201,34 @@ ip6_ecn_ingress(int mode, uint32_t *outer, const uint32_t *inner)
|
||||
{
|
||||
uint8_t outer8, inner8;
|
||||
|
||||
if (!outer || !inner)
|
||||
panic("NULL pointer passed to ip6_ecn_ingress");
|
||||
KASSERT(outer != NULL && inner != NULL,
|
||||
("NULL pointer passed to %s", __func__));
|
||||
|
||||
inner8 = (ntohl(*inner) >> 20) & 0xff;
|
||||
inner8 = (ntohl(*inner) >> IPV6_FLOWLABEL_LEN) & 0xff;
|
||||
ip_ecn_ingress(mode, &outer8, &inner8);
|
||||
*outer &= ~htonl(0xff << 20);
|
||||
*outer |= htonl((uint32_t)outer8 << 20);
|
||||
*outer &= ~htonl(0xff << IPV6_FLOWLABEL_LEN);
|
||||
*outer |= htonl((uint32_t)outer8 << IPV6_FLOWLABEL_LEN);
|
||||
}
|
||||
|
||||
int
|
||||
ip6_ecn_egress(int mode, const uint32_t *outer, uint32_t *inner)
|
||||
{
|
||||
uint8_t outer8, inner8, oinner8;
|
||||
int ret;
|
||||
|
||||
if (!outer || !inner)
|
||||
panic("NULL pointer passed to ip6_ecn_egress");
|
||||
KASSERT(outer != NULL && inner != NULL,
|
||||
("NULL pointer passed to %s", __func__));
|
||||
|
||||
outer8 = (ntohl(*outer) >> 20) & 0xff;
|
||||
inner8 = oinner8 = (ntohl(*inner) >> 20) & 0xff;
|
||||
if (ip_ecn_egress(mode, &outer8, &inner8) == 0)
|
||||
return (0);
|
||||
outer8 = (ntohl(*outer) >> IPV6_FLOWLABEL_LEN) & 0xff;
|
||||
inner8 = oinner8 = (ntohl(*inner) >> IPV6_FLOWLABEL_LEN) & 0xff;
|
||||
|
||||
ret = ip_ecn_egress(mode, &outer8, &inner8);
|
||||
if (ret == ECN_DROP)
|
||||
return (ECN_DROP);
|
||||
if (inner8 != oinner8) {
|
||||
*inner &= ~htonl(0xff << 20);
|
||||
*inner |= htonl((uint32_t)inner8 << 20);
|
||||
*inner &= ~htonl(0xff << IPV6_FLOWLABEL_LEN);
|
||||
*inner |= htonl((uint32_t)inner8 << IPV6_FLOWLABEL_LEN);
|
||||
}
|
||||
return (1);
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
+10
-7
@@ -31,19 +31,22 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* ECN consideration on tunnel ingress/egress operation.
|
||||
* http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
|
||||
*/
|
||||
|
||||
#ifndef _NETINET_IP_ECN_H_
|
||||
#define _NETINET_IP_ECN_H_
|
||||
|
||||
#define ECN_ALLOWED 1 /* ECN allowed */
|
||||
#define ECN_FORBIDDEN 0 /* ECN forbidden */
|
||||
#ifdef _KERNEL
|
||||
#define ECN_COMPLETE 2 /* ECN normal mode with security log */
|
||||
#define ECN_ALLOWED 1 /* ECN normal mode */
|
||||
#define ECN_FORBIDDEN 0 /* ECN compatibility mode */
|
||||
#define ECN_NOCARE (-1) /* no consideration to ECN */
|
||||
|
||||
#ifdef _KERNEL
|
||||
/* ip[6]_ecn_egress return values */
|
||||
#define ECN_DROP 0 /* caller MUST drop the packet */
|
||||
#define ECN_SUCCESS 1 /* success */
|
||||
#define ECN_WARN 2 /* caller MAY log */
|
||||
#define ECN_ALARM 3 /* caller SHOULD log and MAY raise alarm */
|
||||
|
||||
extern void ip_ecn_ingress(int, uint8_t *, const uint8_t *);
|
||||
extern int ip_ecn_egress(int, const uint8_t *, uint8_t *);
|
||||
#endif
|
||||
|
||||
@@ -31,11 +31,6 @@
|
||||
* $KAME: ip_ecn.h,v 1.5 2000/03/27 04:58:38 sumikawa Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* ECN consideration on tunnel ingress/egress operation.
|
||||
* http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
|
||||
*/
|
||||
|
||||
#ifdef _KERNEL
|
||||
extern void ip6_ecn_ingress(int, uint32_t *, const uint32_t *);
|
||||
extern int ip6_ecn_egress(int, const uint32_t *, uint32_t *);
|
||||
|
||||
Reference in New Issue
Block a user