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