Changeset 164 for etherws


Ignore:
Timestamp:
06/26/12 02:46:58 (12 years ago)
Author:
atzm
Message:
  • switching support
File:
1 edited

Legend:

Unmodified
Added
Removed
  • etherws/trunk/etherws.py

    r162 r164  
    6060 
    6161class DebugMixIn(object): 
    62     def dprintf(self, msg, *args): 
     62    def dprintf(self, msg, func): 
    6363        if self._debug: 
    6464            prefix = '[%s] %s - ' % (time.asctime(), self.__class__.__name__) 
    65             sys.stderr.write(prefix + (msg % args)) 
     65            sys.stderr.write(prefix + (msg % func())) 
     66 
     67 
     68class EthernetFrame(object): 
     69    def __init__(self, data): 
     70        self.data = data 
     71 
     72    @property 
     73    def multicast(self): 
     74        return ord(self.data[0]) & 1 
     75 
     76    @property 
     77    def dst_mac(self): 
     78        return self.data[:6] 
     79 
     80    @property 
     81    def src_mac(self): 
     82        return self.data[6:12] 
     83 
     84    @property 
     85    def tagged(self): 
     86        return ord(self.data[12]) == 0x81 and ord(self.data[13]) == 0 
     87 
     88    @property 
     89    def vid(self): 
     90        if self.tagged: 
     91            return ((ord(self.data[14]) << 8) | ord(self.data[15])) & 0x0fff 
     92        return -1 
     93 
     94 
     95class SwitchingTable(DebugMixIn): 
     96    def __init__(self, ageout=300, debug=False): 
     97        self._ageout = ageout 
     98        self._debug = debug 
     99        self._table = {} 
     100 
     101    def learn(self, frame, port): 
     102        mac = frame.src_mac 
     103        vid = frame.vid 
     104 
     105        if vid not in self._table: 
     106            self._table[vid] = {} 
     107 
     108        self._table[vid][mac] = {'time': time.time(), 'port': port} 
     109        self.dprintf('learned: [%d] %s\n', 
     110                     lambda: (vid, mac.encode('hex'))) 
     111 
     112    def lookup(self, frame): 
     113        mac = frame.dst_mac 
     114        vid = frame.vid 
     115 
     116        group = self._table.get(vid, None) 
     117        if not group: 
     118            return None 
     119 
     120        entry = group.get(mac, None) 
     121        if not entry: 
     122            return None 
     123 
     124        if time.time() - entry['time'] > self._ageout: 
     125            del self._table[vid][mac] 
     126            if not self._table[vid]: 
     127                del self._table[vid] 
     128            self.dprintf('aged out: [%d] %s\n', 
     129                         lambda: (vid, mac.encode('hex'))) 
     130            return None 
     131 
     132        return entry['port'] 
     133 
     134    def delete(self, port): 
     135        for vid in self._table.keys(): 
     136            for mac in self._table[vid].keys(): 
     137                if self._table[vid][mac]['port'] is port: 
     138                    del self._table[vid][mac] 
     139                    self.dprintf('deleted: [%d] %s\n', 
     140                                 lambda: (vid, mac.encode('hex'))) 
     141            if not self._table[vid]: 
     142                del self._table[vid] 
    66143 
    67144 
     
    72149        self._debug = debug 
    73150        self._clients = [] 
     151        self._table = SwitchingTable(debug=debug) 
     152        self._tablelock = threading.Lock() 
    74153        self._tap = pytun.TunTapDevice(dev, pytun.IFF_TAP | pytun.IFF_NO_PI) 
    75154        self._tap.up() 
    76155        self._taplock = threading.Lock() 
     156        self.register_client(self) 
    77157 
    78158    def fileno(self): 
    79         with self._taplock: 
    80             return self._tap.fileno() 
     159        return self._tap.fileno() 
    81160 
    82161    def register_client(self, client): 
     
    84163 
    85164    def unregister_client(self, client): 
     165        self._table.delete(client) 
    86166        self._clients.remove(client) 
    87167 
     168    # synchronized methods 
     169    def write_message(self, message, binary=False): 
     170        with self._taplock: 
     171            self._tap.write(message) 
     172 
    88173    def write(self, caller, message): 
     174        frame = EthernetFrame(message) 
     175 
     176        with self._tablelock: 
     177            self._table.learn(frame, caller) 
     178 
     179        if not frame.multicast: 
     180            with self._tablelock: 
     181                dst = self._table.lookup(frame) 
     182 
     183            if dst: 
     184                dst.write_message(frame.data, True) 
     185                self.dprintf('sent unicast: [%d] %s -> %s\n', 
     186                             lambda: (frame.vid, 
     187                                      frame.src_mac.encode('hex'), 
     188                                      frame.dst_mac.encode('hex'))) 
     189                return 
     190 
    89191        clients = self._clients[:] 
    90  
    91         if caller is not self: 
    92             clients.remove(caller) 
    93  
    94             with self._taplock: 
    95                 self._tap.write(message) 
     192        clients.remove(caller) 
    96193 
    97194        for c in clients: 
    98             c.write_message(message, True) 
     195            c.write_message(frame.data, True) 
     196 
     197        self.dprintf('sent broadcast: [%d] %s -> %s\n', 
     198                     lambda: (frame.vid, 
     199                              frame.src_mac.encode('hex'), 
     200                              frame.dst_mac.encode('hex'))) 
    99201 
    100202    def __call__(self, fd, events): 
     
    122224    def open(self): 
    123225        self._tap.register_client(self) 
    124         self.dprintf('connected: %s\n', self.request.remote_ip) 
     226        self.dprintf('connected: %s\n', lambda: self.request.remote_ip) 
    125227 
    126228    def on_message(self, message): 
    127229        self._tap.write(self, message) 
    128         self.dprintf('received: %s %s\n', 
    129                      self.request.remote_ip, message.encode('hex')) 
    130230 
    131231    def on_close(self): 
    132232        self._tap.unregister_client(self) 
    133         self.dprintf('disconnected: %s\n', self.request.remote_ip) 
     233        self.dprintf('disconnected: %s\n', lambda: self.request.remote_ip) 
    134234 
    135235 
     
    156256        self._sock = websocket.WebSocket() 
    157257        self._sock.connect(self._url, **self._options) 
    158         self.dprintf('connected: %s\n', self._url) 
     258        self.dprintf('connected: %s\n', lambda: self._url) 
    159259 
    160260    def close(self): 
     
    163263        self._sock.close() 
    164264        self._sock = None 
    165         self.dprintf('disconnected: %s\n', self._url) 
     265        self.dprintf('disconnected: %s\n', lambda: self._url) 
    166266 
    167267    def write_message(self, message, binary=False): 
     
    173273            flag = websocket.ABNF.OPCODE_TEXT 
    174274        self._sock.send(message, flag) 
    175         self.dprintf('sent: %s %s\n', self._url, message.encode('hex')) 
    176275 
    177276    def run_forever(self): 
Note: See TracChangeset for help on using the changeset viewer.