1/*
2 * IS-IS Rout(e)ing protocol - isis_tlv.c
3 *                             IS-IS TLV related routines
4 *
5 * Copyright (C) 2001,2002   Sampo Saaristo
6 *                           Tampere University of Technology
7 *                           Institute of Communications Engineering
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public Licenseas published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17 * more details.
18
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24#include <zebra.h>
25
26#include "log.h"
27#include "linklist.h"
28#include "stream.h"
29#include "memory.h"
30#include "prefix.h"
31#include "vty.h"
32#include "if.h"
33
34#include "isisd/dict.h"
35#include "isisd/isis_constants.h"
36#include "isisd/isis_common.h"
37#include "isisd/isis_flags.h"
38#include "isisd/isis_circuit.h"
39#include "isisd/isis_tlv.h"
40#include "isisd/isisd.h"
41#include "isisd/isis_dynhn.h"
42#include "isisd/isis_misc.h"
43#include "isisd/isis_pdu.h"
44#include "isisd/isis_lsp.h"
45
46void
47free_tlv (void *val)
48{
49  XFREE (MTYPE_ISIS_TLV, val);
50
51  return;
52}
53
54/*
55 * Called after parsing of a PDU. There shouldn't be any tlv's left, so this
56 * is only a caution to avoid memory leaks
57 */
58void
59free_tlvs (struct tlvs *tlvs)
60{
61  if (tlvs->area_addrs)
62    list_delete (tlvs->area_addrs);
63  if (tlvs->is_neighs)
64    list_delete (tlvs->is_neighs);
65  if (tlvs->te_is_neighs)
66    list_delete (tlvs->te_is_neighs);
67  if (tlvs->es_neighs)
68    list_delete (tlvs->es_neighs);
69  if (tlvs->lsp_entries)
70    list_delete (tlvs->lsp_entries);
71  if (tlvs->prefix_neighs)
72    list_delete (tlvs->prefix_neighs);
73  if (tlvs->lan_neighs)
74    list_delete (tlvs->lan_neighs);
75  if (tlvs->ipv4_addrs)
76    list_delete (tlvs->ipv4_addrs);
77  if (tlvs->ipv4_int_reachs)
78    list_delete (tlvs->ipv4_int_reachs);
79  if (tlvs->ipv4_ext_reachs)
80    list_delete (tlvs->ipv4_ext_reachs);
81  if (tlvs->te_ipv4_reachs)
82    list_delete (tlvs->te_ipv4_reachs);
83#ifdef HAVE_IPV6
84  if (tlvs->ipv6_addrs)
85    list_delete (tlvs->ipv6_addrs);
86  if (tlvs->ipv6_reachs)
87    list_delete (tlvs->ipv6_reachs);
88#endif /* HAVE_IPV6 */
89
90  memset (tlvs, 0, sizeof (struct tlvs));
91
92  return;
93}
94
95/*
96 * Parses the tlvs found in the variant length part of the PDU.
97 * Caller tells with flags in "expected" which TLV's it is interested in.
98 */
99int
100parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected,
101	    u_int32_t * found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset)
102{
103  u_char type, length;
104  struct lan_neigh *lan_nei;
105  struct area_addr *area_addr;
106  struct is_neigh *is_nei;
107  struct te_is_neigh *te_is_nei;
108  struct es_neigh *es_nei;
109  struct lsp_entry *lsp_entry;
110  struct in_addr *ipv4_addr;
111  struct ipv4_reachability *ipv4_reach;
112  struct te_ipv4_reachability *te_ipv4_reach;
113#ifdef HAVE_IPV6
114  struct in6_addr *ipv6_addr;
115  struct ipv6_reachability *ipv6_reach;
116  int prefix_octets;
117#endif /* HAVE_IPV6 */
118  u_char virtual;
119  int value_len, retval = ISIS_OK;
120  u_char *start = stream, *pnt = stream, *endpnt;
121
122  *found = 0;
123  memset (tlvs, 0, sizeof (struct tlvs));
124
125  while (pnt < stream + size - 2)
126    {
127      type = *pnt;
128      length = *(pnt + 1);
129      pnt += 2;
130      value_len = 0;
131      if (pnt + length > stream + size)
132	{
133	  zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) exceeds packet "
134		     "boundaries", areatag, type, length);
135	  retval = ISIS_WARNING;
136	  break;
137	}
138      switch (type)
139	{
140	case AREA_ADDRESSES:
141	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
142	   * |                        Address Length                         |
143	   * +-------+-------+-------+-------+-------+-------+-------+-------+
144	   * |                         Area Address                          |
145	   * +-------+-------+-------+-------+-------+-------+-------+-------+
146	   * :                                                               :
147	   */
148	  *found |= TLVFLAG_AREA_ADDRS;
149#ifdef EXTREME_TLV_DEBUG
150	  zlog_debug ("TLV Area Adresses len %d", length);
151#endif /* EXTREME_TLV_DEBUG */
152	  if (*expected & TLVFLAG_AREA_ADDRS)
153	    {
154	      while (length > value_len)
155		{
156		  area_addr = (struct area_addr *) pnt;
157		  value_len += area_addr->addr_len + 1;
158		  pnt += area_addr->addr_len + 1;
159		  if (!tlvs->area_addrs)
160		    tlvs->area_addrs = list_new ();
161		  listnode_add (tlvs->area_addrs, area_addr);
162		}
163	    }
164	  else
165	    {
166	      pnt += length;
167	    }
168	  break;
169
170	case IS_NEIGHBOURS:
171	  *found |= TLVFLAG_IS_NEIGHS;
172#ifdef EXTREME_TLV_DEBUG
173	  zlog_debug ("ISIS-TLV (%s): IS Neighbours length %d",
174		      areatag, length);
175#endif /* EXTREME_TLV_DEBUG */
176	  if (TLVFLAG_IS_NEIGHS & *expected)
177	    {
178	      /* +-------+-------+-------+-------+-------+-------+-------+-------+
179	       * |                        Virtual Flag                           |
180	       * +-------+-------+-------+-------+-------+-------+-------+-------+
181	       */
182	      virtual = *pnt;	/* FIXME: what is the use for this? */
183	      pnt++;
184	      value_len++;
185	      /* +-------+-------+-------+-------+-------+-------+-------+-------+
186	       * |   0   |  I/E  |               Default Metric                  |
187	       * +-------+-------+-------+-------+-------+-------+-------+-------+
188	       * |   S   |  I/E  |               Delay Metric                    |
189	       * +-------+-------+-------+-------+-------+-------+-------+-------+
190	       * |   S   |  I/E  |               Expense Metric                  |
191	       * +-------+-------+-------+-------+-------+-------+-------+-------+
192	       * |   S   |  I/E  |               Error Metric                    |
193	       * +-------+-------+-------+-------+-------+-------+-------+-------+
194	       * |                        Neighbour ID                           |
195	       * +---------------------------------------------------------------+
196	       * :                                                               :
197	       */
198	      while (length > value_len)
199		{
200		  is_nei = (struct is_neigh *) pnt;
201		  value_len += 4 + ISIS_SYS_ID_LEN + 1;
202		  pnt += 4 + ISIS_SYS_ID_LEN + 1;
203		  if (!tlvs->is_neighs)
204		    tlvs->is_neighs = list_new ();
205		  listnode_add (tlvs->is_neighs, is_nei);
206		}
207	    }
208	  else
209	    {
210	      pnt += length;
211	    }
212	  break;
213
214	case TE_IS_NEIGHBOURS:
215	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
216	   * |                        Neighbour ID                           | 7
217	   * +---------------------------------------------------------------+
218	   * |                        TE Metric                              | 3
219	   * +---------------------------------------------------------------+
220	   * |                        SubTLVs Length                         | 1
221	   * +---------------------------------------------------------------+
222	   * :                                                               :
223	   */
224	  *found |= TLVFLAG_TE_IS_NEIGHS;
225#ifdef EXTREME_TLV_DEBUG
226	  zlog_debug ("ISIS-TLV (%s): Extended IS Neighbours length %d",
227		     areatag, length);
228#endif /* EXTREME_TLV_DEBUG */
229	  if (TLVFLAG_TE_IS_NEIGHS & *expected)
230	    {
231	      while (length > value_len)
232		{
233		  te_is_nei = (struct te_is_neigh *) pnt;
234		  value_len += 11;
235		  pnt += 11;
236		  /* FIXME - subtlvs are handled here, for now we skip */
237		  value_len += te_is_nei->sub_tlvs_length;
238		  pnt += te_is_nei->sub_tlvs_length;
239
240		  if (!tlvs->te_is_neighs)
241		    tlvs->te_is_neighs = list_new ();
242		  listnode_add (tlvs->te_is_neighs, te_is_nei);
243		}
244	    }
245	  else
246	    {
247	      pnt += length;
248	    }
249	  break;
250
251	case ES_NEIGHBOURS:
252	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
253	   * |   0   |  I/E  |               Default Metric                  |
254	   * +-------+-------+-------+-------+-------+-------+-------+-------+
255	   * |   S   |  I/E  |               Delay Metric                    |
256	   * +-------+-------+-------+-------+-------+-------+-------+-------+
257	   * |   S   |  I/E  |               Expense Metric                  |
258	   * +-------+-------+-------+-------+-------+-------+-------+-------+
259	   * |   S   |  I/E  |               Error Metric                    |
260	   * +-------+-------+-------+-------+-------+-------+-------+-------+
261	   * |                        Neighbour ID                           |
262	   * +---------------------------------------------------------------+
263	   * |                        Neighbour ID                           |
264	   * +---------------------------------------------------------------+
265	   * :                                                               :
266	   */
267#ifdef EXTREME_TLV_DEBUG
268	  zlog_debug ("ISIS-TLV (%s): ES Neighbours length %d",
269		     areatag, length);
270#endif /* EXTREME_TLV_DEBUG */
271	  *found |= TLVFLAG_ES_NEIGHS;
272	  if (*expected & TLVFLAG_ES_NEIGHS)
273	    {
274	      es_nei = (struct es_neigh *) pnt;
275	      value_len += 4;
276	      pnt += 4;
277	      while (length > value_len)
278		{
279		  /* FIXME FIXME FIXME - add to the list */
280		  /*          sys_id->id = pnt; */
281		  value_len += ISIS_SYS_ID_LEN;
282		  pnt += ISIS_SYS_ID_LEN;
283		  /*  if (!es_nei->neigh_ids) es_nei->neigh_ids = sysid; */
284		}
285	      if (!tlvs->es_neighs)
286		tlvs->es_neighs = list_new ();
287	      listnode_add (tlvs->es_neighs, es_nei);
288	    }
289	  else
290	    {
291	      pnt += length;
292	    }
293	  break;
294
295	case LAN_NEIGHBOURS:
296	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
297	   * |                        LAN Address                            |
298	   * +-------+-------+-------+-------+-------+-------+-------+-------+
299	   * :                                                               :
300	   */
301	  *found |= TLVFLAG_LAN_NEIGHS;
302#ifdef EXTREME_TLV_DEBUG
303	  zlog_debug ("ISIS-TLV (%s): LAN Neigbours length %d",
304		      areatag, length);
305#endif /* EXTREME_TLV_DEBUG */
306	  if (TLVFLAG_LAN_NEIGHS & *expected)
307	    {
308	      while (length > value_len)
309		{
310		  lan_nei = (struct lan_neigh *) pnt;
311		  if (!tlvs->lan_neighs)
312		    tlvs->lan_neighs = list_new ();
313		  listnode_add (tlvs->lan_neighs, lan_nei);
314		  value_len += ETH_ALEN;
315		  pnt += ETH_ALEN;
316		}
317	    }
318	  else
319	    {
320	      pnt += length;
321	    }
322	  break;
323
324	case PADDING:
325#ifdef EXTREME_TLV_DEBUG
326	  zlog_debug ("TLV padding %d", length);
327#endif /* EXTREME_TLV_DEBUG */
328	  pnt += length;
329	  break;
330
331	case LSP_ENTRIES:
332	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
333	   * |                     Remaining Lifetime                        | 2
334	   * +-------+-------+-------+-------+-------+-------+-------+-------+
335	   * |                         LSP ID                                | id+2
336	   * +-------+-------+-------+-------+-------+-------+-------+-------+
337	   * |                   LSP Sequence Number                         |�4
338	   * +-------+-------+-------+-------+-------+-------+-------+-------+
339	   * |                        Checksum                               | 2
340	   * +-------+-------+-------+-------+-------+-------+-------+-------+
341	   */
342#ifdef EXTREME_TLV_DEBUG
343	  zlog_debug ("ISIS-TLV (%s): LSP Entries length %d", areatag, length);
344#endif /* EXTREME_TLV_DEBUG */
345	  *found |= TLVFLAG_LSP_ENTRIES;
346	  if (TLVFLAG_LSP_ENTRIES & *expected)
347	    {
348	      while (length > value_len)
349		{
350		  lsp_entry = (struct lsp_entry *) pnt;
351		  value_len += 10 + ISIS_SYS_ID_LEN;
352		  pnt += 10 + ISIS_SYS_ID_LEN;
353		  if (!tlvs->lsp_entries)
354		    tlvs->lsp_entries = list_new ();
355		  listnode_add (tlvs->lsp_entries, lsp_entry);
356		}
357	    }
358	  else
359	    {
360	      pnt += length;
361	    }
362	  break;
363
364	case CHECKSUM:
365	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
366	   * |                   16 bit fletcher CHECKSUM                    |
367	   * +-------+-------+-------+-------+-------+-------+-------+-------+
368	   * :                                                               :
369	   */
370	  *found |= TLVFLAG_CHECKSUM;
371#ifdef EXTREME_TLV_DEBUG
372	  zlog_debug ("ISIS-TLV (%s): Checksum length %d", areatag, length);
373#endif /* EXTREME_TLV_DEBUG */
374	  if (*expected & TLVFLAG_CHECKSUM)
375	    {
376	      tlvs->checksum = (struct checksum *) pnt;
377	    }
378	  pnt += length;
379	  break;
380
381	case PROTOCOLS_SUPPORTED:
382	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
383	   * |                       NLPID                                   |
384	   * +-------+-------+-------+-------+-------+-------+-------+-------+
385	   * :                                                               :
386	   */
387	  *found |= TLVFLAG_NLPID;
388#ifdef EXTREME_TLV_DEBUG
389	  zlog_debug ("ISIS-TLV (%s): Protocols Supported length %d",
390		      areatag, length);
391#endif /* EXTREME_TLV_DEBUG */
392	  if (*expected & TLVFLAG_NLPID)
393	    {
394	      tlvs->nlpids = (struct nlpids *) (pnt - 1);
395	    }
396	  pnt += length;
397	  break;
398
399	case IPV4_ADDR:
400	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
401	   * +                 IP version 4 address                          + 4
402	   * +-------+-------+-------+-------+-------+-------+-------+-------+
403	   * :                                                               :
404	   */
405	  *found |= TLVFLAG_IPV4_ADDR;
406#ifdef EXTREME_TLV_DEBUG
407	  zlog_debug ("ISIS-TLV (%s): IPv4 Address length %d",
408		      areatag, length);
409#endif /* EXTREME_TLV_DEBUG */
410	  if (*expected & TLVFLAG_IPV4_ADDR)
411	    {
412	      while (length > value_len)
413		{
414		  ipv4_addr = (struct in_addr *) pnt;
415#ifdef EXTREME_TLV_DEBUG
416		  zlog_debug ("ISIS-TLV (%s) : IP ADDR %s, pnt %p", areatag,
417			      inet_ntoa (*ipv4_addr), pnt);
418#endif /* EXTREME_TLV_DEBUG */
419		  if (!tlvs->ipv4_addrs)
420		    tlvs->ipv4_addrs = list_new ();
421		  listnode_add (tlvs->ipv4_addrs, ipv4_addr);
422		  value_len += 4;
423		  pnt += 4;
424		}
425	    }
426	  else
427	    {
428	      pnt += length;
429	    }
430	  break;
431
432	case AUTH_INFO:
433	  *found |= TLVFLAG_AUTH_INFO;
434#ifdef EXTREME_TLV_DEBUG
435	  zlog_debug ("ISIS-TLV (%s): IS-IS Authentication Information",
436		      areatag);
437#endif
438	  if (*expected & TLVFLAG_AUTH_INFO)
439	    {
440	      tlvs->auth_info.type = *pnt;
441              if (length == 0)
442                {
443                  zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) "
444                             "incorrect.", areatag, type, length);
445                  return ISIS_WARNING;
446                }
447              --length;
448	      tlvs->auth_info.len = length;
449	      pnt++;
450	      memcpy (tlvs->auth_info.passwd, pnt, length);
451              /* Return the authentication tlv pos for later computation
452               * of MD5 (RFC 5304, 2)
453               */
454              if (auth_tlv_offset)
455                *auth_tlv_offset += (pnt - start - 3);
456              pnt += length;
457	    }
458	  else
459	    {
460	      pnt += length;
461	    }
462	  break;
463
464	case DYNAMIC_HOSTNAME:
465	  *found |= TLVFLAG_DYN_HOSTNAME;
466#ifdef EXTREME_TLV_DEBUG
467	  zlog_debug ("ISIS-TLV (%s): Dynamic Hostname length %d",
468		      areatag, length);
469#endif /* EXTREME_TLV_DEBUG */
470	  if (*expected & TLVFLAG_DYN_HOSTNAME)
471	    {
472	      /* the length is also included in the pointed struct */
473	      tlvs->hostname = (struct hostname *) (pnt - 1);
474	    }
475	  pnt += length;
476	  break;
477
478	case TE_ROUTER_ID:
479	  /* +---------------------------------------------------------------+
480	   * +                         Router ID                             + 4
481	   * +---------------------------------------------------------------+
482	   */
483	  *found |= TLVFLAG_TE_ROUTER_ID;
484#ifdef EXTREME_TLV_DEBUG
485	  zlog_debug ("ISIS-TLV (%s): TE Router ID %d", areatag, length);
486#endif /* EXTREME_TLV_DEBUG */
487	  if (*expected & TLVFLAG_TE_ROUTER_ID)
488	    tlvs->router_id = (struct te_router_id *) (pnt);
489	  pnt += length;
490	  break;
491
492	case IPV4_INT_REACHABILITY:
493	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
494	   * |   0   |  I/E  |               Default Metric                  | 1
495	   * +-------+-------+-------+-------+-------+-------+-------+-------+
496	   * |   S   |  I/E  |               Delay Metric                    | 1
497	   * +-------+-------+-------+-------+-------+-------+-------+-------+
498	   * |   S   |  I/E  |               Expense Metric                  | 1
499	   * +-------+-------+-------+-------+-------+-------+-------+-------+
500	   * |   S   |  I/E  |               Error Metric                    | 1
501	   * +-------+-------+-------+-------+-------+-------+-------+-------+
502	   * |                        ip address                             | 4
503	   * +---------------------------------------------------------------+
504	   * |                        address mask                           | 4
505	   * +---------------------------------------------------------------+
506	   * :                                                               :
507	   */
508	  *found |= TLVFLAG_IPV4_INT_REACHABILITY;
509#ifdef EXTREME_TLV_DEBUG
510	  zlog_debug ("ISIS-TLV (%s): IPv4 internal Reachability length %d",
511		      areatag, length);
512#endif /* EXTREME_TLV_DEBUG */
513	  if (*expected & TLVFLAG_IPV4_INT_REACHABILITY)
514	    {
515	      while (length > value_len)
516		{
517		  ipv4_reach = (struct ipv4_reachability *) pnt;
518		  if (!tlvs->ipv4_int_reachs)
519		    tlvs->ipv4_int_reachs = list_new ();
520		  listnode_add (tlvs->ipv4_int_reachs, ipv4_reach);
521		  value_len += 12;
522		  pnt += 12;
523		}
524	    }
525	  else
526	    {
527	      pnt += length;
528	    }
529	  break;
530
531	case IPV4_EXT_REACHABILITY:
532	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
533	   * |   0   |  I/E  |               Default Metric                  | 1
534	   * +-------+-------+-------+-------+-------+-------+-------+-------+
535	   * |   S   |  I/E  |               Delay Metric                    | 1
536	   * +-------+-------+-------+-------+-------+-------+-------+-------+
537	   * |   S   |  I/E  |               Expense Metric                  | 1
538	   * +-------+-------+-------+-------+-------+-------+-------+-------+
539	   * |   S   |  I/E  |               Error Metric                    | 1
540	   * +-------+-------+-------+-------+-------+-------+-------+-------+
541	   * |                        ip address                             | 4
542	   * +---------------------------------------------------------------+
543	   * |                        address mask                           | 4
544	   * +---------------------------------------------------------------+
545	   * :                                                               :
546	   */
547	  *found |= TLVFLAG_IPV4_EXT_REACHABILITY;
548#ifdef EXTREME_TLV_DEBUG
549	  zlog_debug ("ISIS-TLV (%s): IPv4 external Reachability length %d",
550		      areatag, length);
551#endif /* EXTREME_TLV_DEBUG */
552	  if (*expected & TLVFLAG_IPV4_EXT_REACHABILITY)
553	    {
554	      while (length > value_len)
555		{
556		  ipv4_reach = (struct ipv4_reachability *) pnt;
557		  if (!tlvs->ipv4_ext_reachs)
558		    tlvs->ipv4_ext_reachs = list_new ();
559		  listnode_add (tlvs->ipv4_ext_reachs, ipv4_reach);
560		  value_len += 12;
561		  pnt += 12;
562		}
563	    }
564	  else
565	    {
566	      pnt += length;
567	    }
568	  break;
569
570	case TE_IPV4_REACHABILITY:
571	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
572	   * |                        TE Metric                              | 4
573	   * +-------+-------+-------+-------+-------+-------+-------+-------+
574	   * |  U/D  | sTLV? |               Prefix Mask Len                 | 1
575	   * +-------+-------+-------+-------+-------+-------+-------+-------+
576	   * |                           Prefix                              | 0-4
577	   * +---------------------------------------------------------------+
578	   * |                         sub tlvs                              |
579	   * +---------------------------------------------------------------+
580	   * :                                                               :
581	   */
582	  *found |= TLVFLAG_TE_IPV4_REACHABILITY;
583#ifdef EXTREME_TLV_DEBUG
584	  zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d",
585		      areatag, length);
586#endif /* EXTREME_TLV_DEBUG */
587	  endpnt = pnt + length;
588	  if (*expected & TLVFLAG_TE_IPV4_REACHABILITY)
589	    {
590	      while (length > value_len)
591		{
592		  te_ipv4_reach = (struct te_ipv4_reachability *) pnt;
593		  if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN)
594		    {
595		      zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach"
596				 "ability prefix length %d", areatag,
597				 te_ipv4_reach->control & 0x3F);
598		      retval = ISIS_WARNING;
599		      break;
600		    }
601		  if (!tlvs->te_ipv4_reachs)
602		    tlvs->te_ipv4_reachs = list_new ();
603		  listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach);
604		  /* this trickery is permitable since no subtlvs are defined */
605		  value_len += 5 + ((te_ipv4_reach->control & 0x3F) ?
606				    ((((te_ipv4_reach->control & 0x3F) -
607				       1) >> 3) + 1) : 0);
608		  pnt += 5 + ((te_ipv4_reach->control & 0x3F) ?
609		              ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0);
610		}
611	    }
612
613	  pnt = endpnt;
614	  break;
615
616#ifdef  HAVE_IPV6
617	case IPV6_ADDR:
618	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
619	   * +                 IP version 6 address                          + 16
620	   * +-------+-------+-------+-------+-------+-------+-------+-------+
621	   * :                                                               :
622	   */
623	  *found |= TLVFLAG_IPV6_ADDR;
624#ifdef EXTREME_TLV_DEBUG
625	  zlog_debug ("ISIS-TLV (%s): IPv6 Address length %d",
626		      areatag, length);
627#endif /* EXTREME_TLV_DEBUG */
628	  if (*expected & TLVFLAG_IPV6_ADDR)
629	    {
630	      while (length > value_len)
631		{
632		  ipv6_addr = (struct in6_addr *) pnt;
633		  if (!tlvs->ipv6_addrs)
634		    tlvs->ipv6_addrs = list_new ();
635		  listnode_add (tlvs->ipv6_addrs, ipv6_addr);
636		  value_len += 16;
637		  pnt += 16;
638		}
639	    }
640	  else
641	    {
642	      pnt += length;
643	    }
644	  break;
645
646	case IPV6_REACHABILITY:
647	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
648	   * |                 Default Metric                                | 4
649	   * +-------+-------+-------+-------+-------+-------+-------+-------+
650	   * |                        Control Informantion                   |
651	   * +---------------------------------------------------------------+
652	   * |                        IPv6 Prefix Length                     |--+
653	   * +---------------------------------------------------------------+  |
654	   * |                        IPv6 Prefix                            |<-+
655	   * +---------------------------------------------------------------+
656	   */
657	  *found |= TLVFLAG_IPV6_REACHABILITY;
658	  endpnt = pnt + length;
659
660	  if (*expected & TLVFLAG_IPV6_REACHABILITY)
661	    {
662	      while (length > value_len)
663		{
664		  ipv6_reach = (struct ipv6_reachability *) pnt;
665		  if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN)
666		    {
667		      zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach"
668				 "ability prefix length %d", areatag,
669				 ipv6_reach->prefix_len);
670		      retval = ISIS_WARNING;
671		      break;
672		    }
673
674		  prefix_octets = ((ipv6_reach->prefix_len + 7) / 8);
675		  value_len += prefix_octets + 6;
676		  pnt += prefix_octets + 6;
677		  /* FIXME: sub-tlvs */
678		  if (!tlvs->ipv6_reachs)
679		    tlvs->ipv6_reachs = list_new ();
680		  listnode_add (tlvs->ipv6_reachs, ipv6_reach);
681		}
682	    }
683
684	  pnt = endpnt;
685	  break;
686#endif /* HAVE_IPV6 */
687
688	case WAY3_HELLO:
689	  /* +---------------------------------------------------------------+
690	   * |                  Adjacency state                              | 1
691	   * +---------------------------------------------------------------+
692	   * |                  Extended Local Circuit ID                    | 4
693	   * +---------------------------------------------------------------+
694	   * |                  Neighbor System ID (If known)                | 0-8
695	   *                                      (probably 6)
696	   * +---------------------------------------------------------------+
697	   * |                  Neighbor Local Circuit ID (If known)         | 4
698	   * +---------------------------------------------------------------+
699	   */
700	  *found |= TLVFLAG_3WAY_HELLO;
701	  if (*expected & TLVFLAG_3WAY_HELLO)
702	    {
703	      while (length > value_len)
704		{
705		  /* FIXME: make this work */
706/*           Adjacency State (one octet):
707              0 = Up
708              1 = Initializing
709              2 = Down
710            Extended Local Circuit ID (four octets)
711            Neighbor System ID if known (zero to eight octets)
712            Neighbor Extended Local Circuit ID (four octets, if Neighbor
713              System ID is present) */
714		  pnt += length;
715		  value_len += length;
716		}
717	    }
718	  else
719	    {
720	      pnt += length;
721	    }
722
723	  break;
724	case GRACEFUL_RESTART:
725	  /* +-------+-------+-------+-------+-------+-------+-------+-------+
726	   * |         Reserved                      |  SA   |  RA   |  RR   | 1
727	   * +-------+-------+-------+-------+-------+-------+-------+-------+
728	   * |                          Remaining Time                       | 2
729	   * +---------------------------------------------------------------+
730	   * |                Restarting Neighbor ID (If known)              | 0-8
731	   * +---------------------------------------------------------------+
732	   */
733	  *found |= TLVFLAG_GRACEFUL_RESTART;
734	  if (*expected & TLVFLAG_GRACEFUL_RESTART)
735	    {
736	      /* FIXME: make this work */
737	    }
738	  pnt += length;
739	  break;
740
741	default:
742	  zlog_warn ("ISIS-TLV (%s): unsupported TLV type %d, length %d",
743		     areatag, type, length);
744
745	  pnt += length;
746	  break;
747	}
748    }
749
750  return retval;
751}
752
753int
754add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream)
755{
756  if ((stream_get_size (stream) - stream_get_endp (stream)) <
757      (((unsigned)len) + 2))
758    {
759      zlog_warn ("No room for TLV of type %d "
760                 "(total size %d available %d required %d)",
761                 tag, (int)stream_get_size (stream),
762                 (int)(stream_get_size (stream) - stream_get_endp (stream)),
763                 len+2);
764      return ISIS_WARNING;
765    }
766
767  stream_putc (stream, tag);	/* TAG */
768  stream_putc (stream, len);	/* LENGTH */
769  stream_put (stream, value, (int) len);	/* VALUE */
770
771#ifdef EXTREME_DEBUG
772  zlog_debug ("Added TLV %d len %d", tag, len);
773#endif /* EXTREME DEBUG */
774  return ISIS_OK;
775}
776
777int
778tlv_add_area_addrs (struct list *area_addrs, struct stream *stream)
779{
780  struct listnode *node;
781  struct area_addr *area_addr;
782
783  u_char value[255];
784  u_char *pos = value;
785
786  for (ALL_LIST_ELEMENTS_RO (area_addrs, node, area_addr))
787    {
788      if (pos - value + area_addr->addr_len > 255)
789	goto err;
790      *pos = area_addr->addr_len;
791      pos++;
792      memcpy (pos, area_addr->area_addr, (int) area_addr->addr_len);
793      pos += area_addr->addr_len;
794    }
795
796  return add_tlv (AREA_ADDRESSES, pos - value, value, stream);
797
798err:
799  zlog_warn ("tlv_add_area_addrs(): TLV longer than 255");
800  return ISIS_WARNING;
801}
802
803int
804tlv_add_is_neighs (struct list *is_neighs, struct stream *stream)
805{
806  struct listnode *node;
807  struct is_neigh *is_neigh;
808  u_char value[255];
809  u_char *pos = value;
810  int retval;
811
812  *pos = 0;			/*is_neigh->virtual; */
813  pos++;
814
815  for (ALL_LIST_ELEMENTS_RO (is_neighs, node, is_neigh))
816    {
817      if (pos - value + IS_NEIGHBOURS_LEN > 255)
818	{
819	  retval = add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
820	  if (retval != ISIS_OK)
821	    return retval;
822	  pos = value;
823	}
824      *pos = is_neigh->metrics.metric_default;
825      pos++;
826      *pos = is_neigh->metrics.metric_delay;
827      pos++;
828      *pos = is_neigh->metrics.metric_expense;
829      pos++;
830      *pos = is_neigh->metrics.metric_error;
831      pos++;
832      memcpy (pos, is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
833      pos += ISIS_SYS_ID_LEN + 1;
834    }
835
836  return add_tlv (IS_NEIGHBOURS, pos - value, value, stream);
837}
838
839int
840tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream)
841{
842  struct listnode *node;
843  struct te_is_neigh *te_is_neigh;
844  u_char value[255];
845  u_char *pos = value;
846  int retval;
847
848  for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh))
849    {
850      /* FIXME: This will be wrong if we are going to add TE sub TLVs. */
851      if (pos - value + IS_NEIGHBOURS_LEN > 255)
852        {
853          retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
854          if (retval != ISIS_OK)
855            return retval;
856          pos = value;
857        }
858
859      memcpy (pos, te_is_neigh->neigh_id, ISIS_SYS_ID_LEN + 1);
860      pos += ISIS_SYS_ID_LEN + 1;
861      memcpy (pos, te_is_neigh->te_metric, 3);
862      pos += 3;
863      /* Sub TLVs length. */
864      *pos = 0;
865      pos++;
866    }
867
868  return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream);
869}
870
871int
872tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream)
873{
874  struct listnode *node;
875  u_char *snpa;
876  u_char value[255];
877  u_char *pos = value;
878  int retval;
879
880  for (ALL_LIST_ELEMENTS_RO (lan_neighs, node, snpa))
881    {
882      if (pos - value + ETH_ALEN > 255)
883	{
884	  retval = add_tlv (LAN_NEIGHBOURS, pos - value, value, stream);
885	  if (retval != ISIS_OK)
886	    return retval;
887	  pos = value;
888	}
889      memcpy (pos, snpa, ETH_ALEN);
890      pos += ETH_ALEN;
891    }
892
893  return add_tlv (LAN_NEIGHBOURS, pos - value, value, stream);
894}
895
896int
897tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream)
898{
899  return add_tlv (PROTOCOLS_SUPPORTED, nlpids->count, nlpids->nlpids, stream);
900}
901
902int
903tlv_add_authinfo (u_char auth_type, u_char auth_len, u_char *auth_value,
904		  struct stream *stream)
905{
906  u_char value[255];
907  u_char *pos = value;
908  *pos++ = auth_type;
909  memcpy (pos, auth_value, auth_len);
910
911  return add_tlv (AUTH_INFO, auth_len + 1, value, stream);
912}
913
914int
915tlv_add_checksum (struct checksum *checksum, struct stream *stream)
916{
917  u_char value[255];
918  u_char *pos = value;
919  return add_tlv (CHECKSUM, pos - value, value, stream);
920}
921
922int
923tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream)
924{
925  struct listnode *node;
926  struct prefix_ipv4 *ipv4;
927  u_char value[255];
928  u_char *pos = value;
929  int retval;
930
931  for (ALL_LIST_ELEMENTS_RO (ip_addrs, node, ipv4))
932    {
933      if (pos - value + IPV4_MAX_BYTELEN > 255)
934	{
935	  /* RFC 1195 s4.2: only one tuple of 63 allowed. */
936	  zlog_warn ("tlv_add_ip_addrs(): cutting off at 63 IP addresses");
937	  break;
938	}
939      *(u_int32_t *) pos = ipv4->prefix.s_addr;
940      pos += IPV4_MAX_BYTELEN;
941    }
942
943  return add_tlv (IPV4_ADDR, pos - value, value, stream);
944}
945
946/* Used to add TLV containing just one IPv4 address - either IPv4 address TLV
947 * (in case of LSP) or TE router ID TLV. */
948int
949tlv_add_in_addr (struct in_addr *addr, struct stream *stream, u_char tag)
950{
951  u_char value[255];
952  u_char *pos = value;
953
954  memcpy (pos, addr, IPV4_MAX_BYTELEN);
955  pos += IPV4_MAX_BYTELEN;
956
957  return add_tlv (tag, pos - value, value, stream);
958}
959
960int
961tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream)
962{
963  return add_tlv (DYNAMIC_HOSTNAME, hostname->namelen, hostname->name,
964		  stream);
965}
966
967int
968tlv_add_lsp_entries (struct list *lsps, struct stream *stream)
969{
970  struct listnode *node;
971  struct isis_lsp *lsp;
972  u_char value[255];
973  u_char *pos = value;
974  int retval;
975
976  for (ALL_LIST_ELEMENTS_RO (lsps, node, lsp))
977    {
978      if (pos - value + LSP_ENTRIES_LEN > 255)
979	{
980	  retval = add_tlv (LSP_ENTRIES, pos - value, value, stream);
981	  if (retval != ISIS_OK)
982	    return retval;
983	  pos = value;
984	}
985      *((u_int16_t *) pos) = lsp->lsp_header->rem_lifetime;
986      pos += 2;
987      memcpy (pos, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2);
988      pos += ISIS_SYS_ID_LEN + 2;
989      *((u_int32_t *) pos) = lsp->lsp_header->seq_num;
990      pos += 4;
991      *((u_int16_t *) pos) = lsp->lsp_header->checksum;
992      pos += 2;
993    }
994
995  return add_tlv (LSP_ENTRIES, pos - value, value, stream);
996}
997
998int
999tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream)
1000{
1001  struct listnode *node;
1002  struct ipv4_reachability *reach;
1003  u_char value[255];
1004  u_char *pos = value;
1005  int retval;
1006
1007  for (ALL_LIST_ELEMENTS_RO (ipv4_reachs, node, reach))
1008    {
1009      if (pos - value + IPV4_REACH_LEN > 255)
1010	{
1011	  retval =
1012	    add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream);
1013	  if (retval != ISIS_OK)
1014	    return retval;
1015	  pos = value;
1016	}
1017      *pos = reach->metrics.metric_default;
1018      pos++;
1019      *pos = reach->metrics.metric_delay;
1020      pos++;
1021      *pos = reach->metrics.metric_expense;
1022      pos++;
1023      *pos = reach->metrics.metric_error;
1024      pos++;
1025      *(u_int32_t *) pos = reach->prefix.s_addr;
1026      pos += IPV4_MAX_BYTELEN;
1027      *(u_int32_t *) pos = reach->mask.s_addr;
1028      pos += IPV4_MAX_BYTELEN;
1029    }
1030
1031  return add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream);
1032}
1033
1034int
1035tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream)
1036{
1037  struct listnode *node;
1038  struct te_ipv4_reachability *te_reach;
1039  u_char value[255];
1040  u_char *pos = value;
1041  u_char prefix_size;
1042  int retval;
1043
1044  for (ALL_LIST_ELEMENTS_RO (te_ipv4_reachs, node, te_reach))
1045    {
1046      prefix_size = ((((te_reach->control & 0x3F) - 1) >> 3) + 1);
1047
1048      if (pos - value + (5 + prefix_size) > 255)
1049	{
1050	  retval =
1051	    add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
1052	  if (retval != ISIS_OK)
1053	    return retval;
1054	  pos = value;
1055	}
1056      *(u_int32_t *) pos = te_reach->te_metric;
1057      pos += 4;
1058      *pos = te_reach->control;
1059      pos++;
1060      memcpy (pos, &te_reach->prefix_start, prefix_size);
1061      pos += prefix_size;
1062    }
1063
1064  return add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream);
1065}
1066
1067#ifdef HAVE_IPV6
1068int
1069tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream)
1070{
1071  struct listnode *node;
1072  struct prefix_ipv6 *ipv6;
1073  u_char value[255];
1074  u_char *pos = value;
1075  int retval;
1076
1077  for (ALL_LIST_ELEMENTS_RO (ipv6_addrs, node, ipv6))
1078    {
1079      if (pos - value + IPV6_MAX_BYTELEN > 255)
1080	{
1081	  retval = add_tlv (IPV6_ADDR, pos - value, value, stream);
1082	  if (retval != ISIS_OK)
1083	    return retval;
1084	  pos = value;
1085	}
1086      memcpy (pos, ipv6->prefix.s6_addr, IPV6_MAX_BYTELEN);
1087      pos += IPV6_MAX_BYTELEN;
1088    }
1089
1090  return add_tlv (IPV6_ADDR, pos - value, value, stream);
1091}
1092
1093int
1094tlv_add_ipv6_reachs (struct list *ipv6_reachs, struct stream *stream)
1095{
1096  struct listnode *node;
1097  struct ipv6_reachability *ip6reach;
1098  u_char value[255];
1099  u_char *pos = value;
1100  int retval, prefix_octets;
1101
1102  for (ALL_LIST_ELEMENTS_RO (ipv6_reachs, node, ip6reach))
1103    {
1104      if (pos - value + IPV6_MAX_BYTELEN + 6 > 255)
1105	{
1106	  retval = add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
1107	  if (retval != ISIS_OK)
1108	    return retval;
1109	  pos = value;
1110	}
1111      *(uint32_t *) pos = ip6reach->metric;
1112      pos += 4;
1113      *pos = ip6reach->control_info;
1114      pos++;
1115      prefix_octets = ((ip6reach->prefix_len + 7) / 8);
1116      *pos = ip6reach->prefix_len;
1117      pos++;
1118      memcpy (pos, ip6reach->prefix, prefix_octets);
1119      pos += prefix_octets;
1120    }
1121
1122  return add_tlv (IPV6_REACHABILITY, pos - value, value, stream);
1123}
1124#endif /* HAVE_IPV6 */
1125
1126int
1127tlv_add_padding (struct stream *stream)
1128{
1129  int fullpads, i, left;
1130
1131  /*
1132   * How many times can we add full padding ?
1133   */
1134  fullpads = (stream_get_size (stream) - stream_get_endp (stream)) / 257;
1135  for (i = 0; i < fullpads; i++)
1136    {
1137      if (!stream_putc (stream, (u_char) PADDING))	/* TAG */
1138	goto err;
1139      if (!stream_putc (stream, (u_char) 255))	/* LENGHT */
1140	goto err;
1141      stream_put (stream, NULL, 255);		/* zero padding */
1142    }
1143
1144  left = stream_get_size (stream) - stream_get_endp (stream);
1145
1146  if (left < 2)
1147    return ISIS_OK;
1148
1149  if (left == 2)
1150    {
1151      stream_putc (stream, PADDING);
1152      stream_putc (stream, 0);
1153      return ISIS_OK;
1154    }
1155
1156  stream_putc (stream, PADDING);
1157  stream_putc (stream, left - 2);
1158  stream_put (stream, NULL, left-2);
1159
1160  return ISIS_OK;
1161
1162err:
1163  zlog_warn ("tlv_add_padding(): no room for tlv");
1164  return ISIS_WARNING;
1165}
1166