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