def connect(self, addr): if self in _active_socks: raise SockError('connect', 'connection alreay exists') if self in _listening_socks: raise SockError('connect', 'listening socket') ip, port = addr ip = ip4(ip) if ip == 0: raise SockError('connect', 'ip address not specified') if port == 0: raise SockError('connect', 'port not specified') if not 0 < port < 65535: raise SockError('connect', 'invalid port number %d' % port) with _slock: if self in _binding_socks: if self.la.ip == 0: lip = net.ip_hint(ip) if not lip: raise SockError('connect', 'no route to %s' % ip) assert (self.la.port) self.la = sockaddr(lip, self.la.port) _binding_socks.remove(self) else: assert self.la is None lip = net.ip_hint(ip) if not lip: raise SockError('connect', 'no route to %s' % ip) self.la = sockaddr(lip, _alloc_port()) self.fa = sockaddr(ip, port) assert all(s.la != self.la or s.fa != self.fa for s in _active_socks) self._inq = queue.Queue() self._txbuf = b'' self._rtq = [] _active_socks.append(self) self.iss = gen_isn(self.la, self.fa) self.snd_una = self.iss self.snd_nxt = self.iss + 1 self._send(None, self.iss, None, (MaxSynRetries, SynInterval), SYN=1, wnd=DefWindowSize, mss=1460) self._trans(TCPS.SYN_SENT) # block self._connect_evt = threading.Event() self._connect_status = None self._connect_evt.wait() if self._connect_status is not None: raise SockError('Connect', self._connect_status)
def addrmask(ip): '''For testing only''' ip = ip4(ip) sip = net.ip_hint(ip) if not sip: raise ValueError('no route to %s', ip) send(sip, ip, Pack.MaskRequest(12345, 1))
def ping(ip): '''For testing only''' ip = ip4(ip) sip = net.ip_hint(ip) if not sip: raise ValueError('no route to %s', ip) send(sip, ip, Pack.Echo(12345, 1, b'1234567' * 8))
def time(ip): '''For testing only''' ip = ip4(ip) sip = net.ip_hint(ip) if not sip: raise ValueError('no route to %s', ip) ts = _timestamp() send(sip, ip, Pack.TimestampRequest(12345, 1, ts, 0, 0))
def sendto(self, ip, pkt): if self._closed: raise SockError('socket closed') if self.fip: raise SockError('foreign address already bound') if self.IP_HDRINCL: raise SockError('calling sendto() with IP_HDRINCL') ip = ip4(ip) if ip == 0: raise SockError('not a valid IP address') lip = self.lip if not lip: lip = net.ip_hint(ip) if not lip: raise SockError('no route to %s' % ip) log.info('SEND %s -> %s, %r', lip, ip, pkt) ipv4.send(lip, ip, pkt)
def send(self, pkt): if self._closed: raise SockError('socket closed') if self.IP_HDRINCL: if not isinstance(pkt, ipv4.Pack): raise SockError('not a IPv4 packet') _stat.rawout += 1 log.info('SEND %s', pkt) ipv4.send_ip(pkt) else: fip = self.fip if not fip: raise SockError('foreign address not bound') lip = self.lip if not lip: lip = net.ip_hint(fip) if not lip: raise SockError('no route to %s' % fip) log.info('SEND %s -> %s, %r', lip, fip, pkt) ipv4.send(lip, fip, pkt)
def send(from_ip, to_ip, pdu, **extras): if to_ip == 0: log.error('target IP cannot be 0!') return 0 if from_ip == 0: from_ip = net.ip_hint(to_ip) _stat.tx_count += 1 pac = Pack(from_ip, to_ip, pdu, **extras) log.debug('SEND %s', pac) # TODO: obtain MTU from the sending interface mtu = 1500 # Fast path - no fragmentation if len(pac) <= mtu: net.send(pac) return 1 # fragmentation if pac.DF: # TODO: ICMP error log.warning('cannot send large packet with DF set') _stat.tx_dropped += 1 return 0 _stat.tx_fragmented += 1 log.info('fragmenting large packet') hdr = pac.hdr nhdr = len(hdr) if hasattr(pdu, 'pack'): pdu = pdu.pack(None) npdu = len(pdu) ps = (mtu - nhdr) >> 3 << 3 (n, r) = divmod(npdu, ps) if r == 0: n -= 1 # send the last frag first net.send(Pack(from_ip, to_ip, pdu[n * ps:], DSCP=pac.DSCP, ECN=pac.ECN, proto=pac.proto, ttl=pac.ttl, ident=pac.ident, MF=0, offset=n*ps>>3, opts=tuple(opt for opt in pac.opts if opt.Copy))) # send remaining frags in reversed order while n > 0: n -= 1 offset = n * ps net.send(Pack(from_ip, to_ip, pdu[offset:offset+ps], DSCP=pac.DSCP, ECN=pac.ECN, proto=pac.proto, ttl=pac.ttl, ident=pac.ident, MF=1, offset=offset>>3, opts=tuple(opt for opt in pac.opts if n==0 or opt.Copy))) return 1
def _recv(self, from_ip, seg): faddr = sockaddr(from_ip, seg.sp) self.dbg('RECV from %s: %s', faddr, seg) if self._st == TCPS.CLOSED: # TODO: check ACK? self._send(None, seg.ack, seg.seq + seg.len, None, RST=1) return if self._st == TCPS.LISTEN: if seg.RST: self.warn('RST from %s', faddr) elif seg.ACK: self.warn('ACK from %s', faddr) if self in _active_socks: self._send(None, seg.ack, None, None, RST=1) elif seg.FIN: self.warn('FIN from %s', faddr) if self in _active_socks: self._send(None, seg.ack, None, None, RST=1) return elif seg.SYN: if seg.data: # TODO self.error('SYN with data') return if hasattr(self, '_pending_conns'): # this is a listening socket if self._pending_conns.qsize() > MaxSynBacklog: self.warn('backlog queue overflow!') return newso = _Socket(self.blocking, self.timeout) newso.la = self.la newso._st = TCPS.LISTEN newso._listenq = self._pending_conns newso._inq = queue.Queue() newso._txbuf = b'' newso._rtq = [] newso.fa = faddr # _slock should be hold _active_socks.append(newso) newso._recv(from_ip, seg) return self.irs = seg.seq self.rcv_nxt = seg.seq + 1 if seg.ws: self.peer_ws = 1 << seg.ws else: self.peer_ws = 1 self.snd_wnd = seg.wnd * self.peer_ws if seg.mss: self.peer_mss = seg.mss else: self.peer_mss = 576 if self.la.ip == 0: # update local self.la = sockaddr(net.ip_hint(from_ip), self.la.port) assert self.la.ip self.iss = gen_isn(self.la, self.fa) self.snd_nxt = self.iss + 1 self.snd_una = self.iss self._send(None, self.iss, self.rcv_nxt, (MaxSynAckRetries, SynInterval), SYN=1, wnd=DefWindowSize, mss=1460) self._trans(TCPS.SYN_RCVD) else: self.warn('bad segment from %s', faddr) self._send(None, seg.ack, None, None, RST=1) return if self._st == TCPS.SYN_SENT: if seg.ACK: if seg.ack != self.iss + 1: self.warn('bad ACK, expecting %d, got %d', self.iss + 1, seg.ack) self._send(None, seg.ack, None, None, RST=1) elif seg.RST: self.warn('Connection refused by %s', faddr) # unblock connect() self._connect_evt.set() self._connect_status = 'Connection refused' self._close('Connection refused') elif seg.SYN: self.irs = seg.seq self.rcv_nxt = seg.seq + 1 if seg.ws: self.peer_ws = 1 << seg.ws else: self.peer_ws = 1 self.snd_wnd = seg.wnd * self.peer_ws if seg.mss: self.peer_mss = seg.mss else: self.peer_mss = 576 self._ack_received(seg) self.log('connected with %s', faddr) # unblock connect() self._send(None, self.snd_nxt, self.rcv_nxt, None) self._trans(TCPS.ESTABLISHED) self._connect_evt.set() else: # keep waiting self.warn('ACK w/o SYN from %s', faddr) elif seg.RST: self.warn('receiving RST in SYN_SENT') return elif seg.SYN: # TODO self.warn('Simultaneous SYN') else: # TODO: send a RST? self.warn('bad segment from %s', faddr) return if self._st == TCPS.SYN_RCVD: if self._check_seq(seg): # keep waiting, will retransmit or timeout pass elif seg.RST: self._rtq.clear() if self._st_p == TCPS.LISTEN: self.warn('RST segment, back to LISTEN') self._trans(TCPS.LISTEN) else: # connection was initiated with an active open self.warn('RST segment, closing') self._close('RST from peer in SYN_RCVD') elif seg.SYN: self.error('bad SYN, closing') self._rtq.clear() self._send(None, self.snd_nxt, None, None, RST=1) self._close('bad SYN from peer in SYN_RCVD') elif seg.FIN: self.warn('FIN in SYN_RCVD') self._rtq.clear() self.rcv_nxt = seg.seq + 1 self._send(None, self.snd_nxt, self.rcv_nxt, None) self._trans(TCPS.CLOSE_WAIT) elif not seg.ACK: # keep waiting self.warn('expecting ACK') elif seg.ack != self.iss + 1: self.error('bad ACK, closing') self._send(None, self.snd_nxt, None, None, RST=1) self._close('bad ACK from peer in SYN_RCVD') else: self._ack_received(seg) if seg.data: self.warn('ACK with data') self._inq.put(bytes(seg.data)) self.rcv_nxt = seg.seq + len(seg.data) self.log('Connected with %s', faddr) self._trans(TCPS.ESTABLISHED) # ready to be accept() self._listenq.put(self) del self._listenq return # check incoming segment if self._check_seq(seg): if not seg.RST: self._send(None, self.snd_nxt, self.rcv_nxt, None) return if seg.RST: self.warn('RST segment, closing') self._close('RST from peer') return if seg.SYN: self.error('SYN segment, closing') self._send(None, self.snd_nxt, None, None, RST=1) self._close('bad SYN from peer') return if seg.ACK: if seg.ack > self.snd_nxt: self.warn('invalid ACK, ack (%d) > %d', seg.ack, self.snd_nxt) return elif seg.ack < self.snd_una: self.warn('obsolete ACK') return else: self._ack_received(seg) elif self._st not in (TCPS.FIN_WAIT_1, TCPS.FIN_WAIT_2): self.warn('ACK is off') return # data transfer if self._st == TCPS.ESTABLISHED: if seg.seq == self.rcv_nxt: if seg.data: self._inq.put(bytes(seg.data)) self.rcv_nxt = seg.seq + seg.len # TODO: delayed ACK instead of immediate ACK self._send(None, self.snd_nxt, self.rcv_nxt, None) if seg.FIN: # passive close self._inq.put(None) self._trans(TCPS.CLOSE_WAIT) else: self.warn('OO segment') return # passive close if self._st == TCPS.CLOSE_WAIT: if seg.FIN: log.warn('FIN_ACK lost, resent') self._send(None, self.snd_nxt, self.rcv_nxt, None) return if self._st == TCPS.LAST_ACK: self.log('Connection closed (passive)') self._close('Connection closed (passive)') return # active close if self._st == TCPS.FIN_WAIT_1: if seg.seq == self.rcv_nxt: if seg.data: self.warn('RECV data in FIN_WAIT_1') self._inq.put(bytes(seg.data)) self.rcv_nxt = seg.seq + seg.len if seg.ACK: if seg.FIN: self._send(None, self.snd_nxt, self.rcv_nxt, None) self._tw_tmo = time.time() + MSL * 2 self._trans(TCPS.TIME_WAIT) else: self._trans(TCPS.FIN_WAIT_2) elif seg.FIN: self.warn('simultaneous FIN') self._send(None, self.snd_nxt, self.rcv_nxt, None) self._trans(TCPS.CLOSING) else: self.warn('OO segment') return if self._st == TCPS.FIN_WAIT_2: if seg.seq == self.rcv_nxt: if seg.data: self.warn('RECV data in FIN_WAIT_2') self._inq.put(bytes(seg.data)) self.rcv_nxt = seg.seq + seg.len if seg.FIN: self._send(None, self.snd_nxt, self.rcv_nxt, None) self._tw_tmo = time.time() + MSL * 2 self._trans(TCPS.TIME_WAIT) else: self.warn('OO segment') return if self._st == TCPS.CLOSING: self._tw_tmo = time.time() + MSL * 2 self._trans(TCPS.TIME_WAIT) return # time wait if self._st == TCPS.TIME_WAIT: if seg.FIN: self.warn('lost FIN_ACK, resent') self._send(None, self.snd_nxt, seg.seq + 1, None) self._tw_tmo = time.time() + MSL * 2 else: self.warn('spurious segment in TIME_WAIT') return # shouldn't reach here self.error('***unhandled segment!') assert 0