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