GoBGP is an open source initiative providing a feature rich and scalable BGP solution to the open source community. GoBGP can be found on github at the following URL https://github.com/osrg/gobgp.
If your like me, you like the ability to extract the data you deem important from your protocols. Generally speaking, getting data from protocols has always been an excercise of SNMP calls or screen scrape of CLI commads. In this tutorial, we are going to discuss the mechanism to interface the GoBGP (gobgpd) daemon more directly with the language Python and GRPC. We'll begin by reviewing the Protocol Buffers file definitions and then take that file, compile it for python and then write some easy to follow programs that illustrate how to make use of it.
GoBGP makes information available via GRPC and Protocol Buffers files. This makes it very easy to interface using multiple languages as GRPC and Protocol Buffers have several language drivers that they support. Such as, but not limited too:
In this tutorial it is assumed you have a running GoBGP network (refer to Introduction to GoBGP part 1 or part 2 ) to accomplish this. And you will chose one of the routers in your setup to query against. Also ensure port 50051 is listening on the interface or all interfaces that you will query by executing the command as shown in figure 1.- C++
- Java
- Python
- Gorman
- Ruby
- C#
- Node.js
- Android Java
- Objective Chip
- PHP
root@R1:~# netstat -ntlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.11:40351 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:2601 0.0.0.0:* LISTEN 20/zebra tcp 0 0 127.0.0.1:6060 0.0.0.0:* LISTEN 26/gobgpd tcp 0 0 0.0.0.0:179 0.0.0.0:* LISTEN 26/gobgpd tcp6 0 0 :::50051 :::* LISTEN 26/gobgpd tcp6 0 0 :::179 :::* LISTEN 26/gobgpd
Figure 1.
The Protocol Buffers for GoBGP is published on the github site and you can obtain the pb (protocol buffer) file as shown in figure 2.
$ wget https://raw.githubusercontent.com/osrg/gobgp/master/api/gobgp.proto
Figure 2.
This should pull down the pb file to your local desktop. Next, you have to install Python GRPC libraries, goto the following URL to see how to accomplish this.
Once you have the Python GRPC libraries installed, we need to convert the pb file into python libraries. There is an outdated GoBGP document out on the Internet that should no longer be used, figure 3 shows the correct way to do the conversion.
$ python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. gobgp.proto
Figure 3.
This should have created the following two files:
- gobgp_pb2_grpc.py
- gobgp_pb2.py
service GobgpApi {
Figure 4.
Therefore, the stub object is called GobgpApiStub. This will be used in setting up communications to GoBGP. If we execute python and execute the code shown in figure 5, our connection will be established to GoBGPd.
>>> import grpc
>>> import gobgp_pb2_grpc
>>> import gobgp_pb2
>>> channel = grpc.insecure_channel('10.8.1.2:50051')
>>> stub = gobgp_pb2_grpc.GobgpApiStub(channel)
Figure 5.
The IP address depends on your GoBGP routers IP that your communicating on. Mine is 10.8.1.2 as depicted in red in Figure 5. We will pick two services from the pb file from the services listing as shown in figure 6.
service GobgpApi { rpc StartServer(StartServerRequest) returns (StartServerResponse) {} rpc StopServer(StopServerRequest) returns (StopServerResponse) {} rpc GetServer(GetServerRequest) returns (GetServerResponse) {} rpc AddNeighbor(AddNeighborRequest) returns (AddNeighborResponse) {} rpc DeleteNeighbor(DeleteNeighborRequest) returns (DeleteNeighborResponse) {} rpc GetNeighbor(GetNeighborRequest) returns (GetNeighborResponse) {} rpc ResetNeighbor(ResetNeighborRequest) returns (ResetNeighborResponse) {} rpc SoftResetNeighbor(SoftResetNeighborRequest) returns (SoftResetNeighborResponse) {} rpc ShutdownNeighbor(ShutdownNeighborRequest) returns (ShutdownNeighborResponse) {} rpc EnableNeighbor(EnableNeighborRequest) returns (EnableNeighborResponse) {} rpc DisableNeighbor(DisableNeighborRequest) returns (DisableNeighborResponse) {} rpc GetRib(GetRibRequest) returns (GetRibResponse) {} . . . }
Figure 6.
For the first example I am going to choose the GetRib service. If we look at the GetRib service, we see it takes an argument of GetRibRequest and receives a reply of type GetRibResponse, both of which can be seen in the pb file. Figure 7 shows these 2 structures, plus other defined fields they leverage.
message GetRibRequest { Table table = 1; } message GetRibResponse { Table table = 1; } message Table { Resource type = 1; string name = 2; uint32 family = 3; repeated Destination destinations = 4; bool post_policy = 5; } enum Resource { GLOBAL = 0; LOCAL = 1; ADJ_IN = 2; ADJ_OUT = 3; VRF = 4; } message Destination { string prefix = 1; repeated Path paths = 2; bool longer_prefixes = 3; bool shorter_prefixes = 4; } message Path { bytes nlri = 1; repeated bytes pattrs = 2; int64 age = 3; bool best = 4; bool is_withdraw = 5; int32 validation = 6; bool no_implicit_withdraw = 7; uint32 family = 8; uint32 source_asn = 9; string source_id = 10; bool filtered = 11; bool stale = 12; bool is_from_external = 13; string neighbor_ip = 14; }
Figure 7.
Using the information referenced in figure 7, we see that GetRib Service uses the GetRibRequest as an argument, this helps, there are a few other things to make the call successful that have to be done, that are not fully documented yet.
1: import gobgp_pb2 as gobgp 2: import gobgp_pb2_grpc as gobgp_grpc 3: import grpc 4: 5: AFI_IP4 = 1 6: SAFI_UNICAST = 1 7: FAMILY = AFI_IP << 16 | SAFI_UNICAST 8: 9: ch = grpc.insecure_channel('10.8.1.2:50051') 10: stub = gobgp_grpc.GobgpApiStub(ch) 11: 12: req = gobgp.GetRibRequest() 13: t = gobgp.Table(family=FAMILY) 14: req.table.MergeFrom(t) 15: routes = stub.GetRib(req) 16: for n,route in enumerate(routes.table.destinations): 17: print "Prefix %d -> %s" % (n,route.prefix) 18: path_count = 1 19: for path in route.paths: 20: print " path %d --> %s %s" % (path_count,path.source_asn,path.neighbor_ip) 21: path_count += 1
Figure 8.
Output from code in figure 8 can be seen in Figure 9.
Prefix 0 -> 10.8.3.0/24 path 1 --> 65001 172.40.2.3 Prefix 1 -> 10.8.1.0/24 path 1 --> 65001 <nil> Prefix 2 -> 172.40.1.0/29 path 1 --> 65001 <nil> path 2 --> 65001 172.40.1.3 Prefix 3 -> 172.40.2.0/29 path 1 --> 65001 <nil> path 2 --> 65001 172.40.2.3 Prefix 4 -> 192.65.1.0/29 path 1 --> 65001 <nil> path 2 --> 65002 192.65.1.3 Prefix 5 -> 10.8.2.0/24 path 1 --> 65001 172.40.1.3 Prefix 6 -> 10.20.0.0/24 path 1 --> 65002 192.65.1.3
Figure 9.
Let's go over some of the highlighted items in figure 8. Lines 1-3 import grpc and the protocol buffer files that were generated (leaving off the .py extension). Line 7 uses a bit shift of AFI and SAFI to create the variable FAMILY which equals 65537. Lines 9-10 setup the connection to the gobgp daemon running on the device with IP 10.8.1.2 using port 50051. Lines 12-15 creates the req variable giving it fields from the GetRibRequest structure, creates a variable t that gets assigned variables from the routing table structure and assigns it the address family equal to the FAMILY variable (or 65537 for IPv4 Unicast) and makes a call to GetRib, whose result is stored in routes. Lines 16-21 just loops through the routes, formats and prints. The inner loop starting at line 24 then formats and outputs the path or paths a prefix can take.
For the second example we will use the GetNeighbor service, But won't go into as much detail. This code example, shown in figure 10, shows the neighbors that the gobgpd instance has.1: import grpc 2: import gobgp_pb2_grpc 3: import gobgp_pb2 4: 5: _TIMEOUT_SECONDS = 10 6: 7: channel = grpc.insecure_channel('10.8.1.2:50051') 8: stub = gobgp_pb2_grpc.GobgpApiStub(channel) 9: 10: peers = stub.GetNeighbor(gobgp_pb2.GetNeighborRequest(),_TIMEOUT_SECONDS) 11: for peer in peers.peers: 12: print "%s %s %s" % (peer.conf.neighbor_address,peer.conf.peer_as,peer.info.bgp_state)
Figure 10.
Figure 11 shows the output from the program in figure 10.
172.40.1.3 65001 BGP_FSM_ACTIVE 172.40.2.3 65001 BGP_FSM_ESTABLISHED 192.65.1.3 65002 BGP_FSM_ACTIVE
Figure 11.
The code in figure 10 shows the standard imports made prior in Lines 1-3. Line 5 just establishes a private variable time-out variable to be used later. Lines 7-8 sets up the connection as before. Line 10 makes the call to the GetNeighbor function and has two arguments, one is to pass GetNeighborRequest (as seen in the protobuf file and the other optional one is the timeout value (which was set to 10 seconds). Lines 11-12 just loops the neighbors and prints out formatted information of the neighbors IP, AS and current BGP state.
If we refer to the protobuf file (see figure 12) we see that the GetNeighbor function returns a GetNeighborResponse, which has a variable called peer that refers to a structure Peer. Wow that was a mouthful.
In the Peer, we have two subfields I used in my output (peer.conf... and peer.info....). Peer has conf and info which refer to yet other structures PeerConf and PeerState. In PeerConf, you can see neighbor_address and peer_as. Likewise in PeerState you can see where I got the bgp_state field. This structure gives you and idea of other fields you can use in your output and becomes a very helpful guide.
If we refer to the protobuf file (see figure 12) we see that the GetNeighbor function returns a GetNeighborResponse, which has a variable called peer that refers to a structure Peer. Wow that was a mouthful.
In the Peer, we have two subfields I used in my output (peer.conf... and peer.info....). Peer has conf and info which refer to yet other structures PeerConf and PeerState. In PeerConf, you can see neighbor_address and peer_as. Likewise in PeerState you can see where I got the bgp_state field. This structure gives you and idea of other fields you can use in your output and becomes a very helpful guide.
message GetNeighborResponse { repeated Peer peers = 1; } message Peer { repeated uint32 families = 1; ApplyPolicy apply_policy = 2; PeerConf conf = 3; EbgpMultihop ebgp_multihop = 4; RouteReflector route_reflector = 5; PeerState info = 6; Timers timers = 7; Transport transport = 8; RouteServer route_server = 9; } message PeerConf { string auth_password = 1; string description = 2; uint32 local_as = 3; string neighbor_address = 4; uint32 peer_as = 5; string peer_group = 6; uint32 peer_type = 7; uint32 remove_private_as = 8; bool route_flap_damping = 9; uint32 send_community = 10; repeated bytes remote_cap = 11; repeated bytes local_cap = 12; string id = 13; repeated PrefixLimit prefix_limits = 14; string local_address = 15; string neighbor_interface = 16; string vrf = 17; } message PeerState { string auth_password = 1; string description = 2; uint32 local_as = 3; Messages messages = 4; string neighbor_address = 5; uint32 peer_as = 6; string peer_group = 7; uint32 peer_type = 8; Queues queues = 9; uint32 remove_private_as = 10; bool route_flap_damping = 11; uint32 send_community = 12; uint32 session_state = 13; repeated string supported_capabilities = 14; string bgp_state = 15; enum AdminState { UP = 0; DOWN = 1; PFX_CT = 2; // prefix counter over limit } AdminState admin_state = 16; uint32 received = 17; uint32 accepted = 18; uint32 advertised = 19; uint32 out_q = 20; uint32 flops = 21; }
Figure 12.
These are just two simple examples of how to get information from GoBGP. In a later post I will show additional capabilities. I hope you found this helpful.