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