diff options
Diffstat (limited to 'vnet/vnet/ipsec/ikev2_payload.c')
-rw-r--r-- | vnet/vnet/ipsec/ikev2_payload.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/vnet/vnet/ipsec/ikev2_payload.c b/vnet/vnet/ipsec/ikev2_payload.c new file mode 100644 index 00000000000..f523fa81cba --- /dev/null +++ b/vnet/vnet/ipsec/ikev2_payload.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2015 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/vnet.h> +#include <vnet/api_errno.h> +#include <vnet/ip/ip.h> +#include <vnet/interface.h> + +#include <vnet/ipsec/ipsec.h> +#include <vnet/ipsec/ikev2.h> +#include <vnet/ipsec/ikev2_priv.h> + +typedef CLIB_PACKED (struct { + u8 nextpayload; + u8 flags; + u16 length; + u8 protocol_id; + u8 spi_size; + u16 msg_type; + u8 payload[0]; +}) ike_notify_payload_header_t; + +typedef CLIB_PACKED (struct { + u8 ts_type; + u8 protocol_id; + u16 selector_len; + u16 start_port; + u16 end_port; + ip4_address_t start_addr; + ip4_address_t end_addr; +}) ikev2_ts_payload_entry_t; + +typedef CLIB_PACKED (struct { + u8 nextpayload; + u8 flags; + u16 length; + u8 num_ts; + u8 reserved[3]; + ikev2_ts_payload_entry_t ts[0]; +}) ike_ts_payload_header_t; + +typedef CLIB_PACKED (struct { + u8 last_or_more; + u8 reserved; + u16 proposal_len; + u8 proposal_num; + u8 protocol_id; + u8 spi_size; + u8 num_transforms; + u32 spi[0]; +}) ike_sa_proposal_data_t; + +typedef CLIB_PACKED (struct { + u8 last_or_more; + u8 reserved; + u16 transform_len; + u8 transform_type; + u8 reserved2; + u16 transform_id; + u8 attributes[0]; +}) ike_sa_transform_data_t; + +typedef CLIB_PACKED (struct { + u8 nextpayload; + u8 flags; + u16 length; + u8 protocol_id; + u8 spi_size; + u16 num_of_spi; + u32 spi[0]; +}) ike_delete_payload_header_t; + +static ike_payload_header_t * +ikev2_payload_add_hdr(ikev2_payload_chain_t * c, u8 payload_type, int len) +{ + ike_payload_header_t * hdr = (ike_payload_header_t *) &c->data[c->last_hdr_off]; + u8 * tmp; + + if (c->data) + hdr->nextpayload = payload_type; + else + c->first_payload_type = payload_type; + + c->last_hdr_off = vec_len(c->data); + vec_add2(c->data, tmp, len); + hdr = (ike_payload_header_t *) tmp; + memset(hdr, 0, len); + + hdr->length = clib_host_to_net_u16(len); + + return hdr; +} + +static void +ikev2_payload_add_data(ikev2_payload_chain_t * c, u8 * data) +{ + u16 len; + ike_payload_header_t * hdr; + + vec_append(c->data, data); + hdr = (ike_payload_header_t *) &c->data[c->last_hdr_off]; + len = clib_net_to_host_u16(hdr->length); + hdr->length = clib_host_to_net_u16(len + vec_len(data)); +} + +void +ikev2_payload_add_notify(ikev2_payload_chain_t * c, u16 msg_type, u8 * data) +{ + ike_notify_payload_header_t * n; + + n = (ike_notify_payload_header_t *) ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_NOTIFY, sizeof (*n)); + n->msg_type = clib_host_to_net_u16(msg_type); + ikev2_payload_add_data(c, data); +} + +void +ikev2_payload_add_sa(ikev2_payload_chain_t * c, ikev2_sa_proposal_t * proposals) +{ + ike_payload_header_t * ph; + ike_sa_proposal_data_t * prop; + ike_sa_transform_data_t * tr; + ikev2_sa_proposal_t * p; + ikev2_sa_transform_t * t; + + u8 * tmp; + u8 * pr_data = 0; + u8 * tr_data = 0; + + ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_SA, sizeof (*ph)); + + vec_foreach(p, proposals) + { + int spi_size = (p->protocol_id == IKEV2_PROTOCOL_ESP) ? 4 : 0; + pr_data = vec_new(u8, sizeof(ike_sa_proposal_data_t) + spi_size); + prop = (ike_sa_proposal_data_t *) pr_data; + prop->last_or_more = proposals - p + 1 < vec_len(proposals) ? 2 : 0; + prop->protocol_id = p->protocol_id; + prop->proposal_num = p->proposal_num; + prop->spi_size = spi_size; + prop->num_transforms = vec_len(p->transforms); + + if (spi_size) + prop->spi[0] = clib_host_to_net_u32(p->spi); + + DBG_PLD("proposal num %u protocol_id %u last_or_more %u spi_size %u%s%U", + prop->proposal_num, prop->protocol_id, prop->last_or_more, + prop->spi_size, prop->spi_size ? " spi_data " : "", + format_hex_bytes, prop->spi, prop->spi_size); + + vec_foreach(t, p->transforms) + { + vec_add2(tr_data, tmp, sizeof(*tr) + vec_len(t->attrs)); + tr = (ike_sa_transform_data_t *) tmp; + tr->last_or_more = ((t - p->transforms) + 1 < vec_len(p->transforms)) ? 3 : 0; + tr->transform_type = t->type; + tr->transform_id = clib_host_to_net_u16(t->transform_id); + tr->transform_len = clib_host_to_net_u16(sizeof(*tr) + vec_len(t->attrs)); + + if (vec_len(t->attrs) > 0) + memcpy(tr->attributes, t->attrs, vec_len(t->attrs)); + + DBG_PLD("transform type %U transform_id %u last_or_more %u attr_size %u%s%U", + format_ikev2_transform_type, tr->transform_type, + t->transform_id, tr->last_or_more, vec_len(t->attrs), + vec_len(t->attrs) ? " attrs " : "", + format_hex_bytes, tr->attributes, vec_len(t->attrs)); + } + + prop->proposal_len = clib_host_to_net_u16(vec_len(tr_data) + vec_len(pr_data)); + ikev2_payload_add_data(c, pr_data); + ikev2_payload_add_data(c, tr_data); + vec_free(pr_data); + vec_free(tr_data); + } +} + +void +ikev2_payload_add_ke(ikev2_payload_chain_t * c, u16 dh_group, u8 * dh_data) +{ + ike_ke_payload_header_t * ke; + ke = (ike_ke_payload_header_t *) ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_KE, + sizeof (*ke)); + + ke->dh_group = clib_host_to_net_u16(dh_group); + ikev2_payload_add_data(c, dh_data); +} + +void +ikev2_payload_add_nonce(ikev2_payload_chain_t * c, u8 * nonce) +{ + ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_NONCE, sizeof (ike_payload_header_t)); + ikev2_payload_add_data(c, nonce); +} + +void +ikev2_payload_add_id(ikev2_payload_chain_t *c, ikev2_id_t * id, u8 type) +{ + ike_id_payload_header_t * idp; + idp = (ike_id_payload_header_t *) ikev2_payload_add_hdr(c, type, sizeof (*idp)); + + idp->id_type = id->type; + ikev2_payload_add_data(c, id->data); +} + +void +ikev2_payload_add_delete(ikev2_payload_chain_t *c, ikev2_delete_t * d) +{ + ike_delete_payload_header_t * dp; + u16 num_of_spi = vec_len(d); + ikev2_delete_t * d2; + dp = (ike_delete_payload_header_t *) ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_DELETE, + sizeof (*dp)); + + if (d[0].protocol_id == IKEV2_PROTOCOL_IKE) + { + dp->protocol_id = 1; + } + else + { + dp->protocol_id = d[0].protocol_id; + dp->spi_size = 4; + dp->num_of_spi = clib_host_to_net_u16(num_of_spi); + vec_foreach(d2, d) + { + u8 * data = vec_new(u8, 4); + u32 spi = clib_host_to_net_u32(d2->spi); + memcpy(data, &spi, 4); + ikev2_payload_add_data(c, data); + vec_free(data); + } + } +} + +void +ikev2_payload_add_auth(ikev2_payload_chain_t *c, ikev2_auth_t * auth) +{ + ike_auth_payload_header_t * ap; + ap = (ike_auth_payload_header_t *) ikev2_payload_add_hdr(c, IKEV2_PAYLOAD_AUTH, + sizeof (*ap)); + + ap->auth_method = auth->method; + ikev2_payload_add_data(c, auth->data); +} + +void +ikev2_payload_add_ts(ikev2_payload_chain_t * c, ikev2_ts_t * ts, u8 type) +{ + ike_ts_payload_header_t * tsh; + ikev2_ts_t *ts2; + u8 * data = 0, * tmp; + + tsh = (ike_ts_payload_header_t *) ikev2_payload_add_hdr(c, type, sizeof (*tsh)); + tsh->num_ts = vec_len(ts); + + vec_foreach(ts2, ts) + { + ASSERT(ts2->ts_type == 7); /*TS_IPV4_ADDR_RANGE */ + ikev2_ts_payload_entry_t * entry; + vec_add2(data, tmp, sizeof(*entry)); + entry = (ikev2_ts_payload_entry_t *) tmp; + entry->ts_type = ts2->ts_type; + entry->protocol_id = ts2->protocol_id; + entry->selector_len = clib_host_to_net_u16(16); + entry->start_port = clib_host_to_net_u16(ts2->start_port); + entry->end_port = clib_host_to_net_u16(ts2->end_port); + entry->start_addr.as_u32 = ts2->start_addr.as_u32; + entry->end_addr.as_u32 = ts2->end_addr.as_u32; + } + + ikev2_payload_add_data(c, data); + vec_free(data); +} + +void +ikev2_payload_chain_add_padding(ikev2_payload_chain_t * c, int bs) +{ + u8 * tmp __attribute__((unused)); + u8 pad_len = (vec_len(c->data) / bs + 1) * bs - vec_len(c->data); + vec_add2(c->data, tmp, pad_len); + c->data[vec_len(c->data)-1] = pad_len - 1; +} + +ikev2_sa_proposal_t * +ikev2_parse_sa_payload(ike_payload_header_t * ikep) +{ + ikev2_sa_proposal_t * v = 0; + ikev2_sa_proposal_t * proposal; + ikev2_sa_transform_t * transform; + + u32 plen = clib_net_to_host_u16(ikep->length); + + ike_sa_proposal_data_t * sap; + int proposal_ptr = 0; + + do + { + sap = (ike_sa_proposal_data_t *) &ikep->payload[proposal_ptr]; + int i; + int transform_ptr; + + DBG_PLD("proposal num %u len %u last_or_more %u id %u " + "spi_size %u num_transforms %u", + sap->proposal_num, clib_net_to_host_u16(sap->proposal_len), + sap->last_or_more, sap->protocol_id, sap->spi_size, + sap->num_transforms); + + /* IKE proposal should not have SPI */ + if (sap->protocol_id == IKEV2_PROTOCOL_IKE && sap->spi_size != 0) + goto data_corrupted; + + /* IKE proposal should not have SPI */ + if (sap->protocol_id == IKEV2_PROTOCOL_ESP && sap->spi_size != 4) + goto data_corrupted; + + transform_ptr = proposal_ptr + sizeof(*sap) + sap->spi_size; + + vec_add2(v, proposal, 1); + proposal->proposal_num = sap->proposal_num; + proposal->protocol_id = sap->protocol_id; + + if (sap->spi_size == 4) { + proposal->spi = clib_net_to_host_u32(sap->spi[0]); + } + + for(i=0; i< sap->num_transforms; i++) + { + ike_sa_transform_data_t * tr = (ike_sa_transform_data_t *) &ikep->payload[transform_ptr]; + u16 tlen = clib_net_to_host_u16(tr->transform_len); + + if (tlen < sizeof(*tr)) + goto data_corrupted; + + vec_add2(proposal->transforms, transform, 1); + + transform->type = tr->transform_type; + transform->transform_id = clib_net_to_host_u16(tr->transform_id); + if (tlen > sizeof(*tr)) + vec_add(transform->attrs, tr->attributes, tlen - sizeof(*tr)); + + DBG_PLD("transform num %u len %u last_or_more %u type %U id %u%s%U", + i, tlen, tr->last_or_more, + format_ikev2_sa_transform, transform, + clib_net_to_host_u16(tr->transform_id), + tlen > sizeof(*tr) ? " attrs " : "", + format_hex_bytes, tr->attributes, tlen - sizeof (*tr)); + + transform_ptr += tlen; + } + + proposal_ptr += clib_net_to_host_u16(sap->proposal_len); + } + while (proposal_ptr < (plen - sizeof(*ikep)) && sap->last_or_more == 2); + + /* data validation */ + if (proposal_ptr != (plen - sizeof(*ikep)) || sap->last_or_more) + goto data_corrupted; + + return v; + +data_corrupted: + DBG_PLD("SA payload data corrupted"); + ikev2_sa_free_proposal_vector(&v); + return 0; +} + +ikev2_ts_t * +ikev2_parse_ts_payload(ike_payload_header_t * ikep) +{ + ike_ts_payload_header_t * tsp = (ike_ts_payload_header_t *) ikep; + ikev2_ts_t * r = 0, *ts; + u8 i; + + for (i = 0; i < tsp->num_ts; i++) + { + if (tsp->ts[i].ts_type != 7) /* TS_IPV4_ADDR_RANGE */ + { + DBG_PLD("unsupported TS type received (%u)", tsp->ts[i].ts_type); + continue; + } + + vec_add2(r, ts, 1); + ts->ts_type = tsp->ts[i].ts_type; + ts->protocol_id = tsp->ts[i].protocol_id; + ts->start_port = tsp->ts[i].start_port; + ts->end_port = tsp->ts[i].end_port; + ts->start_addr.as_u32 = tsp->ts[i].start_addr.as_u32; + ts->end_addr.as_u32 = tsp->ts[i].end_addr.as_u32; + } + return r; +} + +ikev2_notify_t * +ikev2_parse_notify_payload(ike_payload_header_t * ikep) +{ + ike_notify_payload_header_t * n = (ike_notify_payload_header_t *) ikep; + u32 plen = clib_net_to_host_u16(ikep->length); + ikev2_notify_t * r = 0; + u32 spi; + + DBG_PLD("msg_type %U len %u%s%U", + format_ikev2_notify_msg_type, clib_net_to_host_u16(n->msg_type), + plen, plen > sizeof(*n) ? " data ":"", + format_hex_bytes, n->payload, plen - sizeof(*n)); + + r = vec_new(ikev2_notify_t, 1); + r->msg_type = clib_net_to_host_u16(n->msg_type); + r->protocol_id = n->protocol_id; + + if (n->spi_size == 4) + { + memcpy(&spi, n->payload, n->spi_size); + r->spi = clib_net_to_host_u32(spi); + DBG_PLD("spi %lx", r->spi); + } + else if (n->spi_size == 0) + { + r->spi = 0; + } + else + { + clib_warning("invalid SPI Size %d", n->spi_size); + } + + if (plen > (sizeof(*n) + n->spi_size)) + { + vec_add(r->data, n->payload + n->spi_size, plen - sizeof(*n) - n->spi_size); + } + + return r; +} + +void +ikev2_parse_vendor_payload(ike_payload_header_t * ikep) +{ + u32 plen = clib_net_to_host_u16(ikep->length); + int i; + int is_string = 1; + + for(i=0; i < plen - 4; i++) + if (!isprint(ikep->payload[i])) + is_string = 0; + + DBG_PLD("len %u data %s:%U", + plen, + is_string ? "string":"hex", + is_string ? format_ascii_bytes : format_hex_bytes, + ikep->payload, plen - sizeof(*ikep)); +} + +ikev2_delete_t * +ikev2_parse_delete_payload(ike_payload_header_t * ikep) +{ + ike_delete_payload_header_t * d = (ike_delete_payload_header_t *) ikep; + u32 plen = clib_net_to_host_u16(ikep->length); + ikev2_delete_t * r = 0, * del; + u16 num_of_spi = clib_net_to_host_u16(d->num_of_spi); + u16 i = 0; + + DBG_PLD("protocol_id %u spi_size %u num_of_spi %u len %u%s%U", + d->protocol_id, d->spi_size, num_of_spi, + plen, plen > sizeof(d) ? " data ":"", + format_hex_bytes, d->spi, plen - sizeof(*d)); + + if (d->protocol_id == IKEV2_PROTOCOL_IKE) + { + r = vec_new(ikev2_delete_t, 1); + r->protocol_id = 1; + } + else + { + r = vec_new(ikev2_delete_t, num_of_spi); + vec_foreach(del, r) + { + del->protocol_id = d->protocol_id; + del->spi = clib_net_to_host_u32(d->spi[i++]); + } + } + + return r; +} |