Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* Socket union related function. |
3 | | * Copyright (c) 1997, 98 Kunihiro Ishiguro |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include "prefix.h" |
9 | | #include "vty.h" |
10 | | #include "sockunion.h" |
11 | | #include "memory.h" |
12 | | #include "log.h" |
13 | | #include "jhash.h" |
14 | | #include "lib_errors.h" |
15 | | #include "printfrr.h" |
16 | | |
17 | 8 | DEFINE_MTYPE_STATIC(LIB, SOCKUNION, "Socket union"); |
18 | 8 | |
19 | 8 | const char *inet_sutop(const union sockunion *su, char *str) |
20 | 8 | { |
21 | 0 | switch (su->sa.sa_family) { |
22 | 0 | case AF_INET: |
23 | 0 | inet_ntop(AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN); |
24 | 0 | break; |
25 | 0 | case AF_INET6: |
26 | 0 | inet_ntop(AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN); |
27 | 0 | break; |
28 | 0 | } |
29 | 0 | return str; |
30 | 0 | } |
31 | | |
32 | | int str2sockunion(const char *str, union sockunion *su) |
33 | 0 | { |
34 | 0 | int ret; |
35 | |
|
36 | 0 | if (str == NULL) |
37 | 0 | return -1; |
38 | | |
39 | 0 | memset(su, 0, sizeof(union sockunion)); |
40 | |
|
41 | 0 | ret = inet_pton(AF_INET, str, &su->sin.sin_addr); |
42 | 0 | if (ret > 0) /* Valid IPv4 address format. */ |
43 | 0 | { |
44 | 0 | su->sin.sin_family = AF_INET; |
45 | | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN |
46 | | su->sin.sin_len = sizeof(struct sockaddr_in); |
47 | | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ |
48 | 0 | return 0; |
49 | 0 | } |
50 | 0 | ret = inet_pton(AF_INET6, str, &su->sin6.sin6_addr); |
51 | 0 | if (ret > 0) /* Valid IPv6 address format. */ |
52 | 0 | { |
53 | 0 | su->sin6.sin6_family = AF_INET6; |
54 | | #ifdef SIN6_LEN |
55 | | su->sin6.sin6_len = sizeof(struct sockaddr_in6); |
56 | | #endif /* SIN6_LEN */ |
57 | 0 | return 0; |
58 | 0 | } |
59 | 0 | return -1; |
60 | 0 | } |
61 | | |
62 | | const char *sockunion2str(const union sockunion *su, char *buf, size_t len) |
63 | 1 | { |
64 | 1 | switch (sockunion_family(su)) { |
65 | 0 | case AF_UNSPEC: |
66 | 0 | snprintf(buf, len, "(unspec)"); |
67 | 0 | return buf; |
68 | 1 | case AF_INET: |
69 | 1 | return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len); |
70 | 0 | case AF_INET6: |
71 | 0 | return inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, len); |
72 | 1 | } |
73 | 0 | snprintf(buf, len, "(af %d)", sockunion_family(su)); |
74 | 0 | return buf; |
75 | 1 | } |
76 | | |
77 | | union sockunion *sockunion_str2su(const char *str) |
78 | 0 | { |
79 | 0 | union sockunion *su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
80 | |
|
81 | 0 | if (!str2sockunion(str, su)) |
82 | 0 | return su; |
83 | | |
84 | 0 | XFREE(MTYPE_SOCKUNION, su); |
85 | 0 | return NULL; |
86 | 0 | } |
87 | | |
88 | | /* Convert IPv4 compatible IPv6 address to IPv4 address. */ |
89 | | static void sockunion_normalise_mapped(union sockunion *su) |
90 | 0 | { |
91 | 0 | struct sockaddr_in sin; |
92 | |
|
93 | 0 | if (su->sa.sa_family == AF_INET6 |
94 | 0 | && IN6_IS_ADDR_V4MAPPED(&su->sin6.sin6_addr)) { |
95 | 0 | memset(&sin, 0, sizeof(sin)); |
96 | 0 | sin.sin_family = AF_INET; |
97 | 0 | sin.sin_port = su->sin6.sin6_port; |
98 | 0 | memcpy(&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4); |
99 | 0 | memcpy(su, &sin, sizeof(struct sockaddr_in)); |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | /* return sockunion structure : this function should be revised. */ |
104 | | static const char *sockunion_log(const union sockunion *su, char *buf, |
105 | | size_t len) |
106 | 0 | { |
107 | 0 | switch (su->sa.sa_family) { |
108 | 0 | case AF_INET: |
109 | 0 | return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len); |
110 | 0 |
|
111 | 0 | case AF_INET6: |
112 | 0 | return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len); |
113 | 0 |
|
114 | 0 | default: |
115 | 0 | snprintf(buf, len, "af_unknown %d ", su->sa.sa_family); |
116 | 0 | return buf; |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | /* Return socket of sockunion. */ |
121 | | int sockunion_socket(const union sockunion *su) |
122 | 0 | { |
123 | 0 | int sock; |
124 | |
|
125 | 0 | sock = socket(su->sa.sa_family, SOCK_STREAM, 0); |
126 | 0 | if (sock < 0) { |
127 | 0 | char buf[SU_ADDRSTRLEN]; |
128 | 0 | flog_err(EC_LIB_SOCKET, "Can't make socket for %s : %s", |
129 | 0 | sockunion_log(su, buf, SU_ADDRSTRLEN), |
130 | 0 | safe_strerror(errno)); |
131 | 0 | return -1; |
132 | 0 | } |
133 | | |
134 | 0 | return sock; |
135 | 0 | } |
136 | | |
137 | | /* Return accepted new socket file descriptor. */ |
138 | | int sockunion_accept(int sock, union sockunion *su) |
139 | 0 | { |
140 | 0 | socklen_t len; |
141 | 0 | int client_sock; |
142 | |
|
143 | 0 | len = sizeof(union sockunion); |
144 | 0 | client_sock = accept(sock, (struct sockaddr *)su, &len); |
145 | |
|
146 | 0 | sockunion_normalise_mapped(su); |
147 | 0 | return client_sock; |
148 | 0 | } |
149 | | |
150 | | /* Return sizeof union sockunion. */ |
151 | | int sockunion_sizeof(const union sockunion *su) |
152 | 0 | { |
153 | 0 | int ret; |
154 | |
|
155 | 0 | ret = 0; |
156 | 0 | switch (su->sa.sa_family) { |
157 | 0 | case AF_INET: |
158 | 0 | ret = sizeof(struct sockaddr_in); |
159 | 0 | break; |
160 | 0 | case AF_INET6: |
161 | 0 | ret = sizeof(struct sockaddr_in6); |
162 | 0 | break; |
163 | 0 | } |
164 | 0 | return ret; |
165 | 0 | } |
166 | | |
167 | | /* Performs a non-blocking connect(). */ |
168 | | enum connect_result sockunion_connect(int fd, const union sockunion *peersu, |
169 | | unsigned short port, ifindex_t ifindex) |
170 | 0 | { |
171 | 0 | int ret; |
172 | 0 | union sockunion su; |
173 | |
|
174 | 0 | memcpy(&su, peersu, sizeof(union sockunion)); |
175 | |
|
176 | 0 | switch (su.sa.sa_family) { |
177 | 0 | case AF_INET: |
178 | 0 | su.sin.sin_port = port; |
179 | 0 | break; |
180 | 0 | case AF_INET6: |
181 | 0 | su.sin6.sin6_port = port; |
182 | | #ifdef KAME |
183 | | if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) { |
184 | | su.sin6.sin6_scope_id = ifindex; |
185 | | SET_IN6_LINKLOCAL_IFINDEX(su.sin6.sin6_addr, ifindex); |
186 | | } |
187 | | #endif /* KAME */ |
188 | 0 | break; |
189 | 0 | } |
190 | | |
191 | | /* Call connect function. */ |
192 | 0 | ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su)); |
193 | | |
194 | | /* Immediate success */ |
195 | 0 | if (ret == 0) |
196 | 0 | return connect_success; |
197 | | |
198 | | /* If connect is in progress then return 1 else it's real error. */ |
199 | 0 | if (ret < 0) { |
200 | 0 | if (errno != EINPROGRESS) { |
201 | 0 | char str[SU_ADDRSTRLEN]; |
202 | 0 | zlog_info("can't connect to %s fd %d : %s", |
203 | 0 | sockunion_log(&su, str, sizeof(str)), fd, |
204 | 0 | safe_strerror(errno)); |
205 | 0 | return connect_error; |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | 0 | return connect_in_progress; |
210 | 0 | } |
211 | | |
212 | | /* Make socket from sockunion union. */ |
213 | | int sockunion_stream_socket(union sockunion *su) |
214 | 0 | { |
215 | 0 | int sock; |
216 | |
|
217 | 0 | if (su->sa.sa_family == 0) |
218 | 0 | su->sa.sa_family = AF_INET_UNION; |
219 | |
|
220 | 0 | sock = socket(su->sa.sa_family, SOCK_STREAM, 0); |
221 | |
|
222 | 0 | if (sock < 0) |
223 | 0 | flog_err(EC_LIB_SOCKET, |
224 | 0 | "can't make socket sockunion_stream_socket"); |
225 | |
|
226 | 0 | return sock; |
227 | 0 | } |
228 | | |
229 | | /* Bind socket to specified address. */ |
230 | | int sockunion_bind(int sock, union sockunion *su, unsigned short port, |
231 | | union sockunion *su_addr) |
232 | 0 | { |
233 | 0 | int size = 0; |
234 | 0 | int ret; |
235 | |
|
236 | 0 | if (su->sa.sa_family == AF_INET) { |
237 | 0 | size = sizeof(struct sockaddr_in); |
238 | 0 | su->sin.sin_port = htons(port); |
239 | | #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN |
240 | | su->sin.sin_len = size; |
241 | | #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ |
242 | 0 | if (su_addr == NULL) |
243 | 0 | sockunion2ip(su) = htonl(INADDR_ANY); |
244 | 0 | } else if (su->sa.sa_family == AF_INET6) { |
245 | 0 | size = sizeof(struct sockaddr_in6); |
246 | 0 | su->sin6.sin6_port = htons(port); |
247 | | #ifdef SIN6_LEN |
248 | | su->sin6.sin6_len = size; |
249 | | #endif /* SIN6_LEN */ |
250 | 0 | if (su_addr == NULL) { |
251 | 0 | #ifdef LINUX_IPV6 |
252 | 0 | memset(&su->sin6.sin6_addr, 0, sizeof(struct in6_addr)); |
253 | | #else |
254 | | su->sin6.sin6_addr = in6addr_any; |
255 | | #endif /* LINUX_IPV6 */ |
256 | 0 | } |
257 | 0 | } |
258 | |
|
259 | 0 | ret = bind(sock, (struct sockaddr *)su, size); |
260 | 0 | if (ret < 0) { |
261 | 0 | char buf[SU_ADDRSTRLEN]; |
262 | 0 | flog_err(EC_LIB_SOCKET, "can't bind socket for %s : %s", |
263 | 0 | sockunion_log(su, buf, SU_ADDRSTRLEN), |
264 | 0 | safe_strerror(errno)); |
265 | 0 | } |
266 | |
|
267 | 0 | return ret; |
268 | 0 | } |
269 | | |
270 | | int sockopt_reuseaddr(int sock) |
271 | 0 | { |
272 | 0 | int ret; |
273 | 0 | int on = 1; |
274 | |
|
275 | 0 | ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, |
276 | 0 | sizeof(on)); |
277 | 0 | if (ret < 0) { |
278 | 0 | flog_err( |
279 | 0 | EC_LIB_SOCKET, |
280 | 0 | "can't set sockopt SO_REUSEADDR to socket %d errno=%d: %s", |
281 | 0 | sock, errno, safe_strerror(errno)); |
282 | 0 | return -1; |
283 | 0 | } |
284 | 0 | return 0; |
285 | 0 | } |
286 | | |
287 | | #ifdef SO_REUSEPORT |
288 | | int sockopt_reuseport(int sock) |
289 | 0 | { |
290 | 0 | int ret; |
291 | 0 | int on = 1; |
292 | |
|
293 | 0 | ret = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on, |
294 | 0 | sizeof(on)); |
295 | 0 | if (ret < 0) { |
296 | 0 | flog_err(EC_LIB_SOCKET, |
297 | 0 | "can't set sockopt SO_REUSEPORT to socket %d", sock); |
298 | 0 | return -1; |
299 | 0 | } |
300 | 0 | return 0; |
301 | 0 | } |
302 | | #else |
303 | | int sockopt_reuseport(int sock) |
304 | | { |
305 | | return 0; |
306 | | } |
307 | | #endif /* 0 */ |
308 | | |
309 | | int sockopt_ttl(int family, int sock, int ttl) |
310 | 0 | { |
311 | 0 | int ret; |
312 | |
|
313 | 0 | #ifdef IP_TTL |
314 | 0 | if (family == AF_INET) { |
315 | 0 | ret = setsockopt(sock, IPPROTO_IP, IP_TTL, (void *)&ttl, |
316 | 0 | sizeof(int)); |
317 | 0 | if (ret < 0) { |
318 | 0 | flog_err(EC_LIB_SOCKET, |
319 | 0 | "can't set sockopt IP_TTL %d to socket %d", |
320 | 0 | ttl, sock); |
321 | 0 | return -1; |
322 | 0 | } |
323 | 0 | return 0; |
324 | 0 | } |
325 | 0 | #endif /* IP_TTL */ |
326 | 0 | if (family == AF_INET6) { |
327 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, |
328 | 0 | (void *)&ttl, sizeof(int)); |
329 | 0 | if (ret < 0) { |
330 | 0 | flog_err( |
331 | 0 | EC_LIB_SOCKET, |
332 | 0 | "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d", |
333 | 0 | ttl, sock); |
334 | 0 | return -1; |
335 | 0 | } |
336 | 0 | return 0; |
337 | 0 | } |
338 | 0 | return 0; |
339 | 0 | } |
340 | | |
341 | | int sockopt_minttl(int family, int sock, int minttl) |
342 | 0 | { |
343 | 0 | #ifdef IP_MINTTL |
344 | 0 | if (family == AF_INET) { |
345 | 0 | int ret = setsockopt(sock, IPPROTO_IP, IP_MINTTL, &minttl, |
346 | 0 | sizeof(minttl)); |
347 | 0 | if (ret < 0) |
348 | 0 | flog_err( |
349 | 0 | EC_LIB_SOCKET, |
350 | 0 | "can't set sockopt IP_MINTTL to %d on socket %d: %s", |
351 | 0 | minttl, sock, safe_strerror(errno)); |
352 | 0 | return ret; |
353 | 0 | } |
354 | 0 | #endif /* IP_MINTTL */ |
355 | 0 | #ifdef IPV6_MINHOPCOUNT |
356 | 0 | if (family == AF_INET6) { |
357 | 0 | int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MINHOPCOUNT, |
358 | 0 | &minttl, sizeof(minttl)); |
359 | 0 | if (ret < 0) |
360 | 0 | flog_err( |
361 | 0 | EC_LIB_SOCKET, |
362 | 0 | "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s", |
363 | 0 | minttl, sock, safe_strerror(errno)); |
364 | 0 | return ret; |
365 | 0 | } |
366 | 0 | #endif |
367 | | |
368 | 0 | errno = EOPNOTSUPP; |
369 | 0 | return -1; |
370 | 0 | } |
371 | | |
372 | | int sockopt_v6only(int family, int sock) |
373 | 0 | { |
374 | 0 | int ret, on = 1; |
375 | |
|
376 | 0 | #ifdef IPV6_V6ONLY |
377 | 0 | if (family == AF_INET6) { |
378 | 0 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, |
379 | 0 | sizeof(int)); |
380 | 0 | if (ret < 0) { |
381 | 0 | flog_err(EC_LIB_SOCKET, |
382 | 0 | "can't set sockopt IPV6_V6ONLY to socket %d", |
383 | 0 | sock); |
384 | 0 | return -1; |
385 | 0 | } |
386 | 0 | return 0; |
387 | 0 | } |
388 | 0 | #endif /* IPV6_V6ONLY */ |
389 | 0 | return 0; |
390 | 0 | } |
391 | | |
392 | | /* If same family and same prefix return 1. */ |
393 | | int sockunion_same(const union sockunion *su1, const union sockunion *su2) |
394 | 0 | { |
395 | 0 | int ret = 0; |
396 | |
|
397 | 0 | if (su1->sa.sa_family != su2->sa.sa_family) |
398 | 0 | return 0; |
399 | | |
400 | 0 | switch (su1->sa.sa_family) { |
401 | 0 | case AF_INET: |
402 | 0 | ret = memcmp(&su1->sin.sin_addr, &su2->sin.sin_addr, |
403 | 0 | sizeof(struct in_addr)); |
404 | 0 | break; |
405 | 0 | case AF_INET6: |
406 | 0 | ret = memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, |
407 | 0 | sizeof(struct in6_addr)); |
408 | 0 | if ((ret == 0) && IN6_IS_ADDR_LINKLOCAL(&su1->sin6.sin6_addr)) { |
409 | | /* compare interface indices */ |
410 | 0 | if (su1->sin6.sin6_scope_id && su2->sin6.sin6_scope_id) |
411 | 0 | ret = (su1->sin6.sin6_scope_id |
412 | 0 | == su2->sin6.sin6_scope_id) |
413 | 0 | ? 0 |
414 | 0 | : 1; |
415 | 0 | } |
416 | 0 | break; |
417 | 0 | } |
418 | 0 | if (ret == 0) |
419 | 0 | return 1; |
420 | 0 | else |
421 | 0 | return 0; |
422 | 0 | } |
423 | | |
424 | | unsigned int sockunion_hash(const union sockunion *su) |
425 | 1 | { |
426 | 1 | switch (sockunion_family(su)) { |
427 | 1 | case AF_INET: |
428 | 1 | return jhash_1word(su->sin.sin_addr.s_addr, 0); |
429 | 0 | case AF_INET6: |
430 | 0 | return jhash2(su->sin6.sin6_addr.s6_addr32, |
431 | 0 | array_size(su->sin6.sin6_addr.s6_addr32), 0); |
432 | 1 | } |
433 | 0 | return 0; |
434 | 1 | } |
435 | | |
436 | | size_t family2addrsize(int family) |
437 | 0 | { |
438 | 0 | switch (family) { |
439 | 0 | case AF_INET: |
440 | 0 | return sizeof(struct in_addr); |
441 | 0 | case AF_INET6: |
442 | 0 | return sizeof(struct in6_addr); |
443 | 0 | } |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | | size_t sockunion_get_addrlen(const union sockunion *su) |
448 | 0 | { |
449 | 0 | return family2addrsize(sockunion_family(su)); |
450 | 0 | } |
451 | | |
452 | | const uint8_t *sockunion_get_addr(const union sockunion *su) |
453 | 0 | { |
454 | 0 | switch (sockunion_family(su)) { |
455 | 0 | case AF_INET: |
456 | 0 | return (const uint8_t *)&su->sin.sin_addr.s_addr; |
457 | 0 | case AF_INET6: |
458 | 0 | return (const uint8_t *)&su->sin6.sin6_addr; |
459 | 0 | } |
460 | 0 | return NULL; |
461 | 0 | } |
462 | | |
463 | | void sockunion_set(union sockunion *su, int family, const uint8_t *addr, |
464 | | size_t bytes) |
465 | 0 | { |
466 | 0 | if (family2addrsize(family) != bytes) |
467 | 0 | return; |
468 | | |
469 | 0 | sockunion_family(su) = family; |
470 | 0 | switch (family) { |
471 | 0 | case AF_INET: |
472 | 0 | memcpy(&su->sin.sin_addr.s_addr, addr, bytes); |
473 | 0 | break; |
474 | 0 | case AF_INET6: |
475 | 0 | memcpy(&su->sin6.sin6_addr, addr, bytes); |
476 | 0 | break; |
477 | 0 | } |
478 | 0 | } |
479 | | |
480 | | /* After TCP connection is established. Get local address and port. */ |
481 | | union sockunion *sockunion_getsockname(int fd) |
482 | 0 | { |
483 | 0 | int ret; |
484 | 0 | socklen_t len; |
485 | 0 | union { |
486 | 0 | struct sockaddr sa; |
487 | 0 | struct sockaddr_in sin; |
488 | 0 | struct sockaddr_in6 sin6; |
489 | 0 | char tmp_buffer[128]; |
490 | 0 | } name; |
491 | 0 | union sockunion *su; |
492 | |
|
493 | 0 | memset(&name, 0, sizeof(name)); |
494 | 0 | len = sizeof(name); |
495 | |
|
496 | 0 | ret = getsockname(fd, (struct sockaddr *)&name, &len); |
497 | 0 | if (ret < 0) { |
498 | 0 | flog_err(EC_LIB_SOCKET, |
499 | 0 | "Can't get local address and port by getsockname: %s", |
500 | 0 | safe_strerror(errno)); |
501 | 0 | return NULL; |
502 | 0 | } |
503 | | |
504 | 0 | if (name.sa.sa_family == AF_INET) { |
505 | 0 | su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
506 | 0 | memcpy(su, &name, sizeof(struct sockaddr_in)); |
507 | 0 | return su; |
508 | 0 | } |
509 | 0 | if (name.sa.sa_family == AF_INET6) { |
510 | 0 | su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
511 | 0 | memcpy(su, &name, sizeof(struct sockaddr_in6)); |
512 | 0 | sockunion_normalise_mapped(su); |
513 | 0 | return su; |
514 | 0 | } |
515 | | |
516 | 0 | flog_err( |
517 | 0 | EC_LIB_SOCKET, |
518 | 0 | "Unexpected AFI received(%d) for sockunion_getsockname call for fd: %d", |
519 | 0 | name.sa.sa_family, fd); |
520 | 0 | return NULL; |
521 | 0 | } |
522 | | |
523 | | /* After TCP connection is established. Get remote address and port. */ |
524 | | union sockunion *sockunion_getpeername(int fd) |
525 | 0 | { |
526 | 0 | int ret; |
527 | 0 | socklen_t len; |
528 | 0 | union { |
529 | 0 | struct sockaddr sa; |
530 | 0 | struct sockaddr_in sin; |
531 | 0 | struct sockaddr_in6 sin6; |
532 | 0 | char tmp_buffer[128]; |
533 | 0 | } name; |
534 | 0 | union sockunion *su; |
535 | |
|
536 | 0 | memset(&name, 0, sizeof(name)); |
537 | 0 | len = sizeof(name); |
538 | 0 | ret = getpeername(fd, (struct sockaddr *)&name, &len); |
539 | 0 | if (ret < 0) { |
540 | 0 | flog_err(EC_LIB_SOCKET, "Can't get remote address and port: %s", |
541 | 0 | safe_strerror(errno)); |
542 | 0 | return NULL; |
543 | 0 | } |
544 | | |
545 | 0 | if (name.sa.sa_family == AF_INET) { |
546 | 0 | su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
547 | 0 | memcpy(su, &name, sizeof(struct sockaddr_in)); |
548 | 0 | return su; |
549 | 0 | } |
550 | 0 | if (name.sa.sa_family == AF_INET6) { |
551 | 0 | su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
552 | 0 | memcpy(su, &name, sizeof(struct sockaddr_in6)); |
553 | 0 | sockunion_normalise_mapped(su); |
554 | 0 | return su; |
555 | 0 | } |
556 | | |
557 | 0 | flog_err( |
558 | 0 | EC_LIB_SOCKET, |
559 | 0 | "Unexpected AFI received(%d) for sockunion_getpeername call for fd: %d", |
560 | 0 | name.sa.sa_family, fd); |
561 | 0 | return NULL; |
562 | 0 | } |
563 | | |
564 | | /* Print sockunion structure */ |
565 | | static void __attribute__((unused)) sockunion_print(const union sockunion *su) |
566 | 0 | { |
567 | 0 | if (su == NULL) |
568 | 0 | return; |
569 | 0 |
|
570 | 0 | switch (su->sa.sa_family) { |
571 | 0 | case AF_INET: |
572 | 0 | printf("%pI4\n", &su->sin.sin_addr); |
573 | 0 | break; |
574 | 0 | case AF_INET6: |
575 | 0 | printf("%pI6\n", &su->sin6.sin6_addr); |
576 | 0 | break; |
577 | 0 | #ifdef AF_LINK |
578 | 0 | case AF_LINK: { |
579 | 0 | struct sockaddr_dl *sdl; |
580 | 0 |
|
581 | 0 | sdl = (struct sockaddr_dl *)&(su->sa); |
582 | 0 | printf("link#%d\n", sdl->sdl_index); |
583 | 0 | } break; |
584 | 0 | #endif /* AF_LINK */ |
585 | 0 | default: |
586 | 0 | printf("af_unknown %d\n", su->sa.sa_family); |
587 | 0 | break; |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | | int in6addr_cmp(const struct in6_addr *addr1, const struct in6_addr *addr2) |
592 | 0 | { |
593 | 0 | unsigned int i; |
594 | 0 | const uint8_t *p1, *p2; |
595 | |
|
596 | 0 | p1 = (const uint8_t *)addr1; |
597 | 0 | p2 = (const uint8_t *)addr2; |
598 | |
|
599 | 0 | for (i = 0; i < sizeof(struct in6_addr); i++) { |
600 | 0 | if (p1[i] > p2[i]) |
601 | 0 | return 1; |
602 | 0 | else if (p1[i] < p2[i]) |
603 | 0 | return -1; |
604 | 0 | } |
605 | 0 | return 0; |
606 | 0 | } |
607 | | |
608 | | int sockunion_cmp(const union sockunion *su1, const union sockunion *su2) |
609 | 0 | { |
610 | 0 | if (su1->sa.sa_family > su2->sa.sa_family) |
611 | 0 | return 1; |
612 | 0 | if (su1->sa.sa_family < su2->sa.sa_family) |
613 | 0 | return -1; |
614 | | |
615 | 0 | if (su1->sa.sa_family == AF_INET) { |
616 | 0 | if (ntohl(sockunion2ip(su1)) == ntohl(sockunion2ip(su2))) |
617 | 0 | return 0; |
618 | 0 | if (ntohl(sockunion2ip(su1)) > ntohl(sockunion2ip(su2))) |
619 | 0 | return 1; |
620 | 0 | else |
621 | 0 | return -1; |
622 | 0 | } |
623 | 0 | if (su1->sa.sa_family == AF_INET6) |
624 | 0 | return in6addr_cmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr); |
625 | 0 | return 0; |
626 | 0 | } |
627 | | |
628 | | /* Duplicate sockunion. */ |
629 | | union sockunion *sockunion_dup(const union sockunion *su) |
630 | 0 | { |
631 | 0 | union sockunion *dup = |
632 | 0 | XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion)); |
633 | 0 | memcpy(dup, su, sizeof(union sockunion)); |
634 | 0 | return dup; |
635 | 0 | } |
636 | | |
637 | | void sockunion_free(union sockunion *su) |
638 | 0 | { |
639 | 0 | XFREE(MTYPE_SOCKUNION, su); |
640 | 0 | } |
641 | | |
642 | | void sockunion_init(union sockunion *su) |
643 | 1 | { |
644 | 1 | memset(su, 0, sizeof(union sockunion)); |
645 | 1 | } |
646 | | |
647 | | printfrr_ext_autoreg_p("SU", printfrr_psu); |
648 | | static ssize_t printfrr_psu(struct fbuf *buf, struct printfrr_eargs *ea, |
649 | | const void *ptr) |
650 | 0 | { |
651 | 0 | const union sockunion *su = ptr; |
652 | 0 | bool include_port = false, include_scope = false; |
653 | 0 | bool endflags = false; |
654 | 0 | ssize_t ret = 0; |
655 | 0 | char cbuf[INET6_ADDRSTRLEN]; |
656 | |
|
657 | 0 | if (!su) |
658 | 0 | return bputs(buf, "(null)"); |
659 | | |
660 | 0 | while (!endflags) { |
661 | 0 | switch (*ea->fmt) { |
662 | 0 | case 'p': |
663 | 0 | ea->fmt++; |
664 | 0 | include_port = true; |
665 | 0 | break; |
666 | 0 | case 's': |
667 | 0 | ea->fmt++; |
668 | 0 | include_scope = true; |
669 | 0 | break; |
670 | 0 | default: |
671 | 0 | endflags = true; |
672 | 0 | break; |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | 0 | switch (sockunion_family(su)) { |
677 | 0 | case AF_UNSPEC: |
678 | 0 | ret += bputs(buf, "(unspec)"); |
679 | 0 | break; |
680 | 0 | case AF_INET: |
681 | 0 | inet_ntop(AF_INET, &su->sin.sin_addr, cbuf, sizeof(cbuf)); |
682 | 0 | ret += bputs(buf, cbuf); |
683 | 0 | if (include_port) |
684 | 0 | ret += bprintfrr(buf, ":%d", ntohs(su->sin.sin_port)); |
685 | 0 | break; |
686 | 0 | case AF_INET6: |
687 | 0 | if (include_port) |
688 | 0 | ret += bputch(buf, '['); |
689 | 0 | inet_ntop(AF_INET6, &su->sin6.sin6_addr, cbuf, sizeof(cbuf)); |
690 | 0 | ret += bputs(buf, cbuf); |
691 | 0 | if (include_scope && su->sin6.sin6_scope_id) |
692 | 0 | ret += bprintfrr(buf, "%%%u", |
693 | 0 | (unsigned int)su->sin6.sin6_scope_id); |
694 | 0 | if (include_port) |
695 | 0 | ret += bprintfrr(buf, "]:%d", |
696 | 0 | ntohs(su->sin6.sin6_port)); |
697 | 0 | break; |
698 | 0 | case AF_UNIX: { |
699 | 0 | int len; |
700 | 0 | #ifdef __linux__ |
701 | 0 | if (su->sun.sun_path[0] == '\0' && su->sun.sun_path[1]) { |
702 | 0 | len = strnlen(su->sun.sun_path + 1, |
703 | 0 | sizeof(su->sun.sun_path) - 1); |
704 | 0 | ret += bprintfrr(buf, "@%*pSE", len, |
705 | 0 | su->sun.sun_path + 1); |
706 | 0 | break; |
707 | 0 | } |
708 | 0 | #endif |
709 | 0 | len = strnlen(su->sun.sun_path, sizeof(su->sun.sun_path)); |
710 | 0 | ret += bprintfrr(buf, "%*pSE", len, su->sun.sun_path); |
711 | 0 | break; |
712 | 0 | } |
713 | 0 | default: |
714 | 0 | ret += bprintfrr(buf, "(af %d)", sockunion_family(su)); |
715 | 0 | } |
716 | | |
717 | 0 | return ret; |
718 | 0 | } |
719 | | |
720 | | int sockunion_is_null(const union sockunion *su) |
721 | 0 | { |
722 | 0 | unsigned char null_s6_addr[16] = {0}; |
723 | |
|
724 | 0 | switch (sockunion_family(su)) { |
725 | 0 | case AF_UNSPEC: |
726 | 0 | return 1; |
727 | 0 | case AF_INET: |
728 | 0 | return (su->sin.sin_addr.s_addr == 0); |
729 | 0 | case AF_INET6: |
730 | 0 | return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr, |
731 | 0 | sizeof(null_s6_addr)); |
732 | 0 | default: |
733 | 0 | return 0; |
734 | 0 | } |
735 | 0 | } |
736 | | |
737 | | printfrr_ext_autoreg_i("PF", printfrr_pf); |
738 | | static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea, |
739 | | uintmax_t val) |
740 | 0 | { |
741 | 0 | switch (val) { |
742 | 0 | case AF_INET: |
743 | 0 | return bputs(buf, "AF_INET"); |
744 | 0 | case AF_INET6: |
745 | 0 | return bputs(buf, "AF_INET6"); |
746 | 0 | case AF_UNIX: |
747 | 0 | return bputs(buf, "AF_UNIX"); |
748 | 0 | #ifdef AF_PACKET |
749 | 0 | case AF_PACKET: |
750 | 0 | return bputs(buf, "AF_PACKET"); |
751 | 0 | #endif |
752 | 0 | #ifdef AF_NETLINK |
753 | 0 | case AF_NETLINK: |
754 | 0 | return bputs(buf, "AF_NETLINK"); |
755 | 0 | #endif |
756 | 0 | } |
757 | 0 | return bprintfrr(buf, "AF_(%ju)", val); |
758 | 0 | } |
759 | | |
760 | | printfrr_ext_autoreg_i("SO", printfrr_so); |
761 | | static ssize_t printfrr_so(struct fbuf *buf, struct printfrr_eargs *ea, |
762 | | uintmax_t val) |
763 | 0 | { |
764 | 0 | switch (val) { |
765 | 0 | case SOCK_STREAM: |
766 | 0 | return bputs(buf, "SOCK_STREAM"); |
767 | 0 | case SOCK_DGRAM: |
768 | 0 | return bputs(buf, "SOCK_DGRAM"); |
769 | 0 | case SOCK_SEQPACKET: |
770 | 0 | return bputs(buf, "SOCK_SEQPACKET"); |
771 | 0 | #ifdef SOCK_RAW |
772 | 0 | case SOCK_RAW: |
773 | 0 | return bputs(buf, "SOCK_RAW"); |
774 | 0 | #endif |
775 | 0 | #ifdef SOCK_PACKET |
776 | 0 | case SOCK_PACKET: |
777 | 0 | return bputs(buf, "SOCK_PACKET"); |
778 | 0 | #endif |
779 | 0 | } |
780 | 0 | return bprintfrr(buf, "SOCK_(%ju)", val); |
781 | 0 | } |