Initial working version

This commit is contained in:
Dennis Schwerdel 2015-11-19 16:34:20 +01:00
commit 84813fa011
10 changed files with 776 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target
Cargo.lock
._*

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "ethcloud"
version = "0.1.0"
authors = ["Dennis Schwerdel <schwerdel@informatik.uni-kl.de>"]
build = "build.rs"
[dependencies]
time = "0.1"
docopt = "0.6"
rustc-serialize = "0.3"
log = "*"
env_logger = "*"
[build-dependencies]
gcc = "0.3"

5
build.rs Normal file
View File

@ -0,0 +1,5 @@
extern crate gcc;
fn main() {
gcc::Config::new().file("src/c/tapdev.c").include("src").compile("libtapdev.a");
}

9
src/c/Makefile Normal file
View File

@ -0,0 +1,9 @@
default: libtapdev.a
tapdev.o: tapdev.c
gcc -Os -c tapdev.c
libtapdev.a: tapdev.o
ar rcs libtapdev.a tapdev.o

15
src/c/tapdev.c Normal file
View File

@ -0,0 +1,15 @@
#include <stdint.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <string.h>
#include <sys/ioctl.h>
int32_t setup_tap_device(int32_t fd, char *ifname) {
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) return 1;
strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
return 0;
}

301
src/ethcloud.rs Normal file
View File

@ -0,0 +1,301 @@
use std::net::{SocketAddr, ToSocketAddrs};
use std::collections::HashMap;
use std::hash::Hasher;
use std::net::UdpSocket;
use std::io::{Read, ErrorKind};
use std::os::unix::io::AsRawFd;
use std::fmt;
use time::{Duration, SteadyTime};
use libc;
pub use ethernet::{encode as eth_encode, decode as eth_decode, Frame as EthernetFrame};
pub use tapdev::TapDevice;
pub use udpmessage::{encode as udp_encode, decode as udp_decode, Message as UdpMessage};
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Mac(pub [u8; 6]);
impl fmt::Debug for Mac {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5])
}
}
pub type Token = u64;
#[derive(Debug)]
pub enum Error {
ParseError(&'static str),
WrongToken(Token),
SocketError(&'static str),
TapdevError(&'static str),
}
struct PeerList {
timeout: Duration,
peers: HashMap<SocketAddr, SteadyTime>
}
impl PeerList {
fn new(timeout: Duration) -> PeerList {
PeerList{peers: HashMap::new(), timeout: timeout}
}
fn timeout(&mut self) {
let now = SteadyTime::now();
let mut del: Vec<SocketAddr> = Vec::new();
for (&addr, &timeout) in &self.peers {
if timeout < now {
del.push(addr);
}
}
for addr in del {
debug!("Forgot peer: {:?}", addr);
self.peers.remove(&addr);
}
}
fn contains(&mut self, addr: &SocketAddr) -> bool {
self.peers.contains_key(addr)
}
fn add(&mut self, addr: &SocketAddr) {
if self.peers.insert(*addr, SteadyTime::now()+self.timeout).is_none() {
info!("New peer: {:?}", addr);
}
}
fn as_vec(&self) -> Vec<SocketAddr> {
self.peers.keys().map(|addr| *addr).collect()
}
fn remove(&mut self, addr: &SocketAddr) {
if self.peers.remove(&addr).is_some() {
info!("Removed peer: {:?}", addr);
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
struct MacTableKey {
mac: Mac,
vlan: u16
}
struct MacTableValue {
address: SocketAddr,
timeout: SteadyTime
}
struct MacTable {
table: HashMap<MacTableKey, MacTableValue>,
timeout: Duration
}
impl MacTable {
fn new(timeout: Duration) -> MacTable {
MacTable{table: HashMap::new(), timeout: timeout}
}
fn timeout(&mut self) {
let now = SteadyTime::now();
let mut del: Vec<MacTableKey> = Vec::new();
for (&key, val) in &self.table {
if val.timeout < now {
del.push(key);
}
}
for key in del {
info!("Forgot mac: {:?} (vlan {})", key.mac, key.vlan);
self.table.remove(&key);
}
}
fn learn(&mut self, mac: &Mac, vlan: u16, addr: &SocketAddr) {
let key = MacTableKey{mac: *mac, vlan: vlan};
let value = MacTableValue{address: *addr, timeout: SteadyTime::now()+self.timeout};
if self.table.insert(key, value).is_none() {
info!("Learned mac: {:?} (vlan {}) => {}", mac, vlan, addr);
}
}
fn lookup(&self, mac: &Mac, vlan: u16) -> Option<SocketAddr> {
let key = MacTableKey{mac: *mac, vlan: vlan};
match self.table.get(&key) {
Some(value) => Some(value.address),
None => None
}
}
}
pub struct EthCloud {
peers: PeerList,
mactable: MacTable,
socket: UdpSocket,
tapdev: TapDevice,
token: Token,
next_peerlist: SteadyTime,
update_freq: Duration
}
impl EthCloud {
pub fn new(device: &str, listen: String, token: Token, mac_timeout: Duration, peer_timeout: Duration) -> Self {
let socket = match UdpSocket::bind(&listen as &str) {
Ok(socket) => socket,
_ => panic!("Failed to open socket")
};
let res: i32;
unsafe {
res = libc::fcntl(socket.as_raw_fd(), libc::consts::os::posix01::F_SETFL, libc::consts::os::extra::O_NONBLOCK);
}
if res != 0 {
panic!("Failed to set socket to non-blocking");
}
let tapdev = match TapDevice::new(device) {
Ok(tapdev) => tapdev,
_ => panic!("Failed to open tap device")
};
info!("Opened tap device {}", tapdev.ifname());
EthCloud{
peers: PeerList::new(peer_timeout),
mactable: MacTable::new(mac_timeout),
socket: socket,
tapdev: tapdev,
token: token,
next_peerlist: SteadyTime::now(),
update_freq: peer_timeout/2
}
}
fn send_msg<A: ToSocketAddrs + fmt::Display>(&mut self, addr: A, msg: &UdpMessage) -> Result<(), Error> {
debug!("Sending {:?} to {}", msg, addr);
let mut buffer = [0u8; 64*1024];
let size = udp_encode(self.token, msg, &mut buffer);
match self.socket.send_to(&buffer[..size], addr) {
Ok(written) if written == size => Ok(()),
Ok(_) => Err(Error::SocketError("Sent out truncated packet")),
Err(e) => {
error!("Failed to send via network {:?}", e);
Err(Error::SocketError("IOError when sending"))
}
}
}
pub fn connect<A: ToSocketAddrs + fmt::Display>(&mut self, addr: A) -> Result<(), Error> {
info!("Connecting to {}", addr);
self.send_msg(addr, &UdpMessage::GetPeers)
}
fn housekeep(&mut self) -> Result<(), Error> {
self.peers.timeout();
self.mactable.timeout();
if self.next_peerlist <= SteadyTime::now() {
debug!("Send peer list to all peers");
let peers = self.peers.as_vec();
let msg = UdpMessage::Peers(peers);
for addr in &self.peers.as_vec() {
try!(self.send_msg(addr, &msg));
}
self.next_peerlist = SteadyTime::now() + self.update_freq;
}
Ok(())
}
fn handle_ethernet_frame(&mut self, frame: EthernetFrame) -> Result<(), Error> {
debug!("Read ethernet frame from tap {:?}", frame);
match self.mactable.lookup(frame.dst, frame.vlan) {
Some(addr) => {
debug!("Found destination for {:?} (vlan {}) => {}", frame.dst, frame.vlan, addr);
try!(self.send_msg(addr, &UdpMessage::Frame(frame)))
},
None => {
debug!("No destination for {:?} (vlan {}) found, broadcasting", frame.dst, frame.vlan);
let msg = UdpMessage::Frame(frame);
for addr in &self.peers.as_vec() {
try!(self.send_msg(addr, &msg));
}
}
}
Ok(())
}
fn handle_net_message(&mut self, peer: SocketAddr, token: Token, msg: UdpMessage) -> Result<(), Error> {
if token != self.token {
info!("Ignoring message from {} with wrong token {}", peer, token);
return Err(Error::WrongToken(token));
}
debug!("Recieved {:?} from {}", msg, peer);
match msg {
UdpMessage::Frame(frame) => {
self.peers.add(&peer);
self.mactable.learn(frame.src, frame.vlan, &peer);
let mut buffer = [0u8; 64*1024];
let size = eth_encode(&frame, &mut buffer);
debug!("Writing ethernet frame to tap: {:?}", frame);
match self.tapdev.write(&buffer[..size]) {
Ok(()) => (),
Err(e) => {
error!("Failed to send via tap device {:?}", e);
return Err(Error::TapdevError("Failed to write to tap device"));
}
}
},
UdpMessage::Peers(peers) => {
self.peers.add(&peer);
for p in &peers {
if ! self.peers.contains(p) {
try!(self.connect(p));
}
}
},
UdpMessage::GetPeers => {
self.peers.add(&peer);
let peers = self.peers.as_vec();
try!(self.send_msg(peer, &UdpMessage::Peers(peers)));
},
UdpMessage::Close => self.peers.remove(&peer)
}
Ok(())
}
pub fn run(&mut self) {
let mut buffer = [0u8; 64*1024];
loop {
match self.socket.recv_from(&mut buffer) {
Ok((size, src)) => {
match udp_decode(&buffer[..size]).and_then(|(token, msg)| self.handle_net_message(src, token, msg)) {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
}
},
Err(error) => match error.kind() {
ErrorKind::WouldBlock => (),
_ => panic!("Failed to read from network socket")
}
}
match self.tapdev.read(&mut buffer) {
Ok(size) => {
match eth_decode(&buffer[..size]).and_then(|frame| self.handle_ethernet_frame(frame)) {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
}
},
Err(error) => match error.kind() {
ErrorKind::WouldBlock => (),
_ => panic!("Failed to read from tap device")
}
}
match self.housekeep() {
Ok(_) => (),
Err(e) => error!("Error: {:?}", e)
}
}
}
}

102
src/ethernet.rs Normal file
View File

@ -0,0 +1,102 @@
use std::{mem, slice, ptr, fmt};
pub use super::{Mac, Error};
unsafe fn as_bytes<T>(obj: &T) -> &[u8] {
slice::from_raw_parts(mem::transmute::<&T, *const u8>(obj), mem::size_of::<T>())
}
unsafe fn as_obj<T>(data: &[u8]) -> &T {
assert!(data.len() >= mem::size_of::<T>());
mem::transmute(data.as_ptr())
}
#[derive(PartialEq)]
pub struct Frame<'a> {
pub vlan: u16,
pub src: &'a Mac,
pub dst: &'a Mac,
pub payload: &'a [u8]
}
impl<'a> fmt::Debug for Frame<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "src: {:?}, dst: {:?}, vlan: {}, payload: {} bytes",
self.src, self.dst, self.vlan, self.payload.len())
}
}
pub fn decode(data: &[u8]) -> Result<Frame, Error> {
if data.len() < 14 {
return Err(Error::ParseError("Frame is too short"));
}
let mut pos = 0;
let dst = unsafe { as_obj::<Mac>(&data[pos..]) };
pos += mem::size_of::<Mac>();
let src = unsafe { as_obj::<Mac>(&data[pos..]) };
pos += mem::size_of::<Mac>();
let mut vlan = 0;
let mut payload = &data[pos..];
if data[pos] == 0x81 && data[pos+1] == 0x00 {
pos += 2;
if data.len() < pos + 2 {
return Err(Error::ParseError("Vlan frame is too short"));
}
vlan = u16::from_be(* unsafe { as_obj::<u16>(&data[pos..]) });
pos += 2;
payload = &data[pos..];
}
Ok(Frame{vlan: vlan, src: src, dst: dst, payload: payload})
}
pub fn encode(frame: &Frame, buf: &mut [u8]) -> usize {
assert!(buf.len() >= 16 + frame.payload.len());
let mut pos = 0;
unsafe {
let dst_dat = as_bytes::<Mac>(frame.dst);
ptr::copy_nonoverlapping(dst_dat.as_ptr(), buf[pos..].as_mut_ptr(), dst_dat.len());
pos += dst_dat.len();
let src_dat = as_bytes::<Mac>(frame.src);
ptr::copy_nonoverlapping(src_dat.as_ptr(), buf[pos..].as_mut_ptr(), src_dat.len());
pos += src_dat.len();
if frame.vlan != 0 {
buf[pos] = 0x81; buf[pos+1] = 0x00;
pos += 2;
let vlan_dat = mem::transmute::<u16, [u8; 2]>(frame.vlan.to_be());
ptr::copy_nonoverlapping(vlan_dat.as_ptr(), buf[pos..].as_mut_ptr(), vlan_dat.len());
pos += vlan_dat.len();
}
ptr::copy_nonoverlapping(frame.payload.as_ptr(), buf[pos..].as_mut_ptr(), frame.payload.len());
}
pos += frame.payload.len();
pos
}
#[test]
fn without_vlan() {
let src = Mac([1,2,3,4,5,6]);
let dst = Mac([6,5,4,3,2,1]);
let payload = [1,2,3,4,5,6,7,8];
let mut buf = [0u8; 1024];
let frame = Frame{src: &src, dst: &dst, vlan: 0, payload: &payload};
let size = encode(&frame, &mut buf);
assert_eq!(size, 20);
assert_eq!(&buf[..size], &[6,5,4,3,2,1,1,2,3,4,5,6,1,2,3,4,5,6,7,8]);
let frame2 = decode(&buf[..size]).unwrap();
assert_eq!(frame, frame2);
}
#[test]
fn with_vlan() {
let src = Mac([1,2,3,4,5,6]);
let dst = Mac([6,5,4,3,2,1]);
let payload = [1,2,3,4,5,6,7,8];
let mut buf = [0u8; 1024];
let frame = Frame{src: &src, dst: &dst, vlan: 1234, payload: &payload};
let size = encode(&frame, &mut buf);
assert_eq!(size, 24);
assert_eq!(&buf[..size], &[6,5,4,3,2,1,1,2,3,4,5,6,0x81,0,4,210,1,2,3,4,5,6,7,8]);
let frame2 = decode(&buf[..size]).unwrap();
assert_eq!(frame, frame2);
}

86
src/main.rs Normal file
View File

@ -0,0 +1,86 @@
#![feature(libc)]
#[macro_use] extern crate log;
extern crate time;
extern crate libc;
extern crate docopt;
extern crate rustc_serialize;
mod udpmessage;
mod tapdev;
mod ethernet;
mod ethcloud;
use time::Duration;
use docopt::Docopt;
pub use ethcloud::{Error, Mac, Token, EthCloud};
pub use ethernet::{encode as eth_encode, decode as eth_decode, Frame as EthernetFrame};
pub use tapdev::TapDevice;
pub use udpmessage::{encode as udp_encode, decode as udp_decode, Message as UdpMessage};
struct SimpleLogger;
impl log::Log for SimpleLogger {
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
true
}
fn log(&self, record: &log::LogRecord) {
if self.enabled(record.metadata()) {
println!("{} - {}", record.level(), record.args());
}
}
}
static USAGE: &'static str = "
Usage:
ethcloud [options]
Options:
-d <device>, --device <device> Name of the tap device [default: ethcloud%d]
-l <listen>, --listen <listen> Address to listen on [default: 0.0.0.0:3210]
-t <token>, --token <token> Token that identifies the network [default: 0]
-c <connect>, --connect <connect> List of peers (addr:port) to connect to
--peer-timeout <peer_timeout> Peer timeout in seconds [default: 300]
--mac-timeout <mac_timeout> Mac table entry timeout in seconds [default: 60]
-v, --verbose Log verbosely
";
#[derive(RustcDecodable, Debug)]
struct Args {
flag_device: String,
flag_listen: String,
flag_token: Token,
flag_connect: Vec<String>,
flag_peer_timeout: usize,
flag_mac_timeout: usize,
flag_verbose: bool
}
fn main() {
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
log::set_logger(|max_log_level| {
if args.flag_verbose {
max_log_level.set(log::LogLevelFilter::Debug);
} else {
max_log_level.set(log::LogLevelFilter::Info);
}
Box::new(SimpleLogger)
}).unwrap();
debug!("Args: {:?}", args);
let mut tapcloud = EthCloud::new(
&args.flag_device,
args.flag_listen,
args.flag_token,
Duration::seconds(args.flag_mac_timeout as i64),
Duration::seconds(args.flag_peer_timeout as i64)
);
for addr in args.flag_connect {
tapcloud.connect(&addr as &str).expect("Failed to send");
}
tapcloud.run();
}

49
src/tapdev.rs Normal file
View File

@ -0,0 +1,49 @@
use std::fs;
use std::io::{Read, Write, Result as IoResult, Error as IoError};
use std::os::unix::io::AsRawFd;
use libc;
extern {
fn setup_tap_device(fd: i32, ifname: *mut u8) -> i32;
}
pub struct TapDevice {
fd: fs::File,
ifname: String
}
impl TapDevice {
pub fn new(ifname: &str) -> IoResult<TapDevice> {
let fd = try!(fs::OpenOptions::new().read(true).write(true).open("/dev/net/tun"));
let mut ifname_string = String::with_capacity(32);
ifname_string.push_str(ifname);
ifname_string.push('\0');
let mut ifname_c = ifname_string.into_bytes();
let mut res: i32;
unsafe {
res = setup_tap_device(fd.as_raw_fd(), ifname_c.as_mut_ptr());
if res == 0 {
res = libc::fcntl(fd.as_raw_fd(), libc::consts::os::posix01::F_SETFL, libc::consts::os::extra::O_NONBLOCK);
}
}
match res {
0 => Ok(TapDevice{fd: fd, ifname: String::from_utf8(ifname_c).unwrap()}),
_ => Err(IoError::last_os_error())
}
}
pub fn ifname(&self) -> &str {
&self.ifname
}
#[inline(always)]
pub fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
self.fd.read(buffer)
}
#[inline(always)]
pub fn write(&mut self, buffer: &[u8]) -> IoResult<()> {
self.fd.write_all(buffer)
}
}

191
src/udpmessage.rs Normal file
View File

@ -0,0 +1,191 @@
use std::{mem, ptr, fmt};
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::u16;
pub use super::{Mac, Error, Token, EthernetFrame, eth_decode, eth_encode};
unsafe fn as_obj<T>(data: &[u8]) -> &T {
assert!(data.len() >= mem::size_of::<T>());
mem::transmute(data.as_ptr())
}
#[derive(PartialEq)]
pub enum Message<'a> {
Frame(EthernetFrame<'a>),
Peers(Vec<SocketAddr>),
GetPeers,
Close,
}
impl<'a> fmt::Debug for Message<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
&Message::Frame(ref frame) => write!(formatter, "Frame({:?})", frame),
&Message::Peers(ref peers) => {
try!(write!(formatter, "Peers ["));
let mut first = true;
for p in peers {
if !first {
try!(write!(formatter, ", "));
}
first = false;
try!(p.fmt(formatter));
}
write!(formatter, "]")
},
&Message::GetPeers => write!(formatter, "GetPeers"),
&Message::Close => write!(formatter, "Close"),
}
}
}
pub fn decode(data: &[u8]) -> Result<(Token, Message), Error> {
if data.len() < 1 + mem::size_of::<Token>() {
return Err(Error::ParseError("Empty message"));
}
let mut pos = 0;
let token = Token::from_be(* unsafe { as_obj::<Token>(&data[pos..]) });
pos += mem::size_of::<Token>();
match data[pos] {
0 => {
pos += 1;
Ok((token, Message::Frame(try!(eth_decode(&data[pos..])))))
},
1 => {
pos += 1;
if data.len() < pos + 1 {
return Err(Error::ParseError("Empty peers"));
}
let count = data[pos];
pos += 1;
let len = count as usize * 6;
if data.len() < pos + len {
return Err(Error::ParseError("Peer data too short"));
}
let mut peers = Vec::with_capacity(count as usize);
for _ in 0..count {
let (ip, port) = unsafe {
let ip = as_obj::<[u8; 4]>(&data[pos..]);
pos += 4;
let port = *as_obj::<u16>(&data[pos..]);
let port = u16::from_be(port);
pos += 2;
(ip, port)
};
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]), port));
peers.push(addr);
}
Ok((token, Message::Peers(peers)))
},
2 => Ok((token, Message::GetPeers)),
3 => Ok((token, Message::Close)),
_ => Err(Error::ParseError("Unknown message type"))
}
}
pub fn encode(token: Token, msg: &Message, buf: &mut [u8]) -> usize {
assert!(buf.len() >= mem::size_of::<Token>() + 1);
let mut pos = 0;
let token_dat = unsafe { mem::transmute::<Token, [u8; 8]>(token.to_be()) };
unsafe { ptr::copy_nonoverlapping(token_dat.as_ptr(), buf[pos..].as_mut_ptr(), token_dat.len()) };
pos += token_dat.len();
match msg {
&Message::Frame(ref frame) => {
buf[pos] = 0;
pos += 1;
pos += eth_encode(&frame, &mut buf[pos..])
},
&Message::Peers(ref peers) => {
buf[pos] = 1;
pos += 1;
let count_pos = pos;
pos += 1;
assert!(buf.len() >= 2 + peers.len() * mem::size_of::<SocketAddrV4>());
let mut count = 0;
for p in peers {
match p {
&SocketAddr::V4(addr) => {
let ip = addr.ip().octets();
let port = addr.port();
unsafe {
ptr::copy_nonoverlapping(ip.as_ptr(), buf[pos..].as_mut_ptr(), ip.len());
pos += ip.len();
let port = mem::transmute::<u16, [u8; 2]>(port.to_be());
ptr::copy_nonoverlapping(port.as_ptr(), buf[pos..].as_mut_ptr(), port.len());
pos += port.len();
}
count += 1;
},
&SocketAddr::V6(_addr) => unimplemented!()
}
};
buf[count_pos] = count;
},
&Message::GetPeers => {
buf[pos] = 2;
pos += 1;
},
&Message::Close => {
buf[pos] = 3;
pos += 1;
}
}
pos
}
#[test]
fn encode_message_packet() {
let token = 134;
let src = Mac([1,2,3,4,5,6]);
let dst = Mac([7,8,9,10,11,12]);
let payload = [1,2,3,4,5];
let msg = Message::Frame(EthernetFrame{src: &src, dst: &dst, vlan: 0, payload: &payload});
let mut buf = [0; 1024];
let size = encode(token, &msg, &mut buf[..]);
assert_eq!(size, 26);
assert_eq!(&buf[..9], &[0,0,0,0,0,0,0,134,0]);
let (token2, msg2) = decode(&buf[..size]).unwrap();
assert_eq!(token, token2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_peers() {
use std::str::FromStr;
let token = 134;
let msg = Message::Peers(vec![SocketAddr::from_str("1.2.3.4:123").unwrap(), SocketAddr::from_str("5.6.7.8:12345").unwrap()]);
let mut buf = [0; 1024];
let size = encode(token, &msg, &mut buf[..]);
assert_eq!(size, 22);
assert_eq!(&buf[..size], &[0,0,0,0,0,0,0,134,1,2,1,2,3,4,0,123,5,6,7,8,48,57]);
let (token2, msg2) = decode(&buf[..size]).unwrap();
assert_eq!(token, token2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_getpeers() {
let token = 134;
let msg = Message::GetPeers;
let mut buf = [0; 1024];
let size = encode(token, &msg, &mut buf[..]);
assert_eq!(size, 9);
assert_eq!(&buf[..size], &[0,0,0,0,0,0,0,134,2]);
let (token2, msg2) = decode(&buf[..size]).unwrap();
assert_eq!(token, token2);
assert_eq!(msg, msg2);
}
#[test]
fn encode_message_close() {
let token = 134;
let msg = Message::Close;
let mut buf = [0; 1024];
let size = encode(token, &msg, &mut buf[..]);
assert_eq!(size, 9);
assert_eq!(&buf[..size], &[0,0,0,0,0,0,0,134,3]);
let (token2, msg2) = decode(&buf[..size]).unwrap();
assert_eq!(token, token2);
assert_eq!(msg, msg2);
}