diff options
| author | Balaji Gurudoss <G_Balaji1@dell.com> | 2022-03-03 12:24:19 +0530 |
|---|---|---|
| committer | David Lamparter <equinox@opensourcerouting.org> | 2022-03-12 15:59:01 +0100 |
| commit | 023d3e4af8bb9190cb1bd9b151cb4ae158c683f8 (patch) | |
| tree | 46ee563809b195f2c0c02279b56ead0a810b40b1 /pimd/pim_sock.c | |
| parent | c532e50351691077263be0aca9a19755b8bed7b0 (diff) | |
pim6d: pim6d socket changes
socket changes to support IPv6 PIM
Signed-off-by: Balaji Gurudoss <G_Balaji1@dell.com>
[DL: cleaned up & refactored a whole bunch more.]
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'pimd/pim_sock.c')
| -rw-r--r-- | pimd/pim_sock.c | 418 |
1 files changed, 194 insertions, 224 deletions
diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 8d3fbabd27..92e2d18451 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -34,22 +34,27 @@ #include "vrf.h" #include "sockopt.h" #include "lib_errors.h" +#include "network.h" #include "pimd.h" #include "pim_mroute.h" #include "pim_sock.h" #include "pim_str.h" -/* GLOBAL VARS */ +#if PIM_IPV == 4 +#define setsockopt_iptos setsockopt_ipv4_tos +#define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop +#else +#define setsockopt_iptos setsockopt_ipv6_tclass +#define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop +#endif int pim_socket_raw(int protocol) { int fd; frr_with_privs(&pimd_privs) { - - fd = socket(AF_INET, SOCK_RAW, protocol); - + fd = socket(PIM_AF, SOCK_RAW, protocol); } if (fd < 0) { @@ -66,10 +71,16 @@ void pim_socket_ip_hdr(int fd) const int on = 1; frr_with_privs(&pimd_privs) { - +#if PIM_IPV == 4 if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) - zlog_err("%s: Could not turn on IP_HDRINCL option: %s", - __func__, safe_strerror(errno)); + zlog_err("%s: Could not turn on IP_HDRINCL option: %m", + __func__); +#else + if (setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on))) + zlog_err( + "%s: Could not turn on IPV6_HDRINCL option: %m", + __func__); +#endif } } @@ -80,248 +91,252 @@ void pim_socket_ip_hdr(int fd) int pim_socket_bind(int fd, struct interface *ifp) { int ret = 0; -#ifdef SO_BINDTODEVICE +#ifdef SO_BINDTODEVICE frr_with_privs(&pimd_privs) { - ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name, strlen(ifp->name)); - } - #endif return ret; } -int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, - uint8_t loop) +#if PIM_IPV == 4 +static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp) { - int rcvbuf = 1024 * 1024 * 8; -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - struct ip_mreqn mreq; -#else - struct ip_mreq mreq; -#endif - int fd; + int one = 1; + int ttl = 1; - fd = pim_socket_raw(protocol); - if (fd < 0) { - zlog_warn("Could not create multicast socket: errno=%d: %s", - errno, safe_strerror(errno)); - return PIM_SOCK_ERR_SOCKET; - } - -#ifdef SO_BINDTODEVICE - int ret; - - ret = pim_socket_bind(fd, ifp); - if (ret) { - close(fd); - zlog_warn( - "Could not set fd: %d for interface: %s to device", - fd, ifp->name); - return PIM_SOCK_ERR_BIND; - } -#else -/* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to - * only use 1 socket for all interfaces? */ -#endif - - /* Needed to obtain destination address from recvmsg() */ - { #if defined(HAVE_IP_PKTINFO) - /* Linux and Solaris IP_PKTINFO */ - int opt = 1; - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) { - zlog_warn( - "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - } + /* Linux and Solaris IP_PKTINFO */ + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one))) + zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd); #elif defined(HAVE_IP_RECVDSTADDR) - /* BSD IP_RECVDSTADDR */ - int opt = 1; - if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, - sizeof(opt))) { - zlog_warn( - "Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - } + /* BSD IP_RECVDSTADDR */ + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one))) + zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m", + fd); #else - flog_err( - EC_LIB_DEVELOPMENT, - "%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", - __FILE__, __func__); - close(fd); - return PIM_SOCK_ERR_DSTADDR; + flog_err( + EC_LIB_DEVELOPMENT, + "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()"); + close(fd); + return PIM_SOCK_ERR_DSTADDR; #endif - } - - /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. - * Message Formats)*/ + /* Set router alert (RFC 2113) for all IGMP messages (RFC + * 3376 4. Message Formats)*/ if (protocol == IPPROTO_IGMP) { uint8_t ra[4]; + ra[0] = 148; ra[1] = 4; ra[2] = 0; ra[3] = 0; if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { zlog_warn( - "Could not set Router Alert Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + "Could not set Router Alert Option on socket fd=%d: %m", + fd); close(fd); return PIM_SOCK_ERR_RA; } } - { - int reuse = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, - sizeof(reuse))) { - zlog_warn( - "Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_REUSE; - } - } - - { - const int MTTL = 1; - int ttl = MTTL; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, - sizeof(ttl))) { - zlog_warn( - "Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", - MTTL, fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_TTL; - } + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) { + zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m", + ttl, fd); + close(fd); + return PIM_SOCK_ERR_TTL; } - if (setsockopt_ipv4_multicast_loop(fd, loop)) { + if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) { zlog_warn( - "Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", - loop ? "enable" : "disable", fd, errno, - safe_strerror(errno)); + "Could not set Outgoing Interface Option on socket fd=%d: %m", + fd); close(fd); - return PIM_SOCK_ERR_LOOP; + return PIM_SOCK_ERR_IFACE; } - memset(&mreq, 0, sizeof(mreq)); -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - mreq.imr_ifindex = ifp->ifindex; -#else -/* - * I am not sure what to do here yet for *BSD - */ -// mreq.imr_interface = ifindex; -#endif + return 0; +} +#else /* PIM_IPV != 4 */ +static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp) +{ + int ttl = 1; + struct ipv6_mreq mreq = {}; + + setsockopt_ipv6_pktinfo(fd, 1); + setsockopt_ipv6_multicast_hops(fd, ttl); - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreq, + mreq.ipv6mr_interface = ifp->ifindex; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq, sizeof(mreq))) { zlog_warn( - "Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + "Could not set Outgoing Interface Option on socket fd=%d: %m", + fd); close(fd); return PIM_SOCK_ERR_IFACE; } - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) - zlog_warn("%s: Failure to set buffer size to %d", __func__, - rcvbuf); + return 0; +} +#endif - { - long flags; +int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp, + uint8_t loop) +{ + int fd; + int ret; - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) { - zlog_warn( - "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_NONBLOCK_GETFL; - } + fd = pim_socket_raw(protocol); + if (fd < 0) { + zlog_warn("Could not create multicast socket: errno=%d: %s", + errno, safe_strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - zlog_warn( - "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_NONBLOCK_SETFL; - } + /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF + * to emulate behaviour? Or change to only use 1 socket for all + * interfaces? */ + ret = pim_socket_bind(fd, ifp); + if (ret) { + close(fd); + zlog_warn("Could not set fd: %d for interface: %s to device", + fd, ifp->name); + return PIM_SOCK_ERR_BIND; } - /* Set Tx socket DSCP byte */ - if (setsockopt_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL)) { - zlog_warn("can't set sockopt IP_TOS to PIM/IGMP socket %d: %s", - fd, safe_strerror(errno)); + set_nonblocking(fd); + sockopt_reuseaddr(fd); + setsockopt_so_recvbuf(fd, 8 * 1024 * 1024); + + ret = pim_setsockopt(protocol, fd, ifp); + if (ret) { + zlog_warn("pim_setsockopt failed for interface: %s to device ", + ifp->name); + return ret; } + /* leftover common sockopts */ + if (setsockopt_multicast_loop(fd, loop)) { + zlog_warn( + "Could not %s Multicast Loopback Option on socket fd=%d: %m", + loop ? "enable" : "disable", fd); + close(fd); + return PIM_SOCK_ERR_LOOP; + } + + /* Set Tx socket DSCP byte */ + if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL)) + zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd); + return fd; } -int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, - ifindex_t ifindex) +int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex) { int ret; -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - struct ip_mreqn opt; +#if PIM_IPV == 4 + ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr, + group.s_addr, ifindex); #else - struct ip_mreq opt; -#endif - - opt.imr_multiaddr = group; + struct ipv6_mreq opt; -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - opt.imr_address = ifaddr; - opt.imr_ifindex = ifindex; -#else - opt.imr_interface = ifaddr; + memcpy(&opt.ipv6mr_multiaddr, &group, 16); + opt.ipv6mr_interface = ifindex; + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt)); #endif - ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); if (ret) { - char group_str[INET_ADDRSTRLEN]; - char ifaddr_str[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - snprintf(group_str, sizeof(group_str), "<group?>"); - if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, - sizeof(ifaddr_str))) - snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>"); - flog_err( EC_LIB_SOCKET, - "Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", - fd, group_str, ifaddr_str, errno, safe_strerror(errno)); + "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m", + fd, &group, &ifaddr); return ret; } - if (PIM_DEBUG_TRACE) { - char group_str[INET_ADDRSTRLEN]; - char ifaddr_str[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - snprintf(group_str, sizeof(group_str), "<group?>"); - if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, - sizeof(ifaddr_str))) - snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>"); - + if (PIM_DEBUG_TRACE) zlog_debug( - "Socket fd=%d joined group %s on interface address %s", - fd, group_str, ifaddr_str); + "Socket fd=%d joined group %pPAs on interface address %pPAs", + fd, &group, &ifaddr); + return ret; +} + +#if PIM_IPV == 4 +static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst, + ifindex_t *ifindex) +{ + struct cmsghdr *cmsg; + struct sockaddr_in *dst4 = (struct sockaddr_in *)dst; + + for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; + cmsg = CMSG_NXTHDR(mh, cmsg)) { +#ifdef HAVE_IP_PKTINFO + if ((cmsg->cmsg_level == IPPROTO_IP) && + (cmsg->cmsg_type == IP_PKTINFO)) { + struct in_pktinfo *i; + + i = (struct in_pktinfo *)CMSG_DATA(cmsg); + if (dst4) + dst4->sin_addr = i->ipi_addr; + if (ifindex) + *ifindex = i->ipi_ifindex; + + break; + } +#endif + +#ifdef HAVE_IP_RECVDSTADDR + if ((cmsg->cmsg_level == IPPROTO_IP) && + (cmsg->cmsg_type == IP_RECVDSTADDR)) { + struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); + + if (dst4) + dst4->sin_addr = *i; + + break; + } +#endif + +#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) + if (cmsg->cmsg_type == IP_RECVIF) + if (ifindex) + *ifindex = CMSG_IFINDEX(cmsg); +#endif } +} +#else /* PIM_IPV != 4 */ +static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst, + ifindex_t *ifindex) +{ + struct cmsghdr *cmsg; + struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; - return ret; + for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; + cmsg = CMSG_NXTHDR(mh, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IPV6) && + (cmsg->cmsg_type == IPV6_PKTINFO)) { + struct in6_pktinfo *i; + + i = (struct in6_pktinfo *)CMSG_DATA(cmsg); + + if (dst6) + dst6->sin6_addr = i->ipi6_addr; + if (ifindex) + *ifindex = i->ipi6_ifindex; + break; + } + } } +#endif /* PIM_IPV != 4 */ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, - struct sockaddr_in *from, socklen_t *fromlen, - struct sockaddr_in *to, socklen_t *tolen, + struct sockaddr_storage *from, socklen_t *fromlen, + struct sockaddr_storage *to, socklen_t *tolen, ifindex_t *ifindex) { struct msghdr msgh; - struct cmsghdr *cmsg; struct iovec iov; char cbuf[1000]; int err; @@ -331,19 +346,12 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, * Use getsockname() to get sin_port. */ if (to) { - struct sockaddr_in si; - socklen_t si_len = sizeof(si); - - memset(&si, 0, sizeof(si)); - to->sin_family = AF_INET; - - pim_socket_getsockname(fd, (struct sockaddr *)&si, &si_len); + socklen_t to_len = sizeof(*to); - to->sin_port = si.sin_port; - to->sin_addr = si.sin_addr; + pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len); if (tolen) - *tolen = sizeof(si); + *tolen = sizeof(*to); } memset(&msgh, 0, sizeof(struct msghdr)); @@ -364,45 +372,7 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, if (fromlen) *fromlen = msgh.msg_namelen; - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - -#ifdef HAVE_IP_PKTINFO - if ((cmsg->cmsg_level == IPPROTO_IP) - && (cmsg->cmsg_type == IP_PKTINFO)) { - struct in_pktinfo *i = - (struct in_pktinfo *)CMSG_DATA(cmsg); - if (to) - to->sin_addr = i->ipi_addr; - if (tolen) - *tolen = sizeof(struct sockaddr_in); - if (ifindex) - *ifindex = i->ipi_ifindex; - - break; - } -#endif - -#ifdef HAVE_IP_RECVDSTADDR - if ((cmsg->cmsg_level == IPPROTO_IP) - && (cmsg->cmsg_type == IP_RECVDSTADDR)) { - struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); - if (to) - to->sin_addr = *i; - if (tolen) - *tolen = sizeof(struct sockaddr_in); - - break; - } -#endif - -#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) - if (cmsg->cmsg_type == IP_RECVIF) - if (ifindex) - *ifindex = CMSG_IFINDEX(cmsg); -#endif - - } /* for (cmsg) */ + cmsg_getdstaddr(&msgh, to, ifindex); return err; /* len */ } |
