Message ID | 1431344907-30359-1-git-send-email-alexandru.badicioiu@linaro.org |
---|---|
State | Accepted |
Commit | 672527913097507903ec9286fa8330f09c68340c |
Headers | show |
Hello Robbie, can you please review that patch and next 2/2 patch. Thanks, Maxim. On 05/11/2015 14:48, alexandru.badicioiu@linaro.org wrote: > From: Alexandru Badicioiu <alexandru.badicioiu@linaro.org> > > Tunnel mode is enabled from the command line using -t argument with > the following format: SrcIP:DstIP:TunnelSrcIP:TunnelDstIP. > SrcIP - cleartext packet source IP > DstIP - cleartext packet destination IP > TunnelSrcIP - tunnel source IP > TunnelDstIP - tunnel destination IP > > The outbound packets matching SrcIP:DstIP will be encapsulated > in a TunnelSrcIP:TunnelDstIP IPSec tunnel (AH/ESP/AH+ESP) > if a matching outbound SA is determined (as for transport mode). > For inbound packets each entry in the IPSec cache is matched > for the cleartext addresses, as in the transport mode (SrcIP:DstIP) > and then for the tunnel addresses (TunnelSrcIP:TunnelDstIP) > in case cleartext addresses didn't match. After authentication and > decryption tunneled packets are verified against the tunnel entry > (packets came in from the expected tunnel). > > Signed-off-by: Alexandru Badicioiu <alexandru.badicioiu@linaro.org> > --- > example/ipsec/odp_ipsec.c | 105 +++++++++++++++++++++++++++--- > example/ipsec/odp_ipsec_cache.c | 31 +++++++++- > example/ipsec/odp_ipsec_cache.h | 5 ++ > example/ipsec/odp_ipsec_sa_db.c | 133 +++++++++++++++++++++++++++++++++++++- > example/ipsec/odp_ipsec_sa_db.h | 57 ++++++++++++++++ > example/ipsec/odp_ipsec_stream.c | 101 ++++++++++++++++++++++++---- > 6 files changed, 402 insertions(+), 30 deletions(-) > > diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c > index cb8f535..e325347 100644 > --- a/example/ipsec/odp_ipsec.c > +++ b/example/ipsec/odp_ipsec.c > @@ -135,13 +135,20 @@ typedef struct { > uint8_t ip_ttl; /**< Saved IP TTL value */ > int hdr_len; /**< Length of IPsec headers */ > int trl_len; /**< Length of IPsec trailers */ > + uint16_t tun_hdr_offset; /**< Offset of tunnel header from > + buffer start */ > uint16_t ah_offset; /**< Offset of AH header from buffer start */ > uint16_t esp_offset; /**< Offset of ESP header from buffer start */ > > + /* Input only */ > + uint32_t src_ip; /**< SA source IP address */ > + uint32_t dst_ip; /**< SA dest IP address */ > + > /* Output only */ > odp_crypto_op_params_t params; /**< Parameters for crypto call */ > uint32_t *ah_seq; /**< AH sequence number location */ > uint32_t *esp_seq; /**< ESP sequence number location */ > + uint16_t *tun_hdr_id; /**< Tunnel header ID > */ > } ipsec_ctx_t; > > /** > @@ -357,6 +364,7 @@ void ipsec_init_pre(void) > /* Initialize our data bases */ > init_sp_db(); > init_sa_db(); > + init_tun_db(); > init_ipsec_cache(); > } > > @@ -376,19 +384,27 @@ void ipsec_init_post(crypto_api_mode_e api_mode) > for (entry = sp_db->list; NULL != entry; entry = entry->next) { > sa_db_entry_t *cipher_sa = NULL; > sa_db_entry_t *auth_sa = NULL; > + tun_db_entry_t *tun; > > - if (entry->esp) > + if (entry->esp) { > cipher_sa = find_sa_db_entry(&entry->src_subnet, > &entry->dst_subnet, > 1); > - if (entry->ah) > + tun = find_tun_db_entry(cipher_sa->src_ip, > + cipher_sa->dst_ip); > + } > + if (entry->ah) { > auth_sa = find_sa_db_entry(&entry->src_subnet, > &entry->dst_subnet, > 0); > + tun = find_tun_db_entry(auth_sa->src_ip, > + auth_sa->dst_ip); > + } > > if (cipher_sa || auth_sa) { > if (create_ipsec_cache_entry(cipher_sa, > auth_sa, > + tun, > api_mode, > entry->input, > completionq, > @@ -661,6 +677,8 @@ pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt, > ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; > ctx->ipsec.hdr_len = hdr_len; > ctx->ipsec.trl_len = 0; > + ctx->ipsec.src_ip = entry->src_ip; > + ctx->ipsec.dst_ip = entry->dst_ip; > > /*If authenticating, zero the mutable fields build the request */ > if (ah) { > @@ -741,6 +759,23 @@ pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt, > trl_len += esp_t->pad_len + sizeof(*esp_t); > } > > + /* We have a tunneled IPv4 packet */ > + if (ip->proto == ODPH_IPV4) { > + odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len); > + odp_packet_pull_tail(pkt, trl_len); > + odph_ethhdr_t *eth; > + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); > + eth->type = ODPH_ETHTYPE_IPV4; > + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); > + > + /* Check inbound policy */ > + if ((ip->src_addr != ctx->ipsec.src_ip || > + ip->dst_addr != ctx->ipsec.dst_ip)) > + return PKT_DROP; > + > + return PKT_CONTINUE; > + } > + > /* Finalize the IPv4 header */ > ipv4_adjust_len(ip, -(hdr_len + trl_len)); > ip->ttl = ctx->ipsec.ip_ttl; > @@ -812,9 +847,13 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, > params.pkt = pkt; > params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > + hdr_len += sizeof(odph_ipv4hdr_t); > + ip_data = (uint8_t *)ip; > + } > /* Compute ah and esp, determine length of headers, move the data */ > if (entry->ah.alg) { > - ah = (odph_ahhdr_t *)(ip_data); > + ah = (odph_ahhdr_t *)(ip_data + hdr_len); > hdr_len += sizeof(odph_ahhdr_t); > hdr_len += entry->ah.icv_len; > } > @@ -826,21 +865,39 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, > memmove(ip_data + hdr_len, ip_data, ip_data_len); > ip_data += hdr_len; > > + /* update outer header in tunnel mode */ > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > + /* tunnel addresses */ > + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); > + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); > + } > + > /* For cipher, compute encrypt length, build headers and request */ > if (esp) { > uint32_t encrypt_len; > odph_esptrl_t *esp_t; > > - encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t), > - entry->esp.block_len); > - trl_len = encrypt_len - ip_data_len; > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > + encrypt_len = ESP_ENCODE_LEN(ip->tot_len + > + sizeof(*esp_t), > + entry->esp.block_len); > + trl_len = encrypt_len - ip->tot_len; > + } else { > + encrypt_len = ESP_ENCODE_LEN(ip_data_len + > + sizeof(*esp_t), > + entry->esp.block_len); > + trl_len = encrypt_len - ip_data_len; > + } > > esp->spi = odp_cpu_to_be_32(entry->esp.spi); > memcpy(esp + 1, entry->state.iv, entry->esp.iv_len); > > esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1; > esp_t->pad_len = trl_len - sizeof(*esp_t); > - esp_t->next_header = ip->proto; > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) > + esp_t->next_header = ODPH_IPV4; > + else > + esp_t->next_header = ip->proto; > ip->proto = ODPH_IPPROTO_ESP; > > params.cipher_range.offset = ip_data - buf; > @@ -852,7 +909,10 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, > memset(ah, 0, sizeof(*ah) + entry->ah.icv_len); > ah->spi = odp_cpu_to_be_32(entry->ah.spi); > ah->ah_len = 1 + (entry->ah.icv_len / 4); > - ah->next_header = ip->proto; > + if (entry->mode == IPSEC_SA_MODE_TUNNEL && !esp) > + ah->next_header = ODPH_IPV4; > + else > + ah->next_header = ip->proto; > ip->proto = ODPH_IPPROTO_AH; > > ip->chksum = 0; > @@ -875,8 +935,11 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, > ctx->ipsec.trl_len = trl_len; > ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0; > ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; > + ctx->ipsec.tun_hdr_offset = (entry->mode == IPSEC_SA_MODE_TUNNEL) ? > + ((uint8_t *)ip - buf) : 0; > ctx->ipsec.ah_seq = &entry->state.ah_seq; > ctx->ipsec.esp_seq = &entry->state.esp_seq; > + ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id; > memcpy(&ctx->ipsec.params, ¶ms, sizeof(params)); > > *skip = FALSE; > @@ -915,6 +978,20 @@ pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt, > esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf); > esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++); > } > + if (ctx->ipsec.tun_hdr_offset) { > + odph_ipv4hdr_t *ip; > + int ret; > + ip = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf); > + ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++); > + if (!ip->id) { > + /* re-init tunnel hdr id */ > + ret = odp_random_data((uint8_t *)ctx->ipsec.tun_hdr_id, > + sizeof(*ctx->ipsec.tun_hdr_id), > + 1); > + if (ret != sizeof(*ctx->ipsec.tun_hdr_id)) > + abort(); > + } > + } > > /* Issue crypto request */ > if (odp_crypto_operation(&ctx->ipsec.params, > @@ -1306,8 +1383,9 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) > {"mode", required_argument, NULL, 'm'}, /* return 'm' */ > {"route", required_argument, NULL, 'r'}, /* return 'r' */ > {"policy", required_argument, NULL, 'p'}, /* return 'p' */ > - {"ah", required_argument, NULL, 'a'}, /* return 'a' */ > - {"esp", required_argument, NULL, 'e'}, /* return 'e' */ > + {"ah", required_argument, NULL, 'a'}, /* return 'a' */ > + {"esp", required_argument, NULL, 'e'}, /* return 'e' */ > + {"tunnel", required_argument, NULL, 't'}, /* return 't' */ > {"stream", required_argument, NULL, 's'}, /* return 's' */ > {"help", no_argument, NULL, 'h'}, /* return 'h' */ > {NULL, 0, NULL, 0} > @@ -1318,7 +1396,7 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) > appl_args->mode = 0; /* turn off async crypto API by default */ > > while (!rc) { > - opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:s:", > + opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:t:s:", > longopts, &long_index); > > if (-1 == opt) > @@ -1389,6 +1467,10 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) > rc = create_sa_db_entry(optarg, TRUE); > break; > > + case 't': > + rc = create_tun_db_entry(optarg); > + break; > + > case 's': > rc = create_stream_db_entry(optarg); > break; > @@ -1449,6 +1531,7 @@ static void print_info(char *progname, appl_args_t *appl_args) > dump_fwd_db(); > dump_sp_db(); > dump_sa_db(); > + dump_tun_db(); > printf("\n\n"); > fflush(NULL); > } > diff --git a/example/ipsec/odp_ipsec_cache.c b/example/ipsec/odp_ipsec_cache.c > index 12b960d..046e43c 100644 > --- a/example/ipsec/odp_ipsec_cache.c > +++ b/example/ipsec/odp_ipsec_cache.c > @@ -38,6 +38,7 @@ void init_ipsec_cache(void) > > int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > sa_db_entry_t *auth_sa, > + tun_db_entry_t *tun, > crypto_api_mode_e api_mode, > odp_bool_t in, > odp_queue_t completionq, > @@ -47,12 +48,18 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > ipsec_cache_entry_t *entry; > enum odp_crypto_ses_create_err ses_create_rc; > odp_crypto_session_t session; > + sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT; > > /* Verify we have a good entry */ > entry = &ipsec_cache->array[ipsec_cache->index]; > if (MAX_DB <= ipsec_cache->index) > return -1; > > + /* Verify SA mode match in case of cipher&auth */ > + if (cipher_sa && auth_sa && > + (cipher_sa->mode != auth_sa->mode)) > + return -1; > + > /* Setup parameters and call crypto library to create session */ > params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE; > params.auth_cipher_text = TRUE; > @@ -79,6 +86,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > params.cipher_key.length = cipher_sa->key.length; > params.iv.data = entry->state.iv; > params.iv.length = cipher_sa->iv_len; > + mode = cipher_sa->mode; > } else { > params.cipher_alg = ODP_CIPHER_ALG_NULL; > params.iv.data = NULL; > @@ -90,6 +98,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > params.auth_alg = auth_sa->alg.u.auth; > params.auth_key.data = auth_sa->key.data; > params.auth_key.length = auth_sa->key.length; > + mode = auth_sa->mode; > } else { > params.auth_alg = ODP_AUTH_ALG_NULL; > } > @@ -128,6 +137,24 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t)); > } > > + if (tun) { > + entry->tun_src_ip = tun->tun_src_ip; > + entry->tun_dst_ip = tun->tun_dst_ip; > + mode = IPSEC_SA_MODE_TUNNEL; > + > + int ret; > + if (!in) { > + /* init tun hdr id */ > + ret = odp_random_data((uint8_t *) > + &entry->state.tun_hdr_id, > + sizeof(entry->state.tun_hdr_id), > + 1); > + if (ret != sizeof(entry->state.tun_hdr_id)) > + return -1; > + } > + } > + entry->mode = mode; > + > /* Initialize state */ > entry->state.esp_seq = 0; > entry->state.ah_seq = 0; > @@ -156,7 +183,9 @@ ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip, > /* Look for a hit */ > for (; NULL != entry; entry = entry->next) { > if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip)) > - continue; > + if ((entry->tun_src_ip != src_ip) || > + (entry->tun_dst_ip != dst_ip)) > + continue; > if (ah && > ((!entry->ah.alg) || > (entry->ah.spi != odp_be_to_cpu_32(ah->spi)))) > diff --git a/example/ipsec/odp_ipsec_cache.h b/example/ipsec/odp_ipsec_cache.h > index 714cae8..6b1be5f 100644 > --- a/example/ipsec/odp_ipsec_cache.h > +++ b/example/ipsec/odp_ipsec_cache.h > @@ -34,6 +34,9 @@ typedef struct ipsec_cache_entry_s { > odp_bool_t in_place; /**< Crypto API mode */ > uint32_t src_ip; /**< Source v4 address */ > uint32_t dst_ip; /**< Destination v4 address */ > + sa_mode_t mode; /**< SA mode - transport/tun */ > + uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */ > + uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */ > struct { > enum odp_cipher_alg alg; /**< Cipher algorithm */ > uint32_t spi; /**< Cipher SPI */ > @@ -54,6 +57,7 @@ typedef struct ipsec_cache_entry_s { > uint32_t esp_seq; /**< ESP TX sequence number */ > uint32_t ah_seq; /**< AH TX sequence number */ > uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */ > + uint16be_t tun_hdr_id; /**< Tunnel header IP ID */ > } state; > } ipsec_cache_entry_t; > > @@ -87,6 +91,7 @@ void init_ipsec_cache(void); > */ > int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > sa_db_entry_t *auth_sa, > + tun_db_entry_t *tun, > crypto_api_mode_e api_mode, > odp_bool_t in, > odp_queue_t completionq, > diff --git a/example/ipsec/odp_ipsec_sa_db.c b/example/ipsec/odp_ipsec_sa_db.c > index 5837cb6..78f43da 100644 > --- a/example/ipsec/odp_ipsec_sa_db.c > +++ b/example/ipsec/odp_ipsec_sa_db.c > @@ -1,7 +1,7 @@ > /* Copyright (c) 2014, Linaro Limited > * All rights reserved. > * > - * SPDX-License-Identifier: BSD-3-Clause > + * SPDX-License-Identifier: BSD-3-Clause > */ > > /* enable strtok */ > @@ -19,6 +19,9 @@ > /** Global pointer to sa db */ > static sa_db_t *sa_db; > > +/** Global pointer to tun db */ > +static tun_db_t *tun_db; > + > void init_sa_db(void) > { > odp_shm_t shm; > @@ -37,6 +40,22 @@ void init_sa_db(void) > memset(sa_db, 0, sizeof(*sa_db)); > } > > +void init_tun_db(void) > +{ > + odp_shm_t shm; > + shm = odp_shm_reserve("shm_tun_db", > + sizeof(tun_db_t), > + ODP_CACHE_LINE_SIZE, > + 0); > + tun_db = odp_shm_addr(shm); > + > + if (tun_db == NULL) { > + EXAMPLE_ERR("Error: shared mem alloc failed.\n"); > + exit(EXIT_FAILURE); > + } > + memset(tun_db, 0, sizeof(*tun_db)); > +} > + > int create_sa_db_entry(char *input, odp_bool_t cipher) > { > int pos = 0; > @@ -81,7 +100,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) > entry->alg.u.cipher = > ODP_CIPHER_ALG_3DES_CBC; > entry->block_len = 8; > - entry->iv_len = 8; > + entry->iv_len = 8; > } else { > entry->alg.u.cipher = > ODP_CIPHER_ALG_NULL; > @@ -90,7 +109,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) > if (0 == strcmp(token, "md5")) { > entry->alg.u.auth = > ODP_AUTH_ALG_MD5_96; > - entry->icv_len = 12; > + entry->icv_len = 12; > } else { > entry->alg.u.auth = ODP_AUTH_ALG_NULL; > } > @@ -132,6 +151,89 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) > return 0; > } > > +int create_tun_db_entry(char *input) > +{ > + int pos = 0; > + char *local; > + char *str; > + char *save; > + char *token; > + tun_db_entry_t *entry = &tun_db->array[tun_db->index]; > + > + /* Verify we have a good entry */ > + if (MAX_DB <= tun_db->index) > + return -1; > + > + /* Make a local copy */ > + local = malloc(strlen(input) + 1); > + if (NULL == local) > + return -1; > + strcpy(local, input); > + > + /* Setup for using "strtok_r" to search input string */ > + str = local; > + save = NULL; > + > + /* Parse tokens separated by ':' */ > + while (NULL != (token = strtok_r(str, ":", &save))) { > + str = NULL; /* reset str for subsequent strtok_r calls */ > + > + /* Parse token based on its position */ > + switch (pos) { > + case 0: > + parse_ipv4_string(token, &entry->src_ip, NULL); > + break; > + case 1: > + parse_ipv4_string(token, &entry->dst_ip, NULL); > + break; > + case 2: > + parse_ipv4_string(token, &entry->tun_src_ip, NULL); > + break; > + case 3: > + parse_ipv4_string(token, &entry->tun_dst_ip, NULL); > + break; > + default: > + printf("ERROR: extra token \"%s\" at position %d\n", > + token, pos); > + break; > + } > + pos++; > + } > + > + /* Verify we parsed exactly the number of tokens we expected */ > + if (4 != pos) { > + printf("ERROR: \"%s\" contains %d tokens, expected 4\n", > + input, > + pos); > + free(local); > + return -1; > + } > + > + /* Add route to the list */ > + tun_db->index++; > + entry->next = tun_db->list; > + tun_db->list = entry; > + > + free(local); > + return 0; > +} > + > +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src, > + uint32_t ip_dst) > +{ > + tun_db_entry_t *entry = NULL; > + > + /* Scan all entries and return first match */ > + for (entry = tun_db->list; NULL != entry; entry = entry->next) { > + if (entry->src_ip != ip_src) > + continue; > + if (entry->dst_ip != ip_dst) > + continue; > + break; > + } > + return entry; > +} > + > void dump_sa_db(void) > { > sa_db_entry_t *entry; > @@ -182,3 +284,28 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src, > } > return entry; > } > + > +void dump_tun_db(void) > +{ > + tun_db_entry_t *entry; > + > + printf("\n" > + "Tunnel table\n" > + "--------------------------\n"); > + > + for (entry = tun_db->list; NULL != entry; entry = entry->next) { > + char src_ip_str[MAX_STRING]; > + char dst_ip_str[MAX_STRING]; > + char tun_src_ip_str[MAX_STRING]; > + char tun_dst_ip_str[MAX_STRING]; > + > + printf(" %s:%s %s:%s ", > + ipv4_addr_str(src_ip_str, entry->src_ip), > + ipv4_addr_str(dst_ip_str, entry->dst_ip), > + ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip), > + ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip) > + ); > + > + printf("\n"); > + } > +} > diff --git a/example/ipsec/odp_ipsec_sa_db.h b/example/ipsec/odp_ipsec_sa_db.h > index c30cbdb..79bfc78 100644 > --- a/example/ipsec/odp_ipsec_sa_db.h > +++ b/example/ipsec/odp_ipsec_sa_db.h > @@ -13,6 +13,10 @@ extern "C" { > > #include <odp_ipsec_misc.h> > > +typedef enum sa_mode_s { > + IPSEC_SA_MODE_TRANSPORT, > + IPSEC_SA_MODE_TUNNEL > +} sa_mode_t; > /** > * Security Assocation (SA) data base entry > */ > @@ -26,6 +30,7 @@ typedef struct sa_db_entry_s { > uint32_t block_len; /**< Cipher block length */ > uint32_t iv_len; /**< Initialization Vector length */ > uint32_t icv_len; /**< Integrity Check Value length */ > + sa_mode_t mode; /**< SA mode - transport/tun */ > } sa_db_entry_t; > > /** > @@ -37,6 +42,7 @@ typedef struct sa_db_s { > sa_db_entry_t array[MAX_DB]; /**< Entry storage */ > } sa_db_t; > > + > /** Initialize SA database global control structure */ > void init_sa_db(void); > > @@ -69,6 +75,57 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src, > ip_addr_range_t *dst, > odp_bool_t cipher); > > +/** > + * Tunnel entry > + */ > +typedef struct tun_db_entry_s { > + struct tun_db_entry_s *next; > + uint32_t src_ip; /**< Inner Source IPv4 address */ > + uint32_t dst_ip; /**< Inner Destination IPv4 address */ > + uint32_t tun_src_ip; /**< Tunnel Source IPv4 address */ > + uint32_t tun_dst_ip; /**< Tunnel Source IPv4 address */ > +} tun_db_entry_t; > + > +/** > + * Tunnel database > + */ > +typedef struct tun_db_s { > + uint32_t index; /**< Index of next available entry */ > + tun_db_entry_t *list; /**< List of active entries */ > + tun_db_entry_t array[MAX_DB]; /**< Entry storage */ > +} tun_db_t; > + > +/** Initialize tun database global control structure */ > +void init_tun_db(void); > + > +/** > + * Create an tunnel DB entry > + * > + * String is of the format "SrcIP:DstIP:TunSrcIp:TunDstIp" > + * > + * @param input Pointer to string describing tun > + * > + * @return 0 if successful else -1 > + */ > +int create_tun_db_entry(char *input); > + > +/** > + * Display the tun DB > + */ > +void dump_tun_db(void); > + > +/** > + * Find a matching tun DB entry > + * > + * @param ip_src Inner source IP address > + * @param ip_dst Inner destination IP address > + * > + * @return pointer to tun DB entry else NULL > + */ > +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src, > + uint32_t ip_dst); > + > + > #ifdef __cplusplus > } > #endif > diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c > index 35042f5..69f73f1 100644 > --- a/example/ipsec/odp_ipsec_stream.c > +++ b/example/ipsec/odp_ipsec_stream.c > @@ -169,18 +169,24 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > uint8_t *dmac, > odp_pool_t pkt_pool) > { > - ipsec_cache_entry_t *entry = stream->input.entry; > + ipsec_cache_entry_t *entry = NULL; > odp_packet_t pkt; > uint8_t *base; > uint8_t *data; > odph_ethhdr_t *eth; > odph_ipv4hdr_t *ip; > + odph_ipv4hdr_t *inner_ip = NULL; > odph_ahhdr_t *ah = NULL; > odph_esphdr_t *esp = NULL; > odph_icmphdr_t *icmp; > stream_pkt_hdr_t *test; > unsigned i; > > + if (stream->input.entry) > + entry = stream->input.entry; > + else if (stream->output.entry) > + entry = stream->output.entry; > + > /* Get packet */ > pkt = odp_packet_alloc(pkt_pool, 0); > if (ODP_PACKET_INVALID == pkt) > @@ -205,13 +211,22 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > /* Wait until almost finished to fill in mutable fields */ > memset((char *)ip, 0, sizeof(*ip)); > ip->ver_ihl = 0x45; > - ip->proto = ODPH_IPPROTO_ICMP; > ip->id = odp_cpu_to_be_16(stream->id); > - ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > - ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > + /* Outer IP header in tunnel mode */ > + if (entry && entry->mode == IPSEC_SA_MODE_TUNNEL && > + (entry == stream->input.entry)) { > + ip->proto = ODPH_IPV4; > + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); > + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); > + } else { > + ip->proto = ODPH_IPPROTO_ICMP; > + ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > + ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > + } > > /* AH (if specified) */ > - if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) { > + if (entry && (entry == stream->input.entry) && > + (ODP_AUTH_ALG_NULL != entry->ah.alg)) { > if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg) > abort(); > > @@ -226,7 +241,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > } > > /* ESP (if specified) */ > - if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { > + if (entry && (entry == stream->input.entry) && > + (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { > if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg) > abort(); > > @@ -239,6 +255,23 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > RAND_bytes(esp->iv, 8); > } > > + /* Inner IP header in tunnel mode */ > + if (entry && (entry == stream->input.entry) && > + (entry->mode == IPSEC_SA_MODE_TUNNEL)) { > + inner_ip = (odph_ipv4hdr_t *)data; > + memset((char *)inner_ip, 0, sizeof(*inner_ip)); > + inner_ip->ver_ihl = 0x45; > + inner_ip->proto = ODPH_IPPROTO_ICMP; > + inner_ip->id = odp_cpu_to_be_16(stream->id); > + inner_ip->ttl = 64; > + inner_ip->tos = 0; > + inner_ip->frag_offset = 0; > + inner_ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > + inner_ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > + inner_ip->chksum = odp_chksum(inner_ip, sizeof(inner_ip)); > + data += sizeof(*inner_ip); > + } > + > /* ICMP header so we can see it on wireshark */ > icmp = (odph_icmphdr_t *)data; > data += sizeof(*icmp); > @@ -261,6 +294,13 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > /* Close ESP if specified */ > if (esp) { > int payload_len = data - (uint8_t *)icmp; > + uint8_t *encrypt_start = (uint8_t *)icmp; > + > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > + payload_len = data - (uint8_t *)inner_ip; > + encrypt_start = (uint8_t *)inner_ip; > + } > + > int encrypt_len; > odph_esptrl_t *esp_t; > DES_key_schedule ks1, ks2, ks3; > @@ -282,8 +322,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2); > DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3); > > - DES_ede3_cbc_encrypt((uint8_t *)icmp, > - (uint8_t *)icmp, > + DES_ede3_cbc_encrypt(encrypt_start, > + encrypt_start, > encrypt_len, > &ks1, > &ks2, > @@ -332,7 +372,7 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, > odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > odp_packet_t pkt) > { > - ipsec_cache_entry_t *entry = stream->output.entry; > + ipsec_cache_entry_t *entry = NULL; > uint8_t *data; > odph_ipv4hdr_t *ip; > odph_ahhdr_t *ah = NULL; > @@ -340,6 +380,12 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > int hdr_len; > odph_icmphdr_t *icmp; > stream_pkt_hdr_t *test; > + uint32_t src_ip, dst_ip; > + > + if (stream->input.entry) > + entry = stream->input.entry; > + else if (stream->output.entry) > + entry = stream->output.entry; > > /* Basic IPv4 verify (add checksum verification) */ > data = odp_packet_l3_ptr(pkt, NULL); > @@ -347,13 +393,29 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > data += sizeof(*ip); > if (0x45 != ip->ver_ihl) > return FALSE; > - if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr)) > + > + src_ip = odp_be_to_cpu_32(ip->src_addr); > + dst_ip = odp_be_to_cpu_32(ip->dst_addr); > + if ((stream->src_ip != src_ip) && stream->output.entry && > + (stream->output.entry->tun_src_ip != src_ip)) > + return FALSE; > + if ((stream->dst_ip != dst_ip) && stream->output.entry && > + (stream->output.entry->tun_dst_ip != dst_ip)) > + return FALSE; > + > + if ((stream->src_ip != src_ip) && stream->input.entry && > + (stream->input.entry->tun_src_ip != src_ip)) > return FALSE; > - if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr)) > + if ((stream->dst_ip != dst_ip) && stream->input.entry && > + (stream->input.entry->tun_dst_ip != dst_ip)) > return FALSE; > > /* Find IPsec headers if any and compare against entry */ > hdr_len = locate_ipsec_headers(ip, &ah, &esp); > + > + /* Cleartext packet */ > + if (!ah && !esp) > + goto clear_packet; > if (ah) { > if (!entry) > return FALSE; > @@ -446,12 +508,21 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > ip->proto = esp_t->next_header; > } > > - /* Verify ICMP packet */ > - if (ODPH_IPPROTO_ICMP != ip->proto) > - return FALSE; > +clear_packet: > + /* Verify IP/ICMP packet */ > + if (entry && (entry->mode == IPSEC_SA_MODE_TUNNEL) && (ah || esp)) { > + if (ODPH_IPV4 != ip->proto) > + return FALSE; > + odph_ipv4hdr_t *inner_ip = (odph_ipv4hdr_t *)data; > + icmp = (odph_icmphdr_t *)(inner_ip + 1); > + data = (uint8_t *)icmp; > + } else { > + if (ODPH_IPPROTO_ICMP != ip->proto) > + return FALSE; > + icmp = (odph_icmphdr_t *)data; > + } > > /* Verify ICMP header */ > - icmp = (odph_icmphdr_t *)data; > data += sizeof(*icmp); > if (ICMP_ECHO != icmp->type) > return FALSE;
Hi Steve, please find comments inline. Alex On 19 May 2015 at 04:13, Steve Kordus (skordus) <skordus@cisco.com> wrote: > Sorry it took so long. > > I just have a couple of questions/comments (see the SRK>>> imbedded in > the diffs below), otherwise it looked fine to me. > > Steve > > > -----Original Message----- > From: Robbie King (robking) > Sent: Monday, May 18, 2015 1:47 PM > To: Maxim Uvarov; lng-odp@lists.linaro.org > Cc: Steve Kordus (skordus) > Subject: RE: [lng-odp] [PATCH 1/2] examples: ipsec: tunnel mode support > > Hi Maxim, Steve Kordus is going to handle this one. > > Thanks Steve! > > -----Original Message----- > From: Maxim Uvarov [mailto:maxim.uvarov@linaro.org] > Sent: Monday, May 18, 2015 12:56 PM > To: lng-odp@lists.linaro.org; Robbie King (robking) > Subject: Re: [lng-odp] [PATCH 1/2] examples: ipsec: tunnel mode support > > Hello Robbie, > > can you please review that patch and next 2/2 patch. > > Thanks, > Maxim. > > On 05/11/2015 14:48, alexandru.badicioiu@linaro.org wrote: > > From: Alexandru Badicioiu <alexandru.badicioiu@linaro.org> > > > > Tunnel mode is enabled from the command line using -t argument with > > the following format: SrcIP:DstIP:TunnelSrcIP:TunnelDstIP. > > SrcIP - cleartext packet source IP > > DstIP - cleartext packet destination IP TunnelSrcIP - tunnel source IP > > TunnelDstIP - tunnel destination IP > > > > The outbound packets matching SrcIP:DstIP will be encapsulated in a > > TunnelSrcIP:TunnelDstIP IPSec tunnel (AH/ESP/AH+ESP) if a matching > > outbound SA is determined (as for transport mode). > > For inbound packets each entry in the IPSec cache is matched for the > > cleartext addresses, as in the transport mode (SrcIP:DstIP) and then > > for the tunnel addresses (TunnelSrcIP:TunnelDstIP) in case cleartext > > addresses didn't match. After authentication and decryption tunneled > > packets are verified against the tunnel entry (packets came in from > > the expected tunnel). > > > > Signed-off-by: Alexandru Badicioiu <alexandru.badicioiu@linaro.org> > > --- > > example/ipsec/odp_ipsec.c | 105 +++++++++++++++++++++++++++--- > > example/ipsec/odp_ipsec_cache.c | 31 +++++++++- > > example/ipsec/odp_ipsec_cache.h | 5 ++ > > example/ipsec/odp_ipsec_sa_db.c | 133 > +++++++++++++++++++++++++++++++++++++- > > example/ipsec/odp_ipsec_sa_db.h | 57 ++++++++++++++++ > > example/ipsec/odp_ipsec_stream.c | 101 ++++++++++++++++++++++++---- > > 6 files changed, 402 insertions(+), 30 deletions(-) > > > > diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c > > index cb8f535..e325347 100644 > > --- a/example/ipsec/odp_ipsec.c > > +++ b/example/ipsec/odp_ipsec.c > > @@ -135,13 +135,20 @@ typedef struct { > > uint8_t ip_ttl; /**< Saved IP TTL value */ > > int hdr_len; /**< Length of IPsec headers */ > > int trl_len; /**< Length of IPsec trailers */ > > + uint16_t tun_hdr_offset; /**< Offset of tunnel header from > > + buffer start */ > > uint16_t ah_offset; /**< Offset of AH header from buffer > start */ > > uint16_t esp_offset; /**< Offset of ESP header from buffer > start */ > > > > + /* Input only */ > > + uint32_t src_ip; /**< SA source IP address */ > > + uint32_t dst_ip; /**< SA dest IP address */ > > + > > /* Output only */ > > odp_crypto_op_params_t params; /**< Parameters for crypto call */ > > uint32_t *ah_seq; /**< AH sequence number location */ > > uint32_t *esp_seq; /**< ESP sequence number location > */ > > + uint16_t *tun_hdr_id; /**< Tunnel header ID > */ > > } ipsec_ctx_t; > > > > /** > > @@ -357,6 +364,7 @@ void ipsec_init_pre(void) > > /* Initialize our data bases */ > > init_sp_db(); > > init_sa_db(); > > + init_tun_db(); > > init_ipsec_cache(); > > } > > > > @@ -376,19 +384,27 @@ void ipsec_init_post(crypto_api_mode_e api_mode) > > for (entry = sp_db->list; NULL != entry; entry = entry->next) { > > sa_db_entry_t *cipher_sa = NULL; > > sa_db_entry_t *auth_sa = NULL; > > + tun_db_entry_t *tun; > > > > - if (entry->esp) > > + if (entry->esp) { > > cipher_sa = find_sa_db_entry(&entry->src_subnet, > > &entry->dst_subnet, > > 1); > > - if (entry->ah) > > + tun = find_tun_db_entry(cipher_sa->src_ip, > > + cipher_sa->dst_ip); > > + } > > + if (entry->ah) { > > auth_sa = find_sa_db_entry(&entry->src_subnet, > > &entry->dst_subnet, > > 0); > > + tun = find_tun_db_entry(auth_sa->src_ip, > > + auth_sa->dst_ip); > > + } > > > > if (cipher_sa || auth_sa) { > > if (create_ipsec_cache_entry(cipher_sa, > > auth_sa, > > + tun, > > api_mode, > > entry->input, > > completionq, > > @@ -661,6 +677,8 @@ pkt_disposition_e do_ipsec_in_classify(odp_packet_t > pkt, > > ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; > > ctx->ipsec.hdr_len = hdr_len; > > ctx->ipsec.trl_len = 0; > > + ctx->ipsec.src_ip = entry->src_ip; > > + ctx->ipsec.dst_ip = entry->dst_ip; > > > > /*If authenticating, zero the mutable fields build the request */ > > if (ah) { > > @@ -741,6 +759,23 @@ pkt_disposition_e do_ipsec_in_finish(odp_packet_t > pkt, > > trl_len += esp_t->pad_len + sizeof(*esp_t); > > } > > > > + /* We have a tunneled IPv4 packet */ > > + if (ip->proto == ODPH_IPV4) { > > + odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len); > > + odp_packet_pull_tail(pkt, trl_len); > > + odph_ethhdr_t *eth; > > + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); > > + eth->type = ODPH_ETHTYPE_IPV4; > > + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); > > + > > + /* Check inbound policy */ > > + if ((ip->src_addr != ctx->ipsec.src_ip || > > + ip->dst_addr != ctx->ipsec.dst_ip)) > > + return PKT_DROP; > > + > > + return PKT_CONTINUE; > > + } > > + > > /* Finalize the IPv4 header */ > > ipv4_adjust_len(ip, -(hdr_len + trl_len)); > > ip->ttl = ctx->ipsec.ip_ttl; > > @@ -812,9 +847,13 @@ pkt_disposition_e > do_ipsec_out_classify(odp_packet_t pkt, > > params.pkt = pkt; > > params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; > > > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > > + hdr_len += sizeof(odph_ipv4hdr_t); > > + ip_data = (uint8_t *)ip; > > + } > > /* Compute ah and esp, determine length of headers, move the data > */ > > if (entry->ah.alg) { > > - ah = (odph_ahhdr_t *)(ip_data); > > + ah = (odph_ahhdr_t *)(ip_data + hdr_len); > > hdr_len += sizeof(odph_ahhdr_t); > > hdr_len += entry->ah.icv_len; > > } > > @@ -826,21 +865,39 @@ pkt_disposition_e > do_ipsec_out_classify(odp_packet_t pkt, > > memmove(ip_data + hdr_len, ip_data, ip_data_len); > > ip_data += hdr_len; > > > > + /* update outer header in tunnel mode */ > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > > + /* tunnel addresses */ > > + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); > > + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); > > + } > > + > > /* For cipher, compute encrypt length, build headers and request */ > > if (esp) { > > uint32_t encrypt_len; > > odph_esptrl_t *esp_t; > > > > - encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t), > > - entry->esp.block_len); > > - trl_len = encrypt_len - ip_data_len; > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > > + encrypt_len = ESP_ENCODE_LEN(ip->tot_len + > > + sizeof(*esp_t), > > + entry->esp.block_len); > > + trl_len = encrypt_len - ip->tot_len; > > + } else { > > + encrypt_len = ESP_ENCODE_LEN(ip_data_len + > > + sizeof(*esp_t), > > + entry->esp.block_len); > > + trl_len = encrypt_len - ip_data_len; > > + } > > > > esp->spi = odp_cpu_to_be_32(entry->esp.spi); > > memcpy(esp + 1, entry->state.iv, entry->esp.iv_len); > > > > esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1; > > esp_t->pad_len = trl_len - sizeof(*esp_t); > > - esp_t->next_header = ip->proto; > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) > > + esp_t->next_header = ODPH_IPV4; > > + else > > + esp_t->next_header = ip->proto; > > ip->proto = ODPH_IPPROTO_ESP; > > > > params.cipher_range.offset = ip_data - buf; @@ -852,7 > +909,10 @@ > > pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, > > memset(ah, 0, sizeof(*ah) + entry->ah.icv_len); > > ah->spi = odp_cpu_to_be_32(entry->ah.spi); > > ah->ah_len = 1 + (entry->ah.icv_len / 4); > > - ah->next_header = ip->proto; > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL && !esp) > > + ah->next_header = ODPH_IPV4; > > + else > > + ah->next_header = ip->proto; > > ip->proto = ODPH_IPPROTO_AH; > > > > ip->chksum = 0; > > @@ -875,8 +935,11 @@ pkt_disposition_e > do_ipsec_out_classify(odp_packet_t pkt, > > ctx->ipsec.trl_len = trl_len; > > ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0; > > ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; > > + ctx->ipsec.tun_hdr_offset = (entry->mode == IPSEC_SA_MODE_TUNNEL) ? > > + ((uint8_t *)ip - buf) : 0; > > ctx->ipsec.ah_seq = &entry->state.ah_seq; > > ctx->ipsec.esp_seq = &entry->state.esp_seq; > > + ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id; > > memcpy(&ctx->ipsec.params, ¶ms, sizeof(params)); > > > > *skip = FALSE; > > @@ -915,6 +978,20 @@ pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt, > > esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf); > > esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++); > > } > > + if (ctx->ipsec.tun_hdr_offset) { > > + odph_ipv4hdr_t *ip; > > + int ret; > > + ip = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf); > > + ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++); > > + if (!ip->id) { > > + /* re-init tunnel hdr id */ > > + ret = odp_random_data((uint8_t > *)ctx->ipsec.tun_hdr_id, > > + > sizeof(*ctx->ipsec.tun_hdr_id), > > + 1); > > + if (ret != sizeof(*ctx->ipsec.tun_hdr_id)) > > + abort(); > > SRK>>> -- isn't there something like an odp_abort() that should be used > instead ? > [AB] - there's an ODP_ABORT macro but it is implementation internal. Standard library abort() (used in many places in odp_ipsec) seems to be norm, at least for now. > > + } > > + } > > > > /* Issue crypto request */ > > if (odp_crypto_operation(&ctx->ipsec.params, > > @@ -1306,8 +1383,9 @@ static void parse_args(int argc, char *argv[], > appl_args_t *appl_args) > > {"mode", required_argument, NULL, 'm'}, /* return > 'm' */ > > {"route", required_argument, NULL, 'r'}, /* return > 'r' */ > > {"policy", required_argument, NULL, 'p'}, /* return > 'p' */ > > - {"ah", required_argument, NULL, 'a'}, /* return > 'a' */ > > - {"esp", required_argument, NULL, 'e'}, /* return > 'e' */ > > + {"ah", required_argument, NULL, 'a'}, /* return > 'a' */ > > + {"esp", required_argument, NULL, 'e'}, /* return > 'e' */ > > + {"tunnel", required_argument, NULL, 't'}, /* return > 't' */ > > {"stream", required_argument, NULL, 's'}, /* return > 's' */ > > {"help", no_argument, NULL, 'h'}, /* return > 'h' */ > > {NULL, 0, NULL, 0} > > @@ -1318,7 +1396,7 @@ static void parse_args(int argc, char *argv[], > appl_args_t *appl_args) > > appl_args->mode = 0; /* turn off async crypto API by default */ > > > > while (!rc) { > > - opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:s:", > > + opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:t:s:", > > longopts, &long_index); > > > > if (-1 == opt) > > @@ -1389,6 +1467,10 @@ static void parse_args(int argc, char *argv[], > appl_args_t *appl_args) > > rc = create_sa_db_entry(optarg, TRUE); > > break; > > > > + case 't': > > + rc = create_tun_db_entry(optarg); > > + break; > > + > > case 's': > > rc = create_stream_db_entry(optarg); > > break; > > @@ -1449,6 +1531,7 @@ static void print_info(char *progname, appl_args_t > *appl_args) > > dump_fwd_db(); > > dump_sp_db(); > > dump_sa_db(); > > + dump_tun_db(); > > printf("\n\n"); > > fflush(NULL); > > } > > diff --git a/example/ipsec/odp_ipsec_cache.c > > b/example/ipsec/odp_ipsec_cache.c index 12b960d..046e43c 100644 > > --- a/example/ipsec/odp_ipsec_cache.c > > +++ b/example/ipsec/odp_ipsec_cache.c > > @@ -38,6 +38,7 @@ void init_ipsec_cache(void) > > > > int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > > sa_db_entry_t *auth_sa, > > + tun_db_entry_t *tun, > > crypto_api_mode_e api_mode, > > odp_bool_t in, > > odp_queue_t completionq, > > @@ -47,12 +48,18 @@ int create_ipsec_cache_entry(sa_db_entry_t > *cipher_sa, > > ipsec_cache_entry_t *entry; > > enum odp_crypto_ses_create_err ses_create_rc; > > odp_crypto_session_t session; > > + sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT; > > > > /* Verify we have a good entry */ > > entry = &ipsec_cache->array[ipsec_cache->index]; > > if (MAX_DB <= ipsec_cache->index) > > return -1; > > > > + /* Verify SA mode match in case of cipher&auth */ > > + if (cipher_sa && auth_sa && > > + (cipher_sa->mode != auth_sa->mode)) > > + return -1; > > + > > /* Setup parameters and call crypto library to create session */ > > params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE; > > params.auth_cipher_text = TRUE; > > @@ -79,6 +86,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > > params.cipher_key.length = cipher_sa->key.length; > > params.iv.data = entry->state.iv; > > params.iv.length = cipher_sa->iv_len; > > + mode = cipher_sa->mode; > > } else { > > params.cipher_alg = ODP_CIPHER_ALG_NULL; > > params.iv.data = NULL; > > @@ -90,6 +98,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > > params.auth_alg = auth_sa->alg.u.auth; > > params.auth_key.data = auth_sa->key.data; > > params.auth_key.length = auth_sa->key.length; > > + mode = auth_sa->mode; > > } else { > > params.auth_alg = ODP_AUTH_ALG_NULL; > > } > > @@ -128,6 +137,24 @@ int create_ipsec_cache_entry(sa_db_entry_t > *cipher_sa, > > memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t)); > > } > > > > + if (tun) { > > + entry->tun_src_ip = tun->tun_src_ip; > > + entry->tun_dst_ip = tun->tun_dst_ip; > > + mode = IPSEC_SA_MODE_TUNNEL; > > + > > + int ret; > > + if (!in) { > > + /* init tun hdr id */ > > + ret = odp_random_data((uint8_t *) > > + &entry->state.tun_hdr_id, > > + > sizeof(entry->state.tun_hdr_id), > > + 1); > > + if (ret != sizeof(entry->state.tun_hdr_id)) > > + return -1; > > + } > > + } > > + entry->mode = mode; > > + > > /* Initialize state */ > > entry->state.esp_seq = 0; > > entry->state.ah_seq = 0; > > @@ -156,7 +183,9 @@ ipsec_cache_entry_t > *find_ipsec_cache_entry_in(uint32_t src_ip, > > /* Look for a hit */ > > for (; NULL != entry; entry = entry->next) { > > if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip)) > > - continue; > > + if ((entry->tun_src_ip != src_ip) || > > + (entry->tun_dst_ip != dst_ip)) > > + continue; > > if (ah && > > ((!entry->ah.alg) || > > (entry->ah.spi != odp_be_to_cpu_32(ah->spi)))) diff > --git > > a/example/ipsec/odp_ipsec_cache.h b/example/ipsec/odp_ipsec_cache.h > > index 714cae8..6b1be5f 100644 > > --- a/example/ipsec/odp_ipsec_cache.h > > +++ b/example/ipsec/odp_ipsec_cache.h > > @@ -34,6 +34,9 @@ typedef struct ipsec_cache_entry_s { > > odp_bool_t in_place; /**< Crypto API mode */ > > uint32_t src_ip; /**< Source v4 address */ > > uint32_t dst_ip; /**< Destination v4 > address */ > > + sa_mode_t mode; /**< SA mode - > transport/tun */ > > + uint32_t tun_src_ip; /**< Tunnel src IPv4 > addr */ > > + uint32_t tun_dst_ip; /**< Tunnel dst IPv4 > addr */ > > struct { > > enum odp_cipher_alg alg; /**< Cipher algorithm */ > > uint32_t spi; /**< Cipher SPI */ > > @@ -54,6 +57,7 @@ typedef struct ipsec_cache_entry_s { > > uint32_t esp_seq; /**< ESP TX sequence number > */ > > uint32_t ah_seq; /**< AH TX sequence number > */ > > uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */ > > + uint16be_t tun_hdr_id; /**< Tunnel header IP ID */ > > } state; > > } ipsec_cache_entry_t; > > > > @@ -87,6 +91,7 @@ void init_ipsec_cache(void); > > */ > > int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, > > sa_db_entry_t *auth_sa, > > + tun_db_entry_t *tun, > SRK>>> shouldn't the comments above this include a @param tun > pupose/description added ? > [AB] I agree. A v2 patch will address this. > > crypto_api_mode_e api_mode, > > odp_bool_t in, > > odp_queue_t completionq, > > diff --git a/example/ipsec/odp_ipsec_sa_db.c > > b/example/ipsec/odp_ipsec_sa_db.c index 5837cb6..78f43da 100644 > > --- a/example/ipsec/odp_ipsec_sa_db.c > > +++ b/example/ipsec/odp_ipsec_sa_db.c > > @@ -1,7 +1,7 @@ > > /* Copyright (c) 2014, Linaro Limited > > * All rights reserved. > > * > > - * SPDX-License-Identifier: BSD-3-Clause > > + * SPDX-License-Identifier: BSD-3-Clause > > */ > > > > /* enable strtok */ > > @@ -19,6 +19,9 @@ > > /** Global pointer to sa db */ > > static sa_db_t *sa_db; > > > > +/** Global pointer to tun db */ > > +static tun_db_t *tun_db; > > + > > void init_sa_db(void) > > { > > odp_shm_t shm; > > @@ -37,6 +40,22 @@ void init_sa_db(void) > > memset(sa_db, 0, sizeof(*sa_db)); > > } > > > > +void init_tun_db(void) > > +{ > > + odp_shm_t shm; > > + shm = odp_shm_reserve("shm_tun_db", > > + sizeof(tun_db_t), > > + ODP_CACHE_LINE_SIZE, > > + 0); > > + tun_db = odp_shm_addr(shm); > > + > > + if (tun_db == NULL) { > > + EXAMPLE_ERR("Error: shared mem alloc failed.\n"); > > + exit(EXIT_FAILURE); > > + } > > + memset(tun_db, 0, sizeof(*tun_db)); > > +} > > + > > int create_sa_db_entry(char *input, odp_bool_t cipher) > > { > > int pos = 0; > > @@ -81,7 +100,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) > > entry->alg.u.cipher = > > ODP_CIPHER_ALG_3DES_CBC; > > entry->block_len = 8; > > - entry->iv_len = 8; > > + entry->iv_len = 8; > > } else { > > entry->alg.u.cipher = > > ODP_CIPHER_ALG_NULL; > > @@ -90,7 +109,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) > > if (0 == strcmp(token, "md5")) { > > entry->alg.u.auth = > > ODP_AUTH_ALG_MD5_96; > > - entry->icv_len = 12; > > + entry->icv_len = 12; > > } else { > > entry->alg.u.auth = > ODP_AUTH_ALG_NULL; > > } > > @@ -132,6 +151,89 @@ int create_sa_db_entry(char *input, odp_bool_t > cipher) > > return 0; > > } > > > > +int create_tun_db_entry(char *input) > > +{ > > + int pos = 0; > > + char *local; > > + char *str; > > + char *save; > > + char *token; > > + tun_db_entry_t *entry = &tun_db->array[tun_db->index]; > > + > > + /* Verify we have a good entry */ > > + if (MAX_DB <= tun_db->index) > > + return -1; > > + > > + /* Make a local copy */ > > + local = malloc(strlen(input) + 1); > > + if (NULL == local) > > + return -1; > > + strcpy(local, input); > > + > > + /* Setup for using "strtok_r" to search input string */ > > + str = local; > > + save = NULL; > > + > > + /* Parse tokens separated by ':' */ > > + while (NULL != (token = strtok_r(str, ":", &save))) { > > + str = NULL; /* reset str for subsequent strtok_r calls */ > > + > > + /* Parse token based on its position */ > > + switch (pos) { > > + case 0: > > + parse_ipv4_string(token, &entry->src_ip, NULL); > > + break; > > + case 1: > > + parse_ipv4_string(token, &entry->dst_ip, NULL); > > + break; > > + case 2: > > + parse_ipv4_string(token, &entry->tun_src_ip, NULL); > > + break; > > + case 3: > > + parse_ipv4_string(token, &entry->tun_dst_ip, NULL); > > + break; > > + default: > > + printf("ERROR: extra token \"%s\" at position > %d\n", > > + token, pos); > > + break; > > + } > > + pos++; > > + } > > + > > + /* Verify we parsed exactly the number of tokens we expected */ > > + if (4 != pos) { > > + printf("ERROR: \"%s\" contains %d tokens, expected 4\n", > > + input, > > + pos); > > + free(local); > > + return -1; > > + } > > + > > + /* Add route to the list */ > > + tun_db->index++; > > + entry->next = tun_db->list; > > + tun_db->list = entry; > > + > > + free(local); > > + return 0; > > +} > > + > > +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src, > > + uint32_t ip_dst) > > +{ > > + tun_db_entry_t *entry = NULL; > > + > > + /* Scan all entries and return first match */ > > + for (entry = tun_db->list; NULL != entry; entry = entry->next) { > > + if (entry->src_ip != ip_src) > > + continue; > > + if (entry->dst_ip != ip_dst) > > + continue; > > + break; > > + } > > + return entry; > > +} > > + > > void dump_sa_db(void) > > { > > sa_db_entry_t *entry; > > @@ -182,3 +284,28 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t > *src, > > } > > return entry; > > } > > + > > +void dump_tun_db(void) > > +{ > > + tun_db_entry_t *entry; > > + > > + printf("\n" > > + "Tunnel table\n" > > + "--------------------------\n"); > > + > > + for (entry = tun_db->list; NULL != entry; entry = entry->next) { > > + char src_ip_str[MAX_STRING]; > > + char dst_ip_str[MAX_STRING]; > > + char tun_src_ip_str[MAX_STRING]; > > + char tun_dst_ip_str[MAX_STRING]; > > + > > + printf(" %s:%s %s:%s ", > > + ipv4_addr_str(src_ip_str, entry->src_ip), > > + ipv4_addr_str(dst_ip_str, entry->dst_ip), > > + ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip), > > + ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip) > > + ); > > + > > + printf("\n"); > > + } > > +} > > diff --git a/example/ipsec/odp_ipsec_sa_db.h > > b/example/ipsec/odp_ipsec_sa_db.h index c30cbdb..79bfc78 100644 > > --- a/example/ipsec/odp_ipsec_sa_db.h > > +++ b/example/ipsec/odp_ipsec_sa_db.h > > @@ -13,6 +13,10 @@ extern "C" { > > > > #include <odp_ipsec_misc.h> > > > > +typedef enum sa_mode_s { > > + IPSEC_SA_MODE_TRANSPORT, > > + IPSEC_SA_MODE_TUNNEL > > +} sa_mode_t; > > /** > > * Security Assocation (SA) data base entry > > */ > > @@ -26,6 +30,7 @@ typedef struct sa_db_entry_s { > > uint32_t block_len; /**< Cipher block length */ > > uint32_t iv_len; /**< Initialization Vector length > */ > > uint32_t icv_len; /**< Integrity Check Value length > */ > > + sa_mode_t mode; /**< SA mode - transport/tun */ > > } sa_db_entry_t; > > > > /** > > @@ -37,6 +42,7 @@ typedef struct sa_db_s { > > sa_db_entry_t array[MAX_DB]; /**< Entry storage */ > > } sa_db_t; > > > > + > > /** Initialize SA database global control structure */ > > void init_sa_db(void); > > > > @@ -69,6 +75,57 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src, > > ip_addr_range_t *dst, > > odp_bool_t cipher); > > > > +/** > > + * Tunnel entry > > + */ > > +typedef struct tun_db_entry_s { > > + struct tun_db_entry_s *next; > > + uint32_t src_ip; /**< Inner Source IPv4 address */ > > + uint32_t dst_ip; /**< Inner Destination IPv4 address > */ > > + uint32_t tun_src_ip; /**< Tunnel Source IPv4 address */ > > + uint32_t tun_dst_ip; /**< Tunnel Source IPv4 address */ > > +} tun_db_entry_t; > > + > > +/** > > + * Tunnel database > > + */ > > +typedef struct tun_db_s { > > + uint32_t index; /**< Index of next available > entry */ > > + tun_db_entry_t *list; /**< List of active entries */ > > + tun_db_entry_t array[MAX_DB]; /**< Entry storage */ } tun_db_t; > > + > > +/** Initialize tun database global control structure */ void > > +init_tun_db(void); > > + > > +/** > > + * Create an tunnel DB entry > > + * > > + * String is of the format "SrcIP:DstIP:TunSrcIp:TunDstIp" > > + * > > + * @param input Pointer to string describing tun > > + * > > + * @return 0 if successful else -1 > > + */ > > +int create_tun_db_entry(char *input); > > + > > +/** > > + * Display the tun DB > > + */ > > +void dump_tun_db(void); > > + > > +/** > > + * Find a matching tun DB entry > > + * > > + * @param ip_src Inner source IP address > > + * @param ip_dst Inner destination IP address > > + * > > + * @return pointer to tun DB entry else NULL */ tun_db_entry_t > > +*find_tun_db_entry(uint32_t ip_src, > > + uint32_t ip_dst); > > + > > + > > #ifdef __cplusplus > > } > > #endif > > diff --git a/example/ipsec/odp_ipsec_stream.c > > b/example/ipsec/odp_ipsec_stream.c > > index 35042f5..69f73f1 100644 > > --- a/example/ipsec/odp_ipsec_stream.c > > +++ b/example/ipsec/odp_ipsec_stream.c > > @@ -169,18 +169,24 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > uint8_t *dmac, > > odp_pool_t pkt_pool) > > { > > - ipsec_cache_entry_t *entry = stream->input.entry; > > + ipsec_cache_entry_t *entry = NULL; > > odp_packet_t pkt; > > uint8_t *base; > > uint8_t *data; > > odph_ethhdr_t *eth; > > odph_ipv4hdr_t *ip; > > + odph_ipv4hdr_t *inner_ip = NULL; > > odph_ahhdr_t *ah = NULL; > > odph_esphdr_t *esp = NULL; > > odph_icmphdr_t *icmp; > > stream_pkt_hdr_t *test; > > unsigned i; > > > > + if (stream->input.entry) > > + entry = stream->input.entry; > > + else if (stream->output.entry) > > + entry = stream->output.entry; > > + > > /* Get packet */ > > pkt = odp_packet_alloc(pkt_pool, 0); > > if (ODP_PACKET_INVALID == pkt) > > @@ -205,13 +211,22 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > /* Wait until almost finished to fill in mutable fields */ > > memset((char *)ip, 0, sizeof(*ip)); > > ip->ver_ihl = 0x45; > > - ip->proto = ODPH_IPPROTO_ICMP; > > ip->id = odp_cpu_to_be_16(stream->id); > > - ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > > - ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > > + /* Outer IP header in tunnel mode */ > > + if (entry && entry->mode == IPSEC_SA_MODE_TUNNEL && > > + (entry == stream->input.entry)) { > > + ip->proto = ODPH_IPV4; > > + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); > > + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); > > + } else { > > + ip->proto = ODPH_IPPROTO_ICMP; > > + ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > > + ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > > + } > > > > /* AH (if specified) */ > > - if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) { > > + if (entry && (entry == stream->input.entry) && > > + (ODP_AUTH_ALG_NULL != entry->ah.alg)) { > > if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg) > > abort(); > > > > @@ -226,7 +241,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > } > > > > /* ESP (if specified) */ > > - if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { > > + if (entry && (entry == stream->input.entry) && > > + (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { > > if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg) > > abort(); > > > > @@ -239,6 +255,23 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > RAND_bytes(esp->iv, 8); > > } > > > > + /* Inner IP header in tunnel mode */ > > + if (entry && (entry == stream->input.entry) && > > + (entry->mode == IPSEC_SA_MODE_TUNNEL)) { > > + inner_ip = (odph_ipv4hdr_t *)data; > > + memset((char *)inner_ip, 0, sizeof(*inner_ip)); > > + inner_ip->ver_ihl = 0x45; > > + inner_ip->proto = ODPH_IPPROTO_ICMP; > > + inner_ip->id = odp_cpu_to_be_16(stream->id); > > + inner_ip->ttl = 64; > > + inner_ip->tos = 0; > > + inner_ip->frag_offset = 0; > > + inner_ip->src_addr = odp_cpu_to_be_32(stream->src_ip); > > + inner_ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); > > + inner_ip->chksum = odp_chksum(inner_ip, sizeof(inner_ip)); > > + data += sizeof(*inner_ip); > > + } > > + > > /* ICMP header so we can see it on wireshark */ > > icmp = (odph_icmphdr_t *)data; > > data += sizeof(*icmp); > > @@ -261,6 +294,13 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > /* Close ESP if specified */ > > if (esp) { > > int payload_len = data - (uint8_t *)icmp; > > + uint8_t *encrypt_start = (uint8_t *)icmp; > > + > > + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { > > + payload_len = data - (uint8_t *)inner_ip; > > + encrypt_start = (uint8_t *)inner_ip; > > + } > > + > > int encrypt_len; > > odph_esptrl_t *esp_t; > > DES_key_schedule ks1, ks2, ks3; > > @@ -282,8 +322,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2); > > DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3); > > > > - DES_ede3_cbc_encrypt((uint8_t *)icmp, > > - (uint8_t *)icmp, > > + DES_ede3_cbc_encrypt(encrypt_start, > > + encrypt_start, > > encrypt_len, > > &ks1, > > &ks2, > > @@ -332,7 +372,7 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t > *stream, > > odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > > odp_packet_t pkt) > > { > > - ipsec_cache_entry_t *entry = stream->output.entry; > > + ipsec_cache_entry_t *entry = NULL; > > uint8_t *data; > > odph_ipv4hdr_t *ip; > > odph_ahhdr_t *ah = NULL; > > @@ -340,6 +380,12 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t > *stream, > > int hdr_len; > > odph_icmphdr_t *icmp; > > stream_pkt_hdr_t *test; > > + uint32_t src_ip, dst_ip; > > + > > + if (stream->input.entry) > > + entry = stream->input.entry; > > + else if (stream->output.entry) > > + entry = stream->output.entry; > > > > /* Basic IPv4 verify (add checksum verification) */ > > data = odp_packet_l3_ptr(pkt, NULL); @@ -347,13 +393,29 @@ > > odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, > > data += sizeof(*ip); > > if (0x45 != ip->ver_ihl) > > return FALSE; > > - if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr)) > > + > > + src_ip = odp_be_to_cpu_32(ip->src_addr); > > + dst_ip = odp_be_to_cpu_32(ip->dst_addr); > > + if ((stream->src_ip != src_ip) && stream->output.entry && > > + (stream->output.entry->tun_src_ip != src_ip)) > > + return FALSE; > > + if ((stream->dst_ip != dst_ip) && stream->output.entry && > > + (stream->output.entry->tun_dst_ip != dst_ip)) > > + return FALSE; > > + > > + if ((stream->src_ip != src_ip) && stream->input.entry && > > + (stream->input.entry->tun_src_ip != src_ip)) > > return FALSE; > > - if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr)) > > + if ((stream->dst_ip != dst_ip) && stream->input.entry && > > + (stream->input.entry->tun_dst_ip != dst_ip)) > > return FALSE; > > > > /* Find IPsec headers if any and compare against entry */ > > hdr_len = locate_ipsec_headers(ip, &ah, &esp); > > + > > + /* Cleartext packet */ > > + if (!ah && !esp) > > + goto clear_packet; > > if (ah) { > > if (!entry) > > return FALSE; > > @@ -446,12 +508,21 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t > *stream, > > ip->proto = esp_t->next_header; > > } > > > > - /* Verify ICMP packet */ > > - if (ODPH_IPPROTO_ICMP != ip->proto) > > - return FALSE; > > +clear_packet: > > + /* Verify IP/ICMP packet */ > > + if (entry && (entry->mode == IPSEC_SA_MODE_TUNNEL) && (ah || esp)) > { > > + if (ODPH_IPV4 != ip->proto) > > + return FALSE; > > + odph_ipv4hdr_t *inner_ip = (odph_ipv4hdr_t *)data; > > + icmp = (odph_icmphdr_t *)(inner_ip + 1); > > + data = (uint8_t *)icmp; > > + } else { > > + if (ODPH_IPPROTO_ICMP != ip->proto) > > + return FALSE; > > + icmp = (odph_icmphdr_t *)data; > > + } > > > > /* Verify ICMP header */ > > - icmp = (odph_icmphdr_t *)data; > > data += sizeof(*icmp); > > if (ICMP_ECHO != icmp->type) > > return FALSE; > > _______________________________________________ > lng-odp mailing list > lng-odp@lists.linaro.org > https://lists.linaro.org/mailman/listinfo/lng-odp >
diff --git a/example/ipsec/odp_ipsec.c b/example/ipsec/odp_ipsec.c index cb8f535..e325347 100644 --- a/example/ipsec/odp_ipsec.c +++ b/example/ipsec/odp_ipsec.c @@ -135,13 +135,20 @@ typedef struct { uint8_t ip_ttl; /**< Saved IP TTL value */ int hdr_len; /**< Length of IPsec headers */ int trl_len; /**< Length of IPsec trailers */ + uint16_t tun_hdr_offset; /**< Offset of tunnel header from + buffer start */ uint16_t ah_offset; /**< Offset of AH header from buffer start */ uint16_t esp_offset; /**< Offset of ESP header from buffer start */ + /* Input only */ + uint32_t src_ip; /**< SA source IP address */ + uint32_t dst_ip; /**< SA dest IP address */ + /* Output only */ odp_crypto_op_params_t params; /**< Parameters for crypto call */ uint32_t *ah_seq; /**< AH sequence number location */ uint32_t *esp_seq; /**< ESP sequence number location */ + uint16_t *tun_hdr_id; /**< Tunnel header ID > */ } ipsec_ctx_t; /** @@ -357,6 +364,7 @@ void ipsec_init_pre(void) /* Initialize our data bases */ init_sp_db(); init_sa_db(); + init_tun_db(); init_ipsec_cache(); } @@ -376,19 +384,27 @@ void ipsec_init_post(crypto_api_mode_e api_mode) for (entry = sp_db->list; NULL != entry; entry = entry->next) { sa_db_entry_t *cipher_sa = NULL; sa_db_entry_t *auth_sa = NULL; + tun_db_entry_t *tun; - if (entry->esp) + if (entry->esp) { cipher_sa = find_sa_db_entry(&entry->src_subnet, &entry->dst_subnet, 1); - if (entry->ah) + tun = find_tun_db_entry(cipher_sa->src_ip, + cipher_sa->dst_ip); + } + if (entry->ah) { auth_sa = find_sa_db_entry(&entry->src_subnet, &entry->dst_subnet, 0); + tun = find_tun_db_entry(auth_sa->src_ip, + auth_sa->dst_ip); + } if (cipher_sa || auth_sa) { if (create_ipsec_cache_entry(cipher_sa, auth_sa, + tun, api_mode, entry->input, completionq, @@ -661,6 +677,8 @@ pkt_disposition_e do_ipsec_in_classify(odp_packet_t pkt, ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; ctx->ipsec.hdr_len = hdr_len; ctx->ipsec.trl_len = 0; + ctx->ipsec.src_ip = entry->src_ip; + ctx->ipsec.dst_ip = entry->dst_ip; /*If authenticating, zero the mutable fields build the request */ if (ah) { @@ -741,6 +759,23 @@ pkt_disposition_e do_ipsec_in_finish(odp_packet_t pkt, trl_len += esp_t->pad_len + sizeof(*esp_t); } + /* We have a tunneled IPv4 packet */ + if (ip->proto == ODPH_IPV4) { + odp_packet_pull_head(pkt, sizeof(*ip) + hdr_len); + odp_packet_pull_tail(pkt, trl_len); + odph_ethhdr_t *eth; + eth = (odph_ethhdr_t *)odp_packet_l2_ptr(pkt, NULL); + eth->type = ODPH_ETHTYPE_IPV4; + ip = (odph_ipv4hdr_t *)odp_packet_l3_ptr(pkt, NULL); + + /* Check inbound policy */ + if ((ip->src_addr != ctx->ipsec.src_ip || + ip->dst_addr != ctx->ipsec.dst_ip)) + return PKT_DROP; + + return PKT_CONTINUE; + } + /* Finalize the IPv4 header */ ipv4_adjust_len(ip, -(hdr_len + trl_len)); ip->ttl = ctx->ipsec.ip_ttl; @@ -812,9 +847,13 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, params.pkt = pkt; params.out_pkt = entry->in_place ? pkt : ODP_PACKET_INVALID; + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { + hdr_len += sizeof(odph_ipv4hdr_t); + ip_data = (uint8_t *)ip; + } /* Compute ah and esp, determine length of headers, move the data */ if (entry->ah.alg) { - ah = (odph_ahhdr_t *)(ip_data); + ah = (odph_ahhdr_t *)(ip_data + hdr_len); hdr_len += sizeof(odph_ahhdr_t); hdr_len += entry->ah.icv_len; } @@ -826,21 +865,39 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, memmove(ip_data + hdr_len, ip_data, ip_data_len); ip_data += hdr_len; + /* update outer header in tunnel mode */ + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { + /* tunnel addresses */ + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); + } + /* For cipher, compute encrypt length, build headers and request */ if (esp) { uint32_t encrypt_len; odph_esptrl_t *esp_t; - encrypt_len = ESP_ENCODE_LEN(ip_data_len + sizeof(*esp_t), - entry->esp.block_len); - trl_len = encrypt_len - ip_data_len; + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { + encrypt_len = ESP_ENCODE_LEN(ip->tot_len + + sizeof(*esp_t), + entry->esp.block_len); + trl_len = encrypt_len - ip->tot_len; + } else { + encrypt_len = ESP_ENCODE_LEN(ip_data_len + + sizeof(*esp_t), + entry->esp.block_len); + trl_len = encrypt_len - ip_data_len; + } esp->spi = odp_cpu_to_be_32(entry->esp.spi); memcpy(esp + 1, entry->state.iv, entry->esp.iv_len); esp_t = (odph_esptrl_t *)(ip_data + encrypt_len) - 1; esp_t->pad_len = trl_len - sizeof(*esp_t); - esp_t->next_header = ip->proto; + if (entry->mode == IPSEC_SA_MODE_TUNNEL) + esp_t->next_header = ODPH_IPV4; + else + esp_t->next_header = ip->proto; ip->proto = ODPH_IPPROTO_ESP; params.cipher_range.offset = ip_data - buf; @@ -852,7 +909,10 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, memset(ah, 0, sizeof(*ah) + entry->ah.icv_len); ah->spi = odp_cpu_to_be_32(entry->ah.spi); ah->ah_len = 1 + (entry->ah.icv_len / 4); - ah->next_header = ip->proto; + if (entry->mode == IPSEC_SA_MODE_TUNNEL && !esp) + ah->next_header = ODPH_IPV4; + else + ah->next_header = ip->proto; ip->proto = ODPH_IPPROTO_AH; ip->chksum = 0; @@ -875,8 +935,11 @@ pkt_disposition_e do_ipsec_out_classify(odp_packet_t pkt, ctx->ipsec.trl_len = trl_len; ctx->ipsec.ah_offset = ah ? ((uint8_t *)ah) - buf : 0; ctx->ipsec.esp_offset = esp ? ((uint8_t *)esp) - buf : 0; + ctx->ipsec.tun_hdr_offset = (entry->mode == IPSEC_SA_MODE_TUNNEL) ? + ((uint8_t *)ip - buf) : 0; ctx->ipsec.ah_seq = &entry->state.ah_seq; ctx->ipsec.esp_seq = &entry->state.esp_seq; + ctx->ipsec.tun_hdr_id = &entry->state.tun_hdr_id; memcpy(&ctx->ipsec.params, ¶ms, sizeof(params)); *skip = FALSE; @@ -915,6 +978,20 @@ pkt_disposition_e do_ipsec_out_seq(odp_packet_t pkt, esp = (odph_esphdr_t *)(ctx->ipsec.esp_offset + buf); esp->seq_no = odp_cpu_to_be_32((*ctx->ipsec.esp_seq)++); } + if (ctx->ipsec.tun_hdr_offset) { + odph_ipv4hdr_t *ip; + int ret; + ip = (odph_ipv4hdr_t *)(ctx->ipsec.tun_hdr_offset + buf); + ip->id = odp_cpu_to_be_16((*ctx->ipsec.tun_hdr_id)++); + if (!ip->id) { + /* re-init tunnel hdr id */ + ret = odp_random_data((uint8_t *)ctx->ipsec.tun_hdr_id, + sizeof(*ctx->ipsec.tun_hdr_id), + 1); + if (ret != sizeof(*ctx->ipsec.tun_hdr_id)) + abort(); + } + } /* Issue crypto request */ if (odp_crypto_operation(&ctx->ipsec.params, @@ -1306,8 +1383,9 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) {"mode", required_argument, NULL, 'm'}, /* return 'm' */ {"route", required_argument, NULL, 'r'}, /* return 'r' */ {"policy", required_argument, NULL, 'p'}, /* return 'p' */ - {"ah", required_argument, NULL, 'a'}, /* return 'a' */ - {"esp", required_argument, NULL, 'e'}, /* return 'e' */ + {"ah", required_argument, NULL, 'a'}, /* return 'a' */ + {"esp", required_argument, NULL, 'e'}, /* return 'e' */ + {"tunnel", required_argument, NULL, 't'}, /* return 't' */ {"stream", required_argument, NULL, 's'}, /* return 's' */ {"help", no_argument, NULL, 'h'}, /* return 'h' */ {NULL, 0, NULL, 0} @@ -1318,7 +1396,7 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) appl_args->mode = 0; /* turn off async crypto API by default */ while (!rc) { - opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:s:", + opt = getopt_long(argc, argv, "+c:i:m:h:r:p:a:e:t:s:", longopts, &long_index); if (-1 == opt) @@ -1389,6 +1467,10 @@ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) rc = create_sa_db_entry(optarg, TRUE); break; + case 't': + rc = create_tun_db_entry(optarg); + break; + case 's': rc = create_stream_db_entry(optarg); break; @@ -1449,6 +1531,7 @@ static void print_info(char *progname, appl_args_t *appl_args) dump_fwd_db(); dump_sp_db(); dump_sa_db(); + dump_tun_db(); printf("\n\n"); fflush(NULL); } diff --git a/example/ipsec/odp_ipsec_cache.c b/example/ipsec/odp_ipsec_cache.c index 12b960d..046e43c 100644 --- a/example/ipsec/odp_ipsec_cache.c +++ b/example/ipsec/odp_ipsec_cache.c @@ -38,6 +38,7 @@ void init_ipsec_cache(void) int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, sa_db_entry_t *auth_sa, + tun_db_entry_t *tun, crypto_api_mode_e api_mode, odp_bool_t in, odp_queue_t completionq, @@ -47,12 +48,18 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, ipsec_cache_entry_t *entry; enum odp_crypto_ses_create_err ses_create_rc; odp_crypto_session_t session; + sa_mode_t mode = IPSEC_SA_MODE_TRANSPORT; /* Verify we have a good entry */ entry = &ipsec_cache->array[ipsec_cache->index]; if (MAX_DB <= ipsec_cache->index) return -1; + /* Verify SA mode match in case of cipher&auth */ + if (cipher_sa && auth_sa && + (cipher_sa->mode != auth_sa->mode)) + return -1; + /* Setup parameters and call crypto library to create session */ params.op = (in) ? ODP_CRYPTO_OP_DECODE : ODP_CRYPTO_OP_ENCODE; params.auth_cipher_text = TRUE; @@ -79,6 +86,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, params.cipher_key.length = cipher_sa->key.length; params.iv.data = entry->state.iv; params.iv.length = cipher_sa->iv_len; + mode = cipher_sa->mode; } else { params.cipher_alg = ODP_CIPHER_ALG_NULL; params.iv.data = NULL; @@ -90,6 +98,7 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, params.auth_alg = auth_sa->alg.u.auth; params.auth_key.data = auth_sa->key.data; params.auth_key.length = auth_sa->key.length; + mode = auth_sa->mode; } else { params.auth_alg = ODP_AUTH_ALG_NULL; } @@ -128,6 +137,24 @@ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, memcpy(&entry->ah.key, &auth_sa->key, sizeof(ipsec_key_t)); } + if (tun) { + entry->tun_src_ip = tun->tun_src_ip; + entry->tun_dst_ip = tun->tun_dst_ip; + mode = IPSEC_SA_MODE_TUNNEL; + + int ret; + if (!in) { + /* init tun hdr id */ + ret = odp_random_data((uint8_t *) + &entry->state.tun_hdr_id, + sizeof(entry->state.tun_hdr_id), + 1); + if (ret != sizeof(entry->state.tun_hdr_id)) + return -1; + } + } + entry->mode = mode; + /* Initialize state */ entry->state.esp_seq = 0; entry->state.ah_seq = 0; @@ -156,7 +183,9 @@ ipsec_cache_entry_t *find_ipsec_cache_entry_in(uint32_t src_ip, /* Look for a hit */ for (; NULL != entry; entry = entry->next) { if ((entry->src_ip != src_ip) || (entry->dst_ip != dst_ip)) - continue; + if ((entry->tun_src_ip != src_ip) || + (entry->tun_dst_ip != dst_ip)) + continue; if (ah && ((!entry->ah.alg) || (entry->ah.spi != odp_be_to_cpu_32(ah->spi)))) diff --git a/example/ipsec/odp_ipsec_cache.h b/example/ipsec/odp_ipsec_cache.h index 714cae8..6b1be5f 100644 --- a/example/ipsec/odp_ipsec_cache.h +++ b/example/ipsec/odp_ipsec_cache.h @@ -34,6 +34,9 @@ typedef struct ipsec_cache_entry_s { odp_bool_t in_place; /**< Crypto API mode */ uint32_t src_ip; /**< Source v4 address */ uint32_t dst_ip; /**< Destination v4 address */ + sa_mode_t mode; /**< SA mode - transport/tun */ + uint32_t tun_src_ip; /**< Tunnel src IPv4 addr */ + uint32_t tun_dst_ip; /**< Tunnel dst IPv4 addr */ struct { enum odp_cipher_alg alg; /**< Cipher algorithm */ uint32_t spi; /**< Cipher SPI */ @@ -54,6 +57,7 @@ typedef struct ipsec_cache_entry_s { uint32_t esp_seq; /**< ESP TX sequence number */ uint32_t ah_seq; /**< AH TX sequence number */ uint8_t iv[MAX_IV_LEN]; /**< ESP IV storage */ + uint16be_t tun_hdr_id; /**< Tunnel header IP ID */ } state; } ipsec_cache_entry_t; @@ -87,6 +91,7 @@ void init_ipsec_cache(void); */ int create_ipsec_cache_entry(sa_db_entry_t *cipher_sa, sa_db_entry_t *auth_sa, + tun_db_entry_t *tun, crypto_api_mode_e api_mode, odp_bool_t in, odp_queue_t completionq, diff --git a/example/ipsec/odp_ipsec_sa_db.c b/example/ipsec/odp_ipsec_sa_db.c index 5837cb6..78f43da 100644 --- a/example/ipsec/odp_ipsec_sa_db.c +++ b/example/ipsec/odp_ipsec_sa_db.c @@ -1,7 +1,7 @@ /* Copyright (c) 2014, Linaro Limited * All rights reserved. * - * SPDX-License-Identifier: BSD-3-Clause + * SPDX-License-Identifier: BSD-3-Clause */ /* enable strtok */ @@ -19,6 +19,9 @@ /** Global pointer to sa db */ static sa_db_t *sa_db; +/** Global pointer to tun db */ +static tun_db_t *tun_db; + void init_sa_db(void) { odp_shm_t shm; @@ -37,6 +40,22 @@ void init_sa_db(void) memset(sa_db, 0, sizeof(*sa_db)); } +void init_tun_db(void) +{ + odp_shm_t shm; + shm = odp_shm_reserve("shm_tun_db", + sizeof(tun_db_t), + ODP_CACHE_LINE_SIZE, + 0); + tun_db = odp_shm_addr(shm); + + if (tun_db == NULL) { + EXAMPLE_ERR("Error: shared mem alloc failed.\n"); + exit(EXIT_FAILURE); + } + memset(tun_db, 0, sizeof(*tun_db)); +} + int create_sa_db_entry(char *input, odp_bool_t cipher) { int pos = 0; @@ -81,7 +100,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) entry->alg.u.cipher = ODP_CIPHER_ALG_3DES_CBC; entry->block_len = 8; - entry->iv_len = 8; + entry->iv_len = 8; } else { entry->alg.u.cipher = ODP_CIPHER_ALG_NULL; @@ -90,7 +109,7 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) if (0 == strcmp(token, "md5")) { entry->alg.u.auth = ODP_AUTH_ALG_MD5_96; - entry->icv_len = 12; + entry->icv_len = 12; } else { entry->alg.u.auth = ODP_AUTH_ALG_NULL; } @@ -132,6 +151,89 @@ int create_sa_db_entry(char *input, odp_bool_t cipher) return 0; } +int create_tun_db_entry(char *input) +{ + int pos = 0; + char *local; + char *str; + char *save; + char *token; + tun_db_entry_t *entry = &tun_db->array[tun_db->index]; + + /* Verify we have a good entry */ + if (MAX_DB <= tun_db->index) + return -1; + + /* Make a local copy */ + local = malloc(strlen(input) + 1); + if (NULL == local) + return -1; + strcpy(local, input); + + /* Setup for using "strtok_r" to search input string */ + str = local; + save = NULL; + + /* Parse tokens separated by ':' */ + while (NULL != (token = strtok_r(str, ":", &save))) { + str = NULL; /* reset str for subsequent strtok_r calls */ + + /* Parse token based on its position */ + switch (pos) { + case 0: + parse_ipv4_string(token, &entry->src_ip, NULL); + break; + case 1: + parse_ipv4_string(token, &entry->dst_ip, NULL); + break; + case 2: + parse_ipv4_string(token, &entry->tun_src_ip, NULL); + break; + case 3: + parse_ipv4_string(token, &entry->tun_dst_ip, NULL); + break; + default: + printf("ERROR: extra token \"%s\" at position %d\n", + token, pos); + break; + } + pos++; + } + + /* Verify we parsed exactly the number of tokens we expected */ + if (4 != pos) { + printf("ERROR: \"%s\" contains %d tokens, expected 4\n", + input, + pos); + free(local); + return -1; + } + + /* Add route to the list */ + tun_db->index++; + entry->next = tun_db->list; + tun_db->list = entry; + + free(local); + return 0; +} + +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src, + uint32_t ip_dst) +{ + tun_db_entry_t *entry = NULL; + + /* Scan all entries and return first match */ + for (entry = tun_db->list; NULL != entry; entry = entry->next) { + if (entry->src_ip != ip_src) + continue; + if (entry->dst_ip != ip_dst) + continue; + break; + } + return entry; +} + void dump_sa_db(void) { sa_db_entry_t *entry; @@ -182,3 +284,28 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src, } return entry; } + +void dump_tun_db(void) +{ + tun_db_entry_t *entry; + + printf("\n" + "Tunnel table\n" + "--------------------------\n"); + + for (entry = tun_db->list; NULL != entry; entry = entry->next) { + char src_ip_str[MAX_STRING]; + char dst_ip_str[MAX_STRING]; + char tun_src_ip_str[MAX_STRING]; + char tun_dst_ip_str[MAX_STRING]; + + printf(" %s:%s %s:%s ", + ipv4_addr_str(src_ip_str, entry->src_ip), + ipv4_addr_str(dst_ip_str, entry->dst_ip), + ipv4_addr_str(tun_src_ip_str, entry->tun_src_ip), + ipv4_addr_str(tun_dst_ip_str, entry->tun_dst_ip) + ); + + printf("\n"); + } +} diff --git a/example/ipsec/odp_ipsec_sa_db.h b/example/ipsec/odp_ipsec_sa_db.h index c30cbdb..79bfc78 100644 --- a/example/ipsec/odp_ipsec_sa_db.h +++ b/example/ipsec/odp_ipsec_sa_db.h @@ -13,6 +13,10 @@ extern "C" { #include <odp_ipsec_misc.h> +typedef enum sa_mode_s { + IPSEC_SA_MODE_TRANSPORT, + IPSEC_SA_MODE_TUNNEL +} sa_mode_t; /** * Security Assocation (SA) data base entry */ @@ -26,6 +30,7 @@ typedef struct sa_db_entry_s { uint32_t block_len; /**< Cipher block length */ uint32_t iv_len; /**< Initialization Vector length */ uint32_t icv_len; /**< Integrity Check Value length */ + sa_mode_t mode; /**< SA mode - transport/tun */ } sa_db_entry_t; /** @@ -37,6 +42,7 @@ typedef struct sa_db_s { sa_db_entry_t array[MAX_DB]; /**< Entry storage */ } sa_db_t; + /** Initialize SA database global control structure */ void init_sa_db(void); @@ -69,6 +75,57 @@ sa_db_entry_t *find_sa_db_entry(ip_addr_range_t *src, ip_addr_range_t *dst, odp_bool_t cipher); +/** + * Tunnel entry + */ +typedef struct tun_db_entry_s { + struct tun_db_entry_s *next; + uint32_t src_ip; /**< Inner Source IPv4 address */ + uint32_t dst_ip; /**< Inner Destination IPv4 address */ + uint32_t tun_src_ip; /**< Tunnel Source IPv4 address */ + uint32_t tun_dst_ip; /**< Tunnel Source IPv4 address */ +} tun_db_entry_t; + +/** + * Tunnel database + */ +typedef struct tun_db_s { + uint32_t index; /**< Index of next available entry */ + tun_db_entry_t *list; /**< List of active entries */ + tun_db_entry_t array[MAX_DB]; /**< Entry storage */ +} tun_db_t; + +/** Initialize tun database global control structure */ +void init_tun_db(void); + +/** + * Create an tunnel DB entry + * + * String is of the format "SrcIP:DstIP:TunSrcIp:TunDstIp" + * + * @param input Pointer to string describing tun + * + * @return 0 if successful else -1 + */ +int create_tun_db_entry(char *input); + +/** + * Display the tun DB + */ +void dump_tun_db(void); + +/** + * Find a matching tun DB entry + * + * @param ip_src Inner source IP address + * @param ip_dst Inner destination IP address + * + * @return pointer to tun DB entry else NULL + */ +tun_db_entry_t *find_tun_db_entry(uint32_t ip_src, + uint32_t ip_dst); + + #ifdef __cplusplus } #endif diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c index 35042f5..69f73f1 100644 --- a/example/ipsec/odp_ipsec_stream.c +++ b/example/ipsec/odp_ipsec_stream.c @@ -169,18 +169,24 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, uint8_t *dmac, odp_pool_t pkt_pool) { - ipsec_cache_entry_t *entry = stream->input.entry; + ipsec_cache_entry_t *entry = NULL; odp_packet_t pkt; uint8_t *base; uint8_t *data; odph_ethhdr_t *eth; odph_ipv4hdr_t *ip; + odph_ipv4hdr_t *inner_ip = NULL; odph_ahhdr_t *ah = NULL; odph_esphdr_t *esp = NULL; odph_icmphdr_t *icmp; stream_pkt_hdr_t *test; unsigned i; + if (stream->input.entry) + entry = stream->input.entry; + else if (stream->output.entry) + entry = stream->output.entry; + /* Get packet */ pkt = odp_packet_alloc(pkt_pool, 0); if (ODP_PACKET_INVALID == pkt) @@ -205,13 +211,22 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, /* Wait until almost finished to fill in mutable fields */ memset((char *)ip, 0, sizeof(*ip)); ip->ver_ihl = 0x45; - ip->proto = ODPH_IPPROTO_ICMP; ip->id = odp_cpu_to_be_16(stream->id); - ip->src_addr = odp_cpu_to_be_32(stream->src_ip); - ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); + /* Outer IP header in tunnel mode */ + if (entry && entry->mode == IPSEC_SA_MODE_TUNNEL && + (entry == stream->input.entry)) { + ip->proto = ODPH_IPV4; + ip->src_addr = odp_cpu_to_be_32(entry->tun_src_ip); + ip->dst_addr = odp_cpu_to_be_32(entry->tun_dst_ip); + } else { + ip->proto = ODPH_IPPROTO_ICMP; + ip->src_addr = odp_cpu_to_be_32(stream->src_ip); + ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); + } /* AH (if specified) */ - if (entry && (ODP_AUTH_ALG_NULL != entry->ah.alg)) { + if (entry && (entry == stream->input.entry) && + (ODP_AUTH_ALG_NULL != entry->ah.alg)) { if (ODP_AUTH_ALG_MD5_96 != entry->ah.alg) abort(); @@ -226,7 +241,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, } /* ESP (if specified) */ - if (entry && (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { + if (entry && (entry == stream->input.entry) && + (ODP_CIPHER_ALG_NULL != entry->esp.alg)) { if (ODP_CIPHER_ALG_3DES_CBC != entry->esp.alg) abort(); @@ -239,6 +255,23 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, RAND_bytes(esp->iv, 8); } + /* Inner IP header in tunnel mode */ + if (entry && (entry == stream->input.entry) && + (entry->mode == IPSEC_SA_MODE_TUNNEL)) { + inner_ip = (odph_ipv4hdr_t *)data; + memset((char *)inner_ip, 0, sizeof(*inner_ip)); + inner_ip->ver_ihl = 0x45; + inner_ip->proto = ODPH_IPPROTO_ICMP; + inner_ip->id = odp_cpu_to_be_16(stream->id); + inner_ip->ttl = 64; + inner_ip->tos = 0; + inner_ip->frag_offset = 0; + inner_ip->src_addr = odp_cpu_to_be_32(stream->src_ip); + inner_ip->dst_addr = odp_cpu_to_be_32(stream->dst_ip); + inner_ip->chksum = odp_chksum(inner_ip, sizeof(inner_ip)); + data += sizeof(*inner_ip); + } + /* ICMP header so we can see it on wireshark */ icmp = (odph_icmphdr_t *)data; data += sizeof(*icmp); @@ -261,6 +294,13 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, /* Close ESP if specified */ if (esp) { int payload_len = data - (uint8_t *)icmp; + uint8_t *encrypt_start = (uint8_t *)icmp; + + if (entry->mode == IPSEC_SA_MODE_TUNNEL) { + payload_len = data - (uint8_t *)inner_ip; + encrypt_start = (uint8_t *)inner_ip; + } + int encrypt_len; odph_esptrl_t *esp_t; DES_key_schedule ks1, ks2, ks3; @@ -282,8 +322,8 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, DES_set_key((DES_cblock *)&entry->esp.key.data[8], &ks2); DES_set_key((DES_cblock *)&entry->esp.key.data[16], &ks3); - DES_ede3_cbc_encrypt((uint8_t *)icmp, - (uint8_t *)icmp, + DES_ede3_cbc_encrypt(encrypt_start, + encrypt_start, encrypt_len, &ks1, &ks2, @@ -332,7 +372,7 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream, odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, odp_packet_t pkt) { - ipsec_cache_entry_t *entry = stream->output.entry; + ipsec_cache_entry_t *entry = NULL; uint8_t *data; odph_ipv4hdr_t *ip; odph_ahhdr_t *ah = NULL; @@ -340,6 +380,12 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, int hdr_len; odph_icmphdr_t *icmp; stream_pkt_hdr_t *test; + uint32_t src_ip, dst_ip; + + if (stream->input.entry) + entry = stream->input.entry; + else if (stream->output.entry) + entry = stream->output.entry; /* Basic IPv4 verify (add checksum verification) */ data = odp_packet_l3_ptr(pkt, NULL); @@ -347,13 +393,29 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, data += sizeof(*ip); if (0x45 != ip->ver_ihl) return FALSE; - if (stream->src_ip != odp_be_to_cpu_32(ip->src_addr)) + + src_ip = odp_be_to_cpu_32(ip->src_addr); + dst_ip = odp_be_to_cpu_32(ip->dst_addr); + if ((stream->src_ip != src_ip) && stream->output.entry && + (stream->output.entry->tun_src_ip != src_ip)) + return FALSE; + if ((stream->dst_ip != dst_ip) && stream->output.entry && + (stream->output.entry->tun_dst_ip != dst_ip)) + return FALSE; + + if ((stream->src_ip != src_ip) && stream->input.entry && + (stream->input.entry->tun_src_ip != src_ip)) return FALSE; - if (stream->dst_ip != odp_be_to_cpu_32(ip->dst_addr)) + if ((stream->dst_ip != dst_ip) && stream->input.entry && + (stream->input.entry->tun_dst_ip != dst_ip)) return FALSE; /* Find IPsec headers if any and compare against entry */ hdr_len = locate_ipsec_headers(ip, &ah, &esp); + + /* Cleartext packet */ + if (!ah && !esp) + goto clear_packet; if (ah) { if (!entry) return FALSE; @@ -446,12 +508,21 @@ odp_bool_t verify_ipv4_packet(stream_db_entry_t *stream, ip->proto = esp_t->next_header; } - /* Verify ICMP packet */ - if (ODPH_IPPROTO_ICMP != ip->proto) - return FALSE; +clear_packet: + /* Verify IP/ICMP packet */ + if (entry && (entry->mode == IPSEC_SA_MODE_TUNNEL) && (ah || esp)) { + if (ODPH_IPV4 != ip->proto) + return FALSE; + odph_ipv4hdr_t *inner_ip = (odph_ipv4hdr_t *)data; + icmp = (odph_icmphdr_t *)(inner_ip + 1); + data = (uint8_t *)icmp; + } else { + if (ODPH_IPPROTO_ICMP != ip->proto) + return FALSE; + icmp = (odph_icmphdr_t *)data; + } /* Verify ICMP header */ - icmp = (odph_icmphdr_t *)data; data += sizeof(*icmp); if (ICMP_ECHO != icmp->type) return FALSE;