1 /*
2  * Copyright 2019-2021 OARC, Inc.
3  * Copyright 2017-2018 Akamai Technologies
4  * Copyright 2006-2016 Nominum, Inc.
5  * All rights reserved.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #include "config.h"
21 
22 #include "net.h"
23 
24 #include "log.h"
25 #include "opt.h"
26 
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <poll.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 
perf_net_parsemode(const char * mode)34 enum perf_net_mode perf_net_parsemode(const char* mode)
35 {
36     if (!strcmp(mode, "udp")) {
37         return sock_udp;
38     } else if (!strcmp(mode, "tcp")) {
39         return sock_tcp;
40     } else if (!strcmp(mode, "tls") || !strcmp(mode, "dot")) {
41         return sock_dot;
42     } else if (!strcmp(mode, "doh")) {
43         return sock_doh;
44     }
45 
46     perf_log_warning("invalid socket mode");
47     perf_opt_usage();
48     exit(1);
49 }
50 
perf_net_parsefamily(const char * family)51 int perf_net_parsefamily(const char* family)
52 {
53     if (family == NULL || strcmp(family, "any") == 0)
54         return AF_UNSPEC;
55     else if (strcmp(family, "inet") == 0)
56         return AF_INET;
57     else if (strcmp(family, "inet6") == 0)
58         return AF_INET6;
59     else {
60         fprintf(stderr, "invalid family %s\n", family);
61         perf_opt_usage();
62         exit(1);
63     }
64 }
65 
perf_sockaddr_fromin(perf_sockaddr_t * sockaddr,const struct in_addr * in,in_port_t port)66 void perf_sockaddr_fromin(perf_sockaddr_t* sockaddr, const struct in_addr* in, in_port_t port)
67 {
68     memset(sockaddr, 0, sizeof(*sockaddr));
69     sockaddr->sa.sin.sin_family = AF_INET;
70     sockaddr->sa.sin.sin_addr   = *in;
71     sockaddr->sa.sin.sin_port   = htons(port);
72     sockaddr->length            = sizeof(sockaddr->sa.sin);
73 }
74 
perf_sockaddr_fromin6(perf_sockaddr_t * sockaddr,const struct in6_addr * in,in_port_t port)75 void perf_sockaddr_fromin6(perf_sockaddr_t* sockaddr, const struct in6_addr* in, in_port_t port)
76 {
77     memset(sockaddr, 0, sizeof(*sockaddr));
78     sockaddr->sa.sin6.sin6_family = AF_INET6;
79     sockaddr->sa.sin6.sin6_addr   = *in;
80     sockaddr->sa.sin6.sin6_port   = htons(port);
81     sockaddr->length              = sizeof(sockaddr->sa.sin6);
82 }
83 
perf_sockaddr_port(const perf_sockaddr_t * sockaddr)84 in_port_t perf_sockaddr_port(const perf_sockaddr_t* sockaddr)
85 {
86     switch (sockaddr->sa.sa.sa_family) {
87     case AF_INET:
88         return ntohs(sockaddr->sa.sin.sin_port);
89     case AF_INET6:
90         return ntohs(sockaddr->sa.sin6.sin6_port);
91     default:
92         break;
93     }
94     return 0;
95 }
96 
perf_sockaddr_setport(perf_sockaddr_t * sockaddr,in_port_t port)97 void perf_sockaddr_setport(perf_sockaddr_t* sockaddr, in_port_t port)
98 {
99     switch (sockaddr->sa.sa.sa_family) {
100     case AF_INET:
101         sockaddr->sa.sin.sin_port = htons(port);
102         break;
103     case AF_INET6:
104         sockaddr->sa.sin6.sin6_port = htons(port);
105         break;
106     default:
107         break;
108     }
109 }
110 
perf_sockaddr_format(const perf_sockaddr_t * sockaddr,char * buf,size_t len)111 void perf_sockaddr_format(const perf_sockaddr_t* sockaddr, char* buf, size_t len)
112 {
113     const void* src;
114 
115     *buf = 0;
116 
117     switch (sockaddr->sa.sa.sa_family) {
118     case AF_INET:
119         src = &sockaddr->sa.sin.sin_addr;
120         break;
121     case AF_INET6:
122         src = &sockaddr->sa.sin6.sin6_addr;
123         break;
124     default:
125         return;
126     }
127 
128     (void)inet_ntop(sockaddr->sa.sa.sa_family, src, buf, len);
129 }
130 
perf_net_parseserver(int family,const char * name,unsigned int port,perf_sockaddr_t * addr)131 void perf_net_parseserver(int family, const char* name, unsigned int port, perf_sockaddr_t* addr)
132 {
133     struct addrinfo* ai;
134 
135     if (getaddrinfo(name, 0, 0, &ai) == 0) {
136         struct addrinfo* a;
137 
138         for (a = ai; a; a = a->ai_next) {
139             if (a->ai_family == family || family == AF_UNSPEC) {
140                 switch (a->ai_family) {
141                 case AF_INET:
142                     perf_sockaddr_fromin(addr, &((struct sockaddr_in*)a->ai_addr)->sin_addr, port);
143                     break;
144                 case AF_INET6:
145                     perf_sockaddr_fromin6(addr, &((struct sockaddr_in6*)a->ai_addr)->sin6_addr, port);
146                     break;
147                 default:
148                     continue;
149                 }
150 
151                 freeaddrinfo(ai);
152                 return;
153             }
154         }
155         freeaddrinfo(ai);
156     }
157 
158     fprintf(stderr, "invalid server address %s\n", name);
159     perf_opt_usage();
160     exit(1);
161 }
162 
perf_net_parselocal(int family,const char * name,unsigned int port,perf_sockaddr_t * addr)163 void perf_net_parselocal(int family, const char* name, unsigned int port,
164     perf_sockaddr_t* addr)
165 {
166     struct in_addr  in4a;
167     struct in6_addr in6a;
168 
169     if (name == NULL) {
170         switch (family) {
171         case AF_INET:
172             in4a.s_addr = INADDR_ANY;
173             perf_sockaddr_fromin(addr, &in4a, port);
174             return;
175         case AF_INET6:
176             perf_sockaddr_fromin6(addr, &in6addr_any, port);
177             return;
178         default:
179             break;
180         }
181     } else if (inet_pton(AF_INET, name, &in4a) == 1) {
182         perf_sockaddr_fromin(addr, &in4a, port);
183         return;
184     } else if (inet_pton(AF_INET6, name, &in6a) == 1) {
185         perf_sockaddr_fromin6(addr, &in6a, port);
186         return;
187     }
188 
189     fprintf(stderr, "invalid local address %s\n", name);
190     perf_opt_usage();
191     exit(1);
192 }
193 
perf_net_opensocket(enum perf_net_mode mode,const perf_sockaddr_t * server,const perf_sockaddr_t * local,unsigned int offset,size_t bufsize,void * data,perf_net_sent_cb_t sent,perf_net_event_cb_t event)194 struct perf_net_socket* perf_net_opensocket(enum perf_net_mode mode, const perf_sockaddr_t* server, const perf_sockaddr_t* local, unsigned int offset, size_t bufsize, void* data, perf_net_sent_cb_t sent, perf_net_event_cb_t event)
195 {
196     int             port;
197     perf_sockaddr_t tmp;
198 
199     if (server->sa.sa.sa_family != local->sa.sa.sa_family) {
200         perf_log_fatal("server and local addresses have different families");
201     }
202 
203     tmp  = *local;
204     port = perf_sockaddr_port(&tmp);
205     if (port != 0 && offset != 0) {
206         port += offset;
207         if (port >= 0xFFFF)
208             perf_log_fatal("port %d out of range", port);
209         perf_sockaddr_setport(&tmp, port);
210     }
211 
212     switch (mode) {
213     case sock_udp:
214         return perf_net_udp_opensocket(server, &tmp, bufsize, data, sent, event);
215     case sock_tcp:
216         return perf_net_tcp_opensocket(server, &tmp, bufsize, data, sent, event);
217     case sock_dot:
218         return perf_net_dot_opensocket(server, &tmp, bufsize, data, sent, event);
219     case sock_doh:
220         return perf_net_doh_opensocket(server, &tmp, bufsize, data, sent, event);
221     default:
222         perf_log_fatal("perf_net_opensocket(): invalid mode");
223     }
224 
225     return 0;
226 }
227 
perf_net_stats_init(enum perf_net_mode mode)228 void perf_net_stats_init(enum perf_net_mode mode)
229 {
230     switch (mode) {
231     case sock_doh:
232         perf_net_doh_stats_init();
233     default:
234         break;
235     }
236 }
237 
perf_net_stats_compile(enum perf_net_mode mode,struct perf_net_socket * sock)238 void perf_net_stats_compile(enum perf_net_mode mode, struct perf_net_socket* sock)
239 {
240     switch (mode) {
241     case sock_doh:
242         perf_net_doh_stats_compile(sock);
243     default:
244         break;
245     }
246 }
247 
perf_net_stats_print(enum perf_net_mode mode)248 void perf_net_stats_print(enum perf_net_mode mode)
249 {
250     switch (mode) {
251     case sock_doh:
252         perf_net_doh_stats_print();
253     default:
254         break;
255     }
256 }
257