]>
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"
30 DEFINE_MTYPE_STATIC(LIB
, SOCKUNION
, "Socket union")
32 const char *inet_sutop(const union sockunion
*su
, char *str
)
34 switch (su
->sa
.sa_family
) {
36 inet_ntop(AF_INET
, &su
->sin
.sin_addr
, str
, INET_ADDRSTRLEN
);
39 inet_ntop(AF_INET6
, &su
->sin6
.sin6_addr
, str
, INET6_ADDRSTRLEN
);
45 int str2sockunion(const char *str
, union sockunion
*su
)
49 memset(su
, 0, sizeof(union sockunion
));
51 ret
= inet_pton(AF_INET
, str
, &su
->sin
.sin_addr
);
52 if (ret
> 0) /* Valid IPv4 address format. */
54 su
->sin
.sin_family
= AF_INET
;
55 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
56 su
->sin
.sin_len
= sizeof(struct sockaddr_in
);
57 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
60 ret
= inet_pton(AF_INET6
, str
, &su
->sin6
.sin6_addr
);
61 if (ret
> 0) /* Valid IPv6 address format. */
63 su
->sin6
.sin6_family
= AF_INET6
;
65 su
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
72 const char *sockunion2str(const union sockunion
*su
, char *buf
, size_t len
)
74 switch (sockunion_family(su
)) {
76 snprintf(buf
, len
, "(unspec)");
79 return inet_ntop(AF_INET
, &su
->sin
.sin_addr
, buf
, len
);
81 return inet_ntop(AF_INET6
, &su
->sin6
.sin6_addr
, buf
, len
);
83 snprintf(buf
, len
, "(af %d)", sockunion_family(su
));
87 union sockunion
*sockunion_str2su(const char *str
)
89 union sockunion
*su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
91 if (!str2sockunion(str
, su
))
94 XFREE(MTYPE_SOCKUNION
, su
);
98 /* Convert IPv4 compatible IPv6 address to IPv4 address. */
99 static void sockunion_normalise_mapped(union sockunion
*su
)
101 struct sockaddr_in sin
;
103 if (su
->sa
.sa_family
== AF_INET6
104 && IN6_IS_ADDR_V4MAPPED(&su
->sin6
.sin6_addr
)) {
105 memset(&sin
, 0, sizeof(struct sockaddr_in
));
106 sin
.sin_family
= AF_INET
;
107 sin
.sin_port
= su
->sin6
.sin6_port
;
108 memcpy(&sin
.sin_addr
, ((char *)&su
->sin6
.sin6_addr
) + 12, 4);
109 memcpy(su
, &sin
, sizeof(struct sockaddr_in
));
113 /* return sockunion structure : this function should be revised. */
114 static const char *sockunion_log(const union sockunion
*su
, char *buf
,
117 switch (su
->sa
.sa_family
) {
119 return inet_ntop(AF_INET
, &su
->sin
.sin_addr
, buf
, len
);
122 return inet_ntop(AF_INET6
, &(su
->sin6
.sin6_addr
), buf
, len
);
126 snprintf(buf
, len
, "af_unknown %d ", su
->sa
.sa_family
);
131 /* Return socket of sockunion. */
132 int sockunion_socket(const union sockunion
*su
)
136 sock
= socket(su
->sa
.sa_family
, SOCK_STREAM
, 0);
138 char buf
[SU_ADDRSTRLEN
];
139 zlog_warn("Can't make socket for %s : %s",
140 sockunion_log(su
, buf
, SU_ADDRSTRLEN
),
141 safe_strerror(errno
));
148 /* Return accepted new socket file descriptor. */
149 int sockunion_accept(int sock
, union sockunion
*su
)
154 len
= sizeof(union sockunion
);
155 client_sock
= accept(sock
, (struct sockaddr
*)su
, &len
);
157 sockunion_normalise_mapped(su
);
161 /* Return sizeof union sockunion. */
162 static int sockunion_sizeof(const union sockunion
*su
)
167 switch (su
->sa
.sa_family
) {
169 ret
= sizeof(struct sockaddr_in
);
172 ret
= sizeof(struct sockaddr_in6
);
178 /* sockunion_connect returns
181 1 : connect is in progress */
182 enum connect_result
sockunion_connect(int fd
, const union sockunion
*peersu
,
183 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 /* Make socket non-block. */
207 val
= fcntl(fd
, F_GETFL
, 0);
208 fcntl(fd
, F_SETFL
, val
| O_NONBLOCK
);
210 /* Call connect function. */
211 ret
= connect(fd
, (struct sockaddr
*)&su
, sockunion_sizeof(&su
));
213 /* Immediate success */
215 fcntl(fd
, F_SETFL
, val
);
216 return connect_success
;
219 /* If connect is in progress then return 1 else it's real error. */
221 if (errno
!= EINPROGRESS
) {
222 char str
[SU_ADDRSTRLEN
];
223 zlog_info("can't connect to %s fd %d : %s",
224 sockunion_log(&su
, str
, sizeof str
), fd
,
225 safe_strerror(errno
));
226 return connect_error
;
230 fcntl(fd
, F_SETFL
, val
);
232 return connect_in_progress
;
235 /* Make socket from sockunion union. */
236 int sockunion_stream_socket(union sockunion
*su
)
240 if (su
->sa
.sa_family
== 0)
241 su
->sa
.sa_family
= AF_INET_UNION
;
243 sock
= socket(su
->sa
.sa_family
, SOCK_STREAM
, 0);
246 zlog_warn("can't make socket sockunion_stream_socket");
251 /* Bind socket to specified address. */
252 int sockunion_bind(int sock
, union sockunion
*su
, unsigned short port
,
253 union sockunion
*su_addr
)
258 if (su
->sa
.sa_family
== AF_INET
) {
259 size
= sizeof(struct sockaddr_in
);
260 su
->sin
.sin_port
= htons(port
);
261 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
262 su
->sin
.sin_len
= size
;
263 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
265 sockunion2ip(su
) = htonl(INADDR_ANY
);
266 } else if (su
->sa
.sa_family
== AF_INET6
) {
267 size
= sizeof(struct sockaddr_in6
);
268 su
->sin6
.sin6_port
= htons(port
);
270 su
->sin6
.sin6_len
= size
;
271 #endif /* SIN6_LEN */
272 if (su_addr
== NULL
) {
274 memset(&su
->sin6
.sin6_addr
, 0, sizeof(struct in6_addr
));
276 su
->sin6
.sin6_addr
= in6addr_any
;
277 #endif /* LINUX_IPV6 */
281 ret
= bind(sock
, (struct sockaddr
*)su
, size
);
283 char buf
[SU_ADDRSTRLEN
];
284 zlog_warn("can't bind socket for %s : %s",
285 sockunion_log(su
, buf
, SU_ADDRSTRLEN
),
286 safe_strerror(errno
));
292 int sockopt_reuseaddr(int sock
)
297 ret
= setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&on
,
300 zlog_warn("can't set sockopt SO_REUSEADDR to socket %d", sock
);
307 int sockopt_reuseport(int sock
)
312 ret
= setsockopt(sock
, SOL_SOCKET
, SO_REUSEPORT
, (void *)&on
,
315 zlog_warn("can't set sockopt SO_REUSEPORT to socket %d", sock
);
321 int sockopt_reuseport(int sock
)
327 int sockopt_ttl(int family
, int sock
, int ttl
)
332 if (family
== AF_INET
) {
333 ret
= setsockopt(sock
, IPPROTO_IP
, IP_TTL
, (void *)&ttl
,
336 zlog_warn("can't set sockopt IP_TTL %d to socket %d",
343 if (family
== AF_INET6
) {
344 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
,
345 (void *)&ttl
, sizeof(int));
348 "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
358 * This function called setsockopt(.., TCP_CORK,...)
359 * Which on linux is a no-op since it is enabled by
360 * default and on BSD it uses TCP_NOPUSH to do
361 * the same thing( which it was not configured to
362 * use). This cleanup of the api occured on 8/1/17
363 * I imagine if after more than 1 year of no-one
364 * complaining, and a major upgrade release we
365 * can deprecate and remove this function call
367 int sockopt_cork(int sock
, int onoff
)
372 int sockopt_mark_default(int sock
, int mark
, struct zebra_privs_t
*cap
)
377 if (cap
->change(ZPRIVS_RAISE
))
378 zlog_err("routing_socket: Can't raise privileges");
380 ret
= setsockopt(sock
, SOL_SOCKET
, SO_MARK
, &mark
, sizeof(mark
));
382 if (cap
->change(ZPRIVS_LOWER
))
383 zlog_err("routing_socket: Can't lower privileges");
391 int sockopt_minttl(int family
, int sock
, int minttl
)
394 if (family
== AF_INET
) {
395 int ret
= setsockopt(sock
, IPPROTO_IP
, IP_MINTTL
, &minttl
,
399 "can't set sockopt IP_MINTTL to %d on socket %d: %s",
400 minttl
, sock
, safe_strerror(errno
));
403 #endif /* IP_MINTTL */
404 #ifdef IPV6_MINHOPCOUNT
405 if (family
== AF_INET6
) {
406 int ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_MINHOPCOUNT
,
407 &minttl
, sizeof(minttl
));
410 "can't set sockopt IPV6_MINHOPCOUNT to %d on socket %d: %s",
411 minttl
, sock
, safe_strerror(errno
));
420 int sockopt_v6only(int family
, int sock
)
425 if (family
== AF_INET6
) {
426 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, (void *)&on
,
430 "can't set sockopt IPV6_V6ONLY "
437 #endif /* IPV6_V6ONLY */
441 /* If same family and same prefix return 1. */
442 int sockunion_same(const union sockunion
*su1
, const union sockunion
*su2
)
446 if (su1
->sa
.sa_family
!= su2
->sa
.sa_family
)
449 switch (su1
->sa
.sa_family
) {
451 ret
= memcmp(&su1
->sin
.sin_addr
, &su2
->sin
.sin_addr
,
452 sizeof(struct in_addr
));
455 ret
= memcmp(&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
,
456 sizeof(struct in6_addr
));
457 if ((ret
== 0) && IN6_IS_ADDR_LINKLOCAL(&su1
->sin6
.sin6_addr
)) {
458 /* compare interface indices */
459 if (su1
->sin6
.sin6_scope_id
&& su2
->sin6
.sin6_scope_id
)
460 ret
= (su1
->sin6
.sin6_scope_id
461 == su2
->sin6
.sin6_scope_id
)
473 unsigned int sockunion_hash(const union sockunion
*su
)
475 switch (sockunion_family(su
)) {
477 return jhash_1word(su
->sin
.sin_addr
.s_addr
, 0);
479 return jhash2(su
->sin6
.sin6_addr
.s6_addr32
,
480 ZEBRA_NUM_OF(su
->sin6
.sin6_addr
.s6_addr32
), 0);
485 size_t family2addrsize(int family
)
489 return sizeof(struct in_addr
);
491 return sizeof(struct in6_addr
);
496 size_t sockunion_get_addrlen(const union sockunion
*su
)
498 return family2addrsize(sockunion_family(su
));
501 const u_char
*sockunion_get_addr(const union sockunion
*su
)
503 switch (sockunion_family(su
)) {
505 return (const u_char
*)&su
->sin
.sin_addr
.s_addr
;
507 return (const u_char
*)&su
->sin6
.sin6_addr
;
512 void sockunion_set(union sockunion
*su
, int family
, const u_char
*addr
,
515 if (family2addrsize(family
) != bytes
)
518 sockunion_family(su
) = family
;
521 memcpy(&su
->sin
.sin_addr
.s_addr
, addr
, bytes
);
524 memcpy(&su
->sin6
.sin6_addr
, addr
, bytes
);
529 /* After TCP connection is established. Get local address and port. */
530 union sockunion
*sockunion_getsockname(int fd
)
536 struct sockaddr_in sin
;
537 struct sockaddr_in6 sin6
;
538 char tmp_buffer
[128];
542 memset(&name
, 0, sizeof name
);
545 ret
= getsockname(fd
, (struct sockaddr
*)&name
, &len
);
547 zlog_warn("Can't get local address and port by getsockname: %s",
548 safe_strerror(errno
));
552 if (name
.sa
.sa_family
== AF_INET
) {
553 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
554 memcpy(su
, &name
, sizeof(struct sockaddr_in
));
557 if (name
.sa
.sa_family
== AF_INET6
) {
558 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
559 memcpy(su
, &name
, sizeof(struct sockaddr_in6
));
560 sockunion_normalise_mapped(su
);
566 /* After TCP connection is established. Get remote address and port. */
567 union sockunion
*sockunion_getpeername(int fd
)
573 struct sockaddr_in sin
;
574 struct sockaddr_in6 sin6
;
575 char tmp_buffer
[128];
579 memset(&name
, 0, sizeof name
);
581 ret
= getpeername(fd
, (struct sockaddr
*)&name
, &len
);
583 zlog_warn("Can't get remote address and port: %s",
584 safe_strerror(errno
));
588 if (name
.sa
.sa_family
== AF_INET
) {
589 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
590 memcpy(su
, &name
, sizeof(struct sockaddr_in
));
593 if (name
.sa
.sa_family
== AF_INET6
) {
594 su
= XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
595 memcpy(su
, &name
, sizeof(struct sockaddr_in6
));
596 sockunion_normalise_mapped(su
);
602 /* Print sockunion structure */
603 static void __attribute__((unused
)) sockunion_print(const union sockunion
*su
)
608 switch (su
->sa
.sa_family
) {
610 printf("%s\n", inet_ntoa(su
->sin
.sin_addr
));
613 char buf
[SU_ADDRSTRLEN
];
615 printf("%s\n", inet_ntop(AF_INET6
, &(su
->sin6
.sin6_addr
), buf
,
621 struct sockaddr_dl
*sdl
;
623 sdl
= (struct sockaddr_dl
*)&(su
->sa
);
624 printf("link#%d\n", sdl
->sdl_index
);
628 printf("af_unknown %d\n", su
->sa
.sa_family
);
633 static int in6addr_cmp(const struct in6_addr
*addr1
,
634 const struct in6_addr
*addr2
)
637 const u_char
*p1
, *p2
;
639 p1
= (const u_char
*)addr1
;
640 p2
= (const u_char
*)addr2
;
642 for (i
= 0; i
< sizeof(struct in6_addr
); i
++) {
645 else if (p1
[i
] < p2
[i
])
651 int sockunion_cmp(const union sockunion
*su1
, const union sockunion
*su2
)
653 if (su1
->sa
.sa_family
> su2
->sa
.sa_family
)
655 if (su1
->sa
.sa_family
< su2
->sa
.sa_family
)
658 if (su1
->sa
.sa_family
== AF_INET
) {
659 if (ntohl(sockunion2ip(su1
)) == ntohl(sockunion2ip(su2
)))
661 if (ntohl(sockunion2ip(su1
)) > ntohl(sockunion2ip(su2
)))
666 if (su1
->sa
.sa_family
== AF_INET6
)
667 return in6addr_cmp(&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
);
671 /* Duplicate sockunion. */
672 union sockunion
*sockunion_dup(const union sockunion
*su
)
674 union sockunion
*dup
=
675 XCALLOC(MTYPE_SOCKUNION
, sizeof(union sockunion
));
676 memcpy(dup
, su
, sizeof(union sockunion
));
680 void sockunion_free(union sockunion
*su
)
682 XFREE(MTYPE_SOCKUNION
, su
);
685 void sockunion_init(union sockunion
*su
)
687 memset(su
, 0, sizeof(union sockunion
));