frr/babeld/net.c

206 lines
4.1 KiB
C

// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include "babeld.h"
#include "util.h"
#include "net.h"
#include "sockopt.h"
int
babel_socket(int port)
{
struct sockaddr_in6 sin6;
int s, rc;
int saved_errno;
int one = 1, zero = 0;
s = socket(PF_INET6, SOCK_DGRAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&zero, sizeof(zero));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&one, sizeof(one));
if(rc < 0)
goto fail;
setsockopt_ipv6_tclass (s, IPTOS_PREC_INTERNETCONTROL);
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}
int
babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
{
struct iovec iovec;
struct msghdr msg;
int rc;
memset(&msg, 0, sizeof(msg));
iovec.iov_base = buf;
iovec.iov_len = buflen;
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = &iovec;
msg.msg_iovlen = 1;
rc = recvmsg(s, &msg, 0);
return rc;
}
int
babel_send(int s,
void *buf1, int buflen1, void *buf2, int buflen2,
struct sockaddr *sin, int slen)
{
struct iovec iovec[2];
struct msghdr msg;
int rc;
iovec[0].iov_base = buf1;
iovec[0].iov_len = buflen1;
iovec[1].iov_base = buf2;
iovec[1].iov_len = buflen2;
memset(&msg, 0, sizeof(msg));
msg.msg_name = sin;
msg.msg_namelen = slen;
msg.msg_iov = iovec;
msg.msg_iovlen = 2;
again:
rc = sendmsg(s, &msg, 0);
if(rc < 0) {
if(errno == EINTR)
goto again;
else if(errno == EAGAIN) {
int rc2;
rc2 = wait_for_fd(1, s, 5);
if(rc2 > 0)
goto again;
errno = EAGAIN;
}
}
return rc;
}
int
tcp_server_socket(int port, int local)
{
struct sockaddr_in6 sin6;
int s, rc, saved_errno;
int one = 1;
s = socket(PF_INET6, SOCK_STREAM, 0);
if(s < 0)
return -1;
rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFL, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
if(rc < 0)
goto fail;
rc = fcntl(s, F_GETFD, 0);
if(rc < 0)
goto fail;
rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
if(rc < 0)
goto fail;
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
if(local) {
rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr);
if(rc < 0)
goto fail;
}
rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
if(rc < 0)
goto fail;
rc = listen(s, 2);
if(rc < 0)
goto fail;
return s;
fail:
saved_errno = errno;
close(s);
errno = saved_errno;
return -1;
}