I want to transmit an skb that contains a valid tcp and ip header in the linux kernel. It should go out a specific interface without being routed.
My problem is, that I cannot use dev_queue_xmit because I dont know the destination mac-address.
My attempts to find out the mac-address with arp_find failed:
...
mh = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
...
arp_find(mh->h_dest, skb); //this or the next line
val = dev_queue_xmit(skb); //crashes kernel
Experiments with ip_local_out also failed. I set the ip header information and call ip_local_out which also results in a kernel crash.
I could not use ip_queue_xmit because I was not able to find out, what data to provide in the struct flowi *fl field.
So my questions:
How can I send out an skb on a device with ip information and no knowledge about lower levels?
If there is no answer to the first question: How can I find out the destination mac/trigger an arp request for the destination ip?
Remark:
I would like to avoid using (raw) sockets.
I have successfully
transmitted self-made skbs via dev_queue_xmit if I had the
destination mac address. So the code building the skb is not an
issue.
If you still want to use ip_local_out or ip_queue_xmit, then you can create your flowinfo like this.
struct flowi4 fl4 = {
.flowi4_oif = skb->dev->ifindex,
.daddr = iph->daddr,
.saddr = iph->saddr,
};
then create dst(rtable) with
ip_route_output_key
set to skb_buff with
skb_dst_set
I think it's a cleaner solution to use socket programming in kernel to send it as RAW IP packet.
You can create socket like this.
sock_create_kern(&sk, AF_INET,
SOCK_RAW, IPPROTO_RAW,
net);
then call
kernel_sendmsg
to let your packet going through the protocol stack.
Related
I want to send packets using packet_mmap for getting high packet transmission rate. I managed to send packets using packet socket in raw mode, which for that purpose I created L2, L3, and etc in buffer and send it using
sendto(fd_socket, NULL, 0, 0, NULL, sizeof(struct sockaddr_ll));
However, I don't want to bother with destination mac address. So I turned into using Dgram instead. If I want to employ sendto there is an argument for destination MAC Address. Even though this is not what I wished for in terms of not being concerned with arp cache and specifying destination's MAC address:
sendto(fd_socket, NULL, 0, 0, (struct sockaddr *) ps_sockaddr, sizeof(struct sockaddr_ll));
However I found that send is also allowed to be used with packet socket.
http://man7.org/linux/man-pages/man7/packet.7.html
Therefore, I filled the buffer with ip header and so on. In this case send returns zero as nothing is found to be sent, which I expect to be something other than zero, if there is an error with transmission of packet.
Is there a way to use packet_mmap without being concerned about L2 address?
No. When you use a packet socket in SOCK_DGRAM mode, you needn't construct the L2 header, but you must still provide the L2 address and then the system will construct the header for you. (So that would have the benefit of actually building the L2 portion of the packet for you and you wouldn't need to specify the source MAC address but you still need to specify the interface from which you want the packet sent and the target MAC address.)
How would the system know where to send the packet otherwise? I believe you were hoping that the system would look at the IP header you've already constructed in the packet buffer, and then do an interface selection and ARP lookup on your behalf, but by using a packet socket, you're bypassing that part of the network stack. (Also, if the IP address is not on the local LAN, there would be a routing step needed -- usually just finding the default gateway and ARPing for its MAC address.)
Now you can get most of what (I think) you want with a raw socket (http://man7.org/linux/man-pages/man7/raw.7.html). In this case, you're telling the kernel that you're using IP but not relying on it for any of the higher layers (> L3). And you can also construct your own IP header in this case and have the routing decision based on it using the IP_HDRINCL option.
I am currently writing a kernel module that modifies packet payloads as a learning experience. I have the packet modifications done, but now I want to send out this new modified packet after the original (I don't want to drop the original). I can't seem to find a kernel function that sends SKB's for transmission. I've tried dev_queue_xmit(nskb) but that causes a kernel panic, I also tried skb->next = nskb but that does nothing. Do I have to implement the SKB list handling? I am unsure of how to do that since this article seems to be outdated .
EDIT:
So I was able to fix the kernel panic when calling dev_queue_xmit(nskb), I was accidentally doing dev_queue_xmit(skb) which would delete skb and cause a panic from net filter. The problem is now that everything works, but I'm not seeing duplicate packets being sent out, there is no trace of the second packet ever being sent. TCPDump on the machine doesn't see anything and TPCDump on the target doesn't see anything either, the following is my code.
unsigned int in_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct sk_buff *nskb = skb_copy(skb, GFP_KERNEL);
/* Various other variables not relevant to the problem */
__u32 saddr, daddr;
saddr = ntohl(iph->saddr);
if (saddr == ipToInt(10,0,2,12) || saddr == ipToInt(10,0,2,13)) {
/*For loop that saves the payload contents into a variable */
/* Here is where the problem is,
I have this if statement to prevent a feedback loop
then if the ip matches, I call dev_queue_xmit(nskb)
which is supposed to send out sk_buff's, but TCPDump doesn't
show anything on any computer */
if (saddr == ipToInt(10,0,2,13)) {
dev_queue_xmit(nskb);
}
/* Rest of the code that isn't relevant to sending packets */
}
return NF_ACCEPT;
}
My network setup is as follows, it's 3 Ubuntu Server VM's, all of them are being SSH'd into from the host computer (macOS if it matters, I don't know at this point). The computer running the above kernel module spoofs bidirectionally the other two VM's. The other two VM's then talk to each other via a netcat session. I'm hoping that when I send one message from the VM with ip 10.0.2.13, that 10.0.2.12 sees two of the same message. I know the acknowledgement number mishap will break the connection, but I'm not getting that. TCPDump on any of the 3 computers doesn't show anything besides the packets that are supposed to be sent.
I have so far tried dev_queue_xmit(nskb) as well as nskb->dev->netdev_ops->ndo_start_xmit(nskb, skb->dev).
As far as I remember dev_queue_xmit() is the right procedure for sending. The question is how do you prepared the skb you want to send? Also give us the calltrace from dmesg when the kernel panic occured. Do you set skb->dev?
I figured it out, skb_copy doesn't copy the ethernet header of an skb, so the sent packet never reaches its destination.
can anyone tell how can i find ip/udp header while it created by socket type SOCK_DGARM option.in my VOIP application while this option set sendto() function send only RTP data not whole buffer of IP/UDP/RTP header and data.that's why i want to find that where IP/UDP header are created.so can anyone tell at which point i find it..???
error = sendto (sockfd, (char*)m->b_rptr, (int) (m->b_wptr - m->b_rptr),
0,destaddr,destlen);
here,m->b_rptr is point out rtp header and data. and only this send and recv.
You can find it using a packet capture tool like tcpdump or Wireshark. You can't access the low-level protocol details directly via the sendto() function call--the OS crafts the headers for you and normally you wouldn't need to see them. But a packet capture on either the sending or the receiving end will show you the headers.
Using the socket API, you do not need to create the IP/UDP header. The OS will automatically fill the appropriate headers. All that you need, is to create your RTP packet and encapsulate it inside a UDP packet. At the receiver side, when you use recvfrom(), your buffer will contain only the UDP payload, which is the RTP packet in your case.
If you want to retrieve other information too, like the source IP or source port of a UDP packet received you have too do some more steps. For example for the source IP:
struct sockaddr src_addr;
socklen_t src_addr_len;
struct sockaddr_in *src_addr_in;
/* Receive a UDP packet. Error Checking is omitted */
memset(&src_addr, 0, sizeof(struct sockaddr));
recvfrom(sockfd, buffer, MSG_MAX_LEN, 0, &src_addr, &src_addr_len));
/* Now lets retrieve the IP of the sender */
/* Cast first the sockaddr struct to another more appropriate struct */
src_addr_in = (struct sockaddr_in *) &src_addr;
/* And now use inet_ntoa to print the source IP in a human readable form */
printf("Source IP: %s\n", inet_ntoa(src_addr_in->sin_addr));
In a similar way you can retrieve the the source port of the packet.
I want to change the linux kernel code to filter some tcp packet and drop it.
But I always keep receiving it again and again. Here is my code in
/net/ipv4/tcp_ipv4.c
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
// my code start
struct iphdr *iph;
iph = skb->nh.iph;
if(iph->ttl > 64) // I want to drop all tcp packet that meet this requirement
{
return 0;
}
// my code end
// start normal linux code
if(sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
...
}
As #nos said, TCP is reliable, so the other end will retransmit the dropped packet. You would need to send a RST or an ICMP ERROR (probably host unreachable, administratively prohibited) to teardown the connection.
Also, note that you've created a memory leak, you're responsible for freeing skb's when you discard them.
There is a ttl module for iptables, which can filter by ttl:
iptables –A INPUT -m ttl --ttl-gt 65 –j DROP
If you really wanted to, you could modify the code to send an acknowledgment for the packet, but instead drop it. I don't really recommend this.
For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.
I use the following code:
char myhostname[32];
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
if( (my_ip % 256) == 127) {
/* Wrong IP adress as it's 127.x.x.x */
printf("Error, local IP address!");
return;
}
The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).
Is there a way to solve this without relying on the content of /etc/hosts?
Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:
struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;
hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;
if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip = *(unsigned *) &sinp->sin_addr;
(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)
So my question still stands...
There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.
See man getaddrinfo.
Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.
These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.
Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.
The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())
I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:
The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch
So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.
Hope this helps someone.
Look at this:
Discovering public IP programmatically
Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.
Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.
Use getaddrinfo()
You're almost there. I'm not sure how you're getting my_ip from hp.
gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.
The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.
I think you're getting the loopback address because it's the first entry in h_addr_list.
EDIT: It should work something like this:
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
// hp->addr_list[i] is a non-loopback address
}
}
// no address found
If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.
Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.
Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...
struct addrinfo {
...
struct addrinfo *ai_next; /* next structure in linked list */
};