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