I've been writing some sockets code in C. I need modify packet headers and control how they're sent out, so I took the raw sockets approach. However, the code I wrote will not compile on BSD systems (Mac OS X/Darwin, FreeBSD, etc.)
I've done a bunch of research on this and have found that BSD systems can't handle raw sockets the way Linux (or even Windows) does. From what I've read, it seems I need to use bpf (berkley packet filter), but I can't figure out how bpf works or how I would go about using it with raw sockets.
If someone could shed some light on this one, I'd be very excited :D
P.S. I'll even be happy with some source code showing how raw sockets are handled in a BSD environment. It doesn't have to be a guide or explanation. I just want to see how it works.
Using raw sockets isn't hard but it's not entirely portable. For instance, both in BSD and in Linux you can send whatever you want, but in BSD you can't receive anything that has a handler (like TCP and UDP).
Here is an example program that sends a SYN.
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <err.h>
#include <stdio.h>
#include <string.h>
#include <sysexits.h>
int
main(int argc, char *argv[])
{
int s, rc;
struct protoent *p;
struct sockaddr_in sin;
struct tcphdr tcp;
if (argc != 2)
errx(EX_USAGE, "%s addr", argv[0]);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = 0;
/* Parse command line address. */
if (inet_pton(AF_INET, argv[1], &sin.sin_addr) <= 0)
err(EX_USAGE, "Parse address");
/* Look up tcp although it's 6. */
p = getprotobyname("tcp");
if (p == NULL)
err(EX_UNAVAILABLE, "getprotobyname");
/* Make a new shiny (Firefly) socket. */
s = socket(AF_INET, SOCK_RAW, p->p_proto);
if (s < 0)
err(EX_OSERR, "socket");
memset(&tcp, 0, sizeof(tcp));
/* Fill in some random stuff. */
tcp.th_sport = htons(4567);
tcp.th_dport = htons(80);
tcp.th_seq = 4; /* Chosen by fair dice roll. */
tcp.th_ack = 0;
tcp.th_off = 5;
tcp.th_flags = TH_SYN;
tcp.th_win = htonl(65535);
rc = sendto(s, &tcp, sizeof(tcp), 0, (struct sockaddr *)&sin,
sizeof(sin));
printf("Wrote %d bytes\n", rc);
return 0;
}
Of course, more BSD-specific solutions are available. For instance you could use divert(4) to intercept packets as they traverse your system and alter them.
Related
I'm required to make a 'height sensing subsystem' to read the data sent from a moonlander by making a UDP protocol. The client is already set up for me, and is a 64bit executable on linux run by using ./simulator. So I need to make the UDP server in linux to connect with the client.
The client sends readings from many subsystems in the moonlander, but I only need to read one of them, which is the laser altimeter reading that corresponds to the a type specified by 0xaa01, there are other types such as 0xaa##, and 0xff##, but those correspond to different subsystems of the moonlander I assume. The data sent from the ./simulator file is sent through the type, which I then need to decode to find if its the laser altimeter, and then I need to decode the values to convert into distance to find when the moonlander has touched down. I need to read the time first, which has a size of 4 bytes and is an unsigned 32 bit integer, and the laser altimeter reading is 3 unsigned 16-bit integers that correspond to 3 different measurements (as there are 3 different sensors on the altimeter, max height of 1000m, convert by dividing by 65.535 which is UINT16_MAX, and multiplying by 100 to convert to cm). I need to then take those readings, convert them into height, and then acknowledge that we've landed once we've hit 40cm away from the ground.
How do I read the data from the ./simulator file? The problem is that when I run the ./receiver file, it stops working at the recvfrom() function as in my code below. In the instructions, they tell me to connect to port 12778, which works, but I'm not receiving anything.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// Create a UDP datagram socket
int main() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("Can't connect");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; // use IPv4
addr.sin_addr.s_addr = INADDR_ANY; // bind to all interfaces
addr.sin_port = htons(12778); // the port we want to bind
// Bind to the port specified above
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("cant connect");
exit(EXIT_FAILURE);
}
printf("here");
// Listen for data on our port (this is blocking)
char buffer[4096];
int n = recvfrom(fd, buffer, 4096, MSG_WAITALL, NULL, NULL);
printf("Recieved!");
}
I'm new to socket programming. I want to send a UDP package to a specific port on another PC in the network. When I analysie my network traffic with Wireshark I see a different port number in the outgoing packages from what I specified in my code.
With port=10000 the actual port it gets send to is 4135. When I changed it to port=15000 (just to try something different) it got send to port 38970.
The port numbers wireshark shows must be true, as a receiving program set to listen on these ports (4135 or 38970) does react to sent packages, although the received data seems like garble (the console shows unknown characters, the debug console shows "1\355I\211\321^H\211...").
Any ideas why?
I'm running this on a Debian VM connected to other VMs in a virtual network. A very similar program using TCP worked just fine.
#include <sys/types.h> //Various types, including ssize_t
#include <sys/socket.h> //Types, macros and functions for sockets
#include <netinet/in.h> //Types, including sockaddr_in, macros
#include <arpa/inet.h> //Types and functions, including inet_aton()
#include <unistd.h> //Types, macros and functions for Unix/Posix
#include <stdlib.h> //GP types, macros and functions
#include <stdio.h> //IO operations (streams)
#include <string.h> //Functions for string operations
#define BC_ADDR "192.168.1.255"
int main(int argc, char** argv) {
int clientSocket;
const int setBroadcast = 1;
in_port_t port = 10000;
struct sockaddr_in broadcastAddr;
int broadcastAddrLen = sizeof(struct sockaddr_in);
const char* msg = "Hello World";
//Create a new UDP socket
clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
if(clientSocket > 0) {
printf("Socket created successfully\n");
}
else {
printf("Failure during socket creation\n");
exit(EXIT_FAILURE);
}
//Set socket options to allow broadcasts
int setSockOptStatus = setsockopt(clientSocket, SOL_SOCKET,
SO_BROADCAST, &setBroadcast, sizeof(setBroadcast));
if(setSockOptStatus != 0) {
printf("Error setting socket options!");
close(clientSocket);
return EXIT_FAILURE;
}
//Form boradcast address structure
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_port = port;
broadcastAddr.sin_addr.s_addr = inet_addr(BC_ADDR);
//Send broadcast message
sendto(clientSocket, msg, strlen(msg)+1, 0,
(struct sockaddr*) &broadcastAddr, broadcastAddrLen);
//Close socket and exit program
close(clientSocket);
return EXIT_SUCCESS;
}
The problem is how you're setting the port:
broadcastAddr.sin_port = port;
The IP address and port number stored in a struct sockaddr_in must both be in network byte order which is big-endian byte ordering, i.e. most significant byte first. Your machine apparently uses little-endian byte ordering, i.e. least significant byte first, so by assigning the port number directly to the sin_port member that conversion is not being done.
This is more apparent if you look at the hexadecimal representation of the expected and actual port numbers:
10000d = 2710h, 4135d = 1027h
15000d = 3a98h, 38970 = 983ah
You need to call the htons() function, which converts a 16-bit value from host byte order to network byte order:
broadcastAddr.sin_port = htons(port);
I have a client server program written in C. The intent is to see how fast big data can be trasported over TCP. The receiving side OS (Ubuntu Linux 14.*) is tuned to improve the TCP performance, as per the documentation around tcp / socket / windows scaling etc. as below:
net.ipv4.tcp_window_scaling = 1
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 16384 16777216
This aprt, I have also increased the individual socket buffer size through setsockopt call.
But I am not seeing the program responding to these changes - the overall throughput is either flat or even reduced at times. When I took tcpdump at the receiving side, I see a monotonic pattern of tcp packets of length 1368 as coming to it, in most (99%) cases.
19:26:06.531968 IP <SRC> > <DEST>: Flags [.], seq 25993:27361, ack 63, win 57, options [nop,nop,TS val 196975830 ecr 488095483], length 1368
As per the documentation, the tcp window scaling option increases the receiving frame size in propotion to the demand and capacity - but all I see is "win 57" - very few bytes remaining in the receiving buffer, which is not matching with the expection.
Hence I start suspecting my assumptions on the tuning itself, and have these questions:
Is there any specific tunables required at the sending side to improve the client side reception? Making sure that you (program) writes the whole chunk of data in one go is not enough?
In the client side tunable as mentioned above necessary and sufficient? The default on in the system are too low, but I don't see the changes applied in /etc/sysctl.conf having any effect. Is running sysctl --system after changes sufficient to make the changes in effect? or do we need to reboot the system?
If the OS is a virtual machine, will these tunables make meaning in its completeness, or are there additional steps at the real physical machine?
I can share the source code if that helps, but I can guarentee that it is just a trivial code.
Here is the code:
#cat client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#define size 1024 * 1024 * 32
int main(){
int s;
char buffer[size];
struct sockaddr_in sa;
socklen_t addr_size;
s = socket(PF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_port = htons(25000);
sa.sin_addr.s_addr = inet_addr("<SERVERIP");
memset(sa.sin_zero, '\0', sizeof sa.sin_zero);
addr_size = sizeof sa;
connect(s, (struct sockaddr *) &sa, addr_size);
int rbl = 1048576;
int g = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rbl, sizeof(rbl));
while(1) {
int ret = read(s, buffer, size);
if(ret <= 0) break;
}
return 0;
}
And the server code:
bash-4.1$ cat server.c
#include <sys/types.h>
#include <sys/mman.h>
#include <memory.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <sys/socket.h>
extern int errno;
#define size 32 * 1024 * 1024
int main() {
int fdsocket;
struct sockaddr_in sock;
fdsocket = socket(AF_INET,SOCK_STREAM, 0);
int rbl = 1048576;
int g = setsockopt(fdsocket, SOL_SOCKET, SO_SNDBUF, &rbl, sizeof(rbl));
sock.sin_family = AF_INET;
sock.sin_addr.s_addr = inet_addr("<SERVERIP");
sock.sin_port = htons(25000);
memset(sock.sin_zero, '\0', sizeof sock.sin_zero);
g = bind(fdsocket, (struct sockaddr *) &sock, sizeof(sock));
if(g == -1) {
fprintf(stderr, "bind error: %d\n", errno);
exit(1);
}
int p = listen(fdsocket, 1);
char *buffer = (char *) mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(buffer == -1) {
fprintf(stderr, "%d\n", errno);
exit(-1);
}
memset(buffer, 0xc, size);
int connfd = accept(fdsocket, (struct sockaddr*)NULL, NULL);
rbl = 1048576;
g = setsockopt(connfd, SOL_SOCKET, SO_SNDBUF, &rbl, sizeof(rbl));
int wr = write(connfd, buffer, size);
close(connfd);
}
There are many tunables, but whether they have an effect and whether the effect is positive or negative also depends on the situation. What are the defaults for the tunables? The values you set might actually be lower than the defaults on your OS, thereby decreasing performance. But larger buffers might sometimes also be detrimental, because more RAM is used, and it might not fit into cache memory anymore. It also depends on your network itself. Is it wired, wireless, how many hops, what kind of routers are inbetween? But sending data in as large chunks as possible is usually the right thing to do.
One tunable you have missed is the congestion control algorithm, which you can tune with net.ipv4.tcp_congestion_control. Which ones are available depends on your kernel, and which one is the best depends on your network and the kind of traffic that you are sending.
Another thing is that TCP has two endpoints, and tunables on both sides are important.
The changes made with sysctl are taking effect immediately for new TCP connections.
The TCP parameters only have effect on the endpoints of a TCP connection. So you don't have to change them on the VM host. But running in a guest means that the packets it sends still need to be processed by the host in some way (if only just to forward them to the real physical network interface). It will always be slower to run your test from inside a virtual machine than if you'd run it on a physical machine.
What I'm missing is any benchmark numbers that you can compare with the actual network speed. Is there room for improvement at all? Maybe you are already at the maximum speed that is possible? In that case no amount of tuning will help. Note that the defaults are normally very reasonable.
I'm writing a very small C UDP client. I know that a random port is chosen as source port when you send data to the server. I also know that you can use bind to specify yourself the port you want a response.
However, I don't know when is the port randomly chosen? For example, I would like to rely on the sender address to keep track of users. It currently works only if the client does not shutdown, the port is still the same then a simple memcmp is enough to detect the same client.
This small code will use the same source port until it exits:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
int main(void)
{
int s, error, ch;
struct addrinfo hints, *res;
memset(&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if ((error = getaddrinfo("localhost", "9988", &hints, &res)))
errx(1, "%s", gai_strerror(error));
if ((s = socket(res->ai_family, res->ai_socktype, 0)) < 0)
err(1, "socket");
while ((ch = fgetc(stdin)) != EOF)
sendto(s, &ch, 1, 0, res->ai_addr, res->ai_addrlen);
}
And running something like : dmesg | ./client will use the same address until the program exits. However, when you run it again, the port is different.
So is it the socket function that choose a port? Or the system? Is it sure that the port will still be the same during the client lifetime?
If the socket is not explicitly bound, then the OS will bind it (with a random port) when you send the first packet. This binding will be active as long as the socket is open, once it's closed the socket is (of course) unbound.
And due to the connectionless nature of UDP sockets, the "server" (if done correctly) should not keep the address of all "clients" that send to it indefinitely. Instead it should use the source address as received in the recvfrom call, and use that for a reply. The only reason to store the source address for more than just a simple request/response, is if you have a more advanced protocol on top of UDP with your own "connection" handling.
There is very useful funcion call getifaddrs which retrieves all machin network addresses. The problem is that I'm using old glibc version which doesn't have this function. Is there any replacement for it? I was looking and found getipnodebyname but it is unuseful when address isn't mapped in /etc/hosts file.
To add to the previous answer, here is an example for the SIOCGIFCONF-approach. You have to do something like this:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
int fd;
int get_iface_list(struct ifconf *ifconf)
{
int rval;
if((rval = ioctl(fd, SIOCGIFCONF , (char*) ifconf )) < 0 )
perror("ioctl(SIOGIFCONF)");
return rval;
}
int main()
{
static struct ifreq ifreqs[100];
static struct ifconf ifc;
char *ptr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return 1;
ifc.ifc_buf = (char*) (ifreqs);
ifc.ifc_len = sizeof(ifreqs);
if(get_iface_list(&ifc) < 0) return -1;
/* Go through the list of interfaces */
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len;)
{
struct ifreq *ifr = (struct ifreq*)ptr;
int len = (sizeof(struct sockaddr) > ifr->ifr_addr.sa_len) ?
sizeof(struct sockaddr) : ifr->ifr_addr.sa_len;
ptr += sizeof(ifr->ifr_name) + len;
/* Do what you need with the ifr-structure.
* ifr->ifr_addr contains either sockaddr_dl,
* sockaddr_in or sockaddr_in6 depending on
* what addresses and L2 protocols the interface
* has associated in it.
*/
}
close(fd);
return 0;
}
There are some gotchas, of course. According to Unix Network Programming chapter 17.6 ioctl(fd, SIOCGIFCONF, array) may not return an error on some platforms if the array pointed in the argument is too small. The data will then be concatenated. Only way to work around this is to call ioctl() in a loop until you get same result length twice while increasing the size of the array. Of course, since this is 2012, I'm not sure how relevant this is anymore.
Size of ifreqs array is purely a guess in this case. Keep in mind though that the array will contain one struct ifreq for every L2 and L3 address associated with a interface. For example, assuming you have also IPv6 addresses, for lo-interface you'd get three entries: ethernet, IPv4 and IPv6. Therefore reserve enough space or apply the kludge.
To get broadcast addresses and other additional information, you will need to additional ioctl() calls in the loop. All possible options depends on what your OS provides, of course.
For more information I'd recommend reading Unix Network Programming by W. Richard Stevens. It is the most comprehesive book about this subject.
The traditional way to do the equivalent was with the SIOCGIFCONF operation to ioctl. Any socket can be used for the operation. It's not as easy as a single function call though.