- Timestamp:
- 06/26/12 02:46:58 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
etherws/trunk/etherws.py
r162 r164 60 60 61 61 class DebugMixIn(object): 62 def dprintf(self, msg, *args):62 def dprintf(self, msg, func): 63 63 if self._debug: 64 64 prefix = '[%s] %s - ' % (time.asctime(), self.__class__.__name__) 65 sys.stderr.write(prefix + (msg % args)) 65 sys.stderr.write(prefix + (msg % func())) 66 67 68 class 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 95 class 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] 66 143 67 144 … … 72 149 self._debug = debug 73 150 self._clients = [] 151 self._table = SwitchingTable(debug=debug) 152 self._tablelock = threading.Lock() 74 153 self._tap = pytun.TunTapDevice(dev, pytun.IFF_TAP | pytun.IFF_NO_PI) 75 154 self._tap.up() 76 155 self._taplock = threading.Lock() 156 self.register_client(self) 77 157 78 158 def fileno(self): 79 with self._taplock: 80 return self._tap.fileno() 159 return self._tap.fileno() 81 160 82 161 def register_client(self, client): … … 84 163 85 164 def unregister_client(self, client): 165 self._table.delete(client) 86 166 self._clients.remove(client) 87 167 168 # synchronized methods 169 def write_message(self, message, binary=False): 170 with self._taplock: 171 self._tap.write(message) 172 88 173 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 89 191 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) 96 193 97 194 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'))) 99 201 100 202 def __call__(self, fd, events): … … 122 224 def open(self): 123 225 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) 125 227 126 228 def on_message(self, message): 127 229 self._tap.write(self, message) 128 self.dprintf('received: %s %s\n',129 self.request.remote_ip, message.encode('hex'))130 230 131 231 def on_close(self): 132 232 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) 134 234 135 235 … … 156 256 self._sock = websocket.WebSocket() 157 257 self._sock.connect(self._url, **self._options) 158 self.dprintf('connected: %s\n', self._url)258 self.dprintf('connected: %s\n', lambda: self._url) 159 259 160 260 def close(self): … … 163 263 self._sock.close() 164 264 self._sock = None 165 self.dprintf('disconnected: %s\n', self._url)265 self.dprintf('disconnected: %s\n', lambda: self._url) 166 266 167 267 def write_message(self, message, binary=False): … … 173 273 flag = websocket.ABNF.OPCODE_TEXT 174 274 self._sock.send(message, flag) 175 self.dprintf('sent: %s %s\n', self._url, message.encode('hex'))176 275 177 276 def run_forever(self):
Note: See TracChangeset
for help on using the changeset viewer.