]>
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
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
26 #include "sockunion.h"
31 #ifndef HAVE_INET_ATON
33 inet_aton (const char *cp
, struct in_addr
*inaddr
)
36 register u_long addr
= 0;
37 register u_long val
= 0, base
= 10;
41 register char c
= *cp
;
45 case '0': case '1': case '2': case '3': case '4': case '5':
46 case '6': case '7': case '8': case '9':
47 val
= (val
* base
) + (c
- '0');
55 addr
= addr
<< 8 | val
;
64 addr
<<= 8 * (3 - dots
);
66 inaddr
->s_addr
= htonl (addr
);
69 #endif /* ! HAVE_INET_ATON */
72 #ifndef HAVE_INET_PTON
74 inet_pton (int family
, const char *strptr
, void *addrptr
)
76 if (family
== AF_INET
)
78 struct in_addr in_val
;
80 if (inet_aton (strptr
, &in_val
))
82 memcpy (addrptr
, &in_val
, sizeof (struct in_addr
));
90 #endif /* ! HAVE_INET_PTON */
92 #ifndef HAVE_INET_NTOP
94 inet_ntop (int family
, const void *addrptr
, char *strptr
, size_t len
)
96 unsigned char *p
= (unsigned char *) addrptr
;
98 if (family
== AF_INET
)
100 char temp
[INET_ADDRSTRLEN
];
102 snprintf(temp
, sizeof(temp
), "%d.%d.%d.%d", p
[0], p
[1], p
[2], p
[3]);
104 if (strlen(temp
) >= len
)
109 strcpy(strptr
, temp
);
113 errno
= EAFNOSUPPORT
;
116 #endif /* ! HAVE_INET_NTOP */
119 inet_sutop (union sockunion
*su
, char *str
)
121 switch (su
->sa
.sa_family
)
124 inet_ntop (AF_INET
, &su
->sin
.sin_addr
, str
, INET_ADDRSTRLEN
);
128 inet_ntop (AF_INET6
, &su
->sin6
.sin6_addr
, str
, INET6_ADDRSTRLEN
);
130 #endif /* HAVE_IPV6 */
136 str2sockunion (const char *str
, union sockunion
*su
)
140 memset (su
, 0, sizeof (union sockunion
));
142 ret
= inet_pton (AF_INET
, str
, &su
->sin
.sin_addr
);
143 if (ret
> 0) /* Valid IPv4 address format. */
145 su
->sin
.sin_family
= AF_INET
;
146 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
147 su
->sin
.sin_len
= sizeof(struct sockaddr_in
);
148 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
152 ret
= inet_pton (AF_INET6
, str
, &su
->sin6
.sin6_addr
);
153 if (ret
> 0) /* Valid IPv6 address format. */
155 su
->sin6
.sin6_family
= AF_INET6
;
157 su
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
158 #endif /* SIN6_LEN */
161 #endif /* HAVE_IPV6 */
166 sockunion2str (union sockunion
*su
, char *buf
, size_t len
)
168 if (su
->sa
.sa_family
== AF_INET
)
169 return inet_ntop (AF_INET
, &su
->sin
.sin_addr
, buf
, len
);
171 else if (su
->sa
.sa_family
== AF_INET6
)
172 return inet_ntop (AF_INET6
, &su
->sin6
.sin6_addr
, buf
, len
);
173 #endif /* HAVE_IPV6 */
178 sockunion_str2su (const char *str
)
183 su
= XCALLOC (MTYPE_SOCKUNION
, sizeof (union sockunion
));
185 ret
= inet_pton (AF_INET
, str
, &su
->sin
.sin_addr
);
186 if (ret
> 0) /* Valid IPv4 address format. */
188 su
->sin
.sin_family
= AF_INET
;
189 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
190 su
->sin
.sin_len
= sizeof(struct sockaddr_in
);
191 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
195 ret
= inet_pton (AF_INET6
, str
, &su
->sin6
.sin6_addr
);
196 if (ret
> 0) /* Valid IPv6 address format. */
198 su
->sin6
.sin6_family
= AF_INET6
;
200 su
->sin6
.sin6_len
= sizeof(struct sockaddr_in6
);
201 #endif /* SIN6_LEN */
204 #endif /* HAVE_IPV6 */
206 XFREE (MTYPE_SOCKUNION
, su
);
211 sockunion_su2str (union sockunion
*su
)
213 char str
[SU_ADDRSTRLEN
];
215 switch (su
->sa
.sa_family
)
218 inet_ntop (AF_INET
, &su
->sin
.sin_addr
, str
, sizeof (str
));
222 inet_ntop (AF_INET6
, &su
->sin6
.sin6_addr
, str
, sizeof (str
));
224 #endif /* HAVE_IPV6 */
226 return XSTRDUP (MTYPE_TMP
, str
);
229 /* Convert IPv4 compatible IPv6 address to IPv4 address. */
231 sockunion_normalise_mapped (union sockunion
*su
)
233 struct sockaddr_in sin
;
236 if (su
->sa
.sa_family
== AF_INET6
237 && IN6_IS_ADDR_V4MAPPED (&su
->sin6
.sin6_addr
))
239 memset (&sin
, 0, sizeof (struct sockaddr_in
));
240 sin
.sin_family
= AF_INET
;
241 memcpy (&sin
.sin_addr
, ((char *)&su
->sin6
.sin6_addr
) + 12, 4);
242 memcpy (su
, &sin
, sizeof (struct sockaddr_in
));
244 #endif /* HAVE_IPV6 */
247 /* Return socket of sockunion. */
249 sockunion_socket (union sockunion
*su
)
253 sock
= socket (su
->sa
.sa_family
, SOCK_STREAM
, 0);
256 zlog (NULL
, LOG_WARNING
, "Can't make socket : %s", safe_strerror (errno
));
263 /* Return accepted new socket file descriptor. */
265 sockunion_accept (int sock
, union sockunion
*su
)
270 len
= sizeof (union sockunion
);
271 client_sock
= accept (sock
, (struct sockaddr
*) su
, &len
);
273 sockunion_normalise_mapped (su
);
277 /* Return sizeof union sockunion. */
279 sockunion_sizeof (union sockunion
*su
)
284 switch (su
->sa
.sa_family
)
287 ret
= sizeof (struct sockaddr_in
);
291 ret
= sizeof (struct sockaddr_in6
);
293 #endif /* AF_INET6 */
298 /* return sockunion structure : this function should be revised. */
300 sockunion_log (union sockunion
*su
)
302 static char buf
[SU_ADDRSTRLEN
];
304 switch (su
->sa
.sa_family
)
307 snprintf (buf
, SU_ADDRSTRLEN
, "%s", inet_ntoa (su
->sin
.sin_addr
));
311 snprintf (buf
, SU_ADDRSTRLEN
, "%s",
312 inet_ntop (AF_INET6
, &(su
->sin6
.sin6_addr
), buf
, SU_ADDRSTRLEN
));
314 #endif /* HAVE_IPV6 */
316 snprintf (buf
, SU_ADDRSTRLEN
, "af_unknown %d ", su
->sa
.sa_family
);
319 return (XSTRDUP (MTYPE_TMP
, buf
));
322 /* sockunion_connect returns
325 1 : connect is in progress */
327 sockunion_connect (int fd
, union sockunion
*peersu
, unsigned short port
,
328 unsigned int ifindex
)
334 memcpy (&su
, peersu
, sizeof (union sockunion
));
336 switch (su
.sa
.sa_family
)
339 su
.sin
.sin_port
= port
;
343 su
.sin6
.sin6_port
= port
;
345 if (IN6_IS_ADDR_LINKLOCAL(&su
.sin6
.sin6_addr
) && ifindex
)
347 #ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID
348 /* su.sin6.sin6_scope_id = ifindex; */
350 su
.sin6
.sin6_scope_id
= ifindex
;
352 #endif /* HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID */
354 SET_IN6_LINKLOCAL_IFINDEX (su
.sin6
.sin6_addr
, ifindex
);
359 #endif /* HAVE_IPV6 */
362 /* Make socket non-block. */
363 val
= fcntl (fd
, F_GETFL
, 0);
364 fcntl (fd
, F_SETFL
, val
|O_NONBLOCK
);
366 /* Call connect function. */
367 ret
= connect (fd
, (struct sockaddr
*) &su
, sockunion_sizeof (&su
));
369 /* Immediate success */
372 fcntl (fd
, F_SETFL
, val
);
373 return connect_success
;
376 /* If connect is in progress then return 1 else it's real error. */
379 if (errno
!= EINPROGRESS
)
381 zlog_info ("can't connect to %s fd %d : %s",
382 sockunion_log (&su
), fd
, safe_strerror (errno
));
383 return connect_error
;
387 fcntl (fd
, F_SETFL
, val
);
389 return connect_in_progress
;
392 /* Make socket from sockunion union. */
394 sockunion_stream_socket (union sockunion
*su
)
398 if (su
->sa
.sa_family
== 0)
399 su
->sa
.sa_family
= AF_INET_UNION
;
401 sock
= socket (su
->sa
.sa_family
, SOCK_STREAM
, 0);
404 zlog (NULL
, LOG_WARNING
, "can't make socket sockunion_stream_socket");
409 /* Bind socket to specified address. */
411 sockunion_bind (int sock
, union sockunion
*su
, unsigned short port
,
412 union sockunion
*su_addr
)
417 if (su
->sa
.sa_family
== AF_INET
)
419 size
= sizeof (struct sockaddr_in
);
420 su
->sin
.sin_port
= htons (port
);
421 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
422 su
->sin
.sin_len
= size
;
423 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
425 su
->sin
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
428 else if (su
->sa
.sa_family
== AF_INET6
)
430 size
= sizeof (struct sockaddr_in6
);
431 su
->sin6
.sin6_port
= htons (port
);
433 su
->sin6
.sin6_len
= size
;
434 #endif /* SIN6_LEN */
437 #if defined(LINUX_IPV6) || defined(NRL)
438 memset (&su
->sin6
.sin6_addr
, 0, sizeof (struct in6_addr
));
440 su
->sin6
.sin6_addr
= in6addr_any
;
441 #endif /* LINUX_IPV6 */
444 #endif /* HAVE_IPV6 */
447 ret
= bind (sock
, (struct sockaddr
*)su
, size
);
449 zlog (NULL
, LOG_WARNING
, "can't bind socket : %s", safe_strerror (errno
));
455 sockopt_reuseaddr (int sock
)
460 ret
= setsockopt (sock
, SOL_SOCKET
, SO_REUSEADDR
,
461 (void *) &on
, sizeof (on
));
464 zlog (NULL
, LOG_WARNING
, "can't set sockopt SO_REUSEADDR to socket %d", sock
);
472 sockopt_reuseport (int sock
)
477 ret
= setsockopt (sock
, SOL_SOCKET
, SO_REUSEPORT
,
478 (void *) &on
, sizeof (on
));
481 zlog (NULL
, LOG_WARNING
, "can't set sockopt SO_REUSEPORT to socket %d", sock
);
488 sockopt_reuseport (int sock
)
495 sockopt_ttl (int family
, int sock
, int ttl
)
500 if (family
== AF_INET
)
502 ret
= setsockopt (sock
, IPPROTO_IP
, IP_TTL
,
503 (void *) &ttl
, sizeof (int));
506 zlog (NULL
, LOG_WARNING
, "can't set sockopt IP_TTL %d to socket %d", ttl
, sock
);
513 if (family
== AF_INET6
)
515 ret
= setsockopt (sock
, IPPROTO_IPV6
, IPV6_UNICAST_HOPS
,
516 (void *) &ttl
, sizeof (int));
519 zlog (NULL
, LOG_WARNING
, "can't set sockopt IPV6_UNICAST_HOPS %d to socket %d",
525 #endif /* HAVE_IPV6 */
529 /* If same family and same prefix return 1. */
531 sockunion_same (union sockunion
*su1
, union sockunion
*su2
)
535 if (su1
->sa
.sa_family
!= su2
->sa
.sa_family
)
538 switch (su1
->sa
.sa_family
)
541 ret
= memcmp (&su1
->sin
.sin_addr
, &su2
->sin
.sin_addr
,
542 sizeof (struct in_addr
));
546 ret
= memcmp (&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
,
547 sizeof (struct in6_addr
));
549 #endif /* HAVE_IPV6 */
557 /* After TCP connection is established. Get local address and port. */
559 sockunion_getsockname (int fd
)
566 struct sockaddr_in sin
;
568 struct sockaddr_in6 sin6
;
569 #endif /* HAVE_IPV6 */
570 char tmp_buffer
[128];
574 memset (&name
, 0, sizeof name
);
577 ret
= getsockname (fd
, (struct sockaddr
*)&name
, &len
);
580 zlog_warn ("Can't get local address and port by getsockname: %s",
581 safe_strerror (errno
));
585 if (name
.sa
.sa_family
== AF_INET
)
587 su
= XCALLOC (MTYPE_SOCKUNION
, sizeof (union sockunion
));
588 memcpy (su
, &name
, sizeof (struct sockaddr_in
));
592 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
);
599 #endif /* HAVE_IPV6 */
603 /* After TCP connection is established. Get remote address and port. */
605 sockunion_getpeername (int fd
)
612 struct sockaddr_in sin
;
614 struct sockaddr_in6 sin6
;
615 #endif /* HAVE_IPV6 */
616 char tmp_buffer
[128];
620 memset (&name
, 0, sizeof name
);
622 ret
= getpeername (fd
, (struct sockaddr
*)&name
, &len
);
625 zlog (NULL
, LOG_WARNING
, "Can't get remote address and port: %s",
626 safe_strerror (errno
));
630 if (name
.sa
.sa_family
== AF_INET
)
632 su
= XCALLOC (MTYPE_SOCKUNION
, sizeof (union sockunion
));
633 memcpy (su
, &name
, sizeof (struct sockaddr_in
));
637 if (name
.sa
.sa_family
== AF_INET6
)
639 su
= XCALLOC (MTYPE_SOCKUNION
, sizeof (union sockunion
));
640 memcpy (su
, &name
, sizeof (struct sockaddr_in6
));
641 sockunion_normalise_mapped (su
);
644 #endif /* HAVE_IPV6 */
648 /* Print sockunion structure */
649 static void __attribute__ ((unused
))
650 sockunion_print (union sockunion
*su
)
655 switch (su
->sa
.sa_family
)
658 printf ("%s\n", inet_ntoa (su
->sin
.sin_addr
));
663 char buf
[SU_ADDRSTRLEN
];
665 printf ("%s\n", inet_ntop (AF_INET6
, &(su
->sin6
.sin6_addr
),
669 #endif /* HAVE_IPV6 */
674 struct sockaddr_dl
*sdl
;
676 sdl
= (struct sockaddr_dl
*)&(su
->sa
);
677 printf ("link#%d\n", sdl
->sdl_index
);
682 printf ("af_unknown %d\n", su
->sa
.sa_family
);
689 in6addr_cmp (struct in6_addr
*addr1
, struct in6_addr
*addr2
)
694 p1
= (u_char
*)addr1
;
695 p2
= (u_char
*)addr2
;
697 for (i
= 0; i
< sizeof (struct in6_addr
); i
++)
701 else if (p1
[i
] < p2
[i
])
706 #endif /* HAVE_IPV6 */
709 sockunion_cmp (union sockunion
*su1
, union sockunion
*su2
)
711 if (su1
->sa
.sa_family
> su2
->sa
.sa_family
)
713 if (su1
->sa
.sa_family
< su2
->sa
.sa_family
)
716 if (su1
->sa
.sa_family
== AF_INET
)
718 if (ntohl (su1
->sin
.sin_addr
.s_addr
) == ntohl (su2
->sin
.sin_addr
.s_addr
))
720 if (ntohl (su1
->sin
.sin_addr
.s_addr
) > ntohl (su2
->sin
.sin_addr
.s_addr
))
726 if (su1
->sa
.sa_family
== AF_INET6
)
727 return in6addr_cmp (&su1
->sin6
.sin6_addr
, &su2
->sin6
.sin6_addr
);
728 #endif /* HAVE_IPV6 */
732 /* Duplicate sockunion. */
734 sockunion_dup (union sockunion
*su
)
736 union sockunion
*dup
= XCALLOC (MTYPE_SOCKUNION
, sizeof (union sockunion
));
737 memcpy (dup
, su
, sizeof (union sockunion
));
742 sockunion_free (union sockunion
*su
)
744 XFREE (MTYPE_SOCKUNION
, su
);