1121684Sume/*	$KAME: ip_ecn.c,v 1.12 2002/01/07 11:34:47 kjc Exp $	*/
262587Sitojun
3139823Simp/*-
455009Sshin * Copyright (C) 1999 WIDE Project.
555009Sshin * All rights reserved.
655009Sshin *
755009Sshin * Redistribution and use in source and binary forms, with or without
855009Sshin * modification, are permitted provided that the following conditions
955009Sshin * are met:
1055009Sshin * 1. Redistributions of source code must retain the above copyright
1155009Sshin *    notice, this list of conditions and the following disclaimer.
1255009Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355009Sshin *    notice, this list of conditions and the following disclaimer in the
1455009Sshin *    documentation and/or other materials provided with the distribution.
1555009Sshin * 3. Neither the name of the project nor the names of its contributors
1655009Sshin *    may be used to endorse or promote products derived from this software
1755009Sshin *    without specific prior written permission.
1855009Sshin *
1955009Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055009Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155009Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255009Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355009Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455009Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555009Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655009Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755009Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855009Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955009Sshin * SUCH DAMAGE.
3055009Sshin *
3155009Sshin */
3255009Sshin/*
3355009Sshin * ECN consideration on tunnel ingress/egress operation.
3455009Sshin * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
3555009Sshin */
3655009Sshin
37172467Ssilby#include <sys/cdefs.h>
38172467Ssilby__FBSDID("$FreeBSD$");
39172467Ssilby
4055009Sshin#include "opt_inet.h"
4155009Sshin#include "opt_inet6.h"
4255009Sshin
4355009Sshin#include <sys/param.h>
4455009Sshin#include <sys/systm.h>
4555009Sshin#include <sys/mbuf.h>
4655009Sshin#include <sys/errno.h>
4755009Sshin
4855009Sshin#include <netinet/in.h>
4955009Sshin#include <netinet/in_systm.h>
5055009Sshin#include <netinet/ip.h>
5155009Sshin#ifdef INET6
5255009Sshin#include <netinet/ip6.h>
5355009Sshin#endif
5455009Sshin
5555009Sshin#include <netinet/ip_ecn.h>
5655009Sshin#ifdef INET6
5755009Sshin#include <netinet6/ip6_ecn.h>
5855009Sshin#endif
5955009Sshin
6055009Sshin/*
61121684Sume * ECN and TOS (or TCLASS) processing rules at tunnel encapsulation and
62121684Sume * decapsulation from RFC3168:
63121684Sume *
64121684Sume *                      Outer Hdr at                 Inner Hdr at
65121684Sume *                      Encapsulator                 Decapsulator
66121684Sume *   Header fields:     --------------------         ------------
67121684Sume *     DS Field         copied from inner hdr        no change
68121684Sume *     ECN Field        constructed by (I)           constructed by (E)
69121684Sume *
70121684Sume * ECN_ALLOWED (full functionality):
71121684Sume *    (I) if the ECN field in the inner header is set to CE, then set the
72121684Sume *    ECN field in the outer header to ECT(0).
73121684Sume *    otherwise, copy the ECN field to the outer header.
74121684Sume *
75121684Sume *    (E) if the ECN field in the outer header is set to CE and the ECN
76121684Sume *    field of the inner header is not-ECT, drop the packet.
77121684Sume *    if the ECN field in the inner header is set to ECT(0) or ECT(1)
78121684Sume *    and the ECN field in the outer header is set to CE, then copy CE to
79121684Sume *    the inner header.  otherwise, make no change to the inner header.
80121684Sume *
81121684Sume * ECN_FORBIDDEN (limited functionality):
82121684Sume *    (I) set the ECN field to not-ECT in the outer header.
83121684Sume *
84121684Sume *    (E) if the ECN field in the outer header is set to CE, drop the packet.
85121684Sume *    otherwise, make no change to the ECN field in the inner header.
86121684Sume *
87121684Sume * the drop rule is for backward compatibility and protection against
88121684Sume * erasure of CE.
89121684Sume */
90121684Sume
91121684Sume/*
9255009Sshin * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation).
9355009Sshin */
9455009Sshinvoid
95169454Srwatsonip_ecn_ingress(int mode, u_int8_t *outer, const u_int8_t *inner)
9655009Sshin{
97169454Srwatson
9855009Sshin	if (!outer || !inner)
9955009Sshin		panic("NULL pointer passed to ip_ecn_ingress");
10055009Sshin
10178064Sume	*outer = *inner;
10255009Sshin	switch (mode) {
10355009Sshin	case ECN_ALLOWED:		/* ECN allowed */
104121684Sume		/*
105121684Sume		 * full-functionality: if the inner is CE, set ECT(0)
106121684Sume		 * to the outer.  otherwise, copy the ECN field.
107121684Sume		 */
108121684Sume		if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
109121684Sume			*outer &= ~IPTOS_ECN_ECT1;
11055009Sshin		break;
11155009Sshin	case ECN_FORBIDDEN:		/* ECN forbidden */
112121684Sume		/*
113121684Sume		 * limited-functionality: set not-ECT to the outer
114121684Sume		 */
115121684Sume		*outer &= ~IPTOS_ECN_MASK;
11655009Sshin		break;
11755009Sshin	case ECN_NOCARE:	/* no consideration to ECN */
11855009Sshin		break;
11955009Sshin	}
12055009Sshin}
12155009Sshin
12255009Sshin/*
12355009Sshin * modify inner ECN (TOS) field on egress operation (tunnel decapsulation).
124121684Sume * the caller should drop the packet if the return value is 0.
12555009Sshin */
126121684Sumeint
127169454Srwatsonip_ecn_egress(int mode, const u_int8_t *outer, u_int8_t *inner)
12855009Sshin{
129169454Srwatson
13055009Sshin	if (!outer || !inner)
13155009Sshin		panic("NULL pointer passed to ip_ecn_egress");
13255009Sshin
13355009Sshin	switch (mode) {
13455009Sshin	case ECN_ALLOWED:
135121684Sume		/*
136121684Sume		 * full-functionality: if the outer is CE and the inner is
137121684Sume		 * not-ECT, should drop it.  otherwise, copy CE.
138121684Sume		 */
139121684Sume		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
140121684Sume			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
141121684Sume				return (0);
142121684Sume			*inner |= IPTOS_ECN_CE;
143121684Sume		}
14455009Sshin		break;
14555009Sshin	case ECN_FORBIDDEN:		/* ECN forbidden */
146121684Sume		/*
147121684Sume		 * limited-functionality: if the outer is CE, should drop it.
148121684Sume		 * otherwise, leave the inner.
149121684Sume		 */
150121684Sume		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
151121684Sume			return (0);
152121684Sume		break;
15355009Sshin	case ECN_NOCARE:	/* no consideration to ECN */
15455009Sshin		break;
15555009Sshin	}
156121684Sume	return (1);
15755009Sshin}
15855009Sshin
15955009Sshin#ifdef INET6
16055009Sshinvoid
161169454Srwatsonip6_ecn_ingress(int mode, u_int32_t *outer, const u_int32_t *inner)
16255009Sshin{
16355009Sshin	u_int8_t outer8, inner8;
16455009Sshin
16555009Sshin	if (!outer || !inner)
16655009Sshin		panic("NULL pointer passed to ip6_ecn_ingress");
16755009Sshin
16855009Sshin	inner8 = (ntohl(*inner) >> 20) & 0xff;
16955009Sshin	ip_ecn_ingress(mode, &outer8, &inner8);
17055009Sshin	*outer &= ~htonl(0xff << 20);
17155009Sshin	*outer |= htonl((u_int32_t)outer8 << 20);
17255009Sshin}
17355009Sshin
174121684Sumeint
175169454Srwatsonip6_ecn_egress(int mode, const u_int32_t *outer, u_int32_t *inner)
17655009Sshin{
177121684Sume	u_int8_t outer8, inner8, oinner8;
17855009Sshin
17955009Sshin	if (!outer || !inner)
18055009Sshin		panic("NULL pointer passed to ip6_ecn_egress");
18155009Sshin
18255009Sshin	outer8 = (ntohl(*outer) >> 20) & 0xff;
183121684Sume	inner8 = oinner8 = (ntohl(*inner) >> 20) & 0xff;
184121684Sume	if (ip_ecn_egress(mode, &outer8, &inner8) == 0)
185121684Sume		return (0);
186121684Sume	if (inner8 != oinner8) {
187121684Sume		*inner &= ~htonl(0xff << 20);
188121684Sume		*inner |= htonl((u_int32_t)inner8 << 20);
189121684Sume	}
190121684Sume	return (1);
19155009Sshin}
19255009Sshin#endif
193