]> git.proxmox.com Git - mirror_frr.git/blob - lib/sockunion.c
*: reindent
[mirror_frr.git] / lib / sockunion.c
1 /* Socket union related function.
2 * Copyright (c) 1997, 98 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #include <zebra.h>
23
24 #include "prefix.h"
25 #include "vty.h"
26 #include "sockunion.h"
27 #include "memory.h"
28 #include "log.h"
29 #include "jhash.h"
30
31 DEFINE_MTYPE_STATIC(LIB, SOCKUNION, "Socket union")
32
33 const char *inet_sutop(const union sockunion *su, char *str)
34 {
35 switch (su->sa.sa_family) {
36 case AF_INET:
37 inet_ntop(AF_INET, &su->sin.sin_addr, str, INET_ADDRSTRLEN);
38 break;
39 case AF_INET6:
40 inet_ntop(AF_INET6, &su->sin6.sin6_addr, str, INET6_ADDRSTRLEN);
41 break;
42 }
43 return str;
44 }
45
46 int str2sockunion(const char *str, union sockunion *su)
47 {
48 int ret;
49
50 memset(su, 0, sizeof(union sockunion));
51
52 ret = inet_pton(AF_INET, str, &su->sin.sin_addr);
53 if (ret > 0) /* Valid IPv4 address format. */
54 {
55 su->sin.sin_family = AF_INET;
56 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
57 su->sin.sin_len = sizeof(struct sockaddr_in);
58 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
59 return 0;
60 }
61 ret = inet_pton(AF_INET6, str, &su->sin6.sin6_addr);
62 if (ret > 0) /* Valid IPv6 address format. */
63 {
64 su->sin6.sin6_family = AF_INET6;
65 #ifdef SIN6_LEN
66 su->sin6.sin6_len = sizeof(struct sockaddr_in6);
67 #endif /* SIN6_LEN */
68 return 0;
69 }
70 return -1;
71 }
72
73 const char *sockunion2str(const union sockunion *su, char *buf, size_t len)
74 {
75 switch (sockunion_family(su)) {
76 case AF_UNSPEC:
77 snprintf(buf, len, "(unspec)");
78 return buf;
79 case AF_INET:
80 return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
81 case AF_INET6:
82 return inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, len);
83 }
84 snprintf(buf, len, "(af %d)", sockunion_family(su));
85 return buf;
86 }
87
88 union sockunion *sockunion_str2su(const char *str)
89 {
90 union sockunion *su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
91
92 if (!str2sockunion(str, su))
93 return su;
94
95 XFREE(MTYPE_SOCKUNION, su);
96 return NULL;
97 }
98
99 /* Convert IPv4 compatible IPv6 address to IPv4 address. */
100 static void sockunion_normalise_mapped(union sockunion *su)
101 {
102 struct sockaddr_in sin;
103
104 if (su->sa.sa_family == AF_INET6
105 && IN6_IS_ADDR_V4MAPPED(&su->sin6.sin6_addr)) {
106 memset(&sin, 0, sizeof(struct sockaddr_in));
107 sin.sin_family = AF_INET;
108 sin.sin_port = su->sin6.sin6_port;
109 memcpy(&sin.sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4);
110 memcpy(su, &sin, sizeof(struct sockaddr_in));
111 }
112 }
113
114 /* return sockunion structure : this function should be revised. */
115 static const char *sockunion_log(const union sockunion *su, char *buf,
116 size_t len)
117 {
118 switch (su->sa.sa_family) {
119 case AF_INET:
120 return inet_ntop(AF_INET, &su->sin.sin_addr, buf, len);
121
122 case AF_INET6:
123 return inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf, len);
124 break;
125
126 default:
127 snprintf(buf, len, "af_unknown %d ", su->sa.sa_family);
128 return buf;
129 }
130 }
131
132 /* Return socket of sockunion. */
133 int sockunion_socket(const union sockunion *su)
134 {
135 int sock;
136
137 sock = socket(su->sa.sa_family, SOCK_STREAM, 0);
138 if (sock < 0) {
139 char buf[SU_ADDRSTRLEN];
140 zlog_warn("Can't make socket for %s : %s",
141 sockunion_log(su, buf, SU_ADDRSTRLEN),
142 safe_strerror(errno));
143 return -1;
144 }
145
146 return sock;
147 }
148
149 /* Return accepted new socket file descriptor. */
150 int sockunion_accept(int sock, union sockunion *su)
151 {
152 socklen_t len;
153 int client_sock;
154
155 len = sizeof(union sockunion);
156 client_sock = accept(sock, (struct sockaddr *)su, &len);
157
158 sockunion_normalise_mapped(su);
159 return client_sock;
160 }
161
162 /* Return sizeof union sockunion. */
163 static int sockunion_sizeof(const union sockunion *su)
164 {
165 int ret;
166
167 ret = 0;
168 switch (su->sa.sa_family) {
169 case AF_INET:
170 ret = sizeof(struct sockaddr_in);
171 break;
172 case AF_INET6:
173 ret = sizeof(struct sockaddr_in6);
174 break;
175 }
176 return ret;
177 }
178
179 /* sockunion_connect returns
180 -1 : error occured
181 0 : connect success
182 1 : connect is in progress */
183 enum connect_result sockunion_connect(int fd, const union sockunion *peersu,
184 unsigned short port, ifindex_t ifindex)
185 {
186 int ret;
187 int val;
188 union sockunion su;
189
190 memcpy(&su, peersu, sizeof(union sockunion));
191
192 switch (su.sa.sa_family) {
193 case AF_INET:
194 su.sin.sin_port = port;
195 break;
196 case AF_INET6:
197 su.sin6.sin6_port = port;
198 #ifdef KAME
199 if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr) && ifindex) {
200 su.sin6.sin6_scope_id = ifindex;
201 SET_IN6_LINKLOCAL_IFINDEX(su.sin6.sin6_addr, ifindex);
202 }
203 #endif /* KAME */
204 break;
205 }
206
207 /* Make socket non-block. */
208 val = fcntl(fd, F_GETFL, 0);
209 fcntl(fd, F_SETFL, val | O_NONBLOCK);
210
211 /* Call connect function. */
212 ret = connect(fd, (struct sockaddr *)&su, sockunion_sizeof(&su));
213
214 /* Immediate success */
215 if (ret == 0) {
216 fcntl(fd, F_SETFL, val);
217 return connect_success;
218 }
219
220 /* If connect is in progress then return 1 else it's real error. */
221 if (ret < 0) {
222 if (errno != EINPROGRESS) {
223 char str[SU_ADDRSTRLEN];
224 zlog_info("can't connect to %s fd %d : %s",
225 sockunion_log(&su, str, sizeof str), fd,
226 safe_strerror(errno));
227 return connect_error;
228 }
229 }
230
231 fcntl(fd, F_SETFL, val);
232
233 return connect_in_progress;
234 }
235
236 /* Make socket from sockunion union. */
237 int sockunion_stream_socket(union sockunion *su)
238 {
239 int sock;
240
241 if (su->sa.sa_family == 0)
242 su->sa.sa_family = AF_INET_UNION;
243
244 sock = socket(su->sa.sa_family, SOCK_STREAM, 0);
245
246 if (sock < 0)
247 zlog_warn("can't make socket sockunion_stream_socket");
248
249 return sock;
250 }
251
252 /* Bind socket to specified address. */
253 int sockunion_bind(int sock, union sockunion *su, unsigned short port,
254 union sockunion *su_addr)
255 {
256 int size = 0;
257 int ret;
258
259 if (su->sa.sa_family == AF_INET) {
260 size = sizeof(struct sockaddr_in);
261 su->sin.sin_port = htons(port);
262 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
263 su->sin.sin_len = size;
264 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
265 if (su_addr == NULL)
266 sockunion2ip(su) = htonl(INADDR_ANY);
267 } else if (su->sa.sa_family == AF_INET6) {
268 size = sizeof(struct sockaddr_in6);
269 su->sin6.sin6_port = htons(port);
270 #ifdef SIN6_LEN
271 su->sin6.sin6_len = size;
272 #endif /* SIN6_LEN */
273 if (su_addr == NULL) {
274 #ifdef LINUX_IPV6
275 memset(&su->sin6.sin6_addr, 0, sizeof(struct in6_addr));
276 #else
277 su->sin6.sin6_addr = in6addr_any;
278 #endif /* LINUX_IPV6 */
279 }
280 }
281
282 ret = bind(sock, (struct sockaddr *)su, size);
283 if (ret < 0) {
284 char buf[SU_ADDRSTRLEN];
285 zlog_warn("can't bind socket for %s : %s",
286 sockunion_log(su, buf, SU_ADDRSTRLEN),
287 safe_strerror(errno));
288 }
289
290 return ret;
291 }
292
293 int sockopt_reuseaddr(int sock)
294 {
295 int ret;
296 int on = 1;
297
298 ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on,
299 sizeof(on));
300 if (ret < 0) {
301 zlog_warn("can't set sockopt SO_REUSEADDR to socket %d", sock);
302 return -1;
303 }
304 return 0;
305 }
306
307 #ifdef SO_REUSEPORT
308 int sockopt_reuseport(int sock)
309 {
310 int ret;
311 int on = 1;
312
313 ret = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on,
314 sizeof(on));
315 if (ret < 0) {
316 zlog_warn("can't set sockopt SO_REUSEPORT to socket %d", sock);
317 return -1;
318 }
319 return 0;
320 }
321 #else
322 int sockopt_reuseport(int sock)
323 {
324 return 0;
325 }
326 #endif /* 0 */
327
328 int sockopt_ttl(int family, int sock, int ttl)
329 {
330 int ret;
331
332 #ifdef IP_TTL
333 if (family == AF_INET) {
334 ret = setsockopt(sock, IPPROTO_IP, IP_TTL, (void *)&ttl,
335 sizeof(int));
336 if (ret < 0) {
337 zlog_warn("can't set sockopt IP_TTL %d to socket %d",
338 ttl, sock);
339 return -1;
340 }
341 return 0;
342 }
343 #endif /* IP_TTL */
344 if (family == AF_INET6) {
345 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
346 (void *)&ttl, sizeof(int));
347 if (ret < 0) {
348 zlog_warn(
349 "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
350 ttl, sock);
351 return -1;
352 }
353 return 0;
354 }
355 return 0;
356 }
357
358 int sockopt_cork(int sock, int onoff)
359 {
360 #ifdef TCP_CORK
361 return setsockopt(sock, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff));
362 #else
363 return 0;
364 #endif
365 }
366
367 int sockopt_mark_default(int sock, int mark, struct zebra_privs_t *cap)
368 {
369 #ifdef SO_MARK
370 int ret;
371
372 if (cap->change(ZPRIVS_RAISE))
373 zlog_err("routing_socket: Can't raise privileges");
374
375 ret = setsockopt(sock, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
376
377 if (cap->change(ZPRIVS_LOWER))
378 zlog_err("routing_socket: Can't lower privileges");
379
380 return ret;
381 #else
382 return 0;
383 #endif
384 }
385
386 int sockopt_minttl(int family, int sock, int minttl)
387 {
388 #ifdef IP_MINTTL
389 if (family == AF_INET) {
390 int ret = setsockopt(sock, IPPROTO_IP, IP_MINTTL, &minttl,
391 sizeof(minttl));
392 if (ret < 0)
393 zlog_warn(
394 "can't set sockopt IP_MINTTL to %d on socket %d: %s",
395 minttl, sock, safe_strerror(errno));
396 return ret;
397 }
398 #endif /* IP_MINTTL */
399 #ifdef IPV6_MINHOPCOUNT
400 if (family == AF_INET6) {
401 int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
402 &minttl, sizeof(minttl));
403 if (ret < 0)
404 zlog_warn(
405 "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
406 minttl, sock, safe_strerror(errno));
407 return ret;
408 }
409 #endif
410
411 errno = EOPNOTSUPP;
412 return -1;
413 }
414
415 int sockopt_v6only(int family, int sock)
416 {
417 int ret, on = 1;
418
419 #ifdef IPV6_V6ONLY
420 if (family == AF_INET6) {
421 ret = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on,
422 sizeof(int));
423 if (ret < 0) {
424 zlog_warn(
425 "can't set sockopt IPV6_V6ONLY "
426 "to socket %d",
427 sock);
428 return -1;
429 }
430 return 0;
431 }
432 #endif /* IPV6_V6ONLY */
433 return 0;
434 }
435
436 /* If same family and same prefix return 1. */
437 int sockunion_same(const union sockunion *su1, const union sockunion *su2)
438 {
439 int ret = 0;
440
441 if (su1->sa.sa_family != su2->sa.sa_family)
442 return 0;
443
444 switch (su1->sa.sa_family) {
445 case AF_INET:
446 ret = memcmp(&su1->sin.sin_addr, &su2->sin.sin_addr,
447 sizeof(struct in_addr));
448 break;
449 case AF_INET6:
450 ret = memcmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr,
451 sizeof(struct in6_addr));
452 if ((ret == 0) && IN6_IS_ADDR_LINKLOCAL(&su1->sin6.sin6_addr)) {
453 /* compare interface indices */
454 if (su1->sin6.sin6_scope_id && su2->sin6.sin6_scope_id)
455 ret = (su1->sin6.sin6_scope_id
456 == su2->sin6.sin6_scope_id)
457 ? 0
458 : 1;
459 }
460 break;
461 }
462 if (ret == 0)
463 return 1;
464 else
465 return 0;
466 }
467
468 unsigned int sockunion_hash(const union sockunion *su)
469 {
470 switch (sockunion_family(su)) {
471 case AF_INET:
472 return jhash_1word(su->sin.sin_addr.s_addr, 0);
473 case AF_INET6:
474 return jhash2(su->sin6.sin6_addr.s6_addr32,
475 ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0);
476 }
477 return 0;
478 }
479
480 size_t family2addrsize(int family)
481 {
482 switch (family) {
483 case AF_INET:
484 return sizeof(struct in_addr);
485 case AF_INET6:
486 return sizeof(struct in6_addr);
487 }
488 return 0;
489 }
490
491 size_t sockunion_get_addrlen(const union sockunion *su)
492 {
493 return family2addrsize(sockunion_family(su));
494 }
495
496 const u_char *sockunion_get_addr(const union sockunion *su)
497 {
498 switch (sockunion_family(su)) {
499 case AF_INET:
500 return (const u_char *)&su->sin.sin_addr.s_addr;
501 case AF_INET6:
502 return (const u_char *)&su->sin6.sin6_addr;
503 }
504 return NULL;
505 }
506
507 void sockunion_set(union sockunion *su, int family, const u_char *addr,
508 size_t bytes)
509 {
510 if (family2addrsize(family) != bytes)
511 return;
512
513 sockunion_family(su) = family;
514 switch (family) {
515 case AF_INET:
516 memcpy(&su->sin.sin_addr.s_addr, addr, bytes);
517 break;
518 case AF_INET6:
519 memcpy(&su->sin6.sin6_addr, addr, bytes);
520 break;
521 }
522 }
523
524 /* After TCP connection is established. Get local address and port. */
525 union sockunion *sockunion_getsockname(int fd)
526 {
527 int ret;
528 socklen_t len;
529 union {
530 struct sockaddr sa;
531 struct sockaddr_in sin;
532 struct sockaddr_in6 sin6;
533 char tmp_buffer[128];
534 } name;
535 union sockunion *su;
536
537 memset(&name, 0, sizeof name);
538 len = sizeof name;
539
540 ret = getsockname(fd, (struct sockaddr *)&name, &len);
541 if (ret < 0) {
542 zlog_warn("Can't get local address and port by getsockname: %s",
543 safe_strerror(errno));
544 return NULL;
545 }
546
547 if (name.sa.sa_family == AF_INET) {
548 su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
549 memcpy(su, &name, sizeof(struct sockaddr_in));
550 return su;
551 }
552 if (name.sa.sa_family == AF_INET6) {
553 su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
554 memcpy(su, &name, sizeof(struct sockaddr_in6));
555 sockunion_normalise_mapped(su);
556 return su;
557 }
558 return NULL;
559 }
560
561 /* After TCP connection is established. Get remote address and port. */
562 union sockunion *sockunion_getpeername(int fd)
563 {
564 int ret;
565 socklen_t len;
566 union {
567 struct sockaddr sa;
568 struct sockaddr_in sin;
569 struct sockaddr_in6 sin6;
570 char tmp_buffer[128];
571 } name;
572 union sockunion *su;
573
574 memset(&name, 0, sizeof name);
575 len = sizeof name;
576 ret = getpeername(fd, (struct sockaddr *)&name, &len);
577 if (ret < 0) {
578 zlog_warn("Can't get remote address and port: %s",
579 safe_strerror(errno));
580 return NULL;
581 }
582
583 if (name.sa.sa_family == AF_INET) {
584 su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
585 memcpy(su, &name, sizeof(struct sockaddr_in));
586 return su;
587 }
588 if (name.sa.sa_family == AF_INET6) {
589 su = XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
590 memcpy(su, &name, sizeof(struct sockaddr_in6));
591 sockunion_normalise_mapped(su);
592 return su;
593 }
594 return NULL;
595 }
596
597 /* Print sockunion structure */
598 static void __attribute__((unused)) sockunion_print(const union sockunion *su)
599 {
600 if (su == NULL)
601 return;
602
603 switch (su->sa.sa_family) {
604 case AF_INET:
605 printf("%s\n", inet_ntoa(su->sin.sin_addr));
606 break;
607 case AF_INET6: {
608 char buf[SU_ADDRSTRLEN];
609
610 printf("%s\n", inet_ntop(AF_INET6, &(su->sin6.sin6_addr), buf,
611 sizeof(buf)));
612 } break;
613
614 #ifdef AF_LINK
615 case AF_LINK: {
616 struct sockaddr_dl *sdl;
617
618 sdl = (struct sockaddr_dl *)&(su->sa);
619 printf("link#%d\n", sdl->sdl_index);
620 } break;
621 #endif /* AF_LINK */
622 default:
623 printf("af_unknown %d\n", su->sa.sa_family);
624 break;
625 }
626 }
627
628 static int in6addr_cmp(const struct in6_addr *addr1,
629 const struct in6_addr *addr2)
630 {
631 unsigned int i;
632 const u_char *p1, *p2;
633
634 p1 = (const u_char *)addr1;
635 p2 = (const u_char *)addr2;
636
637 for (i = 0; i < sizeof(struct in6_addr); i++) {
638 if (p1[i] > p2[i])
639 return 1;
640 else if (p1[i] < p2[i])
641 return -1;
642 }
643 return 0;
644 }
645
646 int sockunion_cmp(const union sockunion *su1, const union sockunion *su2)
647 {
648 if (su1->sa.sa_family > su2->sa.sa_family)
649 return 1;
650 if (su1->sa.sa_family < su2->sa.sa_family)
651 return -1;
652
653 if (su1->sa.sa_family == AF_INET) {
654 if (ntohl(sockunion2ip(su1)) == ntohl(sockunion2ip(su2)))
655 return 0;
656 if (ntohl(sockunion2ip(su1)) > ntohl(sockunion2ip(su2)))
657 return 1;
658 else
659 return -1;
660 }
661 if (su1->sa.sa_family == AF_INET6)
662 return in6addr_cmp(&su1->sin6.sin6_addr, &su2->sin6.sin6_addr);
663 return 0;
664 }
665
666 /* Duplicate sockunion. */
667 union sockunion *sockunion_dup(const union sockunion *su)
668 {
669 union sockunion *dup =
670 XCALLOC(MTYPE_SOCKUNION, sizeof(union sockunion));
671 memcpy(dup, su, sizeof(union sockunion));
672 return dup;
673 }
674
675 void sockunion_free(union sockunion *su)
676 {
677 XFREE(MTYPE_SOCKUNION, su);
678 }
679
680 void sockunion_init(union sockunion *su)
681 {
682 memset(su, 0, sizeof(union sockunion));
683 }