sockopt.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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. #ifdef SUNOS_5
  23. #include <ifaddrs.h>
  24. #endif
  25. #include "log.h"
  26. #include "sockopt.h"
  27. #include "sockunion.h"
  28. int
  29. setsockopt_so_recvbuf (int sock, int size)
  30. {
  31. int ret;
  32. if ( (ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *)
  33. &size, sizeof (int))) < 0)
  34. zlog_err ("fd %d: can't setsockopt SO_RCVBUF to %d: %s",
  35. sock,size,safe_strerror(errno));
  36. return ret;
  37. }
  38. int
  39. setsockopt_so_sendbuf (const int sock, int size)
  40. {
  41. int ret = setsockopt (sock, SOL_SOCKET, SO_SNDBUF,
  42. (char *)&size, sizeof (int));
  43. if (ret < 0)
  44. zlog_err ("fd %d: can't setsockopt SO_SNDBUF to %d: %s",
  45. sock, size, safe_strerror (errno));
  46. return ret;
  47. }
  48. int
  49. getsockopt_so_sendbuf (const int sock)
  50. {
  51. u_int32_t optval;
  52. socklen_t optlen = sizeof (optval);
  53. int ret = getsockopt (sock, SOL_SOCKET, SO_SNDBUF,
  54. (char *)&optval, &optlen);
  55. if (ret < 0)
  56. {
  57. zlog_err ("fd %d: can't getsockopt SO_SNDBUF: %d (%s)",
  58. sock, errno, safe_strerror (errno));
  59. return ret;
  60. }
  61. return optval;
  62. }
  63. static void *
  64. getsockopt_cmsg_data (struct msghdr *msgh, int level, int type)
  65. {
  66. struct cmsghdr *cmsg;
  67. void *ptr = NULL;
  68. for (cmsg = ZCMSG_FIRSTHDR(msgh);
  69. cmsg != NULL;
  70. cmsg = CMSG_NXTHDR(msgh, cmsg))
  71. if (cmsg->cmsg_level == level && cmsg->cmsg_type)
  72. return (ptr = CMSG_DATA(cmsg));
  73. return NULL;
  74. }
  75. #ifdef HAVE_IPV6
  76. /* Set IPv6 packet info to the socket. */
  77. int
  78. setsockopt_ipv6_pktinfo (int sock, int val)
  79. {
  80. int ret;
  81. #ifdef IPV6_RECVPKTINFO /*2292bis-01*/
  82. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
  83. if (ret < 0)
  84. zlog_warn ("can't setsockopt IPV6_RECVPKTINFO : %s", safe_strerror (errno));
  85. #else /*RFC2292*/
  86. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val));
  87. if (ret < 0)
  88. zlog_warn ("can't setsockopt IPV6_PKTINFO : %s", safe_strerror (errno));
  89. #endif /* INIA_IPV6 */
  90. return ret;
  91. }
  92. /* Set multicast hops val to the socket. */
  93. int
  94. setsockopt_ipv6_checksum (int sock, int val)
  95. {
  96. int ret;
  97. #ifdef GNU_LINUX
  98. ret = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val));
  99. #else
  100. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val));
  101. #endif /* GNU_LINUX */
  102. if (ret < 0)
  103. zlog_warn ("can't setsockopt IPV6_CHECKSUM");
  104. return ret;
  105. }
  106. /* Set multicast hops val to the socket. */
  107. int
  108. setsockopt_ipv6_multicast_hops (int sock, int val)
  109. {
  110. int ret;
  111. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val));
  112. if (ret < 0)
  113. zlog_warn ("can't setsockopt IPV6_MULTICAST_HOPS");
  114. return ret;
  115. }
  116. /* Set multicast hops val to the socket. */
  117. int
  118. setsockopt_ipv6_unicast_hops (int sock, int val)
  119. {
  120. int ret;
  121. ret = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val));
  122. if (ret < 0)
  123. zlog_warn ("can't setsockopt IPV6_UNICAST_HOPS");
  124. return ret;
  125. }
  126. int
  127. setsockopt_ipv6_hoplimit (int sock, int val)
  128. {
  129. int ret;
  130. #ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/
  131. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val));
  132. if (ret < 0)
  133. zlog_warn ("can't setsockopt IPV6_RECVHOPLIMIT");
  134. #else /*RFC2292*/
  135. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val));
  136. if (ret < 0)
  137. zlog_warn ("can't setsockopt IPV6_HOPLIMIT");
  138. #endif
  139. return ret;
  140. }
  141. /* Set multicast loop zero to the socket. */
  142. int
  143. setsockopt_ipv6_multicast_loop (int sock, int val)
  144. {
  145. int ret;
  146. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val,
  147. sizeof (val));
  148. if (ret < 0)
  149. zlog_warn ("can't setsockopt IPV6_MULTICAST_LOOP");
  150. return ret;
  151. }
  152. static int
  153. getsockopt_ipv6_ifindex (struct msghdr *msgh)
  154. {
  155. struct in6_pktinfo *pktinfo;
  156. pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO);
  157. return pktinfo->ipi6_ifindex;
  158. }
  159. int
  160. setsockopt_ipv6_tclass(int sock, int tclass)
  161. {
  162. int ret = 0;
  163. #ifdef IPV6_TCLASS /* RFC3542 */
  164. ret = setsockopt (sock, IPPROTO_IPV6, IPV6_TCLASS, &tclass, sizeof (tclass));
  165. if (ret < 0)
  166. zlog_warn ("Can't set IPV6_TCLASS option for fd %d to %#x: %s",
  167. sock, tclass, safe_strerror(errno));
  168. #endif
  169. return ret;
  170. }
  171. #endif /* HAVE_IPV6 */
  172. /*
  173. * Process multicast socket options for IPv4 in an OS-dependent manner.
  174. * Supported options are IP_{ADD,DROP}_MEMBERSHIP.
  175. *
  176. * Many operating systems have a limit on the number of groups that
  177. * can be joined per socket (where each group and local address
  178. * counts). This impacts OSPF, which joins groups on each interface
  179. * using a single socket. The limit is typically 20, derived from the
  180. * original BSD multicast implementation. Some systems have
  181. * mechanisms for increasing this limit.
  182. *
  183. * In many 4.4BSD-derived systems, multicast group operations are not
  184. * allowed on interfaces that are not UP. Thus, a previous attempt to
  185. * leave the group may have failed, leaving it still joined, and we
  186. * drop/join quietly to recover. This may not be necessary, but aims to
  187. * defend against unknown behavior in that we will still return an error
  188. * if the second join fails. It is not clear how other systems
  189. * (e.g. Linux, Solaris) behave when leaving groups on down interfaces,
  190. * but this behavior should not be harmful if they behave the same way,
  191. * allow leaves, or implicitly leave all groups joined to down interfaces.
  192. */
  193. int
  194. setsockopt_ipv4_multicast(int sock,
  195. int optname,
  196. unsigned int mcast_addr,
  197. ifindex_t ifindex)
  198. {
  199. #ifdef HAVE_RFC3678
  200. struct group_req gr;
  201. struct sockaddr_in *si;
  202. int ret;
  203. memset (&gr, 0, sizeof(gr));
  204. si = (struct sockaddr_in *)&gr.gr_group;
  205. gr.gr_interface = ifindex;
  206. si->sin_family = AF_INET;
  207. #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
  208. si->sin_len = sizeof(struct sockaddr_in);
  209. #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
  210. si->sin_addr.s_addr = mcast_addr;
  211. ret = setsockopt(sock, IPPROTO_IP, (optname == IP_ADD_MEMBERSHIP) ?
  212. MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
  213. if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
  214. {
  215. setsockopt(sock, IPPROTO_IP, MCAST_LEAVE_GROUP, (void *)&gr, sizeof(gr));
  216. ret = setsockopt(sock, IPPROTO_IP, MCAST_JOIN_GROUP, (void *)&gr, sizeof(gr));
  217. }
  218. return ret;
  219. #elif defined(HAVE_STRUCT_IP_MREQN_IMR_IFINDEX) && !defined(__FreeBSD__)
  220. struct ip_mreqn mreqn;
  221. int ret;
  222. assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
  223. memset (&mreqn, 0, sizeof(mreqn));
  224. mreqn.imr_multiaddr.s_addr = mcast_addr;
  225. mreqn.imr_ifindex = ifindex;
  226. ret = setsockopt(sock, IPPROTO_IP, optname,
  227. (void *)&mreqn, sizeof(mreqn));
  228. if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
  229. {
  230. /* see above: handle possible problem when interface comes back up */
  231. char buf[1][INET_ADDRSTRLEN];
  232. zlog_info("setsockopt_ipv4_multicast attempting to drop and "
  233. "re-add (fd %d, mcast %s, ifindex %u)",
  234. sock,
  235. inet_ntop(AF_INET, &mreqn.imr_multiaddr,
  236. buf[0], sizeof(buf[0])), ifindex);
  237. setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
  238. (void *)&mreqn, sizeof(mreqn));
  239. ret = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  240. (void *)&mreqn, sizeof(mreqn));
  241. }
  242. return ret;
  243. /* Example defines for another OS, boilerplate off other code in this
  244. function, AND handle optname as per other sections for consistency !! */
  245. /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
  246. /* Add your favourite OS here! */
  247. #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK) /* #if OS_TYPE */
  248. /* standard BSD API */
  249. struct in_addr m;
  250. struct ip_mreq mreq;
  251. int ret;
  252. assert(optname == IP_ADD_MEMBERSHIP || optname == IP_DROP_MEMBERSHIP);
  253. m.s_addr = htonl(ifindex);
  254. memset (&mreq, 0, sizeof(mreq));
  255. mreq.imr_multiaddr.s_addr = mcast_addr;
  256. mreq.imr_interface = m;
  257. ret = setsockopt (sock, IPPROTO_IP, optname, (void *)&mreq, sizeof(mreq));
  258. if ((ret < 0) && (optname == IP_ADD_MEMBERSHIP) && (errno == EADDRINUSE))
  259. {
  260. /* see above: handle possible problem when interface comes back up */
  261. char buf[1][INET_ADDRSTRLEN];
  262. zlog_info("setsockopt_ipv4_multicast attempting to drop and "
  263. "re-add (fd %d, mcast %s, ifindex %u)",
  264. sock,
  265. inet_ntop(AF_INET, &mreq.imr_multiaddr,
  266. buf[0], sizeof(buf[0])), ifindex);
  267. setsockopt (sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
  268. (void *)&mreq, sizeof(mreq));
  269. ret = setsockopt (sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
  270. (void *)&mreq, sizeof(mreq));
  271. }
  272. return ret;
  273. #else
  274. #error "Unsupported multicast API"
  275. #endif /* #if OS_TYPE */
  276. }
  277. /*
  278. * Set IP_MULTICAST_IF socket option in an OS-dependent manner.
  279. */
  280. int
  281. setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex)
  282. {
  283. #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
  284. struct ip_mreqn mreqn;
  285. memset (&mreqn, 0, sizeof(mreqn));
  286. mreqn.imr_ifindex = ifindex;
  287. return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn));
  288. /* Example defines for another OS, boilerplate off other code in this
  289. function */
  290. /* #elif defined(BOGON_NIX) && EXAMPLE_VERSION_CODE > -100000 */
  291. /* Add your favourite OS here! */
  292. #elif defined(HAVE_BSD_STRUCT_IP_MREQ_HACK)
  293. struct in_addr m;
  294. m.s_addr = htonl(ifindex);
  295. return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m));
  296. #elif defined(SUNOS_5)
  297. char ifname[IF_NAMESIZE];
  298. struct ifaddrs *ifa, *ifap;
  299. struct in_addr ifaddr;
  300. if (if_indextoname(ifindex, ifname) == NULL)
  301. return -1;
  302. if (getifaddrs(&ifa) != 0)
  303. return -1;
  304. for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next)
  305. {
  306. struct sockaddr_in *sa;
  307. if (strcmp(ifap->ifa_name, ifname) != 0)
  308. continue;
  309. if (ifap->ifa_addr->sa_family != AF_INET)
  310. continue;
  311. sa = (struct sockaddr_in*)ifap->ifa_addr;
  312. memcpy(&ifaddr, &sa->sin_addr, sizeof(ifaddr));
  313. break;
  314. }
  315. freeifaddrs(ifa);
  316. if (!ifap) /* This means we did not find an IP */
  317. return -1;
  318. return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&ifaddr, sizeof(ifaddr));
  319. #else
  320. #error "Unsupported multicast API"
  321. #endif
  322. }
  323. static int
  324. setsockopt_ipv4_ifindex (int sock, ifindex_t val)
  325. {
  326. int ret;
  327. #if defined (IP_PKTINFO)
  328. if ((ret = setsockopt (sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val))) < 0)
  329. zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s",
  330. sock,val,safe_strerror(errno));
  331. #elif defined (IP_RECVIF)
  332. if ((ret = setsockopt (sock, IPPROTO_IP, IP_RECVIF, &val, sizeof (val))) < 0)
  333. zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s",
  334. sock,val,safe_strerror(errno));
  335. #else
  336. #warning "Neither IP_PKTINFO nor IP_RECVIF is available."
  337. #warning "Will not be able to receive link info."
  338. #warning "Things might be seriously broken.."
  339. /* XXX Does this ever happen? Should there be a zlog_warn message here? */
  340. ret = -1;
  341. #endif
  342. return ret;
  343. }
  344. int
  345. setsockopt_ipv4_tos(int sock, int tos)
  346. {
  347. int ret;
  348. ret = setsockopt (sock, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));
  349. if (ret < 0)
  350. zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s",
  351. sock, tos, safe_strerror(errno));
  352. return ret;
  353. }
  354. int
  355. setsockopt_ifindex (int af, int sock, ifindex_t val)
  356. {
  357. int ret = -1;
  358. switch (af)
  359. {
  360. case AF_INET:
  361. ret = setsockopt_ipv4_ifindex (sock, val);
  362. break;
  363. #ifdef HAVE_IPV6
  364. case AF_INET6:
  365. ret = setsockopt_ipv6_pktinfo (sock, val);
  366. break;
  367. #endif
  368. default:
  369. zlog_warn ("setsockopt_ifindex: unknown address family %d", af);
  370. }
  371. return ret;
  372. }
  373. /*
  374. * Requires: msgh is not NULL and points to a valid struct msghdr, which
  375. * may or may not have control data about the incoming interface.
  376. *
  377. * Returns the interface index (small integer >= 1) if it can be
  378. * determined, or else 0.
  379. */
  380. static ifindex_t
  381. getsockopt_ipv4_ifindex (struct msghdr *msgh)
  382. {
  383. /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */
  384. ifindex_t ifindex = -1;
  385. #if defined(IP_PKTINFO)
  386. /* Linux pktinfo based ifindex retrieval */
  387. struct in_pktinfo *pktinfo;
  388. pktinfo =
  389. (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO);
  390. /* XXX Can pktinfo be NULL? Clean up post 0.98. */
  391. ifindex = pktinfo->ipi_ifindex;
  392. #elif defined(IP_RECVIF)
  393. /* retrieval based on IP_RECVIF */
  394. #ifndef SUNOS_5
  395. /* BSD systems use a sockaddr_dl as the control message payload. */
  396. struct sockaddr_dl *sdl;
  397. #else
  398. /* SUNOS_5 uses an integer with the index. */
  399. ifindex_t *ifindex_p;
  400. #endif /* SUNOS_5 */
  401. #ifndef SUNOS_5
  402. /* BSD */
  403. sdl =
  404. (struct sockaddr_dl *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
  405. if (sdl != NULL)
  406. ifindex = sdl->sdl_index;
  407. else
  408. ifindex = 0;
  409. #else
  410. /*
  411. * Solaris. On Solaris 8, IP_RECVIF is defined, but the call to
  412. * enable it fails with errno=99, and the struct msghdr has
  413. * controllen 0.
  414. */
  415. ifindex_p = (uint_t *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_RECVIF);
  416. if (ifindex_p != NULL)
  417. ifindex = *ifindex_p;
  418. else
  419. ifindex = 0;
  420. #endif /* SUNOS_5 */
  421. #else
  422. /*
  423. * Neither IP_PKTINFO nor IP_RECVIF defined - warn at compile time.
  424. * XXX Decide if this is a core service, or if daemons have to cope.
  425. * Since Solaris 8 and OpenBSD seem not to provide it, it seems that
  426. * daemons have to cope.
  427. */
  428. #warning "getsockopt_ipv4_ifindex: Neither IP_PKTINFO nor IP_RECVIF defined."
  429. #warning "Some daemons may fail to operate correctly!"
  430. ifindex = 0;
  431. #endif /* IP_PKTINFO */
  432. return ifindex;
  433. }
  434. /* return ifindex, 0 if none found */
  435. ifindex_t
  436. getsockopt_ifindex (int af, struct msghdr *msgh)
  437. {
  438. switch (af)
  439. {
  440. case AF_INET:
  441. return (getsockopt_ipv4_ifindex (msgh));
  442. break;
  443. #ifdef HAVE_IPV6
  444. case AF_INET6:
  445. return (getsockopt_ipv6_ifindex (msgh));
  446. break;
  447. #endif
  448. default:
  449. zlog_warn ("getsockopt_ifindex: unknown address family %d", af);
  450. return 0;
  451. }
  452. }
  453. /* swab iph between order system uses for IP_HDRINCL and host order */
  454. void
  455. sockopt_iphdrincl_swab_htosys (struct ip *iph)
  456. {
  457. /* BSD and derived take iph in network order, except for
  458. * ip_len and ip_off
  459. */
  460. #ifndef HAVE_IP_HDRINCL_BSD_ORDER
  461. iph->ip_len = htons(iph->ip_len);
  462. iph->ip_off = htons(iph->ip_off);
  463. #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
  464. iph->ip_id = htons(iph->ip_id);
  465. }
  466. void
  467. sockopt_iphdrincl_swab_systoh (struct ip *iph)
  468. {
  469. #ifndef HAVE_IP_HDRINCL_BSD_ORDER
  470. iph->ip_len = ntohs(iph->ip_len);
  471. iph->ip_off = ntohs(iph->ip_off);
  472. #endif /* HAVE_IP_HDRINCL_BSD_ORDER */
  473. iph->ip_id = ntohs(iph->ip_id);
  474. }
  475. int
  476. sockopt_tcp_rtt (int sock)
  477. {
  478. #ifdef TCP_INFO
  479. struct tcp_info ti;
  480. socklen_t len = sizeof(ti);
  481. if (getsockopt (sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0)
  482. return 0;
  483. return ti.tcpi_rtt / 1000;
  484. #else
  485. return 0;
  486. #endif
  487. }
  488. int
  489. sockopt_tcp_signature (int sock, union sockunion *su, const char *password)
  490. {
  491. #if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX)
  492. /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's
  493. * version of the Quagga patch (based on work by Rick Payne, and Bruce
  494. * Simpson)
  495. */
  496. #define TCP_MD5_AUTH 13
  497. #define TCP_MD5_AUTH_ADD 1
  498. #define TCP_MD5_AUTH_DEL 2
  499. struct tcp_rfc2385_cmd {
  500. u_int8_t command; /* Command - Add/Delete */
  501. u_int32_t address; /* IPV4 address associated */
  502. u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */
  503. void *key; /* MD5 Key */
  504. } cmd;
  505. struct in_addr *addr = &su->sin.sin_addr;
  506. cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL);
  507. cmd.address = addr->s_addr;
  508. cmd.keylen = (password != NULL ? strlen (password) : 0);
  509. cmd.key = password;
  510. return setsockopt (sock, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd);
  511. #elif HAVE_DECL_TCP_MD5SIG
  512. int ret;
  513. #ifndef GNU_LINUX
  514. /*
  515. * XXX Need to do PF_KEY operation here to add/remove an SA entry,
  516. * and add/remove an SP entry for this peer's packet flows also.
  517. */
  518. int md5sig = password && *password ? 1 : 0;
  519. #else
  520. int keylen = password ? strlen (password) : 0;
  521. struct tcp_md5sig md5sig;
  522. union sockunion *su2, *susock;
  523. /* Figure out whether the socket and the sockunion are the same family..
  524. * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think..
  525. */
  526. if (!(susock = sockunion_getsockname (sock)))
  527. return -1;
  528. if (susock->sa.sa_family == su->sa.sa_family)
  529. su2 = su;
  530. else
  531. {
  532. /* oops.. */
  533. su2 = susock;
  534. if (su2->sa.sa_family == AF_INET)
  535. {
  536. sockunion_free (susock);
  537. return 0;
  538. }
  539. #ifdef HAVE_IPV6
  540. /* If this does not work, then all users of this sockopt will need to
  541. * differentiate between IPv4 and IPv6, and keep seperate sockets for
  542. * each.
  543. *
  544. * Sadly, it doesn't seem to work at present. It's unknown whether
  545. * this is a bug or not.
  546. */
  547. if (su2->sa.sa_family == AF_INET6
  548. && su->sa.sa_family == AF_INET)
  549. {
  550. su2->sin6.sin6_family = AF_INET6;
  551. /* V4Map the address */
  552. memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr));
  553. su2->sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
  554. memcpy (&su2->sin6.sin6_addr.s6_addr32[3], &su->sin.sin_addr, 4);
  555. }
  556. #endif
  557. }
  558. memset (&md5sig, 0, sizeof (md5sig));
  559. memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2));
  560. md5sig.tcpm_keylen = keylen;
  561. if (keylen)
  562. memcpy (md5sig.tcpm_key, password, keylen);
  563. sockunion_free (susock);
  564. #endif /* GNU_LINUX */
  565. if ((ret = setsockopt (sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof md5sig)) < 0)
  566. {
  567. /* ENOENT is harmless. It is returned when we clear a password for which
  568. one was not previously set. */
  569. if (ENOENT == errno)
  570. ret = 0;
  571. else
  572. zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s",
  573. sock, safe_strerror(errno));
  574. }
  575. return ret;
  576. #else /* HAVE_TCP_MD5SIG */
  577. return -2;
  578. #endif /* !HAVE_TCP_MD5SIG */
  579. }