@@ -614,15 +614,23 @@ def test_11_shaper(self):
614614
615615 for interface in self ._interfaces :
616616 shaper_name = f'qos-shaper-{ interface } '
617+ shaper_path = base_path + ['policy' , 'shaper' , shaper_name ]
618+ class_path = shaper_path + ['class' , '23' ]
617619
618620 self .cli_set (base_path + ['interface' , interface , 'egress' , shaper_name ])
619- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'bandwidth' , f'{ bandwidth } mbit' ])
620- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'default' , 'bandwidth' , f'{ default_bandwidth } mbit' ])
621- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'default' , 'ceiling' , f'{ default_ceil } mbit' ])
622- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'default' , 'queue-type' , 'fair-queue' ])
623- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'class' , '23' , 'bandwidth' , f'{ class_bandwidth } mbit' ])
624- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'class' , '23' , 'ceiling' , f'{ class_ceil } mbit' ])
625- self .cli_set (base_path + ['policy' , 'shaper' , shaper_name , 'class' , '23' , 'match' , '10' , 'ip' , 'destination' , 'address' , dst_address ])
621+ self .cli_set (shaper_path + ['bandwidth' , f'{ bandwidth } mbit' ])
622+ self .cli_set (
623+ shaper_path + ['default' , 'bandwidth' , f'{ default_bandwidth } mbit' ]
624+ )
625+ self .cli_set (shaper_path + ['default' , 'ceiling' , f'{ default_ceil } mbit' ])
626+ self .cli_set (shaper_path + ['default' , 'queue-type' , 'fair-queue' ])
627+ self .cli_set (shaper_path + ['default' , 'set-dscp' , 'AF11' ])
628+ self .cli_set (class_path + ['bandwidth' , f'{ class_bandwidth } mbit' ])
629+ self .cli_set (class_path + ['ceiling' , f'{ class_ceil } mbit' ])
630+ self .cli_set (
631+ class_path
632+ + ['match' , '10' , 'ip' , 'destination' , 'address' , dst_address ]
633+ )
626634
627635 bandwidth += 1
628636 default_bandwidth += 1
@@ -651,6 +659,16 @@ def test_11_shaper(self):
651659 for config_entry in config_entries :
652660 self .assertIn (config_entry , output )
653661
662+ # set-dscp on default class: catch-all filters with pedit
663+ # AF11 = DSCP 10 << 2 = 0x28
664+ filter_output = get_tc_filter_details (interface )
665+ self .assertIn ('pedit' , filter_output )
666+ self .assertIn ('protocol ip pref 255' , filter_output )
667+ self .assertIn ('at ipv4+0: val 00280000' , filter_output )
668+ self .assertIn ('csum (iph)' , filter_output )
669+ self .assertIn ('protocol ipv6 pref 256' , filter_output )
670+ self .assertIn ('at ipv6+0: val 02800000' , filter_output )
671+
654672 bandwidth += 1
655673 default_bandwidth += 1
656674 default_ceil += 1
@@ -1325,5 +1343,73 @@ def test_24_policy_shaper_match_ether(self):
13251343 get_tc_filter_details (interface ))
13261344
13271345
1346+ def test_25_shaper_set_dscp (self ):
1347+ interface = self ._interfaces [0 ]
1348+ shaper_name = f'qos-shaper-{ interface } '
1349+ shaper_path = base_path + ['policy' , 'shaper' , shaper_name ]
1350+ class_path = shaper_path + ['class' , '10' ]
1351+
1352+ self .cli_set (base_path + ['interface' , interface , 'egress' , shaper_name ])
1353+ self .cli_set (shaper_path + ['bandwidth' , '100mbit' ])
1354+ self .cli_set (shaper_path + ['default' , 'bandwidth' , '10mbit' ])
1355+ self .cli_set (class_path + ['bandwidth' , '50mbit' ])
1356+ self .cli_set (class_path + ['set-dscp' , 'CS4' ])
1357+
1358+ # IPv4 match: pedit should target ipv4+0
1359+ self .cli_set (
1360+ class_path + ['match' , 'RULE' , 'ip' , 'source' , 'address' , '172.17.1.2/32' ]
1361+ )
1362+ self .cli_commit ()
1363+
1364+ # CS4 = DSCP 32 << 2 = 0x80
1365+ filter_output = get_tc_filter_details (interface )
1366+ self .assertIn ('action order 1: pedit' , filter_output )
1367+ self .assertIn ('at ipv4+0: val 00800000' , filter_output )
1368+ self .assertIn ('csum (iph)' , filter_output )
1369+ self .assertNotIn ('at ipv6+0' , filter_output )
1370+
1371+ # IPv6 match: pedit should target ipv6+0
1372+ self .cli_delete (class_path + ['match' , 'RULE' ])
1373+ self .cli_set (
1374+ class_path + ['match' , 'RULE' , 'ipv6' , 'source' , 'address' , '2001:db8::/32' ]
1375+ )
1376+ self .cli_set (class_path + ['set-dscp' , 'AF21' ])
1377+ self .cli_commit ()
1378+
1379+ # AF21 = DSCP 18 << 2 = 0x48
1380+ filter_output = get_tc_filter_details (interface )
1381+ self .assertIn ('action order 1: pedit' , filter_output )
1382+ self .assertIn ('at ipv6+0: val 04800000' , filter_output )
1383+ self .assertNotIn ('at ipv4+0' , filter_output )
1384+
1385+ # Ether match with protocol all: pedit is skipped to avoid
1386+ # corrupting non-IP packets
1387+ self .cli_delete (class_path + ['match' , 'RULE' ])
1388+ self .cli_set (class_path + ['match' , 'RULE' , 'ether' , 'protocol' , 'all' ])
1389+ self .cli_set (class_path + ['set-dscp' , '46' ])
1390+ self .cli_commit ()
1391+
1392+ filter_output = get_tc_filter_details (interface )
1393+ self .assertNotIn ('pedit' , filter_output )
1394+
1395+ # Ether match with protocol ip: pedit targets ipv4+0 only
1396+ self .cli_set (class_path + ['match' , 'RULE' , 'ether' , 'protocol' , 'ip' ])
1397+ self .cli_commit ()
1398+
1399+ # numeric 46 << 2 = 0xb8
1400+ filter_output = get_tc_filter_details (interface )
1401+ self .assertIn ('action order 1: pedit' , filter_output )
1402+ self .assertIn ('at ipv4+0: val 00b80000' , filter_output )
1403+ self .assertIn ('csum (iph)' , filter_output )
1404+ self .assertNotIn ('at ipv6+0' , filter_output )
1405+
1406+ # Removing set-dscp: no pedit should remain
1407+ self .cli_delete (class_path + ['set-dscp' ])
1408+ self .cli_commit ()
1409+
1410+ filter_output = get_tc_filter_details (interface )
1411+ self .assertNotIn ('pedit' , filter_output )
1412+
1413+
13281414if __name__ == '__main__' :
13291415 unittest .main (verbosity = 2 , failfast = VyOSUnitTestSHIM .TestCase .debug_on ())
0 commit comments