Cannot send arbitrarily long messages via sockets - sockets

I'm writing a traceroute implementation and I'm deliberately trying to send a long message for the purposes of path maximum transmission unit detection. However, when I try to send something longer than 1500 bytes, I get:
thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 90, kind: Uncategorized, message: "Message too long" }', src/main.rs:115:26
I'm expecting to see a Fragmentation needed ICMP message.
The socket comes from the raw-socket crate.
It seems that PMTU is hard-coded somewhere deep. Or am I misdiagnosing this? What would prevent me from sending arbitrarily long messages via sockets?
MCVE:
use raw_socket::{RawSocket, Domain, Protocol, Type};
use raw_socket::option::{Level, Name};
use raw_socket::ffi::c_int;
fn main() {
let sock = RawSocket::new(Domain::ipv4(), Type::raw(), Protocol::from(255).into()).unwrap();
let buf = [0u8; 9000];
const IP_MTU_DISCOVER: i32 = 10;
const IP_PMTU_DISC_PROBE: i32 = 3;
sock.set_sockopt(Level::IPV4, Name::from(IP_MTU_DISCOVER), &(IP_PMTU_DISC_PROBE as c_int))
.unwrap();
sock.send_to(&buf, "142.250.201.206:33434").unwrap();
}

Related

Rust persistent TcpStream

I seem to be struggling with the std::io::TcpStream. I'm actually trying to open a TCP connection with another system but the below code emulates the problem exactly.
I have a Tcp server that simply writes "Hello World" to the TcpStream upon opening and then loops to keep the connection open.
fn main() {
let listener = io::TcpListener::bind("127.0.0.1", 8080);
let mut acceptor = listener.listen();
for stream in acceptor.incoming() {
match stream {
Err(_) => { /* connection failed */ }
Ok(stream) => spawn(proc() {
handle(stream);
})
}
}
drop(acceptor);
}
fn handle(mut stream: io::TcpStream) {
stream.write(b"Hello Connection");
loop {}
}
All the client does is attempt to read a single byte from the connection and print it.
fn main() {
let mut socket = io::TcpStream::connect("127.0.0.1", 8080).unwrap();
loop {
match socket.read_byte() {
Ok(i) => print!("{}", i),
Err(e) => {
println!("Error: {}", e);
break
}
}
}
}
Now the problem is my client remains blocked on the read until I kill the server or close the TCP connection. This is not what I want, I need to open a TCP connection for a very long time and send messages back and forth between client and server. What am I misunderstanding here? I have the exact same problem with the real system i'm communicating with - I only become unblocked once I kill the connection.
Unfortunately, Rust does not have any facility for asynchronous I/O now. There are some attempts to rectify the situation, but they are far from complete yet. That is, there is a desire to make truly asynchronous I/O possible (proposals include selecting over I/O sources and channels at the same time, which would allow waking tasks which are blocked inside an I/O operation via an event over a channel, though it is not clear how this should be implemented on all supported platforms), but there's still a lot to do and there's nothing really usable now, as far as I'm aware.
You can emulate this to some extent with timeouts, however. This is far from the best solution, but it works. It could look like this (simplified example from my code base):
let mut socket = UdpSocket::bind(address).unwrap();
let mut buf = [0u8, ..MAX_BUF_LEN];
loop {
socket.set_read_timeout(Some(5000));
match socket.recv_from(buf) {
Ok((amt, src)) => { /* handle successful read */ }
Err(ref e) if e.kind == TimedOut => {} // continue
Err(e) => fail!("error receiving data: {}", e) // bail out
}
// do other work, check exit flags, for example
}
Here recv_from will return IoError with kind set to TimedOut if there is no data available on the socket during 5 seconds inside recv_from call. You need to reset the timeout before inside each loop iteration since it is more like a "deadline" than a timeout - when it expires, all calls will start to fail with timeout error.
This is definitely not the way it should be done, but Rust currently does not provide anything better. At least it does its work.
Update
There is now an attempt to create an asynchronous event loop and network I/O based on it. It is called mio. It probably can be a good temporary (or even permanent, who knows) solution for asynchronous I/O.

Send packet with sockets from kernel module

I am writing a kernel module that should receive messages from user-space and send response back via socket.
When program and module are on the same machine and I use IP 127.0.0.1, everything works fine. But when I try it on different machines and use real network IP, something like 192.168.3.146 it works only in one way.
I receive message from user-space, but I can not receive it from kernel. I use sock_sendmsg function for sending message from kernel and it's not return any error. Also I am not get any messages from firewall that something is came up from another machine, from kernel module.
Here were similar questions and examples, but they were not useful enough for me or examples were used too old kernel version.For skeleton I used this one,from UDP sockets: http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html. Any help?
Kernel module code for sending:
void send_data(unsigned char *data)
{
if(!IS_ERR_OR_NULL(data))
{
int ret;
mm_segment_t oldfs;
struct msghdr message;
struct iovec ioVector;
struct sockaddr_in sendAddr;
sendAddr.sin_family = AF_INET;
sendAddr.sin_addr.s_addr = INADDR_ANY;
//sendAddr.sin_addr.s_addr = in_aton("192.168.1.75");
//here I get port from sk_buff structure that I received.
sendAddr.sin_port = *((unsigned short*)skBuffer->data);
memset(&message, 0, sizeof(message));
message.msg_name = &sendAddr;
message.msg_namelen = sizeof(sendAddr);
/* send the message back */
ioVector.iov_base = data;
ioVector.iov_len = strlen(data);
message.msg_iov = &ioVector;
message.msg_iovlen = 1;
message.msg_control = NULL;
message.msg_controllen = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
ret = sock_sendmsg(sendSocket, &message, strlen(data));
set_fs(oldfs);
}
}
I found an alternative solution, using netpoll sockets. It is more easier than sockets, I used before and it works. The answer and proper code is here, on another StackOverflow question.

Cancelling retransmissions on a L2CAP socket

I was wondering if anyone can assist me with a problem that I have with C Bluetooth programming (Linux Bluez).
I am using Ubuntu 10.04, BlueZ 4.60.
My goal is to have a L2CAP socket in which there will be minimal delay for sending data between 2 computers.
So far I managed to open an L2CAP socket, but this socket has endless retransmissions and I'm trying to change it. I want to have no retransmissions at all because I need the data to be transfer fast with minimal delay and the reliability of the data is not important.
I found an example online that deals with changing the flush timout for the socket and by that causing that if a packet is not acked after a certain period of time it is dropped and the next data in the buffer is sent.
The problem is that this example doesn't work :-(
Here is my code, this method is called after the bind command:
int set_flush_timeout(bdaddr_t *ba, int timeout)
{
int err = 0, dd, dev_id;
struct hci_conn_info_req *cr = 0;
struct hci_request rq = { 0 };
struct {
uint16_t handle;
uint16_t flush_timeout;
} cmd_param;
struct {
uint8_t status;
uint16_t handle;
} cmd_response;
// find the connection handle to the specified bluetooth device
cr = (struct hci_conn_info_req*) malloc(
sizeof(struct hci_conn_info_req) +
sizeof(struct hci_conn_info));
bacpy( &cr->bdaddr, ba );
cr->type = ACL_LINK;
dev_id = hci_get_route( NULL);
dd = hci_open_dev( dev_id );
if( dd < 0 ) {
err = dd;
goto cleanup;
}
err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr );
if( err ) goto cleanup;
// build a command packet to send to the bluetooth microcontroller
cmd_param.handle = cr->conn_info->handle;
cmd_param.flush_timeout = htobs(timeout);
rq.ogf = OGF_HOST_CTL;
rq.ocf = 0x28;
rq.cparam = &cmd_param;
rq.clen = sizeof(cmd_param);
rq.rparam = &cmd_response;
rq.rlen = sizeof(cmd_response);
rq.event = EVT_CMD_COMPLETE;
// send the command and wait for the response
err = hci_send_req( dd, &rq, 1 );
if( err ) goto cleanup;
if( cmd_response.status ) {
err = -1;
errno = bt_error(cmd_response.status);
}
cleanup:
free(cr);
if( dd >= 0) close(dd);
return err;
}
What is my mistake?
Does anyone know another option that will solve my problem.
Code examples will also be great!!
Thanks!!
This code to set the automatic flush time out seems to be correct.
You can make sure by ensuring that you are getting "Success" in response to this command's command complete event.
I suspect that the issue might be in your packet sending code, note that for the automatic flush timeout to take effect the individual packets should be marked as automatically flushable, The HCI data packet has the Packet_Boundary_Flag which you can sent to indicate if individual packets are flushable.
Also note that the Flush timeout has to be large enough to allow for enough time so that the packets gets a transmission attempt, the way the flush timeout are defined can cause the packet to be flushed even without the packet being transmitted even once, so you need to tune it. By definition Flush timeout start when the packet is Queued for transmission.

How read the TCP packet of a stream using a socket in C?

let me first tell what I am trying to do.
I am trying to write a very simple proxy server.
I used the socket API to create a socket.
socket = socket(AF_INET, SOCK_STREAM, 0));
my proxy server worked fine until I tried it for a streaming data.
So what I did was my server socket listened to the requests and parsed them and then forwarded them to the actual server, I then used the read() call to read the packet & I blindly forward it back to the client.
For all html pages and images it works fine. but when I try to forward a streaming video I am not able to do it.
My socket always returns the application layer data (HTTP packet) but in a streaming video only the first packet is http and rest all are just TCP packets. So I am able to forward only the first HTTP packet. When I try to read the other packets which contain data (which are all TCP) I don't get anything at the application layer (which is obvious as there is nothing at application layer in those packets ). So I am stuck and I do not know how to read those packets from TCP layer (I dont wanna use raw socket) and get my job done.
thanks in advance
You have to parse the packet header to know how much data to read from the socket. at first, use a ring buffer (a circular one!) for example the BSD sys/queue.h to order the received data from the stream.
The code below shows how to extract header_length, total_length, source and destination Address of an IPv4 packet in layer 3. refer to IPv4 packet layout to understand offsets:
typedef struct {
unsigned char version;
unsigned char header_length;
unsigned short total_length;
struct in_addr src;
struct in_addr dst;
} Packet;
int rb_packet_write_out(RingBuffer *b, int fd, int count) {
int i;
for (i = 0; i < count; i++) {
if (b->level < 20) {
return i;
}
Packet p;
unsigned char *start = b->blob + b->read_cursor;
unsigned char b1 = start[0];
p.version = b1 >> 4;
p.header_length = b1 & 0xf;
p.total_length = bigendian_deserialize_uint16(start + 2);
if (b->level < p.total_length) {
return i;
}
memcpy(&(p.src), start + 12, 4);
memcpy(&(p.dst), start + 16, 4);
char s[5], d[5];
inet_ntop(AF_INET, &(p.src), s, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(p.dst), d, INET_ADDRSTRLEN);
L_DEBUG("Packet: v%u %s -> %s (%u)", p.version, s, d, p.total_length);
}
return i;
}
If you use the socket API, then you are on the layer below HTTP, that is, to you everything is "just TCP". If the connection is stuck somewhere, it is most likely that something else is broken. Note there is no guarantee that the HTTP request or reply header will even fit in a single packet; they just usually do.
An HTTP 1.1 compliant streaming server will use "Content-Encoding: chunked" and report the length of each chunk rather than the length of the entire file, you should keep that in mind when proxying.
So what I did was my server socket
listened to the requests and parsed
them
Why? An HTTP proxy doesn't have to parse anything except the first line of the request, to know where to make the upstream connection to. Everything else is just copying bytes in both directions.

How to flush the socket using boost

I am implementing a server that sends xml to clients using boost. The problem I am facing is that the buffer doesn't get sent immediately and accumulates to a point then sends the whole thing. This cause a problem on my client side, when it parses the xml, it may have incomplete xml tag (incomplete message). Is there a way in boost to flush out the socket whenever it needs to send out a message? Below is server's write code.
void
ClientConnection::handle_write(const boost::system::error_code& error)
{
if (!error)
{
m_push_message_queue.pop_front ();
if (!m_push_message_queue.empty () && !m_disconnected)
{
boost::asio::async_write(m_socket,
boost::asio::buffer(m_push_message_queue.front().data(),
m_push_message_queue.front().length()),
boost::bind(&ClientConnection::handle_write, this,
boost::asio::placeholders::error));
}
}
else
{
std::err << "Error writting out message...\n";
m_disconnected = true;
m_server->DisconnectedClient (this);
}
}
Typically when creating applications using TCP byte streams the sender sends a fixed length header so the receiver knows how many bytes to expect. Then the receiver reads that many bytes and parses the resulting buffer into an XML object.
I assume you are using TCP connection. TCP is stream type, so you can't assume your packet will come in one big packet. You need to fix your communication design, by sending size length first like San Miller answer, or sending flag or delimiter after all xml data has been sent.
Assuming you are definitely going to have some data on the socket you want to clear, you could do something like this:
void fulsh_socket()
{
boost::asio::streambuf b;
boost::asio::streambuf::mutable_buffers_type bufs = b.prepare(BUFFER_SIZE);
std::size_t bytes = socket_.receive(bufs); // !!! This will block until some data becomes available
b.commit(bytes);
boost::asio::socket_base::bytes_readable command(true);
socket_.io_control(command);
while(command.get())
{
bufs = b.prepare(BUFFER_SIZE);
bytes = socket_.receive(bufs);
b.commit(bytes);
socket_.io_control(command); // reset for bytes pending
}
return;
}
where socket_ is a member variable.
HTH