xref: /openbsd/sys/netinet/ip_ecn.c (revision 09467b48)
1 /*	$OpenBSD: ip_ecn.c,v 1.9 2018/11/14 23:55:04 dlg Exp $	*/
2 /*	$KAME: ip_ecn.c,v 1.9 2000/10/01 12:44:48 itojun Exp $	*/
3 
4 /*
5  * Copyright (C) 1999 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 /*
34  * ECN consideration on tunnel ingress/egress operation.
35  * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/mbuf.h>
41 
42 #include <netinet/in.h>
43 #include <netinet/ip.h>
44 
45 #ifdef INET6
46 #include <netinet/ip6.h>
47 #endif
48 
49 #include <netinet/ip_ecn.h>
50 
51 /*
52  * ECN and TOS (or TCLASS) processing rules at tunnel encapsulation and
53  * decapsulation from RFC3168:
54  *
55  *                      Outer Hdr at                 Inner Hdr at
56  *                      Encapsulator                 Decapsulator
57  *   Header fields:     --------------------         ------------
58  *     DS Field         copied from inner hdr        no change
59  *     ECN Field        constructed by (I)           constructed by (E)
60  *
61  * ECN_ALLOWED (full functionality):
62  *    (I) if the ECN field in the inner header is set to CE, then set the
63  *    ECN field in the outer header to ECT(0).
64  *    otherwise, copy the ECN field to the outer header.
65  *
66  *    (E) if the ECN field in the outer header is set to CE and the ECN
67  *    field of the inner header is not-ECT, drop the packet.
68  *    if the ECN field in the inner header is set to ECT(0) or ECT(1)
69  *    and the ECN field in the outer header is set to CE, then copy CE to
70  *    the inner header.  otherwise, make no change to the inner header.
71  *
72  * ECN_FORBIDDEN (limited functionality):
73  *    (I) set the ECN field to not-ECT in the outer header.
74  *
75  *    (E) if the ECN field in the outer header is set to CE, drop the packet.
76  *    otherwise, make no change to the ECN field in the inner header.
77  *
78  * the drop rule is for backward compatibility and protection against
79  * erasure of CE.
80  */
81 
82 /*
83  * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation).
84  * call it after you've done the default initialization/copy for the outer.
85  */
86 void
87 ip_ecn_ingress(int mode, u_int8_t *outer, u_int8_t *inner)
88 {
89 	if (!outer || !inner)
90 		panic("NULL pointer passed to ip_ecn_ingress");
91 
92 	*outer = *inner;
93 	switch (mode) {
94 	case ECN_ALLOWED:		/* ECN allowed */
95 	case ECN_ALLOWED_IPSEC:
96 		/*
97 		 * full-functionality: if the inner is CE, set ECT(0)
98 		 * to the outer.  otherwise, copy the ECN field.
99 		 */
100 		if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
101 			*outer &= ~IPTOS_ECN_ECT1;
102 		break;
103 	case ECN_FORBIDDEN:		/* ECN forbidden */
104 		/*
105 		 * limited-functionality: set not-ECT to the outer
106 		 */
107 		*outer &= ~IPTOS_ECN_MASK;
108 		break;
109 	case ECN_NOCARE:	/* no consideration to ECN */
110 		break;
111 	}
112 }
113 
114 /*
115  * modify inner ECN (TOS) field on egress operation (tunnel decapsulation).
116  * call it after you've done the default initialization/copy for the inner.
117  * the caller should drop the packet if the return value is 0.
118  */
119 int
120 ip_ecn_egress(int mode, u_int8_t *outer, u_int8_t *inner)
121 {
122 	if (!outer || !inner)
123 		panic("NULL pointer passed to ip_ecn_egress");
124 
125 	switch (mode) {
126 	case ECN_ALLOWED:
127 	case ECN_ALLOWED_IPSEC:
128 		/*
129 		 * full-functionality: if the outer is CE and the inner is
130 		 * not-ECT, should drop it.  otherwise, copy CE.
131 		 * However, according to RFC4301, we should just leave the
132 		 * inner as non-ECT for IPsec.
133 		 */
134 		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE) {
135 			if ((*inner & IPTOS_ECN_MASK) == IPTOS_ECN_NOTECT) {
136 				if (mode == ECN_ALLOWED_IPSEC)
137 					return (1);
138 				else
139 					return (0);
140 			}
141 			*inner |= IPTOS_ECN_CE;
142 		}
143 		break;
144 	case ECN_FORBIDDEN:		/* ECN forbidden */
145 		/*
146 		 * limited-functionality: if the outer is CE, should drop it.
147 		 * otherwise, leave the inner.
148 		 */
149 		if ((*outer & IPTOS_ECN_MASK) == IPTOS_ECN_CE)
150 			return (0);
151 		break;
152 	case ECN_NOCARE:	/* no consideration to ECN */
153 		break;
154 	}
155 	return (1);
156 }
157 
158 /*
159  * Patch the checksum with the difference between the old and new tos.
160  * The patching is based on what pf_patch_8() and pf_cksum_fixkup() do,
161  * but they're in pf, so we can't rely on them being available.
162  */
163 void
164 ip_tos_patch(struct ip *ip, uint8_t tos)
165 {
166 	uint16_t old;
167 	uint16_t new;
168 	uint32_t x;
169 
170 	old = htons(ip->ip_tos);
171 	new = htons(tos);
172 
173 	ip->ip_tos = tos;
174 
175 	x = ip->ip_sum + old - new;
176 	ip->ip_sum = (x) + (x >> 16);
177 }
178