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