I've got the TCP Echo example working well on my hardware, and yesterday figured out how to get a UDP Broadcast working. After further thought, I've realized is that what I really need is to be able to set up a TCP Connection to a Static IP, the idea being that my hardware can connect to a server of some sort and then use that connection for all its transactions. The difference is that whereas the echo example sets up a passive connection, that binds with the incoming source (as I understand it), I want to initiate the connection deliberately to a known IP.
Based on what I found on Wikia Here Here
I've attempted as a base case to implement a function that can send a packet to a Defined IP. I'm simply trying to send a packet to my PC, and I'm looking for it on Wireshark.
void echo_tx_tcp()
{
err_t wr_err = ERR_OK;
struct tcp_pcb *l_tcp_pcb;
l_tcp_pcb = tcp_new();
ip_addr_t dest_ip =
{ ((u32_t)0x0C0C0C2BUL) };
wr_err = tcp_bind(l_tcp_pcb, &dest_ip, 12);
wr_err = tcp_connect(l_tcp_pcb, &dest_ip, 12, echo_accept);
tcp_sent(l_tcp_pcb, echo_sent);
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 1024, PBUF_RAM);
unsigned char buffer_send[1024] = "My Name Is TCP";
p->payload = buffer_send;
p->len = 1024;
p->tot_len = 1024;
wr_err = tcp_write(l_tcp_pcb, p->payload, p->len, 1);
wr_err = tcp_output(l_tcp_pcb);
if(wr_err == ERR_OK)
{
p->len++;
}
return;
}
The last if statement just exists so that I can inspect the wr_err value with a debugger. The err is coming back OK but the packet is not seen on wireshark. My setup is my hardare as well as my PC connected to a router in an isolated manner. The IP Address of the PC locally is 12.12.12.43
Am I missing a step here?
The tcp_write() function will fail and return ERR_MEM if:
The length of the data exceeds the current send buffer size.
The length of the queue of the outgoing segment is larger than the upper limit defined in lwipopts.h.
The number of bytes available in the output queue can be retrieved with the tcp_sndbuf() function.
Potential solution(s):
Try again but send less data.
Monitor the amount of space available in the send buffer and only send (more) data when there is space available in the send buffer.
Suggestions:
tcp_snd_buf() can be used to find out how much send buffer space is available.
tcp_sent() can be implemented with callback function, that will be called when send butter space is available.
Related
OBS: I'm using a STM32F2 microcontroller, FreeRTOS and lwIP and I'm using the raw API
I have an application in which I'm listening to one PC and connecting to another. Basically everything works fine for a while when I am trying to achieve high throughput, but after about half an hour ... about 80~90k packets received it hangs. It actually varies a little bit where it hangs, but it stopped doing it when I started closing the connection whenever tcp_write returnd err_mem.
Sometimes it hangs in this line:
/* useg should point to last segment on unacked queue */
useg = pcb->unacked;
if (useg != NULL) {
for (; useg->next != NULL; useg = useg->next); <------- here
}
Sometimes when I call tcp_write it returns ERR_MEM and it never returns anything besides after ERR_MEM. This is how I send data, basically I accept a connection, recieve data, store the PCB, do something and then reply to that same PCB:
err_t ret;
ret = tcp_write(g_response[i].pcb, data, len, 1);
if(ret == ERR_OK)
tcp_output(g_response[i].pcb);
else
tcp_close(g_response[i].pcb);
Here is how I setup the socket to listen:
pcb = tcp_new();
tcp_bind(pcb, IP_ADDR_ANY, port);
pcb = tcp_listen(pcb);
pcb->so_options |= SOF_KEEPALIVE; // enable keep-alive
pcb->keep_intvl = 1000; // sends keep-alive every second
tcp_accept(pcb, accept);
And here are my callbacks to sent and rcv
static err_t rcv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
if(p == NULL) {
return ERR_OK;
} else if(err != ERR_OK) {
return err;
}
tcp_recved(pcb, p->len);
// do something
pbuf_free(p);
return ERR_OK;
}
int sentcounter = 0;
static err_t sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {
sentcounter++;
return ERR_OK;
}
static err_t accept(void *arg, struct tcp_pcb *pcb, err_t err) {
int i;
tcp_arg(pcb, NULL);
/* Set up the various callback functions */
tcp_recv(pcb, rcv);
tcp_err(pcb, error);
tcp_sent(pcb, sent);
tcp_accepted(pcb);
}
The way I send data where I close the pcb whenever there isn ERR_MEM may be strange, but now I have fewer lost packets and it actually got me to exchange up to 90k packets, before that it was failing randomly.
I actually need a high throughput, that's why I'm calling tcp_output instead of letting the tcpip_thread deal with sending the packet. Whenever I let this thread take car of the packet eveything just works, but it's too slow (maybe one packet every 200~300 ms, and by calling tcp_output in the function I got to the point where I'm sending the data sub 10 ms ... I'm also not transfering big amounts of data, so that helps).
Recently I've noticed that the tcpip_thread calls the input function, goes to ipv4_input, goes to memp_free and keeps going but never leaves (I'm actually running a new test right now so later I'll update this question with the callstack to the input before it freezes).
Has somebody done anything similar? Bursts of small packets with high throughput?
EDIT: As promised, here is my call stack
osMutexWait() at cmsis_os.c:681 0x800474c sys_arch_protect() at
sys_arch.c:400 0x80146a6 do_memp_free_pool() at memp.c:415 0x800dca2
memp_free() at memp.c:486 0x800dcf8 tcp_seg_free() at tcp.c:1.336
0x800fb0e tcp_receive() at tcp_in.c:1.162 0x8011712 tcp_process() at
tcp_in.c:877 0x8011048 tcp_input() at tcp_in.c:367 0x8010692
ip4_input() at ip4.c:670 0x800c688 ethernet_input() at ethernet.c:176
0x80142fe tcpip_thread() at tcpip.c:124 0x8006836
pxPortInitialiseStack() at port.c:231 0x8004cd0
Just like #Lundin said, it is a concurrency problem. I should probably try to be more careful with how the functions are called. I'll try to modify my code to work with netconn or socket instead of tcp_pcb, and then measure the speeds I get. I really need high throughput
Is it possible to map physical address as data fragment in sk_buff?
I am working on Zynq Ultrascale+ platform (FPGA + ARM SOC). I have memory buffer mapped to physical address. The goal is to efficiently send that data over UDP. By efficiently I mean ZEROCOPY. What I am trying to do is to develop linux driver that would map that physical address into kernel memory and append it to sk_buff as fragment.
I started with:
#define PACKET_LEN 1024
struct page *pag;
struct net_device *dev;
struct sk_buff *skb = NULL;
skb = alloc_skb(LL_RESERVED_SPACE(dev) + PACKET_LEN + ip_header_l +
udp_header_l, GFP_ATOMIC);
udp = skb_push(skb, udp_header_l);
//Fill up udp header
...
ip = skb_push(skb, ip_header_l);
//fill up ip header
...
dev_hard_header(skb, dev, ETH_P_IP, addr, myaddr, dev->addr_len);
skb->dev = dev;
//map page with data as fragment
skb_fill_page_desc(skb, 0, pag, 0, PACKET_LEN);
//send data
dev_queue_xmit(skb);
And as long as page is created by:
pagebuff = vmalloc(PACKET_LEN);
pag = vmalloc_to_page(pagebuff);
It all works fine. Packet gets send. Packet is send by two DMA transactions (Scatter Gather).
Going towards my goal I replaced vmalloced page with:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
membase = devm_ioremap_resource(&pdev->dev, res);
pag = virt_to_page(membase);
Physical address is 0xb0000000 and is mapped to virtual address 0xffffff800ad30000 page is at 0xffffffbf0025e280.
After dev_queue_xmit packet goes to network queue and ends up being mapped for DMA.
Problem arises when swiotlb_map_page uses 0x00ad30000 as phys_addr, which is different than original 0xb0000000.
virt_to_phys is used in swiotlb_map_page to calculate physical address and it basically takes lower 32 bits as phys address. Is there a different way to map memory region so it can be used as sk_buff fragment?
As a temporary fix I created fake page like this:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pag = alloc_page(0); //create fake page
memset(pag, 0, sizeof(struct page));
pag->private = res->start;
And patched ethernet driver to use page private data as mapping address:
mapping = skb_frag_page(frag)->private;
if (mapping) {
// printk("macb mapping override to %p\n",mapping);
}
else {
mapping = skb_frag_dma_map(&bp->pdev->dev, frag, offset, size, DMA_TO_DEVICE);
if (dma_mapping_error(&bp->pdev->dev, mapping))
goto dma_error;
}
With such a hack it all works. Data is filled with contents of 0xb0000000. Although it works fine I really doubt it is the right way to do it. Nevertheless it shows there is no hardware limitation to do it. Does anyone know how to map that memory correctly?
P.S. I also tried to map physical address to fixed virtual address in such manner that swiotlb_map_page would calculate correct address (and virt_to_phys did), but it ended with "Unable to handle kernel paging request at virtual address" error.
membase = phys_to_virt(res->start);
i = ioremap_page_range(membase, membase + resource_size(res),
res->start, PAGE_KERNEL);
//I tried both
pag = phys_to_page(res->start);
pag = virt_to_page(membase);
Maybe I am looking for page at wrong address or maybe it is nonexistent.
Can anyone point me in the right direction. Is there a way to accomplish the goal without such a nasty hack?
I am interested in creating a DNS (using UDP protocol to send it) response packet, however I found limited information how to create your own packet.
Most tutorials are like this https://opensourceforu.com/2015/03/a-guide-to-using-raw-sockets/
They use structs to fill in the fields and connect them into 1 sequence. But I am concerned that the compiler can pad the struct, making it "corrupted" (make the packet longer then it should be)
I fully know that there are struct attributes, that don't allow the compiler to pad structs, but I don't want to use them
Can anyone point me some resources on packet creation. I can use Libpcap and raw sockets
You do it like this:
// helper function to add uint32_t to a buffer
char *append_uint32(char *buf_position, uint32_t value) {
// network protocols usually use network byte order for numbers,
// htonl is POSIX function so you may have to make your own on other platform
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/htonl.html
value = htonl(value);
memcpy(buf_postion, &value, sizeof value);
return buf_position + sizeof value;
}
// example code using the function:
// generate packet with numbers 0...9 in network byte order
void func() {
char buf[sizeof(int32_t) * 10];
char *bptr = buf;
for(uint32_t i=0; i<10; ++i) {
bptr = append_uint32(bptr, i);
}
// do something with buf (use malloc instead of stack if you want return it!)
}
I'm using uIP on a Tiva C Launchpad board and want to send UDP Packages. But it seems that the uip_buf is not filled when i call the uip_udp_periodic function.
The code looks like this:
uint8_t my_udp_buf = {0x00, 0xAA, 0xBB, 0xCC};
uint32_t my_udp_buf_len = 4;
void main(main){
[...]
uip_ipaddr_t addr;
struct uip_udp_conn *c;
uip_ipaddr(&addr, 172,16,23,1);
c = uip_udp_new(&addr, HTONS(12345)); // setting up a new UDP connection to 172.16.23.1:12345 here
[...]
while(42==42){
uip_udp_conn = c; // set the current connection to our udp connection
uip_appdata = my_udp_buf; // asssign the uip_appdata pointer to our data pointer
uip_send(uip_appdata, my_udp_buf_len); // sending the data
[...]
// call the periodic function for all UDP connections
for(ui32Temp = 0; ui32Temp < UIP_UDP_CONNS; ui32Temp++)
{
uip_udp_periodic(ui32Temp);
// --> The uip_len is always 0! why?
//
// If the above function invocation resulted in data that
// should be sent out on the network, the global variable
// uip_len is set to a value > 0.
//
if(uip_len > 0)
{
uip_arp_out();
PacketTransmit(EMAC0_BASE, uip_buf, uip_len);
uip_len = 0;
}
}
}
}
The question is, do i set the connection correctly? In the header file i cannot find any macro or function to control on which connection i send out the data, so i assume that i need to set the connection pointer. Also do i need to save the data? probably the pointer to uip_appdata is overwritten somewhere else afterwards.
It seems like UDP is not well implemented in the bare uIP version. you need to do a lot of manual stuff:
uip_udp_conn = c; // set your connection
uip_slen = len; // set the length of data to send
memcpy(&uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN], data, len > UIP_BUFSIZE? UIP_BUFSIZE: len); // copy to the buffer
uip_process(UIP_UDP_SEND_CONN); // tell uip to construct the package
uip_arp_out(); // attack Ethernet header
PacketTransmit(EMAC0_BASE, uip_buf, uip_len); // send the package with the Tiva C function
uip_len = 0; // reset length to 0
in the contiki version of uIP is a lot more UDP convenient functionality.
When working with udp and the tiva, I have found that having a seperate function to handle the udp instances works much better. when you run it out of your main function, you will end up having multiple instances and that will cause instability
I am writing a kernel module which forms its own packet at ip level and then sends the data . I just want to know how to find the ip address of a local network interface.
There are defined ioctls SIOCGIFADDR (to get if address) and SIOCSIFADDR (to set if address).
All device configuration for IPv4 is done in net/ipv4/devinet.c. You can refer to this source file to get more information.
Wanting to read interface addresses reeks of a design problem. However, if you are looking for determining the preferred source address to use when contacting a remote peer is given in struct rt6_info.rt6i_prefsrc after obtaining it with ip6_route_output. Something along the lines of (with no implied guarantees):
int pick_addr(struct in6_addr *saddr, struct net *net, const struct in6_addr *daddr)
{
struct rt6_info *rt;
struct flowi6 fl6;
int ret;
memset(&fl6, 0, sizeof(fl6));
memcpy(&fl6.daddr, daddr, sizeof(*daddr));
dst = (struct rt6_info *)ip6_route_output(net, NULL /* or sk if you have it */, &fl6);
ret = rt->dst.error;
if (ret == 0)
memcpy(saddr, &rt->rt6i_prefsrc.addr, sizeof(*saddr));
dst_release(&rt->dst);
return ret;
}
We have if_getconfig function available in linux-x.y.z/Documentation/networking/ifenslave.c file which is a very good example of how to use ioctls and fetch address from kernel space