1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22#include "uv.h"
23#include "task.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29
30#define CHECK_HANDLE(handle) \
31  ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client)
32
33#if defined(__APPLE__)          || \
34    defined(_AIX)               || \
35    defined(__MVS__)            || \
36    defined(__FreeBSD_kernel__) || \
37    defined(__NetBSD__)         || \
38    defined(__OpenBSD__)
39  #define MULTICAST_ADDR "ff02::1%lo0"
40  #define INTERFACE_ADDR "::1%lo0"
41#else
42  #define MULTICAST_ADDR "ff02::1"
43  #define INTERFACE_ADDR NULL
44#endif
45
46static uv_udp_t server;
47static uv_udp_t client;
48static uv_udp_send_t req;
49static uv_udp_send_t req_ss;
50
51static int cl_recv_cb_called;
52
53static int sv_send_cb_called;
54
55static int close_cb_called;
56
57static void alloc_cb(uv_handle_t* handle,
58                     size_t suggested_size,
59                     uv_buf_t* buf) {
60  static char slab[65536];
61  CHECK_HANDLE(handle);
62  ASSERT(suggested_size <= sizeof(slab));
63  buf->base = slab;
64  buf->len = sizeof(slab);
65}
66
67
68static void close_cb(uv_handle_t* handle) {
69  CHECK_HANDLE(handle);
70  close_cb_called++;
71}
72
73
74static void sv_send_cb(uv_udp_send_t* req, int status) {
75  ASSERT_NOT_NULL(req);
76  ASSERT(status == 0);
77  CHECK_HANDLE(req->handle);
78
79  sv_send_cb_called++;
80
81  if (sv_send_cb_called == 2)
82    uv_close((uv_handle_t*) req->handle, close_cb);
83}
84
85
86static int do_send(uv_udp_send_t* send_req) {
87  uv_buf_t buf;
88  struct sockaddr_in6 addr;
89
90  buf = uv_buf_init("PING", 4);
91
92  ASSERT(0 == uv_ip6_addr(MULTICAST_ADDR, TEST_PORT, &addr));
93
94  /* client sends "PING" */
95  return uv_udp_send(send_req,
96                     &client,
97                     &buf,
98                     1,
99                     (const struct sockaddr*) &addr,
100                     sv_send_cb);
101}
102
103
104static void cl_recv_cb(uv_udp_t* handle,
105                       ssize_t nread,
106                       const uv_buf_t* buf,
107                       const struct sockaddr* addr,
108                       unsigned flags) {
109  CHECK_HANDLE(handle);
110  ASSERT(flags == 0);
111
112  if (nread < 0) {
113    ASSERT(0 && "unexpected error");
114  }
115
116  if (nread == 0) {
117    /* Returning unused buffer. Don't count towards cl_recv_cb_called */
118    ASSERT_NULL(addr);
119    return;
120  }
121
122  ASSERT_NOT_NULL(addr);
123  ASSERT(nread == 4);
124  ASSERT(!memcmp("PING", buf->base, nread));
125
126  cl_recv_cb_called++;
127
128  if (cl_recv_cb_called == 2) {
129    /* we are done with the server handle, we can close it */
130    uv_close((uv_handle_t*) &server, close_cb);
131  } else {
132    int r;
133    char source_addr[64];
134
135    r = uv_ip6_name((const struct sockaddr_in6*)addr, source_addr, sizeof(source_addr));
136    ASSERT(r == 0);
137
138    r = uv_udp_set_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, UV_LEAVE_GROUP);
139    ASSERT(r == 0);
140
141    r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, source_addr, UV_JOIN_GROUP);
142    ASSERT(r == 0);
143
144    r = do_send(&req_ss);
145    ASSERT(r == 0);
146  }
147}
148
149
150static int can_ipv6_external(void) {
151  uv_interface_address_t* addr;
152  int supported;
153  int count;
154  int i;
155
156  if (uv_interface_addresses(&addr, &count))
157    return 0;  /* Assume no IPv6 support on failure. */
158
159  supported = 0;
160  for (i = 0; supported == 0 && i < count; i += 1)
161    supported = (AF_INET6 == addr[i].address.address6.sin6_family &&
162                 !addr[i].is_internal);
163
164  uv_free_interface_addresses(addr, count);
165  return supported;
166}
167
168
169TEST_IMPL(udp_multicast_join6) {
170  int r;
171  struct sockaddr_in6 addr;
172
173  if (!can_ipv6_external())
174    RETURN_SKIP("No external IPv6 interface available");
175
176  ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr));
177
178  r = uv_udp_init(uv_default_loop(), &server);
179  ASSERT(r == 0);
180
181  r = uv_udp_init(uv_default_loop(), &client);
182  ASSERT(r == 0);
183
184  /* bind to the desired port */
185  r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0);
186  ASSERT(r == 0);
187
188  r = uv_udp_set_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, UV_JOIN_GROUP);
189  if (r == UV_ENODEV) {
190    MAKE_VALGRIND_HAPPY();
191    RETURN_SKIP("No ipv6 multicast route");
192  }
193
194  ASSERT(r == 0);
195
196/* TODO(gengjiawen): Fix test on QEMU. */
197#if defined(__QEMU__)
198  RETURN_SKIP("Test does not currently work in QEMU");
199#endif
200  r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb);
201  ASSERT(r == 0);
202
203  r = do_send(&req);
204  ASSERT(r == 0);
205
206  ASSERT(close_cb_called == 0);
207  ASSERT(cl_recv_cb_called == 0);
208  ASSERT(sv_send_cb_called == 0);
209
210  /* run the loop till all events are processed */
211  uv_run(uv_default_loop(), UV_RUN_DEFAULT);
212
213  ASSERT(cl_recv_cb_called == 2);
214  ASSERT(sv_send_cb_called == 2);
215  ASSERT(close_cb_called == 2);
216
217  MAKE_VALGRIND_HAPPY();
218  return 0;
219}
220