1 /*
2  *  DOCSIS configuration file encoder.
3  *  Copyright (c) 2001,2005 Cornel Ciocirlan, ctrl@users.sourceforge.net.
4  *  Copyright (c) 2002,2003,2004 Evvolve Media SRL,office@evvolve.com
5  *  Copyright (c) 2014 - 2015 Adrian Simionov, daniel.simionov@gmail.com
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  *  DOCSIS is a registered trademark of Cablelabs, http://www.cablelabs.com
22  */
23 
24 %{
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "docsis.h"
29 #include "docsis_encode.h"
30 #include "docsis_snmp.h"
31 #if YYTEXT_POINTER
32 extern char *yytext;
33 #else
34 extern char yytext[];
35 #endif
36 
37 extern unsigned int line; 	/* current line number, defined in a.l */
38 extern struct tlv *global_tlvtree_head; /* Global list of all config TLVs */
39 extern symbol_type *global_symtable;
40 extern FILE *yyin;
41 
42 struct tlv *_my_tlvtree_head;
43 
44 %}
45 
46 %union { 	/* Token types */
47 	int intval;			/* For integers */
48 	unsigned int uintval;		/* For unsigned integers */
49 	symbol_type *symptr; 		/* For token identifiers */
50 	char *strval;		 	/* For strings */
51 	unsigned char *ustrval; 	/* For any other types */
52 	unsigned int ip;		/* For IP Addresses */
53 	struct tlv_list *tlvlist; 	/* For for struct tlvlist pointers */
54 	struct tlv *tlvptr;		/* For struct tlv pointers; */
55 }
56 
57 %token <symptr>  T_IDENTIFIER
58 %token <uintval> T_INTEGER
59 %token <symptr>  T_IDENT_COS
60 %token <symptr>  T_IDENT_BPI
61 %token <symptr>  T_IDENT_SNMPW
62 %token <symptr>  T_IDENT_SNMPSET
63 %token <symptr>  T_IDENT_GENERIC
64 %token <symptr>  T_IDENT_CVC
65 %token <strval>  T_ETHERMASK
66 %token <strval>  T_LABEL_OID
67 %token <strval>  T_SUBMGT_FILTERS
68 %token <strval>  T_IP
69 %token <strval>  T_IP6
70 %token <strval>  T_MAC
71 %token <strval>  T_MAIN
72 %token <strval>  T_STRING
73 %token <strval>  T_HEX_STRING
74 %token <strval>  T_TIMETICKS
75 %token <strval>  T_IP_LIST
76 %token <strval>  T_IP6_LIST
77 %token <strval>  T_IP6_PREFIX_LIST
78 
79 %token <uintval>  T_ASNTYPE_INT
80 %token <uintval>  T_ASNTYPE_UINT
81 %token <uintval>  T_ASNTYPE_SHORT
82 %token <uintval>  T_ASNTYPE_CHAR
83 %token <uintval>  T_ASNTYPE_GAUGE
84 %token <uintval>  T_ASNTYPE_COUNTER
85 %token <uintval>  T_ASNTYPE_TIMETICKS
86 %token <uintval>  T_ASNTYPE_IP
87 %token <uintval>  T_ASNTYPE_OBJID
88 %token <uintval>  T_ASNTYPE_STRING
89 %token <uintval>  T_ASNTYPE_HEXSTR
90 %token <uintval>  T_ASNTYPE_DECSTR
91 %token <uintval>  T_ASNTYPE_BITSTR
92 %token <uintval>  T_ASNTYPE_BIGINT
93 %token <uintval>  T_ASNTYPE_UBIGINT
94 %token <uintval>  T_ASNTYPE_FLOAT
95 %token <uintval>  T_ASNTYPE_DOUBLE
96 %token <uintval>  T_TLV_CODE
97 %token <uintval>  T_TLV_LENGTH
98 %token <uintval>  T_TLV_VALUE
99 %token <uintval>  T_TLV_STR_VALUE
100 %token <uintval>  T_TLV_STRZERO_VALUE
101 %token <uintval>  T_TLV_TYPE
102 %token <uintval>  T_IP_IP6_PORT
103 
104 %type <tlvptr>  assignment_stmt
105 %type <tlvptr>  generic_stmt
106 %type <tlvptr>  assignment_list
107 %type <tlvptr>  generic_assignment_list
108 %type <tlvptr>  subsettings_stmt
109 
110 %%
111 
112 /*
113 
114 How does this work ?
115 
116 When we recognize an assignment statement (for example "MaxCPE 13;") we create
117 a "struct tlv" which contains the type associated with this configuration
118 setting, the length of this configuration setting and the value. The type and
119 lenth are in docsis_symtable.h. Consecutive assignment_stmts are reduced
120 to an assignment_list, creating a "tlvlist" which is essentially a list of
121 pointers to stuct tlv's.
122 
123 "Special" cases like ClassOfService (configuration settings which have
124 sub-types) are treated a bit different.
125 
126 For example, within the ClassOfService statement we have a list of
127 assignment_stmts which are reduced to an assignment_list, and then to a
128 subsettings_stmt. When we flatten and reduce the subsettings_stmt to an assignment_list we
129 merge the subsettings_stmt tlvlist with the tlvlist corresponding to the
130 assignment_list (may be empty if a subsettings_stmt appears first in
131 the config file) so we have a single tlvlist.
132 
133 The idea is that, when we finish parsing, we end up with a "global_tlvlist"
134 which contains pointers to all the top-level struct tlv's that we assembled while
135 parsing the configuration file. At this point, we "flatten" this list into a
136 tlvbuf, add CM MIC and CMTS MIC and pad and that's it.
137 
138 */
139 
140 /* Definitions of bison/yacc grammar */
141 
142 main_stmt: 	T_MAIN '{' assignment_list '}' {
143 			_my_tlvtree_head = $3;
144 			_my_tlvtree_head->parent = NULL; }
145 		;
146 
147 assignment_list: assignment_list assignment_stmt { $$ = add_tlv_sibling ($1,$2); }
148 		| assignment_stmt {  $$=add_tlv_sibling(NULL,$1); }
149 		| assignment_list subsettings_stmt {
150 			$$ = merge_tlvlist($1,$2);  }
151 		| subsettings_stmt {
152 			$$ = merge_tlvlist (NULL, $1); }
153 		;
154 
155 generic_assignment_list: generic_assignment_list generic_stmt { $$ = add_tlv_sibling ($1,$2); }
156 			 | generic_stmt { $$=add_tlv_sibling(NULL,$1); }
157 			 ;
158 
159 
160 subsettings_stmt:  	T_IDENTIFIER '{' assignment_list  '}'	{
161 			$$ = assemble_tlv_in_parent ( $1->docsis_code, $3 ); }
162 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER '{' generic_assignment_list '}' {
163 			$$ = assemble_tlv_in_parent ( $3, $5 ); }
164 		;
165 
166 assignment_stmt:  T_IDENTIFIER T_INTEGER ';' {
167 			$$ = create_tlv ($1, (union t_val *)&$2);}
168 		| T_IDENTIFIER T_STRING ';'  {
169 			$$ = create_tlv ($1, (union t_val *)&$2);}
170 		| T_IDENTIFIER T_HEX_STRING ';'  {
171 			$$ = create_tlv ($1, (union t_val *)&$2);}
172 		| T_IDENTIFIER T_SUBMGT_FILTERS ';' {
173 			$$ = create_tlv ($1, (union t_val *)&$2);}
174 		| T_IDENTIFIER T_IP ';' {
175 			$$ = create_tlv ($1, (union t_val *)&$2);}
176 		| T_IDENTIFIER T_IP_LIST ';' {
177 			$$ = create_tlv ($1, (union t_val *)&$2);}
178 		| T_IDENTIFIER T_IP6 ';' {
179 			$$ = create_tlv ($1, (union t_val *)&$2);}
180 		| T_IDENTIFIER T_IP6_LIST ';' {
181 			$$ = create_tlv ($1, (union t_val *)&$2);}
182 		| T_IDENTIFIER T_IP6_PREFIX_LIST ';' {
183 			$$ = create_tlv ($1, (union t_val *)&$2);}
184 		| T_IDENTIFIER T_IP_IP6_PORT ';' {
185 			$$ = create_tlv ($1, (union t_val *)&$2);}
186 		| T_IDENTIFIER T_MAC ';' {
187 			$$ = create_tlv ($1, (union t_val *)&$2);}
188 		| T_IDENTIFIER T_ETHERMASK ';' {
189 			$$ = create_tlv ($1, (union t_val *)&$2);}
190 		| T_IDENTIFIER T_LABEL_OID ';' {
191 			$$ = create_tlv ($1, (union t_val *)&$2);}
192 		| T_IDENT_CVC T_STRING ';'  {
193 			$$ = create_external_file_tlv ($1, (union t_val *)&$2);}
194 		| T_IDENT_SNMPW T_LABEL_OID T_INTEGER ';' {
195 			$$ = create_snmpw_tlv ( $1, $2, (union t_val *) &$3 ); }
196 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_INT T_INTEGER ';' {
197 			$$ = create_snmpset_tlv($1,$2,'i',(union t_val *)&$4); }
198 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_GAUGE T_INTEGER ';' {
199 			$$ = create_snmpset_tlv($1,$2,'g',(union t_val *)&$4); }
200 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_UINT T_INTEGER ';' {
201 			$$ = create_snmpset_tlv($1,$2,'u',(union t_val *)&$4); }
202 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_COUNTER T_INTEGER ';' {
203 			$$ = create_snmpset_tlv($1,$2,'c',(union t_val *)&$4); }
204 	        | T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_IP T_IP ';' {
205 			$$ = create_snmpset_tlv($1,$2,'a',(union t_val *)&$4); }
206 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_STRING T_STRING ';' {
207 			$$ = create_snmpset_tlv($1,$2,'s',(union t_val *)&$4); }
208 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_HEXSTR T_HEX_STRING ';' {
209 			$$ = create_snmpset_tlv($1,$2,'x',(union t_val *)&$4); }
210 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_OBJID T_LABEL_OID ';' {
211 			$$ = create_snmpset_tlv($1,$2,'o',(union t_val *)&$4); }
212 		| T_IDENT_SNMPSET T_LABEL_OID T_ASNTYPE_TIMETICKS T_INTEGER ';' {
213 			$$ = create_snmpset_tlv($1,$2,'t',(union t_val *)&$4); }
214 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_STR_VALUE T_STRING ';' {
215 			$$ = create_generic_str_tlv($1,$3, (union t_val *)&$5); }
216 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_STRZERO_VALUE T_STRING ';' {
217 			$$ = create_generic_strzero_tlv($1,$3, (union t_val *)&$5); }
218 		| generic_stmt {
219 			$$ = $1; }
220 		;
221 
222 generic_stmt:	T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_LENGTH T_INTEGER T_TLV_VALUE T_HEX_STRING ';' {
223 			$$ = create_generic_tlv($1,$3,$5, (union t_val *)&$7); }
224 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_TYPE T_ASNTYPE_INT T_TLV_VALUE T_INTEGER ';' {
225 			$$ = create_generic_typed_tlv($1,$3,encode_uint, (union t_val *)&$7); }
226 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_TYPE T_ASNTYPE_SHORT T_TLV_VALUE T_INTEGER ';' {
227 			$$ = create_generic_typed_tlv($1,$3,encode_ushort, (union t_val *)&$7); }
228 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_TYPE T_ASNTYPE_CHAR T_TLV_VALUE T_INTEGER ';' {
229 			$$ = create_generic_typed_tlv($1,$3,encode_uchar, (union t_val *)&$7); }
230 		| T_IDENT_GENERIC T_TLV_CODE T_INTEGER T_TLV_TYPE T_ASNTYPE_IP T_TLV_VALUE T_IP ';' {
231 			$$ = create_generic_typed_tlv($1,$3,encode_ip, (union t_val *)&$7); }
232                 ;
233 %%
234 
235 int yyerror(char *s) {
236 	fprintf(stderr, "%d:%s token %s\n",line,s,yytext );
237 	return 0;
238 }
239 
240 #define TLVINIT(p) if(p) p->first_child=NULL; p->next_sibling=NULL; p->parent=NULL
241 
242 /*
243  * Given a symbol identifier, AND a buffer containing the raw "value",
244  * returns a pointer to a tlv structure containing TLV buffer and the
245  * length of the buffer, including docsis code and value length fields.
246  * The length is relevant when we "assemble" two tlv_lists from an
247  * an assignment_list + assignment_stmt
248  */
249 
250 struct tlv *
create_tlv(struct symbol_entry * sym_ptr,union t_val * value)251 create_tlv(struct symbol_entry *sym_ptr, union t_val *value)
252 {
253   struct tlv *tlvbuf=NULL;
254 
255   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv) ) ;
256   TLVINIT(tlvbuf);
257   tlvbuf->docs_code = sym_ptr->docsis_code;
258   tlvbuf->tlv_len = sym_ptr->encode_func(tlvbuf->tlv_value,value,sym_ptr);
259 /*		if (tlvbuf->tlv_len <= 0 ) {
260 			fprintf(stderr, "Got 0-length value while scanning for %s at line %d\n",sym_ptr->sym_ident,line );
261 			exit (-1);
262    		} */
263   return tlvbuf;
264 }
265 
266 /* Given a symbol identifier ( always SnmpMibObject ), a string
267 ** containing an OID name, a string which sets the value of the OID and a t_val
268 ** which contains the raw value (of type oid_asntype), creates a TLV with the
269 ** corresponding variable binding.
270 */
271 
272 struct tlv *
create_snmpset_tlv(struct symbol_entry * sym_ptr,char * oid_string,char oid_asntype,union t_val * value)273 create_snmpset_tlv ( struct symbol_entry *sym_ptr,
274 				char *oid_string,
275 				char oid_asntype,
276 				union t_val *value )
277 {
278   struct tlv *tlvbuf=NULL;
279 
280   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
281   TLVINIT(tlvbuf);
282   tlvbuf->docs_code = sym_ptr->docsis_code;
283   tlvbuf->tlv_len = encode_vbind (oid_string, oid_asntype, value,
284 			            tlvbuf->tlv_value,TLV_VSIZE );
285 
286 		if (tlvbuf->tlv_len <= 0 ) {
287 			fprintf(stderr, "got len 0 value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,line );
288 			exit (-1);
289    		}
290   free(oid_string);
291 /* We only free non-string values since we parse strings into a static buffer in docsis_lex.l */
292   if (	oid_asntype == 'x' || oid_asntype == 'a' ||
293 	oid_asntype == 'd' || oid_asntype == 'o' )  {
294 	 	free(value->strval);
295   }
296   return tlvbuf;
297 }
298 
299 
300 /* Given a symbol identifier ( always SnmpWrite ), a string
301 ** containing an OID, and an integer, creates a TLV containing the
302 ** OID ASN encoding + 1 control byte to permit/deny SNMP Write operations on a
303 ** MIB variable which has the OID as prefix.
304 */
305 
306 struct tlv *
create_snmpw_tlv(struct symbol_entry * sym_ptr,char * oid_string,union t_val * value)307 create_snmpw_tlv ( struct symbol_entry *sym_ptr,
308                                char *oid_string,
309                                union t_val *value )
310 {
311   struct tlv *tlvbuf=NULL;
312 
313   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
314   TLVINIT(tlvbuf);
315   tlvbuf->docs_code = sym_ptr->docsis_code;
316   tlvbuf->tlv_len = encode_snmp_oid ( oid_string, tlvbuf->tlv_value, TLV_VSIZE );
317 
318                 if (tlvbuf->tlv_len <= 0 ) {
319                         fprintf(stderr, "got len 0 value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,line );
320                         exit (-1);
321                 }
322   tlvbuf->tlv_value[tlvbuf->tlv_len] = (unsigned char) value->intval ;
323   tlvbuf->tlv_len++;
324   free(oid_string);
325   return tlvbuf;
326 }
327 
328 /* Given a code, length and raw value (encoded as a hex string),
329 ** creates a TLV encoding. This can be used for e.g. VendorSpecific
330 ** Information or unsupported (future?) configuration settings.
331 */
332 struct tlv *
create_generic_tlv(struct symbol_entry * sym_ptr,int tlv_code,int tlv_length,union t_val * value)333 create_generic_tlv ( struct symbol_entry *sym_ptr,
334 				 int tlv_code,
335                                  int tlv_length,
336                                  union t_val *value )
337 {
338   struct tlv *tlvbuf=NULL;
339 
340   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
341   TLVINIT(tlvbuf);
342   tlvbuf->docs_code = tlv_code;
343   tlvbuf->tlv_len = encode_hexstr ( tlvbuf->tlv_value, value, sym_ptr );
344 
345                 if (tlvbuf->tlv_len <= 0 ) {
346                         fprintf(stderr, "got len 0 value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,
347 line );
348                         exit (-1);
349                 }
350                 if (tlvbuf->tlv_len != tlv_length ) {
351                         fprintf(stderr, "Length mismatch while encoding GenericTLV: given length %d, value length %d at line %d\n", tlvbuf->tlv_len, tlv_length, line );
352 
353                         exit (-1);
354 		}
355   return tlvbuf;
356 }
357 
358 /*
359 ** Same as above, except the value is encoded as a string.
360 */
361 
362 struct tlv *
create_generic_str_tlv(struct symbol_entry * sym_ptr,int tlv_code,union t_val * value)363 create_generic_str_tlv ( struct symbol_entry *sym_ptr,
364                                  int tlv_code,
365                                  union t_val *value )
366 {
367   struct tlv *tlvbuf=NULL;
368 
369   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
370   TLVINIT(tlvbuf);
371   tlvbuf->docs_code = tlv_code;
372   tlvbuf->tlv_len = encode_string ( tlvbuf->tlv_value, value, sym_ptr );
373 
374                 if (tlvbuf->tlv_len <= 0 ) {
375                         fprintf(stderr, "got len 0 value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,
376 line );
377                         exit (-1);
378                 }
379                 if (tlvbuf->tlv_len > 255 ) {
380                         fprintf(stderr, "Warning: string length %d longer than 255, line %d\n",tlvbuf->tlv_len, line );
381 
382                 }
383   /* don't free strings - we use a static buffer to parse them */
384   return tlvbuf;
385 }
386 
387 /*
388 ** Same as above, except the value is encoded as a zero-terminated string.
389 */
390 
391 struct tlv *
create_generic_strzero_tlv(struct symbol_entry * sym_ptr,int tlv_code,union t_val * value)392 create_generic_strzero_tlv ( struct symbol_entry *sym_ptr,
393                                  int tlv_code,
394                                  union t_val *value )
395 {
396   struct tlv *tlvbuf=NULL;
397 
398   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
399   TLVINIT(tlvbuf);
400   tlvbuf->docs_code = tlv_code;
401   tlvbuf->tlv_len = encode_string ( tlvbuf->tlv_value, value, sym_ptr );
402 
403                 if (tlvbuf->tlv_len <= 0 ) {
404                         fprintf(stderr, "got 0-length value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,
405 line );
406                         exit (-1);
407                 }
408                 if (tlvbuf->tlv_len > 254 ) {
409                         fprintf(stderr, "Warning: total string length %d longer than 255, line %d\n",tlvbuf->tlv_len, line );
410 
411                 } else {
412 			tlvbuf->tlv_len = tlvbuf->tlv_len + 1; /* add terminating 0 */
413 		}
414   tlvbuf->tlv_value[tlvbuf->tlv_len]=0;
415   return tlvbuf;
416 }
417 
418 /* Given symbol name and a strings that represents a filename
419 ** creates a TLV encoding from the raw data in the file.
420 ** If the file is longer than 255, it is split into "parts" of
421 ** 255 octets max.
422 */
423 struct tlv *
create_external_file_tlv(struct symbol_entry * sym_ptr,union t_val * value)424 create_external_file_tlv ( struct symbol_entry *sym_ptr,
425                                  union t_val *value )
426 {
427   struct tlv *new_tlvbuf=NULL, *first_tlvbuf = NULL, *old_tlvbuf = NULL;
428   size_t read_len;
429   unsigned char read_buffer[255];
430   FILE *ext_file;
431 
432   if ((ext_file = fopen (value->strval, "rb")) == NULL) {
433 	fprintf(stderr, "Error: can't open external file %s at line %d\n", value->strval, line);
434 	exit (-5);
435   }
436 
437   while ( !feof(ext_file) ) {
438  	if (! (read_len = fread (read_buffer, 1, 254, ext_file)) ) {
439 	  fprintf (stderr, "Error reading data from %s\n", value->strval) ;
440   	  fclose (ext_file);
441 	  exit(-5);
442   	}
443 
444 	new_tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
445   	TLVINIT(new_tlvbuf);
446 	if (first_tlvbuf == NULL) first_tlvbuf = new_tlvbuf;
447 
448   	new_tlvbuf->docs_code = sym_ptr->docsis_code;
449   	new_tlvbuf->tlv_len = read_len;
450 
451 	memcpy (new_tlvbuf->tlv_value, read_buffer, read_len);
452 	if(old_tlvbuf) old_tlvbuf->next_sibling = new_tlvbuf;
453 	old_tlvbuf = new_tlvbuf;
454   }
455   fclose (ext_file);
456   return first_tlvbuf;
457 }
458 
459 /* Given a code, type, and value, creates a TLV encoding.
460 ** Expected to be used for VendorSpecific settings
461 */
462 struct tlv *
create_generic_typed_tlv(struct symbol_entry * sym_ptr,int tlv_code,int (* encode_func)(unsigned char *,void *,struct symbol_entry *),union t_val * value)463 create_generic_typed_tlv ( struct symbol_entry *sym_ptr,
464 				int tlv_code,
465 			int (*encode_func) (unsigned char *, void *, struct symbol_entry *),
466 				union t_val *value )
467 {
468   struct tlv *tlvbuf=NULL;
469 
470   tlvbuf = (struct tlv *) malloc (sizeof(struct tlv));
471   tlvbuf->docs_code = tlv_code;
472   tlvbuf->tlv_len = encode_func ( tlvbuf->tlv_value, value, sym_ptr );
473 
474   if (tlvbuf->tlv_len <= 0 ) {
475     fprintf (stderr, "got len 0 value while scanning for %s\n at line %d\n",sym_ptr->sym_ident,line );
476     exit (-1);
477   }
478   return tlvbuf;
479 }
480 
481 /*
482  * Adds a TLV to the current tlv "list". If the tlv pointer we are called with is NULL,
483  * we assume this is the "first" tlv and just return the initialized newtlv.
484  */
485 
add_tlv_sibling(struct tlv * tlv,struct tlv * newtlv)486 struct tlv *add_tlv_sibling (struct tlv *tlv, struct tlv *newtlv)
487 {
488   struct tlv *tlvptr, *last_sibling;
489 
490   if (newtlv == NULL ) {
491 	fprintf(stderr, "Error: add_tlv_sibling called with NULL tlv sibling ! \n " ) ;
492 	exit(-23);
493   }
494 
495   if (tlv != NULL ) {
496 /*  	tlv->parent=NULL;  */
497 	/* find "last" sibling in this assignment list */
498 
499 	for (tlvptr=tlv; tlvptr; tlvptr=tlvptr->next_sibling)
500 		last_sibling=tlvptr;
501 	/* Add the new TLV at the end of the list */
502 	last_sibling->next_sibling = newtlv;
503 	return tlv;
504   } else  {
505 	return newtlv;
506   }
507 }
508 
509 /* Merge two parse "subtrees" into one  */
510 
511 struct tlv *
merge_tlvlist(struct tlv * tlv1,struct tlv * tlv2)512 merge_tlvlist(struct tlv *tlv1, struct tlv *tlv2)
513 {
514   struct tlv *tlvptr, *last_sibling;
515   if ( tlv2 == NULL ) {
516 	fprintf(stderr, "merge_tlvlist called with NULL tlv2\n");
517 	exit(-2);
518   }
519 
520   if ( tlv1 == NULL ) {
521 	/* subsettings_stmt can be empty; in that case its value is NULL */
522 	return tlv2;
523   }
524   /* find "last" sibling in this assignment list */
525 
526   for (tlvptr=tlv1; tlvptr; tlvptr=tlvptr->next_sibling)
527 	last_sibling=tlvptr;
528   last_sibling->next_sibling = tlv2;
529   return tlv1;
530 
531 }
532 
533 /*
534  * Creates the "parent" tlv that holds the "subtree" (e.g. the parent TLV's first_child will point to the
535  * first element of this subtree.
536  */
537 
538 struct tlv *
assemble_tlv_in_parent(int tlvcode,struct tlv * child_tlv)539 assemble_tlv_in_parent (int tlvcode, struct tlv *child_tlv)
540 {
541   struct tlv *parent_tlv;
542 
543   parent_tlv = (struct tlv *) malloc ( sizeof ( struct tlv) );
544   memset(parent_tlv, 0, sizeof(struct tlv));
545 
546   parent_tlv->docs_code = tlvcode;
547   parent_tlv->tlv_len = 0;
548   parent_tlv->first_child = child_tlv;
549   child_tlv->parent = parent_tlv;
550   /* TODO: make al children's "parent" point back to parent_tlv */
551   return parent_tlv;
552 }
553 
554 /*
555  * Creates a buffer filled with the TLV bytes sequentially, as they should be
556  * found in the final configuration file.
557  * The function walkts the tree recursively in "depth-last" mode and aggregates
558  * the parsed values into an output "binary".
559  */
560 
flatten_tlvsubtree(unsigned char * buf,unsigned int used_size,struct tlv * tlv)561 unsigned int flatten_tlvsubtree ( unsigned char *buf, unsigned int used_size, struct tlv *tlv)
562 {
563   struct tlv *tlvptr;
564   unsigned int rsize;
565   unsigned short netshort;
566   register unsigned char *cp;
567 
568   if ( buf == NULL ) {
569 	fprintf(stderr, "Error: can't flatten tlvlist in a NULL destination buffer\n" );
570 	exit (-2);
571   }
572 
573   cp = buf + used_size;
574 
575   for (tlvptr=tlv; tlvptr; tlvptr=tlvptr->next_sibling ) {
576 	if (tlvptr->first_child )  { /* Sub-Settings */
577 		/* we don't know the size yet, so we delay writing type & length */
578 		rsize = flatten_tlvsubtree(buf, (cp-buf)+2, tlvptr->first_child);
579 		if (rsize > 255) {
580 			fprintf(stderr, "Warning: at line %d: aggregate size of settings block larger than 255, skipping\n", line);
581 			continue;
582 		}
583 		*cp = (unsigned char) tlvptr->docs_code; cp++;
584 		*cp = (unsigned char) rsize; cp++;
585 		cp = cp + rsize;
586   	} else if (tlvptr->tlv_len <= 255 ) {
587 
588 			*cp = (unsigned char) tlvptr->docs_code; cp++;
589 			*cp = (unsigned char) tlvptr->tlv_len; cp++;
590 			if (tlvptr->tlv_len > 0)
591 				memcpy ( cp, tlvptr->tlv_value, tlvptr->tlv_len );
592   	     		cp = cp + tlvptr->tlv_len;
593 		} else {
594 			/* convert TLV11 to TLV64 */
595 			if (tlvptr->docs_code == 11) {
596 				*cp = 64; cp++;
597 			        netshort = htons(tlvptr->tlv_len);
598 				memcpy ( cp, &netshort, sizeof(unsigned short));
599 				cp=cp+sizeof(unsigned short);
600        				memcpy ( cp, tlvptr->tlv_value, tlvptr->tlv_len );
601              			cp = cp + tlvptr->tlv_len;
602 			} else {
603 				fprintf(stderr, "Warning at line %d: Non-SnmpMibObject TLV larger than 255... skipping.\n", line);
604 				continue;
605 			}
606 		}
607 	}
608   return (unsigned int) (cp - buf - used_size); /* return the number of bytes encoded this time  */
609 }
610 
611 /*
612  *  Recursively walk the tree and calculate the total size of the "concatenated" TLVs
613  */
tlvtreelen(struct tlv * tlv)614 unsigned int tlvtreelen (struct tlv *tlv)
615 {
616    struct tlv *tlvptr;
617    unsigned int current_size=0;
618 
619    if (tlv == NULL) return 0;
620 
621    for (tlvptr=tlv; tlvptr; tlvptr=tlvptr->next_sibling)  {
622         if (tlvptr->first_child)
623             current_size += 2+tlvtreelen(tlvptr->first_child);
624         else
625             current_size += 2+tlvptr->tlv_len;
626    }
627    return current_size;
628 }
629 
parse_config_file(char * file,struct tlv ** parse_tree_result)630 int parse_config_file ( char *file, struct tlv **parse_tree_result )
631 {
632   FILE *cf;
633   int rval;
634 
635   if ( !strcmp(file, "-") )
636   {
637 	cf = stdin;
638   }
639   else if ( (cf = fopen ( file, "r" )) == NULL )
640   {
641 	fprintf (stderr, "docsis: Can't open input file %s\n", file );
642 	return -1;
643   }
644 
645   yyin = cf ;
646 #ifdef DEBUG
647   yydebug = 1;
648 #endif
649   rval = yyparse();
650   if (!rval) *parse_tree_result = _my_tlvtree_head;
651   fclose(cf);
652   return rval;
653 }
654