File: ibgp

package info (click to toggle)
exabgp 4.2.25-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,612 kB
  • sloc: python: 37,482; sh: 581; perl: 31; makefile: 23
file content (213 lines) | stat: -rwxr-xr-x 6,400 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python

import os
import sys
import pwd
try:
    import asyncore
except:
    import py_asyncore as asyncore
import socket
import errno
from struct import unpack
import time
from exabgp.util.od import od


class BGPHandler(asyncore.dispatcher_with_send):
    wire = not not os.environ.get('wire', '')
    update = True

    keepalive = chr(0xFF) * 16 + chr(0x0) + chr(0x13) + chr(0x4)

    _name = {
        chr(1): 'OPEN',
        chr(2): 'UPDATE',
        chr(3): 'NOTIFICATION',
        chr(4): 'KEEPALIVE',
    }

    def isupdate(self, header):
        return header[18] == chr(2)

    def name(self, header):
        return self._name.get(header[18], 'SOME WEIRD RFC PACKET')

    def routes(self, body):
        len_w = unpack('!H', body[0:2])[0]
        prefixes = [ord(_) for _ in body[2 : 2 + len_w :]]

        if not prefixes:
            yield 'no ipv4 withdrawal'

        while prefixes:
            l = prefixes.pop(0)
            r = [0, 0, 0, 0]
            for index in range(4):
                if index * 8 >= l:
                    break
                r[index] = prefixes.pop(0)
            yield 'withdraw ' + '.'.join(str(_) for _ in r) + '/' + str(l)

        len_a = unpack('!H', body[2 + len_w : 2 + len_w + 2])[0]
        prefixes = [ord(_) for _ in body[2 + len_w + 2 + len_a :]]

        if not prefixes:
            yield 'no ipv4 announcement'

        while prefixes:
            l = prefixes.pop(0)
            r = [0, 0, 0, 0]
            for index in range(4):
                if index * 8 >= l:
                    break
                r[index] = prefixes.pop(0)
            yield 'announce ' + '.'.join(str(_) for _ in r) + '/' + str(l)

    def announce(self, *args):
        print self.ip, self.port, ' '.join(str(_) for _ in args) if len(args) > 1 else args[0]

    def setup(self, record, ip, port):
        self.ip = ip
        self.port = port
        now = time.strftime("%a-%d-%b-%Y-%H:%M:%S", time.gmtime())
        self.record = open("%s-%s" % ('bgp', now), 'w') if record else None
        self.handle_read = self.handle_open
        self.update_count = 0
        self.time = time.time()
        return self

    def read_message(self):
        header = ''
        while len(header) != 19:
            try:
                left = 19 - len(header)
                header += self.recv(left)
                if self.wire:
                    self.announce("HEADER ", od(header))
                if self.wire and len(header) != 19:
                    self.announce("left", 19 - len(header))
                if left == 19 - len(header):  # ugly
                    # the TCP session is gone.
                    self.announce("TCP connection closed")
                    self.close()
                    return None, None
            except socket.error, exc:
                if exc.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    continue
                raise exc

        self.announce("read", self.name(header))

        length = unpack('!H', header[16:18])[0] - 19
        if self.wire:
            self.announce("waiting for", length, "bytes")

        if length > 4096 - 19:
            print "packet"
            print od(header)
            print "Invalid length for packet", length
            sys.exit(1)

        body = ''
        left = length
        while len(body) != length:
            try:
                body += self.recv(left)
                left = length - len(body)
                if self.wire:
                    self.announce("BODY   ", od(body))
                if self.wire and left:
                    self.announce("missing", left)
            except socket.error, exc:
                if exc.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
                    continue
                raise exc

        self.update_count += 1

        if self.record:
            self.record.write(header + body)

        elif self.isupdate(header):
            self.announce(
                "received %-6d updates (%6d/sec) " % (self.update_count, self.update_count / (time.time() - self.time)),
                ', '.join(self.routes(body)),
            )

        return header, body

    def handle_open(self):
        # reply with a IBGP response with the same capability (just changing routerID)
        header, body = self.read_message()
        routerid = chr((ord(body[8]) + 1) & 0xFF)
        o = header + body[:8] + routerid + body[9:]
        self.announce("sending open")
        self.send(o)
        self.announce("sending keepalive")
        self.send(self.keepalive)
        self.handle_read = self.handle_keepalive

    def handle_keepalive(self):
        header, body = self.read_message()
        if header is not None:
            self.announce("sending keepalive")
            self.send(self.keepalive)


class BGPServer(asyncore.dispatcher):
    def __init__(self, host, port, record):
        self.record = record
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind((host, port))
        self.listen(5)

    def handle_accept(self):
        pair = self.accept()
        if pair is not None:
            # The if prevent invalid unpacking
            sock, addr = pair  # pylint: disable=W0633
            print "new BGP connection from", addr
            BGPHandler(sock).setup(self.record, *addr)


def drop():
    uid = os.getuid()
    gid = os.getgid()

    if uid and gid:
        return

    for name in [
        'nobody',
    ]:
        try:
            user = pwd.getpwnam(name)
            nuid = int(user.pw_uid)
            ngid = int(user.pw_uid)
        except KeyError:
            pass

    if not gid:
        os.setgid(ngid)
    if not uid:
        os.setuid(nuid)


try:
    if os.environ.get('exabgp.tcp.port', '').isdigit():
        port = int(os.environ.get('exabgp.tcp.port'))
    elif os.environ.get('exabgp_tcp_port', '').isdigit():
        port = int(os.environ.get('exabgp_tcp_port'))
    else:
        port = 179

    bind = os.environ.get('exabgp.tcp.bind', os.environ.get('exabgp_tcp_bind', 'localhost'))
    record = bool(os.environ.get('exabgp.wire.record', os.environ.get('exabgp.wire.record', False)))
    server = BGPServer(bind, port, record)
    drop()
    asyncore.loop()
except socket.error:
    print 'need root right to bind to port 179'