1 1.3 mrg /* $NetBSD: ratelimit.c,v 1.3 2025/09/19 05:07:38 mrg Exp $ */ 2 1.1 christos 3 1.1 christos /*- 4 1.1 christos * Copyright (c) 2021 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.1 christos * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos #include <sys/cdefs.h> 32 1.3 mrg __RCSID("$NetBSD: ratelimit.c,v 1.3 2025/09/19 05:07:38 mrg Exp $"); 33 1.1 christos 34 1.3 mrg #include <sys/param.h> 35 1.1 christos #include <sys/queue.h> 36 1.1 christos 37 1.1 christos #include <arpa/inet.h> 38 1.1 christos 39 1.1 christos #include <stdio.h> 40 1.1 christos #include <stdlib.h> 41 1.1 christos #include <syslog.h> 42 1.1 christos #include <unistd.h> 43 1.1 christos #include <string.h> 44 1.1 christos #include <errno.h> 45 1.1 christos #include <stddef.h> 46 1.1 christos 47 1.1 christos #include "inetd.h" 48 1.1 christos 49 1.1 christos union addr { 50 1.1 christos struct in_addr ipv4_addr; 51 1.1 christos /* ensure aligned for comparison in rl_ipv6_eq (already is on 64-bit) */ 52 1.1 christos #ifdef INET6 53 1.1 christos struct in6_addr ipv6_addr __attribute__((aligned(16))); 54 1.1 christos #endif 55 1.1 christos char other_addr[NI_MAXHOST]; 56 1.1 christos }; 57 1.1 christos 58 1.1 christos static void rl_reset(struct servtab *, time_t); 59 1.1 christos static time_t rl_time(void); 60 1.1 christos static void rl_get_name(struct servtab *, int, union addr *); 61 1.1 christos static void rl_drop_connection(struct servtab *, int); 62 1.1 christos static struct rl_ip_node *rl_add(struct servtab *, union addr *); 63 1.1 christos static struct rl_ip_node *rl_try_get_ip(struct servtab *, union addr *); 64 1.1 christos static bool rl_ip_eq(struct servtab *, union addr *, struct rl_ip_node *); 65 1.1 christos #ifdef INET6 66 1.1 christos static bool rl_ipv6_eq(struct in6_addr *, struct in6_addr *); 67 1.1 christos #endif 68 1.1 christos #ifdef DEBUG_ENABLE 69 1.1 christos static void rl_print_found_node(struct servtab *, struct rl_ip_node *); 70 1.1 christos #endif 71 1.1 christos static void rl_log_address_exceed(struct servtab *, struct rl_ip_node *); 72 1.1 christos static const char *rl_node_tostring(struct servtab *, struct rl_ip_node *, char[NI_MAXHOST]); 73 1.1 christos static bool rl_process_service_max(struct servtab *, int, time_t *); 74 1.1 christos static bool rl_process_ip_max(struct servtab *, int, time_t *); 75 1.1 christos 76 1.1 christos /* Return 0 on allow, -1 if connection should be blocked */ 77 1.1 christos int 78 1.1 christos rl_process(struct servtab *sep, int ctrl) 79 1.1 christos { 80 1.1 christos time_t now = -1; 81 1.1 christos 82 1.1 christos DPRINTF(SERV_FMT ": processing rate-limiting", 83 1.1 christos SERV_PARAMS(sep)); 84 1.1 christos DPRINTF(SERV_FMT ": se_service_max " 85 1.1 christos "%zu and se_count %zu", SERV_PARAMS(sep), 86 1.1 christos sep->se_service_max, sep->se_count); 87 1.1 christos 88 1.1 christos if (sep->se_count == 0) { 89 1.1 christos now = rl_time(); 90 1.1 christos sep->se_time = now; 91 1.1 christos } 92 1.1 christos 93 1.2 rillig if (!rl_process_service_max(sep, ctrl, &now) 94 1.1 christos || !rl_process_ip_max(sep, ctrl, &now)) { 95 1.1 christos return -1; 96 1.1 christos } 97 1.1 christos 98 1.1 christos DPRINTF(SERV_FMT ": running service ", SERV_PARAMS(sep)); 99 1.1 christos 100 1.1 christos /* se_count is only incremented if rl_process will return 0 */ 101 1.1 christos sep->se_count++; 102 1.1 christos return 0; 103 1.1 christos } 104 1.1 christos 105 1.1 christos /* 106 1.1 christos * Get the identifier for the remote peer based on sep->se_socktype and 107 1.1 christos * sep->se_family 108 1.1 christos */ 109 1.1 christos static void 110 1.1 christos rl_get_name(struct servtab *sep, int ctrl, union addr *out) 111 1.1 christos { 112 1.1 christos union { 113 1.1 christos struct sockaddr_storage ss; 114 1.1 christos struct sockaddr sa; 115 1.1 christos struct sockaddr_in sin; 116 1.1 christos struct sockaddr_in6 sin6; 117 1.1 christos } addr; 118 1.1 christos 119 1.1 christos /* Get the sockaddr of socket ctrl */ 120 1.1 christos switch (sep->se_socktype) { 121 1.1 christos case SOCK_STREAM: { 122 1.1 christos socklen_t len = sizeof(struct sockaddr_storage); 123 1.1 christos if (getpeername(ctrl, &addr.sa, &len) == -1) { 124 1.1 christos /* error, log it and skip ip rate limiting */ 125 1.1 christos syslog(LOG_ERR, 126 1.1 christos SERV_FMT " failed to get peer name of the " 127 1.1 christos "connection", SERV_PARAMS(sep)); 128 1.1 christos exit(EXIT_FAILURE); 129 1.1 christos } 130 1.1 christos break; 131 1.1 christos } 132 1.1 christos case SOCK_DGRAM: { 133 1.1 christos struct msghdr header = { 134 1.1 christos .msg_name = &addr.sa, 135 1.1 christos .msg_namelen = sizeof(struct sockaddr_storage), 136 1.1 christos /* scatter/gather and control info is null */ 137 1.1 christos }; 138 1.1 christos ssize_t count; 139 1.1 christos 140 1.1 christos /* Peek so service can still get the packet */ 141 1.1 christos count = recvmsg(ctrl, &header, MSG_PEEK); 142 1.1 christos if (count == -1) { 143 1.1 christos syslog(LOG_ERR, 144 1.1 christos "failed to get dgram source address: %s; exiting", 145 1.1 christos strerror(errno)); 146 1.1 christos exit(EXIT_FAILURE); 147 1.1 christos } 148 1.1 christos break; 149 1.1 christos } 150 1.1 christos default: 151 1.1 christos DPRINTF(SERV_FMT ": ip_max rate limiting not supported for " 152 1.1 christos "socktype", SERV_PARAMS(sep)); 153 1.1 christos syslog(LOG_ERR, SERV_FMT 154 1.1 christos ": ip_max rate limiting not supported for socktype", 155 1.1 christos SERV_PARAMS(sep)); 156 1.1 christos exit(EXIT_FAILURE); 157 1.1 christos } 158 1.1 christos 159 1.1 christos /* Convert addr to to rate limiting address */ 160 1.1 christos switch (sep->se_family) { 161 1.1 christos case AF_INET: 162 1.1 christos out->ipv4_addr = addr.sin.sin_addr; 163 1.1 christos break; 164 1.1 christos #ifdef INET6 165 1.1 christos case AF_INET6: 166 1.1 christos out->ipv6_addr = addr.sin6.sin6_addr; 167 1.1 christos break; 168 1.1 christos #endif 169 1.1 christos default: { 170 1.1 christos int res = getnameinfo(&addr.sa, 171 1.1 christos (socklen_t)addr.sa.sa_len, 172 1.1 christos out->other_addr, NI_MAXHOST, 173 1.1 christos NULL, 0, 174 1.1 christos NI_NUMERICHOST 175 1.1 christos ); 176 1.1 christos if (res != 0) { 177 1.1 christos syslog(LOG_ERR, 178 1.1 christos SERV_FMT ": failed to get name info of " 179 1.1 christos "the incoming connection: %s; exiting", 180 1.1 christos SERV_PARAMS(sep), gai_strerror(res)); 181 1.1 christos exit(EXIT_FAILURE); 182 1.1 christos } 183 1.1 christos break; 184 1.1 christos } 185 1.1 christos } 186 1.1 christos } 187 1.1 christos 188 1.1 christos static void 189 1.1 christos rl_drop_connection(struct servtab *sep, int ctrl) 190 1.1 christos { 191 1.1 christos 192 1.1 christos if (sep->se_wait == 0 && sep->se_socktype == SOCK_STREAM) { 193 1.1 christos /* 194 1.1 christos * If the fd isn't a listen socket, 195 1.1 christos * close the individual connection too. 196 1.1 christos */ 197 1.1 christos close(ctrl); 198 1.1 christos return; 199 1.1 christos } 200 1.1 christos if (sep->se_socktype != SOCK_DGRAM) { 201 1.1 christos return; 202 1.1 christos } 203 1.1 christos /* 204 1.1 christos * Drop the single datagram the service would have 205 1.1 christos * consumed if nowait. If this is a wait service, this 206 1.1 christos * will consume 1 datagram, and further received packets 207 1.1 christos * will be removed in the same way. 208 1.1 christos */ 209 1.1 christos struct msghdr header = { 210 1.1 christos /* All fields null, just consume one message */ 211 1.1 christos }; 212 1.1 christos ssize_t count; 213 1.1 christos 214 1.1 christos count = recvmsg(ctrl, &header, 0); 215 1.1 christos if (count == -1) { 216 1.1 christos syslog(LOG_ERR, 217 1.1 christos SERV_FMT ": failed to consume nowait dgram: %s", 218 1.1 christos SERV_PARAMS(sep), strerror(errno)); 219 1.1 christos exit(EXIT_FAILURE); 220 1.1 christos } 221 1.1 christos DPRINTF(SERV_FMT ": dropped dgram message", 222 1.1 christos SERV_PARAMS(sep)); 223 1.1 christos } 224 1.1 christos 225 1.1 christos static time_t 226 1.1 christos rl_time(void) 227 1.1 christos { 228 1.1 christos struct timespec ts; 229 1.2 rillig if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) { 230 1.1 christos syslog(LOG_ERR, "clock_gettime for rate limiting failed: %s; " 231 1.1 christos "exiting", strerror(errno)); 232 1.1 christos /* Exit inetd if rate limiting fails */ 233 1.1 christos exit(EXIT_FAILURE); 234 1.1 christos } 235 1.1 christos return ts.tv_sec; 236 1.1 christos } 237 1.1 christos 238 1.1 christos /* Add addr to IP tracking or return NULL if malloc fails */ 239 1.1 christos static struct rl_ip_node * 240 1.1 christos rl_add(struct servtab *sep, union addr *addr) 241 1.1 christos { 242 1.1 christos 243 1.1 christos struct rl_ip_node *node; 244 1.1 christos size_t node_size, bufsize; 245 1.1 christos #ifdef DEBUG_ENABLE 246 1.1 christos char buffer[NI_MAXHOST]; 247 1.1 christos #endif 248 1.1 christos 249 1.1 christos switch(sep->se_family) { 250 1.1 christos case AF_INET: 251 1.1 christos /* ip_node to end of IPv4 address */ 252 1.1 christos node_size = offsetof(struct rl_ip_node, ipv4_addr) 253 1.1 christos + sizeof(struct in_addr); 254 1.1 christos break; 255 1.1 christos case AF_INET6: 256 1.1 christos /* ip_node to end of IPv6 address */ 257 1.1 christos node_size = offsetof(struct rl_ip_node, ipv6_addr) 258 1.1 christos + sizeof(struct in6_addr); 259 1.1 christos break; 260 1.1 christos default: 261 1.1 christos /* ip_node to other_addr plus size of string + NULL */ 262 1.1 christos bufsize = strlen(addr->other_addr) + sizeof(char); 263 1.1 christos node_size = offsetof(struct rl_ip_node, other_addr) + bufsize; 264 1.1 christos break; 265 1.1 christos } 266 1.1 christos 267 1.3 mrg node_size = MAX(node_size, sizeof *node); 268 1.3 mrg 269 1.1 christos node = malloc(node_size); 270 1.1 christos if (node == NULL) { 271 1.2 rillig if (errno == ENOMEM) { 272 1.1 christos return NULL; 273 1.1 christos } else { 274 1.1 christos syslog(LOG_ERR, "malloc failed unexpectedly: %s", 275 1.1 christos strerror(errno)); 276 1.1 christos exit(EXIT_FAILURE); 277 1.1 christos } 278 1.1 christos } 279 1.1 christos 280 1.1 christos node->count = 0; 281 1.1 christos 282 1.1 christos /* copy the data into the new allocation */ 283 1.1 christos switch(sep->se_family) { 284 1.1 christos case AF_INET: 285 1.1 christos node->ipv4_addr = addr->ipv4_addr; 286 1.1 christos break; 287 1.1 christos case AF_INET6: 288 1.1 christos /* Hopefully this is inlined, means the same thing as memcpy */ 289 1.1 christos __builtin_memcpy(&node->ipv6_addr, &addr->ipv6_addr, 290 1.1 christos sizeof(struct in6_addr)); 291 1.1 christos break; 292 1.1 christos default: 293 1.1 christos strlcpy(node->other_addr, addr->other_addr, bufsize); 294 1.1 christos break; 295 1.1 christos } 296 1.1 christos 297 1.1 christos /* initializes 'entries' member to NULL automatically */ 298 1.1 christos SLIST_INSERT_HEAD(&sep->se_rl_ip_list, node, entries); 299 1.1 christos 300 1.1 christos DPRINTF(SERV_FMT ": add '%s' to rate limit tracking (%zu byte record)", 301 1.1 christos SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer), node_size); 302 1.1 christos 303 1.1 christos return node; 304 1.1 christos } 305 1.1 christos 306 1.1 christos static void 307 1.1 christos rl_reset(struct servtab *sep, time_t now) 308 1.1 christos { 309 1.1 christos DPRINTF(SERV_FMT ": %ji seconds passed; resetting rate limiting ", 310 1.1 christos SERV_PARAMS(sep), (intmax_t)(now - sep->se_time)); 311 1.1 christos 312 1.1 christos sep->se_count = 0; 313 1.1 christos sep->se_time = now; 314 1.1 christos if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) { 315 1.1 christos rl_clear_ip_list(sep); 316 1.1 christos } 317 1.1 christos } 318 1.1 christos 319 1.1 christos void 320 1.1 christos rl_clear_ip_list(struct servtab *sep) 321 1.1 christos { 322 1.1 christos while (!SLIST_EMPTY(&sep->se_rl_ip_list)) { 323 1.1 christos struct rl_ip_node *node = SLIST_FIRST(&sep->se_rl_ip_list); 324 1.1 christos SLIST_REMOVE_HEAD(&sep->se_rl_ip_list, entries); 325 1.1 christos free(node); 326 1.1 christos } 327 1.1 christos } 328 1.1 christos 329 1.1 christos /* Get the node associated with addr, or NULL */ 330 1.1 christos static struct rl_ip_node * 331 1.1 christos rl_try_get_ip(struct servtab *sep, union addr *addr) 332 1.1 christos { 333 1.1 christos 334 1.1 christos struct rl_ip_node *cur; 335 1.1 christos SLIST_FOREACH(cur, &sep->se_rl_ip_list, entries) { 336 1.1 christos if (rl_ip_eq(sep, addr, cur)) { 337 1.1 christos return cur; 338 1.1 christos } 339 1.1 christos } 340 1.1 christos 341 1.1 christos return NULL; 342 1.1 christos } 343 1.1 christos 344 1.1 christos /* Return true if passed service rate limiting checks, false if blocked */ 345 1.1 christos static bool 346 1.1 christos rl_process_service_max(struct servtab *sep, int ctrl, time_t *now) 347 1.1 christos { 348 1.1 christos if (sep->se_count >= sep->se_service_max) { 349 1.2 rillig if (*now == -1) { 350 1.1 christos /* Only get the clock time if we didn't already */ 351 1.1 christos *now = rl_time(); 352 1.1 christos } 353 1.1 christos 354 1.1 christos if (*now - sep->se_time > CNT_INTVL) { 355 1.1 christos rl_reset(sep, *now); 356 1.2 rillig } else { 357 1.1 christos syslog(LOG_ERR, SERV_FMT 358 1.1 christos ": max spawn rate (%zu in %ji seconds) " 359 1.1 christos "already met; closing for %ju seconds", 360 1.1 christos SERV_PARAMS(sep), 361 1.1 christos sep->se_service_max, 362 1.1 christos (intmax_t)CNT_INTVL, 363 1.1 christos (uintmax_t)RETRYTIME); 364 1.1 christos DPRINTF(SERV_FMT 365 1.1 christos ": max spawn rate (%zu in %ji seconds) " 366 1.1 christos "already met; closing for %ju seconds", 367 1.1 christos SERV_PARAMS(sep), 368 1.1 christos sep->se_service_max, 369 1.1 christos (intmax_t)CNT_INTVL, 370 1.1 christos (uintmax_t)RETRYTIME); 371 1.1 christos 372 1.1 christos rl_drop_connection(sep, ctrl); 373 1.1 christos 374 1.1 christos /* Close the server for 10 minutes */ 375 1.1 christos close_sep(sep); 376 1.1 christos if (!timingout) { 377 1.1 christos timingout = true; 378 1.1 christos alarm(RETRYTIME); 379 1.1 christos } 380 1.1 christos 381 1.1 christos return false; 382 1.1 christos } 383 1.1 christos } 384 1.1 christos return true; 385 1.1 christos } 386 1.1 christos 387 1.1 christos /* Return true if passed IP rate limiting checks, false if blocked */ 388 1.1 christos static bool 389 1.1 christos rl_process_ip_max(struct servtab *sep, int ctrl, time_t *now) { 390 1.1 christos if (sep->se_ip_max != SERVTAB_UNSPEC_SIZE_T) { 391 1.1 christos struct rl_ip_node *node; 392 1.1 christos union addr addr; 393 1.1 christos 394 1.1 christos rl_get_name(sep, ctrl, &addr); 395 1.1 christos node = rl_try_get_ip(sep, &addr); 396 1.1 christos if (node == NULL) { 397 1.1 christos node = rl_add(sep, &addr); 398 1.1 christos if (node == NULL) { 399 1.1 christos /* If rl_add can't allocate, reject request */ 400 1.1 christos DPRINTF("Cannot allocate rl_ip_node"); 401 1.1 christos return false; 402 1.1 christos } 403 1.2 rillig } 404 1.1 christos #ifdef DEBUG_ENABLE 405 1.1 christos else { 406 1.1 christos /* 407 1.1 christos * in a separate function to prevent large stack 408 1.1 christos * frame 409 1.1 christos */ 410 1.1 christos rl_print_found_node(sep, node); 411 1.1 christos } 412 1.1 christos #endif 413 1.1 christos 414 1.1 christos DPRINTF( 415 1.1 christos SERV_FMT ": se_ip_max %zu and ip_count %zu", 416 1.1 christos SERV_PARAMS(sep), sep->se_ip_max, node->count); 417 1.1 christos 418 1.1 christos if (node->count >= sep->se_ip_max) { 419 1.1 christos if (*now == -1) { 420 1.1 christos *now = rl_time(); 421 1.1 christos } 422 1.1 christos 423 1.1 christos if (*now - sep->se_time > CNT_INTVL) { 424 1.1 christos rl_reset(sep, *now); 425 1.1 christos node = rl_add(sep, &addr); 426 1.1 christos if (node == NULL) { 427 1.1 christos DPRINTF("Cannot allocate rl_ip_node"); 428 1.1 christos return false; 429 1.1 christos } 430 1.1 christos } else { 431 1.1 christos if (debug && node->count == sep->se_ip_max) { 432 1.1 christos /* 433 1.1 christos * Only log first failed request to 434 1.1 christos * prevent DoS attack writing to system 435 1.1 christos * log 436 1.1 christos */ 437 1.1 christos rl_log_address_exceed(sep, node); 438 1.1 christos } else { 439 1.1 christos DPRINTF(SERV_FMT 440 1.1 christos ": service not started", 441 1.1 christos SERV_PARAMS(sep)); 442 1.1 christos } 443 1.1 christos 444 1.1 christos rl_drop_connection(sep, ctrl); 445 1.1 christos /* 446 1.1 christos * Increment so debug-syslog message will 447 1.1 christos * trigger only once 448 1.1 christos */ 449 1.1 christos if (node->count < SIZE_MAX) { 450 1.1 christos node->count++; 451 1.1 christos } 452 1.1 christos return false; 453 1.1 christos } 454 1.1 christos } 455 1.1 christos node->count++; 456 1.1 christos } 457 1.1 christos return true; 458 1.1 christos } 459 1.1 christos 460 1.1 christos static bool 461 1.1 christos rl_ip_eq(struct servtab *sep, union addr *addr, struct rl_ip_node *cur) { 462 1.1 christos switch(sep->se_family) { 463 1.1 christos case AF_INET: 464 1.1 christos if (addr->ipv4_addr.s_addr == cur->ipv4_addr.s_addr) { 465 1.1 christos return true; 466 1.1 christos } 467 1.1 christos break; 468 1.1 christos #ifdef INET6 469 1.1 christos case AF_INET6: 470 1.2 rillig if (rl_ipv6_eq(&addr->ipv6_addr, &cur->ipv6_addr)) { 471 1.1 christos return true; 472 1.1 christos } 473 1.1 christos break; 474 1.1 christos #endif 475 1.1 christos default: 476 1.1 christos if (strncmp(cur->other_addr, addr->other_addr, NI_MAXHOST) 477 1.1 christos == 0) { 478 1.1 christos return true; 479 1.1 christos } 480 1.1 christos break; 481 1.1 christos } 482 1.1 christos return false; 483 1.1 christos } 484 1.1 christos 485 1.1 christos #ifdef INET6 486 1.1 christos static bool 487 1.1 christos rl_ipv6_eq(struct in6_addr *a, struct in6_addr *b) 488 1.1 christos { 489 1.1 christos #if UINTMAX_MAX >= UINT64_MAX 490 1.1 christos { /* requires 8 byte aligned structs */ 491 1.1 christos uint64_t *ap = (uint64_t *)a->s6_addr; 492 1.1 christos uint64_t *bp = (uint64_t *)b->s6_addr; 493 1.1 christos return (ap[0] == bp[0]) & (ap[1] == bp[1]); 494 1.1 christos } 495 1.1 christos #else 496 1.1 christos { /* requires 4 byte aligned structs */ 497 1.1 christos uint32_t *ap = (uint32_t *)a->s6_addr; 498 1.1 christos uint32_t *bp = (uint32_t *)b->s6_addr; 499 1.1 christos return ap[0] == bp[0] && ap[1] == bp[1] && 500 1.1 christos ap[2] == bp[2] && ap[3] == bp[3]; 501 1.1 christos } 502 1.1 christos #endif 503 1.1 christos } 504 1.1 christos #endif 505 1.1 christos 506 1.1 christos static const char * 507 1.1 christos rl_node_tostring(struct servtab *sep, struct rl_ip_node *node, 508 1.1 christos char buffer[NI_MAXHOST]) 509 1.1 christos { 510 1.1 christos switch (sep->se_family) { 511 1.1 christos case AF_INET: 512 1.1 christos #ifdef INET6 513 1.1 christos case AF_INET6: 514 1.1 christos #endif 515 1.1 christos /* ipv4_addr/ipv6_addr share same address */ 516 1.1 christos return inet_ntop(sep->se_family, (void*)&node->ipv4_addr, 517 1.1 christos (char*)buffer, NI_MAXHOST); 518 1.1 christos default: 519 1.1 christos return (char *)&node->other_addr; 520 1.1 christos } 521 1.1 christos } 522 1.1 christos 523 1.1 christos #ifdef DEBUG_ENABLE 524 1.1 christos /* Separate function due to large buffer size */ 525 1.1 christos static void 526 1.1 christos rl_print_found_node(struct servtab *sep, struct rl_ip_node *node) 527 1.1 christos { 528 1.1 christos char buffer[NI_MAXHOST]; 529 1.1 christos DPRINTF(SERV_FMT ": found record for address '%s'", 530 1.1 christos SERV_PARAMS(sep), rl_node_tostring(sep, node, buffer)); 531 1.1 christos } 532 1.1 christos #endif 533 1.1 christos 534 1.1 christos /* Separate function due to large buffer sie */ 535 1.1 christos static void 536 1.1 christos rl_log_address_exceed(struct servtab *sep, struct rl_ip_node *node) 537 1.1 christos { 538 1.1 christos char buffer[NI_MAXHOST]; 539 1.1 christos const char * name = rl_node_tostring(sep, node, buffer); 540 1.1 christos syslog(LOG_ERR, SERV_FMT 541 1.1 christos ": max ip spawn rate (%zu in " 542 1.1 christos "%ji seconds) for " 543 1.1 christos "'%." TOSTRING(NI_MAXHOST) "s' " 544 1.1 christos "already met; service not started", 545 1.1 christos SERV_PARAMS(sep), 546 1.1 christos sep->se_ip_max, 547 1.1 christos (intmax_t)CNT_INTVL, 548 1.1 christos name); 549 1.1 christos DPRINTF(SERV_FMT 550 1.1 christos ": max ip spawn rate (%zu in " 551 1.1 christos "%ji seconds) for " 552 1.1 christos "'%." TOSTRING(NI_MAXHOST) "s' " 553 1.1 christos "already met; service not started", 554 1.1 christos SERV_PARAMS(sep), 555 1.1 christos sep->se_ip_max, 556 1.1 christos (intmax_t)CNT_INTVL, 557 1.1 christos name); 558 1.1 christos } 559