diff options
Diffstat (limited to 'src/vnet/ipsec/ikev2_payload.c')
-rw-r--r-- | src/vnet/ipsec/ikev2_payload.c | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/src/vnet/ipsec/ikev2_payload.c b/src/vnet/ipsec/ikev2_payload.c new file mode 100644 index 00000000..34595380 --- /dev/null +++ b/src/vnet/ipsec/ikev2_payload.c @@ -0,0 +1,550 @@ +/* + * 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 <ctype.h> + +#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> + +/* *INDENT-OFF* */ +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; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +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; +/* *INDENT-OFF* */ + +/* *INDENT-OFF* */ +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; +/* *INDENT-OFF* */ + +/* *INDENT-OFF* */ +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; +/* *INDENT-OFF* */ + +/* *INDENT-OFF* */ +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; +/* *INDENT-OFF* */ + +/* *INDENT-OFF* */ +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; +/* *INDENT-OFF* */ + +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) +{ + ikev2_payload_add_notify_2(c, msg_type, data, 0); +} + +void +ikev2_payload_add_notify_2 (ikev2_payload_chain_t * c, u16 msg_type, + u8 * data, ikev2_notify_t * notify) +{ + 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); + if (notify) + { + n->protocol_id = notify->protocol_id; + if (notify->spi) + { + n->spi_size = 4; + } + } + 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) + clib_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); + clib_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) + { + clib_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; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |