1 /* setsockopt functions
 
   2  * Copyright (C) 1999 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
 
  30 #include "sockunion.h"
 
  33 setsockopt_so_recvbuf (int sock, int size)
 
  37   if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
 
  38                           &size, sizeof (int))) < 0)
 
  39     zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
 
  40               sock,size,safe_strerror(errno));
 
  46 setsockopt_so_sendbuf (const int sock, int size)
 
  48   int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
 
  49     (char *)&size, sizeof (int));
 
  52     zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
 
  53       sock, size, safe_strerror (errno));
 
  59 getsockopt_so_sendbuf (const int sock)
 
  62   socklen_t optlen = sizeof (optval);
 
  63   int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF,
 
  64     (char *)&optval, &optlen);
 
  67     zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
 
  68       sock, errno, safe_strerror (errno));
 
  75 getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
 
  80   for (cmsg = ZCMSG_FIRSTHDR(msgh); 
 
  82        cmsg = CMSG_NXTHDR(msgh, cmsg))
 
  83     if (cmsg->cmsg_level == level && cmsg->cmsg_type)
 
  84       return (ptr = CMSG_DATA(cmsg));
 
  90 /* Set IPv6 packet info to the socket. */
 
  92 setsockopt_ipv6_pktinfo (int sock, int val)
 
  96 #ifdef IPV6_RECVPKTINFO         /*2292bis-01*/
 
  97   ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
 
  99     zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
 
 101   ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
 
 103     zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
 
 104 #endif /* INIA_IPV6 */
 
 108 /* Set multicast hops val to the socket. */
 
 110 setsockopt_ipv6_checksum (int sock, int val)
 
 115   ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
 
 117   ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
 
 118 #endif /* GNU_LINUX */
 
 120     zlog_warn ("can't setsockopt IPV6_CHECKSUM");
 
 124 /* Set multicast hops val to the socket. */
 
 126 setsockopt_ipv6_multicast_hops (int sock, int val)
 
 130   ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
 
 132     zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
 
 136 /* Set multicast hops val to the socket. */
 
 138 setsockopt_ipv6_unicast_hops (int sock, int val)
 
 142   ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
 
 144     zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
 
 149 setsockopt_ipv6_hoplimit (int sock, int val)
 
 153 #ifdef IPV6_RECVHOPLIMIT        /*2292bis-01*/
 
 154   ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
 
 156     zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
 
 158   ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
 
 160     zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
 
 165 /* Set multicast loop zero to the socket. */
 
 167 setsockopt_ipv6_multicast_loop (int sock, int val)
 
 171   ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
 
 174     zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
 
 179 getsockopt_ipv6_ifindex (struct msghdr *msgh)
 
 181   struct in6_pktinfo *pktinfo;
 
 183   pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
 
 185   return pktinfo->ipi6_ifindex;
 
 189 setsockopt_ipv6_tclass(int sock, int tclass)
 
 193 #ifdef IPV6_TCLASS /* RFC3542 */
 
 194   ret = setsockopt (sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof (tclass));
 
 196     zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s",
 
 197                sock, tclass, safe_strerror(errno));
 
 201 #endif /* HAVE_IPV6 */
 
 204  * Process multicast socket options for IPv4 in an OS-dependent manner.
 
 205  * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
 
 207  * Many operating systems have a limit on the number of groups that
 
 208  * can be joined per socket (where each group and local address
 
 209  * counts).  This impacts OSPF, which joins groups on each interface
 
 210  * using a single socket.  The limit is typically 20, derived from the
 
 211  * original BSD multicast implementation.  Some systems have
 
 212  * mechanisms for increasing this limit.
 
 214  * In many 4.4BSD-derived systems, multicast group operations are not
 
 215  * allowed on interfaces that are not UP.  Thus, a previous attempt to
 
 216  * leave the group may have failed, leaving it still joined, and we
 
 217  * drop/join quietly to recover.  This may not be necessary, but aims to
 
 218  * defend against unknown behavior in that we will still return an error
 
 219  * if the second join fails.  It is not clear how other systems
 
 220  * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
 
 221  * but this behavior should not be harmful if they behave the same way,
 
 222  * allow leaves, or implicitly leave all groups joined to down interfaces.
 
 225 setsockopt_ipv4_multicast(int sock,
 
 227                         unsigned int mcast_addr,
 
 232   struct sockaddr_in *si;
 
 234   memset (&gr, 0, sizeof(gr));
 
 235   si = (struct sockaddr_in *)&gr.gr_group;
 
 236   gr.gr_interface = ifindex;
 
 237   si->sin_family = AF_INET;
 
 238 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
 
 239   si->sin_len = sizeof(struct sockaddr_in);
 
 240 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
 
 241   si->sin_addr.s_addr = mcast_addr;
 
 242   ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ? 
 
 243     MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
 
 244   if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
 
 246       setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
 
 247       ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr));
 
 251 #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
 
 252   struct ip_mreqn mreqn;
 
 255   assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
 
 256   memset (&mreqn, 0, sizeof(mreqn));
 
 258   mreqn.imr_multiaddr.s_addr = mcast_addr;
 
 259   mreqn.imr_ifindex = ifindex;
 
 261   ret = setsockopt(sock, IPPROTO_IP, optname,
 
 262                    (void *)&mreqn, sizeof(mreqn));
 
 263   if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
 
 265       /* see above: handle possible problem when interface comes back up */
 
 266       char buf[1][INET_ADDRSTRLEN];
 
 267       zlog_info("setsockopt_ipv4_multicast attempting to drop and "
 
 268                 "re-add (fd %d, mcast %s, ifindex %u)",
 
 270                 inet_ntop(AF_INET, &mreqn.imr_multiaddr,
 
 271                           buf[0], sizeof(buf[0])), ifindex);
 
 272       setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
 
 273                  (void *)&mreqn, sizeof(mreqn));
 
 274       ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
 
 275                        (void *)&mreqn, sizeof(mreqn));
 
 279   /* Example defines for another OS, boilerplate off other code in this
 
 280      function, AND handle optname as per other sections for consistency !! */
 
 281   /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
 
 282   /* Add your favourite OS here! */
 
 284 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */ 
 
 285   /* standard BSD API */
 
 291   assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
 
 293   m.s_addr = htonl(ifindex);
 
 295   memset (&mreq, 0, sizeof(mreq));
 
 296   mreq.imr_multiaddr.s_addr = mcast_addr;
 
 297   mreq.imr_interface = m;
 
 299   ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
 
 300   if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
 
 302       /* see above: handle possible problem when interface comes back up */
 
 303       char buf[1][INET_ADDRSTRLEN];
 
 304       zlog_info("setsockopt_ipv4_multicast attempting to drop and "
 
 305                 "re-add (fd %d, mcast %s, ifindex %u)",
 
 307                 inet_ntop(AF_INET, &mreq.imr_multiaddr,
 
 308                           buf[0], sizeof(buf[0])), ifindex);
 
 309       setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
 
 310                   (void *)&mreq, sizeof(mreq));
 
 311       ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
 
 312                         (void *)&mreq, sizeof(mreq));
 
 317   #error "Unsupported multicast API"
 
 318 #endif /* #if OS_TYPE */
 
 323  * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
 
 326 setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex)
 
 329 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
 
 330   struct ip_mreqn mreqn;
 
 331   memset (&mreqn, 0, sizeof(mreqn));
 
 333   mreqn.imr_ifindex = ifindex;
 
 334   return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn));
 
 336   /* Example defines for another OS, boilerplate off other code in this
 
 338   /* #elif  defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
 
 339   /* Add your favourite OS here! */
 
 340 #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
 
 343   m.s_addr = htonl(ifindex);
 
 345   return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m));
 
 346 #elif defined(SUNOS_5)
 
 347   char ifname[IF_NAMESIZE];
 
 348   struct ifaddrs *ifa, *ifap;
 
 349   struct in_addr ifaddr;
 
 351   if (if_indextoname(ifindex, ifname) == NULL)
 
 354   if (getifaddrs(&ifa) != 0)
 
 357   for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
 
 359       struct sockaddr_in *sa;
 
 361       if (strcmp(ifap->ifa_name, ifname) != 0)
 
 363       if (ifap->ifa_addr->sa_family != AF_INET)
 
 365       sa = (struct sockaddr_in*)ifap->ifa_addr;
 
 366       memcpy(&ifaddr, &sa->sin_addr, sizeof(ifaddr));
 
 371   if (!ifap) /* This means we did not find an IP */
 
 374   return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&ifaddr, sizeof(ifaddr));
 
 376   #error "Unsupported multicast API"
 
 381 setsockopt_ipv4_ifindex (int sock, ifindex_t val)
 
 385 #if defined (IP_PKTINFO)
 
 386   if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
 
 387     zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
 
 388                sock,val,safe_strerror(errno));
 
 389 #elif defined (IP_RECVIF)
 
 390   if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
 
 391     zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
 
 392                sock,val,safe_strerror(errno));
 
 394 #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
 
 395 #warning "Will not be able to receive link info."
 
 396 #warning "Things might be seriously broken.."
 
 397   /* XXX Does this ever happen?  Should there be a zlog_warn message here? */
 
 404 setsockopt_ipv4_tos(int sock, int tos)
 
 408   ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
 
 410     zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
 
 411                sock, tos, safe_strerror(errno));
 
 417 setsockopt_ifindex (int af, int sock, ifindex_t val)
 
 424         ret = setsockopt_ipv4_ifindex (sock, val);
 
 428         ret = setsockopt_ipv6_pktinfo (sock, val);
 
 432         zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
 
 438  * Requires: msgh is not NULL and points to a valid struct msghdr, which
 
 439  * may or may not have control data about the incoming interface.
 
 441  * Returns the interface index (small integer >= 1) if it can be
 
 442  * determined, or else 0.
 
 445 getsockopt_ipv4_ifindex (struct msghdr *msgh)
 
 447   /* XXX: initialize to zero?  (Always overwritten, so just cosmetic.) */
 
 448   ifindex_t ifindex = -1;
 
 450 #if defined(IP_PKTINFO)
 
 451 /* Linux pktinfo based ifindex retrieval */
 
 452   struct in_pktinfo *pktinfo;
 
 455     (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
 
 456   /* XXX Can pktinfo be NULL?  Clean up post 0.98. */
 
 457   ifindex = pktinfo->ipi_ifindex;
 
 459 #elif defined(IP_RECVIF)
 
 461   /* retrieval based on IP_RECVIF */
 
 464   /* BSD systems use a sockaddr_dl as the control message payload. */
 
 465   struct sockaddr_dl *sdl;
 
 467   /* SUNOS_5 uses an integer with the index. */
 
 468   ifindex_t *ifindex_p;
 
 474     (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
 
 476     ifindex = sdl->sdl_index;
 
 481    * Solaris.  On Solaris 8, IP_RECVIF is defined, but the call to
 
 482    * enable it fails with errno=99, and the struct msghdr has
 
 485   ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF); 
 
 486   if (ifindex_p != NULL)
 
 487     ifindex = *ifindex_p;
 
 494    * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
 
 495    * XXX Decide if this is a core service, or if daemons have to cope.
 
 496    * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
 
 497    * daemons have to cope.
 
 499 #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
 
 500 #warning "Some daemons may fail to operate correctly!"
 
 503 #endif /* IP_PKTINFO */ 
 
 508 /* return ifindex, 0 if none found */
 
 510 getsockopt_ifindex (int af, struct msghdr *msgh)
 
 515         return (getsockopt_ipv4_ifindex (msgh));
 
 519         return (getsockopt_ipv6_ifindex (msgh));
 
 523         zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
 
 528 /* swab iph between order system uses for IP_HDRINCL and host order */
 
 530 sockopt_iphdrincl_swab_htosys (struct ip *iph)
 
 532   /* BSD and derived take iph in network order, except for 
 
 535 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
 
 536   iph->ip_len = htons(iph->ip_len);
 
 537   iph->ip_off = htons(iph->ip_off);
 
 538 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
 
 540   iph->ip_id = htons(iph->ip_id);
 
 544 sockopt_iphdrincl_swab_systoh (struct ip *iph)
 
 546 #ifndef HAVE_IP_HDRINCL_BSD_ORDER
 
 547   iph->ip_len = ntohs(iph->ip_len);
 
 548   iph->ip_off = ntohs(iph->ip_off);
 
 549 #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
 
 551   iph->ip_id = ntohs(iph->ip_id);
 
 555 sockopt_tcp_rtt (int sock)
 
 559   socklen_t len = sizeof(ti);
 
 561   if (getsockopt (sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0)
 
 564   return ti.tcpi_rtt / 1000;
 
 571 sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
 
 573 #if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
 
 574   /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
 
 575    * version of the Quagga patch (based on work by Rick Payne, and Bruce
 
 578 #define TCP_MD5_AUTH 13
 
 579 #define TCP_MD5_AUTH_ADD 1
 
 580 #define TCP_MD5_AUTH_DEL 2
 
 581   struct tcp_rfc2385_cmd {
 
 582     u_int8_t     command;    /* Command - Add/Delete */
 
 583     u_int32_t    address;    /* IPV4 address associated */
 
 584     u_int8_t     keylen;     /* MD5 Key len (do NOT assume 0 terminated ascii) */
 
 585     void         *key;       /* MD5 Key */
 
 587   struct in_addr *addr = &su->sin.sin_addr;
 
 589   cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL);
 
 590   cmd.address = addr->s_addr;
 
 591   cmd.keylen = (password != NULL ? strlen (password) : 0);
 
 594   return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd);
 
 596 #elif HAVE_DECL_TCP_MD5SIG
 
 600    * XXX Need to do PF_KEY operation here to add/remove an SA entry,
 
 601    * and add/remove an SP entry for this peer's packet flows also.
 
 603   int md5sig = password && *password ? 1 : 0;
 
 605   int keylen = password ? strlen (password) : 0;
 
 606   struct tcp_md5sig md5sig;
 
 607   union sockunion *su2, *susock;
 
 609   /* Figure out whether the socket and the sockunion are the same family..
 
 610    * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
 
 612   if (!(susock = sockunion_getsockname (sock)))
 
 615   if (susock->sa.sa_family == su->sa.sa_family)
 
 622       if (su2->sa.sa_family == AF_INET)
 
 624           sockunion_free (susock);
 
 629       /* If this does not work, then all users of this sockopt will need to
 
 630        * differentiate between IPv4 and IPv6, and keep seperate sockets for
 
 633        * Sadly, it doesn't seem to work at present. It's unknown whether
 
 634        * this is a bug or not.
 
 636       if (su2->sa.sa_family == AF_INET6
 
 637           && su->sa.sa_family == AF_INET)
 
 639            su2->sin6.sin6_family = AF_INET6;
 
 640            /* V4Map the address */
 
 641            memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
 
 642            su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
 
 643            memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
 
 648   memset (&md5sig, 0, sizeof (md5sig));
 
 649   memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2));
 
 650   md5sig.tcpm_keylen = keylen;
 
 652     memcpy (md5sig.tcpm_key, password, keylen);
 
 653   sockunion_free (susock);
 
 654 #endif /* GNU_LINUX */
 
 655   if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0)
 
 657       /* ENOENT is harmless.  It is returned when we clear a password for which
 
 658          one was not previously set. */
 
 662         zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
 
 663                   sock, safe_strerror(errno));
 
 666 #else /* HAVE_TCP_MD5SIG */
 
 668 #endif /* !HAVE_TCP_MD5SIG */