1/*
2 *  This file is free software: you may copy, redistribute and/or modify it
3 *  under the terms of the GNU General Public License as published by the
4 *  Free Software Foundation, either version 2 of the License, or (at your
5 *  option) any later version.
6 *
7 *  This file is distributed in the hope that it will be useful, but
8 *  WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 *  General Public License for more details.
11 *
12 *  You should have received a copy of the GNU General Public License
13 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
14 *
15 * This file incorporates work covered by the following copyright and
16 * permission notice:
17 *
18Copyright (c) 2007, 2008 by Juliusz Chroboczek
19
20Permission is hereby granted, free of charge, to any person obtaining a copy
21of this software and associated documentation files (the "Software"), to deal
22in the Software without restriction, including without limitation the rights
23to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24copies of the Software, and to permit persons to whom the Software is
25furnished to do so, subject to the following conditions:
26
27The above copyright notice and this permission notice shall be included in
28all copies or substantial portions of the Software.
29
30THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
33AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36THE SOFTWARE.
37*/
38
39#include <unistd.h>
40#include <fcntl.h>
41#include <string.h>
42#include <sys/ioctl.h>
43#include <sys/types.h>
44#include <sys/uio.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include <arpa/inet.h>
48#include <errno.h>
49
50#include "babeld.h"
51#include "util.h"
52#include "net.h"
53
54int
55babel_socket(int port)
56{
57    struct sockaddr_in6 sin6;
58    int s, rc;
59    int saved_errno;
60    int one = 1, zero = 0;
61    const int ds = 0xc0;        /* CS6 - Network Control */
62
63    s = socket(PF_INET6, SOCK_DGRAM, 0);
64    if(s < 0)
65        return -1;
66
67    rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
68    if(rc < 0)
69        goto fail;
70
71    rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
72    if(rc < 0)
73        goto fail;
74
75    rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
76                    &zero, sizeof(zero));
77    if(rc < 0)
78        goto fail;
79
80    rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
81                    &one, sizeof(one));
82    if(rc < 0)
83        goto fail;
84
85    rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
86                    &one, sizeof(one));
87    if(rc < 0)
88        goto fail;
89
90#ifdef IPV6_TCLASS
91    rc = setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds));
92#else
93    rc = -1;
94    errno = ENOSYS;
95#endif
96    if(rc < 0)
97        perror("Couldn't set traffic class");
98
99    rc = fcntl(s, F_GETFL, 0);
100    if(rc < 0)
101        goto fail;
102
103    rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
104    if(rc < 0)
105        goto fail;
106
107    rc = fcntl(s, F_GETFD, 0);
108    if(rc < 0)
109        goto fail;
110
111    rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
112    if(rc < 0)
113        goto fail;
114
115    memset(&sin6, 0, sizeof(sin6));
116    sin6.sin6_family = AF_INET6;
117    sin6.sin6_port = htons(port);
118    rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
119    if(rc < 0)
120        goto fail;
121
122    return s;
123
124 fail:
125    saved_errno = errno;
126    close(s);
127    errno = saved_errno;
128    return -1;
129}
130
131int
132babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen)
133{
134    struct iovec iovec;
135    struct msghdr msg;
136    int rc;
137
138    memset(&msg, 0, sizeof(msg));
139    iovec.iov_base = buf;
140    iovec.iov_len = buflen;
141    msg.msg_name = sin;
142    msg.msg_namelen = slen;
143    msg.msg_iov = &iovec;
144    msg.msg_iovlen = 1;
145
146    rc = recvmsg(s, &msg, 0);
147    return rc;
148}
149
150int
151babel_send(int s,
152           void *buf1, int buflen1, void *buf2, int buflen2,
153           struct sockaddr *sin, int slen)
154{
155    struct iovec iovec[2];
156    struct msghdr msg;
157    int rc;
158
159    iovec[0].iov_base = buf1;
160    iovec[0].iov_len = buflen1;
161    iovec[1].iov_base = buf2;
162    iovec[1].iov_len = buflen2;
163    memset(&msg, 0, sizeof(msg));
164    msg.msg_name = (struct sockaddr*)sin;
165    msg.msg_namelen = slen;
166    msg.msg_iov = iovec;
167    msg.msg_iovlen = 2;
168
169 again:
170    rc = sendmsg(s, &msg, 0);
171    if(rc < 0) {
172        if(errno == EINTR)
173            goto again;
174        else if(errno == EAGAIN) {
175            int rc2;
176            rc2 = wait_for_fd(1, s, 5);
177            if(rc2 > 0)
178                goto again;
179            errno = EAGAIN;
180        }
181    }
182    return rc;
183}
184
185int
186tcp_server_socket(int port, int local)
187{
188    struct sockaddr_in6 sin6;
189    int s, rc, saved_errno;
190    int one = 1;
191
192    s = socket(PF_INET6, SOCK_STREAM, 0);
193    if(s < 0)
194        return -1;
195
196    rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
197    if(rc < 0)
198        goto fail;
199
200    rc = fcntl(s, F_GETFL, 0);
201    if(rc < 0)
202        goto fail;
203
204    rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK));
205    if(rc < 0)
206        goto fail;
207
208    rc = fcntl(s, F_GETFD, 0);
209    if(rc < 0)
210        goto fail;
211
212    rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC);
213    if(rc < 0)
214        goto fail;
215
216    memset(&sin6, 0, sizeof(sin6));
217    sin6.sin6_family = AF_INET6;
218    sin6.sin6_port = htons(port);
219    if(local) {
220        rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr);
221        if(rc < 0)
222            goto fail;
223    }
224    rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6));
225    if(rc < 0)
226        goto fail;
227
228    rc = listen(s, 2);
229    if(rc < 0)
230        goto fail;
231
232    return s;
233
234 fail:
235    saved_errno = errno;
236    close(s);
237    errno = saved_errno;
238    return -1;
239}
240