1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/atomic.h> 31 #include <sys/pattr.h> 32 #include <netinet/in.h> 33 #include <netinet/ip6.h> 34 #include <inet/common.h> 35 #include <inet/ip.h> 36 #include <inet/ip6.h> 37 #include <ipp/dscpmk/dscpmk_impl.h> 38 39 /* Module to mark the ToS/DS field for a given packet */ 40 41 /* Debug level */ 42 int dscpmk_debug = 0; 43 44 /* 45 * Given a packet, this routine marks the ToS or DSCP for IPv4 and IPv6 resp. 46 * using the configured dscp_map. 47 * Note that this module does not change the ECN bits. 48 */ 49 int 50 dscpmk_process(mblk_t **mpp, dscpmk_data_t *dscpmk_data, ip_proc_t proc) 51 { 52 ipha_t *ipha; 53 ip6_t *ip6_hdr; 54 boolean_t is_v4; 55 uint8_t dscp, new_dscp; 56 mblk_t *mp; 57 58 ASSERT((mpp != NULL) && (*mpp != NULL)); 59 mp = *mpp; 60 61 /* 62 * The action module will receive an M_DATA or an M_CTL followed 63 * by an M_DATA. In the latter case skip the M_CTL. 64 */ 65 if (mp->b_datap->db_type != M_DATA) { 66 if ((mp->b_cont != NULL) && 67 (mp->b_cont->b_datap->db_type == M_DATA)) { 68 mp = mp->b_cont; 69 } else { 70 dscpmk0dbg(("dscpmk_process: no data\n")); 71 atomic_add_64(&dscpmk_data->epackets, 1); 72 return (EINVAL); 73 } 74 } 75 76 /* Pull-up needed? */ 77 if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) { 78 if (!pullupmsg(mp, IP_SIMPLE_HDR_LENGTH)) { 79 dscpmk0dbg(("dscpmk_process: pullup failed\n")); 80 atomic_add_64(&dscpmk_data->epackets, 1); 81 return (EINVAL); 82 } 83 } 84 ipha = (ipha_t *)mp->b_rptr; 85 86 /* Update global stats */ 87 atomic_add_64(&dscpmk_data->npackets, 1); 88 89 /* 90 * This should only be called for outgoing packets. For inbound packets 91 * proceed with the next action. 92 */ 93 if ((proc == IPP_LOCAL_IN) || (proc == IPP_FWD_IN)) { 94 dscpmk2dbg(("dscpmk_process: cannot mark incoming packets\n")); 95 atomic_add_64(&dscpmk_data->ipackets, 1); 96 return (0); 97 } 98 99 /* Figure out the ToS or the Traffic Class from the message */ 100 if (IPH_HDR_VERSION(ipha) == IPV4_VERSION) { 101 dscp = ipha->ipha_type_of_service; 102 is_v4 = B_TRUE; 103 } else { 104 ip6_hdr = (ip6_t *)mp->b_rptr; 105 dscp = __IPV6_TCLASS_FROM_FLOW(ip6_hdr->ip6_vcf); 106 is_v4 = B_FALSE; 107 } 108 109 /* 110 * Select the new dscp from the dscp_map after ignoring the 111 * ECN/CU from dscp (hence dscp >> 2). new_dscp will be the 112 * 6-bit DSCP value. 113 */ 114 new_dscp = dscpmk_data->dscp_map[dscp >> 2]; 115 116 /* Update stats for this new_dscp */ 117 atomic_add_64(&dscpmk_data->dscp_stats[new_dscp].npackets, 1); 118 119 /* 120 * if new_dscp is same as the original, update stats and 121 * return. 122 */ 123 if (new_dscp == (dscp >> 2)) { 124 atomic_add_64(&dscpmk_data->unchanged, 1); 125 return (0); 126 } 127 128 /* Get back the ECN/CU value from the original dscp */ 129 new_dscp = (new_dscp << 2) | (dscp & 0x3); 130 131 atomic_add_64(&dscpmk_data->changed, 1); 132 /* 133 * IPv4 : ToS structure -- RFC 791 134 * 135 * 0 1 2 3 4 5 6 7 136 * +---+---+---+---+---+---+---+---+ 137 * | IP Precd | D | T | R | 0 | 0 | 138 * | | | | | | | 139 * +---+---+---+---+---+---+---+---+ 140 * 141 * For Backward Compatability the diff serv DSCP will be mapped 142 * to the 3-bits Precedence field. DTR is not supported. Thus, 143 * the following Class Seletor CodePoints are reserved from this 144 * purpose : xxx000; where x is 0 or 1 (note the last 2 bits are 145 * 00) -- see RFC 2474. 146 */ 147 148 if (is_v4) { 149 ipha->ipha_type_of_service = new_dscp; 150 /* 151 * If the hardware supports checksumming, we don't need 152 * to do anything. 153 */ 154 if (!(mp->b_datap->db_struioun.cksum.flags & 155 HCK_IPV4_HDRCKSUM)) { 156 ipha->ipha_hdr_checksum = 0; 157 ipha->ipha_hdr_checksum = ip_csum_hdr(ipha); 158 } 159 } else { 160 161 /* 162 * IPv6 : DSCP field structure is as given -- RFC 2474 163 * 164 * 0 1 2 3 4 5 6 7 165 * +---+---+---+---+---+---+---+---+ 166 * | DSCP | CU | 167 * | | | 168 * +---+---+---+---+---+---+---+---+ 169 * 170 * CU -- Currently Unused 171 * 172 * the 32 bit vcf consists of version (4 bits), Traffic class (8 bits) 173 * and flow id (20 bits). Need to take care of Big/Little-Endianess. 174 */ 175 #ifdef _BIG_ENDIAN 176 ip6_hdr->ip6_vcf = (ip6_hdr->ip6_vcf & TCLASS_MASK) | 177 (new_dscp << 20); 178 #else 179 ip6_hdr->ip6_vcf = (ip6_hdr->ip6_vcf & TCLASS_MASK) | 180 ((new_dscp >> 4) | ((new_dscp << 12) & 0xF000)); 181 #endif 182 } 183 184 return (0); 185 } 186