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