1 #include <vector>
2 #include <algorithm>
3 #include <future>
4
5 #include "gtest/gtest.h"
6 #include "packet.h"
7 #include "fec.h"
8 #include "core.h"
9 #include "packetfilter.h"
10 #include "packetfilter_api.h"
11
12 // For direct imp access
13 #include "api.h"
14
15 using namespace std;
16 using namespace srt;
17
18 class TestFECRebuilding: public testing::Test
19 {
20 protected:
21 FECFilterBuiltin* fec = nullptr;
22 vector<SrtPacket> provided;
23 vector<unique_ptr<CPacket>> source;
24 int sockid = 54321;
25 int isn = 123456;
26 size_t plsize = 1316;
27
TestFECRebuilding()28 TestFECRebuilding()
29 {
30 // Required to make ParseCorrectorConfig work
31 PacketFilter::globalInit();
32 }
33
SetUp()34 void SetUp() override
35 {
36 int timestamp = 10;
37
38 SrtFilterInitializer init = {
39 sockid,
40 isn - 1, // It's passed in this form to PacketFilter constructor, it should increase it
41 isn - 1, // XXX Probably this better be changed.
42 plsize,
43 CSrtConfig::DEF_BUFFER_SIZE
44 };
45
46
47 // Make configuration row-only with size 7
48 string conf = "fec,rows:1,cols:7";
49
50 provided.clear();
51
52 fec = new FECFilterBuiltin(init, provided, conf);
53
54 int32_t seq = isn;
55
56 for (int i = 0; i < 7; ++i)
57 {
58 source.emplace_back(new CPacket);
59 CPacket& p = *source.back();
60
61 p.allocate(SRT_LIVE_MAX_PLSIZE);
62
63 uint32_t* hdr = p.getHeader();
64
65 // Fill in the values
66 hdr[SRT_PH_SEQNO] = seq;
67 hdr[SRT_PH_MSGNO] = 1 | MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
68 hdr[SRT_PH_ID] = sockid;
69 hdr[SRT_PH_TIMESTAMP] = timestamp;
70
71 // Fill in the contents.
72 // Randomly chose the size
73
74 int minsize = 732;
75 int divergence = plsize - minsize - 1;
76 size_t length = minsize + rand() % divergence;
77
78 p.setLength(length);
79 for (size_t b = 0; b < length; ++b)
80 {
81 p.data()[b] = rand() % 255;
82 }
83
84 timestamp += 10;
85 seq = CSeqNo::incseq(seq);
86 }
87 }
88
TearDown()89 void TearDown() override
90 {
91 delete fec;
92 }
93 };
94
95 namespace srt {
96 class TestMockCUDT
97 {
98 public:
99 CUDT* core;
100
checkApplyFilterConfig(const string & s)101 bool checkApplyFilterConfig(const string& s)
102 {
103 return core->checkApplyFilterConfig(s);
104 }
105 };
106 }
107
108 // The expected whole procedure of connection using FEC is
109 // expected to:
110 //
111 // 1. Successfully set the FEC option for correct filter type.
112 // - STOP ON FAILURE: unknown filter type (the table below, case D)
113 // 2. Perform the connection and integrate configurations.
114 // - STOP on failed integration (the table below, cases A and B)
115 // 3. Deliver on both sides identical configurations consisting
116 // of combined configurations and completed with default values.
117 // - Not possible if stopped before.
118 //
119 // Test coverage for the above cases:
120 //
121 // Success cases in all of the above: ConfigExchange, Connection, ConnectionReorder
122 // Failure cases:
123 // 1. ConfigExchangeFaux - setting unknown filter type
124 // 2. ConfigExchangeFaux, RejectionConflict, RejectionIncomplete, RejectionIncompleteEmpty
125 //
126 // For config exchange we have several possibilities here:
127 //
128 // - any same parameters with different values are rejected (Case A)
129 // - resulting configuiration should have the `cols` value set (Cases B)
130 //
131 // The configuration API rules that control correctness:
132 //
133 // 1. The first word defines an existing filter type.
134 // 2. Parameters are defined in whatever order.
135 // 3. Some parameters are optional and have default values. Others are mandatory.
136 // 4. A parameter provided twice remains with the last specification.
137 // 5. A parameter with empty value is like not provided parameter.
138 // 6. Only parameters handled by given filter type are allowed.
139 // 7. Every parameter may have limitations on the provided value:
140 // a. Numeric values in appropriate range
141 // b. String-enumeration with only certain values allowed
142 //
143 // Additionally there are rules for configuration integration:
144 //
145 // 8. Configuration consists of parameters provided in both sides.
146 // 9. Parameters lacking after integration are set to default values.
147 // 10. Parameters specified on both sides (including type) must be equal.
148 // 11. Empty configuration blindly accepts the configuration from the peer.
149 // 12. The final configuration must provide mandatory parameters
150 //
151 // Restrictive rules type are: 1, 6, 7, 10
152 //
153 // Case description:
154 // A: Conflicting values on the same parameter (rejection, rule 10 failure)
155 // B: Missing a mandatory parameter (rejection, rule 12 failure)
156 // C: Successful setting and combining parameters
157 // 1: rules (positive): 1, 3, 6, 7(part), 8, 9, 12
158 // 2: rules (positive): 1, 2, 3, 6, 7(part), 9, 10, 12
159 // 3,4: rules (positive): 1, 2, 3(all), 6, 7(all), 8, 10, 12
160 // 5: rules (positive): 1, 3, 4, 5, 6, 7, 8, 9, 12
161 // 6: rules (positive): 1, 3, 6, 7, 8, 11, 12
162 // D: Unknown filter type (failed option, rule 1)
163 // E: Incorrect values of the parameters (failed option, rule 7)
164 // F: Unknown excessive parameters (failed option, rule 6)
165 //
166 // Case |Party A | Party B | Situation | Test coverage
167 //------|------------------------|--------------------|---------------------|---------------
168 // A |fec,cols:10 | fec,cols:20 | Conflict | ConfigExchangeFaux, RejectionConflict
169 // B1 |fec,rows:10 | fec,arq:never | Missing `cols` | RejectionIncomplete
170 // B2 |fec,rows:10 | | Missing `cols` | RejectionIncompleteEmpty
171 // C1 |fec,cols:10,rows:10 | fec | OK | ConfigExchange, Connection
172 // C2 |fec,cols:10,rows:10 | fec,rows:10,cols:10| OK | ConnectionReorder
173 // C3 |FULL 1 (see below) | FULL 2 (see below) | OK | ConnectionFull1
174 // C4 |FULL 3 (see below) | FULL 4 (see below) | OK | ConnectionFull2
175 // C5 |fec,cols:,cols:10 | fec,cols:,rows:10 | OK | ConnectionMess
176 // C6 |fec,rows:20,cols:20 | | OK | ConnectionForced
177 // D |FEC,Cols:10 | (unimportant) | Option rejected | ConfigExchangeFaux
178 // E1 |fec,cols:-10 | (unimportant) | Option rejected | ConfigExchangeFaux
179 // E2 |fec,cols:10,rows:0 | (unimportant) | Option rejected | ConfigExchangeFaux
180 // E3 |fec,cols:10,rows:-1 | (unimportant) | Option rejected | ConfigExchangeFaux
181 // E4 |fec,cols:10,layout:x (*)| (unimportant) | Option rejected | ConfigExchangeFaux
182 // E5 |fec,cols:10,arq:x (*) | (unimportant) | Option rejected | ConfigExchangeFaux
183 // F |fec,cols:10,weight:2 | (unimportant) | Option rejected | ConfigExchangeFaux
184 //
185 // (*) Here is just an example of a longer string that surely is wrong for this parameter.
186 //
187 // The configurations for FULL (cases C3 and C4) are longer and use all possible
188 // values in different order:
189 // 1. fec,cols:10,rows:20,arq:never,layout:even
190 // 1. fec,layout:even,rows:20,cols:10,arq:never
191 // 1. fec,cols:10,rows:20,arq:always,layout:even
192 // 1. fec,layout:even,rows:20,cols:10,arq:always
193
194
filterConfigSame(const string & config1,const string & config2)195 bool filterConfigSame(const string& config1, const string& config2)
196 {
197 vector<string> config1_vector;
198 Split(config1, ',', back_inserter(config1_vector));
199 sort(config1_vector.begin(), config1_vector.end());
200
201 vector<string> config2_vector;
202 Split(config2, ',', back_inserter(config2_vector));
203 sort(config2_vector.begin(), config2_vector.end());
204
205 return config1_vector == config2_vector;
206 }
207
TEST(TestFEC,ConfigExchange)208 TEST(TestFEC, ConfigExchange)
209 {
210 srt_startup();
211
212 CUDTSocket* s1;
213
214 SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1);
215
216 TestMockCUDT m1;
217 m1.core = &s1->core();
218
219 // Can't access the configuration storage without
220 // accessing the private fields, so let's use the official API
221
222 char fec_config1 [] = "fec,cols:10,rows:10";
223
224 srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
225
226 EXPECT_TRUE(m1.checkApplyFilterConfig("fec,cols:10,arq:never"));
227
228 char fec_configback[200];
229 int fec_configback_size = 200;
230 srt_getsockflag(sid1, SRTO_PACKETFILTER, fec_configback, &fec_configback_size);
231
232 // Order of parameters may differ, so store everything in a vector and sort it.
233
234 string exp_config = "fec,cols:10,rows:10,arq:never,layout:staircase";
235
236 EXPECT_TRUE(filterConfigSame(fec_configback, exp_config));
237 srt_cleanup();
238 }
239
TEST(TestFEC,ConfigExchangeFaux)240 TEST(TestFEC, ConfigExchangeFaux)
241 {
242 srt_startup();
243
244 CUDTSocket* s1;
245
246 SRTSOCKET sid1 = CUDT::uglobal()->newSocket(&s1);
247
248 const char* fec_config_wrong [] = {
249 "FEC,Cols:20", // D: unknown filter
250 "fec,cols:-10", // E1: invalid value for cols
251 "fec,cols:10,rows:0", // E2: invalid value for rows
252 "fec,cols:10,rows:-1", // E3: invalid value for rows
253 "fec,cols:10,layout:stairwars", // E4: invalid value for layout
254 "fec,cols:10,arq:sometimes", // E5: invalid value for arq
255 "fec,cols:10,weight:2" // F: invalid parameter name
256 };
257
258 for (auto badconfig: fec_config_wrong)
259 {
260 ASSERT_EQ(srt_setsockflag(sid1, SRTO_PACKETFILTER, badconfig, strlen(badconfig)), -1);
261 }
262
263 TestMockCUDT m1;
264 m1.core = &s1->core();
265
266 // Can't access the configuration storage without
267 // accessing the private fields, so let's use the official API
268
269 char fec_config1 [] = "fec,cols:20,rows:10";
270
271 EXPECT_NE(srt_setsockflag(sid1, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
272
273 cout << "(NOTE: expecting a failure message)\n";
274 EXPECT_FALSE(m1.checkApplyFilterConfig("fec,cols:10,arq:never"));
275
276 srt_cleanup();
277 }
278
TEST(TestFEC,Connection)279 TEST(TestFEC, Connection)
280 {
281 srt_startup();
282
283 SRTSOCKET s = srt_create_socket();
284 SRTSOCKET l = srt_create_socket();
285
286 sockaddr_in sa;
287 memset(&sa, 0, sizeof sa);
288 sa.sin_family = AF_INET;
289 sa.sin_port = htons(5555);
290 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
291
292 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
293
294 const char fec_config1 [] = "fec,cols:10,rows:10";
295 const char fec_config2 [] = "fec,cols:10,arq:never";
296 const char fec_config_final [] = "fec,cols:10,rows:10,arq:never,layout:staircase";
297
298 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
299 ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
300
301 srt_listen(l, 1);
302
303 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
304 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
305 });
306
307 SRTSOCKET la[] = { l };
308 // Given 2s timeout for accepting as it has occasionally happened with Travis
309 // that 1s might not be enough.
310 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
311 ASSERT_NE(a, SRT_ERROR);
312 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
313
314 // Now that the connection is established, check negotiated config
315
316 char result_config1[200] = "";
317 int result_config1_size = 200;
318 char result_config2[200] = "";
319 int result_config2_size = 200;
320
321 EXPECT_NE(srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size), -1);
322 EXPECT_NE(srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size), -1);
323
324 string caller_config = result_config1;
325 string accept_config = result_config2;
326 EXPECT_EQ(caller_config, accept_config);
327
328 EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
329 EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
330
331 srt_cleanup();
332 }
333
TEST(TestFEC,ConnectionReorder)334 TEST(TestFEC, ConnectionReorder)
335 {
336 srt_startup();
337
338 SRTSOCKET s = srt_create_socket();
339 SRTSOCKET l = srt_create_socket();
340
341 sockaddr_in sa;
342 memset(&sa, 0, sizeof sa);
343 sa.sin_family = AF_INET;
344 sa.sin_port = htons(5555);
345 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
346
347 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
348
349 const char fec_config1 [] = "fec,cols:10,rows:10";
350 const char fec_config2 [] = "fec,rows:10,cols:10";
351 const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase";
352
353 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
354 ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
355
356 srt_listen(l, 1);
357
358 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
359 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
360 });
361
362 SRTSOCKET la[] = { l };
363 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
364 ASSERT_NE(a, SRT_ERROR);
365 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
366
367 // Now that the connection is established, check negotiated config
368
369 char result_config1[200] = "";
370 int result_config1_size = 200;
371 char result_config2[200] = "";
372 int result_config2_size = 200;
373
374 srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
375 srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
376
377 string caller_config = result_config1;
378 string accept_config = result_config2;
379 EXPECT_EQ(caller_config, accept_config);
380
381 EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
382 EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
383
384 srt_cleanup();
385 }
386
TEST(TestFEC,ConnectionFull1)387 TEST(TestFEC, ConnectionFull1)
388 {
389 srt_startup();
390
391 SRTSOCKET s = srt_create_socket();
392 SRTSOCKET l = srt_create_socket();
393
394 sockaddr_in sa;
395 memset(&sa, 0, sizeof sa);
396 sa.sin_family = AF_INET;
397 sa.sin_port = htons(5555);
398 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
399
400 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
401
402 const char fec_config1 [] = "fec,cols:10,rows:20,arq:never,layout:even";
403 const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:never";
404 const char fec_config_final [] = "fec,cols:10,rows:20,arq:never,layout:even";
405
406 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
407 ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
408
409 srt_listen(l, 1);
410
411 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
412 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
413 });
414
415 SRTSOCKET la[] = { l };
416 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
417 ASSERT_NE(a, SRT_ERROR);
418 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
419
420 // Now that the connection is established, check negotiated config
421
422 char result_config1[200] = "";
423 int result_config1_size = 200;
424 char result_config2[200] = "";
425 int result_config2_size = 200;
426
427 srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
428 srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
429
430 string caller_config = result_config1;
431 string accept_config = result_config2;
432 EXPECT_EQ(caller_config, accept_config);
433
434 EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
435 EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
436
437 srt_cleanup();
438 }
TEST(TestFEC,ConnectionFull2)439 TEST(TestFEC, ConnectionFull2)
440 {
441 srt_startup();
442
443 SRTSOCKET s = srt_create_socket();
444 SRTSOCKET l = srt_create_socket();
445
446 sockaddr_in sa;
447 memset(&sa, 0, sizeof sa);
448 sa.sin_family = AF_INET;
449 sa.sin_port = htons(5555);
450 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
451
452 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
453
454 const char fec_config1 [] = "fec,cols:10,rows:20,arq:always,layout:even";
455 const char fec_config2 [] = "fec,layout:even,rows:20,cols:10,arq:always";
456 const char fec_config_final [] = "fec,cols:10,rows:20,arq:always,layout:even";
457
458 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
459 ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
460
461 srt_listen(l, 1);
462
463 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
464 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
465 });
466
467 SRTSOCKET la[] = { l };
468 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
469 ASSERT_NE(a, SRT_ERROR);
470 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
471
472 // Now that the connection is established, check negotiated config
473
474 char result_config1[200] = "";
475 int result_config1_size = 200;
476 char result_config2[200] = "";
477 int result_config2_size = 200;
478
479 srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
480 srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
481
482 string caller_config = result_config1;
483 string accept_config = result_config2;
484 EXPECT_EQ(caller_config, accept_config);
485
486 EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
487 EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
488
489 srt_cleanup();
490 }
491
TEST(TestFEC,ConnectionMess)492 TEST(TestFEC, ConnectionMess)
493 {
494 srt_startup();
495
496 SRTSOCKET s = srt_create_socket();
497 SRTSOCKET l = srt_create_socket();
498
499 sockaddr_in sa;
500 memset(&sa, 0, sizeof sa);
501 sa.sin_family = AF_INET;
502 sa.sin_port = htons(5555);
503 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
504
505 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
506
507 const char fec_config1 [] = "fec,cols:,cols:10";
508 const char fec_config2 [] = "fec,cols:,rows:10";
509 const char fec_config_final [] = "fec,cols:10,rows:10,arq:onreq,layout:staircase";
510
511 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
512 ASSERT_NE(srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1), -1);
513
514 srt_listen(l, 1);
515
516 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
517 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
518 });
519
520 SRTSOCKET la[] = { l };
521 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
522 ASSERT_NE(a, SRT_ERROR);
523 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
524
525 // Now that the connection is established, check negotiated config
526
527 char result_config1[200] = "";
528 int result_config1_size = 200;
529 char result_config2[200] = "";
530 int result_config2_size = 200;
531
532 srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
533 srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
534
535 string caller_config = result_config1;
536 string accept_config = result_config2;
537 EXPECT_EQ(caller_config, accept_config);
538
539 EXPECT_TRUE(filterConfigSame(caller_config, fec_config_final));
540 EXPECT_TRUE(filterConfigSame(accept_config, fec_config_final));
541
542 srt_cleanup();
543 }
544
TEST(TestFEC,ConnectionForced)545 TEST(TestFEC, ConnectionForced)
546 {
547 srt_startup();
548
549 SRTSOCKET s = srt_create_socket();
550 SRTSOCKET l = srt_create_socket();
551
552 sockaddr_in sa;
553 memset(&sa, 0, sizeof sa);
554 sa.sin_family = AF_INET;
555 sa.sin_port = htons(5555);
556 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
557
558 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
559
560 const char fec_config1 [] = "fec,rows:20,cols:20";
561 const char fec_config_final [] = "fec,cols:20,rows:20";
562
563 ASSERT_NE(srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1), -1);
564
565 srt_listen(l, 1);
566
567 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
568 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
569 });
570
571 SRTSOCKET la[] = { l };
572 SRTSOCKET a = srt_accept_bond(la, 1, 2000);
573 ASSERT_NE(a, SRT_ERROR);
574 EXPECT_EQ(connect_res.get(), SRT_SUCCESS);
575
576 // Now that the connection is established, check negotiated config
577
578 char result_config1[200] = "";
579 int result_config1_size = 200;
580 char result_config2[200] = "";
581 int result_config2_size = 200;
582
583 srt_getsockflag(s, SRTO_PACKETFILTER, result_config1, &result_config1_size);
584 srt_getsockflag(a, SRTO_PACKETFILTER, result_config2, &result_config2_size);
585
586 EXPECT_TRUE(filterConfigSame(result_config1, fec_config_final));
587 EXPECT_TRUE(filterConfigSame(result_config2, fec_config_final));
588
589 srt_cleanup();
590 }
591
TEST(TestFEC,RejectionConflict)592 TEST(TestFEC, RejectionConflict)
593 {
594 srt_startup();
595
596 SRTSOCKET s = srt_create_socket();
597 SRTSOCKET l = srt_create_socket();
598
599 sockaddr_in sa;
600 memset(&sa, 0, sizeof sa);
601 sa.sin_family = AF_INET;
602 sa.sin_port = htons(5555);
603 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
604
605 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
606
607 const char fec_config1 [] = "fec,cols:10,rows:10";
608 const char fec_config2 [] = "fec,cols:20,arq:never";
609
610 srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
611 srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1);
612
613 srt_listen(l, 1);
614
615 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
616 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
617 });
618
619 EXPECT_EQ(connect_res.get(), SRT_ERROR);
620 EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
621
622 bool no = false;
623 // Set non-blocking so that srt_accept can return
624 // immediately with failure. Just to make sure that
625 // the connection is not about to be established,
626 // also on the listener side.
627 srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
628 sockaddr_in scl;
629 int sclen = sizeof scl;
630 EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
631
632 srt_cleanup();
633 }
634
TEST(TestFEC,RejectionIncompleteEmpty)635 TEST(TestFEC, RejectionIncompleteEmpty)
636 {
637 srt_startup();
638
639 SRTSOCKET s = srt_create_socket();
640 SRTSOCKET l = srt_create_socket();
641
642 sockaddr_in sa;
643 memset(&sa, 0, sizeof sa);
644 sa.sin_family = AF_INET;
645 sa.sin_port = htons(5555);
646 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
647
648 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
649
650 const char fec_config1 [] = "fec,rows:10";
651 srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
652
653 srt_listen(l, 1);
654
655 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
656 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
657 });
658
659 EXPECT_EQ(connect_res.get(), SRT_ERROR);
660 EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
661
662 bool no = false;
663 // Set non-blocking so that srt_accept can return
664 // immediately with failure. Just to make sure that
665 // the connection is not about to be established,
666 // also on the listener side.
667 srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
668 sockaddr_in scl;
669 int sclen = sizeof scl;
670 EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
671
672 srt_cleanup();
673 }
674
675
TEST(TestFEC,RejectionIncomplete)676 TEST(TestFEC, RejectionIncomplete)
677 {
678 srt_startup();
679
680 SRTSOCKET s = srt_create_socket();
681 SRTSOCKET l = srt_create_socket();
682
683 sockaddr_in sa;
684 memset(&sa, 0, sizeof sa);
685 sa.sin_family = AF_INET;
686 sa.sin_port = htons(5555);
687 ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr), 1);
688
689 srt_bind(l, (sockaddr*)& sa, sizeof(sa));
690
691 const char fec_config1 [] = "fec,rows:10";
692 const char fec_config2 [] = "fec,arq:never";
693
694 srt_setsockflag(s, SRTO_PACKETFILTER, fec_config1, (sizeof fec_config1)-1);
695 srt_setsockflag(l, SRTO_PACKETFILTER, fec_config2, (sizeof fec_config2)-1);
696
697 srt_listen(l, 1);
698
699 auto connect_res = std::async(std::launch::async, [&s, &sa]() {
700 return srt_connect(s, (sockaddr*)& sa, sizeof(sa));
701 });
702
703 EXPECT_EQ(connect_res.get(), SRT_ERROR);
704 EXPECT_EQ(srt_getrejectreason(s), SRT_REJ_FILTER);
705
706 bool no = false;
707 // Set non-blocking so that srt_accept can return
708 // immediately with failure. Just to make sure that
709 // the connection is not about to be established,
710 // also on the listener side.
711 srt_setsockflag(l, SRTO_RCVSYN, &no, sizeof no);
712 sockaddr_in scl;
713 int sclen = sizeof scl;
714 EXPECT_EQ(srt_accept(l, (sockaddr*)& scl, &sclen), SRT_ERROR);
715
716 srt_cleanup();
717 }
718
TEST_F(TestFECRebuilding,Prepare)719 TEST_F(TestFECRebuilding, Prepare)
720 {
721 // Stuff in prepared packets into the source fec.
722 int32_t seq;
723 for (int i = 0; i < 7; ++i)
724 {
725 CPacket& p = *source[i].get();
726
727 // Feed it simultaneously into the sender FEC
728 fec->feedSource(p);
729 seq = p.getSeqNo();
730 }
731
732 SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
733
734 // Use the sequence number of the last packet, as usual.
735 bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
736
737 EXPECT_EQ(have_fec_ctl, true);
738 }
739
TEST_F(TestFECRebuilding,NoRebuild)740 TEST_F(TestFECRebuilding, NoRebuild)
741 {
742 // Stuff in prepared packets into the source fec.
743 int32_t seq;
744 for (int i = 0; i < 7; ++i)
745 {
746 CPacket& p = *source[i].get();
747
748 // Feed it simultaneously into the sender FEC
749 fec->feedSource(p);
750 seq = p.getSeqNo();
751 }
752
753 SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
754
755 // Use the sequence number of the last packet, as usual.
756 const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
757
758 ASSERT_EQ(have_fec_ctl, true);
759 // By having all packets and FEC CTL packet, now stuff in
760 // these packets into the receiver
761
762 FECFilterBuiltin::loss_seqs_t loss; // required as return, ignore
763
764 for (int i = 0; i < 7; ++i)
765 {
766 // SKIP packet 4 to simulate loss
767 if (i == 4 || i == 6)
768 continue;
769
770 // Stuff in the packet into the FEC filter
771 bool want_passthru = fec->receive(*source[i], loss);
772 EXPECT_EQ(want_passthru, true);
773 }
774
775 // Prepare a real packet basing on the SrtPacket.
776
777 // XXX Consider packing this into a callable function as this
778 // is a code directly copied from PacketFilter::packControlPacket.
779
780 unique_ptr<CPacket> fecpkt ( new CPacket );
781
782 uint32_t* chdr = fecpkt->getHeader();
783 memcpy(chdr, fec_ctl.hdr, SRT_PH_E_SIZE * sizeof(*chdr));
784
785 // The buffer can be assigned.
786 fecpkt->m_pcData = fec_ctl.buffer;
787 fecpkt->setLength(fec_ctl.length);
788
789 // This sets only the Packet Boundary flags, while all other things:
790 // - Order
791 // - Rexmit
792 // - Crypto
793 // - Message Number
794 // will be set to 0/false
795 fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
796
797 // ... and then fix only the Crypto flags
798 fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0));
799
800 // And now receive the FEC control packet
801
802 bool want_passthru_fec = fec->receive(*fecpkt, loss);
803 EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up
804 EXPECT_EQ(provided.size(), 0U); // Confirm that nothing was rebuilt
805
806 /*
807 // XXX With such a short sequence, losses will not be reported.
808 // You need at least one packet past the row, even in 1-row config.
809 // Probably a better way for loss collection should be devised.
810
811 ASSERT_EQ(loss.size(), 2);
812 EXPECT_EQ(loss[0].first, isn + 4);
813 EXPECT_EQ(loss[1].first, isn + 6);
814 */
815 }
816
TEST_F(TestFECRebuilding,Rebuild)817 TEST_F(TestFECRebuilding, Rebuild)
818 {
819 // Stuff in prepared packets into the source fec->
820 int32_t seq;
821 for (int i = 0; i < 7; ++i)
822 {
823 CPacket& p = *source[i].get();
824
825 // Feed it simultaneously into the sender FEC
826 fec->feedSource(p);
827 seq = p.getSeqNo();
828 }
829
830 SrtPacket fec_ctl(SRT_LIVE_MAX_PLSIZE);
831
832 // Use the sequence number of the last packet, as usual.
833 const bool have_fec_ctl = fec->packControlPacket(fec_ctl, seq);
834
835 ASSERT_EQ(have_fec_ctl, true);
836 // By having all packets and FEC CTL packet, now stuff in
837 // these packets into the receiver
838
839 FECFilterBuiltin::loss_seqs_t loss; // required as return, ignore
840
841 for (int i = 0; i < 7; ++i)
842 {
843 // SKIP packet 4 to simulate loss
844 if (i == 4)
845 continue;
846
847 // Stuff in the packet into the FEC filter
848 bool want_passthru = fec->receive(*source[i], loss);
849 EXPECT_EQ(want_passthru, true);
850 }
851
852 // Prepare a real packet basing on the SrtPacket.
853
854 // XXX Consider packing this into a callable function as this
855 // is a code directly copied from PacketFilter::packControlPacket.
856
857 unique_ptr<CPacket> fecpkt ( new CPacket );
858
859 uint32_t* chdr = fecpkt->getHeader();
860 memcpy(chdr, fec_ctl.hdr, SRT_PH_E_SIZE * sizeof(*chdr));
861
862 // The buffer can be assigned.
863 fecpkt->m_pcData = fec_ctl.buffer;
864 fecpkt->setLength(fec_ctl.length);
865
866 // This sets only the Packet Boundary flags, while all other things:
867 // - Order
868 // - Rexmit
869 // - Crypto
870 // - Message Number
871 // will be set to 0/false
872 fecpkt->m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
873
874 // ... and then fix only the Crypto flags
875 fecpkt->setMsgCryptoFlags(EncryptionKeySpec(0));
876
877 // And now receive the FEC control packet
878
879 const bool want_passthru_fec = fec->receive(*fecpkt, loss);
880 EXPECT_EQ(want_passthru_fec, false); // Confirm that it's been eaten up
881
882 EXPECT_EQ(loss.size(), 0U);
883 ASSERT_EQ(provided.size(), 1U);
884
885 SrtPacket& rebuilt = provided[0];
886 CPacket& skipped = *source[4];
887
888 // Set artificially the SN_REXMIT flag in the skipped source packet
889 // because the rebuilt packet shall have REXMIT flag set.
890 skipped.m_iMsgNo |= MSGNO_REXMIT::wrap(true);
891
892 // Compare the header
893 EXPECT_EQ(skipped.getHeader()[SRT_PH_SEQNO], rebuilt.hdr[SRT_PH_SEQNO]);
894 EXPECT_EQ(skipped.getHeader()[SRT_PH_MSGNO], rebuilt.hdr[SRT_PH_MSGNO]);
895 EXPECT_EQ(skipped.getHeader()[SRT_PH_ID], rebuilt.hdr[SRT_PH_ID]);
896 EXPECT_EQ(skipped.getHeader()[SRT_PH_TIMESTAMP], rebuilt.hdr[SRT_PH_TIMESTAMP]);
897
898 // Compare sizes and contents
899 ASSERT_EQ(skipped.size(), rebuilt.size());
900
901 EXPECT_EQ(memcmp(skipped.data(), rebuilt.data(), rebuilt.size()), 0);
902 }
903