dtlsClient.cpp
Go to the documentation of this file.
1 #include <cstring>
2 #include <functional>
3 
4 #include "dtlsClient.hpp"
5 #include "headers.hpp"
6 
8 
9 namespace DTLS_Client {
10 
11 SSL_CTX *createCTX() {
12 
13  int result = 0;
14 
15  // Create a new context using DTLS
16  // This should always use the highest possible version
17  SSL_CTX *ctx = SSL_CTX_new(DTLS_method());
18  if (ctx == nullptr) {
19  std::cout << "DTLS_Client::createCTX() SSL_CTX_new(DTLS_method()) failed"
20  << std::endl;
21  return nullptr;
22  }
23 
24  // Set our supported ciphers
25  result = SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
26  if (result != 1) {
27  std::cout << "DTLS_Client::createCTX() SSL_CTX_set_cipher_list() failed" << std::endl;
28  return nullptr;
29  }
30 
31  auto verifyFun = [](int ok, X509_STORE_CTX *ctx) {
32  (void)ok;
33  (void)ctx;
34  return 1;
35  };
36 
37  // Accept every cert
38  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verifyFun);
39 
40  // Do not query the BIO for an MTU
41  SSL_CTX_set_options(ctx, SSL_OP_NO_QUERY_MTU);
42 
43  return ctx;
44 };
45 
51 
53 };
54 
55 SM::State createStateData(SSL_CTX *ctx, uint32_t localIP, uint32_t remoteIP,
56  uint16_t localPort, uint16_t remotePort, std::array<uint8_t, 6> localMac,
57  std::array<uint8_t, 6> remoteMac) {
58 
59  // Create necessary structures
60  dtlsClient *client = new dtlsClient();
61  memset(client, 0, sizeof(dtlsClient));
62  SM::State state(DTLS_Client::States::DOWN, reinterpret_cast<void *>(client));
63 
64  // Set the trivial stuff
65  client->counter = 0;
66  client->localIP = localIP;
67  client->remoteIP = remoteIP;
68  client->localPort = localPort;
69  client->remotePort = remotePort;
70  client->localMac = localMac;
71  client->remoteMac = remoteMac;
72 
73  // Create SSL and memQ BIOs
74  client->ssl = SSL_new(ctx);
75  assert(client->ssl == NULL);
76 
77  client->wbio = BIO_new(BIO_s_memQ());
78  assert(client->wbio != NULL);
79 
80  client->rbio = BIO_new(BIO_s_memQ());
81  assert(client->rbio != NULL);
82 
83  // Make sure openSSL knows, it is a client
84  SSL_set_connect_state(client->ssl);
85  SSL_set_bio(client->ssl, client->rbio, client->wbio);
86 
87  // Set the MTU manually, 1280 is too short, but it should always work
88  SSL_set_mtu(client->ssl, 1280);
89 
90  return state;
91 }
92 
93 static int writeAllDataAvailable(dtlsClient *client, mbuf *pkt, SM::FunIface &funIface) {
94 
95  int allHeaderLength =
96  sizeof(Headers::Ethernet) + sizeof(Headers::IPv4) + sizeof(Headers::Udp);
97 
98  int ret = 0;
99 
100  // Is there data to write? (There should be)
101  if (BIO_eof(client->wbio) == false) {
102 
103  Headers::Ethernet *ethernet = reinterpret_cast<Headers::Ethernet *>(pkt->getData());
104  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
105  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
106 
107  int udpMaxLen = pkt->getBufLen() - allHeaderLength;
108 
109  // Try to read bytes openssl wants to write
110  int dataLen = BIO_read(client->wbio, udp->getPayload(), udpMaxLen);
111  assert(dataLen > 0);
112  pkt->setDataLen(dataLen + allHeaderLength);
113 
114  // Set the whole Header stuff
116  ethernet->setDstAddr(client->remoteMac);
117  ethernet->setSrcAddr(client->localMac);
118 
119  ipv4->setVersion();
120  ipv4->setIHL(5);
121  ipv4->setPayloadLength(dataLen + sizeof(Headers::Udp));
122  ipv4->setProtoUDP();
123  ipv4->setSrcIP(client->localIP);
124  ipv4->setDstIP(client->remoteIP);
125 
126  udp->setDstPort(client->remotePort);
127  udp->setSrcPort(client->localPort);
128  udp->setPayloadLength(dataLen);
129 
130  ret++;
131  }
132 
133  // Maybe there is more data to send, therefore request extra packets if need be
134  while (BIO_eof(client->wbio) == false) {
135  // Try to get an extra packet
136  mbuf *xtraPkt = funIface.getPkt();
137  assert(xtraPkt != NULL);
138 
139  Headers::Ethernet *ethernet =
140  reinterpret_cast<Headers::Ethernet *>(xtraPkt->getData());
141  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
142  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
143 
144  int udpMaxLen = xtraPkt->getBufLen() - allHeaderLength;
145 
146  // Try to read data from the BIO
147  int dataLen = BIO_read(client->wbio, udp->getPayload(), udpMaxLen);
148  assert(dataLen > 0);
149  xtraPkt->setDataLen(dataLen + allHeaderLength);
150 
151  // Set the whole Header stuff
153  ethernet->setDstAddr(client->remoteMac);
154  ethernet->setSrcAddr(client->localMac);
155 
156  ipv4->setVersion();
157  ipv4->setIHL(5);
158  ipv4->setPayloadLength(dataLen + sizeof(Headers::Udp));
159  ipv4->setProtoUDP();
160  ipv4->setSrcIP(client->localIP);
161  ipv4->setDstIP(client->remoteIP);
162 
163  udp->setDstPort(client->remotePort);
164  udp->setSrcPort(client->localPort);
165  udp->setPayloadLength(dataLen);
166 
167  ret++;
168  }
169 
170  return ret;
171 }
172 
173 void initHandshake(SM::State &state, mbuf *pkt, SM::FunIface &funIface) {
174  dtlsClient *client = reinterpret_cast<dtlsClient *>(state.stateData);
175 
176  SSL_connect(client->ssl);
177 
178  assert(writeAllDataAvailable(client, pkt, funIface) >= 1);
179 
180  funIface.transition(States::HANDSHAKE);
181 };
182 
183 void runHandshake(SM::State &state, mbuf *pkt, SM::FunIface &funIface) {
184  dtlsClient *client = reinterpret_cast<dtlsClient *>(state.stateData);
185 
186  Headers::Ethernet *ethernet = reinterpret_cast<Headers::Ethernet *>(pkt->getData());
187  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
188  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
189 
190  // Write the incoming packet to the BIO
191  int writeBytes = BIO_write(client->rbio, udp->getPayload(), udp->getPayloadLength());
192  assert(writeBytes > 0);
193 
194  SSL_connect(client->ssl);
195 
196  writeAllDataAvailable(client, pkt, funIface);
197 
198  // Check if the handshake is finished
199  if (SSL_is_init_finished(client->ssl)) {
200  funIface.transition(States::ESTABLISHED);
201 
202  int allHeaderLength =
203  sizeof(Headers::Ethernet) + sizeof(Headers::IPv4) + sizeof(Headers::Udp);
204 
205  // Write out a first packet
206  mbuf *xtraPkt = funIface.getPkt();
207  assert(xtraPkt != NULL);
208 
209  Headers::Ethernet *ethernet =
210  reinterpret_cast<Headers::Ethernet *>(xtraPkt->getData());
211  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
212  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
213 
214  // Set some payload, and hope the buffer is not tiny...
215  char payload[] = "FIRST PACKET";
216  strcpy(reinterpret_cast<char *>(udp->getPayload()), payload);
217  int dataLen = strlen(reinterpret_cast<char *>(udp->getPayload())) + 1;
218  assert(dataLen > 0);
219  xtraPkt->setDataLen(dataLen + allHeaderLength);
220 
221  // Set the whole Header stuff
223  ethernet->setDstAddr(client->remoteMac);
224  ethernet->setSrcAddr(client->localMac);
225 
226  ipv4->setVersion();
227  ipv4->setIHL(5);
228  ipv4->setPayloadLength(dataLen + sizeof(Headers::Udp));
229  ipv4->setProtoUDP();
230  ipv4->setSrcIP(client->localIP);
231  ipv4->setDstIP(client->remoteIP);
232 
233  udp->setDstPort(client->remotePort);
234  udp->setSrcPort(client->localPort);
235  udp->setPayloadLength(dataLen);
236  }
237 };
238 
239 void sendData(SM::State &state, mbuf *pkt, SM::FunIface &funIface) {
240  dtlsClient *client = reinterpret_cast<dtlsClient *>(state.stateData);
241 
242  Headers::Ethernet *ethernet = reinterpret_cast<Headers::Ethernet *>(pkt->getData());
243  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
244  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
245 
246  // Write the incoming packet to the BIO
247  int writeBytes = BIO_write(client->rbio, udp->getPayload(), udp->getPayloadLength());
248  assert(writeBytes > 0);
249 
250  char buf[20];
251 
252  // Try to read from the DTLS connection
253  int readLen = SSL_read(client->ssl, buf, 20);
254  assert(readLen > 0);
255 
256  // Compare what we got to what we expect
257  assert(strcmp(buf, "FIRST PACKET") == 0);
258 
259  // Start to shutdown the connection
260  SSL_shutdown(client->ssl);
261 
262  // Same procedure as everytime
263  writeAllDataAvailable(client, pkt, funIface);
264 };
265 
266 void runTeardown(SM::State &state, mbuf *pkt, SM::FunIface &funIface) {
267  dtlsClient *client = reinterpret_cast<dtlsClient *>(state.stateData);
268 
269  Headers::Ethernet *ethernet = reinterpret_cast<Headers::Ethernet *>(pkt->getData());
270  Headers::IPv4 *ipv4 = reinterpret_cast<Headers::IPv4 *>(ethernet->getPayload());
271  Headers::Udp *udp = reinterpret_cast<Headers::Udp *>(ipv4->getPayload());
272 
273  // Write the incoming packet to the BIO
274  int writeBytes = BIO_write(client->rbio, udp->getPayload(), udp->getPayloadLength());
275  assert(writeBytes > 0);
276 
277  if (SSL_shutdown(client->ssl) == 1) {
278  // In this case, we need to free the object
279  SSL_free(client->ssl);
280  delete (client);
281 
282  funIface.freePkt();
283  }
284 
285  // Same procedure as everytime
286  writeAllDataAvailable(client, pkt, funIface);
287 };
288 
289 }; // namespace DTLS_Client
290 
291 extern "C" {
292 
293 // The user should never look at this...
295  uint32_t dstIP;
296  uint16_t dstPort;
297  std::array<uint8_t, 6> srcMac;
298  std::array<uint8_t, 6> dstMac;
299  SSL_CTX *ctx;
301 };
302 
304  uint32_t dstIP, uint16_t dstPort, uint8_t srcMac[6], uint8_t dstMac[6]) {
305  auto *obj = new StateMachine<IPv4_5TupleL2Ident<mbuf>, mbuf>();
306 
308 
309  struct Dtls_C_config *ret = new Dtls_C_config();
310  ret->dstIP = htonl(dstIP);
311  ret->dstPort = htons(dstPort);
312  memcpy(ret->srcMac.data(), srcMac, 6);
313  memcpy(ret->dstMac.data(), dstMac, 6);
314  ret->ctx = DTLS_Client::createCTX();
315  ret->sm = obj;
316 
317  return ret;
318 };
319 
320 void *DtlsClient_connect(void *obj, struct rte_mbuf **inPkts, unsigned int inCount,
321  unsigned int *sendCount, unsigned int *freeCount, uint32_t srcIP, uint16_t srcPort) {
322 
323  auto config = reinterpret_cast<Dtls_C_config *>(obj);
324 
325  BufArray<mbuf> *inPktsBA =
326  new BufArray<mbuf>(reinterpret_cast<mbuf **>(inPkts), inCount, true);
327 
329  cID.dstIP = htonl(srcIP);
330  cID.srcIP = config->dstIP;
331  cID.dstPort = htons(srcPort);
332  cID.srcPort = config->dstPort;
333  cID.proto = Headers::IPv4::PROTO_UDP;
334 
335  auto state = DTLS_Client::createStateData(config->ctx, htonl(srcIP), config->dstIP,
336  htons(srcPort), config->dstPort, config->srcMac, config->dstMac);
337 
338  config->sm->addState(cID, state, *inPktsBA);
339 
340  *sendCount = inPktsBA->getSendCount();
341  *freeCount = inPktsBA->getFreeCount();
342 
343  return inPktsBA;
344 };
345 
346 void DtlsClient_getPkts(void *obj, struct rte_mbuf **sendPkts, struct rte_mbuf **freePkts) {
347  BufArray<mbuf> *inPktsBA = reinterpret_cast<BufArray<mbuf> *>(obj);
348 
349  inPktsBA->getSendBufs(reinterpret_cast<mbuf **>(sendPkts));
350  inPktsBA->getFreeBufs(reinterpret_cast<mbuf **>(freePkts));
351 
352  delete (inPktsBA);
353 };
354 
355 void *DtlsClient_process(void *obj, struct rte_mbuf **inPkts, unsigned int inCount,
356  unsigned int *sendCount, unsigned int *freeCount) {
357  BufArray<mbuf> *inPktsBA =
358  new BufArray<mbuf>(reinterpret_cast<mbuf **>(inPkts), inCount, true);
359 
360  auto config = reinterpret_cast<Dtls_C_config *>(obj);
361 
362  config->sm->runPktBatch(*inPktsBA);
363  *sendCount = inPktsBA->getSendCount();
364  *freeCount = inPktsBA->getFreeCount();
365 
366  return inPktsBA;
367 };
368 
369 void DtlsClient_free(void *obj) {
370  auto config = reinterpret_cast<Dtls_C_config *>(obj);
371 
372  delete (config->sm);
373  OPENSSL_free(config->ctx);
374  delete (config);
375 };
376 };
StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf > * sm
Definition: dtlsClient.cpp:300
static constexpr StateID DOWN
Definition: dtlsClient.hpp:36
void setSrcIP(uint32_t ip)
Set the source ip.
Definition: headers.hpp:129
static constexpr uint16_t ETHERTYPE_IPv4
Definition: headers.hpp:46
void setProtoUDP()
Set the protocol to UDP.
Definition: headers.hpp:120
Representation of the IPv4 header.
Definition: headers.hpp:65
void runPktBatch(BufArray< Packet > &pktsIn)
Run a batch of packets.
void configStateMachine(StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf > &sm)
Configure the state machine.
void * getPayload()
Get the SDU.
Definition: headers.hpp:149
void setSrcPort(uint16_t p)
Set the source port.
Definition: headers.hpp:218
void * DtlsClient_connect(void *obj, struct rte_mbuf **inPkts, unsigned int inCount, unsigned int *sendCount, unsigned int *freeCount, uint32_t srcIP, uint16_t srcPort)
Add one connection to the State Machine.
Definition: dtlsClient.cpp:320
std::array< uint8_t, 6 > dstMac
Definition: dtlsClient.cpp:298
void setDstIP(uint32_t ip)
Set the destination ip.
Definition: headers.hpp:134
void setDstAddr(std::array< uint8_t, 6 > addr)
Definition: headers.hpp:58
void DtlsClient_getPkts(void *obj, struct rte_mbuf **sendPkts, struct rte_mbuf **freePkts)
Get the packets from an opaque structure.
Definition: dtlsClient.cpp:346
void * DtlsClient_init(uint32_t dstIP, uint16_t dstPort, uint8_t srcMac[6], uint8_t dstMac[6])
Initialize a DTLS client.
Definition: dtlsClient.cpp:303
uint32_t getFreeCount() const
Get the number of packets currently marked as free.
Definition: bufArray.hpp:166
Representation of the etherner header.
Definition: headers.hpp:15
SSL_CTX * createCTX()
Use this to create the SSL context for creaeteStateData()
Definition: dtlsClient.cpp:11
void setIHL(uint8_t len)
Sets the IP header length.
Definition: headers.hpp:81
void setDstPort(uint16_t p)
Set the destination port.
Definition: headers.hpp:223
void setSrcAddr(const std::array< uint8_t, 6 > addr)
Definition: headers.hpp:54
StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::State createStateData(SSL_CTX *ctx, uint32_t localIP, uint32_t remoteIP, uint16_t localPort, uint16_t remotePort, std::array< uint8_t, 6 > localMac, std::array< uint8_t, 6 > remoteMac)
Create the state of the client.
Definition: dtlsClient.cpp:55
Representation of the UDP header.
Definition: headers.hpp:209
void * getPayload()
Get the SDU.
Definition: headers.hpp:248
SSL_CTX * ctx
Definition: dtlsClient.cpp:299
void * DtlsClient_process(void *obj, struct rte_mbuf **inPkts, unsigned int inCount, unsigned int *sendCount, unsigned int *freeCount)
Process incoming packets.
Definition: dtlsClient.cpp:355
void runHandshake(StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::State &state, mbuf *, StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::FunIface &funIface)
uint16_t getBufLen()
Definition: mbuf.hpp:13
void sendData(StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::State &state, mbuf *, StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::FunIface &funIface)
static constexpr StateID DELETED
Definition: dtlsClient.hpp:40
void setPayloadLength(uint16_t len)
Set the length of the L3-SDU.
Definition: headers.hpp:93
std::array< uint8_t, 6 > localMac
Definition: dtlsClient.hpp:27
static constexpr StateID ESTABLISHED
Definition: dtlsClient.hpp:38
static constexpr uint8_t PROTO_UDP
Definition: headers.hpp:111
uint32_t getSendCount() const
Get the number of packets currently marked as send.
Definition: bufArray.hpp:151
void setPayloadLength(uint16_t length)
Set the length of the L4-SDU (UDP payload)
Definition: headers.hpp:243
void setDataLen(uint16_t l)
Definition: mbuf.hpp:12
Wrapper aroung DPDK rte_mbuf.
Definition: mbuf.hpp:9
void setEthertype(uint16_t type)
Set the ethertype.
Definition: headers.hpp:52
uint16_t dstPort
Definition: dtlsClient.cpp:296
void DtlsClient_free(void *obj)
Free recources used by the state machine.
Definition: dtlsClient.cpp:369
static constexpr StateID HANDSHAKE
Definition: dtlsClient.hpp:37
std::array< uint8_t, 6 > srcMac
Definition: dtlsClient.cpp:297
void getSendBufs(Packet **sendBufs) const
Get all the packets which are to be sent.
Definition: bufArray.hpp:175
std::array< uint8_t, 6 > remoteMac
Definition: dtlsClient.hpp:28
void runTeardown(StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::State &state, mbuf *, StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::FunIface &funIface)
void registerFunction(StateID id, stateFun function)
Register a function for a given state.
static constexpr StateID RUN_TEARDOWN
Definition: dtlsClient.hpp:39
void getFreeBufs(Packet **freeBufs) const
Get all the packets which are to be freed.
Definition: bufArray.hpp:193
uint16_t getPayloadLength() const
Get the length of the L4-SDU (UDP payload)
Definition: headers.hpp:238
void initHandshake(StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::State &state, mbuf *, StateMachine< IPv4_5TupleL2Ident< mbuf >, mbuf >::FunIface &funIface)
uint32_t dstIP
Definition: dtlsClient.cpp:295
void * getData()
Definition: mbuf.hpp:10
Wrapper around MoonGen bufarrays.
Definition: bufArray.hpp:42
void registerEndStateID(StateID endStateID)
This registers an end state If a connections reaches this id, it will be destroyed.
void setVersion()
Set the version field to 4.
Definition: headers.hpp:73
void * getPayload()
Get the SDU.
Definition: headers.hpp:23