Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2015 Cisco and/or its affiliates. |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at: |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | /* |
| 16 | * ip4/ip_checksum.c: ip/tcp/udp checksums |
| 17 | * |
| 18 | * Copyright (c) 2008 Eliot Dresselhaus |
| 19 | * |
| 20 | * Permission is hereby granted, free of charge, to any person obtaining |
| 21 | * a copy of this software and associated documentation files (the |
| 22 | * "Software"), to deal in the Software without restriction, including |
| 23 | * without limitation the rights to use, copy, modify, merge, publish, |
| 24 | * distribute, sublicense, and/or sell copies of the Software, and to |
| 25 | * permit persons to whom the Software is furnished to do so, subject to |
| 26 | * the following conditions: |
| 27 | * |
| 28 | * The above copyright notice and this permission notice shall be |
| 29 | * included in all copies or substantial portions of the Software. |
| 30 | * |
| 31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 32 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 33 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 34 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 35 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 36 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 37 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 38 | */ |
| 39 | |
| 40 | #include <vnet/ip/ip.h> |
| 41 | |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 42 | static ip_csum_t |
| 43 | _ip_incremental_checksum (ip_csum_t sum, void *_data, uword n_bytes) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 44 | { |
| 45 | uword data = pointer_to_uword (_data); |
| 46 | ip_csum_t sum0, sum1; |
| 47 | |
| 48 | sum0 = 0; |
| 49 | sum1 = sum; |
| 50 | |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 51 | /* |
| 52 | * Align pointer to 64 bits. The ip checksum is a 16-bit |
| 53 | * one's complememt sum. It's impractical to optimize |
| 54 | * the calculation if the incoming address is odd. |
| 55 | */ |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 56 | #define _(t) \ |
| 57 | do { \ |
| 58 | if (n_bytes >= sizeof (t) \ |
| 59 | && sizeof (t) < sizeof (ip_csum_t) \ |
| 60 | && (data % (2 * sizeof (t))) != 0) \ |
| 61 | { \ |
| 62 | sum0 += * uword_to_pointer (data, t *); \ |
| 63 | data += sizeof (t); \ |
| 64 | n_bytes -= sizeof (t); \ |
| 65 | } \ |
| 66 | } while (0) |
| 67 | |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 68 | if (PREDICT_TRUE ((data & 1) == 0)) |
| 69 | { |
| 70 | _(u16); |
| 71 | if (BITS (ip_csum_t) > 32) |
| 72 | _(u32); |
| 73 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 74 | #undef _ |
| 75 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 76 | { |
| 77 | ip_csum_t *d = uword_to_pointer (data, ip_csum_t *); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 78 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 79 | while (n_bytes >= 2 * sizeof (d[0])) |
| 80 | { |
| 81 | sum0 = ip_csum_with_carry (sum0, d[0]); |
| 82 | sum1 = ip_csum_with_carry (sum1, d[1]); |
| 83 | d += 2; |
| 84 | n_bytes -= 2 * sizeof (d[0]); |
| 85 | } |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 86 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 87 | data = pointer_to_uword (d); |
| 88 | } |
| 89 | |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 90 | #define _(t) \ |
| 91 | do { \ |
| 92 | if (n_bytes >= sizeof (t) && sizeof (t) <= sizeof (ip_csum_t)) \ |
| 93 | { \ |
| 94 | sum0 = ip_csum_with_carry (sum0, * uword_to_pointer (data, t *)); \ |
| 95 | data += sizeof (t); \ |
| 96 | n_bytes -= sizeof (t); \ |
| 97 | } \ |
| 98 | } while (0) |
| 99 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 100 | if (BITS (ip_csum_t) > 32) |
| 101 | _(u64); |
| 102 | _(u32); |
| 103 | _(u16); |
| 104 | _(u8); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 105 | |
| 106 | #undef _ |
| 107 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 108 | /* Combine even and odd sums. */ |
| 109 | sum0 = ip_csum_with_carry (sum0, sum1); |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 110 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 111 | return sum0; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 112 | } |
| 113 | |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 114 | /* |
| 115 | * Note: the tcp / udp checksum calculation is performance critical |
| 116 | * [e.g. when NIC h/w offload is not available], |
| 117 | * so it's worth producing architecture-dependent code. |
| 118 | * |
| 119 | * ip_incremental_checksum() is an always-inlined static |
| 120 | * function which uses the function pointer we set up in |
| 121 | * ip_checksum_init(). |
| 122 | */ |
| 123 | #if CLIB_DEBUG > 0 |
| 124 | #define IP_INCREMENTAL_CHECKSUM_CLONE_TEMPLATE(arch, fn) |
| 125 | #define IP_INCREMENTAL_CHECKSUM_MULTIARCH_CLONE(fn) |
| 126 | #else |
| 127 | #define IP_INCREMENTAL_CHECKSUM_CLONE_TEMPLATE(arch, fn, tgt) \ |
| 128 | uword \ |
| 129 | __attribute__ ((flatten)) \ |
| 130 | __attribute__ ((target (tgt))) \ |
| 131 | CLIB_CPU_OPTIMIZED \ |
| 132 | fn ## _ ## arch (ip_csum_t sum, \ |
| 133 | void *_data, \ |
| 134 | uword n_bytes) \ |
| 135 | { return fn (sum, _data, n_bytes); } |
| 136 | |
| 137 | #define IP_INCREMENTAL_CHECKSUM_MULTIARCH_CLONE(fn) \ |
| 138 | foreach_march_variant(IP_INCREMENTAL_CHECKSUM_CLONE_TEMPLATE,fn) |
| 139 | #endif |
| 140 | |
| 141 | IP_INCREMENTAL_CHECKSUM_MULTIARCH_CLONE (_ip_incremental_checksum); |
| 142 | |
| 143 | CLIB_MULTIARCH_SELECT_FN (_ip_incremental_checksum, static inline); |
| 144 | |
| 145 | ip_csum_t (*vnet_incremental_checksum_fp) (ip_csum_t, void *, uword); |
| 146 | |
| 147 | static clib_error_t * |
| 148 | ip_checksum_init (vlib_main_t * vm) |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 149 | { |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 150 | vnet_incremental_checksum_fp = _ip_incremental_checksum_multiarch_select (); |
| 151 | return 0; |
Ed Warnicke | cb9cada | 2015-12-08 15:45:58 -0700 | [diff] [blame] | 152 | } |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 153 | |
Dave Barach | c6215d9 | 2018-06-14 18:05:30 -0400 | [diff] [blame^] | 154 | VLIB_INIT_FUNCTION (ip_checksum_init); |
| 155 | |
| 156 | #if CLIB_DEBUG > 0 |
| 157 | |
| 158 | static const char test_pkt[] = { |
| 159 | 0x45, 0x00, 0x00, 0x3c, 0x5d, 0x6f, 0x40, 0x00, |
| 160 | 0x40, 0x06, 0x3f, 0x6b, 0x0a, 0x76, 0x72, 0x44, |
| 161 | 0x0a, 0x56, 0x16, 0xd2, |
| 162 | }; |
| 163 | |
| 164 | static clib_error_t * |
| 165 | test_ip_checksum_fn (vlib_main_t * vm, |
| 166 | unformat_input_t * input, vlib_cli_command_t * cmd) |
| 167 | { |
| 168 | u16 csum; |
| 169 | ip4_header_t *hp; |
| 170 | u8 *align_test = 0; |
| 171 | int offset; |
| 172 | |
| 173 | vec_validate (align_test, ARRAY_LEN (test_pkt) + 7); |
| 174 | |
| 175 | for (offset = 0; offset < 8; offset++) |
| 176 | { |
| 177 | memcpy (align_test + offset, test_pkt, ARRAY_LEN (test_pkt)); |
| 178 | |
| 179 | hp = (ip4_header_t *) (align_test + offset); |
| 180 | csum = ip4_header_checksum (hp); |
| 181 | |
| 182 | vlib_cli_output (vm, "offset %d checksum %u expected result 27455", |
| 183 | offset, (u32) csum); |
| 184 | } |
| 185 | |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | /* *INDENT-OFF* */ |
| 190 | VLIB_CLI_COMMAND (test_checksum, static) = |
| 191 | { |
| 192 | .path = "test ip checksum", |
| 193 | .short_help = "test ip checksum", |
| 194 | .function = test_ip_checksum_fn, |
| 195 | }; |
| 196 | /* *INDENT-ON* */ |
| 197 | |
| 198 | #endif /* CLIB_DEBUG */ |
| 199 | |
Dave Barach | d7cb1b5 | 2016-12-09 09:52:16 -0500 | [diff] [blame] | 200 | /* |
| 201 | * fd.io coding-style-patch-verification: ON |
| 202 | * |
| 203 | * Local Variables: |
| 204 | * eval: (c-set-style "gnu") |
| 205 | * End: |
| 206 | */ |