xref: /dpdk/lib/ipsec/iph.h (revision 99a2dd955fba6e4cc23b77d590a033650ced9c45)
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
remove_esph(char * np,char * op,uint32_t hlen)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
insert_esph(char * np,char * op,uint32_t hlen)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
update_trs_l3hdr(const struct rte_ipsec_sa * sa,void * p,uint32_t plen,uint32_t l2len,uint32_t l3len,uint8_t proto)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
get_ipv6_tc(rte_be32_t vtc_flow)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
set_ipv6_tc(rte_be32_t vtc_flow,uint32_t tos)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
update_outb_tun_tos(const void * ref_h,void * update_h,uint32_t tos_mask,uint8_t is_outh_ipv4,uint8_t is_inh_ipv4)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
update_inb_tun_tos(const void * ref_h,void * update_h,uint8_t is_outh_ipv4,uint8_t is_inh_ipv4)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
update_tun_outb_l3hdr(const struct rte_ipsec_sa * sa,void * outh,const void * inh,uint32_t plen,uint32_t l2len,rte_be16_t pid)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
update_tun_inb_l3hdr(const struct rte_ipsec_sa * sa,const void * outh,void * inh)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