1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Intel Corporation 3 */ 4 5 #ifndef _IPH_H_ 6 #define _IPH_H_ 7 8 #include <rte_ip.h> 9 10 /** 11 * @file iph.h 12 * Contains functions/structures/macros to manipulate IPv4/IPv6 headers 13 * used internally by ipsec library. 14 */ 15 16 /* 17 * Move preceding (L3) headers down to remove ESP header and IV. 18 */ 19 static inline void 20 remove_esph(char *np, char *op, uint32_t hlen) 21 { 22 uint32_t i; 23 24 for (i = hlen; i-- != 0; np[i] = op[i]) 25 ; 26 } 27 28 /* 29 * Move preceding (L3) headers up to free space for ESP header and IV. 30 */ 31 static inline void 32 insert_esph(char *np, char *op, uint32_t hlen) 33 { 34 uint32_t i; 35 36 for (i = 0; i != hlen; i++) 37 np[i] = op[i]; 38 } 39 40 /* update original ip header fields for transport case */ 41 static inline int 42 update_trs_l3hdr(const struct rte_ipsec_sa *sa, void *p, uint32_t plen, 43 uint32_t l2len, uint32_t l3len, uint8_t proto) 44 { 45 int32_t rc; 46 47 /* IPv4 */ 48 if ((sa->type & RTE_IPSEC_SATP_IPV_MASK) == RTE_IPSEC_SATP_IPV4) { 49 struct rte_ipv4_hdr *v4h; 50 51 v4h = p; 52 rc = v4h->next_proto_id; 53 v4h->next_proto_id = proto; 54 v4h->total_length = rte_cpu_to_be_16(plen - l2len); 55 /* IPv6 */ 56 } else { 57 struct rte_ipv6_hdr *v6h; 58 uint8_t *p_nh; 59 60 v6h = p; 61 62 /* basic IPv6 header with no extensions */ 63 if (l3len == sizeof(struct rte_ipv6_hdr)) 64 p_nh = &v6h->proto; 65 66 /* IPv6 with extensions */ 67 else { 68 size_t ext_len; 69 int nh; 70 uint8_t *pd, *plimit; 71 72 /* locate last extension within l3len bytes */ 73 pd = (uint8_t *)p; 74 plimit = pd + l3len; 75 ext_len = sizeof(struct rte_ipv6_hdr); 76 nh = v6h->proto; 77 while (pd + ext_len < plimit) { 78 pd += ext_len; 79 nh = rte_ipv6_get_next_ext(pd, nh, &ext_len); 80 if (unlikely(nh < 0)) 81 return -EINVAL; 82 } 83 84 /* invalid l3len - extension exceeds header length */ 85 if (unlikely(pd + ext_len != plimit)) 86 return -EINVAL; 87 88 /* save last extension offset */ 89 p_nh = pd; 90 } 91 92 /* update header type; return original value */ 93 rc = *p_nh; 94 *p_nh = proto; 95 96 /* fix packet length */ 97 v6h->payload_len = rte_cpu_to_be_16(plen - l2len - 98 sizeof(*v6h)); 99 } 100 101 return rc; 102 } 103 104 /* 105 * Inline functions to get and set ipv6 packet header traffic class (TC) field. 106 */ 107 static inline uint8_t 108 get_ipv6_tc(rte_be32_t vtc_flow) 109 { 110 uint32_t v; 111 112 v = rte_be_to_cpu_32(vtc_flow); 113 return v >> RTE_IPV6_HDR_TC_SHIFT; 114 } 115 116 static inline rte_be32_t 117 set_ipv6_tc(rte_be32_t vtc_flow, uint32_t tos) 118 { 119 uint32_t v; 120 121 v = rte_cpu_to_be_32(tos << RTE_IPV6_HDR_TC_SHIFT); 122 vtc_flow &= ~rte_cpu_to_be_32(RTE_IPV6_HDR_TC_MASK); 123 124 return (v | vtc_flow); 125 } 126 127 /** 128 * Update type-of-service/traffic-class field of outbound tunnel packet. 129 * 130 * @param ref_h: reference header, for outbound it is inner header, otherwise 131 * outer header. 132 * @param update_h: header to be updated tos/tc field, for outbound it is outer 133 * header, otherwise inner header. 134 * @param tos_mask: type-of-service mask stored in sa. 135 * @param is_outh_ipv4: 1 if outer header is ipv4, 0 if it is ipv6. 136 * @param is_inner_ipv4: 1 if inner header is ipv4, 0 if it is ipv6. 137 */ 138 static inline void 139 update_outb_tun_tos(const void *ref_h, void *update_h, uint32_t tos_mask, 140 uint8_t is_outh_ipv4, uint8_t is_inh_ipv4) 141 { 142 uint8_t idx = ((is_outh_ipv4 << 1) | is_inh_ipv4); 143 struct rte_ipv4_hdr *v4out_h; 144 struct rte_ipv6_hdr *v6out_h; 145 uint32_t itp, otp; 146 147 switch (idx) { 148 case 0: /*outh ipv6, inh ipv6 */ 149 v6out_h = update_h; 150 otp = get_ipv6_tc(v6out_h->vtc_flow) & ~tos_mask; 151 itp = get_ipv6_tc(((const struct rte_ipv6_hdr *)ref_h)-> 152 vtc_flow) & tos_mask; 153 v6out_h->vtc_flow = set_ipv6_tc(v6out_h->vtc_flow, otp | itp); 154 break; 155 case 1: /*outh ipv6, inh ipv4 */ 156 v6out_h = update_h; 157 otp = get_ipv6_tc(v6out_h->vtc_flow) & ~tos_mask; 158 itp = ((const struct rte_ipv4_hdr *)ref_h)->type_of_service & 159 tos_mask; 160 v6out_h->vtc_flow = set_ipv6_tc(v6out_h->vtc_flow, otp | itp); 161 break; 162 case 2: /*outh ipv4, inh ipv6 */ 163 v4out_h = update_h; 164 otp = v4out_h->type_of_service & ~tos_mask; 165 itp = get_ipv6_tc(((const struct rte_ipv6_hdr *)ref_h)-> 166 vtc_flow) & tos_mask; 167 v4out_h->type_of_service = (otp | itp); 168 break; 169 case 3: /* outh ipv4, inh ipv4 */ 170 v4out_h = update_h; 171 otp = v4out_h->type_of_service & ~tos_mask; 172 itp = ((const struct rte_ipv4_hdr *)ref_h)->type_of_service & 173 tos_mask; 174 v4out_h->type_of_service = (otp | itp); 175 break; 176 } 177 } 178 179 /** 180 * Update type-of-service/traffic-class field of inbound tunnel packet. 181 * 182 * @param ref_h: reference header, for outbound it is inner header, otherwise 183 * outer header. 184 * @param update_h: header to be updated tos/tc field, for outbound it is outer 185 * header, otherwise inner header. 186 * @param is_outh_ipv4: 1 if outer header is ipv4, 0 if it is ipv6. 187 * @param is_inner_ipv4: 1 if inner header is ipv4, 0 if it is ipv6. 188 */ 189 static inline void 190 update_inb_tun_tos(const void *ref_h, void *update_h, 191 uint8_t is_outh_ipv4, uint8_t is_inh_ipv4) 192 { 193 uint8_t idx = ((is_outh_ipv4 << 1) | is_inh_ipv4); 194 struct rte_ipv4_hdr *v4in_h; 195 struct rte_ipv6_hdr *v6in_h; 196 uint8_t ecn_v4out, ecn_v4in; 197 uint32_t ecn_v6out, ecn_v6in; 198 199 switch (idx) { 200 case 0: /* outh ipv6, inh ipv6 */ 201 v6in_h = update_h; 202 ecn_v6out = ((const struct rte_ipv6_hdr *)ref_h)->vtc_flow & 203 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_MASK); 204 ecn_v6in = v6in_h->vtc_flow & 205 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_MASK); 206 if ((ecn_v6out == rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_CE)) && 207 (ecn_v6in != 0)) 208 v6in_h->vtc_flow |= 209 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_CE); 210 break; 211 case 1: /* outh ipv6, inh ipv4 */ 212 v4in_h = update_h; 213 ecn_v6out = ((const struct rte_ipv6_hdr *)ref_h)->vtc_flow & 214 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_MASK); 215 ecn_v4in = v4in_h->type_of_service & RTE_IPV4_HDR_ECN_MASK; 216 if ((ecn_v6out == rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_CE)) && 217 (ecn_v4in != 0)) 218 v4in_h->type_of_service |= RTE_IPV4_HDR_ECN_CE; 219 break; 220 case 2: /* outh ipv4, inh ipv6 */ 221 v6in_h = update_h; 222 ecn_v4out = ((const struct rte_ipv4_hdr *)ref_h)-> 223 type_of_service & RTE_IPV4_HDR_ECN_MASK; 224 ecn_v6in = v6in_h->vtc_flow & 225 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_MASK); 226 if (ecn_v4out == RTE_IPV4_HDR_ECN_CE && ecn_v6in != 0) 227 v6in_h->vtc_flow |= 228 rte_cpu_to_be_32(RTE_IPV6_HDR_ECN_CE); 229 break; 230 case 3: /* outh ipv4, inh ipv4 */ 231 v4in_h = update_h; 232 ecn_v4out = ((const struct rte_ipv4_hdr *)ref_h)-> 233 type_of_service & RTE_IPV4_HDR_ECN_MASK; 234 ecn_v4in = v4in_h->type_of_service & RTE_IPV4_HDR_ECN_MASK; 235 if (ecn_v4out == RTE_IPV4_HDR_ECN_CE && ecn_v4in != 0) 236 v4in_h->type_of_service |= RTE_IPV4_HDR_ECN_CE; 237 break; 238 } 239 } 240 241 /* update original and new ip header fields for tunnel case */ 242 static inline void 243 update_tun_outb_l3hdr(const struct rte_ipsec_sa *sa, void *outh, 244 const void *inh, uint32_t plen, uint32_t l2len, rte_be16_t pid) 245 { 246 struct rte_ipv4_hdr *v4h; 247 struct rte_ipv6_hdr *v6h; 248 uint8_t is_outh_ipv4; 249 250 if (sa->type & RTE_IPSEC_SATP_MODE_TUNLV4) { 251 is_outh_ipv4 = 1; 252 v4h = outh; 253 v4h->packet_id = pid; 254 v4h->total_length = rte_cpu_to_be_16(plen - l2len); 255 } else { 256 is_outh_ipv4 = 0; 257 v6h = outh; 258 v6h->payload_len = rte_cpu_to_be_16(plen - l2len - 259 sizeof(*v6h)); 260 } 261 262 if (sa->type & TUN_HDR_MSK) 263 update_outb_tun_tos(inh, outh, sa->tos_mask, is_outh_ipv4, 264 ((sa->type & RTE_IPSEC_SATP_IPV_MASK) == 265 RTE_IPSEC_SATP_IPV4)); 266 } 267 268 static inline void 269 update_tun_inb_l3hdr(const struct rte_ipsec_sa *sa, const void *outh, 270 void *inh) 271 { 272 if (sa->type & TUN_HDR_MSK) 273 update_inb_tun_tos(outh, inh, 274 ((sa->type & RTE_IPSEC_SATP_MODE_TUNLV4) != 0), 275 ((sa->type & RTE_IPSEC_SATP_IPV_MASK) == 276 RTE_IPSEC_SATP_IPV4)); 277 } 278 279 #endif /* _IPH_H_ */ 280