I'am writing an network library in C from scratch. I already implemented the Ethernet protocol and now I want to get ARP working.
Sending Requests/Replies works fine but receiving isn't working well.
When I send an send an Request and wait for the Reply after it, recvfrom() just takes the first incoming ARP packet. But I want to get the Reply from the host replying to my Request.
I can't just receive packets until the correct one arrives because the library should support socket timeouts. (set with setsockopt())
The socket is created like this:
int sfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP))
Output of my test program:
Sending ARP-Request ...
0o0o0o0 ARP-FRAME DUMP 0o0o0o0
HwType: 256 (0x7ab74e358670)
PrType: 8 (0x7ab74e358672)
HwALen: 6 (0x7ab74e358674)
PrALen: 4 (0x7ab74e358675)
ArpOP : 256 (0x7ab74e358676)
Sha : 84:4b:10:14:a0:04 (0x7ab74e358678)
Spa : 192.168.12.1 (0x7ab74e35867e)
Tha : 00:00:00:00:00:00 (0x7ab74e358682)
Tpa : 192.168.0.3 (0x7ab74e358688)
Receiving ARP-Reply ...
0o0o0o0 ARP-FRAME DUMP 0o0o0o0
HwType: 256 (0x7ab74e358670)
PrType: 8 (0x7ab74e358672)
HwALen: 6 (0x7ab74e358674)
PrALen: 4 (0x7ab74e358675)
ArpOP : 512 (0x7ab74e358676)
Sha : 10:00:00:00:00:01 (0x7ab74e358678)
Spa : 192.168.12.78 (0x7ab74e35867e)
Tha : 84:4b:10:14:a0:04 (0x7ab74e358682)
Tpa : 192.168.12.1 (0x7ab74e358688)
Is it possible to filter incoming ARP packets/how to do it?
Thanks in advance.
--- EDIT ---
I played around with the BPF and it worked fine for filtering out ARP packets.
It wrote this filter
ld [28]
jne #0x4e0ca8c0, drop
ret #-1
drop: ret #0
to filter the incoming ARP replies.
It should load the source IP from the packet and compare it with the one defined in the code. 0x4e0ca8c0 is the valid IP addr. of the host whose reply I want. tcpdump shows the incoming reply but my program freezes (Waits forever).
I used the BPF like this:
struct sock_fprog prog;
struct sock_filter filter[4] =\
{{ 0x20, 0, 0, 0x0000001c },
{ 0x15, 0, 1, 0x4e0ca8c0 },
{ 0x06, 0, 0, 0xffffffff },
{ 0x06, 0, 0, 0000000000 }};
prog.len = 4;
prog.filter = filter;
if(setsockopt(sfd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) ==\
-1) {
fprintf(stderr, "ERROR enabling bpf: setsockopt(): %s\n",\
strerror(errno));
goto ... (error handler);
}
/* Receive incoming ARP reply */
I hope I haven't made any stupid/obvious mistakes.
Thanks in advance!
-- LAST EDIT --
The IP addr. must be in NBO.
So with the correct BPF:
struct sock_filter filter[4] =\
{{ 0x20, 0, 0, 0x0000001c },
{ 0x15, 0, 1, 0xc0a80c4e },
{ 0x06, 0, 0, 0xffffffff },
{ 0x06, 0, 0, 0000000000 }};
... only replies from 192.168.12.78 (0xc0a80c4e) are accepted/received.
Huge thanks to Ctx!
You can use a (properly configured) Berkeley Packet Filter (BPF) for it.
For this, you have to write a (small) BPF-filter, which exactly matches the reply you are looking for.
#include <linux/filter.h>
struct sock_filter filter[NR_INSTRUCTIONS];
filter[0].code = ...;
filter[0].k = ...;
filter[0].jt = ...;
filter[0].jf = ...;
filter[1].code = ...;
...
The "language" is very basic and is described here
Then put the instructions together into a BPF program:
struct sock_fprog prog;
prog.len = NR_INSTRUCTIONS;
prog.filter = filter;
Finally, you can attach the filter to your socket:
setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog));
If you configured your filter correctly, you will only receive the matching arp replies on your socket from now on.
For the next request/reply you have to reconfigure the filter to match the new parameters (SO_DETACH_FILTER followed by SO_ATTACH_FILTER with the new program).
For ARP packets it is especially easy to construct a filter, since the offsets of the fields in the packet are fixed.
Related
I have a self implemented NTP server working on a Ubuntu machine.
NTP Client is another Ubuntu machine with both ntpdate and ntpd utilities installed. Server has has address 192.168.2.200 and the client is 192.168.2.74 . If I run:
ntpdate 192.168.2.200
Or
ntpd -c /root/NTP.conf
With NTP.conf
server 192.168.2.200 minpoll 0 maxpoll 0 prefer
The client OS will synchronize with my server.
Next step is to put server behind a NAT.
NTP Client still has address 192.168.2.74 but now the server is behind a Windows 10 machine with address 192.168.2.75 .
I set the port forwarding in order to forward UDP packets directed to 192.168.2.75:123 to the internal address 192.168.2.200:123, where the server is located. For some reason, even if the client (2.74) receives the same answer from the server (2.200 natted as 2.75), it won't synchronize.
This is what I've done so far:
Define a custom struct for NTP message as defined in rfc5905:
typedef struct
{
uint8_t hdrhead[4];
uint32_t delay;
uint32_t dispersion;
uint32_t reference_id;
uint64_t reference_ts;
uint64_t origin_ts;
uint64_t receive_ts;
uint64_t transmit_ts;
} ntp_msg5905;
In main function, I have a simple while loop:
// socket initialization...
while (1)
{
uint8_t recv_msg[NTP_MSGLEN];
while (recvfrom(s, recv_msg, NTP_MSGLEN, 0, (struct sockaddr*) &src_addr, &src_addrlen) < NTP_MSGLEN)
{
_log_("Arrived wrong NTP request: message too short", stdout);
}
_log_("Arrived NTP request", stdout);
uint64_t recvtime;
timestamp_64(&recvtime);
ntp_reply(s, &src_addr , src_addrlen, *(ntp_msg5905*)recv_msg, recvtime);
}
timestamp_64() function just build a 64bit timestamp obtained with gettimeofday(), nothing special or ntp related.
The ntp_reply function is:
void ntp_reply(int clisock, struct sockaddr_in* saddr_p, socklen_t saddrlen, ntp_msg5905 request, uint64_t recvtime)
{
ntp_msg5905 reply;
memset(&reply, 0x00, NTP_MSGLEN);
reply.hdrhead[STRATUM] = NTP_STRATUM_PRIMARY_SERVER;
reply.hdrhead[PREC] = NTP_HOST_PRECISION;
reply.delay = NTP_NODELAY;
reply.dispersion = NTP_NODISPERSION;
reply.reference_id = NTP_REF_ID_GPS;
reply.hdrhead[LIVN] = request.hdrhead[LIVN] & 0x38 + NTP_MODE_SERVER;
reply.hdrhead[POLL] = request.hdrhead[POLL];
reply.receive_ts = recvtime;
reply.origin_ts = request.transmit_ts;
uint64_t* rts = &reply.reference_ts;
uint64_t* tts = &reply.transmit_ts;
timestamp_64(rts);
timestamp_64(tts);
if (sendto(clisock, (char*) &reply, NTP_MSGLEN, 0, (struct sockaddr*) saddr_p, saddrlen) < NTP_MSGLEN)
{
_log_("Cannot send NTP reply", stdout);
}
else
{
_log_("Request reply sent back to client", stdout);
}
}
Global defines below:
/* Global NTP parameters */
#define NTP_UTC 2208988800U
#define NTP_PORT 123
#define NTP_MSGLEN 48
/* NTP header's first 4 bytes */
#define LIVN 0
#define STRATUM 1
#define POLL 2
#define PREC 3
/* NTP header field values */
#define NTP_MODE_SERVER 4
#define NTP_STRATUM_PRIMARY_SERVER 1
#define NTP_HOST_PRECISION -6
#define NTP_NODELAY 0
#define NTP_NODISPERSION 0
#define NTP_REF_ID_GPS *(uint32_t*)("GPS")
Where am I wrong?
I am taking a shot at writing a wrapper for the S1V30120 dectalk text synthesis IC in C language using the ESP IDF. I am running into a problem in the following code.
printf("Hello world!\n");
esp_err_t ret;
spi_device_handle_t spi;
spi_bus_config_t buscfg={
.miso_io_num=PIN_NUM_MISO,
.mosi_io_num=PIN_NUM_MOSI,
.sclk_io_num=PIN_NUM_CLK,
.quadhd_io_num=-1,
.quadwp_io_num=-1
};
spi_device_interface_config_t devcfg={
.clock_speed_hz=750000,
.mode=0,
.spics_io_num=PIN_NUM_CS,
.queue_size=7
};
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
assert(ret==ESP_OK);
ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
uint8_t rec[20];
assert(ret==ESP_OK);
uint8_t cmd[] = {0xAA, 0x04, 0x00, 0x05, 0x00}; // get information command
printf("waiting for rdy");
while (gpio_get_level(PIN_NUM_RDY) == 0){};
//create 2 transactions 1 for transmitting the GET INFORMATION command
//and 1 for getting the data back from the ic
spi_transaction_t t;
spi_transaction_t r;
memset(&t, 0, sizeof(t));
t.length=5*8;
t.tx_buffer=&cmd;
r.length=20*8;
r.rx_buffer=&rec;
ret = spi_device_transmit(spi, &t);
assert( ret == ESP_OK);
ret = spi_device_transmit(spi, &r);
assert(ret == ESP_OK);
printf((char*)r.rx_data);
/* Print chip information */
printf("Restarting now.\n");
I'm pretty sure connection should be in full duplex mode and I believe that is set up correctly. the information coming back should be 20 bytes but I am getting the error
rxdata transfer > 32 bits without configured DMA
at the moment I am following 2 pieces of code that may help.
an example of using SPI in the esp idf:
https://github.com/espressif/esp-idf/blob/3276a1316f66a923ee2e75b9bd5c7f1006d160f5/examples/peripherals/spi_master/main/spi_master_example_main.c
an example of using the dectalk ic in Arduino ide:
https://electronza.com/arduino-due-s1v30120-text-speech-code/2/
the dectalk ic protocol sheet:
https://github.com/MikroElektronika/Click_TextToSpeech_S1V30120/blob/master/datasheet/S1V30120%20Protocol%20Specification.pdf
and the SPI documentation for the esp idf:
https://gitdemo.readthedocs.io/en/latest/api/peripherals/spi_master.html
the protocol sheet also says something about sending 16 bytes of 0x00 after a transaction I've never seen that before though.
I hope I was thorough enough with all this information and I thank anyone in advance who can help!
You have used "1" for DMA in the line
ret=spi_bus_initialize(HSPI_HOST, &buscfg, 1);
But as mentioned in the docs, the DMA needs to be configured.
For external PSRAM available on these boards that means something like
pvPortMallocCaps(20, MALLOC_CAP_DMA)
Without DMA only 32 bits data are possible per transaction.
I have the following:
Server Side: TCP python server (not scapy)
Client Side: Scapy to establish connection and sent TCP packet
I am trying to send TCP packet via scapy on established connection after 3 way handshaking
I am able to build the 3 way handshaking and the server side (other side -python TCP server- not scapy- create TCP socket, bind, listen, accpet, recv()) shows new connection comes and accept() returns the created FD
I am trying to send packet from scapy after the 3 way handshake succeeded but recv() on the not-scapy side can't get the packet
scapy side:
#!/usr/bin/env python
from scapy.all import *
import time
# VARIABLES
src = sys.argv[1]
dst = sys.argv[2]
sport = random.randint(1024,65535)
dport = int(sys.argv[3])
# SYN
ip=IP(src=src,dst=dst)
SYN=TCP(sport=sport,dport=dport,flags='S',seq=1000)
SYNACK=sr1(ip/SYN)
# ACK
ACK=TCP(sport=sport, dport=dport, flags='A', seq=SYNACK.ack, ack=SYNACK.seq + 1)
send(ip/ACK)
time.sleep(15)
ip = IP(src=src, dst=dst)
tcp = ip / TCP(sport=sport, dport=dport, flags="PA", seq=123, ack=1) / "scapy packet 123"
tcp.show2()
send(tcp)
Not scapy side:
#!/usr/bin/python
import socket
from scapy.all import *
ip = sys.argv[1]
port = sys.argv[2]
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((ip, int(port)))
s.listen(1)
while True :
conn, addr = s.accept()
print 'Connection address:', addr
data = conn.recv(1024) # Stuck here .....
tcpdump output shows:
tcpdump: listening on ens1f1, link-type EN10MB (Ethernet), capture size 65535 bytes
18:09:35.820865 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto TCP (6), length 40)
11.4.3.31.63184 > 11.4.3.30.strexec-d: Flags [S], cksum 0x6543 (correct), seq 1000, win 8192, length 0
18:09:35.821017 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 44)
11.4.3.30.strexec-d > 11.4.3.31.63184: Flags [S.], cksum 0x748d (correct), seq 3017593595, ac k 1001, win 29200, options [mss 1460], length 0
18:09:35.930593 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto TCP (6), length 40)
11.4.3.31.63184 > 11.4.3.30.strexec-d: Flags [.], cksum 0xde5a (correct), seq 1, ack 1, win 8 192, length 0
18:09:51.057904 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto TCP (6), length 56)
11.4.3.31.63184 > 11.4.3.30.strexec-d: Flags [P.], cksum 0x8eef (correct), seq 4294966419:429 4966435, ack 1277373702, win 8192, length 16
18:09:51.057996 IP (tos 0x0, ttl 64, id 1194, offset 0, flags [DF], proto TCP (6), length 40)
11.4.3.30.strexec-d > 11.4.3.31.63184: Flags [.], cksum 0x8c4a (correct), seq 1, ack 1, win 2 9200, length 0
My question why receiver side is not getting the sent packet?
Note: My target to send TCP packet on established connection with bad checksum and receive it by not scapy tcp server
Thanks in advance!!
Your sequence numbers must accurately track the payload bytes you send. A packet with the SYN or FIN flag set is an exception and is treated as if it had a payload of length 1. In other words, you can use whatever initial sequence number you like, but then it must increase byte-for-byte with your sent payload (+1 for SYN or SYN+ACK [or FIN]).
So, if you start with a sequence number of 1000 in the SYN packet, then the next packet with payload (call this pktA) should have a sequence number of 1001. Then your next packet (pktB) should have sequence number 1001 + pktA.payload_size, and so forth.
Likewise, you cannot simply set the acknowledge number field in the TCP header to 1 (as you're doing with the "scapy packet 123"). Whenever you provide the ACK flag in the header, you need to acknowledge the other side's payload by setting the acknowledge number in the header to the last-received sequence number from the other side's last payload. In this case, you already sent a bare ACK packet with that acknowledge number, so you don't strictly need to include the ACK flag, but it's typical to always include it and if you are going to include the flag, the acknowledge sequence number should be set correctly.
See this link for a good overview:
http://packetlife.net/blog/2010/jun/7/understanding-tcp-sequence-acknowledgment-numbers/
I wrote a program to send 1460 byte data using TCP client and server continuously. My system interface MTU is 1500.
Here is my program of client
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
setsockopt(sockfd,SOL_TCP,TCP_NODELAY,&one,sizeof(one));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9998);
serv_addr.sin_addr.s_addr = inet_addr("10.10.12.1");
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
while(1)
{
write(sockfd, send_buff, 1448) ;
}
In wireshark initial 15 to 30 packets are showing that 1514 byte of packet is going but afterwards showing as below
wireshark output of some packet
No. Time Source Destination Protocol Length Info
16 0.000000 10.10.12.2 10.10.12.1 TCP 5858 53649 > distinct32 [ACK] Seq=3086892290 Ack=250285353 Win=14608 Len=5792 TSval=23114307 TSecr=23833274
Frame 16: 5858 bytes on wire (46864 bits), 5858 bytes captured (46864 bits)
Ethernet II, Src: 6c:3b:e5:14:9a:a2 (6c:3b:e5:14:9a:a2), Dst: Ibm_b5:86:85 (00:1a:64:b5:86:85)
Internet Protocol Version 4, Src: 10.10.12.2 (10.10.12.2), Dst: 10.10.12.1 (10.10.12.1)
Version: 4
Header length: 20 bytes
Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
Total Length: 5844
Identification: 0x8480 (33920)
Flags: 0x00
Fragment offset: 0
Time to live: 64
Protocol: TCP (6)
Header checksum: 0xb38d [correct]
Source: 10.10.12.2 (10.10.12.2)
Destination: 10.10.12.1 (10.10.12.1)
Transmission Control Protocol, Src Port: 53649 (53649), Dst Port: distinct32 (9998), Seq: 3086892290, Ack: 250285353, Len: 5792
Source port: 53649 (53649)
Destination port: distinct32 (9998)
[Stream index: 0]
Sequence number: 3086892290
[Next sequence number: 3086898082]
Acknowledgement number: 250285353
Header length: 32 bytes
Flags: 0x010 (ACK)
Window size value: 913
[Calculated window size: 14608]
[Window size scaling factor: 16]
Checksum: 0x42dd [validation disabled]
Options: (12 bytes)
No-Operation (NOP)
No-Operation (NOP)
Timestamps: TSval 23114307, TSecr 23833274
Data (5792 bytes)
On wireshark it is showing that more than 5792, 7000, 65535 byte of packet are going.
But i am sending 1514 byte of packet in one go. on other side i am receiving 1514 byte of packets only due to network mtu.
So my question is
why this much of huge packets are going ?
I tried without NODELAY option also but it is not working.
Is there any solution to send particular packet size (such as 1514 byte) can be send, no jumbo frames ?
I update my tcp_rmem and tcp_wmem also for tcp sending buffer and receiving buffer also. But did not found any solution.
TCP, by design, bundles up multiple write() calls into larger packets. Also, TCP coalesces packets by default according to Nagle's Algorithm.
If you want more control over the actual size of network packets, use UDP.
These are "jumbo frames", and they're faster than traditional frame sizes because they don't load up a CPU as much.
Consider yourself fortunate that you're getting them without futzing around with your IP stack's settings.
I searched a lot and found that, we need to change some parameter on interface.
On my interface eth0 default option are
Offload parameters for eth0:
rx-checksumming: on
tx-checksumming: on
scatter-gather: on
tcp-segmentation-offload: on
udp-fragmentation-offload: off
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off
rx-vlan-offload: on
tx-vlan-offload: on
ntuple-filters: off
receive-hashing: off
now using ethtool we need to off some sending side segementation offload.
For that
sudo ethtool -K eth0 tso off gso off
using this
Offload parameters for eth0:
rx-checksumming: on
tx-checksumming: on
scatter-gather: on
tcp-segmentation-offload: off
udp-fragmentation-offload: off
generic-segmentation-offload: off
generic-receive-offload: on
large-receive-offload: off
rx-vlan-offload: on
tx-vlan-offload: on
ntuple-filters: off
receive-hashing: off
After this your interface will send packets whatever you want to send.
I'm writing network analyzer and I need to filter packets saved in file, I have written some code to filter http packets but I'm not sure if it work as it should because when I use my code on a pcap dump the result is 5 packets but in wireshark writing http in filter gives me 2 packets and if I use:
tcpdump port http -r trace-1.pcap
it gives me 11 packets.
Well, 3 different results, that's a little confusing.
The filter and the packet processing in me code is:
...
if (pcap_compile(handle, &fcode, "tcp port 80", 1, netmask) < 0)
...
while ((packet = pcap_next(handle,&header))) {
u_char *pkt_ptr = (u_char *)packet;
//parse the first (ethernet) header, grabbing the type field
int ether_type = ((int)(pkt_ptr[12]) << 8) | (int)pkt_ptr[13];
int ether_offset = 0;
if (ether_type == ETHER_TYPE_IP) // ethernet II
ether_offset = 14;
else if (ether_type == ETHER_TYPE_8021Q) // 802
ether_offset = 18;
else
fprintf(stderr, "Unknown ethernet type, %04X, skipping...\n", ether_type);
//parse the IP header
pkt_ptr += ether_offset; //skip past the Ethernet II header
struct ip_header *ip_hdr = (struct ip_header *)pkt_ptr;
int packet_length = ntohs(ip_hdr->tlen);
printf("\n%d - packet length: %d, and the capture lenght: %d\n", cnt++,packet_length, header.caplen);
}
My question is why there are 3 different result when filtering the http? And/Or if I'm filtering it wrong then how can I do it right, also is there a way to filter http(or ssh, ftp, telnet ...) packets using something else than the port numbers?
Thanks
So I have figured it out. It took a little search and understanding but I did it.
Wireshark filter set to http filter packets that have set in tcp port 80 and also flags set to PSH, ACK. After realizing this, the tcpdump command parameters which result in the same numbers of packets was easy to write.
So now the wireshark and tcpdump gives the same results
What about my code? well I figured that I actually had an error in my question, the filter
if (pcap_compile(handle, &fcode, "tcp port 80", 1, netmask) < 0)
indeed gives 11 packets (src and dst port set to 80 no matter what tcp flags are)
Now to filter the desired packets is a question of good understanding the filter syntax
or setting to filter only port 80 (21,22, ...) and then in callback function or in while loop get the tcp header and from there get the flags and use mask to see if it is the correct packet (PSH, ACK, SYN ...) the flags number are for example here