FILE
udpclash.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/net/netfilter/udpclash.c
1// SPDX-License-Identifier: GPL-2.0 2 3/* Usage: ./udpclash <IP> <PORT> 4 * 5 * Emit THREAD_COUNT UDP packets sharing the same saddr:daddr pair. 6 * 7 * This mimics DNS resolver libraries that emit A and AAAA requests 8 * in parallel. 9 * 10 * This exercises conntrack clash resolution logic added and later 11 * refined in 12 * 13 * 71d8c47fc653 ("netfilter: conntrack: introduce clash resolution on insertion race") 14 * ed07d9a021df ("netfilter: nf_conntrack: resolve clash for matching conntracks") 15 * 6a757c07e51f ("netfilter: conntrack: allow insertion of clashing entries") 16 */ 17#include <stdio.h> 18#include <string.h> 19#include <stdlib.h> 20#include <unistd.h> 21#include <arpa/inet.h> 22#include <sys/socket.h> 23#include <pthread.h> 24 25#define THREAD_COUNT 128 26 27struct thread_args { 28 const struct sockaddr_in *si_remote; 29 int sockfd; 30}; 31 32static volatile int wait = 1; 33 34static void *thread_main(void *varg) 35{ 36 const struct sockaddr_in *si_remote; 37 const struct thread_args *args = varg; 38 static const char msg[] = "foo"; 39 40 si_remote = args->si_remote; 41 42 while (wait == 1) 43 ; 44 45 if (sendto(args->sockfd, msg, strlen(msg), MSG_NOSIGNAL, 46 (struct sockaddr *)si_remote, sizeof(*si_remote)) < 0) 47 exit(111); 48 49 return varg; 50} 51 52static int run_test(int fd, const struct sockaddr_in *si_remote) 53{ 54 struct thread_args thread_args = { 55 .si_remote = si_remote, 56 .sockfd = fd, 57 }; 58 pthread_t *tid = calloc(THREAD_COUNT, sizeof(pthread_t)); 59 unsigned int repl_count = 0, timeout = 0; 60 int i; 61 62 if (!tid) { 63 perror("calloc"); 64 return 1; 65 } 66 67 for (i = 0; i < THREAD_COUNT; i++) { 68 int err = pthread_create(&tid[i], NULL, &thread_main, &thread_args); 69 70 if (err != 0) { 71 perror("pthread_create"); 72 exit(1); 73 } 74 } 75 76 wait = 0; 77 78 for (i = 0; i < THREAD_COUNT; i++) 79 pthread_join(tid[i], NULL); 80 81 while (repl_count < THREAD_COUNT) { 82 struct sockaddr_in si_repl; 83 socklen_t si_repl_len = sizeof(si_repl); 84 char repl[512]; 85 ssize_t ret; 86 87 ret = recvfrom(fd, repl, sizeof(repl), MSG_NOSIGNAL, 88 (struct sockaddr *) &si_repl, &si_repl_len); 89 if (ret < 0) { 90 if (timeout++ > 5000) { 91 fputs("timed out while waiting for reply from thread\n", stderr); 92 break; 93 } 94 95 /* give reply time to pass though the stack */ 96 usleep(1000); 97 continue; 98 } 99 100 if (si_repl_len != sizeof(*si_remote)) { 101 fprintf(stderr, "warning: reply has unexpected repl_len %d vs %d\n", 102 (int)si_repl_len, (int)sizeof(si_repl)); 103 } else if (si_remote->sin_addr.s_addr != si_repl.sin_addr.s_addr || 104 si_remote->sin_port != si_repl.sin_port) { 105 char a[64], b[64]; 106 107 inet_ntop(AF_INET, &si_remote->sin_addr, a, sizeof(a)); 108 inet_ntop(AF_INET, &si_repl.sin_addr, b, sizeof(b)); 109 110 fprintf(stderr, "reply from wrong source: want %s:%d got %s:%d\n", 111 a, ntohs(si_remote->sin_port), b, ntohs(si_repl.sin_port)); 112 } 113 114 repl_count++; 115 } 116 117 printf("got %d of %d replies\n", repl_count, THREAD_COUNT); 118 119 free(tid); 120 121 return repl_count == THREAD_COUNT ? 0 : 1; 122} 123 124int main(int argc, char *argv[]) 125{ 126 struct sockaddr_in si_local = { 127 .sin_family = AF_INET, 128 }; 129 struct sockaddr_in si_remote = { 130 .sin_family = AF_INET, 131 }; 132 int fd, ret; 133 134 if (argc < 3) { 135 fputs("Usage: send_udp <daddr> <dport>\n", stderr); 136 return 1; 137 } 138 139 si_remote.sin_port = htons(atoi(argv[2])); 140 si_remote.sin_addr.s_addr = inet_addr(argv[1]); 141 142 fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_UDP); 143 if (fd < 0) { 144 perror("socket"); 145 return 1; 146 } 147 148 if (bind(fd, (struct sockaddr *)&si_local, sizeof(si_local)) < 0) { 149 perror("bind"); 150 return 1; 151 } 152 153 ret = run_test(fd, &si_remote); 154 155 close(fd); 156 157 return ret; 158}
~/
Read-only file preview.