1 #include "tcp_helper.h"
3 #include "lwip/tcp_impl.h"
4 #include "lwip/stats.h"
6 #include "lwip/inet_chksum.h"
8 #if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
9 #error "This tests needs TCP- and MEMP-statistics enabled"
12 /** Remove all pcbs on the given list. */
14 tcp_remove(struct tcp_pcb
* pcb_list
)
16 struct tcp_pcb
*pcb
= pcb_list
;
26 /** Remove all pcbs on listen-, active- and time-wait-list (bound- isn't exported). */
30 tcp_remove(tcp_listen_pcbs
.pcbs
);
31 tcp_remove(tcp_active_pcbs
);
32 tcp_remove(tcp_tw_pcbs
);
33 fail_unless(lwip_stats
.memp
[MEMP_TCP_PCB
].used
== 0);
34 fail_unless(lwip_stats
.memp
[MEMP_TCP_PCB_LISTEN
].used
== 0);
35 fail_unless(lwip_stats
.memp
[MEMP_TCP_SEG
].used
== 0);
36 fail_unless(lwip_stats
.memp
[MEMP_PBUF_POOL
].used
== 0);
39 /** Create a TCP segment usable for passing to tcp_input */
41 tcp_create_segment_wnd(ip_addr_t
* src_ip
, ip_addr_t
* dst_ip
,
42 u16_t src_port
, u16_t dst_port
, void* data
, size_t data_len
,
43 u32_t seqno
, u32_t ackno
, u8_t headerflags
, u16_t wnd
)
47 struct tcp_hdr
* tcphdr
;
48 u16_t pbuf_len
= (u16_t
)(sizeof(struct ip_hdr
) + sizeof(struct tcp_hdr
) + data_len
);
50 p
= pbuf_alloc(PBUF_RAW
, pbuf_len
, PBUF_POOL
);
51 EXPECT_RETNULL(p
!= NULL
);
52 /* first pbuf must be big enough to hold the headers */
53 EXPECT_RETNULL(p
->len
>= (sizeof(struct ip_hdr
) + sizeof(struct tcp_hdr
)));
55 /* first pbuf must be big enough to hold at least 1 data byte, too */
56 EXPECT_RETNULL(p
->len
> (sizeof(struct ip_hdr
) + sizeof(struct tcp_hdr
)));
59 for(q
= p
; q
!= NULL
; q
= q
->next
) {
60 memset(q
->payload
, 0, q
->len
);
65 iphdr
->dest
.addr
= dst_ip
->addr
;
66 iphdr
->src
.addr
= src_ip
->addr
;
67 IPH_VHL_SET(iphdr
, 4, IP_HLEN
/ 4);
68 IPH_TOS_SET(iphdr
, 0);
69 IPH_LEN_SET(iphdr
, htons(p
->tot_len
));
70 IPH_CHKSUM_SET(iphdr
, inet_chksum(iphdr
, IP_HLEN
));
72 /* let p point to TCP header */
73 pbuf_header(p
, -(s16_t
)sizeof(struct ip_hdr
));
76 tcphdr
->src
= htons(src_port
);
77 tcphdr
->dest
= htons(dst_port
);
78 tcphdr
->seqno
= htonl(seqno
);
79 tcphdr
->ackno
= htonl(ackno
);
80 TCPH_HDRLEN_SET(tcphdr
, sizeof(struct tcp_hdr
)/4);
81 TCPH_FLAGS_SET(tcphdr
, headerflags
);
82 tcphdr
->wnd
= htons(wnd
);
85 /* let p point to TCP data */
86 pbuf_header(p
, -(s16_t
)sizeof(struct tcp_hdr
));
88 pbuf_take(p
, data
, data_len
);
89 /* let p point to TCP header again */
90 pbuf_header(p
, sizeof(struct tcp_hdr
));
93 /* calculate checksum */
95 tcphdr
->chksum
= inet_chksum_pseudo(p
,
96 IP_PROTO_TCP
, p
->tot_len
, src_ip
, dst_ip
);
98 pbuf_header(p
, sizeof(struct ip_hdr
));
103 /** Create a TCP segment usable for passing to tcp_input */
105 tcp_create_segment(ip_addr_t
* src_ip
, ip_addr_t
* dst_ip
,
106 u16_t src_port
, u16_t dst_port
, void* data
, size_t data_len
,
107 u32_t seqno
, u32_t ackno
, u8_t headerflags
)
109 return tcp_create_segment_wnd(src_ip
, dst_ip
, src_port
, dst_port
, data
,
110 data_len
, seqno
, ackno
, headerflags
, TCP_WND
);
113 /** Create a TCP segment usable for passing to tcp_input
114 * - IP-addresses, ports, seqno and ackno are taken from pcb
115 * - seqno and ackno can be altered with an offset
118 tcp_create_rx_segment(struct tcp_pcb
* pcb
, void* data
, size_t data_len
, u32_t seqno_offset
,
119 u32_t ackno_offset
, u8_t headerflags
)
121 return tcp_create_segment(&pcb
->remote_ip
, &pcb
->local_ip
, pcb
->remote_port
, pcb
->local_port
,
122 data
, data_len
, pcb
->rcv_nxt
+ seqno_offset
, pcb
->lastack
+ ackno_offset
, headerflags
);
125 /** Create a TCP segment usable for passing to tcp_input
126 * - IP-addresses, ports, seqno and ackno are taken from pcb
127 * - seqno and ackno can be altered with an offset
128 * - TCP window can be adjusted
130 struct pbuf
* tcp_create_rx_segment_wnd(struct tcp_pcb
* pcb
, void* data
, size_t data_len
,
131 u32_t seqno_offset
, u32_t ackno_offset
, u8_t headerflags
, u16_t wnd
)
133 return tcp_create_segment_wnd(&pcb
->remote_ip
, &pcb
->local_ip
, pcb
->remote_port
, pcb
->local_port
,
134 data
, data_len
, pcb
->rcv_nxt
+ seqno_offset
, pcb
->lastack
+ ackno_offset
, headerflags
, wnd
);
137 /** Safely bring a tcp_pcb into the requested state */
139 tcp_set_state(struct tcp_pcb
* pcb
, enum tcp_state state
, ip_addr_t
* local_ip
,
140 ip_addr_t
* remote_ip
, u16_t local_port
, u16_t remote_port
)
142 /* @todo: are these all states? */
143 /* @todo: remove from previous list */
145 if (state
== ESTABLISHED
) {
146 TCP_REG(&tcp_active_pcbs
, pcb
);
147 pcb
->local_ip
.addr
= local_ip
->addr
;
148 pcb
->local_port
= local_port
;
149 pcb
->remote_ip
.addr
= remote_ip
->addr
;
150 pcb
->remote_port
= remote_port
;
151 } else if(state
== LISTEN
) {
152 TCP_REG(&tcp_listen_pcbs
.pcbs
, pcb
);
153 pcb
->local_ip
.addr
= local_ip
->addr
;
154 pcb
->local_port
= local_port
;
155 } else if(state
== TIME_WAIT
) {
156 TCP_REG(&tcp_tw_pcbs
, pcb
);
157 pcb
->local_ip
.addr
= local_ip
->addr
;
158 pcb
->local_port
= local_port
;
159 pcb
->remote_ip
.addr
= remote_ip
->addr
;
160 pcb
->remote_port
= remote_port
;
167 test_tcp_counters_err(void* arg
, err_t err
)
169 struct test_tcp_counters
* counters
= arg
;
170 EXPECT_RET(arg
!= NULL
);
171 counters
->err_calls
++;
172 counters
->last_err
= err
;
176 test_tcp_counters_check_rxdata(struct test_tcp_counters
* counters
, struct pbuf
* p
)
180 if(counters
->expected_data
== NULL
) {
181 /* no data to compare */
184 EXPECT_RET(counters
->recved_bytes
+ p
->tot_len
<= counters
->expected_data_len
);
185 received
= counters
->recved_bytes
;
186 for(q
= p
; q
!= NULL
; q
= q
->next
) {
187 char *data
= q
->payload
;
188 for(i
= 0; i
< q
->len
; i
++) {
189 EXPECT_RET(data
[i
] == counters
->expected_data
[received
]);
193 EXPECT(received
== counters
->recved_bytes
+ p
->tot_len
);
197 test_tcp_counters_recv(void* arg
, struct tcp_pcb
* pcb
, struct pbuf
* p
, err_t err
)
199 struct test_tcp_counters
* counters
= arg
;
200 EXPECT_RETX(arg
!= NULL
, ERR_OK
);
201 EXPECT_RETX(pcb
!= NULL
, ERR_OK
);
202 EXPECT_RETX(err
== ERR_OK
, ERR_OK
);
205 if (counters
->close_calls
== 0) {
206 counters
->recv_calls
++;
207 test_tcp_counters_check_rxdata(counters
, p
);
208 counters
->recved_bytes
+= p
->tot_len
;
210 counters
->recv_calls_after_close
++;
211 counters
->recved_bytes_after_close
+= p
->tot_len
;
215 counters
->close_calls
++;
217 EXPECT(counters
->recv_calls_after_close
== 0 && counters
->recved_bytes_after_close
== 0);
221 /** Allocate a pcb and set up the test_tcp_counters_* callbacks */
223 test_tcp_new_counters_pcb(struct test_tcp_counters
* counters
)
225 struct tcp_pcb
* pcb
= tcp_new();
227 /* set up args and callbacks */
228 tcp_arg(pcb
, counters
);
229 tcp_recv(pcb
, test_tcp_counters_recv
);
230 tcp_err(pcb
, test_tcp_counters_err
);
231 pcb
->snd_wnd
= TCP_WND
;
232 pcb
->snd_wnd_max
= TCP_WND
;
237 /** Calls tcp_input() after adjusting current_iphdr_dest */
238 void test_tcp_input(struct pbuf
*p
, struct netif
*inp
)
240 struct ip_hdr
*iphdr
= (struct ip_hdr
*)p
->payload
;
241 /* these lines are a hack, don't use them as an example :-) */
242 ip_addr_copy(*ipX_current_dest_addr(), iphdr
->dest
);
243 ip_addr_copy(*ipX_current_src_addr(), iphdr
->src
);
244 ip_current_netif() = inp
;
245 ip_current_header() = iphdr
;
247 /* since adding IPv6, p->payload must point to tcp header, not ip header */
248 pbuf_header(p
, -(s16_t
)sizeof(struct ip_hdr
));
252 ipX_current_dest_addr()->addr
= 0;
253 ipX_current_src_addr()->addr
= 0;
254 ip_current_netif() = NULL
;
255 ip_current_header() = NULL
;
258 static err_t
test_tcp_netif_output(struct netif
*netif
, struct pbuf
*p
,
261 struct test_tcp_txcounters
*txcounters
= (struct test_tcp_txcounters
*)netif
->state
;
262 LWIP_UNUSED_ARG(ipaddr
);
263 if (txcounters
!= NULL
)
265 txcounters
->num_tx_calls
++;
266 txcounters
->num_tx_bytes
+= p
->tot_len
;
267 if (txcounters
->copy_tx_packets
) {
268 struct pbuf
*p_copy
= pbuf_alloc(PBUF_LINK
, p
->tot_len
, PBUF_RAM
);
270 EXPECT(p_copy
!= NULL
);
271 err
= pbuf_copy(p_copy
, p
);
272 EXPECT(err
== ERR_OK
);
273 if (txcounters
->tx_packets
== NULL
) {
274 txcounters
->tx_packets
= p_copy
;
276 pbuf_cat(txcounters
->tx_packets
, p_copy
);
283 void test_tcp_init_netif(struct netif
*netif
, struct test_tcp_txcounters
*txcounters
,
284 ip_addr_t
*ip_addr
, ip_addr_t
*netmask
)
287 memset(netif
, 0, sizeof(struct netif
));
288 if (txcounters
!= NULL
) {
289 memset(txcounters
, 0, sizeof(struct test_tcp_txcounters
));
290 netif
->state
= txcounters
;
292 netif
->output
= test_tcp_netif_output
;
293 netif
->flags
|= NETIF_FLAG_UP
;
294 ip_addr_copy(netif
->netmask
, *netmask
);
295 ip_addr_copy(netif
->ip_addr
, *ip_addr
);
296 for (n
= netif_list
; n
!= NULL
; n
= n
->next
) {