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