<?php

class ConfigRouterOSFirewallMangle extends NetworkConfigItem
{
  function ProcessNode($Node)
  {
    global $InetInterface, $ItemsFirewall;

    foreach($Node['Items'] as $Index => $Item)
    {
      if(count($Item['Items']) == 0)
      {
        // Hosts
        $ParentSubnetId = GetSubgroupByRange($Node['Address']->AddressToString().'/'.$Node['Address']->Prefix);
        $Address = $Item['Address']->AddressToString();
        if($Item['Address']->Prefix != 32) $Address .= '/'.$Item['Address']->Prefix;

        $PacketMark = GetMarkByComment($Item['Name'].'-out');
        $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-out', 'src-address' => $Address, 'out-interface' =>  $InetInterface, 'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'passthrough' => 'no', 'comment' => $Item['Name'].'-out');
        $PacketMark = GetMarkByComment($Item['Name'].'-in');
        $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-in', 'dst-address' => $Address, 'in-interface' => $InetInterface, 'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'passthrough' => 'no', 'comment' => $Item['Name'].'-in');
      } else
      {
        // Subnets
        $ParentSubnetId = GetSubgroupByRange($Node['Address']->AddressToString().'/'.$Node['Address']->Prefix);
        $SubnetId = GetSubgroupByRange($Item['Address']->AddressToString().'/'.$Item['Address']->Prefix);
        $PacketMark = GetMarkByComment($Item['Name'].'-out');

        $Address = $Item['Address']->AddressToString();
        if($Item['Address']->Prefix != 32) $Address .= '/'.$Item['Address']->Prefix;

        $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-out', 'src-address' => $Address, 'out-interface' => $InetInterface, 'action' => 'jump', 'jump-target' => 'inet-'.$SubnetId.'-out', 'comment' => $Item['Name'].'-out');
        $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-in', 'dst-address' => $Address, 'in-interface' => $InetInterface, 'action' => 'jump', 'jump-target' => 'inet-'.$SubnetId.'-in', 'comment' => $Item['Name'].'-in');

        $this->ProcessNode($Item);
      }
    }
    if($Node['ForceMark'] == true)
    {
      // Mark member subnets
      $ParentSubnetId = GetSubgroupByRange($Node['Address']->AddressToString().'/'.$Node['Address']->Prefix);
      $PacketMark = GetMarkByComment($Node['Name'].'-out');
      $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-out', 'src-address' => '', 'out-interface' =>  $InetInterface, 'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'passthrough' => 'no', 'comment' => $Node['Name'].'-all-out');
      $PacketMark = GetMarkByComment($Node['Name'].'-in');
      $ItemsFirewall[] = array('chain' => 'inet-'.$ParentSubnetId.'-in', 'dst-address' => '', 'in-interface' => $InetInterface, 'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'passthrough' => 'no', 'comment' => $Node['Name'].'-all-in');
    }
  }

  function Run()
  {
    global $ItemsFirewall;
    
    $PathFirewall = array('ip', 'firewall', 'mangle');

    $Routerboard = new Routerboard();
    $Routerboard->UserName = $this->System->Config['MainRouter']['UserName'];
    $Routerboard->Timeout = $this->System->Config['MainRouter']['ConnectTimeout'];
    $Routerboard->HostName = $this->System->Config['MainRouter']['HostName'];
    $Routerboard->Debug = true;

    $InetInterface = $this->System->Config['MainRouter']['InetInterface'];


    // Generate address tree
    $AddressTree = array('Address' => new NetworkAddressIPv4(), 'Name' => 'main', 'Items' => array(), 'ForceMark' => false);

    // Divide rules by subnet number
    $DbResult = $this->System->Database->query('SELECT `Id`, `Name`, `AddressRange`, `Mask` FROM `NetworkSubnet` WHERE `Member` IS NULL');
    while($Subnet = $DbResult->fetch_assoc())
    {
      $NewAddress = new NetworkAddressIPv4();
      $NewAddress->AddressFromString($Subnet['AddressRange']);
      $NewAddress->Prefix = $Subnet['Mask'];
      InsertToAddressTree($AddressTree, $NewAddress, 'subnet-'.RouterOSIdent($Subnet['Name']));
    }

    // Process users
    $DbResult = $this->System->Database->query('SELECT `Member`.*, `Subject`.`Name` FROM `Member` '.
        'LEFT JOIN `Subject` ON `Subject`.`Id` = `Member`.`Subject` '.
        'WHERE `Member`.`Blocked` = 0');
    while($Member = $DbResult->fetch_assoc())
    {
      $Member['Name'] = RouterOSIdent($Member['Name'].'-'.$Member['Id'] );
      echo('Uživatel '.$Member['Name'].': ');

      $DbResult2 = $this->System->Database->select('NetworkDevice', '*', '`Used` = 1 AND `Member` = '.$Member['Id']);
      while($Device = $DbResult2->fetch_assoc())
      {
        $DbResult3 = $this->Database->select('NetworkInterface', '*', '`Device` = '.$Device['Id'].' AND `LocalIP` != ""');
        while($Interface = $DbResult3->fetch_assoc())
        {
          $Name = $Device['Name'];
          if($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
          $Name = RouterOSIdent($Name);
          echo($Name.', ');
          $NewAddress = new NetworkAddressIPv4();
          $NewAddress->AddressFromString($Interface['LocalIP']);
          $NewAddress->Prefix = 32;
          InsertToAddressTree($AddressTree, $NewAddress, $Name);
        }
      }

      $DbResult2 = $this->Database->select('NetworkSubnet', '*', '`Member`='.$Member['Id']);
      while($Subnet = $DbResult2->fetch_assoc())
      {
        $Subnet['Name'] = RouterOSIdent('subnet-'.$Subnet['Name']);
        echo($Subnet['Name'].', ');
        $NewAddress = new NetworkAddressIPv4();
        $NewAddress->AddressFromString($Subnet['AddressRange']);
        $NewAddress->Prefix = $Subnet['Mask'];
        if($Subnet['Member'] != 0) $ForceMark = true;
        else $ForceMark = false;
        echo($ForceMark.', ');
        InsertToAddressTree($AddressTree, $NewAddress, $Subnet['Name'], false, $ForceMark);
      }
      echo("\n");
    }

    ShowSubnetNode($AddressTree);

    // Generate firewall rules
    $ItemsFirewall = array();

    // Root of tree and main limit
    $ItemsFirewall[] = array('chain' => 'forward', 'out-interface' => $InetInterface, 'dst-address' => '!77.92.221.0/24', 'action' => 'jump', 'jump-target' => 'inet-1-out', 'comment' => 'main-out');
    $ItemsFirewall[] = array('chain' => 'forward', 'in-interface' => $InetInterface, 'src-address' => '!77.92.221.0/24', 'action' => 'jump', 'jump-target' => 'inet-1-in', 'comment' => 'main-in');

    $this->ProcessNode($AddressTree);

    // Limited free internet
    $PacketMark = GetMarkByComment('free-out');
    $ItemsFirewall[] = array('chain' => 'inet-1-out', 'out-interface' => $InetInterface,
        'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'comment' => 'free-out', 'passthrough' => 'yes');
    $PacketMark = GetMarkByComment('free-in');
    $ItemsFirewall[] = array('chain' => 'inet-1-in', 'in-interface' => $InetInterface,
        'action' => 'mark-packet', 'new-packet-mark' => $PacketMark, 'comment' => 'free-in', 'passthrough' => 'no');
    // Unregistred clients add to address list
    $ItemsFirewall[] = array('chain' => 'inet-1-out', 'out-interface' => $InetInterface, 'src-address' => '10.145.0.0/16',
        'action' => 'add-src-to-address-list', 'address-list' => 'unregistred', 'address-list-timeout' => '1d',
        'comment' => 'unregistred-clients');

    //print_r($ItemsFirewall);
    $Routerboard->ListUpdate($PathFirewall, array('chain', 'dst-address', 'in-interface', 'action', 'new-packet-mark', 'passthrough', 'comment', 'out-interface', 'src-address', 'jump-target'), $ItemsFirewall, array(), true);
  }
}
