int rb_maxconnections = 0;
static PF rb_connect_timeout;
-static PF rb_connect_tryconnect;
-#ifdef RB_IPV6
+static PF rb_connect_outcome;
static void mangle_mapped_sockaddr(struct sockaddr *in);
-#endif
#ifndef HAVE_SOCKETPAIR
static int rb_inet_socketpair(int d, int type, int protocol, rb_platform_fd_t sv[2]);
RB_DLINK_FOREACH_SAFE(ptr, next, closed_list.head)
{
F = ptr->data;
+
+ number_fd--;
+
+#ifdef _WIN32
+ if(F->type & (RB_FD_SOCKET | RB_FD_PIPE))
+ closesocket(F->fd);
+ else
+#endif
+ close(F->fd);
+
rb_dlinkDelete(ptr, &closed_list);
rb_bh_free(fd_heap, F);
}
* this will happen.
*/
void
-rb_checktimeouts(void *notused)
+rb_checktimeouts(void *notused __attribute__((unused)))
{
rb_dlink_node *ptr, *next;
struct timeout_data *td;
}
}
-static void
-rb_accept_tryaccept(rb_fde_t *F, void *data)
+static int
+rb_setsockopt_reuseaddr(rb_fde_t *F)
+{
+ int opt_one = 1;
+ int ret;
+
+ ret = setsockopt(F->fd, SOL_SOCKET, SO_REUSEADDR, &opt_one, sizeof(opt_one));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_reuseaddr: Cannot set SO_REUSEADDR for FD %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_LIBSCTP
+static int
+rb_setsockopt_sctp(rb_fde_t *F)
+{
+ int opt_zero = 0;
+ int opt_one = 1;
+ /* workaround for https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/sctp?id=299ee123e19889d511092347f5fc14db0f10e3a6 */
+ char *env_mapped = getenv("SCTP_I_WANT_MAPPED_V4_ADDR");
+ int opt_mapped = env_mapped != NULL ? atoi(env_mapped) : opt_zero;
+ int ret;
+ struct sctp_initmsg initmsg;
+ struct sctp_rtoinfo rtoinfo;
+ struct sctp_paddrparams paddrparams;
+ struct sctp_assocparams assocparams;
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_NODELAY, &opt_one, sizeof(opt_one));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_NODELAY for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_I_WANT_MAPPED_V4_ADDR, &opt_mapped, sizeof(opt_mapped));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot unset SCTP_I_WANT_MAPPED_V4_ADDR for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ /* Configure INIT message to specify that we only want one stream */
+ memset(&initmsg, 0, sizeof(initmsg));
+ initmsg.sinit_num_ostreams = 1;
+ initmsg.sinit_max_instreams = 1;
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_INITMSG for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ /* Configure RTO values to reduce the maximum timeout */
+ memset(&rtoinfo, 0, sizeof(rtoinfo));
+ rtoinfo.srto_initial = 3000;
+ rtoinfo.srto_min = 1000;
+ rtoinfo.srto_max = 10000;
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_RTOINFO, &rtoinfo, sizeof(rtoinfo));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_RTOINFO for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ /*
+ * Configure peer address parameters to ensure that we monitor the connection
+ * more often than the default and don't timeout retransmit attempts before
+ * the ping timeout does.
+ *
+ * Each peer address will timeout reachability in about 750s.
+ */
+ memset(&paddrparams, 0, sizeof(paddrparams));
+ paddrparams.spp_assoc_id = 0;
+ memcpy(&paddrparams.spp_address, &in6addr_any, sizeof(in6addr_any));
+ paddrparams.spp_pathmaxrxt = 50;
+ paddrparams.spp_hbinterval = 5000;
+ paddrparams.spp_flags |= SPP_HB_ENABLE;
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_PEER_ADDR_PARAMS, &paddrparams, sizeof(paddrparams));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_PEER_ADDR_PARAMS for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ /* Configure association parameters for retransmit attempts as above */
+ memset(&assocparams, 0, sizeof(assocparams));
+ assocparams.sasoc_assoc_id = 0;
+ assocparams.sasoc_asocmaxrxt = 50;
+
+ ret = setsockopt(F->fd, SOL_SCTP, SCTP_ASSOCINFO, &assocparams, sizeof(assocparams));
+ if (ret) {
+ rb_lib_log("rb_setsockopt_sctp: Cannot set SCTP_ASSOCINFO for fd %d: %s",
+ F->fd, strerror(rb_get_sockerr(F)));
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+int
+rb_bind(rb_fde_t *F, struct sockaddr *addr)
+{
+ int ret;
+
+ ret = rb_setsockopt_reuseaddr(F);
+ if (ret)
+ return ret;
+
+ ret = bind(F->fd, addr, GET_SS_LEN(addr));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#ifdef HAVE_LIBSCTP
+static int
+rb_sctp_bindx_only(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
+{
+ int ret;
+
+ for (size_t i = 0; i < len; i++) {
+ if (GET_SS_FAMILY(&addrs[i]) == AF_UNSPEC)
+ continue;
+
+ ret = sctp_bindx(F->fd, (struct sockaddr *)&addrs[i], 1, SCTP_BINDX_ADD_ADDR);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+int
+rb_sctp_bindx(rb_fde_t *F, struct sockaddr_storage *addrs, size_t len)
{
+#ifdef HAVE_LIBSCTP
+ int ret;
+
+ if ((F->type & RB_FD_SCTP) == 0)
+ return -1;
+
+ ret = rb_setsockopt_reuseaddr(F);
+ if (ret)
+ return ret;
+
+ ret = rb_sctp_bindx_only(F, addrs, len);
+ if (ret)
+ return ret;
+
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int
+rb_inet_get_proto(rb_fde_t *F)
+{
+#ifdef HAVE_LIBSCTP
+ if (F->type & RB_FD_SCTP)
+ return IPPROTO_SCTP;
+#endif
+ return IPPROTO_TCP;
+}
+
+static void rb_accept_tryaccept(rb_fde_t *F, void *data __attribute__((unused))) {
struct rb_sockaddr_storage st;
rb_fde_t *new_F;
- rb_socklen_t addrlen = sizeof(st);
+ rb_socklen_t addrlen;
int new_fd;
while(1)
{
+ memset(&st, 0, sizeof(st));
+ addrlen = sizeof(st);
+
new_fd = accept(F->fd, (struct sockaddr *)&st, &addrlen);
rb_get_errno();
if(new_fd < 0)
rb_fd_hack(&new_fd);
- new_F = rb_open(new_fd, RB_FD_SOCKET, "Incoming Connection");
+ new_F = rb_open(new_fd, RB_FD_SOCKET | (F->type & RB_FD_INHERIT_TYPES), "Incoming Connection");
if(new_F == NULL)
{
rb_lib_log
- ("rb_accept: new_F == NULL on incoming connection. Closing new_fd == %d\n",
+ ("rb_accept: new_F == NULL on incoming connection. Closing new_fd == %d",
new_fd);
close(new_fd);
continue;
rb_close(new_F);
}
-#ifdef RB_IPV6
mangle_mapped_sockaddr((struct sockaddr *)&st);
-#endif
if(F->accept->precb != NULL)
{
/*
* void rb_connect_tcp(rb_platform_fd_t fd, struct sockaddr *dest,
- * struct sockaddr *clocal, int socklen,
+ * struct sockaddr *clocal,
* CNCB *callback, void *data, int timeout)
* Input: An fd to connect with, a host and port to connect to,
- * a local sockaddr to connect from + length(or NULL to use the
+ * a local sockaddr to connect from (or NULL to use the
* default), a callback, the data to pass into the callback, the
* address family.
* Output: None.
*/
void
rb_connect_tcp(rb_fde_t *F, struct sockaddr *dest,
- struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
+ struct sockaddr *clocal, CNCB * callback, void *data, int timeout)
{
- if(F == NULL)
+ int retval;
+
+ if (F == NULL)
return;
lrb_assert(callback);
* virtual host IP, for completeness.
* -- adrian
*/
- if((clocal != NULL) && (bind(F->fd, clocal, socklen) < 0))
+ if((clocal != NULL) && (bind(F->fd, clocal, GET_SS_LEN(clocal)) < 0))
{
/* Failure, call the callback with RB_ERR_BIND */
rb_connect_callback(F, RB_ERR_BIND);
/* We have a valid IP, so we just call tryconnect */
/* Make sure we actually set the timeout here .. */
rb_settimeout(F, timeout, rb_connect_timeout, NULL);
- rb_connect_tryconnect(F, NULL);
+
+ retval = connect(F->fd,
+ (struct sockaddr *)&F->connect->hostaddr,
+ GET_SS_LEN(&F->connect->hostaddr));
+ /* Error? */
+ if (retval < 0) {
+ /*
+ * If we get EISCONN, then we've already connect()ed the socket,
+ * which is a good thing.
+ * -- adrian
+ */
+ rb_get_errno();
+ if (errno == EISCONN) {
+ rb_connect_callback(F, RB_OK);
+ } else if (rb_ignore_errno(errno)) {
+ /* Ignore error? Reschedule */
+ rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
+ } else {
+ /* Error? Fail with RB_ERR_CONNECT */
+ rb_connect_callback(F, RB_ERR_CONNECT);
+ }
+ return;
+ }
+ /* If we get here, we've succeeded, so call with RB_OK */
+ rb_connect_callback(F, RB_OK);
}
+void
+rb_connect_sctp(rb_fde_t *F, struct sockaddr_storage *dest, size_t dest_len,
+ struct sockaddr_storage *clocal, size_t clocal_len,
+ CNCB *callback, void *data, int timeout)
+{
+#ifdef HAVE_LIBSCTP
+ uint8_t packed_dest[sizeof(struct sockaddr_storage) * dest_len];
+ uint8_t *p = &packed_dest[0];
+ size_t n = 0;
+ int retval;
+
+ if (F == NULL)
+ return;
+
+ lrb_assert(callback);
+ F->connect = rb_malloc(sizeof(struct conndata));
+ F->connect->callback = callback;
+ F->connect->data = data;
+
+ if ((F->type & RB_FD_SCTP) == 0) {
+ rb_connect_callback(F, RB_ERR_CONNECT);
+ return;
+ }
+
+ for (size_t i = 0; i < dest_len; i++) {
+ if (GET_SS_FAMILY(&dest[i]) == AF_INET6) {
+ memcpy(p, &dest[i], sizeof(struct sockaddr_in6));
+ n++;
+ p += sizeof(struct sockaddr_in6);
+ } else if (GET_SS_FAMILY(&dest[i]) == AF_INET) {
+ memcpy(p, &dest[i], sizeof(struct sockaddr_in));
+ n++;
+ p += sizeof(struct sockaddr_in);
+ }
+ }
+ dest_len = n;
+
+ memcpy(&F->connect->hostaddr, &dest[0], sizeof(F->connect->hostaddr));
+
+ if ((clocal_len > 0) && (rb_sctp_bindx_only(F, clocal, clocal_len) < 0)) {
+ /* Failure, call the callback with RB_ERR_BIND */
+ rb_connect_callback(F, RB_ERR_BIND);
+ /* ... and quit */
+ return;
+ }
+
+ rb_settimeout(F, timeout, rb_connect_timeout, NULL);
+
+ retval = sctp_connectx(F->fd, (struct sockaddr *)packed_dest, dest_len, NULL);
+ /* Error? */
+ if (retval < 0) {
+ /*
+ * If we get EISCONN, then we've already connect()ed the socket,
+ * which is a good thing.
+ * -- adrian
+ */
+ rb_get_errno();
+ if (errno == EISCONN) {
+ rb_connect_callback(F, RB_OK);
+ } else if (rb_ignore_errno(errno)) {
+ /* Ignore error? Reschedule */
+ rb_setselect(F, RB_SELECT_CONNECT, rb_connect_outcome, NULL);
+ } else {
+ /* Error? Fail with RB_ERR_CONNECT */
+ rb_connect_callback(F, RB_ERR_CONNECT);
+ }
+ return;
+ }
+ /* If we get here, we've succeeded, so call with RB_OK */
+ rb_connect_callback(F, RB_OK);
+#else
+ rb_connect_callback(F, RB_ERR_CONNECT);
+#endif
+}
/*
* rb_connect_callback() - call the callback, and continue with life
* called ..
*/
static void
-rb_connect_timeout(rb_fde_t *F, void *notused)
+rb_connect_timeout(rb_fde_t *F, void *notused __attribute__((unused)))
{
/* error! */
rb_connect_callback(F, RB_ERR_TIMEOUT);
}
-/* static void rb_connect_tryconnect(rb_platform_fd_t fd, void *notused)
- * Input: The fd, the handler data(unused).
- * Output: None.
- * Side-effects: Try and connect with pending connect data for the FD. If
- * we succeed or get a fatal error, call the callback.
- * Otherwise, it is still blocking or something, so register
- * to select for a write event on this FD.
- */
static void
-rb_connect_tryconnect(rb_fde_t *F, void *notused)
+rb_connect_outcome(rb_fde_t *F, void *notused __attribute__((unused)))
{
int retval;
+ int err = 0;
+ socklen_t len = sizeof(err);
if(F == NULL || F->connect == NULL || F->connect->callback == NULL)
return;
- /* Try the connect() */
- retval = connect(F->fd,
- (struct sockaddr *)&F->connect->hostaddr,
- GET_SS_LEN(&F->connect->hostaddr));
- /* Error? */
- if(retval < 0)
- {
- /*
- * If we get EISCONN, then we've already connect()ed the socket,
- * which is a good thing.
- * -- adrian
- */
+ retval = getsockopt(F->fd, SOL_SOCKET, SO_ERROR, &err, &len);
+ if (retval < 0) {
rb_get_errno();
- if(errno == EISCONN)
- rb_connect_callback(F, RB_OK);
- else if(rb_ignore_errno(errno))
- /* Ignore error? Reschedule */
- rb_setselect(F, RB_SELECT_CONNECT, rb_connect_tryconnect, NULL);
- else
- /* Error? Fail with RB_ERR_CONNECT */
- rb_connect_callback(F, RB_ERR_CONNECT);
+ } else if (err != 0) {
+ errno = err;
+ retval = -1;
+ }
+ if (retval < 0) {
+ /* Error? Fail with RB_ERR_CONNECT */
+ rb_connect_callback(F, RB_ERR_CONNECT);
return;
}
- /* If we get here, we've suceeded, so call with RB_OK */
+ /* If we get here, we've succeeded, so call with RB_OK */
rb_connect_callback(F, RB_OK);
}
if(rb_unlikely(fd < 0))
return NULL; /* errno will be passed through, yay.. */
-#if defined(RB_IPV6) && defined(IPV6_V6ONLY)
/*
* Make sure we can take both IPv4 and IPv6 connections
- * on an AF_INET6 socket
+ * on an AF_INET6 SCTP socket, otherwise keep them separate
*/
if(family == AF_INET6)
{
- int off = 1;
- if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &off, sizeof(off)) == -1)
+#ifdef HAVE_LIBSCTP
+ int v6only = (proto == IPPROTO_SCTP) ? 0 : 1;
+#else
+ int v6only = 1;
+#endif
+ if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &v6only, sizeof(v6only)) == -1)
{
- rb_lib_log("rb_socket: Could not set IPV6_V6ONLY option to 1 on FD %d: %s",
- fd, strerror(errno));
+ rb_lib_log("rb_socket: Could not set IPV6_V6ONLY option to %d on FD %d: %s",
+ v6only, fd, strerror(errno));
close(fd);
return NULL;
}
}
-#endif
F = rb_open(fd, RB_FD_SOCKET, note);
if(F == NULL)
close(fd);
return NULL;
}
+
+#ifdef HAVE_LIBSCTP
+ if (proto == IPPROTO_SCTP) {
+ F->type |= RB_FD_SCTP;
+
+ if (rb_setsockopt_sctp(F)) {
+ rb_lib_log("rb_socket: Could not set SCTP socket options on FD %d: %s",
+ fd, strerror(errno));
+ close(fd);
+ return NULL;
+ }
+ }
+#endif
+
/* Set the socket non-blocking, and other wonderful bits */
if(rb_unlikely(!rb_set_nb(F)))
{
* If a sockaddr_storage is AF_INET6 but is a mapped IPv4
* socket manged the sockaddr.
*/
-#ifdef RB_IPV6
static void
mangle_mapped_sockaddr(struct sockaddr *in)
{
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)in;
- if(in->sa_family == AF_INET)
- return;
-
if(in->sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&in6->sin6_addr))
{
struct sockaddr_in in4;
in4.sin_addr.s_addr = ((uint32_t *)&in6->sin6_addr)[3];
memcpy(in, &in4, sizeof(struct sockaddr_in));
}
- return;
}
-#endif
/*
* rb_listen() - listen on a port
{
int result;
- F->type = RB_FD_SOCKET | RB_FD_LISTEN;
+ F->type = RB_FD_SOCKET | RB_FD_LISTEN | (F->type & RB_FD_INHERIT_TYPES);
result = listen(F->fd, backlog);
#ifdef TCP_DEFER_ACCEPT
lrb_assert(F->read_handler == NULL);
lrb_assert(F->write_handler == NULL);
}
+
+ if (type & RB_FD_LISTEN) {
+ listen(F->fd, 0);
+ }
+
rb_setselect(F, RB_SELECT_WRITE | RB_SELECT_READ, NULL, NULL);
rb_settimeout(F, 0, NULL, NULL);
rb_free(F->accept);
ClearFDOpen(F);
}
- number_fd--;
-
-#ifdef _WIN32
- if(type & (RB_FD_SOCKET | RB_FD_PIPE))
- {
- closesocket(fd);
- return;
- }
- else
-#endif
- close(fd);
+ if(type & RB_FD_LISTEN)
+ shutdown(fd, SHUT_RDWR);
}
*/
static const char *inet_ntop4(const unsigned char *src, char *dst, unsigned int size);
-#ifdef RB_IPV6
static const char *inet_ntop6(const unsigned char *src, char *dst, unsigned int size);
-#endif
/* const char *
* inet_ntop4(src, dst, size)
* author:
* Paul Vixie, 1996.
*/
-#ifdef RB_IPV6
static const char *
inet_ntop6(const unsigned char *src, char *dst, unsigned int size)
{
}
return memcpy(dst, tmp, tp - tmp);
}
-#endif
int
-rb_inet_pton_sock(const char *src, struct sockaddr *dst)
+rb_inet_pton_sock(const char *src, struct sockaddr_storage *dst)
{
+ memset(dst, 0, sizeof(*dst));
if(rb_inet_pton(AF_INET, src, &((struct sockaddr_in *)dst)->sin_addr))
{
- ((struct sockaddr_in *)dst)->sin_port = 0;
- ((struct sockaddr_in *)dst)->sin_family = AF_INET;
+ SET_SS_FAMILY(dst, AF_INET);
+ SET_SS_PORT(dst, 0);
SET_SS_LEN(dst, sizeof(struct sockaddr_in));
return 1;
}
-#ifdef RB_IPV6
else if(rb_inet_pton(AF_INET6, src, &((struct sockaddr_in6 *)dst)->sin6_addr))
{
- ((struct sockaddr_in6 *)dst)->sin6_port = 0;
- ((struct sockaddr_in6 *)dst)->sin6_family = AF_INET6;
+ SET_SS_FAMILY(dst, AF_INET6);
+ SET_SS_PORT(dst, 0);
SET_SS_LEN(dst, sizeof(struct sockaddr_in6));
return 1;
}
-#endif
return 0;
}
{
case AF_INET:
return (rb_inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr, dst, size));
- break;
-#ifdef RB_IPV6
case AF_INET6:
return (rb_inet_ntop
(AF_INET6, &((struct sockaddr_in6 *)src)->sin6_addr, dst, size));
- break;
-#endif
default:
return NULL;
- break;
}
}
{
case AF_INET:
return (inet_ntop4(src, dst, size));
-#ifdef RB_IPV6
case AF_INET6:
if(IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src) ||
IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src))
s6_addr[12], dst, size));
else
return (inet_ntop6(src, dst, size));
-
-
-#endif
default:
return (NULL);
}
return (1);
}
-#ifdef RB_IPV6
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
memcpy(dst, tmp, IN6ADDRSZ);
return (1);
}
-#endif
+
int
rb_inet_pton(int af, const char *src, void *dst)
{
{
case AF_INET:
return (inet_pton4(src, dst));
-#ifdef RB_IPV6
case AF_INET6:
/* Somebody might have passed as an IPv4 address this is sick but it works */
if(inet_pton4(src, dst))
}
else
return (inet_pton6(src, dst));
-#endif
default:
return (-1);
}
}
-
int
rb_ignore_errno(int error)
{
int
-rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid)
+rb_send_fd_buf(rb_fde_t *xF, rb_fde_t **F, int count, void *data, size_t datasize, pid_t pid __attribute__((unused)))
{
struct msghdr msg;
struct cmsghdr *cmsg;
}
return sendmsg(rb_get_fd(xF), &msg, MSG_NOSIGNAL);
}
-#else
+#else /* defined(HAVE_SENDMSG) && !defined(WIN32) */
#ifndef _WIN32
int
rb_recv_fd_buf(rb_fde_t *F, void *data, size_t datasize, rb_fde_t **xF, int nfds)
errno = ENOSYS;
return -1;
}
-#endif
-#endif
+#endif /* _WIN32 */
+#endif /* defined(HAVE_SENDMSG) && !defined(WIN32) */
+
+int
+rb_ipv4_from_ipv6(const struct sockaddr_in6 *restrict ip6, struct sockaddr_in *restrict ip4)
+{
+ int i;
+
+ if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x02", 2))
+ {
+ /* 6to4 and similar */
+ memcpy(&ip4->sin_addr, ip6->sin6_addr.s6_addr + 2, 4);
+ }
+ else if (!memcmp(ip6->sin6_addr.s6_addr, "\x20\x01\x00\x00", 4))
+ {
+ /* Teredo */
+ for (i = 0; i < 4; i++)
+ ((uint8_t *)&ip4->sin_addr)[i] = 0xFF ^
+ ip6->sin6_addr.s6_addr[12 + i];
+ }
+ else
+ return 0;
+ SET_SS_LEN(ip4, sizeof(struct sockaddr_in));
+ ip4->sin_family = AF_INET;
+ ip4->sin_port = 0;
+ return 1;
+}