OSPF is pretty simple to configure on FreeBSD/pfSense, however, the configuration between neighbors must match. Since we have Ansible deployed, we can use it to ensure the configation is correct.

Using the template and variables below, this will create a Quagga OSPFd config file. In this example, interfaces are TAPs but can be substituted with GIFs, GREs or physical interfaces.

ospf

Here's the Ansible template:

!
hostname {{ ansible_hostname }}
password {{ ospf_password }}
# log stdout
!
{# INTERFACES  #}
{% for int in ospf_int %}
interface {{ int.name }}
 description {{ int.desc }}
{% if int.type is defined %}
 ip ospf network {{ int.type }}
{% endif %}
 ip ospf authentication
 ip ospf authentication-key {{ int.auth_key }}
{% if int.cost is defined %}
 ip ospf cost {{ int.cost }}
{% endif %}
!
{% endfor %}
!
router ospf
{# use IP variable from host vars as ospf ID #}
 ospf router-id {{ ip }}
{# INTERFACE PASSIVE #}
 passive-interface default
{% for int in ospf_int %}
 no passive-interface {{ int.name }}
{% endfor %}
{# NETWORKS #}
{% for entry in ospf_net %}
{% for value in entry.net %}
 network {{ value }} area {{ entry.area }}
{% endfor %}
{% endfor %}

{% for entry in ospf_net %}
 area {{ entry.area }} authentication
{% endfor %}

{# ACLS #}
{% if ospf_acl is defined %}
{% for acl in ospf_acl %}
 area {{ acl.area }} import-list {{ acl.name }}
{% endfor %}

{% for entry in ospf_acl %}
{% for value in entry.permit %}
access-list {{ entry.name }} permit {{ value }}
{% endfor %}
{% for value in entry.deny %}
access-list {{ entry.name }} deny {{ value }}
{% endfor %}
{% endfor %}

{% endif %}
!
!
line vty
!

Here's a demo host config (VPS2) for OSPF where ACLs are needed. In this example, you want to make sure each company's subnets are not advertised to each other. Also, the ACL will limit how much of your local subnets are advertised to both companies.

  • Local: Area 0 - 192.168.1.0/24 & 192.168.2.0/24
  • VPN point-to-point supernet: 10.8.0.0/24 (links are /30)

Note: Router ACLs are good when you might have overlapping IP ranges or you want to ensure traffic is not routed between two subnets, however, it should be paired with firewall policies and not be relied on solely for traffic filtering.

ospf_int:
  - name: 'tap2'
    desc: 'TAP to VPS2'
    type: 'point-to-point'
    auth_key: "{{ ospf_key }}"
    cost: '11'
  - name: 'tap3'
    desc: 'TAP to VPS3'
    type: 'point-to-point'
    auth_key: "{{ ospf_key }}"
  - name: 'tap11'
    desc: 'TAP to Local'
    auth_key: "{{ ospf_key }}"
  - name: 'tap12'
    desc: 'TAP to Company1'
    auth_key: "{{ ospf_key }}"
  - name: 'tap13'
    desc: 'TAP to Company2'
    auth_key: "{{ ospf_key }}"
  - name: 'tap14'
    desc: 'TAP to Mobile Clients'
    auth_key: "{{ ospf_key }}"
    cost: '11'

ospf_net:
  - area: 0.0.0.0
    net:
      - 10.8.0.0/24
  - area: 0.0.0.2
    net: 
      - 10.8.0.128/30
  - area: 0.0.0.3
    net: 
      - 10.8.0.132/30

ospf_acl:
  - name: company1
    area: 0.0.0.2
    permit:
      - 10.8.0.0/24
      - 192.168.1.0/28
      - 192.168.1.48/30
      - 192.168.2.64/26
    deny:
      - any
  - name: company2
    area: 0.0.0.3
    permit:
      - 10.8.0.0/24
      - 192.168.1.0/28
      - 192.168.1.48/30
      - 192.168.2.64/26
    deny:
      - any

If ACLs are not needed, you can exclude the ospf_acl variables and the template will skip the ACL section ({% if ospf_acl is defined %}).