]>
git.proxmox.com Git - mirror_frr.git/blob - lib/sockunion.c
1 /* Socket union related function.
2 * Copyright (c) 1997, 98 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
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
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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "sockunion.h"
29 #include "lib_errors.h"
32 DEFINE_MTYPE_STATIC(LIB
, SOCKUNION
, "Socket union");
34 const char *inet_sutop(const union sockunion
*su
, char *str
)
36 switch (su
->sa
.sa_family
) {
38 inet_ntop(AF_INET
, &su
->sin
.sin_addr
, str
, INET_ADDRSTRLEN
);
41 inet_ntop(AF_INET6
, &su
->sin6
.sin6_addr
, str
, INET6_ADDRSTRLEN
);
47 int str2sockunion(const char *str
, union sockunion
*su
)
54 memset(su
, 0, sizeof(union sockunion
));
56 ret
= inet_pton(AF_INET
, str
, &su
->sin
.sin_addr
);
57 if (ret
> 0) /* Valid IPv4 address format. */
59 su
->sin
.sin_family
= AF_INET
;
60 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
61 su
->sin
.sin_len
= sizeof(struct sockaddr_in
);
62 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
65 ret
= inet_pton(AF_INET6
, str
, &su
->sin6
.sin6_addr
);
66 if (ret
> 0) /* Valid IPv6 address format. */
68 su
->sin6
.sin6_family
= AF_INET6
;
70 su
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
77 const char *sockunion2str(const union sockunion
*su
, char *buf
, size_t len
)
79 switch (sockunion_family(su
)) {
81 snprintf(buf
, len
, "(unspec)");
84 return inet_ntop(AF_INET
, &su
->sin
.sin_addr
, buf
, len
);
86 return inet_ntop(AF_INET6
, &su
->sin6
.sin6_addr
, buf
, len
);
88 snprintf(buf
, len
, "(af %d)", sockunion_family(su
));
92 union sockunion
*sockunion_str2su(const char *str
)
94 union sockunion
*su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
96 if (!str2sockunion(str
, su
))
99 XFREE(MTYPE_SOCKUNION
, su
);
103 /* Convert IPv4 compatible IPv6 address to IPv4 address. */
104 static void sockunion_normalise_mapped(union sockunion
*su
)
106 struct sockaddr_in sin
;
108 if (su
->sa
.sa_family
== AF_INET6
109 && IN6_IS_ADDR_V4MAPPED(&su
->sin6
.sin6_addr
)) {
110 memset(&sin
, 0, sizeof(sin
));
111 sin
.sin_family
= AF_INET
;
112 sin
.sin_port
= su
->sin6
.sin6_port
;
113 memcpy(&sin
.sin_addr
, ((char *)&su
->sin6
.sin6_addr
) + 12, 4);
114 memcpy(su
, &sin
, sizeof(struct sockaddr_in
));
118 /* return sockunion structure : this function should be revised. */
119 static const char *sockunion_log(const union sockunion
*su
, char *buf
,
122 switch (su
->sa
.sa_family
) {
124 return inet_ntop(AF_INET
, &su
->sin
.sin_addr
, buf
, len
);
127 return inet_ntop(AF_INET6
, &(su
->sin6
.sin6_addr
), buf
, len
);
130 snprintf(buf
, len
, "af_unknown %d ", su
->sa
.sa_family
);
135 /* Return socket of sockunion. */
136 int sockunion_socket(const union sockunion
*su
)
140 sock
= socket(su
->sa
.sa_family
, SOCK_STREAM
, 0);
142 char buf
[SU_ADDRSTRLEN
];
143 flog_err(EC_LIB_SOCKET
, "Can't make socket for %s : %s",
144 sockunion_log(su
, buf
, SU_ADDRSTRLEN
),
145 safe_strerror(errno
));
152 /* Return accepted new socket file descriptor. */
153 int sockunion_accept(int sock
, union sockunion
*su
)
158 len
= sizeof(union sockunion
);
159 client_sock
= accept(sock
, (struct sockaddr
*)su
, &len
);
161 sockunion_normalise_mapped(su
);
165 /* Return sizeof union sockunion. */
166 int sockunion_sizeof(const union sockunion
*su
)
171 switch (su
->sa
.sa_family
) {
173 ret
= sizeof(struct sockaddr_in
);
176 ret
= sizeof(struct sockaddr_in6
);
182 /* Performs a non-blocking connect(). */
183 enum connect_result
sockunion_connect(int fd
, const union sockunion
*peersu
,
184 unsigned short port
, ifindex_t ifindex
)
189 memcpy(&su
, peersu
, sizeof(union sockunion
));
191 switch (su
.sa
.sa_family
) {
193 su
.sin
.sin_port
= port
;
196 su
.sin6
.sin6_port
= port
;
198 if (IN6_IS_ADDR_LINKLOCAL(&su
.sin6
.sin6_addr
) && ifindex
) {
199 su
.sin6
.sin6_scope_id
= ifindex
;
200 SET_IN6_LINKLOCAL_IFINDEX(su
.sin6
.sin6_addr
, ifindex
);
206 /* Call connect function. */
207 ret
= connect(fd
, (struct sockaddr
*)&su
, sockunion_sizeof(&su
));
209 /* Immediate success */
211 return connect_success
;
213 /* If connect is in progress then return 1 else it's real error. */
215 if (errno
!= EINPROGRESS
) {
216 char str
[SU_ADDRSTRLEN
];
217 zlog_info("can't connect to %s fd %d : %s",
218 sockunion_log(&su
, str
, sizeof(str
)), fd
,
219 safe_strerror(errno
));
220 return connect_error
;
224 return connect_in_progress
;
227 /* Make socket from sockunion union. */
228 int sockunion_stream_socket(union sockunion
*su
)
232 if (su
->sa
.sa_family
== 0)
233 su
->sa
.sa_family
= AF_INET_UNION
;
235 sock
= socket(su
->sa
.sa_family
, SOCK_STREAM
, 0);
238 flog_err(EC_LIB_SOCKET
,
239 "can't make socket sockunion_stream_socket");
244 /* Bind socket to specified address. */
245 int sockunion_bind(int sock
, union sockunion
*su
, unsigned short port
,
246 union sockunion
*su_addr
)
251 if (su
->sa
.sa_family
== AF_INET
) {
252 size
= sizeof(struct sockaddr_in
);
253 su
->sin
.sin_port
= htons(port
);
254 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
255 su
->sin
.sin_len
= size
;
256 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
258 sockunion2ip(su
) = htonl(INADDR_ANY
);
259 } else if (su
->sa
.sa_family
== AF_INET6
) {
260 size
= sizeof(struct sockaddr_in6
);
261 su
->sin6
.sin6_port
= htons(port
);
263 su
->sin6
.sin6_len
= size
;
264 #endif /* SIN6_LEN */
265 if (su_addr
== NULL
) {
267 memset(&su
->sin6
.sin6_addr
, 0, sizeof(struct in6_addr
));
269 su
->sin6
.sin6_addr
= in6addr_any
;
270 #endif /* LINUX_IPV6 */
274 ret
= bind(sock
, (struct sockaddr
*)su
, size
);
276 char buf
[SU_ADDRSTRLEN
];
277 flog_err(EC_LIB_SOCKET
, "can't bind socket for %s : %s",
278 sockunion_log(su
, buf
, SU_ADDRSTRLEN
),
279 safe_strerror(errno
));
285 int sockopt_reuseaddr(int sock
)
290 ret
= setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
,
295 "can't set sockopt SO_REUSEADDR to socket %d errno=%d: %s",
296 sock
, errno
, safe_strerror(errno
));
303 int sockopt_reuseport(int sock
)
308 ret
= setsockopt(sock
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&on
,
311 flog_err(EC_LIB_SOCKET
,
312 "can't set sockopt SO_REUSEPORT to socket %d", sock
);
318 int sockopt_reuseport(int sock
)
324 int sockopt_ttl(int family
, int sock
, int ttl
)
329 if (family
== AF_INET
) {
330 ret
= setsockopt(sock
, IPPROTO_IP
, IP_TTL
, (void *)&ttl
,
333 flog_err(EC_LIB_SOCKET
,
334 "can't set sockopt IP_TTL %d to socket %d",
341 if (family
== AF_INET6
) {
342 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
,
343 (void *)&ttl
, sizeof(int));
347 "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
356 int sockopt_minttl(int family
, int sock
, int minttl
)
359 if (family
== AF_INET
) {
360 int ret
= setsockopt(sock
, IPPROTO_IP
, IP_MINTTL
, &minttl
,
365 "can't set sockopt IP_MINTTL to %d on socket %d: %s",
366 minttl
, sock
, safe_strerror(errno
));
369 #endif /* IP_MINTTL */
370 #ifdef IPV6_MINHOPCOUNT
371 if (family
== AF_INET6
) {
372 int ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MINHOPCOUNT
,
373 &minttl
, sizeof(minttl
));
377 "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
378 minttl
, sock
, safe_strerror(errno
));
387 int sockopt_v6only(int family
, int sock
)
392 if (family
== AF_INET6
) {
393 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (void *)&on
,
396 flog_err(EC_LIB_SOCKET
,
397 "can't set sockopt IPV6_V6ONLY to socket %d",
403 #endif /* IPV6_V6ONLY */
407 /* If same family and same prefix return 1. */
408 int sockunion_same(const union sockunion
*su1
, const union sockunion
*su2
)
412 if (su1
->sa
.sa_family
!= su2
->sa
.sa_family
)
415 switch (su1
->sa
.sa_family
) {
417 ret
= memcmp(&su1
->sin
.sin_addr
, &su2
->sin
.sin_addr
,
418 sizeof(struct in_addr
));
421 ret
= memcmp(&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
,
422 sizeof(struct in6_addr
));
423 if ((ret
== 0) && IN6_IS_ADDR_LINKLOCAL(&su1
->sin6
.sin6_addr
)) {
424 /* compare interface indices */
425 if (su1
->sin6
.sin6_scope_id
&& su2
->sin6
.sin6_scope_id
)
426 ret
= (su1
->sin6
.sin6_scope_id
427 == su2
->sin6
.sin6_scope_id
)
439 unsigned int sockunion_hash(const union sockunion
*su
)
441 switch (sockunion_family(su
)) {
443 return jhash_1word(su
->sin
.sin_addr
.s_addr
, 0);
445 return jhash2(su
->sin6
.sin6_addr
.s6_addr32
,
446 array_size(su
->sin6
.sin6_addr
.s6_addr32
), 0);
451 size_t family2addrsize(int family
)
455 return sizeof(struct in_addr
);
457 return sizeof(struct in6_addr
);
462 size_t sockunion_get_addrlen(const union sockunion
*su
)
464 return family2addrsize(sockunion_family(su
));
467 const uint8_t *sockunion_get_addr(const union sockunion
*su
)
469 switch (sockunion_family(su
)) {
471 return (const uint8_t *)&su
->sin
.sin_addr
.s_addr
;
473 return (const uint8_t *)&su
->sin6
.sin6_addr
;
478 void sockunion_set(union sockunion
*su
, int family
, const uint8_t *addr
,
481 if (family2addrsize(family
) != bytes
)
484 sockunion_family(su
) = family
;
487 memcpy(&su
->sin
.sin_addr
.s_addr
, addr
, bytes
);
490 memcpy(&su
->sin6
.sin6_addr
, addr
, bytes
);
495 /* After TCP connection is established. Get local address and port. */
496 union sockunion
*sockunion_getsockname(int fd
)
502 struct sockaddr_in sin
;
503 struct sockaddr_in6 sin6
;
504 char tmp_buffer
[128];
508 memset(&name
, 0, sizeof(name
));
511 ret
= getsockname(fd
, (struct sockaddr
*)&name
, &len
);
513 flog_err(EC_LIB_SOCKET
,
514 "Can't get local address and port by getsockname: %s",
515 safe_strerror(errno
));
519 if (name
.sa
.sa_family
== AF_INET
) {
520 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
521 memcpy(su
, &name
, sizeof(struct sockaddr_in
));
524 if (name
.sa
.sa_family
== AF_INET6
) {
525 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
526 memcpy(su
, &name
, sizeof(struct sockaddr_in6
));
527 sockunion_normalise_mapped(su
);
533 "Unexpected AFI received(%d) for sockunion_getsockname call for fd: %d",
534 name
.sa
.sa_family
, fd
);
538 /* After TCP connection is established. Get remote address and port. */
539 union sockunion
*sockunion_getpeername(int fd
)
545 struct sockaddr_in sin
;
546 struct sockaddr_in6 sin6
;
547 char tmp_buffer
[128];
551 memset(&name
, 0, sizeof(name
));
553 ret
= getpeername(fd
, (struct sockaddr
*)&name
, &len
);
555 flog_err(EC_LIB_SOCKET
, "Can't get remote address and port: %s",
556 safe_strerror(errno
));
560 if (name
.sa
.sa_family
== AF_INET
) {
561 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
562 memcpy(su
, &name
, sizeof(struct sockaddr_in
));
565 if (name
.sa
.sa_family
== AF_INET6
) {
566 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
567 memcpy(su
, &name
, sizeof(struct sockaddr_in6
));
568 sockunion_normalise_mapped(su
);
574 "Unexpected AFI received(%d) for sockunion_getpeername call for fd: %d",
575 name
.sa
.sa_family
, fd
);
579 /* Print sockunion structure */
580 static void __attribute__((unused
)) sockunion_print(const union sockunion
*su
)
585 switch (su
->sa
.sa_family
) {
587 printf("%pI4\n", &su
->sin
.sin_addr
);
590 printf("%pI6\n", &su
->sin6
.sin6_addr
);
594 struct sockaddr_dl
*sdl
;
596 sdl
= (struct sockaddr_dl
*)&(su
->sa
);
597 printf("link#%d\n", sdl
->sdl_index
);
601 printf("af_unknown %d\n", su
->sa
.sa_family
);
606 int in6addr_cmp(const struct in6_addr
*addr1
, const struct in6_addr
*addr2
)
609 const uint8_t *p1
, *p2
;
611 p1
= (const uint8_t *)addr1
;
612 p2
= (const uint8_t *)addr2
;
614 for (i
= 0; i
< sizeof(struct in6_addr
); i
++) {
617 else if (p1
[i
] < p2
[i
])
623 int sockunion_cmp(const union sockunion
*su1
, const union sockunion
*su2
)
625 if (su1
->sa
.sa_family
> su2
->sa
.sa_family
)
627 if (su1
->sa
.sa_family
< su2
->sa
.sa_family
)
630 if (su1
->sa
.sa_family
== AF_INET
) {
631 if (ntohl(sockunion2ip(su1
)) == ntohl(sockunion2ip(su2
)))
633 if (ntohl(sockunion2ip(su1
)) > ntohl(sockunion2ip(su2
)))
638 if (su1
->sa
.sa_family
== AF_INET6
)
639 return in6addr_cmp(&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
);
643 /* Duplicate sockunion. */
644 union sockunion
*sockunion_dup(const union sockunion
*su
)
646 union sockunion
*dup
=
647 XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
648 memcpy(dup
, su
, sizeof(union sockunion
));
652 void sockunion_free(union sockunion
*su
)
654 XFREE(MTYPE_SOCKUNION
, su
);
657 void sockunion_init(union sockunion
*su
)
659 memset(su
, 0, sizeof(union sockunion
));
662 printfrr_ext_autoreg_p("SU", printfrr_psu
);
663 static ssize_t
printfrr_psu(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
666 const union sockunion
*su
= ptr
;
667 bool include_port
= false, include_scope
= false;
668 bool endflags
= false;
670 char cbuf
[INET6_ADDRSTRLEN
];
673 return bputs(buf
, "(null)");
683 include_scope
= true;
691 switch (sockunion_family(su
)) {
693 ret
+= bputs(buf
, "(unspec)");
696 inet_ntop(AF_INET
, &su
->sin
.sin_addr
, cbuf
, sizeof(cbuf
));
697 ret
+= bputs(buf
, cbuf
);
699 ret
+= bprintfrr(buf
, ":%d", ntohs(su
->sin
.sin_port
));
703 ret
+= bputch(buf
, '[');
704 inet_ntop(AF_INET6
, &su
->sin6
.sin6_addr
, cbuf
, sizeof(cbuf
));
705 ret
+= bputs(buf
, cbuf
);
706 if (include_scope
&& su
->sin6
.sin6_scope_id
)
707 ret
+= bprintfrr(buf
, "%%%u",
708 (unsigned int)su
->sin6
.sin6_scope_id
);
710 ret
+= bprintfrr(buf
, "]:%d",
711 ntohs(su
->sin6
.sin6_port
));
716 if (su
->sun
.sun_path
[0] == '\0' && su
->sun
.sun_path
[1]) {
717 len
= strnlen(su
->sun
.sun_path
+ 1,
718 sizeof(su
->sun
.sun_path
) - 1);
719 ret
+= bprintfrr(buf
, "@%*pSE", len
,
720 su
->sun
.sun_path
+ 1);
724 len
= strnlen(su
->sun
.sun_path
, sizeof(su
->sun
.sun_path
));
725 ret
+= bprintfrr(buf
, "%*pSE", len
, su
->sun
.sun_path
);
729 ret
+= bprintfrr(buf
, "(af %d)", sockunion_family(su
));
735 int sockunion_is_null(const union sockunion
*su
)
737 unsigned char null_s6_addr
[16] = {0};
739 switch (sockunion_family(su
)) {
743 return (su
->sin
.sin_addr
.s_addr
== 0);
745 return !memcmp(su
->sin6
.sin6_addr
.s6_addr
, null_s6_addr
,
746 sizeof(null_s6_addr
));
752 printfrr_ext_autoreg_i("PF", printfrr_pf
);
753 static ssize_t
printfrr_pf(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
758 return bputs(buf
, "AF_INET");
760 return bputs(buf
, "AF_INET6");
762 return bputs(buf
, "AF_UNIX");
765 return bputs(buf
, "AF_PACKET");
769 return bputs(buf
, "AF_NETLINK");
772 return bprintfrr(buf
, "AF_(%ju)", val
);
775 printfrr_ext_autoreg_i("SO", printfrr_so
);
776 static ssize_t
printfrr_so(struct fbuf
*buf
, struct printfrr_eargs
*ea
,
781 return bputs(buf
, "SOCK_STREAM");
783 return bputs(buf
, "SOCK_DGRAM");
785 return bputs(buf
, "SOCK_SEQPACKET");
788 return bputs(buf
, "SOCK_RAW");
792 return bputs(buf
, "SOCK_PACKET");
795 return bprintfrr(buf
, "SOCK_(%ju)", val
);