Showing posts with label bgp. Show all posts
Showing posts with label bgp. Show all posts

Sunday, July 24, 2016

A Script to Control Quagga BGPD Daemon, Implementing Anycast DNS Server

One way to implement Anycast DNS Server throughout the internet is to use BGP protocol and to announce your DNS IP block from multiple locations so that redundancy and low-latency targets are achieved. There are many ways to design such a infrastructure; you could use hardware routers, you could use load-Balancers in front of your servers etc. I would like to share a script here for the sceanario where Quagga BGPD daemon is used as a software router either on the DNS server or any other server purely as a router:
Using python dnspython library, it is easy to perform checks of a DNS servers ability to respond to different type of DNS queries. Based on the DNS servers answer you could stop, start Quagga BGPD daemon.
You could check the examples on http://www.dnspython.org/examples.html to understand how dnspython is used. A simple A type request is as below:
test_domain = "www.test.com"
server_to_test = ["127.0.0.1"] 

answers = anycast_server1.query(test_domain, "A")
anycast_server1 = dns.resolver.Resolver()
anycast_server1.timeout=1.0
anycast_server1.lifetime=1.0
anycast_server1.nameservers = server_to_test
for data in answers: 
 print data
If DNS and BGPD daemon is on the same server, script should forward the query to local server: 127.0.0.1. Below is the script i wrote to check local server with an A type DNS request, and based on the answer act either to start or stop the BGPD daemon. You could download the latest version of the script from https://github.com/ercintorun/dns-check-quagga-act.
# -*- coding: utf-8 -*-
import dns.resolver, psutil, commands, time, logging, datetime

###VARIABLES 
script_run_time = 60 
test_domain = "www.test.com"
server_to_test = ["127.0.0.1"] 

#############
#############
#############
###logging file folder and logging level config
logging.basicConfig(filename='/var/log/dnsscript.log', filemode='a', level=logging.INFO,
                    format='%(asctime)s [%(name)s] %(levelname)s (%(threadName)-10s): %(message)s')

###define dns servers, parameters
anycast_server1 = dns.resolver.Resolver()
anycast_server1.timeout=1.0
anycast_server1.lifetime=1.0
anycast_server1.nameservers = server_to_test 
   
###time to run the script
starttime = time.time()
timeout = time.time()+ script_run_time

###kill process function 
def kill_process(PROCNAME):
 for proc in psutil.process_iter():
  if proc.name() == PROCNAME:
   proc.kill()

###start a loop with an amount of script_run_time value

while True:
 time.sleep(0.8)
 if time.time()> timeout:
  break
 else:
###get all daemon names into list
  daemon_list=[]
  for proc in psutil.process_iter():
   daemon_list.append(proc.name())
###start the control
  if "bgpd" not in daemon_list: 
   try:
    answers = anycast_server1.query(test_domain, "A")
    commands.getoutput ("/etc/init.d/bgpd restart")
    logging.warning("DNS successful, BGP daemon is down, bgpd restarted")
    time.sleep(2) #give bgp 2 second to get up again
   except dns.resolver.NXDOMAIN:
    logging.warning("DNS exception: No such domain, BGP daemon is already down, no change done")
   except dns.resolver.Timeout:
    logging.warning("DNS exception: Timed out while resolving, BGP daemon is already down, no change done ") 
   except dns.exception.DNSException:
    logging.warning("DNS exception: Unhandled exception, BGP daemon is already down, no change done") 
  else:
   try:
    answers = anycast_server1.query(test_domain, "A")
    for data in answers: 
     resolved = data
    logging.info("DNS successful, nothing has been changed, last resolved ip is: "+str(data))
   except dns.resolver.NXDOMAIN:
    kill_process("bgpd")
    logging.warning("DNS exception: No such domain, BGP daemon terminated")
   except dns.resolver.Timeout:
    kill_process("bgpd")
    logging.warning("DNS exception: Timed out while resolving, BGP daemon terminated")
   except dns.exception.DNSException:
    kill_process("bgpd")
    logging.warning("DNS exception: Unhandled exception, BGP daemon terminated")
If you examine the script you could see that it works for 60 seconds. If you add this script to crontab with 1 minute interval it will check DNS server 60-70 times per minute (on local server) continiously. I've used psutil library to fetch active daemons list on Linux to check whether BGPD daemon is active or not. Script,
  • If DNS successful and BGPD is active does nothing 
  • If DNS successful and BGPD is passive, restarts BGPD using "commands" libary 
  • IF DNS unsuccessful and BGPD is active, stops BGPD 
  • IF DNS unsuccessful and BGPD is passive, does nothing
Also you could see that the script logs its actions for each query to "/var/log/dnsscript.log". If you would like to decrease log size, you could change "level=logging.INFO" to "level=logging.WARNING" to not to log no-action-taken checks.

Wednesday, January 21, 2015

Validating BGP Announcements by Automating Filter Generation with Python: Part1


Securing Internet Routing infrastructure has been a hot topic for a long time as hijack events occurs again and again either mistakenly or on purpose. Operators use different  techniques to validate BGP announcements. I will try to explain creating an ip/as prefix-list  by pulling information from a RIR database, in our case RIPE, using python. But first, lets take a look at other methods in Part1: 

RPKI

Probabily  the best  way to prevent BGP/IP hijacks is to use RPKI infrastructure, , which is a  PKI framework (trust anchor mechanism). Unfortunately the method needs legitamate AS/IP owners to register their sources. needless to say that no one is registering their sources, as a result the method is still not applicable. By 21.01.2015 the current validation states for all IPv4 prefixes is only %5.59, You can check validation states from http://rpki.surfnet.nl/ipcomp.html.


RPSL 

For RPKI is not applicable, we need another way to validate the BGP announcements, where IRR RPSL database entries comes into play. RPSL is a language commonly used by ISPs to describe their routing policies. ISP's could store their routing policies either on their own server or on open public whois databases including RIPE, RADB, APNIC etc.  RIPE (RIR for Europe) transformed its database to RPSL format in 2001.  Example entries in RIPE database are:

as-set:          AS-FUNET
descr:           Macro with all ASes exported by FUNET
members:         AS1741
members:         AS1739
members:         AS565
members:         AS15496
members:         AS30754
members:         AS39098
members:         AS39857
members:         AS39662
tech-c:          FH437-RIPE
admin-c:         FA1183-RIPE
mnt-by:          AS1741-MNT
source:          RIPE # Filtered
As-set object which was created by FUNET, and includes their members

aut-num:         AS5400
as-name:         BT
descr:           British Telecommunications plc
org:             ORG-CNS3-RIPE
import:          from AS1741 action pref=20; accept AS-FUNET
British Telecoms RPSL entry which accepts the informations in AS-FUNET  as-set entry.
Route−set: AS4763:RS−ROUTES:AS681
descr:     prefix filter for AS681
members:   130.216.0.0/16, 130.217.0.0/16,
132.181.0.0/16, 138.75.0.0/16, 139.80.0.0/16,
140.200.0.0/16, 156.62.0.0/16, 192.73.21.0/24
tech−c:    JA39
mnt−by:    MAINT−TELSTRA−NZ
changed:   jabley@patho.gen.nz 19991118
source:    RADB
Route-set sample

Using these databases it is possible to get infos for AS-SETs, AS entries, route objects etc. We know that RPKI validation states are really low, so what about the RPSL entries?  Unfortunately Inter Route Registries are not that accurate also. BGPmon analysis which is done at 2010, shows that only %46 of the global routing table entries has a  matching route-object. 


If  you wanted to update a BGP import policy automatically then you should force the facing AS owner to update their IRR entries (route-objects, as-sets…). Some providers area also very strict in the IRR usage , so  why shouldn’t you ? 

Getting  Info From RIR Databases

Tools

There are many great tools that are ready for use which does the whois queries behind, which some of are: 
  • IRR Toolset
  • IRR Power Tools
  • NETİ:IRR
  • Bgpq3
  • Md
  • P2BGPTool
Lets take a loot at one of these tools, namely  Bgpq3, before writing our own python script: 

Bgpq3 Installation and Usage

Download compressed bgpq3 file from http://snar.spb.ru/prog/bgpq3/ , extract and install it (below steps for Ubuntu):

admin@ubuntu:~# wget http://snar.spb.ru/prog/bgpq3/bgpq3-0.1.21.tgz 
admin@ubuntu:~#  tar -xvzf bgpq3-0.1.21.tgz 
admin@ubuntu:~#  cd bgpq3-0.1.21/
admin@ubuntu:~/bgpq3-0.1.21# ./configure
admin@ubuntu:~/bgpq3-0.1.21# sudo make && sudo make install


After installing we could create a prefix-list from command line using the tool. Below is a sample for creating Junos prefix list for as-set: AS-FUNET


You may check bgpq3 man page for  more parameters (cisco, juniper, as-path, ipv4, ipv6)

Definitely command line tools are useful  but if you wanted get data from command line output in  python script it means that you need to parse the data.  There are better ways which you may continue reading in Part 2. 


 

Internetworking Hints Copyright © 2011 -- Template created by O Pregador -- Powered by Blogger