xref: /src/sys/netinet/ip_ecn.c (revision 32a462ba9cdc8a927c1aba5d9bff9d16d367d7da) !
1 /*	$KAME: ip_ecn.c,v 1.12 2002/01/07 11:34:47 kjc Exp $	*/
2 
3 /*-
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Copyright (C) 1999 WIDE Project.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the project nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #include "opt_inet.h"
36 #include "opt_inet6.h"
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/mbuf.h>
41 #include <sys/errno.h>
42 
43 #include <netinet/in.h>
44 #include <netinet/in_systm.h>
45 #include <netinet/ip.h>
46 #ifdef INET6
47 #include <netinet/ip6.h>
48 #endif
49 
50 #include <netinet/ip_ecn.h>
51 #ifdef INET6
52 #include <netinet6/ip6_ecn.h>
53 #endif
54 
55 /*
56  * ECN and TOS (or TCLASS) processing rules at tunnel encapsulation and
57  * decapsulation from RFC6040:
58  *
59  *                      Outer Hdr at                 Inner Hdr at
60  *                      Encapsulator                 Decapsulator
61  *   Header fields:     --------------------         ------------
62  *     DS Field         copied from inner hdr        no change
63  *     ECN Field        constructed by (I)           constructed by (E)
64  *
65  * ECN_ALLOWED (normal mode):
66  *    (I) copy the ECN field to the outer header.
67  *
68  *    (E) if the ECN field in the outer header is set to CE and the ECN
69  *    field of the inner header is not-ECT, drop the packet.
70  *    If the ECN field in the inner header is set to ECT(0) and the ECN
71  *    field in the outer header is set to ECT(1), copy ECT(1) to
72  *    the inner header. If the ECN field in the inner header is set
73  *    to ECT(0) or ECT(1) and the ECN field in the outer header is set to
74  *    CE, copy CE to the inner header.
75  *    Otherwise, make no change to the inner header. This behaviour can be
76  *    summarized in the table below:
77  *
78  *                      Outer Header at Decapsulator
79  *               +---------+------------+------------+------------+
80  *               | Not-ECT | ECT(0)     | ECT(1)     |     CE     |
81  *    Inner Hdr: +---------+------------+------------+------------+
82  *      Not-ECT  | Not-ECT |Not-ECT(!!!)|Not-ECT(!!!)| <drop>(!!!)|
83  *       ECT(0)  |  ECT(0) | ECT(0)     | ECT(1)     |     CE     |
84  *       ECT(1)  |  ECT(1) | ECT(1) (!) | ECT(1)     |     CE     |
85  *         CE    |      CE |     CE     |     CE(!!!)|     CE     |
86  *               +---------+------------+------------+------------+
87  *
88  * ECN_COMPLETE (normal mode with security log):
89  *    certain combinations indicated in table by '(!!!)' or '(!)',
90  *    where '(!!!)' means the combination always potentially dangerous which
91  *    returns 3, while '(!)' means possibly dangerous in which returns 2.
92  *    These combinations are unsed by previous ECN tunneling specifications
93  *    and could be logged. Also, in case of more dangerous ones, the
94  *    decapsulator SHOULD log the event and MAY also raise an alarm.
95  *
96  *    Note: Caller SHOULD use rate-limited alarms so that the anomalous
97  *    combinations will not amplify into a flood of alarm messages.
98  *    Also, it MUST be possible to suppress alarms or logging.
99  *
100  * ECN_FORBIDDEN (compatibility mode):
101  *    (I) set the ECN field to not-ECT in the outer header.
102  *
103  *    (E) if the ECN field in the outer header is set to CE, drop the packet.
104  *    otherwise, make no change to the ECN field in the inner header.
105  *
106  * the drop rule is for backward compatibility and protection against
107  * erasure of CE.
108  */
109 
110 /*
111  * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation).
112  */
113 void
ip_ecn_ingress(int mode,uint8_t * outer,const uint8_t * inner)114 ip_ecn_ingress(int mode, uint8_t *outer, const uint8_t *inner)
115 {
116 
117 	KASSERT(outer != NULL && inner != NULL,
118 	    ("NULL pointer passed to %s", __func__));
119 
120 	*outer = *inner;
121 	switch (mode) {
122 	case ECN_COMPLETE:
123 	case ECN_ALLOWED:
124 		/* normal mode: always copy the ECN field. */
125 		break;
126 
127 	case ECN_FORBIDDEN:
128 		/* compatibility mode: set not-ECT to the outer */
129 		*outer &= ~IPTOS_ECN_MASK;
130 		break;
131 
132 	case ECN_NOCARE:
133 		break;
134 	}
135 }
136 
137 /*
138  * modify inner ECN (TOS) field on egress operation (tunnel decapsulation).
139  * the caller should drop the packet if the return value is 0.
140  */
141 int
ip_ecn_egress(int mode,const uint8_t * outer,uint8_t * inner)142 ip_ecn_egress(int mode, const uint8_t *outer, uint8_t *inner)
143 {
144 
145 	KASSERT(outer != NULL && inner != NULL,
146 	    ("NULL pointer passed to %s", __func__));
147 
148 	switch (mode) {
149 	case ECN_COMPLETE:
150 		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0) {
151 			/* if the outer is ECT(0) and inner is ECT(1) raise a warning */
152 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1)
153 				return (ECN_WARN);
154 			/* if the inner is not-ECT and outer is ECT(0) raise an alarm */
155 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
156 				return (ECN_ALARM);
157 			return (ECN_SUCCESS);
158 		} else if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1) {
159 			/* if the outer is ECT(1) and inner is CE or ECT(1), raise an alarm */
160 			if (((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_CE) ||
161 			    ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT))
162 				return (ECN_ALARM);
163 			/* if the outer is ECT(1) and inner is ECT(0), copy ECT(1) */
164 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0)
165 				*inner = IPTOS_ECN_ECT1;
166 			return (ECN_SUCCESS);
167 		}
168 		/* fallthrough */
169 	case ECN_ALLOWED:
170 		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
171 			/* if the outer is CE and the inner is not-ECT, drop it. */
172 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT)
173 				return (ECN_DROP);
174 			/* otherwise, copy CE */
175 			*inner |= IPTOS_ECN_CE;
176 		} else if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_ECT1) {
177 			/* if the outer is ECT(1) and inner is ECT(0), copy ECT(1) */
178 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_ECT0)
179 				*inner = IPTOS_ECN_ECT1;
180 		}
181 		break;
182 
183 	case ECN_FORBIDDEN:
184 		/*
185 		 * compatibility mode: if the outer is CE, should drop it.
186 		 * otherwise, leave the inner.
187 		 */
188 		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
189 			return (ECN_DROP);
190 		break;
191 
192 	case ECN_NOCARE:
193 		break;
194 	}
195 	return (ECN_SUCCESS);
196 }
197 
198 #ifdef INET6
199 void
ip6_ecn_ingress(int mode,uint32_t * outer,const uint32_t * inner)200 ip6_ecn_ingress(int mode, uint32_t *outer, const uint32_t *inner)
201 {
202 	uint8_t outer8, inner8;
203 
204 	KASSERT(outer != NULL && inner != NULL,
205 	    ("NULL pointer passed to %s", __func__));
206 
207 	inner8 = (ntohl(*inner) >> IPV6_FLOWLABEL_LEN) & 0xff;
208 	ip_ecn_ingress(mode, &outer8, &inner8);
209 	*outer &= ~htonl(0xff << IPV6_FLOWLABEL_LEN);
210 	*outer |= htonl((uint32_t)outer8 << IPV6_FLOWLABEL_LEN);
211 }
212 
213 int
ip6_ecn_egress(int mode,const uint32_t * outer,uint32_t * inner)214 ip6_ecn_egress(int mode, const uint32_t *outer, uint32_t *inner)
215 {
216 	uint8_t outer8, inner8, oinner8;
217 	int ret;
218 
219 	KASSERT(outer != NULL && inner != NULL,
220 	    ("NULL pointer passed to %s", __func__));
221 
222 	outer8 = (ntohl(*outer) >> IPV6_FLOWLABEL_LEN) & 0xff;
223 	inner8 = oinner8 = (ntohl(*inner) >> IPV6_FLOWLABEL_LEN) & 0xff;
224 
225 	ret = ip_ecn_egress(mode, &outer8, &inner8);
226 	if (ret == ECN_DROP)
227 		return (ECN_DROP);
228 	if (inner8 != oinner8) {
229 		*inner &= ~htonl(0xff << IPV6_FLOWLABEL_LEN);
230 		*inner |= htonl((uint32_t)inner8 << IPV6_FLOWLABEL_LEN);
231 	}
232 	return (ret);
233 }
234 #endif
235