How to send raw bits over an Ethernet cable without using frames? - c

I am trying to send raw bits over an Ethernet cable without using any protocol, even without an Ethernet frame. I realize that this data won't really go anywhere as it will not have a receiving MAC address, but this is purely educational.
I know I can create a socket but it always encapsulates my data in an Ethernet frame. Does this mean I would have to write raw data somehow to the port itself?
This is a pseudo example of how I send data by creating a socket.
int main()
{
char *request = "GET / HTTP/1.1";
socket = socket(AF_INET, SOCK_STREAM, 0);
bind(server_fd, (struct sockaddr *)&address, sizeof(address));
write(new_socket , request , strlen(request));
}

Related

Populate UDP payload in socket programming with my own structure?

I want to send a variable of custom struct over udp sockets using sendto(). This struct contains UDP payload. When I googled a bit, I found that it is possible to create raw socket in c using the flag, SOCK_RAW, while creating the socket. But I think, then i will have to populate ip header as well as udp header too. I want to avoid that.
But when I tried sending the custom structure over socket as mentioned above, I receive different data ( not the structure contents) when sniffed with wireshark. Please help.
I tried changing the struct to normal uint32_t variable and used bit shifting operations to populate this variable. I confirmed that this variable has exactly the contents that I intended to add to it.
But again, I am not able to send this too over sockets. Wireshark displays different content. Is it because in sendto(), we provide the address of the buffer and not its value?
uint32_t disc_req[1];
//disc_req is populated with bit shift operations in the following function
create_discovery_req(disc_req);
//now disc_req has exactly the required binary format - I confirmed this
// But the following sendto() sends different data. Confirmed over wireshark.
sendto(sockfd, &disc_req, sizeof(disc_req),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
Expeced output is value of disc_req sent over socket and the same captured on wireshark. But the actual value of disc_req is something different.
The different content you are observing may be due to the sender and receiver having different endianess (host vs network). If so you will want to use hton...() and ntoh...() when working with multi-byte values.
In general, when you send an array or struct over UDP, treat it like a byte array.
Create a UDP socket, not a RAW socket:
sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
Setup your destination address using the method you prefer (i.e. gethostbyname() )
Then send the payload:
sendto( sockfd, ( char* ) &disc_req[0], sizeof( disc_req ), MSG_CONFIRM, (const struct sockaddr *) &servaddr, sizeof(servaddr));
To receive the payload:
recvfrom( sockfd, ( char* ) &disc_req[0], sizeof( disc_req ), 0, (const struct sockaddr *) &servaddr, sizeof(servaddr) );
The following is a complete example sending an NTP packet using UDP:
https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html

How to see TCP, IP headers in my HTTP proxy?

I have a forking HTTP proxy implemented on my Ubuntu 14.04 x86_64 with the following scheme (I'm reporting the essential code and pseudocode just to show the concept):
socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(socketClient,(struct sockaddr*)&addr, sizeof(addr));
listen(socketClient, 50);
newSocket = accept(socketClient, (struct sockaddr*)&cliAddr, sizeof(cliAddr));
get request from client, parse it to resolve the requested hostname in an IP address;
fork(), open connection to remote server and deal the request;
child process: if it is a GET request, send original request to server and while server is sending data, send data from server to client;
child process: else if it is a CONNECT request, send string 200 ok to client and poll both client socket descriptor and server socket descriptor with select(); if I read data from server socket, send this data to client; else if I read data from client socket, send this data to server.
The good thing is that this proxy works, the bad thing is that now I must collect statistics; this is bad because I'm working on a level where I can't get the data I'm interested in. I don't care about the payload, I just need to check in IP and TCP headers the flags I care about.
For example, I'm interested in:
connection tracking;
number of packets sent and received.
As for the first, I would check in the TCP header the SYN flag, SYN/ACK and then a last ACK; as for the second, I would just do +1 to a counter of mine every time a char buffer[1500] is filled with data when I send() or recv() a full packet.
I realized that this is not correct: SOCK_STREAM doesn't have the concept of packet, it is just a continuous stream of bytes! The char buffer[1500] I use at point 7. and 8. has useful statistic, I may set its capacity to 4096 bytes and yet I couldn't keep track of the TCP packets sent or received, because TCP has segments, not packets.
I couldn't parse the char buffer[] looking for SYN flag in TCP header either, because IP and TCP headers are stripped from the header (because of the level I'm working on, specified with IPPROTO_TCP flag) and, if I understood well, the char buffer[] contains only the payload, useless to me.
So, if I'm working on a too high level, I should go lower: once I saw a simple raw socket sniffer where an unsigned char buffer[65535] was cast to struct ethhdr, iphdt, tcphdr and it could see all the flags of all the headers, all the stats I'm interested in!
After the joy, the disappointment: since raw sockets work on a low level they don't have some concepts vital to my proxy; raw sockets can't bind, listen and accept; my proxy is listening on a fixed port, but raw sockets don't know what a port is, it belongs to the TCP level and they bind to a specified interface with setsockopt.
So, if I'd socket(PF_INET, SOCK_RAW, ntohs(ETH_P_ALL)) I should be able to parse the buffer where I recv() and send() at .7 and .8, but I should use recvfrom() and sendto()...but all this sounds quite messy, and it envolves a nice refactoring of my code.
How can I keep intact the structure of my proxy (bind, listen, accept to a fixed port and interface) and increase my line of vision for IP and TCP headers?
My suggestion is to open a raw socket in, for example, another thread of your application. Sniff all traffic and filter out the relevant packets by addresses and port numbers. Basically you want to implement your own packet sniffer:
int sniff()
{
int sockfd;
int len;
int saddr_size;
struct sockaddr saddr;
unsigned char buffer[65536];
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd < 0) {
perror("socket");
return -1;
}
while (1) {
saddr_size = sizeof(saddr);
len = recvfrom(sockfd, buffer, sizeof(buffer), 0, &saddr, &saddr_size);
if (len < 0) {
perror("recvfrom");
close(sockfd);
return -1;
}
// ... do the things you want to do with the packet received here ...
}
close(sockfd);
return 0;
}
You can also bind that raw socket to a specific interface if you know which interface is going to be used for the proxy's traffic. For example, to bind to "eth0":
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4);
Use getpeername() and getsockname() function calls to find the local and remote addresses and port numbers of your TCP connections. You'll want to filter the packets by those.

Packet Sockets not receiving data for custom protocol ID

I am trying to send and receive packets of type SOCK_RAW over PF_SOCKETs using my own custom protocol ID on the same machine. Here is my sender and receiver sample code-
sender.c
#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define CUSTOM_PROTO 0xB588
int main ()
{
int sockfd = -1;
struct sockaddr_ll dest_addr = {0}, src_addr={0};
char *buffer = NULL;
struct ethhdr *eh;
sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );
if ( sockfd == -1 )
{
perror("socket");
return -1;
}
buffer = malloc(1518);
eh = (struct ethhdr *)buffer;
dest_addr.sll_ifindex = if_nametoindex("eth0");
dest_addr.sll_addr[0] = 0x0;
dest_addr.sll_addr[1] = 0xc;
dest_addr.sll_addr[2] = 0x29;
dest_addr.sll_addr[3] = 0x49;
dest_addr.sll_addr[4] = 0x3f;
dest_addr.sll_addr[5] = 0x5b;
dest_addr.sll_addr[6] = 0x0;
dest_addr.sll_addr[7] = 0x0;
//other host MAC address
unsigned char dest_mac[6] = {0x0, 0xc, 0x29, 0x49, 0x3f, 0x5b};
/*set the frame header*/
memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
memcpy((void*)(buffer+ETH_ALEN), (void*)dest_mac, ETH_ALEN);
eh->h_proto = htons(PAVAN_PROTO);
memcpy((void*)(buffer+ETH_ALEN+ETH_ALEN + 2), "Pavan", 6 );
int send = sendto(sockfd, buffer, 1514, 0, (struct sockaddr*)&dest_addr,
sizeof(dest_addr) );
if ( send == -1 )
{
perror("sendto");
return -1;
}
return 0;
}
receiver.c
#include<sys/socket.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<linux/if_arp.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define CUSTOM_PROTO 0xB588
int main ()
{
int sockfd = -1;
struct sockaddr_ll dest_addr = {0}, src_addr={0};
char *recvbuf = malloc(1514);
sockfd = socket(PF_PACKET, SOCK_RAW, htons(CUSTOM_PROTO) );
if ( sockfd == -1 )
{
perror("socket");
return -1;
}
int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
printf("I received: \n");
return 0;
}
Both sender and receiver are running on Ubuntu Virtualbox. The problem is the receiver hangs in recvfrom. But in receiver.c, if I change htons(CUSTOM_PROTO) to htons(ETH_P_ALL), the receiver works just fine.
Why is the kernel not delivering the packet with my custom protocol ID to my custom protocol ID socket?
I verified in GDB that the ethernet header is formed correctly when I receive packet with htons(ETH_P_ALL)
Update: Instead of interface eth0 and its corresponding MAC, if I choose local loopback lo and a MAC address of 00:00:00:00:00:00, CUSTOM_PROTO works just fine!
Update 2 CUSTOM_PROTO works fine if the sender and receiver are on different machines. This finding and prev update made me suspect that packets being sent out on eth0 are not being received by the same machine. But the fact that ETH_P_ALL works on the same machine, refutes my suspicion.
ETH_P_ALL vs any other protocol
The protocol ETH_P_ALL has the special role of capturing outgoing packets.
Receiver socket with any protocol that is not equal to ETH_P_ALL receives packets of that protocol that come from the device driver.
Socket with protocol ETH_P_ALL receives all packets before sending outgoing packets to the device driver and all incoming packets that are received from the device driver.
Loopback device vs Ethernet device
Packets sent to the loopback device go out from that device and then the same packets are received from the device as incoming.
So, when CUSTOM_PROTO is used with loopback the socket captures packets with custom protocol as incoming.
Note that if ETH_P_ALL is used with the loopback device each packet is received twice. Once it is captured as outgoing and the second time as incoming.
In case of eth0 the packet is transmitted from the device. So, such packets go to the device driver and then they can be seen on the other side of the physical Ethernet port. For example, with VirtualBox "Host-only" network adapter those packets can be captured by some sniffer in the host system.
However, packets transmitted to the physical port (or its emulation) are not redirected back to that port. So, they are not received as incoming from the device. That is why such packets can be captured only by ETH_P_ALL in outgoing direction and they cannot be seen by CUSTOM_PROTO in incoming direction.
Technically it should possible to prepare special setup to do external packet loopback (packets from the device port should be sent back to that port). In that case the behavior should be similar to the loopback device.
Kernel implementation
See the kernel file net/core/dev.c. There are two different lists:
struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
struct list_head ptype_all __read_mostly; /* Taps */
The list ptype_all is for socket handlers with protocol ETH_P_ALL. The list ptype_base is for handlers with normal protocols.
There is a hook for outgoing packets in xmit_one() called from dev_hard_start_xmit():
if (!list_empty(&ptype_all))
dev_queue_xmit_nit(skb, dev);
For outgoing packets the function dev_queue_xmit_nit() is called for ETH_P_ALL processing each item of ptype_all. Finally the sockets of type AF_SOCKET with protocol ETH_P_ALL capture that outgoing packet.
So, the observed behavior is not related to any custom protocol. The same behavior can be observed with ETH_P_IP. In that case the receiver is able to capture all incoming IP packets, however it cannot capture IP packets from sender.c that sends from "eth0" to MAC address of "eth0" device.
It can be also seen by tcpdump. The packets sent by the sender are not captured if tcpdump is called with an option to capture only incoming packets (different versions of tcpdump use different command line argument to enable such filtering).
The initial task where on the same machines it is needed to distinguish packets by protocol IDs can be solved using ETH_P_ALL. The receiver should capture all packets and check the protocol, for example:
while (1) {
int len = recvfrom(sockfd, recvbuf, 1514, 0, NULL, NULL);
if (ntohs(*(uint16_t*)(recvbuf + ETH_ALEN + ETH_ALEN)) == CUSTOM_PROTO) {
printf("I received: \n");
break;
}
}
Useful reference "kernel_flow" with a nice diagram http://www.linuxfoundation.org/images/1/1c/Network_data_flow_through_kernel.png
It is based on the 2.6.20 kernel, however in the modern kernels ETH_P_ALL is treated in the same way.
When packets with same source nad destination MAC address are transmitted from real network device ethX and physically looped back.
If protocol ETH_P_ALL is specified, packet is captured twice:
first packet with socket_address.sll_pkttype is PACKET_OUTGOING
and second packet with socket_address.sll_pkttype is PACKET_HOST
If specific protocol is specified CUSTOM_PROTO, packet is captured once:
in the case of normal packet: socket_address.sll_pkttypeis
PACKET_HOST.
in the case of VLAN packet:
socket_address.sll_pkttypeis PACKET_OTHERHOST.

Send UDP message from Linux C to many Android phones

Rewritten to try and be clear on what I need.
My goal is to duplicate the function of a device made by Digital Yacht in an embedded Intel Edison processor running C and Linux. The device sends via UDP to phone apps such as iRegatta and others. To set up the app, only the port number is entered. No ip address is entered in UDP mode on the app. I thought this was trivial but the experts here so far have said it is impossible so it must not be trivial. Perhaps that is why with all my hours of reading I cannot find an example. I am being voted down because, I am told, that what I am trying to do it impossible but it is not as it is done. I don't know how it is done, which is why I came to experts here.
I want to send nmea messages that might look like this:
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
and I want any number of random Android phones to be able to receive them with the appropriate app. There are many apps that can be set up to receive UDP data where you just specify a port number. There is no ip address involved in the setup of the apps. Also, I do not wish to receive anything from the Android phones. This is one way and no ability to re-transmit so if a message does not get there, it has another chance next time. Everything is updated once a second.
I tried the following and I do not get data in the app. From the comments, I must need to add some kind of router function in my Linux machine.
void init_udp(){
return;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0){
printf("ER UDP Socket error\n");
}
else printf("UP Socket %d OK\n",sock);
}
void write_udp(char *buf){
return;
// nmea data is in buff
if (sock >= 0){
int on = 1;
setsockopt( sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );
struct sockaddr_in address = {0};
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr( "255.255.255.255" ); //
address.sin_port = htons( 3000 );
if ( sendto( sock, buf, strlen(buf), 0, (struct sockaddr*)&address, sizeof(address) ) < 0) printf("ER UDP send error\n");
else {
printf("UP %s\n",buf);
}
}
}
I am not really sure what I need to do.
What you want to do is send a UDP packet to a broadcast IP address. This will send to thing in the subnet.
eg 10.255.255.255 is the broadcast address for the 10.x.x.x subnet. You can also use the global 255.255.255.255 which should also send to your subnet and no router is going to pass that on to another one these days.
Also you need to make your socket able to send broadcast messages. In particular you need the option SO_BROADCAST set. The following is specifically Windows because of the BOOL. Its presumably an int for most platforms.
BOOL on = TRUE;
setsockopt( sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) );
Also you can't use send() for a UDP socket unless its "connected", so you should use sendto() and use the broadcast address.
To specify an address and port, use need to create a socket address.
struct sockaddr_in address = {0};
address.sin_family = AF_INET;
address.sin_add.s_addr = inet_addr( "255.255.255.255" );
address.sin_port = htons( 3000 );
sendto( sock, buff, strlen(buff), 0, (struct sockaddr*)&address, sizeof(address) );
Note the use of the typecast. This is because sendto takes a sockaddr which is a generic address type, and here we are using an IPV4 address specifically. The sockaddr_in maps onto the sockaddr

raw ethernet sockets filling sockaddr_ll

I am buliding a server/client software using PF_PACKET and SOCK_RAW and a custom protocol when calling socket()
When in the client software I create the socket the same way and just do a rcvfrom that socket and I get the data
My question is do I have to fill out the sockaddr_ll struct the same way I do for the server since when I reply from the client the source MAC address I got is a wierd one something like
11:11:00:00:00:00 and of course this is not my client's MAC
Does anyone know what this happens?
Open the socket
if ( (sckfd=socket(PF_PACKET, SOCK_RAW, htons(proto)))<0)
{
myError("socket");
}
this is how I receive the data
n = recvfrom(sckfd, buffer, 2048, 0, NULL, NULL);
printf("%d bytes read\n",n);
So this is how I basically receive the data in the client without filling the struct sockaddr_ll
For the server Program I do have to fill the struct
struct sockaddr_ll saddrll;
memset((void*)&saddrll, 0, sizeof(saddrll));
saddrll.sll_family = PF_PACKET;
saddrll.sll_ifindex = ifindex;
saddrll.sll_halen = ETH_ALEN;
memcpy((void*)(saddrll.sll_addr), (void*)dest, ETH_ALEN);
My question is I receive as shown and send as shown and when I reply to the server call the same function used in the server for Sending then what do I get 11:11:00:00:00:00 when receiving client replies
You should probably use
socket(AF_PACKET, SOCK_DGRAM, htons(proto)))
instead of a SOCK_RAW socket. With a SOCK_RAW, you are sending/receiving the entire ethernet frame, including source and destination MAC address. with SOCK_DGRAM, the kernel will fill in the ethernet header.
You probably want to send the reply to the same address as the request comes from, recvfrom() can fill in the source address;
struct sockaddr_ll src_addr;
socklen_t addr_len = sizeof src_addr;
n = recvfrom(sckfd, buffer, 2048, 0,
(struct sockaddr*)&src_addr, &addr_len);
Now you've learned the source address, so send the packet back to it:
...
sendto(sckfd, data, data_len, src_addr, addr_len);
And if you rather need to use SOCK_RAW, your will receive the ethernet header too, so just copy out the MAC addresses from the received data and swap them around when you are constructing the reply frame.
For an a SOCK_RAW socket, you craft the entire ethernet frame, you don't need to fill in the ethernet address, so the following is not needed;
memcpy((void*)(saddrll.sll_addr), (void*)dest, ETH_ALEN);

Resources