Add settings for TCP
This commit is contained in:
		
							parent
							
								
									bef3884d2a
								
							
						
					
					
						commit
						e3635f37f6
					
				
					 1 changed files with 419 additions and 300 deletions
				
			
		|  | @ -9,12 +9,13 @@ use std::cell::RefCell; | |||
| use std::fs::File; | ||||
| use std::io::{self, Read, Write}; | ||||
| use std::{mem, slice, str}; | ||||
| use std::ops::{Deref, DerefMut}; | ||||
| use std::os::unix::io::FromRawFd; | ||||
| use std::rc::Rc; | ||||
| 
 | ||||
| use event::EventQueue; | ||||
| use netutils::{n16, n32, Ipv4, Ipv4Addr, Ipv4Header, Tcp, TcpHeader, Checksum, TCP_FIN, TCP_SYN, TCP_RST, TCP_PSH, TCP_ACK}; | ||||
| use syscall::data::Packet; | ||||
| use syscall::data::{Packet, TimeSpec}; | ||||
| use syscall::error::{Error, Result, EACCES, EADDRINUSE, EBADF, EIO, EINVAL, EISCONN, EMSGSIZE, ENOTCONN, EWOULDBLOCK}; | ||||
| use syscall::flag::{EVENT_READ, F_GETFL, F_SETFL, O_ACCMODE, O_CREAT, O_RDWR, O_NONBLOCK}; | ||||
| use syscall::scheme::SchemeMut; | ||||
|  | @ -41,11 +42,14 @@ enum State { | |||
|     Closed | ||||
| } | ||||
| 
 | ||||
| struct Handle { | ||||
| struct TcpHandle { | ||||
|     local: (Ipv4Addr, u16), | ||||
|     remote: (Ipv4Addr, u16), | ||||
|     flags: usize, | ||||
|     events: usize, | ||||
|     read_timeout: Option<TimeSpec>, | ||||
|     write_timeout: Option<TimeSpec>, | ||||
|     ttl: u8, | ||||
|     state: State, | ||||
|     seq: u32, | ||||
|     ack: u32, | ||||
|  | @ -55,7 +59,7 @@ struct Handle { | |||
|     todo_write: VecDeque<Packet>, | ||||
| } | ||||
| 
 | ||||
| impl Handle { | ||||
| impl TcpHandle { | ||||
|     fn is_connected(&self) -> bool { | ||||
|         self.remote.0 != Ipv4Addr::NULL && self.remote.1 != 0 | ||||
|     } | ||||
|  | @ -100,7 +104,7 @@ impl Handle { | |||
|                 len: n16::new((data.len() + mem::size_of::<Ipv4Header>()) as u16), | ||||
|                 id: n16::new(id), | ||||
|                 flags_fragment: n16::new(0), | ||||
|                 ttl: 127, | ||||
|                 ttl: self.ttl, | ||||
|                 proto: 0x06, | ||||
|                 checksum: Checksum { data: 0 }, | ||||
|                 src: self.local.0, | ||||
|  | @ -112,6 +116,18 @@ impl Handle { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Copy, Clone)] | ||||
| enum SettingKind { | ||||
|     Ttl, | ||||
|     ReadTimeout, | ||||
|     WriteTimeout | ||||
| } | ||||
| 
 | ||||
| enum Handle { | ||||
|     Tcp(TcpHandle), | ||||
|     Setting(usize, SettingKind), | ||||
| } | ||||
| 
 | ||||
| struct Tcpd { | ||||
|     scheme_file: File, | ||||
|     tcp_file: File, | ||||
|  | @ -144,6 +160,7 @@ impl Tcpd { | |||
|             self.handle(&mut packet); | ||||
|             if packet.a == (-EWOULDBLOCK) as usize { | ||||
|                 if let Some(mut handle) = self.handles.get_mut(&packet.b) { | ||||
|                     if let Handle::Tcp(ref mut handle) = *handle { | ||||
|                         match a { | ||||
|                             syscall::number::SYS_DUP => { | ||||
|                                 packet.a = a; | ||||
|  | @ -162,6 +179,7 @@ impl Tcpd { | |||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 self.scheme_file.write(&packet)?; | ||||
|             } | ||||
|  | @ -182,6 +200,7 @@ impl Tcpd { | |||
|                     let mut closing = Vec::new(); | ||||
|                     let mut found_connection = false; | ||||
|                     for (id, handle) in self.handles.iter_mut() { | ||||
|                         if let Handle::Tcp(ref mut handle) = *handle { | ||||
|                             if handle.state != State::Listen && handle.matches(&ip, &tcp) { | ||||
|                                 found_connection = true; | ||||
| 
 | ||||
|  | @ -299,10 +318,10 @@ impl Tcpd { | |||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     for file in closing { | ||||
|                         let handle = self.handles.remove(&file).unwrap(); | ||||
| 
 | ||||
|                         if let Handle::Tcp(handle) = self.handles.remove(&file).unwrap() { | ||||
|                             let remove = if let Some(mut port) = self.ports.get_mut(&handle.local.1) { | ||||
|                                 *port = *port + 1; | ||||
|                                 *port == 0 | ||||
|  | @ -314,11 +333,13 @@ impl Tcpd { | |||
|                                 self.ports.remove(&handle.local.1); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     if ! found_connection && tcp.header.flags.get() & (TCP_SYN | TCP_ACK) == TCP_SYN { | ||||
|                         let mut new_handles = Vec::new(); | ||||
| 
 | ||||
|                         for (_id, handle) in self.handles.iter_mut() { | ||||
|                             if let Handle::Tcp(ref mut handle) = *handle { | ||||
|                                 if handle.state == State::Listen && handle.matches(&ip, &tcp) { | ||||
|                                     handle.data.push_back((ip.clone(), tcp.clone())); | ||||
| 
 | ||||
|  | @ -326,11 +347,14 @@ impl Tcpd { | |||
|                                         let mut packet = handle.todo_dup.pop_front().unwrap(); | ||||
|                                         let (ip, tcp) = handle.data.pop_front().unwrap(); | ||||
| 
 | ||||
|                                     let mut new_handle = Handle { | ||||
|                                         let mut new_handle = TcpHandle { | ||||
|                                             local: handle.local, | ||||
|                                             remote: (ip.header.src, tcp.header.src.get()), | ||||
|                                             flags: handle.flags, | ||||
|                                             events: 0, | ||||
|                                             read_timeout: handle.read_timeout, | ||||
|                                             write_timeout: handle.write_timeout, | ||||
|                                             ttl: handle.ttl, | ||||
|                                             state: State::SynReceived, | ||||
|                                             seq: self.rng.gen(), | ||||
|                                             ack: tcp.header.sequence.get() + 1, | ||||
|  | @ -363,7 +387,8 @@ impl Tcpd { | |||
| 
 | ||||
|                                         packet.a = id; | ||||
| 
 | ||||
|                                     new_handles.push((packet, new_handle)); | ||||
|                                         new_handles.push((packet, Handle::Tcp(new_handle))); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | @ -401,11 +426,14 @@ impl SchemeMut for Tcpd { | |||
|             return Err(Error::new(EADDRINUSE)); | ||||
|         } | ||||
| 
 | ||||
|         let mut handle = Handle { | ||||
|         let mut handle = TcpHandle { | ||||
|             local: local, | ||||
|             remote: remote, | ||||
|             flags: flags, | ||||
|             events: 0, | ||||
|             read_timeout: None, | ||||
|             write_timeout: None, | ||||
|             ttl: 64, | ||||
|             state: State::Listen, | ||||
|             seq: 0, | ||||
|             ack: 0, | ||||
|  | @ -432,22 +460,22 @@ impl SchemeMut for Tcpd { | |||
|         let id = self.next_id; | ||||
|         self.next_id += 1; | ||||
| 
 | ||||
|         self.handles.insert(id, handle); | ||||
|         self.handles.insert(id, Handle::Tcp(handle)); | ||||
| 
 | ||||
|         Ok(id) | ||||
|     } | ||||
| 
 | ||||
|     fn dup(&mut self, file: usize, buf: &[u8]) -> Result<usize> { | ||||
|         let path = str::from_utf8(buf).or(Err(Error::new(EINVAL)))?; | ||||
| 
 | ||||
|         let handle = { | ||||
|             let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|             let mut new_handle = Handle { | ||||
|         let handle = match *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             Handle::Tcp(ref mut handle) => { | ||||
|                 let mut new_handle = TcpHandle { | ||||
|                     local: handle.local, | ||||
|                     remote: handle.remote, | ||||
|                     flags: handle.flags, | ||||
|                     events: 0, | ||||
|                     read_timeout: handle.read_timeout, | ||||
|                     write_timeout: handle.write_timeout, | ||||
|                     ttl: handle.ttl, | ||||
|                     state: handle.state, | ||||
|                     seq: handle.seq, | ||||
|                     ack: handle.ack, | ||||
|  | @ -457,7 +485,15 @@ impl SchemeMut for Tcpd { | |||
|                     todo_write: VecDeque::new(), | ||||
|                 }; | ||||
| 
 | ||||
|             if path == "listen" { | ||||
|                 let path = str::from_utf8(buf).or(Err(Error::new(EINVAL)))?; | ||||
| 
 | ||||
|                 if path == "ttl" { | ||||
|                     Handle::Setting(file, SettingKind::Ttl) | ||||
|                 } else if path == "read_timeout" { | ||||
|                     Handle::Setting(file, SettingKind::ReadTimeout) | ||||
|                 } else if path == "write_timeout" { | ||||
|                     Handle::Setting(file, SettingKind::WriteTimeout) | ||||
|                 } else if path == "listen" { | ||||
|                     if handle.is_connected() { | ||||
|                         return Err(Error::new(EISCONN)); | ||||
|                     } else if let Some((ip, tcp)) = handle.data.pop_front() { | ||||
|  | @ -483,8 +519,12 @@ impl SchemeMut for Tcpd { | |||
|                             true | ||||
|                         } | ||||
|                     }); | ||||
| 
 | ||||
|                     Handle::Tcp(new_handle) | ||||
|                 } else if path.is_empty() { | ||||
|                     new_handle.data = handle.data.clone(); | ||||
| 
 | ||||
|                     Handle::Tcp(new_handle) | ||||
|                 } else if handle.is_connected() { | ||||
|                     return Err(Error::new(EISCONN)); | ||||
|                 } else { | ||||
|  | @ -507,17 +547,17 @@ impl SchemeMut for Tcpd { | |||
|                         let tcp = new_handle.create_tcp(TCP_SYN, Vec::new()); | ||||
|                         let ip = new_handle.create_ip(self.rng.gen(), tcp.to_bytes()); | ||||
|                         self.tcp_file.write(&ip.to_bytes()).map_err(|err| Error::new(err.raw_os_error().unwrap_or(EIO))).and(Ok(buf.len()))?; | ||||
| 
 | ||||
|                         Handle::Tcp(new_handle) | ||||
|                     } else { | ||||
|                         return Err(Error::new(EINVAL)); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             new_handle | ||||
|         }; | ||||
| 
 | ||||
|         if let Some(mut port) = self.ports.get_mut(&handle.local.1) { | ||||
|             *port = *port + 1; | ||||
|             }, | ||||
|             Handle::Setting(file, kind) => { | ||||
|                 Handle::Setting(file, kind) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let id = self.next_id; | ||||
|         self.next_id += 1; | ||||
|  | @ -528,10 +568,10 @@ impl SchemeMut for Tcpd { | |||
|     } | ||||
| 
 | ||||
|     fn read(&mut self, file: usize, buf: &mut [u8]) -> Result<usize> { | ||||
|         let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|         let (file, kind) = match *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             Handle::Tcp(ref mut handle) => { | ||||
|                 if ! handle.is_connected() { | ||||
|             Err(Error::new(ENOTCONN)) | ||||
|                     return Err(Error::new(ENOTCONN)); | ||||
|                 } else if let Some((_ip, tcp)) = handle.data.pop_front() { | ||||
|                     let mut i = 0; | ||||
|                     while i < buf.len() && i < tcp.data.len() { | ||||
|  | @ -539,21 +579,55 @@ impl SchemeMut for Tcpd { | |||
|                         i += 1; | ||||
|                     } | ||||
| 
 | ||||
|             Ok(i) | ||||
|                     return Ok(i); | ||||
|                 } else if handle.flags & O_NONBLOCK == O_NONBLOCK || handle.read_closed() { | ||||
|             Ok(0) | ||||
|                     return Ok(0); | ||||
|                 } else { | ||||
|             Err(Error::new(EWOULDBLOCK)) | ||||
|                     return Err(Error::new(EWOULDBLOCK)); | ||||
|                 } | ||||
|             }, | ||||
|             Handle::Setting(file, kind) => { | ||||
|                 (file, kind) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             let read_timeout = |timeout: &Option<TimeSpec>, buf: &mut [u8]| -> Result<usize> { | ||||
|                 if let Some(ref timespec) = *timeout { | ||||
|                     timespec.deref().read(buf).map_err(|err| Error::new(err.raw_os_error().unwrap_or(EIO))) | ||||
|                 } else { | ||||
|                     Ok(0) | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             match kind { | ||||
|                 SettingKind::Ttl => { | ||||
|                     if let Some(mut ttl) = buf.get_mut(0) { | ||||
|                         *ttl = handle.ttl; | ||||
|                         Ok(1) | ||||
|                     } else { | ||||
|                         Ok(0) | ||||
|                     } | ||||
|                 }, | ||||
|                 SettingKind::ReadTimeout => { | ||||
|                     read_timeout(&handle.read_timeout, buf) | ||||
|                 }, | ||||
|                 SettingKind::WriteTimeout => { | ||||
|                     read_timeout(&handle.write_timeout, buf) | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             Err(Error::new(EBADF)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn write(&mut self, file: usize, buf: &[u8]) -> Result<usize> { | ||||
|         let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|         let (file, kind) = match *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             Handle::Tcp(ref mut handle) => { | ||||
|                 if ! handle.is_connected() { | ||||
|             Err(Error::new(ENOTCONN)) | ||||
|                     return Err(Error::new(ENOTCONN)); | ||||
|                 } else if buf.len() >= 65507 { | ||||
|             Err(Error::new(EMSGSIZE)) | ||||
|                     return Err(Error::new(EMSGSIZE)); | ||||
|                 } else { | ||||
|                     match handle.state { | ||||
|                         State::Established => { | ||||
|  | @ -561,18 +635,55 @@ impl SchemeMut for Tcpd { | |||
|                             let ip = handle.create_ip(self.rng.gen(), tcp.to_bytes()); | ||||
|                             self.tcp_file.write(&ip.to_bytes()).map_err(|err| Error::new(err.raw_os_error().unwrap_or(EIO)))?; | ||||
|                             handle.seq += buf.len() as u32; | ||||
|                     Ok(buf.len()) | ||||
|                             return Ok(buf.len()); | ||||
|                         }, | ||||
|                         _ => { | ||||
|                     Err(Error::new(EWOULDBLOCK)) | ||||
|                             return Err(Error::new(EWOULDBLOCK)); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             Handle::Setting(file, kind) => { | ||||
|                 (file, kind) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             let write_timeout = |timeout: &mut Option<TimeSpec>, buf: &[u8]| -> Result<usize> { | ||||
|                 if buf.len() >= mem::size_of::<TimeSpec>() { | ||||
|                     let mut timespec = TimeSpec::default(); | ||||
|                     let count = timespec.deref_mut().write(buf).map_err(|err| Error::new(err.raw_os_error().unwrap_or(EIO)))?; | ||||
|                     *timeout = Some(timespec); | ||||
|                     Ok(count) | ||||
|                 } else { | ||||
|                     *timeout = None; | ||||
|                     Ok(0) | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             match kind { | ||||
|                 SettingKind::Ttl => { | ||||
|                     if let Some(ttl) = buf.get(0) { | ||||
|                         handle.ttl = *ttl; | ||||
|                         Ok(1) | ||||
|                     } else { | ||||
|                         Ok(0) | ||||
|                     } | ||||
|                 }, | ||||
|                 SettingKind::ReadTimeout => { | ||||
|                     write_timeout(&mut handle.read_timeout, buf) | ||||
|                 }, | ||||
|                 SettingKind::WriteTimeout => { | ||||
|                     write_timeout(&mut handle.write_timeout, buf) | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             Err(Error::new(EBADF)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn fcntl(&mut self, file: usize, cmd: usize, arg: usize) -> Result<usize> { | ||||
|         let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|         if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             match cmd { | ||||
|                 F_GETFL => Ok(handle.flags), | ||||
|                 F_SETFL => { | ||||
|  | @ -581,20 +692,23 @@ impl SchemeMut for Tcpd { | |||
|                 }, | ||||
|                 _ => Err(Error::new(EINVAL)) | ||||
|             } | ||||
|         } else { | ||||
|             Err(Error::new(EBADF)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn fevent(&mut self, file: usize, flags: usize) -> Result<usize> { | ||||
|         let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|         if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             handle.events = flags; | ||||
| 
 | ||||
|             Ok(file) | ||||
|         } else { | ||||
|             Err(Error::new(EBADF)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> { | ||||
|         let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|         let path_string = format!("tcp:{}:{}/{}:{}", handle.remote.0.to_string(), handle.remote.1, handle.local.0.to_string(), handle.local.1); | ||||
|     fn fpath(&mut self, file: usize, buf: &mut [u8]) -> Result<usize> { | ||||
|         if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|             let path_string = format!("udp:{}:{}/{}:{}", handle.remote.0.to_string(), handle.remote.1, handle.local.0.to_string(), handle.local.1); | ||||
|             let path = path_string.as_bytes(); | ||||
| 
 | ||||
|             let mut i = 0; | ||||
|  | @ -604,6 +718,9 @@ impl SchemeMut for Tcpd { | |||
|             } | ||||
| 
 | ||||
|             Ok(i) | ||||
|         } else { | ||||
|             Err(Error::new(EBADF)) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn fsync(&mut self, file: usize) -> Result<usize> { | ||||
|  | @ -614,8 +731,7 @@ impl SchemeMut for Tcpd { | |||
| 
 | ||||
|     fn close(&mut self, file: usize) -> Result<usize> { | ||||
|         let closed = { | ||||
|             let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|             if let Handle::Tcp(ref mut handle) = *self.handles.get_mut(&file).ok_or(Error::new(EBADF))? { | ||||
|                 handle.data.clear(); | ||||
| 
 | ||||
|                 match handle.state { | ||||
|  | @ -643,11 +759,13 @@ impl SchemeMut for Tcpd { | |||
|                     }, | ||||
|                     _ => true | ||||
|                 } | ||||
|             } else { | ||||
|                 true | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         if closed { | ||||
|             let handle = self.handles.remove(&file).ok_or(Error::new(EBADF))?; | ||||
| 
 | ||||
|             if let Handle::Tcp(handle) = self.handles.remove(&file).ok_or(Error::new(EBADF))? { | ||||
|                 let remove = if let Some(mut port) = self.ports.get_mut(&handle.local.1) { | ||||
|                     *port = *port + 1; | ||||
|                     *port == 0 | ||||
|  | @ -659,6 +777,7 @@ impl SchemeMut for Tcpd { | |||
|                     self.ports.remove(&handle.local.1); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(0) | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jeremy Soller
						Jeremy Soller