sockopt.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. /* setsockopt functions
  2. * Copyright (C) 1999 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. #include <zebra.h>
  22. #include "log.h"
  23. #include "sockopt.h"
  24. int
  25. setsockopt_so_recvbuf (int sock, int size)
  26. {
  27. int ret;
  28. if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
  29. &size, sizeof (int))) < 0)
  30. zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
  31. sock,size,safe_strerror(errno));
  32. return ret;
  33. }
  34. static void *
  35. getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
  36. {
  37. struct cmsghdr *cmsg;
  38. void *ptr = NULL;
  39. for (cmsg = ZCMSG_FIRSTHDR(msgh);
  40. cmsg != NULL;
  41. cmsg = CMSG_NXTHDR(msgh, cmsg))
  42. if (cmsg->cmsg_level == level && cmsg->cmsg_type)
  43. return (ptr = CMSG_DATA(cmsg));
  44. return NULL;
  45. }
  46. #ifdef HAVE_IPV6
  47. /* Set IPv6 packet info to the socket. */
  48. int
  49. setsockopt_ipv6_pktinfo (int sock, int val)
  50. {
  51. int ret;
  52. #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
  53. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
  54. if (ret < 0)
  55. zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
  56. #else /*RFC2292*/
  57. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
  58. if (ret < 0)
  59. zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
  60. #endif /* INIA_IPV6 */
  61. return ret;
  62. }
  63. /* Set multicast hops val to the socket. */
  64. int
  65. setsockopt_ipv6_checksum (int sock, int val)
  66. {
  67. int ret;
  68. #ifdef GNU_LINUX
  69. ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
  70. #else
  71. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
  72. #endif /* GNU_LINUX */
  73. if (ret < 0)
  74. zlog_warn ("can't setsockopt IPV6_CHECKSUM");
  75. return ret;
  76. }
  77. /* Set multicast hops val to the socket. */
  78. int
  79. setsockopt_ipv6_multicast_hops (int sock, int val)
  80. {
  81. int ret;
  82. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
  83. if (ret < 0)
  84. zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
  85. return ret;
  86. }
  87. /* Set multicast hops val to the socket. */
  88. int
  89. setsockopt_ipv6_unicast_hops (int sock, int val)
  90. {
  91. int ret;
  92. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
  93. if (ret < 0)
  94. zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
  95. return ret;
  96. }
  97. int
  98. setsockopt_ipv6_hoplimit (int sock, int val)
  99. {
  100. int ret;
  101. #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
  102. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
  103. if (ret < 0)
  104. zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
  105. #else /*RFC2292*/
  106. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
  107. if (ret < 0)
  108. zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
  109. #endif
  110. return ret;
  111. }
  112. /* Set multicast loop zero to the socket. */
  113. int
  114. setsockopt_ipv6_multicast_loop (int sock, int val)
  115. {
  116. int ret;
  117. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
  118. sizeof (val));
  119. if (ret < 0)
  120. zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
  121. return ret;
  122. }
  123. static int
  124. getsockopt_ipv6_ifindex (struct msghdr *msgh)
  125. {
  126. struct in6_pktinfo *pktinfo;
  127. pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
  128. return pktinfo->ipi6_ifindex;
  129. }
  130. #endif /* HAVE_IPV6 */
  131. /*
  132. * Process multicast socket options for IPv4 in an OS-dependent manner.
  133. * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP.
  134. *
  135. * Many operating systems have a limit on the number of groups that
  136. * can be joined per socket (where each group and local address
  137. * counts). This impacts OSPF, which joins groups on each interface
  138. * using a single socket. The limit is typically 20, derived from the
  139. * original BSD multicast implementation. Some systems have
  140. * mechanisms for increasing this limit.
  141. */
  142. int
  143. setsockopt_multicast_ipv4(int sock,
  144. int optname,
  145. struct in_addr if_addr,
  146. unsigned int mcast_addr,
  147. unsigned int ifindex)
  148. {
  149. /* Linux 2.2.0 and up */
  150. #if defined(GNU_LINUX) && LINUX_VERSION_CODE > 131584
  151. /* This is better because it uses ifindex directly */
  152. struct ip_mreqn mreqn;
  153. switch (optname)
  154. {
  155. case IP_MULTICAST_IF:
  156. case IP_ADD_MEMBERSHIP:
  157. case IP_DROP_MEMBERSHIP:
  158. memset (&mreqn, 0, sizeof(mreqn));
  159. if (mcast_addr)
  160. mreqn.imr_multiaddr.s_addr = mcast_addr;
  161. if (ifindex)
  162. mreqn.imr_ifindex = ifindex;
  163. else
  164. mreqn.imr_address = if_addr;
  165. return setsockopt(sock, IPPROTO_IP, optname, (void *)&mreqn, sizeof(mreqn));
  166. break;
  167. default:
  168. /* Can out and give an understandable error */
  169. errno = EINVAL;
  170. return -1;
  171. break;
  172. }
  173. /* Example defines for another OS, boilerplate off other code in this
  174. function, AND handle optname as per other sections for consistency !! */
  175. /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
  176. /* Add your favourite OS here! */
  177. #else /* #if OS_TYPE */
  178. /* standard BSD API */
  179. struct in_addr m;
  180. struct ip_mreq mreq;
  181. switch (optname)
  182. {
  183. case IP_MULTICAST_IF:
  184. m = if_addr;
  185. return setsockopt (sock, IPPROTO_IP, optname, (void *)&m, sizeof(m));
  186. break;
  187. case IP_ADD_MEMBERSHIP:
  188. case IP_DROP_MEMBERSHIP:
  189. memset (&mreq, 0, sizeof(mreq));
  190. mreq.imr_multiaddr.s_addr = mcast_addr;
  191. mreq.imr_interface = if_addr;
  192. return setsockopt (sock,
  193. IPPROTO_IP,
  194. optname,
  195. (void *)&mreq,
  196. sizeof(mreq));
  197. break;
  198. default:
  199. /* Can out and give an understandable error */
  200. errno = EINVAL;
  201. return -1;
  202. break;
  203. }
  204. #endif /* #if OS_TYPE */
  205. }
  206. static int
  207. setsockopt_ipv4_ifindex (int sock, int val)
  208. {
  209. int ret;
  210. #if defined (IP_PKTINFO)
  211. if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
  212. zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
  213. sock,val,safe_strerror(errno));
  214. #elif defined (IP_RECVIF)
  215. if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
  216. zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
  217. sock,val,safe_strerror(errno));
  218. #else
  219. #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
  220. #warning "Will not be able to receive link info."
  221. #warning "Things might be seriously broken.."
  222. /* XXX Does this ever happen? Should there be a zlog_warn message here? */
  223. ret = -1;
  224. #endif
  225. return ret;
  226. }
  227. int
  228. setsockopt_ifindex (int af, int sock, int val)
  229. {
  230. int ret = -1;
  231. switch (af)
  232. {
  233. case AF_INET:
  234. ret = setsockopt_ipv4_ifindex (sock, val);
  235. break;
  236. #ifdef HAVE_IPV6
  237. case AF_INET6:
  238. ret = setsockopt_ipv6_pktinfo (sock, val);
  239. break;
  240. #endif
  241. default:
  242. zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
  243. }
  244. return ret;
  245. }
  246. /*
  247. * Requires: msgh is not NULL and points to a valid struct msghdr, which
  248. * may or may not have control data about the incoming interface.
  249. *
  250. * Returns the interface index (small integer >= 1) if it can be
  251. * determined, or else 0.
  252. */
  253. static int
  254. getsockopt_ipv4_ifindex (struct msghdr *msgh)
  255. {
  256. /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
  257. int ifindex = -1;
  258. #if defined(IP_PKTINFO)
  259. /* Linux pktinfo based ifindex retrieval */
  260. struct in_pktinfo *pktinfo;
  261. pktinfo =
  262. (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
  263. /* XXX Can pktinfo be NULL? Clean up post 0.98. */
  264. ifindex = pktinfo->ipi_ifindex;
  265. #elif defined(IP_RECVIF)
  266. /* retrieval based on IP_RECVIF */
  267. #ifndef SUNOS_5
  268. /* BSD systems use a sockaddr_dl as the control message payload. */
  269. struct sockaddr_dl *sdl;
  270. #else
  271. /* SUNOS_5 uses an integer with the index. */
  272. int *ifindex_p;
  273. #endif /* SUNOS_5 */
  274. #ifndef SUNOS_5
  275. /* BSD */
  276. sdl =
  277. (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
  278. if (sdl != NULL)
  279. ifindex = sdl->sdl_index;
  280. else
  281. ifindex = 0;
  282. #else
  283. /*
  284. * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
  285. * enable it fails with errno=99, and the struct msghdr has
  286. * controllen 0.
  287. */
  288. ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
  289. if (ifindex_p != NULL)
  290. ifindex = *ifindex_p;
  291. else
  292. ifindex = 0;
  293. #endif /* SUNOS_5 */
  294. #else
  295. /*
  296. * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
  297. * XXX Decide if this is a core service, or if daemons have to cope.
  298. * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
  299. * daemons have to cope.
  300. */
  301. #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
  302. #warning "Some daemons may fail to operate correctly!"
  303. ifindex = 0;
  304. #endif /* IP_PKTINFO */
  305. return ifindex;
  306. }
  307. /* return ifindex, 0 if none found */
  308. int
  309. getsockopt_ifindex (int af, struct msghdr *msgh)
  310. {
  311. int ifindex = 0;
  312. switch (af)
  313. {
  314. case AF_INET:
  315. return (getsockopt_ipv4_ifindex (msgh));
  316. break;
  317. #ifdef HAVE_IPV6
  318. case AF_INET6:
  319. return (getsockopt_ipv6_ifindex (msgh));
  320. break;
  321. #endif
  322. default:
  323. zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
  324. return (ifindex = 0);
  325. }
  326. }
  327. /* swab iph between order system uses for IP_HDRINCL and host order */
  328. void
  329. sockopt_iphdrincl_swab_htosys (struct ip *iph)
  330. {
  331. /* BSD and derived take iph in network order, except for
  332. * ip_len and ip_off
  333. */
  334. #ifndef HAVE_IP_HDRINCL_BSD_ORDER
  335. iph->ip_len = htons(iph->ip_len);
  336. iph->ip_off = htons(iph->ip_off);
  337. #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
  338. iph->ip_id = htons(iph->ip_id);
  339. }
  340. void
  341. sockopt_iphdrincl_swab_systoh (struct ip *iph)
  342. {
  343. #ifndef HAVE_IP_HDRINCL_BSD_ORDER
  344. iph->ip_len = ntohs(iph->ip_len);
  345. iph->ip_off = ntohs(iph->ip_off);
  346. #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
  347. iph->ip_id = ntohs(iph->ip_id);
  348. }