/src/openvswitch/lib/csum.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2008, 2009, 2010, 2011, 2013, 2015 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include <config.h> |
18 | | #include "csum.h" |
19 | | #include "unaligned.h" |
20 | | #include <sys/types.h> |
21 | | #include <netinet/in.h> |
22 | | |
23 | | #ifndef __CHECKER__ |
24 | | /* Returns the IP checksum of the 'n' bytes in 'data'. |
25 | | * |
26 | | * The return value has the same endianness as the data. That is, if 'data' |
27 | | * consists of a packet in network byte order, then the return value is a value |
28 | | * in network byte order, and if 'data' consists of a data structure in host |
29 | | * byte order, then the return value is in host byte order. */ |
30 | | ovs_be16 |
31 | | csum(const void *data, size_t n) |
32 | 0 | { |
33 | 0 | return csum_finish(csum_continue(0, data, n)); |
34 | 0 | } |
35 | | |
36 | | /* Adds the 'n' bytes in 'data' to the partial IP checksum 'partial' and |
37 | | * returns the updated checksum. (To start a new checksum, pass 0 for |
38 | | * 'partial'. To obtain the finished checksum, pass the return value to |
39 | | * csum_finish().) */ |
40 | | uint32_t |
41 | | csum_continue(uint32_t partial, const void *data_, size_t n) |
42 | 0 | { |
43 | 0 | const ovs_be16 *data = data_; |
44 | |
|
45 | 0 | for (; n > 1; n -= 2, data++) { |
46 | 0 | partial = csum_add16(partial, get_unaligned_be16(data)); |
47 | 0 | } |
48 | 0 | if (n) { |
49 | | #ifdef WORDS_BIGENDIAN |
50 | | partial += (*(uint8_t *) data) << 8; |
51 | | #else |
52 | 0 | partial += *(uint8_t *) data; |
53 | 0 | #endif |
54 | 0 | } |
55 | 0 | return partial; |
56 | 0 | } |
57 | | |
58 | | /* Returns the IP checksum corresponding to 'partial', which is a value updated |
59 | | * by some combination of csum_add16(), csum_add32(), and csum_continue(). |
60 | | * |
61 | | * The return value has the same endianness as the checksummed data. That is, |
62 | | * if the data consist of a packet in network byte order, then the return value |
63 | | * is a value in network byte order, and if the data are a data structure in |
64 | | * host byte order, then the return value is in host byte order. */ |
65 | | ovs_be16 |
66 | | csum_finish(uint32_t partial) |
67 | 0 | { |
68 | 0 | while (partial >> 16) { |
69 | 0 | partial = (partial & 0xffff) + (partial >> 16); |
70 | 0 | } |
71 | 0 | return ~partial; |
72 | 0 | } |
73 | | |
74 | | /* Returns the new checksum for a packet in which the checksum field previously |
75 | | * contained 'old_csum' and in which a field that contained 'old_u16' was |
76 | | * changed to contain 'new_u16'. */ |
77 | | ovs_be16 |
78 | | recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16) |
79 | 0 | { |
80 | | /* Ones-complement arithmetic is endian-independent, so this code does not |
81 | | * use htons() or ntohs(). |
82 | | * |
83 | | * See RFC 1624 for formula and explanation. */ |
84 | 0 | uint16_t hc_complement = ~old_csum; |
85 | 0 | uint16_t m_complement = ~old_u16; |
86 | 0 | uint16_t m_prime = new_u16; |
87 | 0 | uint32_t sum = hc_complement + m_complement + m_prime; |
88 | 0 | return csum_finish(sum); |
89 | 0 | } |
90 | | |
91 | | /* Returns the new checksum for a packet in which the checksum field previously |
92 | | * contained 'old_csum' and in which a field that contained 'old_u32' was |
93 | | * changed to contain 'new_u32'. */ |
94 | | ovs_be16 |
95 | | recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32) |
96 | 0 | { |
97 | 0 | return recalc_csum16(recalc_csum16(old_csum, old_u32, new_u32), |
98 | 0 | old_u32 >> 16, new_u32 >> 16); |
99 | 0 | } |
100 | | |
101 | | /* Returns the new checksum for a packet in which the checksum field previously |
102 | | * contained 'old_csum' and in which a field that contained the 6 bytes at |
103 | | * 'old_mac' was changed to contain the 6 bytes at 'new_mac'. */ |
104 | | ovs_be16 |
105 | | recalc_csum48(ovs_be16 old_csum, const struct eth_addr old_mac, |
106 | | const struct eth_addr new_mac) |
107 | 0 | { |
108 | 0 | ovs_be16 new_csum = old_csum; |
109 | |
|
110 | 0 | for (int i = 0; i < 3; ++i) { |
111 | 0 | new_csum = recalc_csum16(new_csum, old_mac.be16[i], new_mac.be16[i]); |
112 | 0 | } |
113 | |
|
114 | 0 | return new_csum; |
115 | 0 | } |
116 | | |
117 | | /* Returns the new checksum for a packet in which the checksum field previously |
118 | | * contained 'old_csum' and in which a field that contained 'old_u32[4]' was |
119 | | * changed to contain 'new_u32[4]'. */ |
120 | | ovs_be16 |
121 | | recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4], |
122 | | const struct in6_addr *new_in6) |
123 | 0 | { |
124 | 0 | ovs_be16 new_csum = old_csum; |
125 | | #ifndef s6_addr32 |
126 | | ovs_be32 new_u32[4]; |
127 | | memcpy(new_u32, new_in6, sizeof new_u32); |
128 | | #else |
129 | 0 | const ovs_be32 *new_u32 = new_in6->s6_addr32; |
130 | 0 | #endif |
131 | 0 | int i; |
132 | |
|
133 | 0 | for (i = 0; i < 4; ++i) { |
134 | 0 | new_csum = recalc_csum32(new_csum, |
135 | 0 | get_16aligned_be32(&old_u32[i]), new_u32[i]); |
136 | 0 | } |
137 | 0 | return new_csum; |
138 | 0 | } |
139 | | #else /* __CHECKER__ */ |
140 | | /* Making sparse happy with these functions also makes them unreadable, so |
141 | | * don't bother to show it their implementations. */ |
142 | | #endif |