# SPDX-License-Identifier: BSD-3-Clause
---
- name: Test for testing routing rules
  hosts: all
  vars:
    type: veth
    interface: ethtest0
  tasks:
    - name: Include the task 'show_interfaces.yml'
      include_tasks: tasks/show_interfaces.yml

    - name: Include the task 'manage_test_interface.yml'
      include_tasks: tasks/manage_test_interface.yml
      vars:
        state: present

    - name: Include the task 'assert_device_present.yml'
      include_tasks: tasks/assert_device_present.yml

    - name: Create a dedicated test file in `/etc/iproute2/rt_tables.d/` and
            add a new routing table
      lineinfile:
        path: /etc/iproute2/rt_tables.d/table.conf
        line: "200 custom"
        mode: "0644"
        create: true

    - name: Set __network_end_play variable
      set_fact:
        __network_end_play: false

    - name: Configure routes and routing rules with error handling
      block:
        - name: Configure connection profile and specify the numeric table in
            static routes
          include_role:
            name: linux-system-roles.network
          vars:
            network_connections:
              - name: "{{ interface }}"
                interface_name: "{{ interface }}"
                state: up
                type: ethernet
                autoconnect: true
                ip:
                  dhcp4: false
                  address:
                    - 198.51.100.3/26
                    - 2001:db8::2/32
                  route:
                    - network: 198.51.100.64
                      prefix: 26
                      gateway: 198.51.100.6
                      metric: 4
                      table: 30200
                    - network: 198.51.100.128
                      prefix: 26
                      gateway: 198.51.100.1
                      metric: 2
                      table: 30400
                    - network: 2001:db8::4
                      prefix: 32
                      gateway: 2001:db8::1
                      metric: 2
                      table: 30600
                  routing_rule:
                    - priority: 30200
                      from: 198.51.100.58/26
                      table: 30200
                    - priority: 30201
                      family: ipv4
                      fwmark: 1
                      fwmask: 1
                      table: 30200
                    - priority: 30202
                      family: ipv4
                      ipproto: 6
                      table: 30200
                    - priority: 30203
                      family: ipv4
                      sport: 128 - 256
                      table: 30200
                    - priority: 30204
                      family: ipv4
                      tos: 8
                      table: 30200
                    - priority: 30205
                      uid: 2000 - 3000
                      family: ipv4
                      table: 30200
                    - priority: 30206
                      suppress_prefixlength: 8
                      family: ipv4
                      table: 30200
                    - priority: 30400
                      to: 198.51.100.128/26
                      table: 30400
                    - priority: 30401
                      family: ipv4
                      iif: iiftest
                      table: 30400
                    - priority: 30402
                      family: ipv4
                      oif: oiftest
                      table: 30400
                    - priority: 30403
                      from: 0.0.0.0/0
                      to: 0.0.0.0/0
                      table: 30400
                    - priority: 30600
                      to: 2001:db8::4/32
                      table: 30600
                    - priority: 30601
                      family: ipv6
                      dport: 128 - 256
                      invert: true
                      table: 30600
                    - priority: 30602
                      from: ::/0
                      to: ::/0
                      table: 30600
                    - priority: 30603
                      uid: 6000 - 8000
                      family: ipv6
                      table: 30600
                    - priority: 30604
                      suppress_prefixlength: 24
                      family: ipv6
                      table: 30600
                    - priority: 200
                      from: 198.51.100.56/26
                      table: custom

      rescue:
        - name: Assert that the routing rule attribute 'suppress_prefixlength'
              validation failure is not raised when the distro's major version is 7
          assert:
            that:
              - __network_connections_result.stderr is search("the routing rule
                selector 'suppress_prefixlength' is not supported in NetworkManger
                until NM 1.20")
            msg: The routing rule attribute 'suppress_prefixlength' validation
              failure is not raised when the distro's major version is 7
          when: ansible_distribution_major_version == "7"

        - name: Clear errors
          meta: clear_host_errors

        - name: Reset __network_end_play variable
          set_fact:
            __network_end_play: true

        - name: Force playbook end earlier after rescue
          meta: end_play

    # the routing rule selector sport and ipproto are not supported by iproute
    # since v4.17.0, and the iproute installed in CentOS-7 and RHEL-7 is
    # v4.11.0
    - name: Get the routing rule for looking up the table 30200
      command: ip rule list table 30200
      register: route_rule_table_30200
      ignore_errors: true
      changed_when: false
      when: ansible_distribution_major_version != "7"

    - name: Get the routing rule for looking up the table 30400
      command: ip rule list table 30400
      register: route_rule_table_30400
      ignore_errors: true
      changed_when: false
      when: ansible_distribution_major_version != "7"

    - name: Get the routing rule for looking up the table 30600
      command: ip -6 rule list table 30600
      register: route_rule_table_30600
      ignore_errors: true
      changed_when: false
      when: ansible_distribution_major_version != "7"

    - name: Get the routing rule for looking up the table 'custom'
      command: ip rule list table custom
      register: route_rule_table_custom
      ignore_errors: true
      changed_when: false
      when: ansible_distribution_major_version != "7"

    - name: Get the IPv4 routing rule for the connection "{{ interface }}"
      command: nmcli -f ipv4.routing-rules c show "{{ interface }}"
      register: connection_route_rule
      ignore_errors: true
      changed_when: false

    - name: Get the IPv6 routing rule for the connection "{{ interface }}"
      command: nmcli -f ipv6.routing-rules c show "{{ interface }}"
      register: connection_route_rule6
      ignore_errors: true
      changed_when: false

    - name: Assert that the routing rule with table lookup 30200 matches the
        specified rule
      assert:
        that:
          - route_rule_table_30200.stdout is search("30200:(\s+)from
            198.51.100.58/26 lookup 30200")
          - route_rule_table_30200.stdout is search("30201:(\s+)from all fwmark
            0x1/0x1 lookup 30200")
          - route_rule_table_30200.stdout is search("30202:(\s+)from all
            ipproto tcp lookup 30200")
          - route_rule_table_30200.stdout is search("30203:(\s+)from all sport
            128-256 lookup 30200")
          - route_rule_table_30200.stdout is search("30204:(\s+)from all tos
            (0x08|throughput) lookup 30200")
          - route_rule_table_30200.stdout is search("30205:(\s+)from all
            uidrange 2000-3000 lookup 30200")
          - route_rule_table_30200.stdout is search("30206:(\s+)from all lookup
            30200 suppress_prefixlength 8")
        msg: "the routing rule with table lookup 30200 does not match the
          specified rule"
      when: ansible_distribution_major_version != "7"

    - name: Assert that the routing rule with table lookup 30400 matches the
        specified rule
      assert:
        that:
          - route_rule_table_30400.stdout is search("30400:(\s+)from all to
            198.51.100.128/26 lookup 30400")
          - route_rule_table_30400.stdout is search("30401:(\s+)from all iif
            iiftest \[detached\] lookup 30400")
          - route_rule_table_30400.stdout is search("30402:(\s+)from all oif
            oiftest \[detached\] lookup 30400")
        msg: "the routing rule with table lookup 30400 does not match the
          specified rule"
      when: ansible_distribution_major_version != "7"

    - name: Assert that the routing rule with table lookup 30600 matches the
        specified rule
      assert:
        that:
          - route_rule_table_30600.stdout is search("30600:(\s+)from all to
            2001:db8::4/32 lookup 30600")
          - route_rule_table_30600.stdout is search("30601:(\s+)not from all
            dport 128-256 lookup 30600")
          - route_rule_table_30600.stdout is search("30603:(\s+)from all
            uidrange 6000-8000 lookup 30600")
          - route_rule_table_30600.stdout is search("30604:(\s+)from all
            lookup 30600 suppress_prefixlength 24")
        msg: "the routing rule with table lookup 30600 does not match the
          specified rule"
      when: ansible_distribution_major_version != "7"

    - name: Assert that the routing rule with 'custom' table lookup matches the
        specified rule
      assert:
        that:
          - route_rule_table_custom.stdout is search("200:(\s+)from
            198.51.100.56/26 lookup custom")
        msg: "the routing rule with 'custom' table lookup does not match the
          specified rule"
      when: ansible_distribution_major_version != "7"

    - name: Assert that the specified IPv4 routing rule was configured in the
        connection "{{ interface }}"
      assert:
        that:
          - connection_route_rule.stdout is search("priority 30200 from
            198.51.100.58/26 table 30200")
          - connection_route_rule.stdout is search("priority 30201 from
            0.0.0.0/0 fwmark 0x1/0x1 table 30200")
          - connection_route_rule.stdout is search("priority 30202 from
            0.0.0.0/0 ipproto 6 table 30200")
          - connection_route_rule.stdout is search("priority 30203 from
            0.0.0.0/0 sport 128-256 table 30200")
          - connection_route_rule.stdout is search("priority 30204 from
            0.0.0.0/0 tos 0x08 table 30200")
          - connection_route_rule.stdout is search("priority 30205 from
            0.0.0.0/0 uidrange 2000-3000 table 30200")
          - connection_route_rule.stdout is search("priority 30206 from
            0.0.0.0/0 suppress_prefixlength 8 table 30200")
          - connection_route_rule.stdout is search("priority 30400 to
            198.51.100.128/26 table 30400")
          - connection_route_rule.stdout is search("priority 30401 from
            0.0.0.0/0 iif iiftest table 30400")
          - connection_route_rule.stdout is search("priority 30402 from
            0.0.0.0/0 oif oiftest table 30400")
          - connection_route_rule.stdout is search("priority 30403 from
            0.0.0.0/0 table 30400")
          - connection_route_rule.stdout is search("priority 200 from
            198.51.100.56/26 table 200")
        msg: "the specified IPv4 routing rule was not configured in the
          connection '{{ interface }}'"

    - name: Assert that the specified IPv6 routing rule was configured in the
        connection "{{ interface }}"
      assert:
        that:
          - connection_route_rule6.stdout is search("priority 30600 to
            2001:db8::4/32 table 30600")
          - connection_route_rule6.stdout is search("priority 30601 not from
            ::/0 dport 128-256 table 30600") or
            connection_route_rule6.stdout is search("not priority 30601 from
            ::/0 dport 128-256 table 30600")
          - connection_route_rule6.stdout is search("priority 30602 from
            ::/0 table 30600")
          - connection_route_rule6.stdout is search("priority 30603 from
            ::/0 uidrange 6000-8000 table 30600")
          - connection_route_rule6.stdout is search("priority 30604 from
            ::/0 suppress_prefixlength 24 table 30600")
        msg: "the specified IPv6 routing rule was not configured in the
          connection '{{ interface }}'"

    - name: Remove the dedicated test file in `/etc/iproute2/rt_tables.d/`
      file:
        state: absent
        path: /etc/iproute2/rt_tables.d/table.conf

    - name: Include the task 'remove+down_profile.yml'
      include_tasks: tasks/remove+down_profile.yml
      vars:
        profile: "{{ interface }}"
      when: not __network_end_play | d(false)

    - name: Include the task 'delete_interface.yml'
      include_tasks: tasks/delete_interface.yml

    - name: Include the task 'assert_profile_absent.yml'
      include_tasks: tasks/assert_profile_absent.yml
      vars:
        profile: "{{ interface }}"

    - name: Include the task 'assert_device_absent.yml'
      include_tasks: tasks/assert_device_absent.yml

    - name: Verify network state restored to default
      include_tasks: tasks/check_network_dns.yml
