dhcp6_parser.yy 32.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")

   This Source Code Form is subject to the terms of the Mozilla Public
   License, v. 2.0. If a copy of the MPL was not distributed with this
   file, You can obtain one at http://mozilla.org/MPL/2.0/. */

%skeleton "lalr1.cc" /* -*- C++ -*- */
%require "3.0.0"
%defines
%define parser_class_name {Dhcp6Parser}
11
%define api.prefix {parser6_}
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
%define api.token.constructor
%define api.value.type variant
%define api.namespace {isc::dhcp}
%define parse.assert
%code requires
{
#include <string>
#include <cc/data.h>
#include <dhcp/option.h>
#include <boost/lexical_cast.hpp>
#include <dhcp6/parser_context_decl.h>

using namespace isc::dhcp;
using namespace isc::data;
using namespace std;
}
// The parsing context.
%param { isc::dhcp::Parser6Context& ctx }
%locations
%define parse.trace
%define parse.error verbose
%code
{
#include <dhcp6/parser_context.h>

}

39

40
41
42
43
44
45
46
47
48
49
50
%define api.token.prefix {TOKEN_}
// Tokens in an order which makes sense and related to the intented use.
%token
  END  0  "end of file"
  COMMA ","
  COLON ":"
  LSQUARE_BRACKET "["
  RSQUARE_BRACKET "]"
  LCURLY_BRACKET "{"
  RCURLY_BRACKET "}"
  NULL_TYPE "null"
51
52
53
54

  DHCP6 "Dhcp6"
  INTERFACES_CONFIG "interfaces-config"
  INTERFACES "interfaces"
55

56
  LEASE_DATABASE "lease-database"
57
  HOSTS_DATABASE "hosts-database"
58
  TYPE "type"
59
60
61
  USER "user"
  PASSWORD "password"
  HOST "host"
62
  PERSIST "persist"
63
  LFC_INTERVAL "lfc-interval"
64

65
66
67
68
69
70
71
72
73
  PREFERRED_LIFETIME "preferred-lifetime"
  VALID_LIFETIME "valid-lifetime"
  RENEW_TIMER "renew-timer"
  REBIND_TIMER "rebind-timer"
  SUBNET6 "subnet6"
  OPTION_DATA "option-data"
  NAME "name"
  DATA "data"
  CODE "code"
74
  SPACE "space"
75
  CSV_FORMAT "csv-format"
76

77
78
  POOLS "pools"
  POOL "pool"
79
80
81
82
83
  PD_POOLS "pd-pools"
  PREFIX "prefix"
  PREFIX_LEN "prefix-len"
  DELEGATED_LEN "delegated-len"

84
85
  SUBNET "subnet"
  INTERFACE "interface"
86
  ID "id"
87

88
89
  MAC_SOURCES "mac-sources"
  RELAY_SUPPLIED_OPTIONS "relay-supplied-options"
90
  HOST_RESERVATION_IDENTIFIERS "host-reservation-identifiers"
91

92
93
94
95
96
  CLIENT_CLASSES "client-classes"
  TEST "test"
  CLIENT_CLASS "client-class"

  RESERVATIONS "reservations"
97
98
  IP_ADDRESSES "ip-addresses"
  PREFIXES "prefixes"
99
  DUID "duid"
100
101
  HW_ADDRESS "hw-address"
  HOSTNAME "hostname"
102

103
104
105
106
107
108
109
110
111
  HOOKS_LIBRARIES "hooks-libraries"
  LIBRARY "library"

  EXPIRED_LEASES_PROCESSING "expired-leases-processing"

  SERVER_ID "server-id"
  IDENTIFIER "identifier"
  HTYPE "htype"
  TIME "time"
112
  ENTERPRISE_ID "enterprise-id"
113

114
115
  DHCP4O6_PORT "dhcp4o6-port"

116
117
118
119
120
121
  LOGGING "Logging"
  LOGGERS "loggers"
  OUTPUT_OPTIONS "output_options"
  OUTPUT "output"
  DEBUGLEVEL "debuglevel"
  SEVERITY "severity"
122

123
124
125
126
  DHCP_DDNS "dhcp-ddns"
  ENABLE_UPDATES "enable-updates"
  QUALIFYING_SUFFIX "qualifying-suffix"

127
128
129
  DHCP4 "Dhcp4"
  DHCPDDNS "DhcpDdns"

130
131
132
 // Not real tokens, just a way to signal what the parser is expected to
 // parse.
  TOPLEVEL_GENERIC_JSON
133
  TOPLEVEL_DHCP6
134
135
136
137
138
139
140
141
142
143
144
145
;

%token <std::string> STRING "constant string"
%token <int64_t> INTEGER "integer"
%token <double> FLOAT "floating point"
%token <bool> BOOLEAN "boolean"

%type <ElementPtr> value

%printer { yyoutput << $$; } <*>;

%%
146

147
148
// The whole grammar starts with a map, because the config file
// constists of Dhcp, Logger and DhcpDdns entries in one big { }.
149
150
// %start map - this will parse everything as generic JSON
// %start dhcp6_map - this will parse everything with Dhcp6 syntax checking
151
152
%start start;

153
154
155
start: TOPLEVEL_GENERIC_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } map2
     | TOPLEVEL_DHCP6 { ctx.ctx_ = ctx.CONFIG; } syntax_map
     ;
156

157
158
// ---- generic JSON parser ---------------------------------

159
160
// Note that ctx_ is NO_KEYWORD here

161
// Values rule
162
163
164
165
166
167
168
169
value: INTEGER { $$ = ElementPtr(new IntElement($1)); }
     | FLOAT { $$ = ElementPtr(new DoubleElement($1)); }
     | BOOLEAN { $$ = ElementPtr(new BoolElement($1)); }
     | STRING { $$ = ElementPtr(new StringElement($1)); }
     | NULL_TYPE { $$ = ElementPtr(new NullElement()); }
     | map2 { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
     ;
170

171
map2: LCURLY_BRACKET {
172
173
174
175
176
177
178
179
180
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.push_back(m);
} map_content RCURLY_BRACKET {
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
};
181
182

// Assignments rule
183
map_content: %empty // empty map
184
           | not_empty_map
185
           ;
186

187
188
189
190
191
192
193
194
195
196
197
not_empty_map: STRING COLON value {
                  // map containing a single entry
                  ctx.stack_.back()->set($1, $3);
                  }
             | not_empty_map COMMA STRING COLON value {
                  // map consisting of a shorter map followed by
                  // comma and string:value
                  ctx.stack_.back()->set($3, $5);
                  }
             ;

198
199
200
list_generic: LSQUARE_BRACKET {
    ElementPtr l(new ListElement());
    ctx.stack_.push_back(l);
201
202
203
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
};
204
205
206

// This one is used in syntax parser.
list2: LSQUARE_BRACKET {
207
208
209
    // List parsing about to start
} list_content RSQUARE_BRACKET {
    // list parsing complete. Put any sanity checking here
210
    //ctx.stack_.pop_back();
211
};
212

213
list_content: %empty // Empty list
214
            | not_empty_list
215
            ;
216

217
218
219
220
221
222
223
224
225
226
not_empty_list: value {
                  // List consisting of a single element.
                  ctx.stack_.back()->add($1);
                  }
              | not_empty_list COMMA value {
                  // List ending with , and a value.
                  ctx.stack_.back()->add($3);
                  }
              ;

227
228
229
230
// ---- generic JSON parser ends here ----------------------------------

// ---- syntax checking parser starts here -----------------------------

231
232
233
234
235
236
237
238
239
// Unknown keyword in a map
unknown_map_entry: STRING COLON {
    const std::string& where = ctx.context_name();
    const std::string& keyword = $1;
    error(@1,
          "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
}


240
241
// This defines the top-level { } that holds Dhcp6, Dhcp4, DhcpDdns or Logging
// objects.
242
// ctx_ = CONFIG
243
244
245
246
247
248
249
250
251
252
253
254
syntax_map: LCURLY_BRACKET {
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.push_back(m);
} global_objects RCURLY_BRACKET {
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
};

// This represents top-level entries: Dhcp6, Dhcp4, DhcpDdns, Logging
255
256
257
global_objects: global_object
              | global_objects COMMA global_object
              ;
258

259
260
261
// This represents a single top level entry, e.g. Dhcp6 or DhcpDdns.
global_object: dhcp6_object
             | logging_object
262
263
264
	     | dhcp4_object
	     | dhcpddns_object
	     | unknown_map_entry
265
266
             ;

267
dhcp6_object: DHCP6 {
268
269
270
271
272
    // This code is executed when we're about to start parsing
    // the content of the map
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("Dhcp6", m);
    ctx.stack_.push_back(m);
273
274
    ctx.enter(ctx.DHCP6);
} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
275
276
277
278
    // map parsing completed. If we ever want to do any wrap up
    // (maybe some sanity checking), this would be the best place
    // for it.
    ctx.stack_.pop_back();
279
    ctx.leave();
280
281
};

282
global_params: global_param
283
284
             | global_params COMMA global_param
             ;
285
286

// These are the parameters that are allowed in the top-level for
287
// Dhcp6.
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
global_param: preferred_lifetime
            | valid_lifetime
            | renew_timer
            | rebind_timer
            | subnet6_list
            | interfaces_config
            | lease_database
            | hosts_database
            | mac_sources
            | relay_supplied_options
            | host_reservation_identifiers
            | client_classes
            | option_data_list
            | hooks_libraries
            | expired_leases_processing
            | server_id
            | dhcp4o6_port
            | dhcp_ddns
306
            | unknown_map_entry
307
            ;
308
309

preferred_lifetime: PREFERRED_LIFETIME COLON INTEGER {
310
311
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("preferred-lifetime", prf);
312
313
314
};

valid_lifetime: VALID_LIFETIME COLON INTEGER {
315
316
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("valid-lifetime", prf);
317
318
319
};

renew_timer: RENEW_TIMER COLON INTEGER {
320
321
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("renew-timer", prf);
322
323
};

324
325
326
327
rebind_timer: REBIND_TIMER COLON INTEGER {
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("rebind-timer", prf);
};
328

329
interfaces_config: INTERFACES_CONFIG {
330
331
332
    ElementPtr i(new MapElement());
    ctx.stack_.back()->set("interfaces-config", i);
    ctx.stack_.push_back(i);
333
334
    ctx.enter(ctx.INTERFACES_CONFIG);
} COLON LCURLY_BRACKET interface_config_map RCURLY_BRACKET {
335
    ctx.stack_.pop_back();
336
    ctx.leave();
337
};
338

339
340
341
342
interface_config_map: INTERFACES {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("interfaces", l);
    ctx.stack_.push_back(l);
343
344
345
346
347
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
    ctx.stack_.pop_back();
    ctx.leave();
};
348
349
350
351
352

lease_database: LEASE_DATABASE {
    ElementPtr i(new MapElement());
    ctx.stack_.back()->set("lease-database", i);
    ctx.stack_.push_back(i);
353
    ctx.enter(ctx.LEASE_DATABASE);
354
355
356
357
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};
358

359
360
361
362
hosts_database: HOSTS_DATABASE {
    ElementPtr i(new MapElement());
    ctx.stack_.back()->set("hosts-database", i);
    ctx.stack_.push_back(i);
363
    ctx.enter(ctx.HOSTS_DATABASE);
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
} COLON LCURLY_BRACKET database_map_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
    ctx.leave();
};

database_map_params: database_map_param
                   | database_map_params COMMA database_map_param
                   ;

database_map_param: type
                  | user
                  | password
                  | host
                  | name
                  | persist
379
380
                  | lfc_interval
                  | unknown_map_entry
381
;
382

383
384
385
386
type: TYPE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr prf(new StringElement($4));
387
    ctx.stack_.back()->set("type", prf);
388
    ctx.leave();
389
390
};

391
392
393
394
user: USER {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr user(new StringElement($4));
395
    ctx.stack_.back()->set("user", user);
396
    ctx.leave();
397
398
};

399
400
401
402
password: PASSWORD {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr pwd(new StringElement($4));
403
    ctx.stack_.back()->set("password", pwd);
404
    ctx.leave();
405
406
};

407
408
409
410
host: HOST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr h(new StringElement($4));
411
    ctx.stack_.back()->set("host", h);
412
    ctx.leave();
413
414
};

415
416
417
name: NAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
418
419
    ElementPtr name(new StringElement($4));
    ctx.stack_.back()->set("name", name);
420
    ctx.leave();
421
422
};

423
424
425
426
427
persist: PERSIST COLON BOOLEAN {
    ElementPtr n(new BoolElement($3));
    ctx.stack_.back()->set("persist", n);
};

428
429
430
431
432
lfc_interval: LFC_INTERVAL COLON INTEGER {
    ElementPtr n(new IntElement($3));
    ctx.stack_.back()->set("lfc-interval", n);
};

433
434
435
436
mac_sources: MAC_SOURCES {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("mac-sources", l);
    ctx.stack_.push_back(l);
437
    ctx.enter(ctx.MAC_SOURCES);
438
} COLON LSQUARE_BRACKET mac_sources_list RSQUARE_BRACKET {
439
    ctx.stack_.pop_back();
440
    ctx.leave();
441
442
};

443
mac_sources_list: mac_sources_value
444
445
                | mac_sources_list COMMA mac_sources_value
;
446

447
448
mac_sources_value: duid_id
                 | string_id
449
450
                 ;

451
452
453
454
455
456
457
458
459
460
duid_id : DUID {
    ElementPtr duid(new StringElement("duid"));
    ctx.stack_.back()->add(duid);
};

string_id : STRING {
    ElementPtr duid(new StringElement($1));
    ctx.stack_.back()->add(duid);
};

461
host_reservation_identifiers: HOST_RESERVATION_IDENTIFIERS {
462
463
464
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("host-reservation-identifiers", l);
    ctx.stack_.push_back(l);
465
466
    ctx.enter(ctx.HOST_RESERVATION_IDENTIFIERS);    
} COLON LSQUARE_BRACKET host_reservation_identifiers_list RSQUARE_BRACKET {
467
    ctx.stack_.pop_back();
468
    ctx.leave();
469
470
471
};

host_reservation_identifiers_list: host_reservation_identifier
472
473
    | host_reservation_identifiers_list COMMA host_reservation_identifier
    ;
474

475
476
host_reservation_identifier: duid_id
                           | hw_address_id
477
                           ;
478

479
480
481
482
483
hw_address_id : HW_ADDRESS {
    ElementPtr hwaddr(new StringElement("hw-address"));
    ctx.stack_.back()->add(hwaddr);
};

484
485
486
487
relay_supplied_options: RELAY_SUPPLIED_OPTIONS {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("relay-supplied-options", l);
    ctx.stack_.push_back(l);
488
    ctx.enter(ctx.NO_KEYWORD);
489
} COLON list2 {
490
    ctx.stack_.pop_back();
491
    ctx.leave();
492
493
};

494
hooks_libraries: HOOKS_LIBRARIES {
495
496
497
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("hooks-libraries", l);
    ctx.stack_.push_back(l);
498
499
    ctx.enter(ctx.HOOKS_LIBRARIES);
} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
500
    ctx.stack_.pop_back();
501
    ctx.leave();
502
503
};

504
hooks_libraries_list: %empty
505
                    | not_empty_hooks_libraries_list
506
                    ;
507

508
509
510
511
not_empty_hooks_libraries_list: hooks_library
    | not_empty_hooks_libraries_list COMMA hooks_library
    ;

512
513
514
515
516
517
518
519
520
hooks_library: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} hooks_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

hooks_params: hooks_param
521
            | hooks_params COMMA hooks_param
522
            ;
523

524
525
526
527
528
529
hooks_param: LIBRARY {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr lib(new StringElement($4));
    ctx.stack_.back()->set("library", lib);
    ctx.leave(); 
530
531
532
};

// --- expired-leases-processing ------------------------
533
expired_leases_processing: EXPIRED_LEASES_PROCESSING {
534
535
536
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("expired-leases-processing", m);
    ctx.stack_.push_back(m);
537
538
    ctx.enter(ctx.NO_KEYWORD);
} COLON LCURLY_BRACKET expired_leases_params RCURLY_BRACKET {
539
    ctx.stack_.pop_back();
540
    ctx.leave();
541
542
543
};

expired_leases_params: expired_leases_param
544
545
                     | expired_leases_params COMMA expired_leases_param
                     ;
546
547
548
549
550

// This is a bit of a simplification. But it can also serve as an example.
// Instead of explicitly listing all allowed expired leases parameters, we
// simply say that all of them as integers.
expired_leases_param: STRING COLON INTEGER {
551
552
    ElementPtr value(new IntElement($3));
    ctx.stack_.back()->set($1, value);
553
554
555
}

// --- subnet6 ------------------------------------------
556
557
// This defines subnet6 as a list of maps.
// "subnet6": [ ... ]
558
subnet6_list: SUBNET6 {
559
    ElementPtr l(new ListElement());
560
    ctx.stack_.back()->set("subnet6", l);
561
    ctx.stack_.push_back(l);
562
563
    ctx.enter(ctx.SUBNET6);
} COLON LSQUARE_BRACKET subnet6_list_content RSQUARE_BRACKET {
564
    ctx.stack_.pop_back();
565
    ctx.leave();
566
567
568
569
570
};

// This defines the ... in "subnet6": [ ... ]
// It can either be empty (no subnets defined), have one subnet
// or have multiple subnets separate by comma.
571
subnet6_list_content: %empty
572
                    | not_empty_subnet6_list
573
                    ;
574

575
576
577
578
not_empty_subnet6_list: subnet6
                      | not_empty_subnet6_list COMMA subnet6
                      ;

579
580
// --- Subnet definitions -------------------------------

581
582
// This defines a single subnet, i.e. a single map with
// subnet6 array.
583
584
585
586
subnet6: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
587
} subnet6_params RCURLY_BRACKET {
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
    // Once we reached this place, the subnet parsing is now complete.
    // If we want to, we can implement default values here.
    // In particular we can do things like this:
    // if (!ctx.stack_.back()->get("interface")) {
    //     ctx.stack_.back()->set("interface", StringElement("loopback"));
    // }
    //
    // We can also stack up one level (Dhcp6) and copy over whatever
    // global parameters we want to:
    // if (!ctx.stack_.back()->get("renew-timer")) {
    //     ElementPtr renew = ctx_stack_[...].get("renew-timer");
    //     if (renew) {
    //         ctx.stack_.back()->set("renew-timer", renew);
    //     }
    // }
603
    ctx.stack_.pop_back();
604
};
605

606
// This defines that subnet can have one or more parameters.
607
subnet6_params: subnet6_param
608
609
              | subnet6_params COMMA subnet6_param
              ;
610

611
612
// This defines a list of allowed parameters for each subnet.
subnet6_param: option_data_list
613
614
615
616
617
618
619
             | pools_list
             | pd_pools_list
             | subnet
             | interface
             | id
             | client_class
             | reservations
620
             | unknown_map_entry
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
             ;

subnet: SUBNET {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr subnet(new StringElement($4));
    ctx.stack_.back()->set("subnet", subnet);
    ctx.leave();
};

interface: INTERFACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr iface(new StringElement($4));
    ctx.stack_.back()->set("interface", iface);
    ctx.leave();
};

client_class: CLIENT_CLASS {
    ctx.enter(ctx.CLIENT_CLASS);
} COLON STRING {
    ElementPtr cls(new StringElement($4));
    ctx.stack_.back()->set("client-class", cls);
    ctx.leave();
645
646
};

647
id: ID COLON INTEGER {
648
649
    ElementPtr id(new IntElement($3));
    ctx.stack_.back()->set("id", id);
650
};
651

652
653
654
655
// ---- option-data --------------------------

// This defines the "option-data": [ ... ] entry that may appear
// in several places, but most notably in subnet6 entries.
656
657
658
659
option_data_list: OPTION_DATA {
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("option-data", l);
    ctx.stack_.push_back(l);
660
    ctx.enter(ctx.OPTION_DATA);
661
662
} COLON LSQUARE_BRACKET option_data_list_content RSQUARE_BRACKET {
    ctx.stack_.pop_back();
663
    ctx.leave();
664
};
665
666
667

// This defines the content of option-data. It may be empty,
// have one entry or multiple entries separated by comma.
668
option_data_list_content: %empty
669
                        | not_empty_option_data_list
670
                        ;
671

672
673
674
675
not_empty_option_data_list: option_data_entry
                          | not_empty_option_data_list COMMA option_data_entry
                          ;

676
677
// This defines th content of a single entry { ... } within
// option-data list.
678
679
680
681
682
683
684
option_data_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} option_data_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
685
686
687

// This defines parameters specified inside the map that itself
// is an entry in option-data list.
688
689
option_data_params: %empty
                  | not_empty_option_data_params
690
                  ;
691

692
693
694
695
696
not_empty_option_data_params: option_data_param
    | not_empty_option_data_params COMMA option_data_param
    ;

option_data_param: option_data_name
697
698
699
700
                 | option_data_data
                 | option_data_code
                 | option_data_space
                 | option_data_csv_format
701
                 | unknown_map_entry
702
703
704
                 ;


705
option_data_name: name;
706

707
708
709
710
711
712
option_data_data: DATA {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr data(new StringElement($4));
    ctx.stack_.back()->set("data", data);
    ctx.leave();
713
714
715
};

option_data_code: CODE COLON INTEGER {
716
717
    ElementPtr code(new IntElement($3));
    ctx.stack_.back()->set("code", code);
718
};
719

720
721
722
723
724
725
option_data_space: SPACE {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr space(new StringElement($4));
    ctx.stack_.back()->set("space", space);
    ctx.leave();
726
727
};

728
option_data_csv_format: CSV_FORMAT COLON BOOLEAN {
729
730
    ElementPtr space(new BoolElement($3));
    ctx.stack_.back()->set("csv-format", space);
731
732
};

733
734
735
// ---- pools ------------------------------------

// This defines the "pools": [ ... ] entry that may appear in subnet6.
736
pools_list: POOLS {
737
738
739
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("pools", l);
    ctx.stack_.push_back(l);
740
741
    ctx.enter(ctx.POOLS);
} COLON LSQUARE_BRACKET pools_list_content RSQUARE_BRACKET {
742
    ctx.stack_.pop_back();
743
    ctx.leave();
744
};
745
746
747

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
748
pools_list_content: %empty
749
                  | not_empty_pools_list
750
                  ;
751

752
753
754
755
not_empty_pools_list: pool_list_entry
                    | not_empty_pools_list COMMA pool_list_entry
                    ;

756
pool_list_entry: LCURLY_BRACKET {
757
758
759
760
761
762
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
763
764

pool_params: pool_param
765
766
767
768
769
           | pool_params COMMA pool_param
           ;

pool_param: pool_entry
          | option_data_list
770
          | unknown_map_entry
771
772
773
774
775
776
777
778
779
          ;

pool_entry: POOL {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr pool(new StringElement($4));
    ctx.stack_.back()->set("pool", pool);
    ctx.leave();
};
780

781
782
// --- end of pools definition -------------------------------

783
// --- pd-pools ----------------------------------------------
784
pd_pools_list: PD_POOLS {
785
786
787
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("pd-pools", l);
    ctx.stack_.push_back(l);
788
789
    ctx.enter(ctx.PD_POOLS);
} COLON LSQUARE_BRACKET pd_pools_list_content RSQUARE_BRACKET {
790
    ctx.stack_.pop_back();
791
    ctx.leave();
792
793
794
795
};

// Pools may be empty, contain a single pool entry or multiple entries
// separate by commas.
796
pd_pools_list_content: %empty
797
                     | not_empty_pd_pools_list
798
                     ;
799

800
801
802
803
not_empty_pd_pools_list: pd_pool_entry
                       | not_empty_pd_pools_list COMMA pd_pool_entry
                       ;

804
805
806
807
808
809
810
811
812
pd_pool_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} pd_pool_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

pd_pool_params: pd_pool_param
813
814
              | pd_pool_params COMMA pd_pool_param
              ;
815
816

pd_pool_param: pd_prefix
817
818
819
             | pd_prefix_len
             | pd_delegated_len
             | option_data_list
820
             | unknown_map_entry
821
822
823
824
825
826
827
828
             ;

pd_prefix: PREFIX {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr prf(new StringElement($4));
    ctx.stack_.back()->set("prefix", prf);
    ctx.leave();
829
830
831
}

pd_prefix_len: PREFIX_LEN COLON INTEGER {
832
833
    ElementPtr prf(new IntElement($3));
    ctx.stack_.back()->set("prefix-len", prf);
834
835
836
}

pd_delegated_len: DELEGATED_LEN COLON INTEGER {
837
838
    ElementPtr deleg(new IntElement($3));
    ctx.stack_.back()->set("delegated-len", deleg);
839
840
841
842
}

// --- end of pd-pools ---------------------------------------

843
// --- reservations ------------------------------------------
844
reservations: RESERVATIONS {
845
846
847
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("reservations", l);
    ctx.stack_.push_back(l);
848
849
    ctx.enter(ctx.RESERVATIONS);
} COLON LSQUARE_BRACKET reservations_list RSQUARE_BRACKET {
850
    ctx.stack_.pop_back();
851
852
    ctx.leave();
};
853

854
reservations_list: %empty
855
                 | not_empty_reservations_list
856
                 ;
857

858
859
860
861
not_empty_reservations_list: reservation
                           | not_empty_reservations_list COMMA reservation
                           ;

862
863
864
865
866
867
868
869
reservation: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} reservation_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

870
871
reservation_params: %empty
                  | not_empty_reservation_params
872
                  ;
873

874
875
876
877
not_empty_reservation_params: reservation_param
    | not_empty_reservation_params COMMA reservation_param
    ;

878
// @todo probably need to add mac-address as well here
879
reservation_param: duid
880
881
882
883
884
885
                 | reservation_client_classes
                 | ip_addresses
                 | prefixes
                 | hw_address
                 | hostname
                 | option_data_list
886
                 | unknown_map_entry
887
888
889
                 ;

ip_addresses: IP_ADDRESSES {
890
891
892
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("ip-addresses", l);
    ctx.stack_.push_back(l);
893
894
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
895
    ctx.stack_.pop_back();
896
    ctx.leave();
897
898
};

899
prefixes: PREFIXES  {
900
901
902
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("prefixes", l);
    ctx.stack_.push_back(l);
903
904
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
905
    ctx.stack_.pop_back();
906
    ctx.leave();
907
908
};

909
910
911
912
913
914
duid: DUID {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr d(new StringElement($4));
    ctx.stack_.back()->set("duid", d);
    ctx.leave();
915
916
};

917
918
919
920
921
922
hw_address: HW_ADDRESS {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr hw(new StringElement($4));
    ctx.stack_.back()->set("hw-address", hw);
    ctx.leave();
923
924
};

925
926
927
928
929
930
hostname: HOSTNAME {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr host(new StringElement($4));
    ctx.stack_.back()->set("hostname", host);
    ctx.leave();
931
932
}

933
reservation_client_classes: CLIENT_CLASSES {
934
935
936
    ElementPtr c(new ListElement());
    ctx.stack_.back()->set("client-classes", c);
    ctx.stack_.push_back(c);
937
938
    ctx.enter(ctx.NO_KEYWORD);
} COLON list2 {
939
    ctx.stack_.pop_back();
940
941
    ctx.leave();
};
942
943
944
945

// --- end of reservations definitions -----------------------

// --- client classes ----------------------------------------
946
client_classes: CLIENT_CLASSES {
947
948
949
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("client-classes", l);
    ctx.stack_.push_back(l);
950
951
    ctx.enter(ctx.CLIENT_CLASSES);
} COLON LSQUARE_BRACKET client_classes_list RSQUARE_BRACKET {
952
    ctx.stack_.pop_back();
953
    ctx.leave();
954
955
956
};

client_classes_list: client_class
957
958
                   | client_classes_list COMMA client_class
                   ;
959
960
961
962
963
964
965
966
967

client_class: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} client_class_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};

968
969
client_class_params: %empty
                   | not_empty_client_class_params
970
                   ;
971

972
973
974
975
976
not_empty_client_class_params: client_class_param
    | not_empty_client_class_params COMMA client_class_param
    ;

client_class_param: client_class_name
977
978
                  | client_class_test
                  | option_data_list
979
                  | unknown_map_entry
980
981
                  ;

982
client_class_name: name;
983

984
985
986
987
client_class_test: TEST {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr test(new StringElement($4));
988
    ctx.stack_.back()->set("test", test);
989
    ctx.leave();
990
991
992
993
994
}


// --- end of client classes ---------------------------------

995
// --- server-id ---------------------------------------------
996
server_id: SERVER_ID {
997
998
999
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("server-id", m);
    ctx.stack_.push_back(m);
1000
1001
    ctx.enter(ctx.SERVER_ID);
} COLON LCURLY_BRACKET server_id_params RCURLY_BRACKET {
1002
    ctx.stack_.pop_back();
1003
    ctx.leave();
1004
1005
1006
};

server_id_params: server_id_param
1007
1008
                | server_id_params COMMA server_id_param
                ;
1009
1010

server_id_param: type
1011
1012
1013
1014
1015
               | identifier
               | time
               | htype
               | enterprise_id
               | persist
1016
               | unknown_map_entry
1017
               ;
1018
1019
1020
1021
1022
1023

htype: HTYPE COLON INTEGER {
    ElementPtr htype(new IntElement($3));
    ctx.stack_.back()->set("htype", htype);
};

1024
1025
1026
1027
identifier: IDENTIFIER {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr id(new StringElement($4));
1028
    ctx.stack_.back()->set("identifier", id);
1029
    ctx.leave();
1030
1031
1032
1033
1034
1035
};

time: TIME COLON INTEGER {
    ElementPtr time(new IntElement($3));
    ctx.stack_.back()->set("time", time);
};
1036
1037
1038
1039
1040
1041

enterprise_id: ENTERPRISE_ID COLON INTEGER {
    ElementPtr time(new IntElement($3));
    ctx.stack_.back()->set("enterprise-id", time);
};

1042
1043
// --- end of server-id --------------------------------------

1044
1045
1046
1047
1048
dhcp4o6_port: DHCP4O6_PORT COLON INTEGER {
    ElementPtr time(new IntElement($3));
    ctx.stack_.back()->set("dhcp4o6-port", time);
};

1049
1050
1051
1052
1053
// --- logging entry -----------------------------------------

// This defines the top level "Logging" object. It parses
// the following "Logging": { ... }. The ... is defined
// by logging_params
1054
logging_object: LOGGING {
1055
1056
1057
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("Logging", m);
    ctx.stack_.push_back(m);
1058
1059
    ctx.enter(ctx.LOGGING);
} COLON LCURLY_BRACKET logging_params RCURLY_BRACKET {
1060
    ctx.stack_.pop_back();
1061
    ctx.leave();
1062
1063
1064
1065
1066
1067
};

// This defines the list of allowed parameters that may appear
// in the top-level Logging object. It can either be a single
// parameter or several parameters separated by commas.
logging_params: logging_param
1068
1069
              | logging_params COMMA logging_param
              ;
1070
1071
1072
1073
1074
1075

// There's currently only one parameter defined, which is "loggers".
logging_param: loggers;

// "loggers", the only parameter currently defined in "Logging" object,
// is "Loggers": [ ... ].
1076
loggers: LOGGERS {
1077
1078
1079
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("loggers", l);
    ctx.stack_.push_back(l);
1080
1081
    ctx.enter(ctx.LOGGERS);
}  COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
1082
    ctx.stack_.pop_back();
1083
    ctx.leave();
1084
};
1085
1086
1087
1088

// These are the parameters allowed in loggers: either one logger
// entry or multiple entries separate by commas.
loggers_entries: logger_entry
1089
1090
               | loggers_entries COMMA logger_entry
               ;
1091
1092

// This defines a single entry defined in loggers in Logging.
1093
1094
1095
1096
1097
1098
1099
logger_entry: LCURLY_BRACKET {
    ElementPtr l(new MapElement());
    ctx.stack_.back()->add(l);
    ctx.stack_.push_back(l);
} logger_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
1100
1101

logger_params: logger_param
1102
1103
             | logger_params COMMA logger_param
             ;
1104

1105
logger_param: name
1106
1107
1108
            | output_options_list
            | debuglevel
            | severity
1109
            | unknown_map_entry
1110
1111
            ;

1112
debuglevel: DEBUGLEVEL COLON INTEGER {
1113
1114
    ElementPtr dl(new IntElement($3));
    ctx.stack_.back()->set("debuglevel", dl);
1115
};
1116
1117
1118
1119
1120
1121
severity: SEVERITY {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr sev(new StringElement($4));
    ctx.stack_.back()->set("severity", sev);
    ctx.leave();
1122
};
1123

1124
output_options_list: OUTPUT_OPTIONS {
1125
1126
1127
    ElementPtr l(new ListElement());
    ctx.stack_.back()->set("output_options", l);
    ctx.stack_.push_back(l);
1128
1129
    ctx.enter(ctx.OUTPUT_OPTIONS);
} COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
1130
    ctx.stack_.pop_back();
1131
    ctx.leave();
1132
};
1133
1134

output_options_list_content: output_entry
1135
1136
                           | output_options_list_content COMMA output_entry
                           ;
1137

1138
1139
1140
1141
1142
1143
1144
output_entry: LCURLY_BRACKET {
    ElementPtr m(new MapElement());
    ctx.stack_.back()->add(m);
    ctx.stack_.push_back(m);
} output_params RCURLY_BRACKET {
    ctx.stack_.pop_back();
};
1145
1146

output_params: output_param
1147
1148
             | output_params COMMA output_param
             ;
1149

1150
1151
1152
1153
1154
1155
output_param: OUTPUT {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr sev(new StringElement($4));
    ctx.stack_.back()->set("output", sev);
    ctx.leave();
1156
};
1157

1158
dhcp_ddns: DHCP_DDNS {
1159
1160
1161
    ElementPtr m(new MapElement());
    ctx.stack_.back()->set("dhcp-ddns", m);
    ctx.stack_.push_back(m);
1162
1163
    ctx.enter(ctx.DHCP_DDNS);
} COLON LCURLY_BRACKET dhcp_ddns_params RCURLY_BRACKET {
1164
    ctx.stack_.pop_back();
1165
    ctx.leave();
1166
1167
1168
};

dhcp_ddns_params: dhcp_ddns_param
1169
1170
                | dhcp_ddns_params COMMA dhcp_ddns_param
                ;
1171
1172

dhcp_ddns_param: enable_updates
1173
               | qualifying_suffix
1174
               | unknown_map_entry
1175
               ;
1176

1177
1178
1179
1180
1181
enable_updates: ENABLE_UPDATES COLON BOOLEAN {
    ElementPtr b(new BoolElement($3));
    ctx.stack_.back()->set("enable-updates", b);
};

1182
1183
1184
1185
qualifying_suffix: QUALIFYING_SUFFIX {
    ctx.enter(ctx.NO_KEYWORD);
} COLON STRING {
    ElementPtr qs(new StringElement($4));
1186
    ctx.stack_.back()->set("qualifying-suffix", qs);
1187
    ctx.leave();
1188
};
1189

1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
dhcp4_object: DHCP4 {
    ctx.enter(ctx.NO_KEYWORD);
} COLON value {
    ctx.stack_.back()->set("Dhcp4", $4);
    ctx.leave();
};

dhcpddns_object: DHCPDDNS {
    ctx.enter(ctx.NO_KEYWORD);
} COLON value {
    ctx.stack_.back()->set("DhcpDdns", $4);
    ctx.leave();
};

1204
1205
1206
1207
%%

void
isc::dhcp::Dhcp6Parser::error(const location_type& loc,
1208
                              const std::string& what)
1209
1210
1211
{
    ctx.error(loc, what);
}