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