1
2
3
4
5
6
7
8
9
10
11
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
|
<!--
- Copyright (C) 2015-2018 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/.
-->
<!-- Converted by db4-upgrade version 1.1 -->
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="classify">
<title>Client Classification</title>
<section>
<title>Client Classification Overview</title>
<para>
In certain cases it is useful to differentiate between different
types of clients and treat them accordingly. Common reasons include:
<itemizedlist>
<listitem><para>
The clients represent different pieces of topology, e.g. a cable
modem is different to the clients behind that modem.
</para></listitem>
<listitem><para>
The clients have different behavior, e.g. a smart phone behaves
differently to a laptop.
</para></listitem>
<listitem><para>
The clients require different values for some options, e.g. a docsis3.0
cable modem requires different settings to docsis2.0 cable modem.
</para></listitem>
</itemizedlist>
</para>
<para>
Conversely, different clients can be grouped into a client class to get a
common option.
</para>
<para>
An incoming packet can be associated with a client class in
serveral ways:
<itemizedlist>
<listitem><para>
Implicitly, using a vendor class option or another builtin condition.
</para></listitem>
<listitem><para>
Using an expression which evaluates to true.
</para></listitem>
<listitem><para>
Using static host reservations, a shared network, a subnet, etc.
</para></listitem>
<listitem><para>
Using a hook.
</para></listitem>
</itemizedlist>
</para>
<para>
It is envisaged that client classification will be used for
changing the behavior of almost any part of the DHCP message
processing. In the current release of the software however,
there are only five mechanisms that take advantage of
client classification: subnet selection, pool selection,
definition of DHCPv4 private (codes 224-254) and code 43
options, assignment of different options and, for DHCPv4 cable
modems, the setting of specific options for use with the TFTP
server address and the boot file field.
</para>
<para>
The process of doing classification is conducted in several steps:
<orderedlist>
<listitem><para>
The ALL class is associated with the incoming packet.
</para></listitem>
<listitem><para>
Vendor class options are processed.
</para></listitem>
<listitem><para>
Classes with matching expressions and not marked for later ("on
request" or depending on the KNOWN/UNKNOWN builtin classes)
evaluation are processed in the order they are defined in the
configuration: the boolean expression is evaluated and when it
returns true ("match") the incoming packet is associated to the
class.
</para></listitem>
<listitem><para>
If a private or code 43 DHCPv4 option is received, decoding it
following its client class or global (or for option 43 last
resort) definition.
</para></listitem>
<listitem><para>
Choose a subnet, possibly based on the class information when
some subnets are guarded. More precisely: when choosing a subnet,
the server will iterate over all of the subnets that are
feasible given the information found in the packet (client
address, relay address etc). It will use the first subnet it
finds that either doesn't have a class associated with it or
that has a class which matches one of the packet's classes.
</para></listitem>
<listitem><para>
Host reservations are looked for. If an identifier from the
incoming packet matches a host reservation in the subnet or
shared network, the packet is associated with the KNOWN class
and all classes of the host reservation. If a reservation is not
found, the packet is assigned to UNKNOWN class.
</para></listitem>
<listitem><para>
Classes with matching expressions using directly or indirectly
the KNOWN/UNKNOWN builtin classes and not marked for later ("on
request") evaluation are processed in the order they are defined
in the configuration: the boolean expression is evaluated and
when it returns true ("match") the incoming packet is associated
to the class. The determination whether there is a reservation
for a given client is made after a subnet is selected. As such, it
is not possible to use KNOWN/UNKNOWN classes to select a shared
network or a subnet.
</para></listitem>
<listitem><para>
If needed, addresses and prefixes from pools are assigned,
possibly based on the class information when some pools are
reserved to class members.
</para></listitem>
<listitem><para>
Evaluate classes marked as "required" in the order in which they
are listed as required: first shared network, then the subnet
and to finally pools assigned resources belong too.
</para></listitem>
<listitem><para>
Assign options, again possibly based on the class information
in order classes were associated with the incoming packet.
For DHCPv4 private and code 43 options this includes class local
option definitions.
</para></listitem>
</orderedlist>
</para>
<note>
<para>
Beginning with Kea 1.4.0 release, client classes follow the order
in which they are specified in the configuration
(vs. alphabetical order in previous releases). Required classes
follow the order in which they are required.
</para>
</note>
<para>
When determining which options to include in the response, the
server will examine the union of options from all of the
assigned classes. In case when two or more classes include the
same option, the value from the first class examined will be
used, and classes are examined in the order they were associated
so ALL is always the first class and matching required classes
are last.
</para>
<para>
As an example, imagine that an incoming packet matches two
classes. Class "foo" defines values for an NTP server (option
42 in DHCPv4) and an SMTP server (option 69 in DHCPv4) while
class "bar" defines values for an NTP server and a POP3 server
(option 70 in DHCPv4). The server will examine the three
options NTP, SMTP and POP3 and return any of them that the
client requested. As the NTP server was defined twice the
server will choose only one of the values for the reply: the
class from which the value is obtained is unspecified.
</para>
<note>
<para>
Care should be taken with client classification as it is easy for
clients that do not meet class criteria to be denied any service altogether.
</para>
</note>
</section>
<section xml:id="classification-using-vendor">
<title>Builtin Client Classes</title>
<para>
Some classes are builtin so do not need to be defined. The main
example uses Vendor Class information: The server checks whether
an incoming DHCPv4 packet includes the vendor class identifier
option (60) or an incoming DHCPv6 packet includes the vendor
class option (16). If it does, the content of that option is
prepended with "VENDOR_CLASS_" and the result is
interpreted as a class. For example, modern cable modems will
send this option with value "docsis3.0" and so the
packet will belong to class "VENDOR_CLASS_docsis3.0".
</para>
<para>The "HA_" prefix is used by the High Availability
hooks library to designate certain servers to process DHCP packets
as a result of load balancing. The class name is constructed by
prepending the "HA_" prefix to the name of the server
which should process the DHCP packet. This server will use appropriate
pool or subnet to allocate IP addresses (and/or prefixes) from, based on
the assigned client classes. The details can be found in
<xref linkend="high-availability-library"/>.</para>
<para>Other examples are: the ALL class which all incoming packets
belong to, and the KNOWN class assigned when host reservations exist
for the particular client. By convention, builtin classes' names
begin with all capital letters.
</para>
<para>Currently recognized builtin class names are ALL, KNOWN
and UNKNOWN, and prefixes VENDOR_CLASS_, HA_, AFTER_ and
EXTERNAL_. The AFTER_ prefix is a provision for a not yet
written hook, the EXTERNAL_ prefix can be freely used: builtin
classes are implicitly defined so never raise warnings if they
do not appear in the configuration.
</para>
</section>
<section xml:id="classification-using-expressions">
<title>Using Expressions In Classification</title>
<para>
The expression portion of classification contains operators and
values. All values are currently strings and operators take a
string or strings and return another string. When all the
operations have completed the result should be a value of
"true" or "false". The packet belongs to
the class (and the class name is added to the list of classes)
if the result is "true". Expressions are written in
standard format and can be nested.
</para>
<para>
Expressions are pre-processed during the parsing of the
configuration file and converted to an internal
representation. This allows certain types of errors to be caught
and logged during parsing. Examples of these errors include an
incorrect number or types of arguments to an operator. The
evaluation code will also check for this class of error and
generally throw an exception, though this should not occur in a
normally functioning system.
</para>
<para>
Other issues, for example the starting position of a substring being
outside of the substring or an option not existing in the packet, result
in the operator returning an empty string.
</para>
<para>
Expressions are a work in progress and the supported operators and
values are limited. The expectation is that additional operators and values
will be added over time, however the basic mechanisms will
remain the same.
</para>
<para>
Dependencies between classes are checked too: for instance
forward dependencies are rejected when the configuration is
parsed: an expression can only depend on already defined classes
(including builtin classes) and which are evaluated in a
previous or the same evaluation phase. This does not apply to
the KNOWN or UNKNOWN classes.
</para>
<para>
<table frame="all" xml:id="classification-values-list">
<title>List of Classification Values</title>
<tgroup cols="3">
<colspec colname="name"/>
<colspec colname="example"/>
<colspec colname="description"/>
<thead>
<row>
<entry>Name</entry>
<entry>Example expression</entry>
<entry>Example value</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>String literal</entry>
<entry>'example'</entry>
<entry>'example'</entry>
<entry>A string</entry>
</row>
<row>
<entry>Hexadecimal string literal</entry>
<entry>0x5a7d</entry>
<entry>'Z}'</entry>
<entry>A hexadecimal string</entry>
</row>
<row>
<entry>IP address literal</entry>
<entry>10.0.0.1</entry>
<entry>0x0a000001</entry>
<entry>An IP address</entry>
</row>
<row>
<entry>Integer literal</entry>
<entry>123</entry>
<entry>'123'</entry>
<entry>A 32 bit unsigned integer value</entry>
</row>
<row/>
<row>
<entry>Binary content of the option</entry>
<entry>option[123].hex</entry>
<entry>'(content of the option)'</entry>
<entry>The value of the option with given code from the
packet as hex</entry>
</row>
<!-- Text option not fully defined yet, leave it out
<row>
<entry>Option Text</entry>
<entry>option[123].text</entry>
<entry>'foobar'</entry>
<entry>The value of the option with given code from the
packet as text</entry>
</row>
-->
<row>
<entry>Option existence</entry>
<entry>option[123].exists</entry>
<entry>'true'</entry>
<entry>If the option with given code is present in the
packet "true" else "false"</entry>
</row>
<row>
<entry>Client class membership</entry>
<entry>member('foobar')</entry>
<entry>'true'</entry>
<entry>If the packet belongs to the given client class
"true" else "false"</entry>
</row>
<row>
<entry>Known client</entry>
<entry>known</entry>
<entry>member('KNOWN')</entry>
<entry>If there is a host reservation for the client
"true" else "false"</entry>
</row>
<row>
<entry>Unknown client</entry>
<entry>unknown</entry>
<entry>not member('KNOWN')</entry>
<entry>If there is a host reservation for the client
"false" else "true"</entry>
</row>
<row>
<entry>DHCPv4 relay agent sub-option</entry>
<entry>relay4[123].hex</entry>
<entry>'(content of the RAI sub-option)'</entry>
<entry>The value of sub-option with given code from the
DHCPv4 Relay Agent Information option (option 82)</entry>
</row>
<row>
<entry>DHCPv6 Relay Options</entry>
<entry>relay6[nest].option[code].hex</entry>
<entry>(value of the option)</entry>
<entry>The value of the option with code "code" from the
relay encapsulation "nest"</entry>
</row>
<row>
<entry>DHCPv6 Relay Peer Address</entry>
<entry>relay6[nest].peeraddr</entry>
<entry>2001:DB8::1</entry>
<entry>The value of the peer address field from the
relay encapsulation "nest"</entry>
</row>
<row>
<entry>DHCPv6 Relay Link Address</entry>
<entry>relay6[nest].linkaddr</entry>
<entry>2001:DB8::1</entry>
<entry>The value of the link address field from the
relay encapsulation "nest"</entry>
</row>
<row>
<entry>Interface name of packet</entry>
<entry>pkt.iface</entry>
<entry>eth0</entry>
<entry>The name of the incoming interface of a DHCP packet.</entry>
</row>
<row>
<entry>Source address of packet</entry>
<entry>pkt.src</entry>
<entry>10.1.2.3</entry>
<entry>The IP source address of a DHCP packet.</entry>
</row>
<row>
<entry>Destination address of packet</entry>
<entry>pkt.dst</entry>
<entry>10.1.2.3</entry>
<entry>The IP destination address of a DHCP packet.</entry>
</row>
<row>
<entry>Length of packet</entry>
<entry>pkt.len</entry>
<entry>513</entry>
<entry>The length of a DHCP packet (UDP header field), expressed
as a 32 bit unsigned integer.</entry>
</row>
<row>
<entry>Hardware address in DHCPv4 packet</entry>
<entry>pkt4.mac</entry>
<entry>0x010203040506</entry>
<entry>The value of the chaddr field of the DHCPv4 packet, hlen (0 to 16) bytes</entry>
</row>
<row>
<entry>Hardware length in DHCPv4 packet</entry>
<entry>pkt4.hlen</entry>
<entry>6</entry>
<entry>The value of the hlen field of the DHCPv4 packet padded to 4 bytes</entry>
</row>
<row>
<entry>Hardware type in DHCPv4 packet</entry>
<entry>pkt4.htype</entry>
<entry>6</entry>
<entry>The value of the htype field of the DHCPv4 packet padded to 4 bytes</entry>
</row>
<row>
<entry>ciaddr field in DHCPv4 packet</entry>
<entry>pkt4.ciaddr</entry>
<entry>192.0.2.1</entry>
<entry>The value of the ciaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
</row>
<row>
<entry>giaddr field in DHCPv4 packet</entry>
<entry>pkt4.giaddr</entry>
<entry>192.0.2.1</entry>
<entry>The value of the giaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
</row>
<row>
<entry>yiaddr field in DHCPv4 packet</entry>
<entry>pkt4.yiaddr</entry>
<entry>192.0.2.1</entry>
<entry>The value of the yiaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
</row>
<row>
<entry>siaddr field in DHCPv4 packet</entry>
<entry>pkt4.siaddr</entry>
<entry>192.0.2.1</entry>
<entry>The value of the siaddr field of the DHCPv4 packet (IPv4 address, 4 bytes)</entry>
</row>
<row>
<entry>Message Type in DHCPv4 packet</entry>
<entry>pkt4.msgtype</entry>
<entry>1</entry>
<entry>The value of the message type field in the DHCPv4
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Transaction ID (xid) in DHCPv4 packet</entry>
<entry>pkt4.transid</entry>
<entry>12345</entry>
<entry>The value of the transaction id in the DHCPv4
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Message Type in DHCPv6 packet</entry>
<entry>pkt6.msgtype</entry>
<entry>1</entry>
<entry>The value of the message type field in the DHCPv6
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Transaction ID in DHCPv6 packet</entry>
<entry>pkt6.transid</entry>
<entry>12345</entry>
<entry>The value of the transaction id in the DHCPv6
packet (expressed as a 32 bit unsigned integer).</entry>
</row>
<row>
<entry>Vendor option existence (any vendor)</entry>
<entry>vendor[*].exists</entry>
<entry>true</entry>
<entry>Returns whether a vendor option from any vendor
is present ('true') or absent ('false').</entry>
</row>
<row>
<entry>Vendor option existence (specific vendor)</entry>
<entry>vendor[4491].exists</entry>
<entry>true</entry>
<entry>Returns whether a vendor option from specified
vendor (determined by its enterprise-id)
is present ('true') or absent ('false').</entry>
</row>
<row>
<entry>Enterprise-id from vendor option</entry>
<entry>vendor.enterprise</entry>
<entry>4491</entry>
<entry>If the vendor option is present, it returns the
value of the enterprise-id field padded to 4
bytes. Returns "" otherwise.</entry>
</row>
<row>
<entry>Vendor sub-option existence</entry>
<entry>vendor[4491].option[1].exists</entry>
<entry>true</entry>
<entry>Returns 'true' if there is vendor option with
specified enterprise-id and given sub-option is present.
Returns 'false' otherwise.</entry>
</row>
<row>
<entry>Vendor sub-option content</entry>
<entry>vendor[4491].option[1].hex</entry>
<entry>docsis3.0</entry>
<entry>Returns content of the specified sub-option of
a vendor option with specified enterprise id. Returns
'' if no such option or sub-option is present.
</entry>
</row>
<row>
<entry>Vendor class option existence (any vendor)</entry>
<entry>vendor-class[*].exists</entry>
<entry>true</entry>
<entry>Returns whether a vendor class option from any vendor
is present ('true') or absent ('false').</entry>
</row>
<row>
<entry>Vendor class option existence (specific vendor)</entry>
<entry>vendor-class[4491].exists</entry>
<entry>true</entry>
<entry>Returns whether a vendor class option from specified
vendor (determined by its enterprise-id)
is present ('true') or absent ('false').</entry>
</row>
<row>
<entry>Enterprise-id from vendor class option</entry>
<entry>vendor-class.enterprise</entry>
<entry>4491</entry>
<entry>If the vendor option is present, it returns the
value of the enterprise-id field padded to 4
bytes. Returns "" otherwise.</entry>
</row>
<row>
<entry>First data chunk from vendor class option</entry>
<entry>vendor-class[4491].data</entry>
<entry>docsis3.0</entry>
<entry>Returns content of the first data chunk from
the vendor class option with specified enterprise-id.
Returns "" if missing.</entry>
</row>
<row>
<entry>Specific data chunk from vendor class option</entry>
<entry>vendor-class[4491].data[3]</entry>
<entry>docsis3.0</entry>
<entry>Returns content of the specified data chunk of
a vendor class option with specified enterprise id. Returns
'' if no such option or data chunk is present.
</entry>
</row>
</tbody>
</tgroup>
</table>
Notes:
</para>
<itemizedlist>
<listitem><para>
Hexadecimal strings are converted into a string as expected. The starting "0X" or
"0x" is removed and if the string is an odd number of characters a
"0" is prepended to it.
</para></listitem>
<listitem><para>
IP addresses are converted into strings of length 4 or 16. IPv4, IPv6,
and IPv4 embedded IPv6 (e.g., IPv4 mapped IPv6) addresses are supported.
</para></listitem>
<listitem><para>
Integers in an expression are converted to 32 bit unsigned integers and
are represented as four-byte strings. For example 123 is represented as
0x0000007b. All expressions that return numeric values use 32-bit
unsigned integers, even if the field in the packet is smaller. In general
it is easier to use decimal notation to represent integers, but it is also
possible to use hex notation. When using hex notation to represent an
integer care should be taken to make sure the value is represented as 32
bits, e.g. use 0x00000001 instead of 0x1 or 0x01. Also, make
sure the value is specified in network order, e.g. 1 is
represented as 0x00000001.
</para></listitem>
<listitem><para>
"option[code].hex" extracts the value of the option with the code "code"
from the incoming packet. If the packet doesn't contain the option, it
returns the empty string. The string is presented as a byte string of
the option payload without the type code or length fields.
</para></listitem>
<listitem><para>
"option[code].exists" checks if an option with the code "code" is present
in the incoming packet. It can be used with empty options.
</para></listitem>
<listitem><para>
"member('foobar')" checks if the packet belongs to the client
class "foobar". To avoid dependency loops the configuration file
parser checks if client classes were already defined or are
built-in, i.e., beginning by "VENDOR_CLASS_",
"AFTER__" (for the to come "after" hook) and
"EXTERNAL_" or equal to "ALL", "KNOWN",
"UNKNOWN"etc.
</para>
<para>"known" and "unknown" are short hands for "member('KNOWN')" and
"not member('KNOWN')". Note the evaluation of any expression using
directly or indirectly the "KNOWN" class is deferred
after the host reservation lookup (i.e. when the "KNOWN"
or "UNKNOWN" partition is determined).
</para></listitem>
<listitem><para>
"relay4[code].hex" attempts to extract the value of the sub-option
"code" from the option inserted as the DHCPv4 Relay Agent Information
(82) option. If the packet doesn't contain a RAI option, or the RAI
option doesn't contain the requested sub-option, the expression returns
an empty string. The string is presented as a byte string of the
option payload without the type code or length fields. This
expression is allowed in DHCPv4 only.
</para></listitem>
<listitem><para>
"relay4" shares the same representation types as "option", for
instance "relay4[code].exists" is supported.
</para></listitem>
<listitem><para>
"relay6[nest]" allows access to the encapsulations used by any DHCPv6
relays that forwarded the packet. The "nest" level specifies the relay
from which to extract the information, with a value of 0 indicating
the relay closest to the DHCPv6 server. Negative values allow to
specify relays counted from the DHCPv6 client, -1 indicating the
relay closest to the client. In general negative "nest" level is
the same as the number of relays + "nest" level.
If the requested encapsulation doesn't exist an empty string ""
is returned. This expression is allowed in DHCPv6 only.
</para></listitem>
<listitem><para>
"relay6[nest].option[code]" shares the same representation types as
"option", for instance "relay6[nest].option[code].exists" is supported.
</para></listitem>
<listitem><para>
Expressions starting with "pkt4" can be used only in DHCPv4.
They allows access to DHCPv4 message fields.
</para></listitem>
<listitem><para>
"pkt6" refers to information from the client request. To access any
information from an intermediate relay use "relay6". "pkt6.msgtype"
and "pkt6.transid" output a 4 byte binary string for the message type
or transaction id. For example the message type SOLICIT will be
"0x00000001" or simply 1 as in "pkt6.msgtype == 1".
</para></listitem>
<listitem><para>
Vendor option means Vendor-Identifying Vendor-specific Information
option in DHCPv4 (code 125, see
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://tools.ietf.org/html/rfc3925#section-4">Section 4 of RFC 3925</link>) and
Vendor-specific Information Option in DHCPv6 (code 17, defined in
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://tools.ietf.org/html/rfc3315#section-22.17">Section 22.17 of
RFC 3315</link>). Vendor class option means Vendor-Identifying Vendor
Class Option in DHCPv4 (code 124, see
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://tools.ietf.org/html/rfc3925#section-3">Section 3 of RFC 3925</link>) in DHCPv4 and
Class Option in DHCPv6 (code 16, see
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://tools.ietf.org/html/rfc3315#section-22.16">Section 22.16 of RFC 3315</link>).
Vendor options may
have sub-options that are referenced by their codes. Vendor class
options do not have sub-options, but rather data chunks, which are
referenced by index value. Index 0 means the first data chunk, Index 1
is for the second data chunk (if present), etc.
</para></listitem>
<listitem><para>
In the vendor and vendor-class constructs Asterisk (*) or 0 can be
used to specify a wildcard enterprise-id value, i.e. it will match any
enterprise-id value.
</para></listitem>
<listitem><para>Vendor Class Identifier (option 60 in DHCPv4) can be
accessed using option[60] expression.</para></listitem>
<listitem><para>
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://tools.ietf.org/html/rfc3925">RFC3925</link> and
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://tools.ietf.org/html/rfc3315">RFC3315</link>
allow for multiple instances of vendor options
to appear in a single message. The client classification code currently
examines the first instance if more than one appear. For vendor.enterprise
and vendor-class.enterprise expressions, the value from the first instance
is returned. Please submit a feature request on Kea website if you need
support for multiple instances.
</para></listitem>
</itemizedlist>
<para>
<table frame="all" xml:id="classification-expressions-list">
<title>List of Classification Expressions</title>
<tgroup cols="3">
<colspec colname="name"/>
<colspec colname="example"/>
<colspec colname="description"/>
<thead>
<row>
<entry>Name</entry>
<entry>Example</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row><entry>Equal</entry> <entry>'foo' == 'bar'</entry><entry>Compare the two values and return "true" or "false"</entry></row>
<row><entry>Not</entry> <entry>not ('foo' == 'bar')</entry><entry>Logical negation</entry></row>
<row><entry>And</entry> <entry>('foo' == 'bar') and ('bar' == 'foo')</entry><entry>Logical and</entry></row>
<row><entry>Or</entry> <entry>('foo' == 'bar') or ('bar' == 'foo')</entry><entry>Logical or</entry></row>
<row><entry>Substring</entry><entry>substring('foobar',0,3)</entry><entry>Return the requested substring</entry></row>
<row><entry>Concat</entry><entry>concat('foo','bar')</entry><entry>Return the
concatenation of the strings</entry></row>
<row><entry>Ifelse</entry><entry>ifelse('foo' == 'bar','us','them')</entry><entry>Return the branch value according to the condition</entry></row>
</tbody>
</tgroup>
</table>
</para>
<section>
<title>Logical operators</title>
The Not, And and Or logical operators are the common operators. Not
has the highest precedence and Or the lowest. And and Or are (left)
associative, parentheses around a logical expression can be used
to enforce a specific grouping, for instance in "A and (B or C)"
(without parentheses "A and B or C" means "(A and B) or C").
</section>
<section>
<title>Substring</title>
The substring operator "substring(value, start, length)" accepts both positive and
negative values for the starting position and the length. For "start", a value of
0 is the first byte in the string while -1 is the last byte. If the starting
point is outside of the original string an empty string is returned. "length"
is the number of bytes to extract. A negative number means to count towards
the beginning of the string but doesn't include the byte pointed to by "start".
The special value "all" means to return all bytes from start to the end of the
string. If length is longer than the remaining portion of the string then
the entire remaining portion is returned. Some examples may be helpful:
<screen>
substring('foobar', 0, 6) == 'foobar'
substring('foobar', 3, 3) == 'bar'
substring('foobar', 3, all) == 'bar'
substring('foobar', 1, 4) == 'ooba'
substring('foobar', -5, 4) == 'ooba'
substring('foobar', -1, -3) == 'oba'
substring('foobar', 4, -2) == 'ob'
substring('foobar', 10, 2) == ''
</screen>
</section>
<section>
<title>Concat</title>
The concat function "concat(string1, string2)" returns the
concatenation of its two arguments. For instance:
<screen>
concat('foo', 'bar') == 'foobar'
</screen>
</section>
<section>
<title>Ifelse</title>
The ifelse function "ifelse(cond, iftrue, ifelse)" returns the
"iftrue" or "ifelse" branch value following the boolean
condition "cond". For instance:
<screen>
ifelse(option[230].exists, option[230].hex, 'none')
</screen>
</section>
</section>
<note>
<para>
The expression for each class is executed on each packet received.
If the expressions are overly complex, the time taken to execute
them may impact the performance of the server. If you need
complex or time consuming expressions you should write a <link linkend="hooks-libraries">hook</link> to perform the necessary work.
</para> </note>
<section xml:id="classification-configuring">
<title>Configuring Classes</title>
<para>
A class contains five items: a name, a test expression, option data,
option definition and only-if-required flag.
The name must exist and must be unique amongst all classes. The test
expression, option data and definition, and only-if-required flag are
optional.
</para>
<para>
The test expression is a string containing the logical expression used to
determine membership in the class. The entire expression is in double
quotes.
</para>
<para>
The option data is a list which defines any options that should be assigned
to members of this class.
</para>
<para>
The option definition is for DHCPv4 option 43 (<xref
linkend="dhcp4-vendor-opts"/> and DHCPv4 private options
(<xref linkend="dhcp4-private-opts"/>).
</para>
<para>
Usually the test expression is evaluated before subnet selection
but in some cases it is useful to evaluate it later when the
subnet, shared-network or pools are known but output option
processing not yet done. The only-if-required flag, false by default,
allows to perform the evaluation of the test expression only
when it was required, i.e. in a require-client-classes list of the
selected subnet, shared-network or pool.
</para>
<para>
The require-client-classes list which is valid for shared-network,
subnet and pool scope specifies the classes which are evaluated
in the second pass before output option processing.
The list is built in the reversed precedence order of option
data, i.e. an option data in a subnet takes precedence on one
in a shared-network but required class in a subnet is added
after one in a shared-network.
The mechanism is related to the only-if-required flag but it is
not mandatory that the flag was set to true.
</para>
<para>
In the following example the class named "Client_foo" is defined.
It is comprised of all clients whose client ids (option 61) start with the
string "foo". Members of this class will be given 192.0.2.1 and
192.0.2.2 as their domain name servers.
<screen>
"Dhcp4": {
"client-classes": [<userinput>
{
"name": "Client_foo",
"test": "substring(option[61].hex,0,3) == 'foo'",
"option-data": [
{
"name": "domain-name-servers",
"code": 6,
"space": "dhcp4",
"csv-format": true,
"data": "192.0.2.1, 192.0.2.2"
}
]
},
...
],</userinput>
...
}</screen>
</para>
<para>
This example shows a client class being defined for use by the DHCPv6 server.
In it the class named "Client_enterprise" is defined. It is comprised
of all clients who's client identifiers start with the given hex string (which
would indicate a DUID based on an enterprise id of 0xAABBCCDD). Members of this
class will be given an 2001:db8:0::1 and 2001:db8:2::1 as their domain name servers.
<screen>
"Dhcp6": {
"client-classes": [<userinput>
{
"name": "Client_enterprise",
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
"option-data": [
{
"name": "dns-servers",
"code": 23,
"space": "dhcp6",
"csv-format": true,
"data": "2001:db8:0::1, 2001:db8:2::1"
}
]
},
...
],</userinput>
...
}</screen>
</para>
</section>
<section id="classification-using-host-reservations">
<title>Using Static Host Reservations In Classification</title>
<para>Classes can be statically assigned to the clients using techniques described
in <xref linkend="reservation4-client-classes"/> and
<xref linkend="reservation6-client-classes"/>.
</para>
</section>
<section xml:id="classification-subnets">
<title>Configuring Subnets With Class Information</title>
<para>
In certain cases it beneficial to restrict access to certain subnets
only to clients that belong to a given class, using the "client-class"
keyword when defining the subnet.
</para>
<para>
Let's assume that the server is connected to a network segment that uses
the 192.0.2.0/24 prefix. The Administrator of that network has decided
that addresses from range 192.0.2.10 to 192.0.2.20 are going to be
managed by the DHCP4 server. Only clients belonging to client class
Client_foo are allowed to use this subnet. Such a
configuration can be achieved in the following way:
<screen>
"Dhcp4": {
"client-classes": [
{
"name": "Client_foo",
"test": "substring(option[61].hex,0,3) == 'foo'",
"option-data": [
{
"name": "domain-name-servers",
"code": 6,
"space": "dhcp4",
"csv-format": true,
"data": "192.0.2.1, 192.0.2.2"
}
]
},
...
],<userinput>
"subnet4": [
{
"subnet": "192.0.2.0/24",
"pools": [ { "pool": "192.0.2.10 - 192.0.2.20" } ],
"client-class": "Client_foo"
},
...
],</userinput>,
...
}</screen>
</para>
<para>
The following example shows restricting access to a DHCPv6 subnet. This
configuration will restrict use of the addresses 2001:db8:1::1 to
2001:db8:1::FFFF to members of the "Client_enterprise" class.
<screen>
"Dhcp6": {
"client-classes": [
{
"name": "Client_enterprise",
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
"option-data": [
{
"name": "dns-servers",
"code": 23,
"space": "dhcp6",
"csv-format": true,
"data": "2001:db8:0::1, 2001:db8:2::1"
}
]
},
...
], <userinput>
"subnet6": [
{
"subnet": "2001:db8:1::/64",
"pools": [ { "pool": "2001:db8:1::-2001:db8:1::ffff" } ],
"client-class": "Client_enterprise"
}
],</userinput>
...
}</screen>
</para>
</section>
<section id="classification-pools">
<title>Configuring Pools With Class Information</title>
<para>
Similar to subnets in certain cases access to certain address or
prefix pools must be restricted to only clients that belong to a
given class, using the "client-class" when defining the pool.
</para>
<para>
Let's assume that the server is connected to a network segment that uses
the 192.0.2.0/24 prefix. The Administrator of that network has decided
that addresses from range 192.0.2.10 to 192.0.2.20 are going to be
managed by the DHCP4 server. Only clients belonging to client class
Client_foo are allowed to use this pool. Such a
configuration can be achieved in the following way:
<screen>
"Dhcp4": {
"client-classes": [
{
"name": "Client_foo",
"test": "substring(option[61].hex,0,3) == 'foo'",
"option-data": [
{
"name": "domain-name-servers",
"code": 6,
"space": "dhcp4",
"csv-format": true,
"data": "192.0.2.1, 192.0.2.2"
}
]
},
...
],<userinput>
"subnet4": [
{
"subnet": "192.0.2.0/24",
"pools": [
{
"pool": "192.0.2.10 - 192.0.2.20",
"client-class": "Client_foo"
}
]
},
...
],</userinput>,
}</screen>
</para>
<para>
The following example shows restricting access to an address pool.
This configuration will restrict use of the addresses 2001:db8:1::1
to 2001:db8:1::FFFF to members of the "Client_enterprise" class.
<screen>
"Dhcp6": {
"client-classes": [
{
"name": "Client_enterprise_",
"test": "substring(option[1].hex,0,6) == 0x0002AABBCCDD'",
"option-data": [
{
"name": "dns-servers",
"code": 23,
"space": "dhcp6",
"csv-format": true,
"data": "2001:db8:0::1, 2001:db8:2::1"
}
]
},
...
],
"subnet6": [
{
"subnet": "2001:db8:1::/64",
<userinput>
"pools": [
{
"pool": "2001:db8:1::-2001:db8:1::ffff",
"client-class": "Client_foo"
}
]</userinput>
},
...
],
...
}</screen>
</para>
</section>
<section>
<title>Using Classes</title>
<para>
Currently classes can be used for two functions. They can supply options
to the members of the class and they can be used to choose a subnet from which an
address will be assigned to the class member.
</para>
<para>
When supplying options, options defined as part of the class definition
are considered "class globals". They will override any global options that
may be defined and in turn will be overridden by any options defined for an
individual subnet.
</para>
</section>
<section>
<title>Classes and Hooks</title>
<para>
You may use a hook to classify your packets. This may be useful if the
expression would either be complex or time consuming and be easier or
better to write as code. Once the hook has added the proper class name
to the packet the rest of the classification system will work as normal
in choosing a subnet and selecting options. For a description of
hooks see <xref linkend="hooks-libraries"/>, for a description on
configuring classes see <xref linkend="classification-configuring"/>
and <xref linkend="classification-subnets"/>.
</para>
</section>
<section>
<title>Debugging Expressions</title>
<para>
While you are constructing your classification expressions you may
find it useful to enable logging see <xref linkend="logging"/> for
a more complete description of the logging facility.
</para>
<para>
To enable the debug statements in the classification system you will
need to set the severity to "DEBUG" and the debug level to at least 55.
The specific loggers are "kea-dhcp4.eval" and "kea-dhcp6.eval".
</para>
<para>
In order to understand the logging statements one must understand a
bit about how expressions are evaluated, for a more complete description
refer to the design document at <uri xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://kea.isc.org/wiki/KeaDesigns">http://kea.isc.org/wiki/KeaDesigns</uri>.
In brief there are two structures used during the evaluation of an expression:
a list of tokens which represent the expressions and a value stack which
represents the values being manipulated.
</para>
<para>
The list of tokens is created when the configuration file is processed with
most expressions and values being converted to a token. The list is organized
in reverse Polish notation. During execution, the list will be traversed
in order. As each token is executed it will be able to pop values
from the top of the stack and eventually push its result on the top of the
stack. Imagine the following expression:
<screen>
"test": "substring(option[61].hex,0,3) == 'foo'",
</screen>
This will result in the following tokens:
<screen>
option, number (0), number (3), substring, text ('foo'), equals
</screen>
In this example the first three tokens will simply push values onto the
stack. The substring token will then remove those three values and
compute a result that it places on the stack. The text option also
places a value on the stack and finally the equals token removes the
two tokens on the stack and places its result on the stack.
</para>
<para>
When debug logging is enabled, each time a token is evaluated it will
emit a log message indicating the values of any objects that were popped
off of the value stack and any objects that were pushed onto the value
stack.
</para>
<para>
The values will be displayed as either text if the command is known
to use text values or hexadecimal if the command either uses binary values or
can manipulate either text or binary values. For expressions that
pop multiple values off the stack, the values will be displayed in
the order they were popped. For most expressions this won't matter
but for the concat expression the values are displayed in reverse
order from how they are written in the expression.
</para>
<para>
Let us assume that the following test has been entered into the configuration.
This example skips most of the configuration to concentrate on the test.
<screen>
"test": "substring(option[61].hex,0,3) == 'foo'",
</screen>
The logging might then resemble this:
<screen>
2016-05-19 13:35:04.163 DEBUG [kea.eval/44478] EVAL_DEBUG_OPTION Pushing option 61 with value 0x666F6F626172
2016-05-19 13:35:04.164 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '0'
2016-05-19 13:35:04.165 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string '3'
2016-05-19 13:35:04.166 DEBUG [kea.eval/44478] EVAL_DEBUG_SUBSTRING Popping length 3, start 0, string 0x666F6F626172 pushing result 0x666F6F
2016-05-19 13:35:04.167 DEBUG [kea.eval/44478] EVAL_DEBUG_STRING Pushing text string 'foo'
2016-05-19 13:35:04.168 DEBUG [kea.eval/44478] EVAL_DEBUG_EQUAL Popping 0x666F6F and 0x666F6F pushing result 'true'
</screen>
</para>
<note><para>
The debug logging may be quite verbose if you have a number of expressions
to evaluate. It is intended as an aid in helping you create and debug
your expressions. You should plan to disable debug logging when you have your
expressions working correctly. You also may wish to include only one set of
expressions at a time in the configuration file while debugging them in order
to limit the log statements. For example when adding a new set of expressions
you might find it more convenient to create a configuration file that only
includes the new expressions until you have them working correctly and then
add the new set to the main configuration file.
</para></note>
</section>
</chapter>
|