diff --git a/config/cron b/config/cron index e6b05d1..de44e92 100644 --- a/config/cron +++ b/config/cron @@ -1,4 +1,8 @@ -*/15 * * * * fping --count 3 --period 2000 --random --generate --quiet {{ network }} >/dev/null 2>&1 +{% for _, network in networks.items() %} +{% if network.cidr is defined %} +*/15 * * * * fping --count 3 --period 2000 --random --generate --quiet {{ network.cidr }} >/dev/null 2>&1 +{% endif %} +{% endfor %} {% if inventory_hostname in groups['routers'] %} 0 4 * * * nft flush chain inet fw4 upnp_forward && nft flush chain inet fw4 upnp_prerouting {% endif %} diff --git a/config/dawn b/config/dawn index d78e994..9c9a79c 100644 --- a/config/dawn +++ b/config/dawn @@ -1,16 +1,20 @@ +{% for _, network in networks.items() %} +{% if network.wireless is defined and network.wireless[0].roaming | default(true) %} config network - option broadcast_ip '{{ network | ansible.utils.ipaddr(255) | ansible.utils.ipv4('address')}}' + option broadcast_ip '{{ network.cidr | ansible.utils.ipaddr(255) | ansible.utils.ipv4('address')}}' option broadcast_port '1025' option server_ip '' option tcp_port '1026' option network_option '2' # 0 udp broadcast, 1 multicast, 2 tcp - option shared_key '{{ (SSID | checksum)[:10] }}' - option iv '{{ (SSID | checksum)[:10] }}' + option shared_key '{{ (network.wireless[0].SSID | checksum)[:10] }}' + option iv '{{ (network.wireless[0].SSID | checksum)[:10] }}' option use_symm_enc '0' option collision_domain '-1' # enter here aps which are in the same collision domain option bandwidth '-1' # enter network bandwidth +{% endif %} +{% endfor %} config ordering option sort_order 'cbfs' diff --git a/config/dhcp b/config/dhcp index 30b6934..90172f6 100644 --- a/config/dhcp +++ b/config/dhcp @@ -17,8 +17,10 @@ config dnsmasq {% endfor %} {% endif %} -config dhcp 'lan' - option interface 'lan' +{% for _, network in networks.items() %} +{% if _ not in ('wan','additional') %} +config dhcp '{{ _ }}' + option interface '{{ _ }}' option limit '150' option leasetime '12h' option start '{{ groups['openwrt'] | length +2 }}' @@ -26,6 +28,8 @@ config dhcp 'lan' option dhcpv6 '{{ 'server' if inventory_hostname in groups['routers'] else 'hybrid' }}' option ra_management '1' +{% endif %} +{% endfor %} config dhcp 'wan' option interface 'wan' option ignore '1' @@ -41,7 +45,7 @@ config odhcpd 'odhcpd' config host option mac '{{ hostvars[ap].mac }}' option dns '1' - option ip '{{ network | ansible.utils.ipaddr(hostvars[ap].id) | ansible.utils.ipv4('address')}}' + option ip '{{ networks.lan.cidr | ansible.utils.ipaddr(hostvars[ap].id) | ansible.utils.ipv4('address')}}' option name '{{ hostvars[ap].name }}' option hostid '{{ hostvars[ap].id }}' option duid '{{ hostvars[ap].duid }}' @@ -53,7 +57,7 @@ config host option name '{{ host.name }}' option mac '{{ host.mac }}' option dns '1' - option ip '{{ network | ansible.utils.ipaddr(host.id) | ansible.utils.ipv4('address')}}' + option ip '{{ networks[host.network | default('lan')].cidr | ansible.utils.ipaddr(host.id) | ansible.utils.ipv4('address')}}' option hostid '{{ host.id }}' option duid '{{ host.duid }}' diff --git a/config/firewall b/config/firewall index 0ebca3a..504f98d 100644 --- a/config/firewall +++ b/config/firewall @@ -155,11 +155,47 @@ config redirect option dest 'lan' option dest_port '{{ forward.port }}' option target 'DNAT' - option dest_ip '{{ network | ansible.utils.ipaddr(forward.to) | ansible.utils.ipv4('address')}}' + option dest_ip '{{ networks[forward.network | default('lan')].cidr | ansible.utils.ipaddr(forward.to) | ansible.utils.ipv4('address')}}' option proto 'tcp udp' {% if forward.source_ip is defined %} option src_ip '{{ forward.source_ip }}' {% endif %} +{% endfor %} +{% endif %} + +{% for _, network in networks.items() %} +{% if _ not in ('lan','wan', 'additional') %} +config zone + option name '{{ _ }}' + option input 'ACCEPT' + option output 'ACCEPT' + option forward 'REJECT' + list network '{{ _ }}' + +config forwarding + option src '{{ _ }}' + option dest 'wan' + +config forwarding + option src 'lan' + option dest '{{ _ }}' + +config rule + option name 'Allow-DNS-{{ _ }}' + option src '{{ _ }}' + option dest_port '53' + option target 'ACCEPT' +config rule + option name 'Allow-DHCP-{{ _ }}' + option src '{{ _ }}' + option dest_port '67' + option target 'ACCEPT' + +config rule + option name 'Disallow-Device-{{ _ }}' + option src '{{ _ }}' + option target 'REJECT' +{% endif %} {% endfor %} -{% endif %} \ No newline at end of file + diff --git a/config/network b/config/network index 4548633..bae07b4 100644 --- a/config/network +++ b/config/network @@ -1,3 +1,4 @@ +{% set vlan = namespace(index=1) %} config interface 'loopback' option device 'lo' @@ -8,14 +9,6 @@ config interface 'loopback' config globals 'globals' option ula_prefix '{{ ula_prefix }}' -config device -{% if vlans is defined %} - option name 'eth0.{{ vlans | community.general.json_query('[?wan].vid') | first }}' -{% else %} - option name 'eth0.2' -{% endif %} - option macaddr '{{ mac }}' - config switch option name 'switch0' option reset '1' @@ -24,60 +17,45 @@ config switch config device option name 'br-lan' option type 'bridge' - list ports 'eth0.1' + list ports 'eth0.{{ networks.lan.vlan_id | default('1') }}' + +config switch_vlan + option device 'switch0' + option vlan '{{ vlan.index }}' + option vid '{{ networks.lan.vlan_id | default('1') }}' + option ports '{{ networks.lan.ports | default('1 0t') }}' + option description '{{ networks.lan.name | default('lan') }}' {% if inventory_hostname in groups['routers'] %} +config device + option name 'eth0.{{ networks.wan.vlan_id | default('2') }}' + config interface 'lan' option device 'br-lan' option proto 'static' - option netmask '{{ network | ansible.utils.ipaddr('netmask') }}' - option ip6assign '60' - option ipaddr '{{ network | ansible.utils.ipaddr(id) | ansible.utils.ipv4('address')}}' + option netmask '{{ networks.lan.cidr | ansible.utils.ipaddr('netmask') }}' + option ip6assign '64' + option ip6hint '{{ networks.lan.ip6hint | default('0') }}' + option ipaddr '{{ networks.lan.cidr | ansible.utils.ipaddr(id) | ansible.utils.ipv4('address')}}' option ip6ifaceid '::{{ id }}' +{% set vlan.index = vlan.index + 1%} config interface 'wan' -{% if vlans is defined %} - option device 'eth0.{{ vlans | community.general.json_query('[?wan].vid') | first }}' -{% else %} - option device 'eth0.2' -{% endif %} - option proto 'dhcp' + option device 'eth0.{{ networks.wan.vlan_id | default('2') }}' + option proto 'dhcp' config interface 'wan6' -{% if vlans is defined %} - option device 'eth0.{{ vlans | community.general.json_query('[?wan].vid') | first }}' -{% else %} - option device 'eth0.2' -{% endif %} - option proto 'dhcpv6' - option reqaddress 'try' - option reqprefix 'auto' - -config switch_vlan - option device 'switch0' - option vlan '1' - option ports '0t 2 3 4 5' - option vid '1' - -{% if vlans is defined %} -{% for vlan in vlans %} -config switch_vlan - option device 'switch0' - option vlan '{{ loop.index+1 }}' - option ports '{{ vlan.ports }}' - option vid '{{ vlan.vid }}' -{% if vlan.name is defined %} - option description '{{ vlan.name }}' -{% endif %} + option device 'eth0.{{ networks.wan.vlan_id | default('2') }}' + option proto 'dhcpv6' + option reqaddress 'try' + option reqprefix 'auto' -{% endfor %} -{% else %} config switch_vlan - option device 'switch0' - option vlan '2' - option ports '0t 1' - option vid '2' -{% endif %} + option device 'switch0' + option vlan '{{ vlan.index }}' + option ports '{{ networks.wan.ports | default('2 3 4 5 0t') }}' + option vid '{{ networks.wan.vlan_id | default('2') }}' + option description '{{ networks.wan.name | default('wan') }}' {% if ipv6_6to4 is defined %} config interface 'wan6to4' @@ -106,17 +84,53 @@ config interface 'lan' option device 'br-lan' option proto 'dhcp' -config switch_vlan - option device 'switch0' - option vlan '1' - option ports '0t 1 2 3 4 5' - option vid '1' - config interface 'lan6' option proto 'dhcpv6' option reqaddress 'try' option reqprefix 'no' option device '@lan' option type 'bridge' +{% endif %} + +{% for _, network in networks.items() %} +{% if _ not in ('lan', 'wan','additional') %} +{% set vlan.index = vlan.index + 1%} +config device + option name 'br-{{ _ }}' + option type 'bridge' + list ports 'eth0.{{ network.vlan_id }}' + +config interface '{{ _ }}' + option device 'br-{{ _ }}' +{% if inventory_hostname in groups['routers'] %} + option proto 'static' + option netmask '{{ network.cidr | ansible.utils.ipaddr('netmask') }}' + option ip6assign '64' + option ip6hint '{{ network.ip6hint | default(loop.index*10) }}' + option ipaddr '{{ network.cidr | ansible.utils.ipaddr(id) | ansible.utils.ipv4('address')}}' + option ip6ifaceid '::{{ id }}' +{% else %} + option proto 'none' +{% endif %} + +config switch_vlan + option device 'switch0' + option vlan '{{ vlan.index }}' + option vid '{{ network.vlan_id }}' + option ports '{{ network.ports | default('2 3 4 5 0t') }}' + option description '{{ network.name }}' + +{% endif %} +{% endfor %} +{% if networks.additional is defined %} +{% for network in networks.additional %} +{% set vlan.index = vlan.index + 1%} +config switch_vlan + option device 'switch0' + option vlan '{{ vlan.index }}' + option ports '{{ network.ports }}' + option vid '{{ network.vlan_id }}' + option description '{{ network.name }}' +{% endfor %} {% endif %} diff --git a/config/upnpd b/config/upnpd index d49d07e..2b754e8 100644 --- a/config/upnpd +++ b/config/upnpd @@ -11,7 +11,7 @@ config upnpd 'config' {% if upnp.configure_with_external_ip is defined and upnp.configure_with_external_ip %} option external_ip '{{ external_ip }}' {% endif %} - option presentation_url 'http://{{ network | ansible.utils.ipaddr(id) | ansible.utils.ipv4('address')}}' + option presentation_url 'http://{{ networks.lan.cidr | ansible.utils.ipaddr(id) | ansible.utils.ipv4('address')}}' config perm_rule option action 'allow' diff --git a/config/wireless b/config/wireless index c7a9602..b7752f4 100644 --- a/config/wireless +++ b/config/wireless @@ -1,3 +1,4 @@ +{% set wifi = namespace(index=0) %} config wifi-device 'radio0' option type 'mac80211' @@ -17,37 +18,21 @@ config wifi-device 'radio1' option country 'NL' option cell_density '0' -config wifi-iface 'default_radio0' +{% for _, network in networks.items() %} +{% set parent_loop_index0 = loop.index0 %} +{% if network.wireless is defined %} +{% for wireless in network.wireless %} +config wifi-iface 'wifi{{ parent_loop_index0 }}_radio0' option device 'radio0' - option network 'lan' + option network '{{ _ }}' option mode 'ap' - option key '{{ passphrase }}' - option encryption '{{ encryption }}' - option ssid '{{ SSID }}' - option ft_over_ds '1' - option ft_psk_generate_local '1' - option ieee80211r '1' - option bss_transition '1' - option wnm_sleep_mode '1' - option time_advertisement '2' - option time_zone 'Europe/Amsterdam' - option ieee80211k '1' - option rrm_neighbor_report '1' - option rrm_beacon_report '1' - option macfilter 'deny' -{% if blocked_mac_addresses_on_wifi is defined %} -{% for macaddress in blocked_mac_addresses_on_wifi %} - list maclist '{{ macaddress }}' -{% endfor %} + option key '{{ wireless.passphrase }}' + option encryption '{{ wireless.encryption | default('psk2') }}' + option ssid '{{ wireless.SSID }}' +{% if wireless.client_isolation is defined and wireless.client_isolation %} + option isolate '1' {% endif %} - -config wifi-iface 'default_radio1' - option device 'radio1' - option network 'lan' - option mode 'ap' - option key '{{ passphrase }}' - option encryption '{{ encryption }}' - option ssid '{{ SSID }}' +{% if wireless.roaming | default(true) %} option ft_over_ds '1' option ft_psk_generate_local '1' option ieee80211r '1' @@ -56,70 +41,35 @@ config wifi-iface 'default_radio1' option time_advertisement '2' option time_zone 'Europe/Amsterdam' option ieee80211k '1' - option rrm_neighbor_report '1' - option rrm_beacon_report '1' - option macfilter 'deny' -{% if blocked_mac_addresses_on_wifi is defined %} -{% for macaddress in blocked_mac_addresses_on_wifi %} - list maclist '{{ macaddress }}' -{% endfor %} -{% endif %} - -{% if additional_wireless_networks is defined %} -{% for wireless_network in additional_wireless_networks %} - -config wifi-iface 'wifi{{ loop.index }}_radio0' - option device 'radio0' - option network 'lan' - option mode 'ap' - option key '{{ wireless_network.passphrase }}' - option encryption '{{ wireless_network.encryption | default('psk2') }}' - option ssid '{{ wireless_network.SSID }}' - option time_zone 'Europe/Amsterdam' -{% if wireless_network.roaming | default(true) %} - option ft_over_ds '1' - option ft_psk_generate_local '1' - option ieee80211r '1' - option bss_transition '1' - option wnm_sleep_mode '1' - option time_advertisement '2' - option ieee80211k '1' - option rrm_neighbor_report '1' - option rrm_beacon_report '1' {% endif %} option macfilter 'deny' -{% if wireless_network.client_isolation is defined and wireless_network.client_isolation %} - option isolate '1' -{% endif %} {% if blocked_mac_addresses_on_wifi is defined %} {% for macaddress in blocked_mac_addresses_on_wifi %} list maclist '{{ macaddress }}' {% endfor %} {% endif %} -config wifi-iface 'wifi{{ loop.index }}_radio1' +config wifi-iface 'wifi{{ parent_loop_index0 }}_radio1' option device 'radio1' - option network 'lan' + option network '{{ _ }}' option mode 'ap' - option key '{{ wireless_network.passphrase }}' - option encryption '{{ wireless_network.encryption | default('psk2') }}' - option ssid '{{ wireless_network.SSID }}' - option time_zone 'Europe/Amsterdam' -{% if wireless_network.roaming | default(true) %} + option key '{{ wireless.passphrase }}' + option encryption '{{ wireless.encryption | default('psk2') }}' + option ssid '{{ wireless.SSID }}' +{% if wireless.client_isolation is defined and wireless.client_isolation %} + option isolate '1' +{% endif %} +{% if wireless.roaming | default(true) %} option ft_over_ds '1' option ft_psk_generate_local '1' option ieee80211r '1' option bss_transition '1' option wnm_sleep_mode '1' option time_advertisement '2' + option time_zone 'Europe/Amsterdam' option ieee80211k '1' - option rrm_neighbor_report '1' - option rrm_beacon_report '1' {% endif %} option macfilter 'deny' -{% if wireless_network.client_isolation is defined and wireless_network.client_isolation %} - option isolate '1' -{% endif %} {% if blocked_mac_addresses_on_wifi is defined %} {% for macaddress in blocked_mac_addresses_on_wifi %} list maclist '{{ macaddress }}' @@ -128,3 +78,4 @@ config wifi-iface 'wifi{{ loop.index }}_radio1' {% endfor %} {% endif %} +{% endfor %} diff --git a/inventory-sample.yaml b/inventory-sample.yaml index a358814..7cb2c76 100644 --- a/inventory-sample.yaml +++ b/inventory-sample.yaml @@ -16,7 +16,7 @@ openwrt: # if the complete network contains different models model: archer-c7-v5 # MAC address of the port that connect upstream. For the router this - # is the port that is connected to the mode. + # is the port that is connected to the modem. mac: DD:73:8A:96:87:61 # HTMode for the wireless radio on the 2.4GHz band radio2_htmode: HT20 @@ -41,6 +41,23 @@ openwrt: # Useful if a specific local version is installed pin: - miniupnpd + # Network settiongs that are specific to this device. This dict will be merged + # with openwrt.vars.networks into one dict. + host_networks: + lan: + # Specific ports used for vlan tagging on this device + ports: 0t 2 3 5t + iot: + ports: 0t 4 5t + # additional has a special meaning and can be used to define any additional + # VLANs that aren't turned into full networks + additional: + - name: Management + vlan_id: 100 + ports: 0t 1t + - name: Television + vlan_id: 640 + ports: 0t 1t # All the access points. There can be 0 or more access points aps: hosts: @@ -104,30 +121,60 @@ openwrt: # to set packages on a group level that should be removed. remove: - wpad-basic-mbedtls - # Network address, this would allow address from 10.0.0.1 - 10.0.0.255 - network: 10.0.0.0/24 # Local domain name local_domain: home.arpa # Local IPv6 addresses ula_prefix: fd54:28cd:b1c3::/48 - # Name of the wireless network - SSID: mylan - # Wireless password - passphrase: mypassword + # Define the networks and wireless networks. This creates networks, + # firewall zones, and wireless networks. Two networks have a special + # meaning: + # 'wan': this is the network that connects upstream, either to the + # modem, or directly to a fibre connection. + # 'lan': the main internal network. Access points are connected to the + # lan network. Any other network are accessible from the lan, but + # other networks cannot connect to clients in the lan. + # Any other networks defined, such as an iot or guest network will + # be turned in separate networks and firewall zones that are isolated + # from other networks. + # Only lan.cidr is required + common_networks: + wan: + # A name for the network, just as VLAN name + name: Internet + # The VLAN id, will result in an eth0.5 network + vlan_id: 5 + # VLAN port tagging + ports: 0t 1t + lan: + name: lan + vlan_id: 10 + # The IPv4 CIDR range for this network + cidr: 10.0.0.0/24 + # Each network becomes a /64 network, and all clients in the network + # will get IPv6 addresses in the range of {{ula_prefix}}:{{ipv6hint}}::/64 + ip6hint: 0 + # The wireless network for this networks/firewall zone + wireless: + - SSID: TMNL-1B5481 + # wifi password + passphrase: M7SSWP7BM4C7KVMR + # wifi encryption method + encryption: psk2 + iot: + name: iot + vlan_id: 20 + cidr: 10.0.1.0/24 + ip6hint: 10 + wireless: + - SSID: TMNL-1B5481_iot + passphrase: A6WK4XQZ632NG5HP + encryption: psk2 + # Enable client isolation, defaults to false + client_isolation: true + # Disable 802.11r/v/k roaming, default to true + roaming: false # SSH password of the root user ssh_password: rootpassword - # Type of encryption, like 'psk2' (WPA2) or 'sae-mixed' (WPA2/WPA3) - encryption: sea-mixed - # Optional additional wireless networks, such as a guest or IoT network - additional_wireless_networks: - - SSID: my-lan-guest - passphrase: mypassword - # Optionally enable client isolation for guest/IoT network - client_isolation: true - # Optionally set encryption, defaults to psk2 (WPA2) - encryption: psk2 - # Optionally disable advanced roaming featured (802.11r/k/v) (default is true to enable) - roaming: false # Physical address of the wireless radios. Move lower if using different model, with different settings # per model radio2_path: platform/ahb/18100000.wmac @@ -137,7 +184,6 @@ openwrt: - ssh-rsa AAAAB3N....== user@machine # All configuration below is optional and can safely be removed - # Optionally, use Hurricane Electric/Tunnelbroker to get IPv6 if you don't have native IPv6 # See https://tunnelbroker.net/ henet: @@ -180,6 +226,12 @@ openwrt: id: 254 mac: 20:09:AF:FA:7B:31 duid: 0001000123f7fb792009affa7b31 + - name: streamingbox + id: 100 + mac: DD:45:4F:4A:2E:99 + duid: 0001000123f7fb79dd454f4a2e99 + # define the network this static client is in. Defaults to lan if not defined + network: iot # Hostname that are created. Devices that have a DHCP name will automatically # be accessible via name.network host_names: @@ -207,15 +259,3 @@ openwrt: # retrieved. upnp: configure_with_external_ip: false - # Optional configuration for VLANs - # vlans: - # - name: Management - # vid: 100 - # ports: 0t 1t - # - name: Internet - # vid: 300 - # ports: 0t 1t - # # for the main VLAN that connect to the WAN, set wan to true - # wan: true - # - name: Television - # vid: 640 diff --git a/openwrt.yaml b/openwrt.yaml index 0b7ba09..f1d26d9 100644 --- a/openwrt.yaml +++ b/openwrt.yaml @@ -235,6 +235,8 @@ - name: cron path: /etc/crontabs filename: root + vars: + networks: "{{ common_networks | combine(host_networks | default({}), recursive=True) }}" loop_control: label: Copying {{ item.path }}/{{ item.filename | default(item.name) }} diff --git a/test_config_local.yaml b/test_config_local.yaml index d92f244..bd0b68a 100644 --- a/test_config_local.yaml +++ b/test_config_local.yaml @@ -81,6 +81,8 @@ - name: cron path: etc/crontabs filename: root + vars: + networks: "{{ common_networks | combine(host_networks | default({}), recursive=True) }}" loop_control: label: Copying {{ item.path }}/{{ item.filename | default(item.name) }}