-
Notifications
You must be signed in to change notification settings - Fork 5
/
tcp.ino
310 lines (270 loc) · 11.8 KB
/
tcp.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#define NEXT_TCP 0x06 /* the next protocol is TCP */
#define TCP_FLAG_SYN 0x02
#define TCP_FLAG_PSH 0x08
#define TCP_FLAG_ACK 0x10
uint8_t tcpHeaderLen;
uint8_t tcpPayloadLen;
uint8_t tcpPayload[TCP_PAYLOAD_LEN];
#define TCP_ACTIVITY_TIMER_START (5*33) /* 5 seconds */
uint16_t tcpActivityTimer;
#define TCP_TRANSMIT_PACKET_LEN 200
uint8_t TcpTransmitPacketLen;
uint8_t TcpTransmitPacket[TCP_TRANSMIT_PACKET_LEN];
#define TCPIP_TRANSMIT_PACKET_LEN 200
uint8_t TcpIpRequestLen;
uint8_t TcpIpRequest[TCPIP_TRANSMIT_PACKET_LEN];
#define TCP_STATE_CLOSED 0
#define TCP_STATE_SYN_SENT 1
#define TCP_STATE_ESTABLISHED 2
uint8_t tcpState = TCP_STATE_CLOSED;
uint32_t TcpSeqNr=200; /* a "random" start sequence number */
uint32_t TcpAckNr;
uint8_t tcp_rxdataLen=0;
uint8_t tcp_rxdata[TCP_RX_DATA_LEN];
void evaluateTcpPacket(void) {
uint8_t flags;
uint32_t remoteSeqNr;
uint32_t remoteAckNr;
uint16_t sourcePort, destinationPort, pLen, hdrLen, tmpPayloadLen;
/* todo: check the IP addresses, checksum etc */
nTcpPacketsReceived++;
pLen = (((uint16_t)myreceivebuffer[18])<<8) + myreceivebuffer[19]; /* length of the IP payload */
hdrLen=(myreceivebuffer[66]>>4) * 4; /* header length in byte */
//log_v("pLen=%d, hdrLen=%d", pLen, hdrLen);
if (pLen>=hdrLen) {
tmpPayloadLen = pLen - hdrLen;
} else {
tmpPayloadLen = 0; /* no TCP payload data */
}
sourcePort = (((uint16_t)myreceivebuffer[54])<<8) + myreceivebuffer[55];
destinationPort = (((uint16_t)myreceivebuffer[56])<<8) + myreceivebuffer[57];
if ((sourcePort != seccTcpPort) || (destinationPort != evccPort)) {
addToTrace("[TCP] wrong port.");
log_v("%d %d %d %d",sourcePort,seccTcpPort,destinationPort, evccPort );
return; /* wrong port */
}
tcpActivityTimer=TCP_ACTIVITY_TIMER_START;
remoteSeqNr =
(((uint32_t)myreceivebuffer[58])<<24) +
(((uint32_t)myreceivebuffer[59])<<16) +
(((uint32_t)myreceivebuffer[60])<<8) +
(((uint32_t)myreceivebuffer[61]));
remoteAckNr =
(((uint32_t)myreceivebuffer[62])<<24) +
(((uint32_t)myreceivebuffer[63])<<16) +
(((uint32_t)myreceivebuffer[64])<<8) +
(((uint32_t)myreceivebuffer[65]));
flags = myreceivebuffer[67];
if (flags == TCP_FLAG_SYN+TCP_FLAG_ACK) { /* This is the connection setup response from the server. */
if (tcpState == TCP_STATE_SYN_SENT) {
TcpSeqNr = remoteAckNr; /* The sequence number of our next transmit packet is given by the received ACK number. */
TcpAckNr = remoteSeqNr+1; /* The ACK number of our next transmit packet is one more than the received seq number. */
tcpState = TCP_STATE_ESTABLISHED;
tcp_sendFirstAck();
connMgr_TcpOk();
addToTrace("[TCP] connected.");
}
return;
}
/* It is no connection setup. We can have the following situations here: */
if (tcpState != TCP_STATE_ESTABLISHED) {
/* received something while the connection is closed. Just ignore it. */
addToTrace("[TCP] ignore, not connected.");
return;
}
/* It can be an ACK, or a data package, or a combination of both. We treat the ACK and the data independent from each other,
to treat each combination. */
//log_v("L=%d", tmpPayloadLen);
if ((tmpPayloadLen>0) && (tmpPayloadLen<TCP_RX_DATA_LEN)) {
/* This is a data transfer packet. */
tcp_rxdataLen = tmpPayloadLen;
/* myreceivebuffer[74] is the first payload byte. */
memcpy(tcp_rxdata, &myreceivebuffer[74], tcp_rxdataLen); /* provide the received data to the application */
connMgr_TcpOk();
TcpAckNr = remoteSeqNr+tcp_rxdataLen; /* The ACK number of our next transmit packet is tcp_rxdataLen more than the received seq number. */
tcp_sendAck();
}
if (flags & TCP_FLAG_ACK) {
nTcpPacketsReceived+=1000;
TcpSeqNr = remoteAckNr; /* The sequence number of our next transmit packet is given by the received ACK number. */
}
}
void tcp_connect(void) {
addToTrace("[TCP] Checkpoint301: connecting");
TcpTransmitPacket[20] = 0x02; /* options: 12 bytes, just copied from the Win10 notebook trace */
TcpTransmitPacket[21] = 0x04;
TcpTransmitPacket[22] = 0x05;
TcpTransmitPacket[23] = 0xA0;
TcpTransmitPacket[24] = 0x01;
TcpTransmitPacket[25] = 0x03;
TcpTransmitPacket[26] = 0x03;
TcpTransmitPacket[27] = 0x08;
TcpTransmitPacket[28] = 0x01;
TcpTransmitPacket[29] = 0x01;
TcpTransmitPacket[30] = 0x04;
TcpTransmitPacket[31] = 0x02;
tcpHeaderLen = 32; /* 20 bytes normal header, plus 12 bytes options */
tcpPayloadLen = 0; /* only the TCP header, no data is in the connect message. */
tcp_prepareTcpHeader(TCP_FLAG_SYN);
tcp_packRequestIntoIp();
tcpState = TCP_STATE_SYN_SENT;
tcpActivityTimer=TCP_ACTIVITY_TIMER_START;
}
void tcp_sendFirstAck(void) {
addToTrace("[TCP] sending first ACK");
tcpHeaderLen = 20; /* 20 bytes normal header, no options */
tcpPayloadLen = 0; /* only the TCP header, no data is in the first ACK message. */
tcp_prepareTcpHeader(TCP_FLAG_ACK);
tcp_packRequestIntoIp();
}
void tcp_sendAck(void) {
//addToTrace("[TCP] sending ACK");
tcpHeaderLen = 20; /* 20 bytes normal header, no options */
tcpPayloadLen = 0; /* only the TCP header, no data is in the first ACK message. */
tcp_prepareTcpHeader(TCP_FLAG_ACK);
tcp_packRequestIntoIp();
}
void tcp_transmit(void) {
//showAsHex(tcpPayload, tcpPayloadLen, "tcp_transmit");
if (tcpState == TCP_STATE_ESTABLISHED) {
//addToTrace("[TCP] sending data");
tcpHeaderLen = 20; /* 20 bytes normal header, no options */
if (tcpPayloadLen+tcpHeaderLen<TCP_TRANSMIT_PACKET_LEN) {
memcpy(&TcpTransmitPacket[tcpHeaderLen], tcpPayload, tcpPayloadLen);
tcp_prepareTcpHeader(TCP_FLAG_PSH + TCP_FLAG_ACK); /* data packets are always sent with flags PUSH and ACK. */
tcp_packRequestIntoIp();
} else {
addToTrace("Error: tcpPayload and header do not fit into TcpTransmitPacket.");
}
}
}
void tcp_testSendData(void) {
if (tcpState == TCP_STATE_ESTABLISHED) {
addToTrace("[TCP] sending data");
tcpHeaderLen = 20; /* 20 bytes normal header, no options */
tcpPayloadLen = 3; /* demo length */
TcpTransmitPacket[tcpHeaderLen] = 0x55; /* demo data */
TcpTransmitPacket[tcpHeaderLen+1] = 0xAA; /* demo data */
TcpTransmitPacket[tcpHeaderLen+2] = 0xBB; /* demo data */
tcp_prepareTcpHeader(TCP_FLAG_PSH + TCP_FLAG_ACK); /* data packets are always sent with flags PUSH and ACK. */
tcp_packRequestIntoIp();
}
}
void tcp_prepareTcpHeader(uint8_t tcpFlag) {
uint8_t i;
uint16_t checksum;
// # TCP header needs at least 24 bytes:
// 2 bytes source port
// 2 bytes destination port
// 4 bytes sequence number
// 4 bytes ack number
// 4 bytes DO/RES/Flags/Windowsize
// 2 bytes checksum
// 2 bytes urgentPointer
// n*4 bytes options/fill (empty for the ACK frame and payload frames)
TcpTransmitPacket[0] = (uint8_t)(evccPort >> 8); /* source port */
TcpTransmitPacket[1] = (uint8_t)(evccPort);
TcpTransmitPacket[2] = (uint8_t)(seccTcpPort >> 8); /* destination port */
TcpTransmitPacket[3] = (uint8_t)(seccTcpPort);
TcpTransmitPacket[4] = (uint8_t)(TcpSeqNr>>24); /* sequence number */
TcpTransmitPacket[5] = (uint8_t)(TcpSeqNr>>16);
TcpTransmitPacket[6] = (uint8_t)(TcpSeqNr>>8);
TcpTransmitPacket[7] = (uint8_t)(TcpSeqNr);
TcpTransmitPacket[8] = (uint8_t)(TcpAckNr>>24); /* ack number */
TcpTransmitPacket[9] = (uint8_t)(TcpAckNr>>16);
TcpTransmitPacket[10] = (uint8_t)(TcpAckNr>>8);
TcpTransmitPacket[11] = (uint8_t)(TcpAckNr);
TcpTransmitPacketLen = tcpHeaderLen + tcpPayloadLen;
TcpTransmitPacket[12] = (tcpHeaderLen/4) << 4; /* High-nibble: DataOffset in 4-byte-steps. Low-nibble: Reserved=0. */
TcpTransmitPacket[13] = tcpFlag;
#define TCP_RECEIVE_WINDOW 1000 /* number of octetts we are able to receive */
TcpTransmitPacket[14] = (uint8_t)(TCP_RECEIVE_WINDOW>>8);
TcpTransmitPacket[15] = (uint8_t)(TCP_RECEIVE_WINDOW);
// checksum will be calculated afterwards
TcpTransmitPacket[16] = 0;
TcpTransmitPacket[17] = 0;
TcpTransmitPacket[18] = 0; /* 16 bit urgentPointer. Always zero in our case. */
TcpTransmitPacket[19] = 0;
checksum = calculateUdpAndTcpChecksumForIPv6(TcpTransmitPacket, TcpTransmitPacketLen, EvccIp, SeccIp, NEXT_TCP);
TcpTransmitPacket[16] = (uint8_t)(checksum >> 8);
TcpTransmitPacket[17] = (uint8_t)(checksum);
}
void tcp_packRequestIntoIp(void) {
// # embeds the TCP into the lower-layer-protocol: IP, Ethernet
uint8_t i;
uint16_t plen;
TcpIpRequestLen = TcpTransmitPacketLen + 8 + 16 + 16; // # IP6 header needs 40 bytes:
// # 4 bytes traffic class, flow
// # 2 bytes destination port
// # 2 bytes length (incl checksum)
// # 2 bytes checksum
TcpIpRequest[0] = 0x60; // # traffic class, flow
TcpIpRequest[1] = 0;
TcpIpRequest[2] = 0;
TcpIpRequest[3] = 0;
plen = TcpTransmitPacketLen; // length of the payload. Without headers.
TcpIpRequest[4] = plen >> 8;
TcpIpRequest[5] = plen & 0xFF;
TcpIpRequest[6] = NEXT_TCP; // next level protocol, 0x06 = TCP in this case
TcpIpRequest[7] = 0x0A; // hop limit
// We are the PEV. So the EvccIp is our own link-local IP address.
//EvccIp = addressManager_getLinkLocalIpv6Address("bytearray");
for (i=0; i<16; i++) {
TcpIpRequest[8+i] = EvccIp[i]; // source IP address
}
for (i=0; i<16; i++) {
TcpIpRequest[24+i] = SeccIp[i]; // destination IP address
}
for (i=0; i<TcpTransmitPacketLen; i++) {
TcpIpRequest[40+i] = TcpTransmitPacket[i];
}
//showAsHex(TcpIpRequest, TcpIpRequestLen, "TcpIpRequest");
tcp_packRequestIntoEthernet();
}
void tcp_packRequestIntoEthernet(void) {
//# packs the IP packet into an ethernet packet
uint8_t i;
mytransmitbufferLen = TcpIpRequestLen + 6 + 6 + 2; // # Ethernet header needs 14 bytes:
// # 6 bytes destination MAC
// # 6 bytes source MAC
// # 2 bytes EtherType
//# fill the destination MAC with the MAC of the charger
fillDestinationMac(evseMac);
fillSourceMac(myMAC); // bytes 6 to 11 are the source MAC
mytransmitbuffer[12] = 0x86; // # 86dd is IPv6
mytransmitbuffer[13] = 0xdd;
for (i=0; i<TcpIpRequestLen; i++) {
mytransmitbuffer[14+i] = TcpIpRequest[i];
}
myEthTransmit();
}
void tcp_Disconnect(void) {
/* we should normally use the FIN handshake, to tell the charger that we closed the connection.
But for the moment, just go away silently, and use an other port for the next connection. The
server will detect our absense sooner or later by timeout, this should be good enough. */
tcpState = TCP_STATE_CLOSED;
/* use a new port */
/* But: This causes multiple open connections and the Win10 messes-up them. */
//evccPort++;
//if (evccPort>65000) evccPort=60000;
}
uint8_t tcp_isClosed(void) {
return (tcpState == TCP_STATE_CLOSED);
}
uint8_t tcp_isConnected(void) {
return (tcpState == TCP_STATE_ESTABLISHED);
}
void tcp_Mainfunction(void) {
if (connMgr_getConnectionLevel()<50) {
/* No SDP done. Means: It does not make sense to start or continue TCP. */
tcpState = TCP_STATE_CLOSED;
return;
}
if ((connMgr_getConnectionLevel()==50) && (tcpState == TCP_STATE_CLOSED)) {
/* SDP is finished, but no TCP connected yet. */
/* use a new port */
evccPort++;
if (evccPort>65000) evccPort=60000;
tcp_connect();
}
}