blob: 48bede1752061ceee783f6080125aecdf689b311 [file] [log] [blame]
Chia-chi Yehf8a6a762011-07-04 17:21:23 -07001/* $NetBSD: sockmisc.c,v 1.19 2011/03/14 17:18:13 tteras Exp $ */
Chung-yih Wang0a1907d2009-04-23 12:26:00 +08002
3/* Id: sockmisc.c,v 1.24 2006/05/07 21:32:59 manubsd Exp */
4
5/*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "config.h"
35
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/uio.h>
40
41#include <netinet/in.h>
42#include PATH_IPSEC_H
43
44#if defined(INET6) && !defined(INET6_ADVAPI) && \
45 defined(IP_RECVDSTADDR) && !defined(IPV6_RECVDSTADDR)
46#define IPV6_RECVDSTADDR IP_RECVDSTADDR
47#endif
48
49#include <stdlib.h>
50#include <stdio.h>
51#include <string.h>
52#include <errno.h>
53#ifdef HAVE_UNISTD_H
54#include <unistd.h>
55#endif
56
57#include "var.h"
58#include "misc.h"
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070059#include "vmbuf.h"
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080060#include "plog.h"
61#include "sockmisc.h"
62#include "debug.h"
63#include "gcmalloc.h"
64#include "debugrm.h"
65#include "libpfkey.h"
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070066#include "isakmp_var.h"
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080067
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070068#ifdef NOUSE_PRIVSEP
69#define BIND bind
70#define SOCKET socket
71#define SETSOCKOPT setsockopt
72#else
73#include "admin.h"
74#include "privsep.h"
75#define BIND privsep_bind
76#define SOCKET privsep_socket
77#define SETSOCKOPT privsep_setsockopt
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080078#endif
79
80const int niflags = 0;
81
82/*
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080083 * compare two sockaddr with port, taking care wildcard.
84 * addr1 is a subject address, addr2 is in a database entry.
85 * OUT: 0: equal.
86 * 1: not equal.
87 */
88int
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070089cmpsaddr(addr1, addr2)
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080090 const struct sockaddr *addr1;
91 const struct sockaddr *addr2;
92{
93 caddr_t sa1, sa2;
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070094 u_short port1 = IPSEC_PORT_ANY;
95 u_short port2 = IPSEC_PORT_ANY;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080096
Chia-chi Yehf8a6a762011-07-04 17:21:23 -070097 if (addr1 == NULL && addr2 == NULL)
98 return CMPSADDR_MATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +080099
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700100 if (addr1 == NULL || addr2 == NULL)
101 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800102
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700103 if (addr1->sa_family != addr2->sa_family ||
104 sysdep_sa_len(addr1) != sysdep_sa_len(addr2))
105 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800106
107 switch (addr1->sa_family) {
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700108 case AF_UNSPEC:
109 break;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800110 case AF_INET:
111 sa1 = (caddr_t)&((struct sockaddr_in *)addr1)->sin_addr;
112 sa2 = (caddr_t)&((struct sockaddr_in *)addr2)->sin_addr;
113 port1 = ((struct sockaddr_in *)addr1)->sin_port;
114 port2 = ((struct sockaddr_in *)addr2)->sin_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800115 if (memcmp(sa1, sa2, sizeof(struct in_addr)) != 0)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700116 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800117 break;
118#ifdef INET6
119 case AF_INET6:
120 sa1 = (caddr_t)&((struct sockaddr_in6 *)addr1)->sin6_addr;
121 sa2 = (caddr_t)&((struct sockaddr_in6 *)addr2)->sin6_addr;
122 port1 = ((struct sockaddr_in6 *)addr1)->sin6_port;
123 port2 = ((struct sockaddr_in6 *)addr2)->sin6_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800124 if (memcmp(sa1, sa2, sizeof(struct in6_addr)) != 0)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700125 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800126 if (((struct sockaddr_in6 *)addr1)->sin6_scope_id !=
127 ((struct sockaddr_in6 *)addr2)->sin6_scope_id)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700128 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800129 break;
130#endif
131 default:
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700132 return CMPSADDR_MISMATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800133 }
134
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700135 if (port1 == port2)
136 return CMPSADDR_MATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800137
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700138 if (port1 == IPSEC_PORT_ANY ||
139 port2 == IPSEC_PORT_ANY)
140 return CMPSADDR_WILDPORT_MATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800141
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700142 return CMPSADDR_WOP_MATCH;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800143}
144
Chia-chi Yehc4549542009-07-22 06:46:14 +0800145#ifdef ANDROID_PATCHED
146
147struct sockaddr *getlocaladdr(struct sockaddr *remote)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700148{
Chia-chi Yehc4549542009-07-22 06:46:14 +0800149 struct sockaddr_storage local;
150 socklen_t len = sysdep_sa_len(remote);
151 int s = socket(remote->sa_family, SOCK_DGRAM, 0);
152 if (s == -1 || connect(s, remote, len) == -1 ||
153 getsockname(s, (struct sockaddr *)&local, &len) == -1) {
154 close(s);
155 return NULL;
156 }
157 close(s);
Chia-chi Yehc4549542009-07-22 06:46:14 +0800158 return dupsaddr((struct sockaddr *)&local);
159}
160
161int recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from,
162 socklen_t *fromlen, struct sockaddr *to, unsigned int *tolen)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700163{
Chia-chi Yehc4549542009-07-22 06:46:14 +0800164 *tolen = sizeof(struct sockaddr_storage);
165 if (getsockname(s, to, (socklen_t *)tolen) == -1) {
166 return -1;
167 }
168 return recvfrom(s, buf, len, flags, from, fromlen);
169}
170
171int sendfromto(int s, const void *buf, size_t len, struct sockaddr *from,
172 struct sockaddr *to, int count)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700173{
Chia-chi Yehc4549542009-07-22 06:46:14 +0800174 socklen_t tolen = sysdep_sa_len(to);
175 int i;
176 for (i = 0; i < count; ++i) {
177 if (sendto(s, buf, len, 0, to, tolen) == -1) {
178 return -1;
179 }
180 }
181 return len;
182}
183
184int setsockopt_bypass(int s, int family)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700185{
Chia-chi Yehc4549542009-07-22 06:46:14 +0800186 struct sadb_x_policy p = {
187 .sadb_x_policy_len = PFKEY_UNIT64(sizeof(struct sadb_x_policy)),
188 .sadb_x_policy_exttype = SADB_X_EXT_POLICY,
189 .sadb_x_policy_type = IPSEC_POLICY_BYPASS,
190 .sadb_x_policy_dir = IPSEC_DIR_INBOUND,
191#ifdef HAVE_PFKEY_POLICY_PRIORITY
192 .sadb_x_policy_priority = PRIORITY_DEFAULT,
193#endif
194 };
195 int level = (family == AF_INET) ? IPPROTO_IP : IPPROTO_IPV6;
196 int option = (family == AF_INET) ? IP_IPSEC_POLICY : IPV6_IPSEC_POLICY;
197 int len = PFKEY_EXTLEN(&p);
198 if (setsockopt(s, level, option, &p, len) == -1 ||
199 (p.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND,
200 setsockopt(s, level, option, &p, len) == -1)) {
201 plog(LLV_ERROR, LOCATION, NULL, "setsockopt_bypass() %s\n", strerror(errno));
202 return -1;
203 }
204 return 0;
205}
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700206
Chia-chi Yehc4549542009-07-22 06:46:14 +0800207#else
208
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800209/* get local address against the destination. */
210struct sockaddr *
211getlocaladdr(remote)
212 struct sockaddr *remote;
213{
214 struct sockaddr *local;
215 u_int local_len = sizeof(struct sockaddr_storage);
216 int s; /* for dummy connection */
217
218 /* allocate buffer */
219 if ((local = racoon_calloc(1, local_len)) == NULL) {
220 plog(LLV_ERROR, LOCATION, NULL,
221 "failed to get address buffer.\n");
222 goto err;
223 }
224
225 /* get real interface received packet */
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700226 if ((s = SOCKET(remote->sa_family, SOCK_DGRAM, 0)) < 0) {
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800227 plog(LLV_ERROR, LOCATION, NULL,
228 "socket (%s)\n", strerror(errno));
229 goto err;
230 }
231
232 setsockopt_bypass(s, remote->sa_family);
233
234 if (connect(s, remote, sysdep_sa_len(remote)) < 0) {
235 plog(LLV_ERROR, LOCATION, NULL,
236 "connect (%s)\n", strerror(errno));
237 close(s);
238 goto err;
239 }
240
241 if (getsockname(s, local, &local_len) < 0) {
242 plog(LLV_ERROR, LOCATION, NULL,
243 "getsockname (%s)\n", strerror(errno));
244 close(s);
245 return NULL;
246 }
247
248 close(s);
249 return local;
250
251 err:
252 if (local != NULL)
253 racoon_free(local);
254 return NULL;
255}
256
257/*
258 * Receive packet, with src/dst information. It is assumed that necessary
259 * setsockopt() have already performed on socket.
260 */
261int
262recvfromto(s, buf, buflen, flags, from, fromlen, to, tolen)
263 int s;
264 void *buf;
265 size_t buflen;
266 int flags;
267 struct sockaddr *from;
268 socklen_t *fromlen;
269 struct sockaddr *to;
270 u_int *tolen;
271{
272 int otolen;
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700273 socklen_t slen;
274 int len;
275 union sockaddr_any sa;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800276 struct msghdr m;
277 struct cmsghdr *cm;
278 struct iovec iov[2];
279 u_char cmsgbuf[256];
280#if defined(INET6) && defined(INET6_ADVAPI)
281 struct in6_pktinfo *pi;
282#endif /*INET6_ADVAPI*/
283 struct sockaddr_in *sin;
284#ifdef INET6
285 struct sockaddr_in6 *sin6;
286#endif
287
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700288 slen = sizeof(sa);
289 if (getsockname(s, &sa.sa, &slen) < 0) {
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800290 plog(LLV_ERROR, LOCATION, NULL,
291 "getsockname (%s)\n", strerror(errno));
292 return -1;
293 }
294
295 m.msg_name = (caddr_t)from;
296 m.msg_namelen = *fromlen;
297 iov[0].iov_base = (caddr_t)buf;
298 iov[0].iov_len = buflen;
299 m.msg_iov = iov;
300 m.msg_iovlen = 1;
301 memset(cmsgbuf, 0, sizeof(cmsgbuf));
302 cm = (struct cmsghdr *)cmsgbuf;
303 m.msg_control = (caddr_t)cm;
304 m.msg_controllen = sizeof(cmsgbuf);
305 if ((len = recvmsg(s, &m, flags)) < 0) {
306 plog(LLV_ERROR, LOCATION, NULL,
307 "recvmsg (%s)\n", strerror(errno));
308 return -1;
309 }
310 *fromlen = m.msg_namelen;
311
312 otolen = *tolen;
313 *tolen = 0;
314 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
315 m.msg_controllen != 0 && cm;
316 cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
317#if 0
318 plog(LLV_ERROR, LOCATION, NULL,
319 "cmsg %d %d\n", cm->cmsg_level, cm->cmsg_type);)
320#endif
321#if defined(INET6) && defined(INET6_ADVAPI)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700322 if (sa.sa.sa_family == AF_INET6
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800323 && cm->cmsg_level == IPPROTO_IPV6
324 && cm->cmsg_type == IPV6_PKTINFO
325 && otolen >= sizeof(*sin6)) {
326 pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
327 *tolen = sizeof(*sin6);
328 sin6 = (struct sockaddr_in6 *)to;
329 memset(sin6, 0, sizeof(*sin6));
330 sin6->sin6_family = AF_INET6;
331#ifndef __linux__
332 sin6->sin6_len = sizeof(*sin6);
333#endif
334 memcpy(&sin6->sin6_addr, &pi->ipi6_addr,
335 sizeof(sin6->sin6_addr));
336 /* XXX other cases, such as site-local? */
337 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
338 sin6->sin6_scope_id = pi->ipi6_ifindex;
339 else
340 sin6->sin6_scope_id = 0;
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700341 sin6->sin6_port = sa.sin6.sin6_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800342 otolen = -1; /* "to" already set */
343 continue;
344 }
345#endif
346#ifdef __linux__
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700347 if (sa.sa.sa_family == AF_INET
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800348 && cm->cmsg_level == IPPROTO_IP
349 && cm->cmsg_type == IP_PKTINFO
350 && otolen >= sizeof(sin)) {
351 struct in_pktinfo *pi = (struct in_pktinfo *)(CMSG_DATA(cm));
352 *tolen = sizeof(*sin);
353 sin = (struct sockaddr_in *)to;
354 memset(sin, 0, sizeof(*sin));
355 sin->sin_family = AF_INET;
356 memcpy(&sin->sin_addr, &pi->ipi_addr,
357 sizeof(sin->sin_addr));
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700358 sin->sin_port = sa.sin.sin_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800359 otolen = -1; /* "to" already set */
360 continue;
361 }
362#endif
363#if defined(INET6) && defined(IPV6_RECVDSTADDR)
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700364 if (sa.sa.sa_family == AF_INET6
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800365 && cm->cmsg_level == IPPROTO_IPV6
366 && cm->cmsg_type == IPV6_RECVDSTADDR
367 && otolen >= sizeof(*sin6)) {
368 *tolen = sizeof(*sin6);
369 sin6 = (struct sockaddr_in6 *)to;
370 memset(sin6, 0, sizeof(*sin6));
371 sin6->sin6_family = AF_INET6;
372 sin6->sin6_len = sizeof(*sin6);
373 memcpy(&sin6->sin6_addr, CMSG_DATA(cm),
374 sizeof(sin6->sin6_addr));
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700375 sin6->sin6_port = sa.sin6.sin6_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800376 otolen = -1; /* "to" already set */
377 continue;
378 }
379#endif
380#ifndef __linux__
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700381 if (sa.sa.sa_family == AF_INET
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800382 && cm->cmsg_level == IPPROTO_IP
383 && cm->cmsg_type == IP_RECVDSTADDR
384 && otolen >= sizeof(*sin)) {
385 *tolen = sizeof(*sin);
386 sin = (struct sockaddr_in *)to;
387 memset(sin, 0, sizeof(*sin));
388 sin->sin_family = AF_INET;
389 sin->sin_len = sizeof(*sin);
390 memcpy(&sin->sin_addr, CMSG_DATA(cm),
391 sizeof(sin->sin_addr));
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700392 sin->sin_port = sa.sin.sin_port;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800393 otolen = -1; /* "to" already set */
394 continue;
395 }
396#endif
397 }
398
399 return len;
400}
401
402/* send packet, with fixing src/dst address pair. */
403int
404sendfromto(s, buf, buflen, src, dst, cnt)
405 int s, cnt;
406 const void *buf;
407 size_t buflen;
408 struct sockaddr *src;
409 struct sockaddr *dst;
410{
411 struct sockaddr_storage ss;
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700412 socklen_t slen;
413 int len = 0;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800414 int i;
415
416 if (src->sa_family != dst->sa_family) {
417 plog(LLV_ERROR, LOCATION, NULL,
418 "address family mismatch\n");
419 return -1;
420 }
421
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700422 slen = sizeof(ss);
423 if (getsockname(s, (struct sockaddr *)&ss, &slen) < 0) {
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800424 plog(LLV_ERROR, LOCATION, NULL,
425 "getsockname (%s)\n", strerror(errno));
426 return -1;
427 }
428
429 plog(LLV_DEBUG, LOCATION, NULL,
430 "sockname %s\n", saddr2str((struct sockaddr *)&ss));
431 plog(LLV_DEBUG, LOCATION, NULL,
432 "send packet from %s\n", saddr2str(src));
433 plog(LLV_DEBUG, LOCATION, NULL,
434 "send packet to %s\n", saddr2str(dst));
435
436 if (src->sa_family != ss.ss_family) {
437 plog(LLV_ERROR, LOCATION, NULL,
438 "address family mismatch\n");
439 return -1;
440 }
441
442 switch (src->sa_family) {
443#if defined(INET6) && defined(INET6_ADVAPI)
444// XXX: This block wasn't compiled on Linux - does it work?
445 case AF_INET6:
446 {
447 struct msghdr m;
448 struct cmsghdr *cm;
449 struct iovec iov[2];
450 u_char cmsgbuf[256];
451 struct in6_pktinfo *pi;
452 int ifindex;
453 struct sockaddr_in6 src6, dst6;
454
455 memcpy(&src6, src, sizeof(src6));
456 memcpy(&dst6, dst, sizeof(dst6));
457
458 /* XXX take care of other cases, such as site-local */
459 ifindex = 0;
460 if (IN6_IS_ADDR_LINKLOCAL(&src6.sin6_addr)
461 || IN6_IS_ADDR_MULTICAST(&src6.sin6_addr)) {
462 ifindex = src6.sin6_scope_id; /*???*/
463 }
464
465 /* XXX some sanity check on dst6.sin6_scope_id */
466
467 /* flowinfo for IKE? mmm, maybe useful but for now make it 0 */
468 src6.sin6_flowinfo = dst6.sin6_flowinfo = 0;
469
470 memset(&m, 0, sizeof(m));
471 m.msg_name = (caddr_t)&dst6;
472 m.msg_namelen = sizeof(dst6);
473 iov[0].iov_base = (char *)buf;
474 iov[0].iov_len = buflen;
475 m.msg_iov = iov;
476 m.msg_iovlen = 1;
477
478 memset(cmsgbuf, 0, sizeof(cmsgbuf));
479 cm = (struct cmsghdr *)cmsgbuf;
480 m.msg_control = (caddr_t)cm;
481 m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
482
483 cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
484 cm->cmsg_level = IPPROTO_IPV6;
485 cm->cmsg_type = IPV6_PKTINFO;
486 pi = (struct in6_pktinfo *)CMSG_DATA(cm);
487 memcpy(&pi->ipi6_addr, &src6.sin6_addr, sizeof(src6.sin6_addr));
488 pi->ipi6_ifindex = ifindex;
489
490 plog(LLV_DEBUG, LOCATION, NULL,
491 "src6 %s %d\n",
492 saddr2str((struct sockaddr *)&src6),
493 src6.sin6_scope_id);
494 plog(LLV_DEBUG, LOCATION, NULL,
495 "dst6 %s %d\n",
496 saddr2str((struct sockaddr *)&dst6),
497 dst6.sin6_scope_id);
498
499 for (i = 0; i < cnt; i++) {
500 len = sendmsg(s, &m, 0 /*MSG_DONTROUTE*/);
501 if (len < 0) {
502 plog(LLV_ERROR, LOCATION, NULL,
503 "sendmsg (%s)\n", strerror(errno));
504 return -1;
505 }
506 plog(LLV_DEBUG, LOCATION, NULL,
507 "%d times of %d bytes message will be sent "
508 "to %s\n",
509 i + 1, len, saddr2str(dst));
510 }
511 plogdump(LLV_DEBUG, (char *)buf, buflen);
512
513 return len;
514 }
515#endif
516#ifdef __linux__
517 case AF_INET:
518 {
519 struct msghdr m;
520 struct cmsghdr *cm;
521 struct iovec iov[2];
522 u_char cmsgbuf[256];
523 struct in_pktinfo *pi;
524 int ifindex = 0;
525 struct sockaddr_in src6, dst6;
526
527 memcpy(&src6, src, sizeof(src6));
528 memcpy(&dst6, dst, sizeof(dst6));
529
530 memset(&m, 0, sizeof(m));
531 m.msg_name = (caddr_t)&dst6;
532 m.msg_namelen = sizeof(dst6);
533 iov[0].iov_base = (char *)buf;
534 iov[0].iov_len = buflen;
535 m.msg_iov = iov;
536 m.msg_iovlen = 1;
537
538 memset(cmsgbuf, 0, sizeof(cmsgbuf));
539 cm = (struct cmsghdr *)cmsgbuf;
540 m.msg_control = (caddr_t)cm;
541 m.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
542
543 cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
544 cm->cmsg_level = IPPROTO_IP;
545 cm->cmsg_type = IP_PKTINFO;
546 pi = (struct in_pktinfo *)CMSG_DATA(cm);
547 memcpy(&pi->ipi_spec_dst, &src6.sin_addr, sizeof(src6.sin_addr));
548 pi->ipi_ifindex = ifindex;
549
550 plog(LLV_DEBUG, LOCATION, NULL,
551 "src4 %s\n",
552 saddr2str((struct sockaddr *)&src6));
553 plog(LLV_DEBUG, LOCATION, NULL,
554 "dst4 %s\n",
555 saddr2str((struct sockaddr *)&dst6));
556
557 for (i = 0; i < cnt; i++) {
558 len = sendmsg(s, &m, 0 /*MSG_DONTROUTE*/);
559 if (len < 0) {
560 plog(LLV_ERROR, LOCATION, NULL,
561 "sendmsg (%s)\n", strerror(errno));
562 return -1;
563 }
564 plog(LLV_DEBUG, LOCATION, NULL,
565 "%d times of %d bytes message will be sent "
566 "to %s\n",
567 i + 1, len, saddr2str(dst));
568 }
569 plogdump(LLV_DEBUG, (char *)buf, buflen);
570
571 return len;
572 }
573#endif /* __linux__ */
574 default:
575 {
576 int needclose = 0;
577 int sendsock;
578
579 if (ss.ss_family == src->sa_family && memcmp(&ss, src, sysdep_sa_len(src)) == 0) {
580 sendsock = s;
581 needclose = 0;
582 } else {
583 int yes = 1;
584 /*
585 * Use newly opened socket for sending packets.
586 * NOTE: this is unsafe, because if the peer is quick enough
587 * the packet from the peer may be queued into sendsock.
588 * Better approach is to prepare bind'ed udp sockets for
589 * each of the interface addresses.
590 */
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700591 sendsock = SOCKET(src->sa_family, SOCK_DGRAM, 0);
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800592 if (sendsock < 0) {
593 plog(LLV_ERROR, LOCATION, NULL,
594 "socket (%s)\n", strerror(errno));
595 return -1;
596 }
597 if (setsockopt(sendsock, SOL_SOCKET,
598#ifdef __linux__
599 SO_REUSEADDR,
600#else
601 SO_REUSEPORT,
602#endif
603 (void *)&yes, sizeof(yes)) < 0) {
604 plog(LLV_ERROR, LOCATION, NULL,
605 "setsockopt SO_REUSEPORT (%s)\n",
606 strerror(errno));
607 close(sendsock);
608 return -1;
609 }
610#ifdef IPV6_USE_MIN_MTU
611 if (src->sa_family == AF_INET6 &&
612 setsockopt(sendsock, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
613 (void *)&yes, sizeof(yes)) < 0) {
614 plog(LLV_ERROR, LOCATION, NULL,
615 "setsockopt IPV6_USE_MIN_MTU (%s)\n",
616 strerror(errno));
617 close(sendsock);
618 return -1;
619 }
620#endif
621 if (setsockopt_bypass(sendsock, src->sa_family) < 0) {
622 close(sendsock);
623 return -1;
624 }
625
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700626 if (BIND(sendsock, (struct sockaddr *)src,
627 sysdep_sa_len(src)) < 0) {
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800628 plog(LLV_ERROR, LOCATION, NULL,
629 "bind 1 (%s)\n", strerror(errno));
630 close(sendsock);
631 return -1;
632 }
633 needclose = 1;
634 }
635
636 for (i = 0; i < cnt; i++) {
637 len = sendto(sendsock, buf, buflen, 0, dst, sysdep_sa_len(dst));
638 if (len < 0) {
639 plog(LLV_ERROR, LOCATION, NULL,
640 "sendto (%s)\n", strerror(errno));
641 if (needclose)
642 close(sendsock);
643 return len;
644 }
645 plog(LLV_DEBUG, LOCATION, NULL,
646 "%d times of %d bytes message will be sent "
647 "to %s\n",
648 i + 1, len, saddr2str(dst));
649 }
650 plogdump(LLV_DEBUG, (char *)buf, buflen);
651
652 if (needclose)
653 close(sendsock);
654
655 return len;
656 }
657 }
658}
659
660int
661setsockopt_bypass(so, family)
662 int so, family;
663{
664 int level;
665 char *buf;
666 char *policy;
667
668 switch (family) {
669 case AF_INET:
670 level = IPPROTO_IP;
671 break;
672#ifdef INET6
673 case AF_INET6:
674 level = IPPROTO_IPV6;
675 break;
676#endif
677 default:
678 plog(LLV_ERROR, LOCATION, NULL,
679 "unsupported address family %d\n", family);
680 return -1;
681 }
682
683 policy = "in bypass";
684 buf = ipsec_set_policy(policy, strlen(policy));
685 if (buf == NULL) {
686 plog(LLV_ERROR, LOCATION, NULL,
687 "ipsec_set_policy (%s)\n",
688 ipsec_strerror());
689 return -1;
690 }
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700691 if (SETSOCKOPT(so, level,
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800692 (level == IPPROTO_IP ?
693 IP_IPSEC_POLICY : IPV6_IPSEC_POLICY),
694 buf, ipsec_get_policylen(buf)) < 0) {
695 plog(LLV_ERROR, LOCATION, NULL,
696 "setsockopt IP_IPSEC_POLICY (%s)\n",
697 strerror(errno));
698 return -1;
699 }
700 racoon_free(buf);
701
702 policy = "out bypass";
703 buf = ipsec_set_policy(policy, strlen(policy));
704 if (buf == NULL) {
705 plog(LLV_ERROR, LOCATION, NULL,
706 "ipsec_set_policy (%s)\n",
707 ipsec_strerror());
708 return -1;
709 }
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700710 if (SETSOCKOPT(so, level,
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800711 (level == IPPROTO_IP ?
712 IP_IPSEC_POLICY : IPV6_IPSEC_POLICY),
713 buf, ipsec_get_policylen(buf)) < 0) {
714 plog(LLV_ERROR, LOCATION, NULL,
715 "setsockopt IP_IPSEC_POLICY (%s)\n",
716 strerror(errno));
717 return -1;
718 }
719 racoon_free(buf);
720
721 return 0;
722}
723
Chia-chi Yehf8a6a762011-07-04 17:21:23 -0700724#endif
725
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800726struct sockaddr *
727newsaddr(len)
728 int len;
729{
730 struct sockaddr *new;
731
732 if ((new = racoon_calloc(1, len)) == NULL) {
733 plog(LLV_ERROR, LOCATION, NULL,
734 "%s\n", strerror(errno));
735 goto out;
736 }
737
738#ifdef __linux__
739 if (len == sizeof (struct sockaddr_in6))
740 new->sa_family = AF_INET6;
741 else
742 new->sa_family = AF_INET;
743#else
744 /* initial */
745 new->sa_len = len;
746#endif
747out:
748 return new;
749}
750
Chung-yih Wang0a1907d2009-04-23 12:26:00 +0800751struct sockaddr *
752dupsaddr(src)
753 struct sockaddr *src;
754{
755 struct sockaddr *dst;
756
757 dst = racoon_calloc(1, sysdep_sa_len(src));
758 if (dst == NULL) {
759 plog(LLV_ERROR, LOCATION, NULL,
760 "%s\n", strerror(errno));
761 return NULL;
762 }
763
764 memcpy(dst, src, sysdep_sa_len(src));
765
766 return dst;
767}
768
769char *
770saddr2str(saddr)
771 const struct sockaddr *saddr;
772{
773 static char buf[NI_MAXHOST + NI_MAXSERV + 10];
774 char addr[NI_MAXHOST], port[NI_MAXSERV];
775
776 if (saddr == NULL)
777 return NULL;
778
779 if (saddr->sa_family == AF_UNSPEC)
780 snprintf (buf, sizeof(buf), "%s", "anonymous");
781 else {
782 GETNAMEINFO(saddr, addr, port);
783 snprintf(buf, sizeof(buf), "%s[%s]", addr, port);
784 }
785
786 return buf;
787}
788
789char *
790saddrwop2str(saddr)
791 const struct sockaddr *saddr;
792{
793 static char buf[NI_MAXHOST + NI_MAXSERV + 10];
794 char addr[NI_MAXHOST];
795
796 if (saddr == NULL)
797 return NULL;
798
799 GETNAMEINFO_NULL(saddr, addr);
800 snprintf(buf, sizeof(buf), "%s", addr);
801
802 return buf;
803}
804
805char *
806naddrwop2str(const struct netaddr *naddr)
807{
808 static char buf[NI_MAXHOST + 10];
809 static const struct sockaddr sa_any; /* this is initialized to all zeros */
810
811 if (naddr == NULL)
812 return NULL;
813
814 if (memcmp(&naddr->sa, &sa_any, sizeof(sa_any)) == 0)
815 snprintf(buf, sizeof(buf), "%s", "any");
816 else {
817 snprintf(buf, sizeof(buf), "%s", saddrwop2str(&naddr->sa.sa));
818 snprintf(&buf[strlen(buf)], sizeof(buf) - strlen(buf), "/%ld", naddr->prefix);
819 }
820 return buf;
821}
822
823char *
824naddrwop2str_fromto(const char *format, const struct netaddr *saddr,
825 const struct netaddr *daddr)
826{
827 static char buf[2*(NI_MAXHOST + NI_MAXSERV + 10) + 100];
828 char *src, *dst;
829
830 src = racoon_strdup(naddrwop2str(saddr));
831 dst = racoon_strdup(naddrwop2str(daddr));
832 STRDUP_FATAL(src);
833 STRDUP_FATAL(dst);
834 /* WARNING: Be careful about the format string! Don't
835 ever pass in something that a user can modify!!! */
836 snprintf (buf, sizeof(buf), format, src, dst);
837 racoon_free (src);
838 racoon_free (dst);
839
840 return buf;
841}
842
843char *
844saddr2str_fromto(format, saddr, daddr)
845 const char *format;
846 const struct sockaddr *saddr;
847 const struct sockaddr *daddr;
848{
849 static char buf[2*(NI_MAXHOST + NI_MAXSERV + 10) + 100];
850 char *src, *dst;
851
852 src = racoon_strdup(saddr2str(saddr));
853 dst = racoon_strdup(saddr2str(daddr));
854 STRDUP_FATAL(src);
855 STRDUP_FATAL(dst);
856 /* WARNING: Be careful about the format string! Don't
857 ever pass in something that a user can modify!!! */
858 snprintf (buf, sizeof(buf), format, src, dst);
859 racoon_free (src);
860 racoon_free (dst);
861
862 return buf;
863}
864
865struct sockaddr *
866str2saddr(host, port)
867 char *host;
868 char *port;
869{
870 struct addrinfo hints, *res;
871 struct sockaddr *saddr;
872 int error;
873
874 memset(&hints, 0, sizeof(hints));
875 hints.ai_family = PF_UNSPEC;
876 hints.ai_socktype = SOCK_DGRAM;
877 hints.ai_flags = AI_NUMERICHOST;
878 error = getaddrinfo(host, port, &hints, &res);
879 if (error != 0) {
880 plog(LLV_ERROR, LOCATION, NULL,
881 "getaddrinfo(%s%s%s): %s\n",
882 host, port ? "," : "", port ? port : "",
883 gai_strerror(error));
884 return NULL;
885 }
886 if (res->ai_next != NULL) {
887 plog(LLV_WARNING, LOCATION, NULL,
888 "getaddrinfo(%s%s%s): "
889 "resolved to multiple address, "
890 "taking the first one\n",
891 host, port ? "," : "", port ? port : "");
892 }
893 saddr = racoon_malloc(res->ai_addrlen);
894 if (saddr == NULL) {
895 plog(LLV_ERROR, LOCATION, NULL,
896 "failed to allocate buffer.\n");
897 freeaddrinfo(res);
898 return NULL;
899 }
900 memcpy(saddr, res->ai_addr, res->ai_addrlen);
901 freeaddrinfo(res);
902
903 return saddr;
904}
905
906void
907mask_sockaddr(a, b, l)
908 struct sockaddr *a;
909 const struct sockaddr *b;
910 size_t l;
911{
912 size_t i;
913 u_int8_t *p, alen;
914
915 switch (b->sa_family) {
916 case AF_INET:
917 alen = sizeof(struct in_addr);
918 p = (u_int8_t *)&((struct sockaddr_in *)a)->sin_addr;
919 break;
920#ifdef INET6
921 case AF_INET6:
922 alen = sizeof(struct in6_addr);
923 p = (u_int8_t *)&((struct sockaddr_in6 *)a)->sin6_addr;
924 break;
925#endif
926 default:
927 plog(LLV_ERROR, LOCATION, NULL,
928 "invalid family: %d\n", b->sa_family);
929 exit(1);
930 }
931
932 if ((alen << 3) < l) {
933 plog(LLV_ERROR, LOCATION, NULL,
934 "unexpected inconsistency: %d %zu\n", b->sa_family, l);
935 exit(1);
936 }
937
938 memcpy(a, b, sysdep_sa_len(b));
939 p[l / 8] &= (0xff00 >> (l % 8)) & 0xff;
940 for (i = l / 8 + 1; i < alen; i++)
941 p[i] = 0x00;
942}
943
944/* Compute a score describing how "accurate" a netaddr is for a given sockaddr.
945 * Examples:
946 * Return values for address 10.20.30.40 [port 500] and given netaddresses...
947 * 10.10.0.0/16 => -1 ... doesn't match
948 * 0.0.0.0/0 => 0 ... matches, but only 0 bits.
949 * 10.20.0.0/16 => 16 ... 16 bits match
950 * 10.20.30.0/24 => 24 ... guess what ;-)
951 * 10.20.30.40/32 => 32 ... whole address match
952 * 10.20.30.40:500 => 33 ... both address and port match
953 * 10.20.30.40:501 => -1 ... port doesn't match and isn't 0 (=any)
954 */
955int
956naddr_score(const struct netaddr *naddr, const struct sockaddr *saddr)
957{
958 static const struct netaddr naddr_any; /* initialized to all-zeros */
959 struct sockaddr sa;
960 u_int16_t naddr_port, saddr_port;
961 int port_score;
962
963 if (!naddr || !saddr) {
964 plog(LLV_ERROR, LOCATION, NULL,
965 "Call with null args: naddr=%p, saddr=%p\n",
966 naddr, saddr);
967 return -1;
968 }
969
970 /* Wildcard address matches, but only 0 bits. */
971 if (memcmp(naddr, &naddr_any, sizeof(naddr_any)) == 0)
972 return 0;
973
974 /* If families don't match we really can't do much... */
975 if (naddr->sa.sa.sa_family != saddr->sa_family)
976 return -1;
977
978 /* If port check fail don't bother to check addresses. */
979 naddr_port = extract_port(&naddr->sa.sa);
980 saddr_port = extract_port(saddr);
981 if (naddr_port == 0 || saddr_port == 0) /* wildcard match */
982 port_score = 0;
983 else if (naddr_port == saddr_port) /* exact match */
984 port_score = 1;
985 else /* mismatch :-) */
986 return -1;
987
988 /* Here it comes - compare network addresses. */
989 mask_sockaddr(&sa, saddr, naddr->prefix);
990 if (loglevel >= LLV_DEBUG) { /* debug only */
991 char *a1, *a2, *a3;
992 a1 = racoon_strdup(naddrwop2str(naddr));
993 a2 = racoon_strdup(saddrwop2str(saddr));
994 a3 = racoon_strdup(saddrwop2str(&sa));
995 STRDUP_FATAL(a1);
996 STRDUP_FATAL(a2);
997 STRDUP_FATAL(a3);
998 plog(LLV_DEBUG, LOCATION, NULL,
999 "naddr=%s, saddr=%s (masked=%s)\n",
1000 a1, a2, a3);
1001 free(a1);
1002 free(a2);
1003 free(a3);
1004 }
Chia-chi Yehf8a6a762011-07-04 17:21:23 -07001005 if (cmpsaddr(&sa, &naddr->sa.sa) <= CMPSADDR_WOP_MATCH)
Chung-yih Wang0a1907d2009-04-23 12:26:00 +08001006 return naddr->prefix + port_score;
1007
1008 return -1;
1009}
1010
Chia-chi Yehf8a6a762011-07-04 17:21:23 -07001011/* Some useful functions for sockaddr port manipulations. */
Chung-yih Wang0a1907d2009-04-23 12:26:00 +08001012u_int16_t
1013extract_port (const struct sockaddr *addr)
1014{
1015 u_int16_t port = 0;
1016
1017 if (!addr)
1018 return port;
1019
1020 switch (addr->sa_family) {
Chia-chi Yehf8a6a762011-07-04 17:21:23 -07001021 case AF_UNSPEC:
1022 break;
Chung-yih Wang0a1907d2009-04-23 12:26:00 +08001023 case AF_INET:
1024 port = ((struct sockaddr_in *)addr)->sin_port;
1025 break;
1026 case AF_INET6:
1027 port = ((struct sockaddr_in6 *)addr)->sin6_port;
1028 break;
1029 default:
1030 plog(LLV_ERROR, LOCATION, NULL, "unknown AF: %u\n", addr->sa_family);
1031 break;
1032 }
1033
1034 return ntohs(port);
1035}
1036
1037u_int16_t *
1038get_port_ptr (struct sockaddr *addr)
1039{
1040 u_int16_t *port_ptr;
1041
1042 if (!addr)
1043 return NULL;
1044
1045 switch (addr->sa_family) {
1046 case AF_INET:
1047 port_ptr = &(((struct sockaddr_in *)addr)->sin_port);
1048 break;
1049 case AF_INET6:
1050 port_ptr = &(((struct sockaddr_in6 *)addr)->sin6_port);
1051 break;
1052 default:
1053 plog(LLV_ERROR, LOCATION, NULL, "unknown AF: %u\n", addr->sa_family);
1054 return NULL;
1055 break;
1056 }
1057
1058 return port_ptr;
1059}
1060
1061u_int16_t *
1062set_port (struct sockaddr *addr, u_int16_t new_port)
1063{
1064 u_int16_t *port_ptr;
1065
1066 port_ptr = get_port_ptr (addr);
1067
1068 if (port_ptr)
1069 *port_ptr = htons(new_port);
1070
1071 return port_ptr;
1072}