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