diff --git a/tools/make_certs.sh b/tools/make_certs.sh new file mode 100755 index 000000000..d521e9218 --- /dev/null +++ b/tools/make_certs.sh @@ -0,0 +1,186 @@ +#!/bin/sh +# +# Copyright (c) 2007, Cameron Rich +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the axTLS project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +AXDIR=`pwd`/`dirname $0` +CWD=`mktemp -d` && cd $dir +cd $CWD + +# +# Generate the certificates and keys for testing. +# + +PROJECT_NAME="axTLS Project" + +# Generate the openssl configuration files. +cat > ca_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Dodgy Certificate Authority +EOF + +cat > certs.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME + CN = 127.0.0.1 +EOF + +cat > device_cert.conf << EOF +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] + O = $PROJECT_NAME Device Certificate +EOF + +# private key generation +openssl genrsa -out axTLS.ca_key.pem 1024 +openssl genrsa -out axTLS.key_512.pem 512 +openssl genrsa -out axTLS.key_1024.pem 1024 +openssl genrsa -out axTLS.key_1042.pem 1042 +openssl genrsa -out axTLS.key_2048.pem 2048 +openssl genrsa -out axTLS.key_4096.pem 4096 +openssl genrsa -out axTLS.device_key.pem 1024 +openssl genrsa -aes128 -passout pass:abcd -out axTLS.key_aes128.pem 512 +openssl genrsa -aes256 -passout pass:abcd -out axTLS.key_aes256.pem 512 + + +# convert private keys into DER format +openssl rsa -in axTLS.key_512.pem -out axTLS.key_512 -outform DER +openssl rsa -in axTLS.key_1024.pem -out axTLS.key_1024 -outform DER +openssl rsa -in axTLS.key_1042.pem -out axTLS.key_1042 -outform DER +openssl rsa -in axTLS.key_2048.pem -out axTLS.key_2048 -outform DER +openssl rsa -in axTLS.key_4096.pem -out axTLS.key_4096 -outform DER +openssl rsa -in axTLS.device_key.pem -out axTLS.device_key -outform DER + +# cert requests +openssl req -out axTLS.ca_x509.req -key axTLS.ca_key.pem -new \ + -config ./ca_cert.conf +openssl req -out axTLS.x509_512.req -key axTLS.key_512.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_1024.req -key axTLS.key_1024.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_1042.req -key axTLS.key_1042.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_2048.req -key axTLS.key_2048.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_4096.req -key axTLS.key_4096.pem -new \ + -config ./certs.conf +openssl req -out axTLS.x509_device.req -key axTLS.device_key.pem -new \ + -config ./device_cert.conf +openssl req -out axTLS.x509_aes128.req -key axTLS.key_aes128.pem \ + -new -config ./certs.conf -passin pass:abcd +openssl req -out axTLS.x509_aes256.req -key axTLS.key_aes256.pem \ + -new -config ./certs.conf -passin pass:abcd + +# generate the actual certs. +openssl x509 -req -in axTLS.ca_x509.req -out axTLS.ca_x509.pem \ + -sha1 -days 5000 -signkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_512.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_1024.req -out axTLS.x509_1024.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_1042.req -out axTLS.x509_1042.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_2048.req -out axTLS.x509_2048.pem \ + -md5 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_4096.req -out axTLS.x509_4096.pem \ + -md5 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_device.req -out axTLS.x509_device.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.x509_512.pem -CAkey axTLS.key_512.pem +openssl x509 -req -in axTLS.x509_aes128.req \ + -out axTLS.x509_aes128.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +openssl x509 -req -in axTLS.x509_aes256.req \ + -out axTLS.x509_aes256.pem \ + -sha1 -CAcreateserial -days 5000 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem + +# note: must be root to do this +DATE_NOW=`date` +if date -s "Jan 1 2025"; then +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_bad_before.pem \ + -sha1 -CAcreateserial -days 365 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem +date -s "$DATE_NOW" +touch axTLS.x509_bad_before.pem +fi +openssl x509 -req -in axTLS.x509_512.req -out axTLS.x509_bad_after.pem \ + -sha1 -CAcreateserial -days -365 \ + -CA axTLS.ca_x509.pem -CAkey axTLS.ca_key.pem + +# some cleanup +rm axTLS*.req +rm axTLS.srl +rm *.conf + +# need this for the client tests +openssl x509 -in axTLS.ca_x509.pem -outform DER -out axTLS.ca_x509.cer +openssl x509 -in axTLS.x509_512.pem -outform DER -out axTLS.x509_512.cer +openssl x509 -in axTLS.x509_1024.pem -outform DER -out axTLS.x509_1024.cer +openssl x509 -in axTLS.x509_1042.pem -outform DER -out axTLS.x509_1042.cer +openssl x509 -in axTLS.x509_2048.pem -outform DER -out axTLS.x509_2048.cer +openssl x509 -in axTLS.x509_4096.pem -outform DER -out axTLS.x509_4096.cer +openssl x509 -in axTLS.x509_device.pem -outform DER -out axTLS.x509_device.cer + +# generate pkcs8 files (use RC4-128 for encryption) +openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted_pem.p8 +openssl pkcs8 -in axTLS.key_512.pem -passout pass:abcd -topk8 -outform DER -v1 PBE-SHA1-RC4-128 -out axTLS.encrypted.p8 +openssl pkcs8 -in axTLS.key_512.pem -nocrypt -topk8 -out axTLS.unencrypted_pem.p8 +openssl pkcs8 -in axTLS.key_512.pem -nocrypt -topk8 -outform DER -out axTLS.unencrypted.p8 + +# generate pkcs12 files (use RC4-128 for encryption) +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -certfile axTLS.ca_x509.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_with_CA" -out axTLS.withCA.p12 -password pass:abcd +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -name "p12_without_CA" -out axTLS.withoutCA.p12 -password pass:abcd +openssl pkcs12 -export -in axTLS.x509_1024.pem -inkey axTLS.key_1024.pem -keypbe PBE-SHA1-RC4-128 -certpbe PBE-SHA1-RC4-128 -out axTLS.noname.p12 -password pass:abcd + +# PEM certificate chain +cat axTLS.ca_x509.pem >> axTLS.x509_device.pem + +# set default key/cert for use in the server +xxd -i axTLS.x509_1024.cer | sed -e \ + "s/axTLS_x509_1024_cer/default_certificate/" > $AXDIR/../ssl/cert.h +xxd -i axTLS.key_1024 | sed -e \ + "s/axTLS_key_1024/default_private_key/" > $AXDIR/../ssl/private_key.h diff --git a/util/README.md b/util/README.md new file mode 100644 index 000000000..6c3b10ba3 --- /dev/null +++ b/util/README.md @@ -0,0 +1,149 @@ +If you are using [LWIP raw tcp mode](http://lwip.wikia.com/wiki/Raw/TCP) and want to add SSL support below are the steps that can help you to achieve this with the help of [axTLS]( http://axtls.sourceforge.net/ ). + +First you have to include the `lwipr_compat.h` header. + +```C +#include "util/lwipr_compat.h" +``` + +Then in the code block where you initialize the tcp raw connection you should call `axl_init`. +Take a look at the example below: + +```C +lwip_init(); + +/* + * The line below should be added AFTER the lwip_init code + * AND BEFORE the call to tcp_new() + * The parameter value 10 specifies how many SSL connections are expected + */ +axl_init(10); + +// .. some more code +tcp = tcp_new(); +tcp_sent(tcp, staticOnSent); +tcp_recv(tcp, staticOnReceive); +tcp_err(tcp, staticOnError); +tcp_poll(tcp, staticOnPoll, 4); +// ... and even more code +res = tcp_connect(tcp, &addr, port, staticOnConnected); + + +``` + +Now we should add in our `staticOnConnected` funciton code to create new ssl context and ssl object. +In the example below the `sslObj` and `sslContext` are defined as global + +```C +// global definitions +SSL *sslObj = NULL; +SSLCTX* sslContext = NULL; + +// and some more code... + +err_t staticOnConnected(void *arg, struct tcp_pcb *tcp, err_t err) +{ + int clientfd = -1; + uint32_t options = 0; + + if (tcp == NULL) { + /* @TODO: Take care to handle error conditions */ + return -1; + } + + clientfd = axl_append(tcp); + if(clientfd == -1) { + printf("Unable to add LWIP tcp -> clientfd mapping\n"); + return ERR_OK; + } + + printf("Connected: ClientId: %d\n", clientfd); +#ifdef SSL_DEBUG + options |= SSL_DISPLAY_STATES | SSL_DISPLAY_BYTES; +#endif + + // if you want to verify the server certificate later you can also add the following option + options |= SSL_SERVER_VERIFY_LATER + + sslContext = ssl_ctx_new(SSL_CONNECT_IN_PARTS | options, 1); // !!! SSL_CONNECT_IN_PARTS must be in the flags !!! + sslObj = ssl_client_new(sslContext, clientfd, NULL, 0); + + return ERR_OK; +} +``` + + +Once we are connected we can send and receive information. For the receiving part we can do the following + +```C +err_t staticOnReceive(void *arg, struct tcp_pcb *tcp, struct pbuf *p, err_t err) +{ + uint8_t *read_buf = NULL; + int read_bytes = 0; + + printf("Err: %d\n", err); + + if(tcp == NULL || p == NULL) { + /* @TODO: Take care to handle error conditions */ + return -1; + } + + read_bytes = axl_ssl_read(sslObj, &read_buf, tcp, p); + if(read_bytes > 0) { + printf("Got data: %s", read_buf); + // @TODO: Do something useful with the read_buf + } + + return ERR_OK; +} +``` + +In the receiving part you can also add debug code to display more information about the SSL handshake, once it was successul. + + +```C +err_t staticOnReceive(void *arg, struct tcp_pcb *tcp, struct pbuf *p, err_t err) +{ + static int show_info = 0; + const char *common_name = NULL; + + // .. + read_bytes = axl_ssl_read(sslObj, &read_buf, tcp, p); + if(read_bytes > 0) { + printf("Got data: %s", read_buf); + // @TODO: Do something useful with the read_buf + } + + if(!show_info && ssl_handshake_status(sslObj) == SSL_OK) { + common_name = ssl_get_cert_dn(sslObj, SSL_X509_CERT_COMMON_NAME); + if (common_name) { + printf("Common Name:\t\t\t%s\n", common_name); + } + + // These two funcitons below can be found in the axtls examples + display_session_id(sslObj); + display_cipher(sslObj); + show_info = 1; + } + + return ERR_OK; +} + +``` + + +And for the sending part we can use the following code sample as a start + +```C +void someSendingfunction() { + uint8_t *out_buf; + int out_bytes = 0; + + // ... take care to store something in the out_buf + + axl_ssl_write(sslObj, out_buf, out_bytes); +} + +``` + +Good luck and send your success stories at slaff@attachix.com. diff --git a/util/lwipr_compat.c b/util/lwipr_compat.c new file mode 100644 index 000000000..6cd75188f --- /dev/null +++ b/util/lwipr_compat.c @@ -0,0 +1,241 @@ +/* + * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP) + * + * Created on: Jan 15, 2016 + * Author: Slavey Karadzhov + */ +#include "lwipr_compat.h" + +#include + +/* High Level "public" functions */ + +/** + * Function that should be called once we are ready to use the axTLS - LWIP raw compatibility + */ +void axl_init(int capacity) { + ax_fd_init(&axlFdArray, capacity); +} + +/** + * Appends a tcp to the internal array. Returns client file descriptor + */ +int axl_append(struct tcp_pcb *tcp) { + return ax_fd_append(&axlFdArray, tcp); +} + +/** + * Reads data from the SSL over TCP stream. Returns decrypted data. + * @param SSL *sslObj + * @param uint8_t **in_data - pointer to the decrypted incoming data, or NULL if nothing was read + * @param void *arg - possible arguments passed to the tcp raw layer during initialization + * @param tcp_pcb *tcp - pointer to the raw tcp object + * @param pbuf *p - pointer to the buffer with the TCP packet data + * + * @return int + * 0 - when everything is fine but there are no symbols to process yet + * < 0 - when there is an error + * > 0 - the length of the clear text characters that were read + */ +int axl_ssl_read(SSL *ssl, uint8_t **in_data, struct tcp_pcb *tcp, struct pbuf *p) { + int read_bytes = 0; + int total_bytes = 0; + int clientfd = -1; + + AxlTcpData* data = NULL; + + if (ssl == NULL) { + return ERR_AXL_INVALID_SSL; + } + + clientfd = ax_fd_getfd(&axlFdArray, tcp); + if(clientfd == -1) { + return ERR_AXL_INVALID_CLIENTFD; + } + + data = ax_fd_get(&axlFdArray, clientfd); + if(data == NULL) { + return ERR_AXL_INVALID_CLIENTFD_DATA; + } + + if (p != NULL) { + data->tcp_pbuf = p; + data->pbuf_offset = 0; + } + + tcp_recved(tcp, p->tot_len); + do { + read_bytes = ssl_read(ssl, in_data); + if(read_bytes < SSL_OK) { + /* An error has occurred. Give it back for further processing */ + total_bytes = read_bytes; + break; + } + total_bytes+= read_bytes; + } while (p->tot_len - data->pbuf_offset > 0); + + pbuf_free(p); + + return total_bytes; +} + +/* + * Lower Level LWIP RAW functions + */ + +/* + * The LWIP tcp raw version of the SOCKET_WRITE(A, B, C) + */ +int ax_port_write(int clientfd, uint8_t *buf, uint16_t bytes_needed) { + AxlTcpData *data = NULL; + int tcp_len = 0; + err_t err = ERR_OK; + + data = ax_fd_get(&axlFdArray, clientfd); + if(data == NULL) { + return ERR_AXL_INVALID_CLIENTFD; + } + + if (data == NULL || data->tcp == NULL || buf == NULL || bytes_needed == 0) { + return 0; + } + + if (tcp_sndbuf(data->tcp) < bytes_needed) { + tcp_len = tcp_sndbuf(data->tcp); + if(tcp_len == 0) { + AXL_DEBUG("The send buffer is full! We have problem.\n"); + return 0; + } + + } else { + tcp_len = bytes_needed; + } + + if (tcp_len > 2 * data->tcp->mss) { + tcp_len = 2 * data->tcp->mss; + } + + do { + err = tcp_write(data->tcp, buf, tcp_len, TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE); + if(err < SSL_OK) { + AXL_DEBUG("Got error: %d\n", err); + } + + if (err == ERR_MEM) { + tcp_len /= 2; + } + } while (err == ERR_MEM && tcp_len > 1); + AXL_DEBUG("send_raw_packet length %d\n", tcp_len); + if (err == ERR_OK) { + err = tcp_output(data->tcp); + if(err != ERR_OK) { + AXL_DEBUG("tcp_output got err: %d\n", err); + } + } + + return tcp_len; +} + +/* + * The LWIP tcp raw version of the SOCKET_READ(A, B, C) + */ +int ax_port_read(int clientfd, uint8_t *buf, int bytes_needed) { + AxlTcpData *data = NULL; + uint8_t *read_buf = NULL; + uint8_t *pread_buf = NULL; + u16_t recv_len = 0; + + data = ax_fd_get(&axlFdArray, clientfd); + if (data == NULL) { + return ERR_AXL_INVALID_CLIENTFD_DATA; + } + + if(data->tcp_pbuf == NULL || data->tcp_pbuf->tot_len == 0) { + AXL_DEBUG("Nothing to read?! May be the connection needs resetting?\n"); + return 0; + } + + read_buf =(uint8_t*)calloc(data->tcp_pbuf->len + 1, sizeof(uint8_t)); + pread_buf = read_buf; + if (pread_buf != NULL){ + recv_len = pbuf_copy_partial(data->tcp_pbuf, read_buf, bytes_needed, data->pbuf_offset); + data->pbuf_offset += recv_len; + } + + if (recv_len != 0) { + memcpy(buf, read_buf, recv_len); + } + + if(bytes_needed < recv_len) { + AXL_DEBUG("Bytes needed: %d, Bytes read: %d\n", bytes_needed, recv_len); + } + + free(pread_buf); + pread_buf = NULL; + + return recv_len; +} + +/* + * Utility functions + */ +void ax_fd_init(AxlTcpDataArray *vector, int capacity) { + vector->size = 0; + vector->capacity = capacity; + vector->data = (AxlTcpData*) malloc(sizeof(AxlTcpData) * vector->capacity); +} + +int ax_fd_append(AxlTcpDataArray *vector, struct tcp_pcb *tcp) { + int index; + + ax_fd_double_capacity_if_full(vector); + index = vector->size++; + vector->data[index].tcp = tcp; + vector->data[index].tcp_pbuf = NULL; + vector->data[index].pbuf_offset = 0; + + return index; +} + +AxlTcpData* ax_fd_get(AxlTcpDataArray *vector, int index) { + if (index >= vector->size || index < 0) { + AXL_DEBUG("Index %d out of bounds for vector of size %d\n", index, + vector->size); + return NULL; + } + return &(vector->data[index]); +} + +int ax_fd_getfd(AxlTcpDataArray *vector, struct tcp_pcb *tcp) { + int i; + for (i = 0; i < vector->size; i++) { + if (vector->data[i].tcp == tcp) { + return i; + } + } + + return -1; +} + +void ax_fd_set(AxlTcpDataArray *vector, int index, struct tcp_pcb *tcp) { + AxlTcpData value; + while (index >= vector->size) { + ax_fd_append(vector, 0); + } + + value.tcp = tcp; + value.tcp_pbuf = NULL; + value.pbuf_offset = 0; + vector->data[index] = value; +} + +void ax_fd_double_capacity_if_full(AxlTcpDataArray *vector) { + if (vector->size >= vector->capacity) { + vector->capacity *= 2; + vector->data = (AxlTcpData*)realloc(vector->data, sizeof(AxlTcpData) * vector->capacity); + } +} + +void ax_fd_free(AxlTcpDataArray *vector) { + free(vector->data); +} diff --git a/util/lwipr_compat.h b/util/lwipr_compat.h new file mode 100644 index 000000000..1e5d73fa2 --- /dev/null +++ b/util/lwipr_compat.h @@ -0,0 +1,82 @@ +/* + * Compatibility for AxTLS with LWIP raw tcp mode (http://lwip.wikia.com/wiki/Raw/TCP) + * + * Created on: Jan 15, 2016 + * Author: Slavey Karadzhov + */ + +#ifndef LWIPR_COMPAT_H +#define LWIPR_COMPAT_H + +#include + +/* + * All those functions will run only if LWIP tcp raw mode is used + */ +#if LWIP_RAW==1 + +#include "lwip/tcp.h" +#include "ssl/ssl.h" +#include "ssl/tls1.h" + +#define ERR_AXL_INVALID_SSL -101 +#define ERR_AXL_INVALID_TCP -102 +#define ERR_AXL_INVALID_CLIENTFD -103 +#define ERR_AXL_INVALID_CLIENTFD_DATA -104 + +#define SOCKET_READ(A, B, C) ax_port_read(A, B, C) +#define SOCKET_WRITE(A, B, C) ax_port_write(A, B, C) + +/* + * Define the AXL_DEBUG function to add debug functionality + */ +#ifndef AXL_DEBUG +#define AXL_DEBUG printf +#endif + +typedef struct { + struct tcp_pcb *tcp; + struct pbuf *tcp_pbuf; + int pbuf_offset; +} AxlTcpData; + + +typedef struct { + int size; /* slots used so far */ + int capacity; /* total available slots */ + AxlTcpData *data; /* array of TcpData objects */ +} AxlTcpDataArray; + +AxlTcpDataArray axlFdArray; + +/* + * High Level Functions - these are the ones that should be used directly + */ + +void axl_init(int capacity); +int axl_append(struct tcp_pcb *tcp); + +#define axl_ssl_write(A, B, C) ssl_write(A, B, C) +int axl_ssl_read(SSL *sslObj, uint8_t **in_data, struct tcp_pcb *tcp, struct pbuf *p); + +/* + * Lower Level Socket Functions - used internally from axTLS + */ + +int ax_port_write(int clientfd, uint8_t *buf, uint16_t bytes_needed); +int ax_port_read(int clientfd, uint8_t *buf, int bytes_needed); + +/* + * Lower Level Utility functions + */ +void ax_fd_init(AxlTcpDataArray *vector, int capacity); +int ax_fd_append(AxlTcpDataArray *vector, struct tcp_pcb *tcp); +AxlTcpData* ax_fd_get(AxlTcpDataArray *vector, int index); +int ax_fd_getfd(AxlTcpDataArray *vector, struct tcp_pcb *tcp); +void ax_fd_set(AxlTcpDataArray *vector, int index, struct tcp_pcb *tcp); +void ax_fd_double_capacity_if_full(AxlTcpDataArray *vector); +void ax_fd_free(AxlTcpDataArray *vector); + +#endif + +#endif /* LWIPR_COMPAT_H */