|
| 1 | +#!/usr/bin/python2.5 |
| 2 | + |
| 3 | +from twisted.internet import reactor, protocol |
| 4 | + |
| 5 | +from pybgp import speaker, pathattr, proto |
| 6 | + |
| 7 | +import tcpcheck |
| 8 | + |
| 9 | +# This script is a simple example of the pybgp library. The setup below |
| 10 | +# assumes the following config: |
| 11 | +# |
| 12 | +# router 192.168.1.1 AS# 65000 |
| 13 | +# | |
| 14 | +# us 192.168.1.2 AS# 65001 |
| 15 | +# |
| 16 | +# ...and virtual IPs bound to the loopback interface, on which a DNS |
| 17 | +# server will be listening - e.g. under linux: |
| 18 | +# |
| 19 | +# ip addr add 192.168.2.1/32 dev lo |
| 20 | +# ip addr add 192.168.2.2/32 dev lo |
| 21 | +# rndc reload |
| 22 | +# |
| 23 | +# As long as the virtual IPs continue to answer TCP port 53 connects the |
| 24 | +# VIPs will be advertised by eBGP to the router |
| 25 | + |
| 26 | +class Checker(tcpcheck.Checker): |
| 27 | + def change(self, old, new): |
| 28 | + if new=='up': |
| 29 | + self.bgp.advertise('%s/32' % (self.host,)) |
| 30 | + else: |
| 31 | + self.bgp.withdraw('%s/32' % (self.host,)) |
| 32 | + |
| 33 | +class BGPp(speaker.BGP): |
| 34 | + def send(self, msg): |
| 35 | + print "sending", msg |
| 36 | + speaker.BGP.send(self, msg) |
| 37 | + |
| 38 | +class BGPd: |
| 39 | + # This is all a bit hacky. Proper support for both listening and |
| 40 | + # connecting, the BGP state machine, disconnects and so forth are |
| 41 | + # all needed... |
| 42 | + |
| 43 | + def __init__(self, asnum, bgpid): |
| 44 | + self.cc = protocol.ClientCreator(reactor, BGPp) |
| 45 | + self.asnum = asnum |
| 46 | + self.bgpid = bgpid |
| 47 | + self.peers = {} |
| 48 | + |
| 49 | + def peer(self, ip, remoteas): |
| 50 | + if ip in self.peers: |
| 51 | + raise Exception('connection already established') |
| 52 | + |
| 53 | + self.peers[ip] = {'remoteas': remoteas, 'state': 'opening', 'proto': None, 'queue': []} |
| 54 | + |
| 55 | + d = self.cc.connectTCP(ip, 179) |
| 56 | + d.addCallbacks(self.ok, self.err, (ip,), {}, (ip,), {}) |
| 57 | + |
| 58 | + def ok(self, proto, ip): |
| 59 | + print "connection to", ip, "up" |
| 60 | + self.peers[ip]['proto'] = proto |
| 61 | + self.peers[ip]['state'] = 'open' |
| 62 | + |
| 63 | + # send our Open message |
| 64 | + proto.open(self.asnum, self.bgpid) |
| 65 | + proto.handle_msg = lambda x: self.msg(ip, x) |
| 66 | + |
| 67 | + def msg(self, ip, msg): |
| 68 | + if not ip in self.peers: |
| 69 | + return |
| 70 | + proto = self.peers[ip]['proto'] |
| 71 | + |
| 72 | + if msg.kind=='open': |
| 73 | + proto.start_timer(msg.holdtime) |
| 74 | + |
| 75 | + q = self.peers[ip]['queue'] |
| 76 | + while q: |
| 77 | + action, data = q.pop(0) |
| 78 | + if action=='update': |
| 79 | + proto.send(data) |
| 80 | + |
| 81 | + elif msg.kind=='notification': |
| 82 | + print "notification from", ip, msg |
| 83 | + proto.transport.loseConnection() |
| 84 | + del self.peers[ip] |
| 85 | + |
| 86 | + def err(self, reason, ip): |
| 87 | + if not ip in self.peers: |
| 88 | + return |
| 89 | + print "connection to", ip, "failed", reason |
| 90 | + del self.peers[ip] |
| 91 | + |
| 92 | + def advertise(self, prefix): |
| 93 | + up = proto.Update( |
| 94 | + pathattr.Origin('incomplete'), |
| 95 | + pathattr.Med(0), |
| 96 | + pathattr.AsPath([[self.asnum]]), |
| 97 | + pathattr.NextHop('155.198.62.12'), |
| 98 | + nlri=[prefix], |
| 99 | + ) |
| 100 | + for p, info in self.peers.items(): |
| 101 | + if info['state']=='open': |
| 102 | + info['proto'].send(up) |
| 103 | + else: |
| 104 | + info['queue'].append(('update', up)) |
| 105 | + |
| 106 | + def withdraw(self, prefix): |
| 107 | + up = proto.Update( |
| 108 | + withdraw=[prefix], |
| 109 | + ) |
| 110 | + for p, info in self.peers.items(): |
| 111 | + if info['state']=='open': |
| 112 | + info['proto'].send(up) |
| 113 | + else: |
| 114 | + info['queue'].append(('update', up)) |
| 115 | + |
| 116 | + |
| 117 | +def main(): |
| 118 | + b = BGPd(65001, '192.168.1.2') |
| 119 | + b.peer('192.168.1.1', 65000) |
| 120 | + |
| 121 | + for ip in ('192.168.2.1', '192.168.2.2'): |
| 122 | + c = Checker(ip, 53, '127.0.0.1') |
| 123 | + c.bgp = b |
| 124 | + c.start(5) |
| 125 | + |
| 126 | +if __name__=='__main__': |
| 127 | + reactor.callWhenRunning(main) |
| 128 | + reactor.run() |
0 commit comments