buffer chain linearization
[vpp.git] / src / vnet / ipsec / ikev2_payload.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <ctype.h>
17
18 #include <vnet/vnet.h>
19 #include <vnet/api_errno.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/interface.h>
22
23 #include <vnet/ipsec/ipsec.h>
24 #include <vnet/ipsec/ikev2.h>
25 #include <vnet/ipsec/ikev2_priv.h>
26
27 /* *INDENT-OFF* */
28 typedef CLIB_PACKED (struct
29                      {
30                      u8 nextpayload;
31                      u8 flags;
32                      u16 length;
33                      u8 protocol_id;
34                      u8 spi_size;
35                      u16 msg_type;
36                      u8 payload[0];}) ike_notify_payload_header_t;
37 /* *INDENT-ON* */
38
39 /* *INDENT-OFF* */
40 typedef CLIB_PACKED (struct
41                      {
42                      u8 ts_type;
43                      u8 protocol_id;
44                      u16 selector_len;
45                      u16 start_port;
46                      u16 end_port;
47                      ip4_address_t start_addr;
48                      ip4_address_t end_addr;}) ikev2_ts_payload_entry_t;
49 /* *INDENT-OFF* */
50
51 /* *INDENT-OFF* */
52 typedef CLIB_PACKED (struct
53                      {
54                      u8 nextpayload;
55                      u8 flags;
56                      u16 length;
57                      u8 num_ts;
58                      u8 reserved[3];
59                      ikev2_ts_payload_entry_t ts[0];})
60   ike_ts_payload_header_t;
61 /* *INDENT-OFF* */
62
63 /* *INDENT-OFF* */
64 typedef CLIB_PACKED (struct {
65   u8 last_or_more;
66   u8 reserved;
67   u16 proposal_len;
68   u8 proposal_num;
69   u8 protocol_id;
70   u8 spi_size;
71   u8 num_transforms; u32 spi[0];
72 }) ike_sa_proposal_data_t;
73 /* *INDENT-OFF* */
74
75 /* *INDENT-OFF* */
76 typedef CLIB_PACKED (struct {
77   u8 last_or_more;
78   u8 reserved;
79   u16 transform_len;
80   u8 transform_type;
81   u8 reserved2;
82   u16 transform_id;
83   u8 attributes[0];
84 }) ike_sa_transform_data_t;
85 /* *INDENT-OFF* */
86
87 /* *INDENT-OFF* */
88 typedef CLIB_PACKED (struct {
89   u8 nextpayload;
90   u8 flags;
91   u16 length;
92   u8 protocol_id;
93   u8 spi_size;
94   u16 num_of_spi;
95   u32 spi[0];
96 }) ike_delete_payload_header_t;
97 /* *INDENT-OFF* */
98
99 static ike_payload_header_t *
100 ikev2_payload_add_hdr (ikev2_payload_chain_t * c, u8 payload_type, int len)
101 {
102   ike_payload_header_t *hdr =
103     (ike_payload_header_t *) & c->data[c->last_hdr_off];
104   u8 *tmp;
105
106   if (c->data)
107     hdr->nextpayload = payload_type;
108   else
109     c->first_payload_type = payload_type;
110
111   c->last_hdr_off = vec_len (c->data);
112   vec_add2 (c->data, tmp, len);
113   hdr = (ike_payload_header_t *) tmp;
114   clib_memset (hdr, 0, len);
115
116   hdr->length = clib_host_to_net_u16 (len);
117
118   return hdr;
119 }
120
121 static void
122 ikev2_payload_add_data (ikev2_payload_chain_t * c, u8 * data)
123 {
124   u16 len;
125   ike_payload_header_t *hdr;
126
127   vec_append (c->data, data);
128   hdr = (ike_payload_header_t *) & c->data[c->last_hdr_off];
129   len = clib_net_to_host_u16 (hdr->length);
130   hdr->length = clib_host_to_net_u16 (len + vec_len (data));
131 }
132
133 void
134 ikev2_payload_add_notify (ikev2_payload_chain_t * c, u16 msg_type, u8 * data)
135 {
136   ikev2_payload_add_notify_2(c, msg_type, data, 0);
137 }
138
139 void
140 ikev2_payload_add_notify_2 (ikev2_payload_chain_t * c, u16 msg_type,
141                                u8 * data, ikev2_notify_t * notify)
142 {
143   ike_notify_payload_header_t *n;
144
145   n =
146     (ike_notify_payload_header_t *) ikev2_payload_add_hdr (c,
147                                                            IKEV2_PAYLOAD_NOTIFY,
148                                                            sizeof (*n));
149   n->msg_type = clib_host_to_net_u16 (msg_type);
150   if (notify)
151     {
152       n->protocol_id = notify->protocol_id;
153       if (notify->spi)
154         {
155           n->spi_size = 4;
156         }
157     }
158   ikev2_payload_add_data (c, data);
159 }
160
161 void
162 ikev2_payload_add_sa (ikev2_payload_chain_t * c,
163                       ikev2_sa_proposal_t * proposals)
164 {
165   ike_payload_header_t *ph;
166   ike_sa_proposal_data_t *prop;
167   ike_sa_transform_data_t *tr;
168   ikev2_sa_proposal_t *p;
169   ikev2_sa_transform_t *t;
170
171   u8 *tmp;
172   u8 *pr_data = 0;
173   u8 *tr_data = 0;
174
175   ikev2_payload_add_hdr (c, IKEV2_PAYLOAD_SA, sizeof (*ph));
176
177   vec_foreach (p, proposals)
178   {
179     int spi_size = (p->protocol_id == IKEV2_PROTOCOL_ESP) ? 4 : 0;
180     pr_data = vec_new (u8, sizeof (ike_sa_proposal_data_t) + spi_size);
181     prop = (ike_sa_proposal_data_t *) pr_data;
182     prop->last_or_more = proposals - p + 1 < vec_len (proposals) ? 2 : 0;
183     prop->protocol_id = p->protocol_id;
184     prop->proposal_num = p->proposal_num;
185     prop->spi_size = spi_size;
186     prop->num_transforms = vec_len (p->transforms);
187
188     if (spi_size)
189       prop->spi[0] = clib_host_to_net_u32 (p->spi);
190
191     DBG_PLD ("proposal num %u protocol_id %u last_or_more %u spi_size %u%s%U",
192              prop->proposal_num, prop->protocol_id, prop->last_or_more,
193              prop->spi_size, prop->spi_size ? " spi_data " : "",
194              format_hex_bytes, prop->spi, prop->spi_size);
195
196     vec_foreach (t, p->transforms)
197     {
198       vec_add2 (tr_data, tmp, sizeof (*tr) + vec_len (t->attrs));
199       tr = (ike_sa_transform_data_t *) tmp;
200       tr->last_or_more =
201         ((t - p->transforms) + 1 < vec_len (p->transforms)) ? 3 : 0;
202       tr->transform_type = t->type;
203       tr->transform_id = clib_host_to_net_u16 (t->transform_id);
204       tr->transform_len =
205         clib_host_to_net_u16 (sizeof (*tr) + vec_len (t->attrs));
206
207       if (vec_len (t->attrs) > 0)
208         clib_memcpy_fast (tr->attributes, t->attrs, vec_len (t->attrs));
209
210       DBG_PLD
211         ("transform type %U transform_id %u last_or_more %u attr_size %u%s%U",
212          format_ikev2_transform_type, tr->transform_type, t->transform_id,
213          tr->last_or_more, vec_len (t->attrs),
214          vec_len (t->attrs) ? " attrs " : "", format_hex_bytes,
215          tr->attributes, vec_len (t->attrs));
216     }
217
218     prop->proposal_len =
219       clib_host_to_net_u16 (vec_len (tr_data) + vec_len (pr_data));
220     ikev2_payload_add_data (c, pr_data);
221     ikev2_payload_add_data (c, tr_data);
222     vec_free (pr_data);
223     vec_free (tr_data);
224   }
225 }
226
227 void
228 ikev2_payload_add_ke (ikev2_payload_chain_t * c, u16 dh_group, u8 * dh_data)
229 {
230   ike_ke_payload_header_t *ke;
231   ke = (ike_ke_payload_header_t *) ikev2_payload_add_hdr (c, IKEV2_PAYLOAD_KE,
232                                                           sizeof (*ke));
233
234   ke->dh_group = clib_host_to_net_u16 (dh_group);
235   ikev2_payload_add_data (c, dh_data);
236 }
237
238 void
239 ikev2_payload_add_nonce (ikev2_payload_chain_t * c, u8 * nonce)
240 {
241   ikev2_payload_add_hdr (c, IKEV2_PAYLOAD_NONCE,
242                          sizeof (ike_payload_header_t));
243   ikev2_payload_add_data (c, nonce);
244 }
245
246 void
247 ikev2_payload_add_id (ikev2_payload_chain_t * c, ikev2_id_t * id, u8 type)
248 {
249   ike_id_payload_header_t *idp;
250   idp =
251     (ike_id_payload_header_t *) ikev2_payload_add_hdr (c, type,
252                                                        sizeof (*idp));
253
254   idp->id_type = id->type;
255   ikev2_payload_add_data (c, id->data);
256 }
257
258 void
259 ikev2_payload_add_delete (ikev2_payload_chain_t * c, ikev2_delete_t * d)
260 {
261   ike_delete_payload_header_t *dp;
262   u16 num_of_spi = vec_len (d);
263   ikev2_delete_t *d2;
264   dp =
265     (ike_delete_payload_header_t *) ikev2_payload_add_hdr (c,
266                                                            IKEV2_PAYLOAD_DELETE,
267                                                            sizeof (*dp));
268
269   if (d[0].protocol_id == IKEV2_PROTOCOL_IKE)
270     {
271       dp->protocol_id = 1;
272     }
273   else
274     {
275       dp->protocol_id = d[0].protocol_id;
276       dp->spi_size = 4;
277       dp->num_of_spi = clib_host_to_net_u16 (num_of_spi);
278       vec_foreach (d2, d)
279       {
280         u8 *data = vec_new (u8, 4);
281         u32 spi = clib_host_to_net_u32 (d2->spi);
282         clib_memcpy (data, &spi, 4);
283         ikev2_payload_add_data (c, data);
284         vec_free (data);
285       }
286     }
287 }
288
289 void
290 ikev2_payload_add_auth (ikev2_payload_chain_t * c, ikev2_auth_t * auth)
291 {
292   ike_auth_payload_header_t *ap;
293   ap =
294     (ike_auth_payload_header_t *) ikev2_payload_add_hdr (c,
295                                                          IKEV2_PAYLOAD_AUTH,
296                                                          sizeof (*ap));
297
298   ap->auth_method = auth->method;
299   ikev2_payload_add_data (c, auth->data);
300 }
301
302 void
303 ikev2_payload_add_ts (ikev2_payload_chain_t * c, ikev2_ts_t * ts, u8 type)
304 {
305   ike_ts_payload_header_t *tsh;
306   ikev2_ts_t *ts2;
307   u8 *data = 0, *tmp;
308
309   tsh =
310     (ike_ts_payload_header_t *) ikev2_payload_add_hdr (c, type,
311                                                        sizeof (*tsh));
312   tsh->num_ts = vec_len (ts);
313
314   vec_foreach (ts2, ts)
315   {
316     ASSERT (ts2->ts_type == 7); /*TS_IPV4_ADDR_RANGE */
317     ikev2_ts_payload_entry_t *entry;
318     vec_add2 (data, tmp, sizeof (*entry));
319     entry = (ikev2_ts_payload_entry_t *) tmp;
320     entry->ts_type = ts2->ts_type;
321     entry->protocol_id = ts2->protocol_id;
322     entry->selector_len = clib_host_to_net_u16 (16);
323     entry->start_port = clib_host_to_net_u16 (ts2->start_port);
324     entry->end_port = clib_host_to_net_u16 (ts2->end_port);
325     entry->start_addr.as_u32 = ts2->start_addr.as_u32;
326     entry->end_addr.as_u32 = ts2->end_addr.as_u32;
327   }
328
329   ikev2_payload_add_data (c, data);
330   vec_free (data);
331 }
332
333 void
334 ikev2_payload_chain_add_padding (ikev2_payload_chain_t * c, int bs)
335 {
336   u8 *tmp __attribute__ ((unused));
337   u8 pad_len = (vec_len (c->data) / bs + 1) * bs - vec_len (c->data);
338   vec_add2 (c->data, tmp, pad_len);
339   c->data[vec_len (c->data) - 1] = pad_len - 1;
340 }
341
342 ikev2_sa_proposal_t *
343 ikev2_parse_sa_payload (ike_payload_header_t * ikep)
344 {
345   ikev2_sa_proposal_t *v = 0;
346   ikev2_sa_proposal_t *proposal;
347   ikev2_sa_transform_t *transform;
348
349   u32 plen = clib_net_to_host_u16 (ikep->length);
350
351   ike_sa_proposal_data_t *sap;
352   int proposal_ptr = 0;
353
354   do
355     {
356       sap = (ike_sa_proposal_data_t *) & ikep->payload[proposal_ptr];
357       int i;
358       int transform_ptr;
359
360       DBG_PLD ("proposal num %u len %u last_or_more %u id %u "
361                "spi_size %u num_transforms %u",
362                sap->proposal_num, clib_net_to_host_u16 (sap->proposal_len),
363                sap->last_or_more, sap->protocol_id, sap->spi_size,
364                sap->num_transforms);
365
366       /* IKE proposal should not have SPI */
367       if (sap->protocol_id == IKEV2_PROTOCOL_IKE && sap->spi_size != 0)
368         goto data_corrupted;
369
370       /* IKE proposal should not have SPI */
371       if (sap->protocol_id == IKEV2_PROTOCOL_ESP && sap->spi_size != 4)
372         goto data_corrupted;
373
374       transform_ptr = proposal_ptr + sizeof (*sap) + sap->spi_size;
375
376       vec_add2 (v, proposal, 1);
377       proposal->proposal_num = sap->proposal_num;
378       proposal->protocol_id = sap->protocol_id;
379
380       if (sap->spi_size == 4)
381         {
382           proposal->spi = clib_net_to_host_u32 (sap->spi[0]);
383         }
384
385       for (i = 0; i < sap->num_transforms; i++)
386         {
387           ike_sa_transform_data_t *tr =
388             (ike_sa_transform_data_t *) & ikep->payload[transform_ptr];
389           u16 tlen = clib_net_to_host_u16 (tr->transform_len);
390
391           if (tlen < sizeof (*tr))
392             goto data_corrupted;
393
394           vec_add2 (proposal->transforms, transform, 1);
395
396           transform->type = tr->transform_type;
397           transform->transform_id = clib_net_to_host_u16 (tr->transform_id);
398           if (tlen > sizeof (*tr))
399             vec_add (transform->attrs, tr->attributes, tlen - sizeof (*tr));
400
401           DBG_PLD
402             ("transform num %u len %u last_or_more %u type %U id %u%s%U", i,
403              tlen, tr->last_or_more, format_ikev2_sa_transform, transform,
404              clib_net_to_host_u16 (tr->transform_id),
405              tlen > sizeof (*tr) ? " attrs " : "", format_hex_bytes,
406              tr->attributes, tlen - sizeof (*tr));
407
408           transform_ptr += tlen;
409         }
410
411       proposal_ptr += clib_net_to_host_u16 (sap->proposal_len);
412     }
413   while (proposal_ptr < (plen - sizeof (*ikep)) && sap->last_or_more == 2);
414
415   /* data validation */
416   if (proposal_ptr != (plen - sizeof (*ikep)) || sap->last_or_more)
417     goto data_corrupted;
418
419   return v;
420
421 data_corrupted:
422   DBG_PLD ("SA payload data corrupted");
423   ikev2_sa_free_proposal_vector (&v);
424   return 0;
425 }
426
427 ikev2_ts_t *
428 ikev2_parse_ts_payload (ike_payload_header_t * ikep)
429 {
430   ike_ts_payload_header_t *tsp = (ike_ts_payload_header_t *) ikep;
431   ikev2_ts_t *r = 0, *ts;
432   u8 i;
433
434   for (i = 0; i < tsp->num_ts; i++)
435     {
436       if (tsp->ts[i].ts_type != 7)      /*  TS_IPV4_ADDR_RANGE */
437         {
438           DBG_PLD ("unsupported TS type received (%u)", tsp->ts[i].ts_type);
439           continue;
440         }
441
442       vec_add2 (r, ts, 1);
443       ts->ts_type = tsp->ts[i].ts_type;
444       ts->protocol_id = tsp->ts[i].protocol_id;
445       ts->start_port = tsp->ts[i].start_port;
446       ts->end_port = tsp->ts[i].end_port;
447       ts->start_addr.as_u32 = tsp->ts[i].start_addr.as_u32;
448       ts->end_addr.as_u32 = tsp->ts[i].end_addr.as_u32;
449     }
450   return r;
451 }
452
453 ikev2_notify_t *
454 ikev2_parse_notify_payload (ike_payload_header_t * ikep)
455 {
456   ike_notify_payload_header_t *n = (ike_notify_payload_header_t *) ikep;
457   u32 plen = clib_net_to_host_u16 (ikep->length);
458   ikev2_notify_t *r = 0;
459   u32 spi;
460
461   DBG_PLD ("msg_type %U len %u%s%U",
462            format_ikev2_notify_msg_type, clib_net_to_host_u16 (n->msg_type),
463            plen, plen > sizeof (*n) ? " data " : "",
464            format_hex_bytes, n->payload, plen - sizeof (*n));
465
466   r = vec_new (ikev2_notify_t, 1);
467   r->msg_type = clib_net_to_host_u16 (n->msg_type);
468   r->protocol_id = n->protocol_id;
469
470   if (n->spi_size == 4)
471     {
472       clib_memcpy (&spi, n->payload, n->spi_size);
473       r->spi = clib_net_to_host_u32 (spi);
474       DBG_PLD ("spi %lx", r->spi);
475     }
476   else if (n->spi_size == 0)
477     {
478       r->spi = 0;
479     }
480   else
481     {
482       clib_warning ("invalid SPI Size %d", n->spi_size);
483     }
484
485   if (plen > (sizeof (*n) + n->spi_size))
486     {
487       vec_add (r->data, n->payload + n->spi_size,
488                plen - sizeof (*n) - n->spi_size);
489     }
490
491   return r;
492 }
493
494 void
495 ikev2_parse_vendor_payload (ike_payload_header_t * ikep)
496 {
497   u32 plen = clib_net_to_host_u16 (ikep->length);
498   int i;
499   int is_string = 1;
500
501   for (i = 0; i < plen - 4; i++)
502     if (!isprint (ikep->payload[i]))
503       is_string = 0;
504
505   DBG_PLD ("len %u data %s:%U",
506            plen,
507            is_string ? "string" : "hex",
508            is_string ? format_ascii_bytes : format_hex_bytes,
509            ikep->payload, plen - sizeof (*ikep));
510 }
511
512 ikev2_delete_t *
513 ikev2_parse_delete_payload (ike_payload_header_t * ikep)
514 {
515   ike_delete_payload_header_t *d = (ike_delete_payload_header_t *) ikep;
516   u32 plen = clib_net_to_host_u16 (ikep->length);
517   ikev2_delete_t *r = 0, *del;
518   u16 num_of_spi = clib_net_to_host_u16 (d->num_of_spi);
519   u16 i = 0;
520
521   DBG_PLD ("protocol_id %u spi_size %u num_of_spi %u len %u%s%U",
522            d->protocol_id, d->spi_size, num_of_spi,
523            plen, plen > sizeof (d) ? " data " : "",
524            format_hex_bytes, d->spi, plen - sizeof (*d));
525
526   if (d->protocol_id == IKEV2_PROTOCOL_IKE)
527     {
528       r = vec_new (ikev2_delete_t, 1);
529       r->protocol_id = 1;
530     }
531   else
532     {
533       r = vec_new (ikev2_delete_t, num_of_spi);
534       vec_foreach (del, r)
535       {
536         del->protocol_id = d->protocol_id;
537         del->spi = clib_net_to_host_u32 (d->spi[i++]);
538       }
539     }
540
541   return r;
542 }
543
544 /*
545  * fd.io coding-style-patch-verification: ON
546  *
547  * Local Variables:
548  * eval: (c-set-style "gnu")
549  * End:
550  */