1/* 2 PIM for Quagga 3 Copyright (C) 2008 Everton da Silva Marques 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; see the file COPYING; if not, write to the 17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 MA 02110-1301 USA 19 20 $QuaggaId: $Format:%an, %ai, %h$ $ 21*/ 22 23#include "pim_mroute.h" 24 25#include <sys/types.h> 26#include <sys/socket.h> 27#include <netinet/in.h> 28#include <netinet/igmp.h> 29#include <arpa/inet.h> 30#include <unistd.h> 31#include <netdb.h> 32#include <errno.h> 33 34#include <zebra.h> 35#include "log.h" 36#include "privs.h" 37 38#include "pimd.h" 39#include "pim_sock.h" 40#include "pim_str.h" 41#include "pim_igmp_join.h" 42 43/* GLOBAL VARS */ 44extern struct zebra_privs_t pimd_privs; 45 46int pim_socket_raw(int protocol) 47{ 48 int fd; 49 50 if ( pimd_privs.change (ZPRIVS_RAISE) ) 51 zlog_err ("pim_sockek_raw: could not raise privs, %s", 52 safe_strerror (errno) ); 53 54 fd = socket(AF_INET, SOCK_RAW, protocol); 55 56 if ( pimd_privs.change (ZPRIVS_LOWER) ) 57 zlog_err ("pim_socket_raw: could not lower privs, %s", 58 safe_strerror (errno) ); 59 60 if (fd < 0) { 61 zlog_warn("Could not create raw socket: errno=%d: %s", 62 errno, safe_strerror(errno)); 63 return PIM_SOCK_ERR_SOCKET; 64 } 65 66 return fd; 67} 68 69int pim_socket_mcast(int protocol, struct in_addr ifaddr, int loop) 70{ 71 int fd; 72 73 fd = pim_socket_raw(protocol); 74 if (fd < 0) { 75 zlog_warn("Could not create multicast socket: errno=%d: %s", 76 errno, safe_strerror(errno)); 77 return PIM_SOCK_ERR_SOCKET; 78 } 79 80 /* Needed to obtain destination address from recvmsg() */ 81 { 82#if defined(HAVE_IP_PKTINFO) 83 /* Linux IP_PKTINFO */ 84 int opt = 1; 85 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { 86 zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", 87 fd, errno, safe_strerror(errno)); 88 } 89#elif defined(HAVE_IP_RECVDSTADDR) 90 /* BSD IP_RECVDSTADDR */ 91 int opt = 1; 92 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { 93 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", 94 fd, errno, safe_strerror(errno)); 95 } 96#else 97 zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", 98 __FILE__, __PRETTY_FUNCTION__); 99 close(fd); 100 return PIM_SOCK_ERR_DSTADDR; 101#endif 102 } 103 104 105 /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/ 106 if (protocol == IPPROTO_IGMP) { 107 char ra[4]; 108 ra[0] = 148; 109 ra[1] = 4; 110 ra[2] = 0; 111 ra[3] = 0; 112 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { 113 zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s", 114 fd, errno, safe_strerror(errno)); 115 close(fd); 116 return PIM_SOCK_ERR_RA; 117 } 118 } 119 120 { 121 int reuse = 1; 122 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 123 (void *) &reuse, sizeof(reuse))) { 124 zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", 125 fd, errno, safe_strerror(errno)); 126 close(fd); 127 return PIM_SOCK_ERR_REUSE; 128 } 129 } 130 131 { 132 const int MTTL = 1; 133 int ttl = MTTL; 134 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 135 (void *) &ttl, sizeof(ttl))) { 136 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", 137 MTTL, fd, errno, safe_strerror(errno)); 138 close(fd); 139 return PIM_SOCK_ERR_TTL; 140 } 141 } 142 143 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 144 (void *) &loop, sizeof(loop))) { 145 zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", 146 loop ? "enable" : "disable", 147 fd, errno, safe_strerror(errno)); 148 close(fd); 149 return PIM_SOCK_ERR_LOOP; 150 } 151 152 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, 153 (void *) &ifaddr, sizeof(ifaddr))) { 154 zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", 155 fd, errno, safe_strerror(errno)); 156 close(fd); 157 return PIM_SOCK_ERR_IFACE; 158 } 159 160 { 161 long flags; 162 163 flags = fcntl(fd, F_GETFL, 0); 164 if (flags < 0) { 165 zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", 166 fd, errno, safe_strerror(errno)); 167 close(fd); 168 return PIM_SOCK_ERR_NONBLOCK_GETFL; 169 } 170 171 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { 172 zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", 173 fd, errno, safe_strerror(errno)); 174 close(fd); 175 return PIM_SOCK_ERR_NONBLOCK_SETFL; 176 } 177 } 178 179 return fd; 180} 181 182int pim_socket_join(int fd, struct in_addr group, 183 struct in_addr ifaddr, int ifindex) 184{ 185 int ret; 186 187#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX 188 struct ip_mreqn opt; 189#else 190 struct ip_mreq opt; 191#endif 192 193 opt.imr_multiaddr = group; 194 195#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX 196 opt.imr_address = ifaddr; 197 opt.imr_ifindex = ifindex; 198#else 199 opt.imr_interface = ifaddr; 200#endif 201 202 ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); 203 if (ret) { 204 char group_str[100]; 205 char ifaddr_str[100]; 206 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) 207 sprintf(group_str, "<group?>"); 208 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) 209 sprintf(ifaddr_str, "<ifaddr?>"); 210 211 zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", 212 fd, group_str, ifaddr_str, errno, safe_strerror(errno)); 213 return ret; 214 } 215 216 if (PIM_DEBUG_TRACE) { 217 char group_str[100]; 218 char ifaddr_str[100]; 219 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str))) 220 sprintf(group_str, "<group?>"); 221 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str))) 222 sprintf(ifaddr_str, "<ifaddr?>"); 223 224 zlog_debug("Socket fd=%d joined group %s on interface address %s", 225 fd, group_str, ifaddr_str); 226 } 227 228 return ret; 229} 230 231int pim_socket_join_source(int fd, int ifindex, 232 struct in_addr group_addr, 233 struct in_addr source_addr, 234 const char *ifname) 235{ 236 if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) { 237 int e = errno; 238 char group_str[100]; 239 char source_str[100]; 240 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); 241 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 242 zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", 243 __PRETTY_FUNCTION__, 244 fd, group_str, source_str, ifindex, ifname, 245 e, safe_strerror(e)); 246 return -1; 247 } 248 249 return 0; 250} 251 252int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, 253 struct sockaddr_in *from, socklen_t *fromlen, 254 struct sockaddr_in *to, socklen_t *tolen, 255 int *ifindex) 256{ 257 struct msghdr msgh; 258 struct cmsghdr *cmsg; 259 struct iovec iov; 260 char cbuf[1000]; 261 int err; 262 263 /* 264 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port. 265 * Use getsockname() to get sin_port. 266 */ 267 if (to) { 268 struct sockaddr_in si; 269 socklen_t si_len = sizeof(si); 270 271 ((struct sockaddr_in *) to)->sin_family = AF_INET; 272 273 if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) { 274 ((struct sockaddr_in *) to)->sin_port = ntohs(0); 275 ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0); 276 } 277 else { 278 ((struct sockaddr_in *) to)->sin_port = si.sin_port; 279 ((struct sockaddr_in *) to)->sin_addr = si.sin_addr; 280 } 281 282 if (tolen) 283 *tolen = sizeof(si); 284 } 285 286 memset(&msgh, 0, sizeof(struct msghdr)); 287 iov.iov_base = buf; 288 iov.iov_len = len; 289 msgh.msg_control = cbuf; 290 msgh.msg_controllen = sizeof(cbuf); 291 msgh.msg_name = from; 292 msgh.msg_namelen = fromlen ? *fromlen : 0; 293 msgh.msg_iov = &iov; 294 msgh.msg_iovlen = 1; 295 msgh.msg_flags = 0; 296 297 err = recvmsg(fd, &msgh, 0); 298 if (err < 0) 299 return err; 300 301 if (fromlen) 302 *fromlen = msgh.msg_namelen; 303 304 for (cmsg = CMSG_FIRSTHDR(&msgh); 305 cmsg != NULL; 306 cmsg = CMSG_NXTHDR(&msgh,cmsg)) { 307 308#ifdef HAVE_IP_PKTINFO 309 if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { 310 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg); 311 if (to) 312 ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr; 313 if (tolen) 314 *tolen = sizeof(struct sockaddr_in); 315 if (ifindex) 316 *ifindex = i->ipi_ifindex; 317 318 if (to && PIM_DEBUG_PACKETS) { 319 char to_str[100]; 320 pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str)); 321 zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d", 322 __PRETTY_FUNCTION__, 323 to_str, ntohs(to->sin_port)); 324 } 325 326 break; 327 } 328#endif 329 330#ifdef HAVE_IP_RECVDSTADDR 331 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) { 332 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg); 333 if (to) 334 ((struct sockaddr_in *) to)->sin_addr = *i; 335 if (tolen) 336 *tolen = sizeof(struct sockaddr_in); 337 338 if (to && PIM_DEBUG_PACKETS) { 339 char to_str[100]; 340 pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str)); 341 zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d", 342 __PRETTY_FUNCTION__, 343 to_str, ntohs(to->sin_port)); 344 } 345 346 break; 347 } 348#endif 349 350#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) 351 if (cmsg->cmsg_type == IP_RECVIF) 352 if (ifindex) 353 *ifindex = CMSG_IFINDEX(cmsg); 354#endif 355 356 } /* for (cmsg) */ 357 358 return err; /* len */ 359} 360 361int pim_socket_mcastloop_get(int fd) 362{ 363 int loop; 364 socklen_t loop_len = sizeof(loop); 365 366 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 367 &loop, &loop_len)) { 368 int e = errno; 369 zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s", 370 fd, errno, safe_strerror(errno)); 371 errno = e; 372 return PIM_SOCK_ERR_LOOP; 373 } 374 375 return loop; 376} 377 378int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen) 379{ 380 if (getsockname(fd, name, namelen)) { 381 int e = errno; 382 zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s", 383 fd, errno, safe_strerror(errno)); 384 errno = e; 385 return PIM_SOCK_ERR_NAME; 386 } 387 388 return PIM_SOCK_ERR_NONE; 389} 390