Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/media/mtransport/test/sctp_unittest.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// Original author: ekr@rtfm.com
8
9
#include <iostream>
10
#include <string>
11
#include <map>
12
13
#include "sigslot.h"
14
15
#include "logging.h"
16
#include "nsNetCID.h"
17
#include "nsITimer.h"
18
#include "nsComponentManagerUtils.h"
19
#include "nsThreadUtils.h"
20
#include "nsXPCOM.h"
21
22
#include "transportflow.h"
23
#include "transportlayer.h"
24
#include "transportlayerloopback.h"
25
26
#include "runnable_utils.h"
27
#include "usrsctp.h"
28
29
#define GTEST_HAS_RTTI 0
30
#include "gtest/gtest.h"
31
#include "gtest_utils.h"
32
33
34
using namespace mozilla;
35
36
static bool sctp_logging = false;
37
static int port_number = 5000;
38
39
namespace {
40
41
class TransportTestPeer;
42
43
class SendPeriodic : public nsITimerCallback {
44
 public:
45
  SendPeriodic(TransportTestPeer *peer, int to_send) :
46
      peer_(peer),
47
0
      to_send_(to_send) {}
48
49
  NS_DECL_THREADSAFE_ISUPPORTS
50
  NS_DECL_NSITIMERCALLBACK
51
52
 protected:
53
0
  virtual ~SendPeriodic() {}
54
55
  TransportTestPeer *peer_;
56
  int to_send_;
57
};
58
59
NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
60
61
62
class TransportTestPeer : public sigslot::has_slots<> {
63
 public:
64
  TransportTestPeer(std::string name, int local_port, int remote_port,
65
                    MtransportTestUtils* utils)
66
      : name_(name), connected_(false),
67
        sent_(0), received_(0),
68
        flow_(new TransportFlow()),
69
        loopback_(new TransportLayerLoopback()),
70
        sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
71
        timer_(NS_NewTimer()),
72
        periodic_(nullptr),
73
0
        test_utils_(utils) {
74
0
    std::cerr << "Creating TransportTestPeer; flow=" <<
75
0
        static_cast<void *>(flow_.get()) <<
76
0
        " local=" << local_port <<
77
0
        " remote=" << remote_port << std::endl;
78
0
79
0
    usrsctp_register_address(static_cast<void *>(this));
80
0
    int r = usrsctp_set_non_blocking(sctp_, 1);
81
0
    EXPECT_GE(r, 0);
82
0
83
0
    struct linger l;
84
0
    l.l_onoff = 1;
85
0
    l.l_linger = 0;
86
0
    r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
87
0
                       (socklen_t)sizeof(l));
88
0
    EXPECT_GE(r, 0);
89
0
90
0
    struct sctp_event subscription;
91
0
    memset(&subscription, 0, sizeof(subscription));
92
0
    subscription.se_assoc_id = SCTP_ALL_ASSOC;
93
0
    subscription.se_on = 1;
94
0
    subscription.se_type = SCTP_ASSOC_CHANGE;
95
0
    r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription,
96
0
                           sizeof(subscription));
97
0
    EXPECT_GE(r, 0);
98
0
99
0
    memset(&local_addr_, 0, sizeof(local_addr_));
100
0
    local_addr_.sconn_family = AF_CONN;
101
#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
102
    local_addr_.sconn_len = sizeof(struct sockaddr_conn);
103
#endif
104
    local_addr_.sconn_port = htons(local_port);
105
0
    local_addr_.sconn_addr = static_cast<void *>(this);
106
0
107
0
108
0
    memset(&remote_addr_, 0, sizeof(remote_addr_));
109
0
    remote_addr_.sconn_family = AF_CONN;
110
#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
111
    remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
112
#endif
113
    remote_addr_.sconn_port = htons(remote_port);
114
0
    remote_addr_.sconn_addr = static_cast<void *>(this);
115
0
116
0
    nsresult res;
117
0
    res = loopback_->Init();
118
0
    EXPECT_EQ((nsresult)NS_OK, res);
119
0
  }
120
121
0
  ~TransportTestPeer() {
122
0
    std::cerr << "Destroying sctp connection flow=" <<
123
0
        static_cast<void *>(flow_.get()) << std::endl;
124
0
    usrsctp_close(sctp_);
125
0
    usrsctp_deregister_address(static_cast<void *>(this));
126
0
127
0
    test_utils_->sts_target()->Dispatch(WrapRunnable(this,
128
0
                                                   &TransportTestPeer::Disconnect_s),
129
0
                                      NS_DISPATCH_SYNC);
130
0
131
0
    std::cerr << "~TransportTestPeer() completed" << std::endl;
132
0
  }
133
134
0
  void ConnectSocket(TransportTestPeer *peer) {
135
0
    test_utils_->sts_target()->Dispatch(WrapRunnable(
136
0
        this, &TransportTestPeer::ConnectSocket_s, peer),
137
0
                                       NS_DISPATCH_SYNC);
138
0
  }
139
140
0
  void ConnectSocket_s(TransportTestPeer *peer) {
141
0
    loopback_->Connect(peer->loopback_);
142
0
    ASSERT_EQ((nsresult)NS_OK, loopback_->Init());
143
0
    flow_->PushLayer(loopback_);
144
0
145
0
    loopback_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
146
0
147
0
    // SCTP here!
148
0
    ASSERT_TRUE(sctp_);
149
0
    std::cerr << "Calling usrsctp_bind()" << std::endl;
150
0
    int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>(
151
0
        &local_addr_), sizeof(local_addr_));
152
0
    ASSERT_GE(0, r);
153
0
154
0
    std::cerr << "Calling usrsctp_connect()" << std::endl;
155
0
    r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
156
0
        &remote_addr_), sizeof(remote_addr_));
157
0
    ASSERT_GE(0, r);
158
0
  }
159
160
0
  void Disconnect_s() {
161
0
    disconnect_all();
162
0
    if (flow_) {
163
0
      flow_ = nullptr;
164
0
    }
165
0
  }
166
167
0
  void Disconnect() {
168
0
    loopback_->Disconnect();
169
0
  }
170
171
172
0
  void StartTransfer(size_t to_send) {
173
0
    periodic_ = new SendPeriodic(this, to_send);
174
0
    timer_->SetTarget(test_utils_->sts_target());
175
0
    timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK);
176
0
  }
177
178
0
  void SendOne() {
179
0
    unsigned char buf[100];
180
0
    memset(buf, sent_ & 0xff, sizeof(buf));
181
0
182
0
    struct sctp_sndinfo info;
183
0
    info.snd_sid = 1;
184
0
    info.snd_flags = 0;
185
0
    info.snd_ppid = 50;  // What the heck is this?
186
0
    info.snd_context = 0;
187
0
    info.snd_assoc_id = 0;
188
0
189
0
    int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0,
190
0
                          static_cast<void *>(&info),
191
0
                          sizeof(info), SCTP_SENDV_SNDINFO, 0);
192
0
    ASSERT_TRUE(r >= 0);
193
0
    ASSERT_EQ(sizeof(buf), (size_t)r);
194
0
195
0
    ++sent_;
196
0
  }
197
198
0
  int sent() const { return sent_; }
199
0
  int received() const { return received_; }
200
0
  bool connected() const { return connected_; }
201
202
  static TransportResult SendPacket_s(nsAutoPtr<MediaPacket> packet,
203
                                      const RefPtr<TransportFlow>& flow,
204
0
                                      TransportLayer* layer) {
205
0
    return layer->SendPacket(*packet);
206
0
  }
207
208
0
  TransportResult SendPacket(const unsigned char* data, size_t len) {
209
0
    nsAutoPtr<MediaPacket> packet(new MediaPacket);
210
0
    packet->Copy(data, len);
211
0
212
0
    // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
213
0
    // from MainThread especially during shutdown (same as DataChannels).
214
0
    // RUN_ON_THREAD short-circuits if already on the STS thread, which is
215
0
    // normal for most transfers outside of connect() and close().  Passes
216
0
    // a refptr to flow_ to avoid any async deletion issues (since we can't
217
0
    // make 'this' into a refptr as it isn't refcounted)
218
0
    RUN_ON_THREAD(test_utils_->sts_target(), WrapRunnableNM(
219
0
        &TransportTestPeer::SendPacket_s, packet, flow_, loopback_),
220
0
                  NS_DISPATCH_NORMAL);
221
0
222
0
    return 0;
223
0
  }
224
225
0
  void PacketReceived(TransportLayer * layer, MediaPacket& packet) {
226
0
    std::cerr << "Received " << packet.len() << " bytes" << std::endl;
227
0
228
0
    // Pass the data to SCTP
229
0
230
0
    usrsctp_conninput(static_cast<void *>(this), packet.data(), packet.len(), 0);
231
0
  }
232
233
  // Process SCTP notification
234
0
  void Notification(union sctp_notification *msg, size_t len) {
235
0
    ASSERT_EQ(msg->sn_header.sn_length, len);
236
0
237
0
    if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
238
0
      struct sctp_assoc_change *change = &msg->sn_assoc_change;
239
0
240
0
      if (change->sac_state == SCTP_COMM_UP) {
241
0
        std::cerr << "Connection up" << std::endl;
242
0
        SetConnected(true);
243
0
      } else {
244
0
        std::cerr << "Connection down" << std::endl;
245
0
        SetConnected(false);
246
0
      }
247
0
    }
248
0
  }
249
250
0
  void SetConnected(bool state) {
251
0
    connected_ = state;
252
0
  }
253
254
0
  static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) {
255
0
    TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr);
256
0
257
0
    peer->SendPacket(static_cast<unsigned char *>(buffer), length);
258
0
259
0
    return 0;
260
0
  }
261
262
  static int receive_cb(struct socket* sock, union sctp_sockstore addr,
263
                        void *data, size_t datalen,
264
0
                        struct sctp_rcvinfo rcv, int flags, void *ulp_info) {
265
0
    TransportTestPeer *me = static_cast<TransportTestPeer *>(
266
0
        addr.sconn.sconn_addr);
267
0
    MOZ_ASSERT(me);
268
0
269
0
    if (flags & MSG_NOTIFICATION) {
270
0
      union sctp_notification *notif =
271
0
          static_cast<union sctp_notification *>(data);
272
0
273
0
      me->Notification(notif, datalen);
274
0
      return 0;
275
0
    }
276
0
277
0
    me->received_ += datalen;
278
0
279
0
    std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
280
0
281
0
    return 0;
282
0
  }
283
284
285
 private:
286
  std::string name_;
287
  bool connected_;
288
  size_t sent_;
289
  size_t received_;
290
  // Owns the TransportLayerLoopback, but basically does nothing else.
291
  RefPtr<TransportFlow> flow_;
292
  TransportLayerLoopback *loopback_;
293
294
  struct sockaddr_conn local_addr_;
295
  struct sockaddr_conn remote_addr_;
296
  struct socket *sctp_;
297
  nsCOMPtr<nsITimer> timer_;
298
  RefPtr<SendPeriodic> periodic_;
299
  MtransportTestUtils* test_utils_;
300
};
301
302
303
// Implemented here because it calls a method of TransportTestPeer
304
0
NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) {
305
0
  peer_->SendOne();
306
0
  --to_send_;
307
0
  if (!to_send_) {
308
0
    timer->Cancel();
309
0
  }
310
0
  return NS_OK;
311
0
}
312
313
class SctpTransportTest : public MtransportTest {
314
 public:
315
0
  SctpTransportTest() {
316
0
  }
317
318
0
  ~SctpTransportTest() {
319
0
  }
320
321
0
  static void debug_printf(const char *format, ...) {
322
0
    va_list ap;
323
0
324
0
    va_start(ap, format);
325
0
    vprintf(format, ap);
326
0
    va_end(ap);
327
0
  }
328
329
330
0
  static void SetUpTestCase() {
331
0
    if (sctp_logging) {
332
0
      usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf);
333
0
      usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
334
0
    } else {
335
0
      usrsctp_init(0, &TransportTestPeer::conn_output, nullptr);
336
0
    }
337
0
  }
338
339
0
  void TearDown() override {
340
0
    if (p1_)
341
0
      p1_->Disconnect();
342
0
    if (p2_)
343
0
      p2_->Disconnect();
344
0
    delete p1_;
345
0
    delete p2_;
346
0
347
0
    MtransportTest::TearDown();
348
0
  }
349
350
351
0
  void ConnectSocket(int p1port = 0, int p2port = 0) {
352
0
    if (!p1port)
353
0
      p1port = port_number++;
354
0
    if (!p2port)
355
0
      p2port = port_number++;
356
0
357
0
    p1_ = new TransportTestPeer("P1", p1port, p2port, test_utils_);
358
0
    p2_ = new TransportTestPeer("P2", p2port, p1port, test_utils_);
359
0
360
0
    p1_->ConnectSocket(p2_);
361
0
    p2_->ConnectSocket(p1_);
362
0
    ASSERT_TRUE_WAIT(p1_->connected(), 2000);
363
0
    ASSERT_TRUE_WAIT(p2_->connected(), 2000);
364
0
  }
365
366
0
  void TestTransfer(int expected = 1) {
367
0
    std::cerr << "Starting trasnsfer test" << std::endl;
368
0
    p1_->StartTransfer(expected);
369
0
    ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000);
370
0
    ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000);
371
0
    std::cerr << "P2 received " << p2_->received() << std::endl;
372
0
  }
373
374
 protected:
375
  TransportTestPeer *p1_;
376
  TransportTestPeer *p2_;
377
};
378
379
0
TEST_F(SctpTransportTest, TestConnect) {
380
0
  ConnectSocket();
381
0
}
382
383
0
TEST_F(SctpTransportTest, TestConnectSymmetricalPorts) {
384
0
  ConnectSocket(5002,5002);
385
0
}
386
387
0
TEST_F(SctpTransportTest, TestTransfer) {
388
0
  ConnectSocket();
389
0
  TestTransfer(50);
390
0
}
391
392
393
}  // end namespace