linux ethernet frame socket clarification - c

I am trying to understand below lines in the sample socket code in found in google.
struct ether_header *eh = (struct ether_header *) sendbuf;
struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
struct ether_header *eh -> So far in know *eh used to access the struct variable
i just want to understand these assignment
(struct ether_header *) sendbuf;
(struct iphdr *) (sendbuf + sizeof(struct ether_header));

In the first line
(struct ether_header *) sendbuf;
the variable sendbuf is cast to a pointer to the struct ether_header, you can read more about casting here
The second line
(struct iphdr *) (sendbuf + sizeof(struct ether_header));
it's adding sizeof(struct ether_header) to the pointer sendbuf, by doing that, it reaches the memory zone after the one occupied by the pointer to the struct ether_header , which seems to contain a pointer to the struct iphdr
This is the schematic representation of sendbuf
+------------------------------------------------------+
| eh |
+------------------------------------------------------+
| iph = eh + sizeof(struct ether_header) |
+------------------------------------------------------+
-- sendbuf --

first one is accessing ethernet header ptr, and next is accessing the iphdr ptr. ( ethernet packet contains IP packet)

Related

sockets: right way to access IP and UDP headers on raw socket buffer

struct msghdr msg;
struct iovec iov;
unsigned char buf[BUFSIZE] = { '\0', };
ssize n;
int fd;
...
fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
...
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
...
n = recvmsg(sockfd, &msg, 0);
...
parse_pkt(buf, BUFSIZE);
So far so good, the packet is received, and now I need to parse it:
static int parse_packet(unsigned char *pkt, int len)
{
struct iphdr *ip = (struct iphdr *)(pkt + 14);
struct udphdr *udp;
/* ip has valid pointer and we can explore IP header fields, ip pointer is NOT modified. */
...
udp = (struct udphdr *)(ip + (ip->ihl << 2));
/* at this point I'm expecting udp to point past IP header space. */
...
}
The problem I'm seeing is that udp does not point where I'm expecting, I don't get why: pkt contains the whole packet (including Ethernet header, no VLANs), so ip obtains a pointer past ether_header, so udp = (struct udphdr *)(ip + (ip->ihl << 2)) should just skip over IP header size, but it does not!
What does work though is this:
struct iphdr *ip = (struct iphdr *)(pkt + 14);
...
udp = (struct udphdr *)(pkt + 14 + (ip->ihl << 2));
What is it so, what am I doing wrong?
When you do this:
udp = (struct udphdr *)(ip + (ip->ihl << 2));
You're doing pointer arithmetic in units of sizeof(*ip) instead of 1.
Your alternate works:
udp = (struct udphdr *)(pkt + 14 + (ip->ihl << 2));
Because pkt is an unsigned char * so pointer arithmetic is done in single byte units.
This would also work:
udp = (struct udphdr *)((unsigned char *)ip + (ip->ihl << 2));
As it allow you to perform pointer arithmetic in single byte units.

One Question Basic TCP/IP programming use bind() function

int serv_sock;
struct socckaddr_in serv_addr;
char *serv_port = "9190";
/*make server socket*/
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
/*serv_addr struct init*/
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_faimily = AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(serv_port));
**bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));**
i don't understand that why use (struct sockaddr *) ?
why can't wrote bind(serv_sock, &serv_addr, sizeof(serv_addr)); ?
why add (struct sockaddr *) type ? i don't understand
what's mean (struct sockaddr *) ?
So, i did one example
struct a {
int a;
int b;
};
int i = 10;
struct a b;
b.a = 10;
b.b = 20;
printf("%d \n", b.a); // A sentence
printf("%d \n", (struct a*)b.a); // B sentence
it returned 10, 10; i don't understand why use '(struct a*)'..
i don't understand a,b sentence difference
i want I would like to know the difference between the type with and without '*'.
The expression &serv_addr has type struct sockaddr_in *.
However, the function bind expects that the argument has type struct socckaddr *. This is a generic type for many address families.
The * character in this context indicates a pointer.
From the manual:
The actual structure passed for the addr argument will depend on the address family. The sockaddr structure is defined as something like:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
While the manual describes struct sockaddr_in as:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
The reason the the generic type exists is that IPv4 is not the only address family that exists.
The manual describes the IPv6 version:
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
Note that the all start with a sa_family_t member, which describes which family is being represented. The struct sockaddr sa_data member is the "payload" of the struct.
Directly passing a struct sockaddr_in * or struct sockaddr_in6 * when struct sockaddr * is expected would cause a warning/error. The (struct sockaddr *) explicitly converts the pointer to the type struct sockaddr *, which bind expects.
The statement
printf("%d \n", (struct a*)b.a);
is not valid. You take an integer, convert it to a pointer to a struct, then tell printf to interpret that argument as an integer.

Difference between skb_header_pointer and skb_transport_header?

I'm trying to implement a netfilter module, while processing sk_buff I found two possible ways to retrieve TCP header:
struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);
struct tcphdr *tcp_header = (struct tcphdr *)skb_transport_header(skb);
And
struct iphdr *ip_header = skb_header_pointer(skb, 0, sizeof(struct iphdr), &_iph)
struct tcphdr *tcp_header = skb_header_pointer(skb, ip_header->ihl * 4, sizeof(struct tcphdr), &_tcph);
Which one should I use?
You should use ip_hdr() from /include/linux/ip.h and tcp_hdr() from /include/linux/tcp.h in case you know that there cannot be paged-skb here:
struct iphdr *ip_header = ip_hdr(skb);
if (ip_header->protocol == IPPROTO_TCP) {
struct tcphdr *tcp_header = tcp_hdr(skb);
//...
skb_header_pointer() should be used in case the appearance of paged-skb is possible. Examples: IP, TCP, ICMP, etc.
So if the header is in paged data (fully or partially) - skb_header_pointer() will correctly handle it.
Also remember to check the return value of skb_header_pointer(), it can return NULL.
Useful links: 1, 2

kernel module for netfilter

I have started writing a kernel module for netfilter.
I am able to hook the netfilter and able to access the sk_buff.
Now I am trying to modify the packet(sk_buff) and trying to reinject the packet in tcp stack but it is not working.
My requirement is to drop SYN-ACK packet from a server and sending back a cooked up ack packet back to server without sending the SYN-ACK to client.
I have tried dev_queue_xmit after swapping source/dest IP, source/dest port
and source/dest MAC and recalculating the checksum and then send the packet using dev_queue_xmit. though dev_queue_xmit return success, the packet is not injecting to stack. Any help will be appreciated.
My code:
code is part of NF_INET_POST_ROUTING hook.
void do_exp(struct sk_buff *skb)
{
printk(KERN_INFO "Enering do_exp\n");
struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);
struct tcphdr *tcp_header = (struct tcphdr *)skb_transport_header(skb);
struct sk_buff *newskb;
struct iphdr *newiph;
struct tcphdr *newtcph;
newskb = skb_copy(skb, GFP_ATOMIC);
//newskb->pkt_type = PACKET_OUTGOING;
// newskb->pkt_type = PACKET_HOST;
/*changing Mac address */
unsigned char srcaddr[6];
struct ethhdr *eth = eth_hdr(skb);
struct ethhdr *neweth = eth_hdr(newskb);
memcpy(srcaddr, eth->h_source, ETH_ALEN);
memcpy(neweth->h_source, eth->h_dest, ETH_ALEN);
memcpy(neweth->h_dest, srcaddr, ETH_ALEN);
newiph = (struct iphdr *) skb_network_header(newskb);
newtcph = (struct tcphdr *) skb_transport_header(newskb);
newiph->saddr = ip_header->daddr;
newiph->daddr = ip_header->saddr;
newtcph->source = tcp_header->dest;
newtcph->dest = tcp_header->source;
newtcph->syn = 0;
newtcph->ack_seq = tcp_header->seq + 1;
newtcph->seq = tcp_header->ack_seq;
newiph->check = ip_fast_csum((unsigned char *)newiph, newiph->ihl);
int ret = dev_queue_xmit(newskb);
}
TCP dump is showing correctly.

error in ip address received

I am implementing raw sockets in C. I have two programs (say server and client). Client is sending info to server and then server is sending ACK. Client has sent info and server has successfully received it. Now server is sending back ACK. Now, Client has parsed ethernet header successfully. I have problem in receiving the IP address.
1. What should be the type of arguments of the function CreateIPHeader()?
2. How to print IP addresses in ParseIPHeader()?
3. What should be the data type of value returned by ParseIPHeader, if I want just the IP addresses?
struct iphdr *CreateIPHeader(char *src_ip,char *dst_ip)
{
struct iphdr *ip_header;
ip_header=malloc(sizeof(struct iphdr));
//OTHER FIELDS OF IP HEADER
ip_header->saddr = inet_ntoa(*((struct in_addr *)(src_ip)));
ip_header->daddr = inet_ntoa(*((struct in_addr *)(dst_ip)));
ip_header->check=ComputeChecksum((unsigned char *)ip_header,ip_header->ihl*4);
printf("\nip to be sent = %s",ip_header->saddr); //printing correct IP
printf("\nip to be rcvd = %s",ip_header->daddr);
return(ip_header);
}
char* ParseIPHeader(unsigned char *packet,int len)
{
struct iphdr *ip_header,*ret_ip;
unsigned char *out;
struct ethhdr *ethernet_header;
out=malloc(2048);
memset(out, 0, 2048);
ethernet_header=(struct ethhdr *) out;
ret_ip=(struct iphdr *) (out + sizeof(struct ethhdr));
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip->saddr = ip_header->daddr;
ret_ip->daddr = ip_header->saddr;
printf("daddr SENT = %s",ret_ip->daddr); //how to print them?
printf("saddr SENT = %s",ret_ip->saddr);
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return out;
}
int main()
{
unsigned char in[2048];
int len;
char *rcv_ip;
Struct iphdr *ip_header;
memset(in,0,2048);
len=recvfrom(raw,in,2048,0,(struct sockaddr *)&packet_info,&packet_info_size);
rcv_ip=ParseIPHeader(in,len); /*I want this function to return me the ip addresses which I would use in the next line.*/
ip_header =CreateIPHeader(rcv_ip+5,rcv_ip);
memset(in,0,2048);
memcpy(in+sizeof(struct ethhdr),ip_header,ip_header->ihl*4);
sendrawpacket(raw,in,pkt_len);
free(ip_header);
return 0;
}
Any help would be appreciated.
Thanks :)
Instead of:
printf("\nDest Addr %s \n",inet_ntoa(*((struct in_addr *)&((ip_header->daddr)))));
printf("\nSource Addr %s \n",inet_ntoa(*((struct in_addr *)&(ip_header->saddr))));
Perhaps:
printf("\nDest Addr %s \n",inet_ntoa(ip_header->daddr));
printf("\nSource Addr %s \n",inet_ntoa(ip_header->saddr));
If indeed the printf is causing you to segfault and not something else, then perhaps:
struct in_addr dest;
dest.s_addr = ip_header->daddr;
printf("\nDest Addr %s \n", inet_ntoa(dest));
This is because (assuming you are using struct iphdr *ip_header) ip_header->daddr has a type of __u32 and inet_ntoa takes a struct in_addr.
There are a couple things wrong.
CreateIPHeader
You are confusing inet_ntoa() with inet_aton(). When creating the IP header, you want to set ip_header->saddr using something like
inet_aton(src_ip, (struct in_addr *) &ip_header->saddr)
Remember, the s_addr and d_addr in struct iphdr are of type __u32, not char arrays. Thus, when you make this change, the printf statements in CreateIPHeader will break.
main
The reason why your printf's are working is due to Problem #1. On receive, you call CreateIPHeader to create an incorrect struct iphdr (see above), which now incorrectly has C-strings assigned to saddr and daddr. Later, abc points to this incorrect header; your printf's magically work because abc->saddr and abc->daddr actually are C strings.
You are only overwriting the iphdr portion of packet_buffer (your memcpy statement). You must also change the h_dest and h_src values in struct ethhdr.
You really don't need to convert the IP addresses into a C string and then convert them back into IP addresses.
Also, you aren't freeing ip_header.
In general, you can do something like this:
int main()
{
unsigned char in[2048];
unsigned char out[2048];
int len;
memset(in, 0, 2048);
memset(out, 0, 2048);
len = recvfrom(raw, in, 2048, 0,
(struct sockaddr *) &packet_info, &packet_info_size);
struct ethhdr *in_eth = (struct ethhdr *) in;
if (ntohs(ethernet_header->h_proto) == ETH_P_IP &&
len >= sizeof(struct ethhdr) + sizeof(struct iphdr) {
struct iphdr *in_ip = (struct iphdr*) (in + sizeof(struct ethhdr));
/* create outbound packet, starting with eth header */
struct ethhdr *out_eth = (struct ethhdr *) out;
/* ... set h_dest and h_src */
struct iphdr *out_ip = (struct iphdr *) (out + sizeof(struct ethhdr));
out_ip->saddr = in_ip->daddr;
out_ip->daddr = in_ip->saddr;
/* calculate the IPv4 checksum, packet len etc */
sendrawpacket(raw, out, pkt_len);
}
return 0;
};
No guarantee that's bug-free. Just wrote it in the browser.

Resources