Interception of udp-packets on router - c

I have a client on PC. I have a server on PC. The client and server are connected via a router with firmware based on Linux OS.
The client sends a packet to the server and receive a response. The router must intercept the packets and modify it. Something like sniffing but it's not a sniffing because i need to modify the packets.
I must to write a program for this.
I tried to open a raw socket on the router, but reсvfrom on raw socket does not intercept the packet and just copy it. The packet is going on.
Could you suggest me any way to solve this problem?
P.S. Sorry for my bad English. :)

I'd use a mix of iptables and libnetfilter_queue (assuming your kernel is relatively recent)
Add to the iptables a rules that forward all the udp packets to the NFQUEUE 0 in order to get packets from kernel to user space.
iptables -A INPUT -p udp -m udp --dport xxxxx -j NFQUEUE --queue-num 0
Build a process who listen to the NFQUEUE number 0, modify payload and give the full packet back to the kernel space using libnetfilter_queue capabilities. Follow this link to know how to do it.
In a nutshell you have to open the queue 0 (nfq_create_queue), set the mode in order to get the content of the packet (nfq_set_mode), then loop in an infinite recv to get ever udp packet filtered by iptables
fd = nfq_fd(h);
while ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
printf("pkt received\n");
nfq_handle_packet(h, buf, rv);
}
Everytime you call nfq_handle_packet is called, the callback defined during the nfq_create_queue phase is called. In that callback you have to modify the payload, update the size and recalculate the checksum, then set as "valid" with nfq_set_verdict

I wrote the module for the kernel and some applications. Module uses netfilter and discards packets that I need to netfilter_queue. The application processes a queue and I decide what to do with each package.
uint hook_main(uint hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *) )
{
struct iphdr *ip;
struct udphdr *udp;
if (skb->protocol == htons(ETH_P_IP)){
ip = (struct iphdr *)(skb->data);
if (ip->version == 4 && ip->protocol == IPPROTO_UDP){
udp = (struct udphdr *)(skb->data + sizeof(struct iphdr));
if(ntohs(udp->dest) == SOME_PORT){
return NF_QUEUE;
}
}
}
return NF_ACCEPT;
}
int init_module ()
{
printk("[udp-catch] start udp-catch\n");
catch_hook.hook = hook_main;
catch_hook.owner = THIS_MODULE;
catch_hook.pf = PF_INET;
catch_hook.hooknum = NF_INET_FORWARD;
catch_hook.priority = NF_IP_PRI_FIRST;
nf_register_hook(&catch_hook);
return 0;
}
And a redesigned sample from netfilter.org is the application.

Routers will automatically send out whatever they receive on their other ports.
e.g. For a 4 port router, what comes in on port 1 will be sent out on ports 2,3 & 4.
To do what you require, you need another PC with 2 network cards. Connect your client PC to one network card, and the server PC to the other.
Then your program will need to recvfrom on one network card, modify the packet and sendto on the other network card.

Related

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.

How to write proxy program for resending packets?

I need to write program using raw sockets in c language on proxy server between two hosts.
I've written some code for it (and set some rules for iptable to change destination address of packets to proxy's interfaces), where I am receiving packet, print data in this packet and then send the packet to receiver.
It's working on my simple client/server programs on raw sockets, but when I am trying to establish a connection through a proxy - it doesn't work.
Do you have any ideas on how I can write this program without using the kernel?
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define PCKT_LEN 8192
int main(void){
int s;
char buffer[PCKT_LEN];
struct sockaddr saddr;
struct sockaddr_in daddr;
memset(buffer, 0, PCKT_LEN);
s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if(s < 0){
printf("socket() error");
return -1;
}
int saddr_size = sizeof(saddr);
int header_size = sizeof(struct iphdr) + sizeof(struct tcphdr);
unsigned int count;
daddr.sin_family = AF_INET;
daddr.sin_port = htons(1234);
daddr.sin_addr.s_addr = inet_addr ("2.2.2.1");
while(1){
if(recvfrom(s, buffer, PCKT_LEN , 0, &saddr, &saddr_size) < 0){
printf("recvfrom() error");
return -1;
}
else{
int i = header_size;
for(; i < PCKT_LEN; i++)
printf("%c", buffer[i]);
if (sendto (s, buffer, PCKT_LEN, 0, &daddr, &saddr_size) < 0)
printf("sendto() error");
return -1;
}
}
}
close(s);
return 0;
}
(Your code has serious bugs. For example, the last argument to sendto(2) should not be a pointer. I'll assume it's not the real code and that the real code compiles without warnings.)
With the nagging out of the way, I think one problem is that you're accidentally including an extra IP header in the packets you send. raw(7) has the following:
The IPv4 layer generates an IP header when sending a packet unless the IP_HDRINCL socket option is enabled on the socket. When it is enabled, the packet must contain an IP header. For receiving the IP header is always included in the packet.
IP_HDRINCL is not enabled by default unless protocol is IPPROTO_RAW (see a bit further down in raw(7)), meaning it's disabled in your case. (I also checked with getsockopt(2).)
You will have to either enable IP_HDRINCL using setsockopt(2) to tell the kernel that you're supplying the header yourself, or not include the header in sendto().
It's better to look at the IHL field in the IP header than assume it has fixed size by the way. The IP header could include options.
There could be other issues as well depending on what you're trying to do, and details might vary for IPv6.
Whatever you are doing I don't think using raw sockets is the way. Those are used for network debugging only.
Fist of all, observe that basically you are copying content from an existing, stabilished connection, rather than tunneling it. You are not doing what is proposed.
If you want to capture connections to a given server:port, for instance, 2.2.2.1:1234, into your application so that you can tunnel it through a proxy, you can use iptables.
iptables -t nat -A OUTPUT -p tcp -d 2.2.2.1 --dport 1234 -j REDIRECT
Create an application bound to ip 0.0.0.0 listening to TCP port 1234 and every connection attempt to 2.2.2.1:1234 will connect to your application instead, and you can do whatever you please with it.

Talking to C socket with Scapy

I have a UDP connection up and listening on a port (localhost) and I am trying to send a Scapy packet from localhost as well. For some reason, my C code never actually captures the packet, however I can see the packet show up in Wireshark just fine. It's been awhile since I've used sockets, but is there some special socket options I have to set or why would I be able to see the packet in Wireshark just fine but not by the C socket?
Note: I was able to successfully catch a packet when I wrote corresponding socket code to send out packets (from localhost) however I am still unable to get the listening code to catch the packet when sent from another computer.
I have found a similar question but when I tried their approach (using UDP instead of TCP), I still couldn't get netcat to catch the Scapy packet.
C Code (condensed for clarity sake)
int main() {
int sock, dataLen, inLen;
struct sockaddr_in inAddr;
short listen_port = 8080;
char buffer[2048];
if (sock = socket(AF_INET,SOCK_DGRAM,0) < 0) {
printf("ERROR: unable to establish socket\n");
return -1;
}
// zero out address structure
memset(&inAddr, 0, sizeof(inAddr));
inAddr.sin_family = AF_INET;
inAddr.sin_addr.s_addr = htonl(INADDR_ANY);
inAddr.sin_port = htons(listen_port);
if (bind(sock, (struct sockaddr*)&inAddr, sizeof(inAddr)) < 0) {
printf("ERROR: unable to bind\n");
return -1;
}
inLen = sizeof(inAddr);
printf("Now listening on port %d\n", listen_port);
while(1) {
dataLen = recvfrom(sock, buffer, 1500, 0, (struct sockaddr*)&inAddr, &inLen);
if (dataLen < 0)
printf("Error receiving datagram\n");
else
printf("Received packet of length %d\n", dataLen);
}
return 0;
}
Scapy Script
# set interface
conf.iface="lo0"
# create IP packet
ip_pkt = IP()/UDP()
ip_pkt.payload = "payload test message"
ip_pkt.dport = 8080
ip_pkt.dst = "127.0.0.1"
ip_pkt.src = "127.0.0.1"
# send out packet
send(ip_pkt)
Scapy needs to be configured slightly differently to work on the Loopback interface, see http://www.secdev.org/projects/scapy/doc/troubleshooting.html under the heading "I can’t ping 127.0.0.1. Scapy does not work with 127.0.0.1 or on the loopback interface"
I used the code given there and sent a scapy packet which was received by a C Socket, this was specifically:
from scapy.all import *
conf.L3socket=L3RawSocket
packet=IP()/UDP(dport=32000)/"HELLO WORLD"
send(packet)
This was then received on a UDP C Socket bound to lo on port 32000 (Scapy defaults to sending IP packets over the loopback interface).
I have the same problem, udp socket does not receive scapy packet.
I suppose there might be something related to this post: Raw Socket Help: Why UDP packets created by raw sockets are not being received by kernel UDP?
And what works for me is the socket.IP_HDRINCL option. Here is the working code for both and sender.
sender:
import socket
from scapy.all import *
rawudp=socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
rawudp.bind(('0.0.0.0',56789))
rawudp.setsockopt(socket.SOL_IP, socket.IP_HDRINCL,1)
pkt = IP()/UDP(sport=56789, dport=7890)/'hello'
rawudp.sendto(pkt.build(), ('127.0.0.1',7890))
receiver:
import socket
so = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
so.bind(('0.0.0.0',7890))
while True:
print so.recv(1024)
Verified on Fedora 14, although doesn't work on my MBP...
I think the problem is in setting incompatible set of interface, src and dst address.
When destination is loopback (127.0.0.1), interface should be lo and addresses (assuming both client and server run on the same host):
ip_pkt.dst = "127.0.0.1"
ip_pkt.src = "127.0.0.1"
Another way is to send to the ethernet address (assuming 192.168.1.1 is configured on eth0 and both client and server run on the same host):
ip_pkt.dst = "192.168.1.1"
ip_pkt.src = "192.168.1.1"
If you try different hosts, then using 127.0.0.1 and lo is not possible. Set src to client machine's ip and dst to server machine's ip.

What is the BSD (or portable) way to get ToS byte (like IP_RECVTOS from linux)?

What is the right (portable, stable) way to get the ToS byte of a received packet? I'm doing UDP with recvmsg() and on linux I can get the ToS if I setsockopt() IP_RECVTOS/IPV6_RECVTCLASS, but IP_RECVTOS doesn't seem to be available on my BSD systems. What is the right way to do this?
I primarily want this to work on the BSDs and Solaris.
Edit:
To clarify:
I currently use recvmsg() where I get the TTL and TOS in the msg_control field on Linux, but in order to get TTL and TOS I need to setsockopt()-enable IP_RECVTTL and IP_RECVTOS. And since Solaris and BSD (working with FreeBSD at the moment) don't have IP_RECVTOS from what I can see I don't get TOS when looping over the CMSG data.
I tried enabling IP_RECVOPTS and IP_RECVRETOPTS, but I still don't get any IP_TOS type CMSG.
Edit 2:
I want ToS to be able to verify (as much as possible) that it wasn't overwritten in transit. If for example a VoIP app all of a sudden notices that it's not getting EF tagged packets, then something is wrong and there should be an alarm. (and no, I'm not expecting EF to be respected or preserved over the public internet)
I want TTL basically just because I can. Hypothetically this could be used to trigger "something changed in the network between me and the other side" alerts, which can be useful to know if somethings stops working at the same time.
I was thinking if you can create two sockets.
One socket of type DGRAM used exclusively for sending
One Raw socket used exclusively for receiving.
Since you are using UDP, you can call a bind + recvFrom on the Raw Sock Fd and then manually unpack the IP header to determine the TOS or TTL.
When you want to send, use the DGRAM sockFd so you dont have to bother to actually create the UDP & IP packet yourself.
There may be issues like the kernel may pass the received buffer to both sockets or to the UDP socket instead of Raw socket or just to the Raw socket. If that is the case (or if it is implementation dependent) then we are back to square one. However, you can try calling bind on the Raw socket and see if it helps. I am aware this maybe a hack but searching on the net for a setsockopt for BSD returned nothing.
EDIT: I wrote a sample program
It kind of achieves the objective.
The code below creates two sockets (one raw & one udp). The udp socket is bound on the actual port I am expecting to receive data whereas the raw socket is bound on Port 0. I tested this on Linux and like I expected any data for port 2905 is received by both the sockets. I am however able to retrieve the TTL & TOS values. Dont downvote for the quality of the code. I am just experimenting whether it will work.
Further EDIT: Disabled the receive by UDP socket.
I have further enhanced the code to disable the receive by the UDP packet. Using setsockopt, I set the UDP's socket receive buffer to 0. This ensures the kernel does not pass the packet to the UDP socket. IMHO,You can now use the UDP socket exclusively for sending and the raw socket for reading. This should work for you in BSD and Solaris also.
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<arpa/inet.h>
#include<string.h>
#include "protHeaders.x"
#include "gen.h"
int main(void)
{
S32 rawSockFd;
S32 udpSockFd;
struct sockaddr_in rsin;
struct sockaddr_in usin;
S32 one = 1;
const S32* val = &one;
struct timeval tv;
fd_set rfds;
S32 maxFd;
S16 ret;
S8 rawBuffer[2048];
S8 udpBuffer[2048];
struct sockaddr udpFrom,rawFrom;
socklen_t rLen,uLen;
memset(rawBuffer,0,sizeof(rawBuffer));
memset(udpBuffer,0,sizeof(udpBuffer));
memset(udpFrom,0,sizeof(udpFrom));
memset(rawFrom,0,sizeof(rawFrom));
if ((rawSockFd = socket(PF_INET,SOCK_RAW,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
/* doing the IP_HDRINCL call */
if (setsockopt(rawSockFd,IPPROTO_IP,IP_HDRINCL,val,sizeof(one)) < 0)
{
perror("Server:setsockopt");
RETVALUE(RFAILED);
}
rsin.sin_family = AF_INET;
rsin.sin_addr.s_addr = htonl(INADDR_ANY);
rsin.sin_port = htons(0);
usin.sin_family = AF_INET;
usin.sin_addr.s_addr = htons(INADDR_ANY);
usin.sin_port = htons(2905);
if(bind(rawSockFd,(struct sockaddr *)&rsin, sizeof(rsin)) < 0 )
{
perror("Server: bind failed");
RETVALUE(RFAILED);
}
if ((udpSockFd = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
{
perror("socket:create");
RETVALUE(RFAILED);
}
if(bind(udpSockFd,(struct sockaddr *)&usin, sizeof(usin)) < 0 )
{
perror("Server: bind failed on udpsocket");
RETVALUE(RFAILED);
}
/*set upd socket receive buffer to 0 */
one = 0;
if (setsockopt(udpSockFd,SOL_SOCKET,SO_RCVBUF,(char *)&one,sizeof(one)) < 0)
{
perror("Server:setsockopt on udpsocket failed");
RETVALUE(RFAILED);
}
tv.tv_sec = 0;
tv.tv_usec = 0;
maxFd = (rawSockFd > udpSockFd)? rawSockFd:udpSockFd;
while(1)
{
FD_ZERO(&rfds);
FD_SET(rawSockFd,&rfds);
FD_SET(udpSockFd,&rfds);
ret = select(maxFd+1,&rfds,0,0,&tv);
if ( ret == -1)
{
perror("Select Failed");
RETVALUE(RFAILED);
}
if(FD_ISSET(rawSockFd,&rfds))
{
printf("Raw Socked Received Message\n");
if(recvfrom(rawSockFd,rawBuffer,sizeof(rawBuffer),0,&rawFrom,&rLen) == -1)
{
perror("Raw socket recvfrom failed");
RETVALUE(RFAILED);
}
/*print the tos */
printf("TOS:%x\n",*(rawBuffer+1));
printf("TTL:%x\n",*(rawBuffer+8));
}
if(FD_ISSET(udpSockFd,&rfds))
{
printf("UDP Socked Received Message\n");
if(recvfrom(udpSockFd,udpBuffer,sizeof(udpBuffer),0,&udpFrom,&uLen) == -1)
{
perror("Udp socket recvfrom failed");
RETVALUE(RFAILED);
}
printf("%s\n",udpBuffer);
}
}
RETVALUE(ROK);
}
The "proper" and standard solution is probably to use cmsg(3). You'll find a complete description in Stevens' "Unix network programming" book, a must-read.
Google Code Search found me this example of use.
My understanding is that firstly BSD does not support IP_RECVTOS like functionality and secondly BSD raw sockets do not support the reception of UDP nor TCP packets. However there are two other ways of doing this, firstly by using the /dev/bpf interface - either directly or via libpcap. Or secondly by using DIVERT sockets which allow for diversion of specified traffic flows to userland.
Has anyone actually tested the code above on a BSD box? (it may work on Solaris...)
On Linux this approach will work but as mentioned it is also possible (and more convenient) to use setsockopt() with IP_TOS on the outgoing socket to set the outgoing TOS byte and setsockopt() with IP_RECVTOS on the incoming socket and use recvmsg() to retrieve the TOS byte.
Unfortuneatly this sort of thing usually varies across different *ixs. On Solaris you want to use getsockopt with IP_TOS; I don't know about BSD.
See man 7 ip for details.

Resources