├── LICENSE ├── Makefile ├── README.md ├── quic_lb.c ├── quic_lb.h ├── quic_lb_cla.pdf ├── quic_lb_test.c └── quic_lb_types.h /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | quic_lb_test: quic_lb.c quic_lb_test.c 2 | gcc -g -o lb_test -D NOBIGIP quic_lb.c quic_lb_test.c -lcrypto -lssl -I. -lm 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QUIC-LB 2 | === 3 | 4 | QUIC-LB is a set of common algorithms that allow a QUIC server to incorporate 5 | a "Server ID" in its Connection ID, and for a low-state load balancer to 6 | extract that connection ID for routing purposes. 7 | 8 | The QUIC Working Group [Editor's draft] 9 | (https://quicwg.org/load-balancers/draft-ietf-quic-load-balancers.html) 10 | specifies the design. 11 | 12 | Building QUIC-LB 13 | --- 14 | 15 | 1. Clone this project from git. 16 | 2. Install openssl 17 | 3. % make 18 | 19 | Guide to files 20 | --- 21 | This project provides a library for load balancer and QUIC server 22 | implementations to generate and decode compliant connection IDs, given a 23 | consistent configuration. 24 | 25 | This library is contained in quic_lb.h and quic_lb.c. As this code was 26 | originally developed for a proprietary microkernel environment, quic_lb_types.h 27 | is also required to compile in conventional Linux. 28 | 29 | quic_lb_test.c creates a series of valid configurations, generates connection 30 | IDs for those configurations, and then extracts the server ID to check that it 31 | is recoverable. The Makefile compiles this test code, generating a full report 32 | of configuration parameters. 33 | 34 | Contribution Guidelines 35 | --- 36 | 37 | Contributions are welcome. However, F5 corporate policy requires contributors to 38 | complete the Contributor License Agreement and email it to a.macedonia@f5.com. 39 | The CLA is in the root directory of this repo. 40 | -------------------------------------------------------------------------------- /quic_lb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 F5 Networks Inc. 3 | * This source code is subject to the terms of the Apache License, 4 | * version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 5 | */ 6 | #include 7 | #include 8 | #include "quic_lb.h" 9 | #ifndef NOBIGIP 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | #define QUIC_LB_TUPLE_ROUTE 0xc0 21 | #define QUIC_LB_USABLE_BYTES (QUIC_LB_MAX_CID_LEN - 1) 22 | 23 | /* Parameter limits */ 24 | #define QUIC_LB_BLOCK_SIZE 16 25 | #define QUIC_LB_NONCE_MIN 4 26 | 27 | typedef UINT8 QUIC_LB_BLOCK[QUIC_LB_BLOCK_SIZE]; 28 | 29 | #define CIDL(cfg) ((cfg)->sidl + (cfg)->nonce_len + 1) 30 | 31 | struct quic_lb_lb_ctx { 32 | UINT8 cr : 2; 33 | UINT8 encode_length : 1; 34 | enum quic_lb_alg alg : 5; 35 | size_t sidl; 36 | size_t nonce_len; 37 | void *crypto_ctx; 38 | 39 | int (*decrypt)(void *ctx, void *cid, void *sid, size_t *cid_len); 40 | }; 41 | 42 | struct quic_lb_server_ctx { 43 | UINT8 cr : 2; 44 | UINT8 encode_length : 1; 45 | enum quic_lb_alg alg : 5; 46 | size_t sidl; 47 | size_t nonce_len; 48 | void *crypto_ctx; 49 | 50 | UINT8 sid[QUIC_LB_USABLE_BYTES]; 51 | void (*encrypt)(void *ctx, void *cid); 52 | UINT128 nonce_ctr; /* counter for nonce */ 53 | }; 54 | 55 | static void 56 | quic_lb_set_first_octet(struct quic_lb_server_ctx *ctx, UINT8 *ptr) 57 | { 58 | if (ctx->encode_length) { 59 | *ptr = CIDL(ctx) - 1; 60 | } else { 61 | rndset(ptr, RND_PSEUDO, 1); 62 | } 63 | *ptr &= 0x3f; 64 | *ptr |= (ctx->cr << 6); 65 | } 66 | 67 | /* Algorithm-specific functions */ 68 | static void 69 | quic_lb_pcid_encrypt(void *ctx, void *cid) 70 | { 71 | struct quic_lb_server_ctx *cfg = ctx; 72 | UINT8 *ptr = cid; 73 | 74 | quic_lb_set_first_octet(cfg, ptr); 75 | ptr++; 76 | memcpy(ptr, cfg->sid, cfg->sidl); 77 | ptr += cfg->sidl; 78 | rndset(ptr, RND_PSEUDO, cfg->nonce_len); 79 | return; 80 | } 81 | 82 | static int 83 | quic_lb_pcid_decrypt(void *ctx, void *cid, void *sid, size_t *cid_len) 84 | { 85 | struct quic_lb_lb_ctx *cfg = ctx; 86 | UINT8 *ptr = cid; 87 | 88 | if (cfg->encode_length) { 89 | *cid_len = (size_t)(*ptr & 0x3f) + 1; 90 | } 91 | ptr++; 92 | memcpy(sid, ptr, cfg->sidl); 93 | return cfg->sidl; 94 | } 95 | 96 | static void 97 | quic_lb_truncate_left(QUIC_LB_BLOCK left, QUIC_LB_BLOCK block, size_t inlen) 98 | { 99 | // Copy what we can evenly 100 | memmove(left, block, inlen / 2); 101 | 102 | if ((inlen % 2) != 0) { 103 | size_t unfriendly = (inlen / 2); 104 | left[unfriendly] = (block[unfriendly] & 0xf0); 105 | } 106 | } 107 | 108 | static void 109 | quic_lb_truncate_right(QUIC_LB_BLOCK right, QUIC_LB_BLOCK block, size_t inlen) 110 | { 111 | if ((inlen % 2) == 0) { 112 | memmove(right, block + (inlen / 2), inlen / 2); 113 | } 114 | else { 115 | memmove(right, block + (inlen / 2), (inlen / 2) + 1); 116 | size_t unfriendly = (inlen / 2); 117 | right[0] = (block[unfriendly] & 0x0f); 118 | } 119 | } 120 | 121 | static void 122 | quic_lb_encrypt_round_left(void *ctx, QUIC_LB_BLOCK left, QUIC_LB_BLOCK right, 123 | QUIC_LB_BLOCK ciphertext, size_t block_id) 124 | { 125 | // This is actually sometimes an lb_ctx, so need to better abstract the types 126 | struct quic_lb_server_ctx *cfg = ctx; 127 | size_t total_len = cfg->sidl + cfg->nonce_len; 128 | size_t sidl_copy_size = ceilf(total_len / 2.0); 129 | int ct_len = 0; 130 | 131 | QUIC_LB_BLOCK scratch = { 0 }; 132 | 133 | memset(ciphertext, '\0', QUIC_LB_BLOCK_SIZE); 134 | 135 | memcpy(ciphertext, left, sidl_copy_size); 136 | ciphertext[QUIC_LB_BLOCK_SIZE - 1] = (UINT8)block_id; 137 | 138 | if (EVP_EncryptUpdate(cfg->crypto_ctx, ciphertext, &ct_len, ciphertext, 139 | QUIC_LB_BLOCK_SIZE) != 1) { 140 | printf("EVP_EncryptUpdate (AES) failed.\n"); 141 | return; 142 | } 143 | 144 | quic_lb_truncate_right(scratch, ciphertext + (QUIC_LB_BLOCK_SIZE - total_len), total_len); 145 | 146 | for (int i = 0; i < total_len; i++) { 147 | right[i] ^= scratch[i]; 148 | } 149 | } 150 | 151 | static void 152 | quic_lb_encrypt_round_right(void *ctx, QUIC_LB_BLOCK left, QUIC_LB_BLOCK right, 153 | QUIC_LB_BLOCK ciphertext, size_t block_id) 154 | { 155 | // This is actually sometimes an lb_ctx, so need to better abstract the types 156 | struct quic_lb_server_ctx *cfg = ctx; 157 | size_t total_len = cfg->sidl + cfg->nonce_len; 158 | size_t sidl_copy_size = ceilf(total_len / 2.0); 159 | int ct_len = 0; 160 | 161 | QUIC_LB_BLOCK scratch = { 0 }; 162 | 163 | memset(ciphertext, '\0', QUIC_LB_BLOCK_SIZE); 164 | 165 | memcpy(ciphertext + (QUIC_LB_BLOCK_SIZE - sidl_copy_size), right, sidl_copy_size); 166 | ciphertext[0] = (UINT8)block_id; 167 | 168 | if (EVP_EncryptUpdate(cfg->crypto_ctx, ciphertext, &ct_len, ciphertext, 169 | QUIC_LB_BLOCK_SIZE) != 1) { 170 | printf("EVP_EncryptUpdate (AES) failed.\n"); 171 | return; 172 | } 173 | 174 | quic_lb_truncate_left(scratch, ciphertext, total_len); 175 | 176 | for (int i = 0; i < total_len; i++) { 177 | left[i] ^= scratch[i]; 178 | } 179 | } 180 | 181 | static void 182 | quic_lb_scid_encrypt(void *ctx, void *cid) 183 | { 184 | struct quic_lb_server_ctx *cfg = ctx; 185 | 186 | size_t total_len = cfg->sidl + cfg->nonce_len; 187 | size_t sidl_safe_len = ceilf(total_len / 2.0); 188 | 189 | QUIC_LB_BLOCK sid = { 0 }; 190 | 191 | // ciphertext store correspoding rounds for each stage 192 | QUIC_LB_BLOCK ciphertext = { 0 }; 193 | 194 | // left_N, right_N store corresponding variables from each stage 195 | QUIC_LB_BLOCK left_N = { 0 }, 196 | right_N = { 0 }; 197 | 198 | if (cfg->nonce_ctr > ((((UINT128)0x1 << cfg->nonce_len * 8)) - 1)) { 199 | /* Nonce is not big enough for unique CIDs */ 200 | goto err; 201 | } 202 | 203 | // XXX: memset(cid, '\0', sidl); If this isn't initialized, bad things 204 | // happen. We should initialize it for the caller but do we know how large 205 | // it is? 206 | 207 | memcpy(sid, cfg->sid, cfg->sidl); 208 | memcpy(sid + cfg->sidl, &cfg->nonce_ctr, cfg->nonce_len); /* Host order! */ 209 | 210 | quic_lb_truncate_left(left_N, sid, total_len); 211 | quic_lb_truncate_right(right_N, sid, total_len); 212 | 213 | quic_lb_encrypt_round_left(ctx, left_N, right_N, ciphertext, 1); 214 | quic_lb_encrypt_round_right(ctx, left_N, right_N, ciphertext, 2); 215 | quic_lb_encrypt_round_left(ctx, left_N, right_N, ciphertext, 3); 216 | quic_lb_encrypt_round_right(ctx, left_N, right_N, ciphertext, 4); 217 | 218 | if ((total_len % 2) == 0) { 219 | memcpy((UINT8 *)cid + 1, left_N, sidl_safe_len); 220 | memcpy((UINT8 *)cid + 1 + sidl_safe_len, right_N, sidl_safe_len); 221 | } 222 | else { 223 | memcpy((UINT8 *)cid + 1 + (total_len - sidl_safe_len), right_N, sidl_safe_len); 224 | memcpy((UINT8 *)cid + 1, left_N, sidl_safe_len - 1); 225 | ((UINT8 *)cid)[sidl_safe_len] |= left_N[sidl_safe_len - 1]; 226 | } 227 | 228 | // Last up, create the final message 229 | quic_lb_set_first_octet(cfg, (UINT8 *)cid); 230 | cfg->nonce_ctr++; 231 | return; 232 | err: 233 | /* Go to 5-tuple routing*/ 234 | rndset(cid, RND_PSEUDO, CIDL(cfg)); 235 | *(UINT8 *)cid &= 0xc0; 236 | return; 237 | } 238 | 239 | static int 240 | quic_lb_scid_decrypt(void *ctx, void *cid, void *sid, size_t *cid_len) 241 | { 242 | struct quic_lb_lb_ctx *cfg = ctx; 243 | 244 | UINT8 *read = cid; 245 | // UINT8 nonce[cfg->nonce_len]; 246 | size_t total_len = cfg->sidl + cfg->nonce_len; 247 | size_t sidl_safe_len = ceilf(total_len / 2.0); 248 | 249 | if (cfg->encode_length) { 250 | *cid_len = (size_t)(*(UINT8 *)cid & 0x3f) + 1; 251 | } 252 | 253 | read++; 254 | 255 | QUIC_LB_BLOCK left_N = { 0 }, 256 | right_N = { 0 }; 257 | 258 | QUIC_LB_BLOCK ciphertext = { 0 }; 259 | quic_lb_truncate_left(left_N, read, total_len); 260 | quic_lb_truncate_right(right_N, read, total_len); 261 | 262 | quic_lb_encrypt_round_right(ctx, left_N, right_N, ciphertext, 0x04); 263 | quic_lb_encrypt_round_left(ctx, left_N, right_N, ciphertext, 0x03); 264 | quic_lb_encrypt_round_right(ctx, left_N, right_N, ciphertext, 0x02); 265 | quic_lb_encrypt_round_left(ctx, left_N, right_N, ciphertext, 0x01); 266 | 267 | QUIC_LB_BLOCK result = { 0 }; 268 | 269 | if ((total_len % 2) == 0) { 270 | memcpy(result, left_N, sidl_safe_len); 271 | memcpy(result + sidl_safe_len, right_N, sidl_safe_len); 272 | } 273 | else { 274 | memcpy(result + (total_len - sidl_safe_len), right_N, sidl_safe_len); 275 | memcpy(result, left_N, sidl_safe_len - 1); 276 | result[sidl_safe_len - 1] |= left_N[sidl_safe_len - 1]; 277 | } 278 | 279 | memcpy(sid, result, cfg->sidl); 280 | 281 | return cfg->sidl; 282 | err: 283 | return 0; 284 | } 285 | 286 | static void 287 | quic_lb_bcid_encrypt(void *ctx, void *cid) 288 | { 289 | struct quic_lb_server_ctx *cfg = ctx; 290 | UINT8 *ptr = cid; 291 | UINT8 block[QUIC_LB_BLOCK_SIZE]; 292 | UINT8 ct_nonce_len = QUIC_LB_BLOCK_SIZE - cfg->sidl; 293 | int ct_len; 294 | 295 | if (cfg->nonce_ctr > (((UINT128)0x1 << (ct_nonce_len * 8)) - 1)) { 296 | /* Nonce is not big enough for unique CIDs */ 297 | goto err; 298 | } 299 | quic_lb_set_first_octet(cfg, ptr); 300 | ptr++; 301 | memcpy(&block[0], cfg->sid, cfg->sidl); 302 | /* Note: call below relies on nonce_ctr in host order */ 303 | memcpy(&block[cfg->sidl], &(cfg->nonce_ctr), ct_nonce_len); 304 | if ((EVP_EncryptUpdate(cfg->crypto_ctx, ptr, &ct_len, block, 305 | sizeof(block)) != 1) || (ct_len != sizeof(block))) { 306 | goto err; 307 | } 308 | rndset(ptr + sizeof(block), RND_PSEUDO, cfg->nonce_len - ct_nonce_len); 309 | cfg->nonce_ctr++; 310 | return; 311 | err: 312 | /* Go to 5-tuple routing*/ 313 | rndset(cid, RND_PSEUDO, CIDL(cfg)); 314 | *(UINT8 *)cid &= 0xc0; 315 | return; 316 | } 317 | 318 | static int 319 | quic_lb_bcid_decrypt(void *ctx, void *cid, void *sid, size_t *cid_len) 320 | { 321 | struct quic_lb_lb_ctx *cfg = ctx; 322 | UINT8 *ptr = cid; 323 | UINT8 block[QUIC_LB_BLOCK_SIZE]; 324 | int pt_len; 325 | 326 | if (cfg->encode_length) { 327 | *cid_len = (size_t)(*(UINT8 *)cid & 0x3f) + 1; 328 | } 329 | if ((EVP_DecryptUpdate(cfg->crypto_ctx, &block[0], &pt_len, ptr + 1, 330 | sizeof(block)) != 1) || (pt_len != sizeof(block))) { 331 | goto err; 332 | } 333 | memcpy(sid, block, cfg->sidl); 334 | return cfg->sidl; 335 | err: 336 | return 0; 337 | } 338 | 339 | void * 340 | quic_lb_lb_ctx_init(enum quic_lb_alg alg, BOOL encode_len, size_t sidl, 341 | UINT8 *key, size_t nonce_len) 342 | { 343 | struct quic_lb_lb_ctx *ctx = umalloc(sizeof(struct quic_lb_lb_ctx), 344 | M_FILTER, UM_ZERO); 345 | 346 | if (ctx == NULL) { 347 | goto fail; 348 | } 349 | ctx->crypto_ctx = NULL; 350 | ctx->encode_length = encode_len; 351 | if ((sidl == 0) || (nonce_len < 4) || 352 | ((sidl + nonce_len) > QUIC_LB_USABLE_BYTES)) { 353 | goto fail; 354 | } 355 | ctx->sidl = sidl; 356 | ctx->nonce_len = nonce_len; 357 | switch (alg) { 358 | case QUIC_LB_PCID: 359 | ctx->decrypt = quic_lb_pcid_decrypt; 360 | break; 361 | case QUIC_LB_BCID: 362 | if ((sidl + nonce_len) < QUIC_LB_BLOCK_SIZE) { 363 | goto fail; 364 | } 365 | /* Fall through */ 366 | case QUIC_LB_SCID: 367 | ctx->decrypt = (alg == QUIC_LB_BCID) ? quic_lb_bcid_decrypt : 368 | quic_lb_scid_decrypt; 369 | ctx->crypto_ctx = EVP_CIPHER_CTX_new(); 370 | if (ctx->crypto_ctx == NULL) { 371 | goto fail; 372 | } 373 | /* 374 | * CTR mode just encrypts the nonce using AES-ECB and XORs it with 375 | * the plaintext or ciphertext. So for SCID the decryption 376 | * operation is technically an encryption (the last arg is 1). 377 | */ 378 | if (EVP_CipherInit_ex(ctx->crypto_ctx, EVP_aes_128_ecb(), NULL, key, 379 | NULL, (alg == QUIC_LB_BCID) ? 0 : 1) == 0) { 380 | goto fail; 381 | } 382 | if (EVP_CIPHER_CTX_set_padding(ctx->crypto_ctx, 0) == 0) { 383 | goto fail; 384 | } 385 | break; 386 | } 387 | return ctx; 388 | fail: 389 | if (ctx != NULL) { 390 | if (ctx->crypto_ctx != NULL) { 391 | EVP_CIPHER_CTX_free(ctx->crypto_ctx); 392 | } 393 | ufree(ctx); 394 | } 395 | return NULL; 396 | } 397 | 398 | void * 399 | quic_lb_server_ctx_init(enum quic_lb_alg alg, UINT8 cr, BOOL encode_len, 400 | size_t sidl, UINT8 *key, size_t nonce_len, UINT8 *sid) 401 | { 402 | struct quic_lb_server_ctx *ctx = umalloc( 403 | sizeof(struct quic_lb_server_ctx), M_FILTER, UM_ZERO); 404 | 405 | if (ctx == NULL) { 406 | goto fail; 407 | } 408 | ctx->crypto_ctx = NULL; 409 | if (cr > 0x2) { 410 | goto fail; 411 | } 412 | ctx->cr = cr; 413 | ctx->encode_length = encode_len; 414 | if ((sidl == 0) || (nonce_len < 4) || 415 | ((sidl + nonce_len) > QUIC_LB_USABLE_BYTES)) { 416 | goto fail; 417 | } 418 | ctx->sidl = sidl; 419 | ctx->nonce_len = nonce_len; 420 | ctx->nonce_ctr = 0; 421 | memcpy(ctx->sid, sid, sidl); 422 | switch (alg) { 423 | case QUIC_LB_PCID: 424 | ctx->encrypt = quic_lb_pcid_encrypt; 425 | break; 426 | case QUIC_LB_BCID: 427 | if (sidl + nonce_len > QUIC_LB_USABLE_BYTES) { 428 | goto fail; 429 | } 430 | /* Fall through */ 431 | case QUIC_LB_SCID: 432 | ctx->encrypt = (alg == QUIC_LB_BCID) ? quic_lb_bcid_encrypt : 433 | quic_lb_scid_encrypt; 434 | ctx->crypto_ctx = EVP_CIPHER_CTX_new(); 435 | if (ctx->crypto_ctx == NULL) { 436 | goto fail; 437 | } 438 | if (EVP_CipherInit_ex(ctx->crypto_ctx, EVP_aes_128_ecb(), NULL, key, 439 | NULL, 1) == 0) { 440 | EVP_CIPHER_CTX_free(ctx->crypto_ctx); 441 | goto fail; 442 | } 443 | if (EVP_CIPHER_CTX_set_padding(ctx->crypto_ctx, 0) == 0) { 444 | goto fail; 445 | } 446 | break; 447 | } 448 | return ctx; 449 | fail: 450 | if (ctx != NULL) { 451 | if (ctx->crypto_ctx != NULL) { 452 | EVP_CIPHER_CTX_free(ctx->crypto_ctx); 453 | } 454 | ufree(ctx); 455 | } 456 | return NULL; 457 | } 458 | 459 | void 460 | quic_lb_lb_ctx_free(void *ctx) 461 | { 462 | struct quic_lb_lb_ctx *todelete = (struct quic_lb_lb_ctx *)ctx; 463 | 464 | if (todelete->crypto_ctx != NULL) { 465 | EVP_CIPHER_CTX_free(todelete->crypto_ctx); 466 | } 467 | ufree(ctx); 468 | } 469 | 470 | void 471 | quic_lb_server_ctx_free(void *ctx) 472 | { 473 | struct quic_lb_server_ctx *todelete = (struct quic_lb_server_ctx *)ctx; 474 | 475 | if (todelete->crypto_ctx != NULL) { 476 | EVP_CIPHER_CTX_free(todelete->crypto_ctx); 477 | } 478 | ufree(ctx); 479 | } 480 | 481 | void 482 | quic_lb_encrypt_cid(void *ctx, void *cid) 483 | { 484 | struct quic_lb_server_ctx *context = ctx; 485 | 486 | if (context == NULL) { 487 | rndset(cid, RND_PSEUDO, 8); 488 | *(UINT8 *)cid |= QUIC_LB_TUPLE_ROUTE; 489 | } else { 490 | context->encrypt(ctx, cid); 491 | } 492 | } 493 | 494 | int 495 | quic_lb_decrypt_cid(void *ctx, void *cid, void *sid, size_t *cid_len) 496 | { 497 | struct quic_lb_lb_ctx *context = ctx; 498 | return ((context == NULL) ? 0 : 499 | context->decrypt(ctx, cid, sid, cid_len)); 500 | } 501 | 502 | void 503 | test_quic_lb_truncate() 504 | { 505 | QUIC_LB_BLOCK result_buffer = { 0 }; 506 | 507 | QUIC_LB_BLOCK test0 = { 0x31, 0x44, 0x1a, 0x9c, 0x69, 0xc2, 0x75 }; 508 | QUIC_LB_BLOCK test0_left = { 0x31, 0x44, 0x1a, 0x90 }; 509 | QUIC_LB_BLOCK test0_right = { 0x0c, 0x69, 0xc2, 0x75 }; 510 | 511 | QUIC_LB_BLOCK test1 = { 0x12 }; 512 | QUIC_LB_BLOCK test1_left = { 0x10 }; 513 | QUIC_LB_BLOCK test1_right = { 0x02 }; 514 | 515 | QUIC_LB_BLOCK test2 = { 0x00, 0x11 }; 516 | QUIC_LB_BLOCK test2_left = { 0x00 }; 517 | QUIC_LB_BLOCK test2_right = { 0x11 }; 518 | 519 | typedef struct { 520 | QUIC_LB_BLOCK *input, *left, *right; 521 | size_t len; 522 | } _QUIC_LB_TRUNCATE_TEST; 523 | 524 | _QUIC_LB_TRUNCATE_TEST tests[] = { 525 | { &test0, &test0_left, &test0_right, 7 }, 526 | { &test1, &test1_left, &test1_right, 1 }, 527 | { &test2, &test2_left, &test2_right, 2 }, 528 | }; 529 | 530 | for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { 531 | memset(result_buffer, '\0', sizeof(result_buffer)); 532 | 533 | _QUIC_LB_TRUNCATE_TEST *test = &tests[0]; 534 | 535 | quic_lb_truncate_left(result_buffer, *test->input, test->len); 536 | if (memcmp(result_buffer, test->left, ceilf(test->len / 2.0)) != 0) { 537 | printf("Truncate test failed %d\n", i); 538 | } 539 | 540 | memset(result_buffer, '\0', sizeof(result_buffer)); 541 | 542 | quic_lb_truncate_right(result_buffer, *test->input, test->len); 543 | if (memcmp(result_buffer, test->right, ceilf(test->len / 2.0)) != 0) { 544 | printf("Truncate test failed %d\n", i); 545 | } 546 | } 547 | } 548 | -------------------------------------------------------------------------------- /quic_lb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 F5 Networks Inc. 3 | * This source code is subject to the terms of the Apache License, 4 | * version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 5 | */ 6 | #ifndef _QUIC_LB_H_ 7 | #define _QUIC_LB_H_ 8 | 9 | #ifdef NOBIGIP 10 | #include "quic_lb_types.h" 11 | #else 12 | #include 13 | #endif 14 | 15 | #define QUIC_LB_MAX_CID_LEN 20 16 | 17 | enum quic_lb_alg { 18 | QUIC_LB_PCID, /* Plaintext CID algorithm (Sec 4.1) */ 19 | QUIC_LB_SCID, /* Stream Cipher CID algorithm (Sec 4.2) */ 20 | QUIC_LB_BCID, /* Block Cipher CID algorithm (Sec 4.3) */ 21 | }; 22 | 23 | /* QUIC-LB context functions */ 24 | /* 25 | * Initialize the QUIC-LB context. One version for load balancers, one for 26 | * servers. 27 | * 28 | * There is one context for each config rotation codepoint. The calling 29 | * application must map the resulting contexts to config rotation bits. 30 | * 31 | * If the algorithm is Plaintext CID (QUIC_LB_PCID), the key argument is 32 | * ignored. If the algorithm is *not* Stream Cipher CID (QUIC_LB_SCID), the 33 | * nonce_len field is ignored. 34 | * 35 | * Returns NULL on a number of errors, including invalid parameters. 36 | */ 37 | void *quic_lb_lb_ctx_init(enum quic_lb_alg alg, BOOL encode_len, 38 | size_t sidl, UINT8 *key, size_t nonce_len); 39 | /* 40 | * Include the config rotation bits, so the server doesn't have to manually 41 | * add them. The server includes the number of bytes it wants to use for 42 | * other * purposes, and the function will compute the result CID length. 43 | */ 44 | void *quic_lb_server_ctx_init(enum quic_lb_alg, UINT8 cr, BOOL encode_len, 45 | size_t sidl, UINT8 *key, size_t nonce_len, UINT8 *sid); 46 | /* Free the context */ 47 | void quic_lb_lb_ctx_free(void *ctx); 48 | void quic_lb_server_ctx_free(void *ctx); 49 | 50 | /* 51 | * Encrypt functions, to be called by the server. The "server use" field can 52 | * contain bits that encode opaque information for the server. In this API, 53 | * the first octet is *always* random or length-encoding, and never uses the 54 | * server_use argument. 55 | */ 56 | void quic_lb_encrypt_cid(void *ctx, void *cid); 57 | 58 | /* 59 | * Decrypt function to be used by the load balancer. Returns the length of 60 | * the server ID. If 0, disregard the server ID in 'sid' and revert to 5- 61 | * tuple * routing. The cid_len field is only filled if self-encoding; it's 62 | * not modified otherwise. 63 | */ 64 | int quic_lb_decrypt_cid(void *ctx, void *cid, void *sid, size_t *cid_len); 65 | #endif /* _QUIC_LB_H */ 66 | -------------------------------------------------------------------------------- /quic_lb_cla.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F5Networks/quic-lb/d50685131887820ac9dc6642a82b435f964c18cf/quic_lb_cla.pdf -------------------------------------------------------------------------------- /quic_lb_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 F5 Networks Inc. 3 | * This source code is subject to the terms of the Apache License, 4 | * version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 5 | */ 6 | 7 | #ifdef NOBIGIP 8 | #include 9 | #include "quic_lb.h" 10 | #else 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | #define TEST_QUIC_LB_NUM_CONFIG 5 22 | #define TEST_QUIC_LB_NUM_SRV_ID 5 23 | #define TEST_QUIC_LB_PER_SERVER 3 24 | #define TEST_QUIC_LB_BLOCK_SIZE 16 25 | 26 | #define TEST_QUIC_KEY_SIZE 16 27 | 28 | // Forward declare, test-only code 29 | void test_quic_lb_truncate(); 30 | 31 | #ifdef NOBIGIP 32 | static void 33 | test_quic_print_buffer(void *buf, size_t len) 34 | { 35 | int i; 36 | UINT8 *ptr = buf; 37 | for (i = 0; i < len; i++) { 38 | printf("%02x", *ptr); 39 | ptr++; 40 | } 41 | } 42 | #endif /* NOBIGIP */ 43 | 44 | static void 45 | test_quic_lb_alg(enum quic_lb_alg alg) 46 | { 47 | UINT8 sid[QUIC_LB_MAX_CID_LEN], cid[QUIC_LB_MAX_CID_LEN]; 48 | UINT8 result[QUIC_LB_MAX_CID_LEN], key[TEST_QUIC_KEY_SIZE]; 49 | int cfg, srv, run; 50 | size_t cid_len, cidl, nonce_len, sidl = 0; 51 | BOOL len_encode; 52 | void *lb_ctx, *server_ctx; 53 | 54 | for (cfg = 0; cfg < TEST_QUIC_LB_NUM_CONFIG; cfg++) { 55 | len_encode = (cfg % 2 == 0); 56 | sidl++; 57 | /* These are not used by all algorithms, but are harmless to set. */ 58 | rndset(key, RND_PSEUDO, sizeof(key)); 59 | nonce_len = rnd8_range(RND_PSEUDO, 6) + 4; 60 | while ((nonce_len + sidl + 1) > QUIC_LB_MAX_CID_LEN) { 61 | nonce_len--; 62 | } 63 | #ifdef NOBIGIP 64 | switch(alg) { 65 | case QUIC_LB_PCID: 66 | printf("PCID"); 67 | break; 68 | case QUIC_LB_SCID: 69 | printf("SCID"); 70 | break; 71 | case QUIC_LB_BCID: 72 | if (nonce_len + sidl < TEST_QUIC_KEY_SIZE) { 73 | nonce_len = 16 - sidl; 74 | } 75 | printf("BCID"); 76 | break; 77 | } 78 | printf(" LB configuration: cr_bits 0x0 length_self_encoding: %s" 79 | " sid_len %zu nonce_len %zu ", len_encode ? "y" : "n", 80 | nonce_len, sidl); 81 | if (alg != QUIC_LB_PCID) { 82 | printf("key "); 83 | test_quic_print_buffer(key, TEST_QUIC_KEY_SIZE); 84 | } 85 | printf("\n"); 86 | #endif 87 | lb_ctx = quic_lb_lb_ctx_init(alg, len_encode, sidl, key, nonce_len); 88 | CUT_ASSERT(lb_ctx != NULL); 89 | for (srv = 0; srv < TEST_QUIC_LB_NUM_SRV_ID; srv++) { 90 | rndset(sid, RND_PSEUDO, sidl); 91 | server_ctx = quic_lb_server_ctx_init(alg, 0x0, len_encode, sidl, 92 | key, nonce_len, sid); 93 | CUT_ASSERT(server_ctx != NULL); 94 | cid_len = sidl + nonce_len + 1; 95 | for (run = 0; run < TEST_QUIC_LB_PER_SERVER; run++) { 96 | quic_lb_encrypt_cid(server_ctx, cid); 97 | CUT_ASSERT(quic_lb_decrypt_cid(lb_ctx, cid, result, &cidl) == 98 | sidl); 99 | #ifdef NOBIGIP 100 | printf("nonce "); 101 | if (alg == QUIC_LB_PCID) { 102 | printf("random"); 103 | } else { 104 | printf("%u", run); 105 | } 106 | printf(" cid "); 107 | test_quic_print_buffer(cid, cid_len); 108 | printf(" sid "); 109 | test_quic_print_buffer(sid, sidl); 110 | printf("\n"); 111 | #endif 112 | CUT_ASSERT(!len_encode || (cidl == cid_len)); 113 | CUT_ASSERT(memcmp(result, sid, sidl) == 0); 114 | } 115 | quic_lb_server_ctx_free(server_ctx); 116 | } 117 | quic_lb_lb_ctx_free(lb_ctx); 118 | } 119 | } 120 | 121 | static void 122 | test_quic_lb_encrypted_test_vectors() { 123 | UINT8 key[TEST_QUIC_KEY_SIZE] = { 124 | 0x8f, 0x95, 0xf0, 0x92, 0x45, 0x76, 0x5f, 0x80, 125 | 0x25, 0x69, 0x34, 0xe5, 0x0c, 0x66, 0x20, 0x7f, 126 | }; 127 | UINT8 sid[] = { 0xed, 0x79, 0x3a, 0x51, 0xd4, 0x9b, 0x8f, 0x5f, 128 | 0xab, 0x65 }; 129 | UINT8 cid1[] = { 0x07, 0xfb, 0xfe, 0x05, 0xf7, 0x31, 0xb4, 0x25 }; 130 | UINT8 cid2[] = { 0x4f, 0x01, 0x09, 0x56, 0xfb, 0x5c, 0x1d, 0x4d, 131 | 0x86, 0xe0, 0x10, 0x18, 0x3e, 0x0b, 0x7d, 0x1e }; 132 | UINT8 cid3[] = { 0x90, 0x4d, 0xd2, 0xd0, 0x5a, 0x7b, 0x0d, 0xe9, 133 | 0xb2, 0xb9, 0x90, 0x7a, 0xfb, 0x5e, 0xcf, 0x8c, 134 | 0xc3 }; 135 | UINT8 cid4[] = { 0x12, 0x7a, 0x28, 0x5a, 0x09, 0xf8, 0x52, 0x80, 136 | 0xf4, 0xfd, 0x6a, 0xbb, 0x43, 0x4a, 0x71, 0x59, 137 | 0xe4, 0xd3, 0xeb }; 138 | UINT8 result[10]; 139 | void *ctx; 140 | size_t len; 141 | ctx = quic_lb_lb_ctx_init(QUIC_LB_SCID, TRUE, 3, key, 4); 142 | quic_lb_decrypt_cid(ctx, cid1, result, &len); 143 | CUT_ASSERT(memcmp(sid, result, 3) == 0); 144 | quic_lb_lb_ctx_free(ctx); 145 | 146 | ctx = quic_lb_lb_ctx_init(QUIC_LB_SCID, TRUE, 10, key, 5); 147 | quic_lb_decrypt_cid(ctx, cid2, result, &len); 148 | CUT_ASSERT(memcmp(sid, result, 10) == 0); 149 | quic_lb_lb_ctx_free(ctx); 150 | 151 | ctx = quic_lb_lb_ctx_init(QUIC_LB_BCID, TRUE, 8, key, 8); 152 | quic_lb_decrypt_cid(ctx, cid3, result, &len); 153 | CUT_ASSERT(memcmp(sid, result, 8) == 0); 154 | quic_lb_lb_ctx_free(ctx); 155 | 156 | ctx = quic_lb_lb_ctx_init(QUIC_LB_SCID, TRUE, 9, key, 9); 157 | quic_lb_decrypt_cid(ctx, cid4, result, &len); 158 | CUT_ASSERT(memcmp(sid, result, 9) == 0); 159 | quic_lb_lb_ctx_free(ctx); 160 | 161 | } 162 | 163 | #ifdef NOBIGIP 164 | int main(int argc, char* argv[]) 165 | #else 166 | static void 167 | test_quic_lb(void) 168 | #endif 169 | { 170 | test_quic_lb_alg(QUIC_LB_PCID); 171 | test_quic_lb_alg(QUIC_LB_SCID); 172 | test_quic_lb_alg(QUIC_LB_BCID); 173 | test_quic_lb_truncate(); 174 | test_quic_lb_encrypted_test_vectors(); 175 | } 176 | 177 | #ifndef NOBIGIP 178 | CUT_SUITE(quic_lb); 179 | CUT_SUITE_TEST(quic_lb, test_quic_lb); 180 | #endif 181 | -------------------------------------------------------------------------------- /quic_lb_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 F5 Networks Inc. 3 | * This source code is subject to the terms of the Apache License, 4 | * version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef _QUIC_LB_TYPES 16 | #define _QUIC_LB_TYPES 17 | 18 | typedef uint8_t UINT8; 19 | typedef uint16_t UINT16; 20 | typedef uint32_t UINT32; 21 | typedef uint64_t UINT64; 22 | typedef __uint128_t UINT128; 23 | typedef enum {FALSE, TRUE} BOOL; 24 | typedef enum {ERR_OK, ERR_REJECT} err_t; 25 | 26 | /* BIGIP constants that don't matter here */ 27 | #define RND_PSEUDO 0 28 | #define M_FILTER 0 29 | #define UM_ZERO 0 30 | static inline void *umalloc(size_t size, int ignored, int cleared) 31 | { 32 | void *ret = malloc(size); 33 | if ((ret != NULL) && (cleared == UM_ZERO)) { 34 | ret = memset(ret, 0, size); 35 | } 36 | return ret; 37 | } 38 | 39 | #define ufree(arg) free(arg) 40 | #define CUT_ASSERT(expr) assert(expr) 41 | 42 | #define ROUNDUPDIV(n, m) (((n) + ((m) - 1)) / (m)) 43 | 44 | #define rndset(ptr,type,len) RAND_bytes((unsigned char *)ptr,len) 45 | 46 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 47 | 48 | /* Template to limit to a certain range for various types */ 49 | #define RND_RANGE(type,fnname,intmax) \ 50 | static inline type fnname(int rnd_type, \ 51 | type max) \ 52 | { \ 53 | type value; \ 54 | rndset((unsigned char *)&value, rnd_type, sizeof(max)); \ 55 | return ((max < intmax) ? (value % (max + 1)) : value); \ 56 | } 57 | RND_RANGE(UINT8, rnd8_range, UINT8_MAX) 58 | RND_RANGE(UINT16, rnd16_range, UINT16_MAX) 59 | 60 | static inline unsigned bit_count(UINT8 i) 61 | { 62 | UINT8 octet = i; 63 | unsigned count = 0; 64 | 65 | while (i > 0) { 66 | if ((i & 0x1) == 1) { 67 | count++; 68 | } 69 | i >>= 1; 70 | } 71 | return count; 72 | } 73 | #endif // _QUIC_LB_TYPES 74 | --------------------------------------------------------------------------------