OSPF
OSPF (Open Shortest Path First) is a dynamic Interior Gateway Protocol (IGP) widely used in large computer networks. It efficiently determines the best paths for data transmission, adapting to network changes in real-time. OSPF’s link-state algorithm builds a detailed network map, optimizing data delivery and ensuring network stability.
There are two versions of OSPF: OSPFv2 and OSPFv3. OSPFv2 is used for IPv4 networks, while OSPFv3 is designed specifically for IPv6 networks.
The BNG Blaster can emulate multiple OSPF instances of both versions, OSPFv2 and OSPFv3 (OSPFv3 is basically working but still under development).
An OSPF instance is a virtual OSPF node with one or more network interfaces attached. Such a
node behaves like a “real router” including database synchronization and flooding. Every
instance generates a self
originated type 1 router LSA describing the node itself.
Configuration
Following an example OSPFv2 configuration with two instances attached to two network interfaces.
{
"interfaces": {
"network": [
{
"interface": "eth1",
"address": "10.0.1.2/30",
"gateway": "10.0.1.1",
"ospfv2-instance-id": 1,
"ospfv2-type": "p2p",
},
{
"interface": "eth2",
"address": "10.0.2.2/24",
"gateway": "10.0.2.1",
"ospfv2-instance-id": 2,
"ospfv2-type": "broadcast"
}
]
},
"ospf": [
{
"instance-id": 1,
"version": 2,
"router-id": "10.0.0.11",
"hostname": "R1"
},
{
"instance-id": 2,
"version": 2,
"router-id": "10.0.0.12",
"hostname": "R2"
}
]
}
{ "ospf": {} }
Attribute |
Description |
---|---|
instance-id |
OSPF instance identifier.
|
version |
OSPF version.
Default: 2
|
auth-key |
OSPF authentication key.
|
auth-type |
OSPF authentication type (simple or md5).
|
hello-interval |
OSPF hello interval in seconds.
Default: 10 Range: 1 - 65535
|
dead-interval |
OSPF dead interval in seconds.
Default: 40 Range: 1 - 65535
|
lsa-retry-interval |
OSPF LSA retry interval in seconds.
Default: 5 Range: 1 - 65535
|
hostname |
OSPF hostname.
Default: bngblaster
|
router-id |
OSPF router identifier.
Default: 10.10.10.10
|
router-priority |
OSPF router priority.
Default: 64 Range: 0 - 255
|
area |
OSPF area.
Default: 0.0.0.0
|
sr-base |
OSPF SR base.
Default: 0 Range: 0 - 1048575
|
sr-range |
OSPF SR range.
Default: 0 Range: 0 - 1048575
|
sr-node-sid |
OSPF SR node SID.
Default: 0 Range: 0 - 1048575
|
teardown-time |
OSPF teardown time in seconds.
Default: 5 Range: 0 - 65535
|
The support for multiple instances allows different use cases. One example might be to create two instances connected to the device or network under test. Now inject an LSA on one instance and check if learned over the tested network on the other instance.
Every OSPF instance can be also connected to an emulated link state graph loaded by MRT files as shown in the example below.
{
"ospf": [
{
"instance-id": 1,
"version": 2,
"router-id": "10.0.0.11",
"hostname": "R1"
"external": {
"mrt-file": "ospf.mrt",
"connections": [
{
"router-id": "10.10.0.1",
"local-ipv4-address": "10.0.0.1",
"metric": 1000
}
]
}
}
]
}
{ "ospf": { "external": {} } }
Attribute |
Description |
---|---|
purge |
Automatically purge all external LSA during teardown.
Default: true
|
mrt-file |
OSPF MRT file.
|
{ "ospf": { "external": { "connections": [] } } }
Attribute |
Description |
---|---|
router-id |
Mandatory remote router identifier.
|
metric |
Optional interface metric.
Default: 10 Range: 0 - 4294967295
|
local-ipv4-address |
Mandatory local IPv4 address (OSPFv2 only).
|
local-interface-id |
Local interface identifier (OSPFv3 only).
Default: 1 (2, 3, …)
|
neighbor-interface-id |
Remote interface identifier (OSPFv3 only).
Default: local-interface-id
|
Interfaces
The BNG Blaster supports P2P and broadcast interfaces only.
The following command returns detailed informations about all OSPF interfaces associated with the given OSPF instance (e.g. instance 1).
$ sudo bngblaster-cli run.sock ospf-interfaces instance 1
Neighbors
The following command returns detailed informations about all OSPF neighbors associated with the given OSPF instance (e.g. instance 1).
$ sudo bngblaster-cli run.sock ospf-neighbors instance 1
Database
The BNG Blaster distinguishes between three different source types of LSA entries in the OSPF database.
The type self
is used for the self-originated LSA describing the own
BNG Blaster OSPF instance. LSP entries of type adjacency
are learned
from OSPF neighbors. The type external
is used for those LSA entries
learned via MRT files or injected via ospf-lsa/pdu-update
command.
$ sudo bngblaster-cli run.sock ospf-database instance 1
The BNG Blaster automatically purges all LSAs of type
self
and external
during teardown. This is done by
generating LSAs with newer sequence numbers and max age.
Flooding
The BNG Blaster floods LSAs received to all other active neighbors of the OSPF instance except to those with neighbor router-id equal to the source router-id of the LSA.
LSA Update Command
It is also possible to inject LSAs using the ospf-lsa-update
command.
This command expects a list of hex encoded LSA including
the OSPF LSA common header (| LS age | Options | LS type |
).
$ cat command.json | jq .
{
"command": "ospf-lsa-update",
"arguments": {
"instance": 1,
"lsa": [
"000100050ac801000aff010180000001e7790024ffffff00000000140000000000000000",
"000100050ac802000aff010180000001dc830024ffffff00000000140000000000000000"
]
}
}
The command ospf-pdu-update
work similar to ospf-lsa-update
expecting OSPF LS update
request PDU including OSPF common header (| Version | Type |
).
{
"command": "ospf-pdu-update",
"arguments": {
"instance": 1,
"pdu": [
"020400400101010100000000456e0000000000000000000000000001000100050ac80c000aff01018000012d13160024ffffff00000000140000000000000000",
"0204004001010101000000003b780000000000000000000000000001000100050ac80b000aff01018000012d1e0c0024ffffff00000000140000000000000000"
]
}
}
LSA Update via Scapy
The following example shows how to generate LSAs via Scapy and inject them using the ospf-lsa-update command.
import sys
import socket
import os
import json
from scapy.contrib.ospf import *
def error(*args, **kwargs):
"""print error and exit"""
print(*args, file=sys.stderr, **kwargs)
sys.exit(1)
def execute_command(socket_path, request):
if os.path.exists(socket_path):
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
client.connect(socket_path)
client.send(json.dumps(request).encode('utf-8'))
data = ""
while True:
junk = client.recv(1024)
if junk:
data += junk.decode('utf-8')
else:
break
print(json.dumps(json.loads(data), indent=4))
except Exception as e:
error(e)
finally:
client.close()
else:
error("socket %s not found" % socket_path)
def main():
"""main function"""
socket_path = sys.argv[1]
command = {
"command": "ospf-lsa-update",
"arguments": {
"instance": 1,
"lsa": []
}
}
lsa = OSPF_External_LSA(type=5, id="10.200.1.0", adrouter="10.255.1.1", mask="255.255.255.0", seq=2147483649)
command["arguments"]["lsa"].append(lsa.build().hex())
lsa = OSPF_External_LSA(type=5, id="10.200.2.0", adrouter="10.255.1.1", mask="255.255.255.0", seq=2147483649)
command["arguments"]["lsa"].append(lsa.build().hex())
execute_command(socket_path, command)
if __name__ == "__main__":
main()
MRT Files
The BNG Blaster can load LSA updates from a MRT file as defined in RFC6396.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Subtype |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message... (variable)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The message field contains the complete OSPF LS Update PDU including the OSPF common header starting with version field.
Those files can be loaded at startup via the configuration option
"ospf": { "external": { "mrt-file": "<file>" } }
or alternative
via ospf-load-mrt
command.
$ sudo bngblaster-cli run.sock ospf-load-mrt file ospf.mrt instance 1
The following example shows how to generate such MRT file via Scapy.
import struct
from scapy.contrib.ospf import *
def main():
"""main function"""
with open("ospf.mrt", "wb") as f:
for i in range(10):
lsa = OSPF_External_LSA(type=5, id="10.222.%s.0" % i, adrouter="10.255.1.1", mask="255.255.255.0", seq=2147483949)
ospf = OSPF_Hdr()/OSPF_LSUpd(lsacount=1)/lsa
pdu = struct.pack("!II", 0, 0) + ospf.build()
mrt = struct.pack("!IHHI", 0, 11, 0, len(pdu)) + pdu
f.write(mrt)
if __name__ == "__main__":
main()
LSPGEN
The BNG Blaster includes a tool called lspgen, which is able to generate
topologies and link state packets for export as MRT and PCAP files. This tool
is also able to inject LSAs directly using the ospf-pdu-update
command.
OSPFv3
The BNG Blaster also provides support for OSPFv3, although it should be noted that its support, compared to OSPFv2, is still considered experimental.
Conceptually, OSPFv2 and OSPFv3 function the same way in the BNG Blaster, and the basic configuration differs only in the version number. The control commands are also identical.
{
"interfaces": {
"network": [
{
"interface": "eth1",
"address-ipv6": "fc66:1337:60::2/64",
"gateway-ipv6": "fc66:1337:60::1",
"ospfv3-instance-id": 1,
"ospfv3-type": "p2p"
}
]
},
"ospf": [
{
"instance-id": 1,
"version": 3,
"router-id": "10.0.0.16",
"hostname": "R6"
}
]
}