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 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 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 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 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