xref: /dpdk/lib/net/rte_cksum.h (revision 21a66096bb44a4468353782c36fc85913520dc6c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 1982, 1986, 1990, 1993
3  *      The Regents of the University of California.
4  * Copyright(c) 2010-2014 Intel Corporation.
5  * Copyright(c) 2014 6WIND S.A.
6  * All rights reserved.
7  */
8 
9 #ifndef _RTE_CKSUM_H_
10 #define _RTE_CKSUM_H_
11 
12 /**
13  * @file
14  *
15  * Protocol independent checksum utilities.
16  */
17 
18 #include <stdint.h>
19 
20 #include <rte_byteorder.h>
21 #include <rte_common.h>
22 #include <rte_mbuf.h>
23 
24 #ifdef __cplusplus
25 extern "C" {
26 #endif
27 
28 
29 /**
30  * @internal Calculate a sum of all words in the buffer.
31  * Helper routine for the rte_raw_cksum().
32  *
33  * @param buf
34  *   Pointer to the buffer.
35  * @param len
36  *   Length of the buffer.
37  * @param sum
38  *   Initial value of the sum.
39  * @return
40  *   sum += Sum of all words in the buffer.
41  */
42 static inline uint32_t
43 __rte_raw_cksum(const void *buf, size_t len, uint32_t sum)
44 {
45 	const void *end;
46 
47 	for (end = RTE_PTR_ADD(buf, RTE_ALIGN_FLOOR(len, sizeof(uint16_t)));
48 	     buf != end; buf = RTE_PTR_ADD(buf, sizeof(uint16_t))) {
49 		uint16_t v;
50 
51 		memcpy(&v, buf, sizeof(uint16_t));
52 		sum += v;
53 	}
54 
55 	/* if length is odd, keeping it byte order independent */
56 	if (unlikely(len % 2)) {
57 		uint16_t left = 0;
58 
59 		memcpy(&left, end, 1);
60 		sum += left;
61 	}
62 
63 	return sum;
64 }
65 
66 /**
67  * @internal Reduce a sum to the non-complemented checksum.
68  * Helper routine for the rte_raw_cksum().
69  *
70  * @param sum
71  *   Value of the sum.
72  * @return
73  *   The non-complemented checksum.
74  */
75 static inline uint16_t
76 __rte_raw_cksum_reduce(uint32_t sum)
77 {
78 	sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
79 	sum = ((sum & 0xffff0000) >> 16) + (sum & 0xffff);
80 	return (uint16_t)sum;
81 }
82 
83 /**
84  * Process the non-complemented checksum of a buffer.
85  *
86  * @param buf
87  *   Pointer to the buffer.
88  * @param len
89  *   Length of the buffer.
90  * @return
91  *   The non-complemented checksum.
92  */
93 static inline uint16_t
94 rte_raw_cksum(const void *buf, size_t len)
95 {
96 	uint32_t sum;
97 
98 	sum = __rte_raw_cksum(buf, len, 0);
99 	return __rte_raw_cksum_reduce(sum);
100 }
101 
102 /**
103  * Compute the raw (non complemented) checksum of a packet.
104  *
105  * @param m
106  *   The pointer to the mbuf.
107  * @param off
108  *   The offset in bytes to start the checksum.
109  * @param len
110  *   The length in bytes of the data to checksum.
111  * @param cksum
112  *   A pointer to the checksum, filled on success.
113  * @return
114  *   0 on success, -1 on error (bad length or offset).
115  */
116 static inline int
117 rte_raw_cksum_mbuf(const struct rte_mbuf *m, uint32_t off, uint32_t len,
118 	uint16_t *cksum)
119 {
120 	const struct rte_mbuf *seg;
121 	const char *buf;
122 	uint32_t sum, tmp;
123 	uint32_t seglen, done;
124 
125 	/* easy case: all data in the first segment */
126 	if (off + len <= rte_pktmbuf_data_len(m)) {
127 		*cksum = rte_raw_cksum(rte_pktmbuf_mtod_offset(m,
128 				const char *, off), len);
129 		return 0;
130 	}
131 
132 	if (unlikely(off + len > rte_pktmbuf_pkt_len(m)))
133 		return -1; /* invalid params, return a dummy value */
134 
135 	/* else browse the segment to find offset */
136 	seglen = 0;
137 	for (seg = m; seg != NULL; seg = seg->next) {
138 		seglen = rte_pktmbuf_data_len(seg);
139 		if (off < seglen)
140 			break;
141 		off -= seglen;
142 	}
143 	RTE_ASSERT(seg != NULL);
144 	if (seg == NULL)
145 		return -1;
146 	seglen -= off;
147 	buf = rte_pktmbuf_mtod_offset(seg, const char *, off);
148 	if (seglen >= len) {
149 		/* all in one segment */
150 		*cksum = rte_raw_cksum(buf, len);
151 		return 0;
152 	}
153 
154 	/* hard case: process checksum of several segments */
155 	sum = 0;
156 	done = 0;
157 	for (;;) {
158 		tmp = __rte_raw_cksum(buf, seglen, 0);
159 		if (done & 1)
160 			tmp = rte_bswap16((uint16_t)tmp);
161 		sum += tmp;
162 		done += seglen;
163 		if (done == len)
164 			break;
165 		seg = seg->next;
166 		buf = rte_pktmbuf_mtod(seg, const char *);
167 		seglen = rte_pktmbuf_data_len(seg);
168 		if (seglen > len - done)
169 			seglen = len - done;
170 	}
171 
172 	*cksum = __rte_raw_cksum_reduce(sum);
173 	return 0;
174 }
175 
176 #ifdef __cplusplus
177 }
178 #endif
179 
180 #endif /* _RTE_CKSUM_H_ */
181