4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 #include <sys/types.h>
36 #include <sys/isa_defs.h>
37 #include <sys/socket.h>
38 #include <net/if_arp.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <sys/sysmacros.h>
42 #include <libinetutil.h>
44 #include <netinet/dhcp6.h>
46 #include "dhcp_symbol.h"
47 #include "dhcp_inittab.h"
49 static void inittab_msg(const char *, ...);
50 static uchar_t
category_to_code(const char *);
51 static boolean_t
encode_number(uint8_t, uint8_t, boolean_t
, uint8_t,
52 const char *, uint8_t *, int *);
53 static boolean_t
decode_number(uint8_t, uint8_t, boolean_t
, uint8_t,
54 const uint8_t *, char *, int *);
55 static dhcp_symbol_t
*inittab_lookup(uchar_t
, char, const char *, int32_t,
57 static dsym_category_t
itabcode_to_dsymcode(uchar_t
);
58 static boolean_t
parse_entry(char *, char **);
61 * forward declaration of our internal inittab_table[]. too bulky to put
62 * up front -- check the end of this file for its definition.
64 * Note: we have only an IPv4 version here. The inittab_verify() function is
65 * used by the DHCP server and manager. We'll need a new function if the
66 * server is extended to DHCPv6.
68 static dhcp_symbol_t inittab_table
[];
71 * the number of fields in the inittab and names for the fields. note that
72 * this order is meaningful to parse_entry(); other functions should just
73 * use them as indexes into the array returned from parse_entry().
76 enum { ITAB_NAME
, ITAB_CODE
, ITAB_TYPE
, ITAB_GRAN
, ITAB_MAX
, ITAB_CONS
,
80 * the category_map_entry_t is used to map the inittab category codes to
81 * the dsym codes. the reason the codes are different is that the inittab
82 * needs to have the codes be ORable such that queries can retrieve more
83 * than one category at a time. this map is also used to map the inittab
84 * string representation of a category to its numerical code.
86 typedef struct category_map_entry
{
87 dsym_category_t cme_dsymcode
;
90 } category_map_entry_t
;
92 static category_map_entry_t category_map
[] = {
93 { DSYM_STANDARD
, "STANDARD", ITAB_CAT_STANDARD
},
94 { DSYM_FIELD
, "FIELD", ITAB_CAT_FIELD
},
95 { DSYM_INTERNAL
, "INTERNAL", ITAB_CAT_INTERNAL
},
96 { DSYM_VENDOR
, "VENDOR", ITAB_CAT_VENDOR
},
97 { DSYM_SITE
, "SITE", ITAB_CAT_SITE
}
101 * inittab_load(): returns all inittab entries with the specified criteria
103 * input: uchar_t: the categories the consumer is interested in
104 * char: the consumer type of the caller
105 * size_t *: set to the number of entries returned
106 * output: dhcp_symbol_t *: an array of dynamically allocated entries
107 * on success, NULL upon failure
111 inittab_load(uchar_t categories
, char consumer
, size_t *n_entries
)
113 return (inittab_lookup(categories
, consumer
, NULL
, -1, n_entries
));
117 * inittab_getbyname(): returns an inittab entry with the specified criteria
119 * input: int: the categories the consumer is interested in
120 * char: the consumer type of the caller
121 * char *: the name of the inittab entry the consumer wants
122 * output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
123 * on success, NULL upon failure
127 inittab_getbyname(uchar_t categories
, char consumer
, const char *name
)
129 return (inittab_lookup(categories
, consumer
, name
, -1, NULL
));
133 * inittab_getbycode(): returns an inittab entry with the specified criteria
135 * input: uchar_t: the categories the consumer is interested in
136 * char: the consumer type of the caller
137 * uint16_t: the code of the inittab entry the consumer wants
138 * output: dhcp_symbol_t *: a dynamically allocated dhcp_symbol structure
139 * on success, NULL upon failure
143 inittab_getbycode(uchar_t categories
, char consumer
, uint16_t code
)
145 return (inittab_lookup(categories
, consumer
, NULL
, code
, NULL
));
149 * inittab_lookup(): returns inittab entries with the specified criteria
151 * input: uchar_t: the categories the consumer is interested in
152 * char: the consumer type of the caller
153 * const char *: the name of the entry the caller is interested
154 * in, or NULL if the caller doesn't care
155 * int32_t: the code the caller is interested in, or -1 if the
156 * caller doesn't care
157 * size_t *: set to the number of entries returned
158 * output: dhcp_symbol_t *: dynamically allocated dhcp_symbol structures
159 * on success, NULL upon failure
162 static dhcp_symbol_t
*
163 inittab_lookup(uchar_t categories
, char consumer
, const char *name
,
164 int32_t code
, size_t *n_entriesp
)
167 dhcp_symbol_t
*new_entries
, *entries
= NULL
;
169 char buffer
[ITAB_MAX_LINE_LEN
];
170 char *fields
[ITAB_FIELDS
];
171 unsigned long line
= 0;
172 size_t i
, n_entries
= 0;
173 const char *inittab_path
;
174 uchar_t category_code
;
177 if (categories
& ITAB_CAT_V6
) {
178 inittab_path
= getenv("DHCP_INITTAB6_PATH");
179 if (inittab_path
== NULL
)
180 inittab_path
= ITAB_INITTAB6_PATH
;
182 inittab_path
= getenv("DHCP_INITTAB_PATH");
183 if (inittab_path
== NULL
)
184 inittab_path
= ITAB_INITTAB_PATH
;
187 inittab_fp
= fopen(inittab_path
, "r");
188 if (inittab_fp
== NULL
) {
189 inittab_msg("inittab_lookup: fopen: %s: %s",
190 inittab_path
, strerror(errno
));
194 (void) bufsplit(",\n", 0, NULL
);
195 while (fgets(buffer
, sizeof (buffer
), inittab_fp
) != NULL
) {
200 * make sure the string didn't overflow our buffer
202 if (strchr(buffer
, '\n') == NULL
) {
203 inittab_msg("inittab_lookup: line %li: too long, "
209 * skip `pure comment' lines
211 for (i
= 0; buffer
[i
] != '\0'; i
++)
212 if (isspace(buffer
[i
]) == 0)
215 if (buffer
[i
] == ITAB_COMMENT_CHAR
|| buffer
[i
] == '\0')
219 * parse the entry out into fields.
221 if (parse_entry(buffer
, fields
) == B_FALSE
) {
222 inittab_msg("inittab_lookup: line %li: syntax error, "
228 * validate the values in the entries; skip if invalid.
230 if (atoi(fields
[ITAB_GRAN
]) > ITAB_GRAN_MAX
) {
231 inittab_msg("inittab_lookup: line %li: granularity `%s'"
232 " out of range, skipping", line
, fields
[ITAB_GRAN
]);
236 if (atoi(fields
[ITAB_MAX
]) > ITAB_MAX_MAX
) {
237 inittab_msg("inittab_lookup: line %li: maximum `%s' "
238 "out of range, skipping", line
, fields
[ITAB_MAX
]);
242 if (dsym_get_type_id(fields
[ITAB_TYPE
], &type
, B_FALSE
) !=
244 inittab_msg("inittab_lookup: line %li: type `%s' "
245 "is invalid, skipping", line
, fields
[ITAB_TYPE
]);
250 * find out whether this entry of interest to our consumer,
251 * and if so, throw it onto the set of entries we'll return.
252 * check categories last since it's the most expensive check.
254 if (strchr(fields
[ITAB_CONS
], consumer
) == NULL
)
257 if (code
!= -1 && atoi(fields
[ITAB_CODE
]) != code
)
260 if (name
!= NULL
&& strcasecmp(fields
[ITAB_NAME
], name
) != 0)
263 category_code
= category_to_code(fields
[ITAB_CAT
]);
264 if ((category_code
& categories
) == 0)
268 * looks like a match. allocate an entry and fill it in
270 new_entries
= reallocarray(entries
, n_entries
+ 1,
271 sizeof (dhcp_symbol_t
));
274 * if we run out of memory, might as well return what we can
276 if (new_entries
== NULL
) {
277 inittab_msg("inittab_lookup: ran out of memory "
278 "allocating dhcp_symbol_t's");
282 entry
.ds_max
= atoi(fields
[ITAB_MAX
]);
283 entry
.ds_code
= atoi(fields
[ITAB_CODE
]);
284 entry
.ds_type
= type
;
285 entry
.ds_gran
= atoi(fields
[ITAB_GRAN
]);
286 entry
.ds_category
= itabcode_to_dsymcode(category_code
);
287 entry
.ds_classes
.dc_cnt
= 0;
288 entry
.ds_classes
.dc_names
= NULL
;
289 (void) strlcpy(entry
.ds_name
, fields
[ITAB_NAME
],
290 sizeof (entry
.ds_name
));
291 entry
.ds_dhcpv6
= (categories
& ITAB_CAT_V6
) ? 1 : 0;
293 entries
= new_entries
;
294 entries
[n_entries
++] = entry
;
297 if (ferror(inittab_fp
) != 0) {
298 inittab_msg("inittab_lookup: error on inittab stream");
299 clearerr(inittab_fp
);
302 (void) fclose(inittab_fp
);
304 if (n_entriesp
!= NULL
)
305 *n_entriesp
= n_entries
;
311 * parse_entry(): parses an entry out into its constituent fields
313 * input: char *: the entry
314 * char **: an array of ITAB_FIELDS length which contains
315 * pointers into the entry on upon return
316 * output: boolean_t: B_TRUE on success, B_FALSE on failure
320 parse_entry(char *entry
, char **fields
)
322 char *category
, *spacep
;
326 * due to a mistake made long ago, the first and second fields of
327 * each entry are not separated by a comma, but rather by
328 * whitespace -- have bufsplit() treat the two fields as one, then
329 * pull them apart afterwards.
331 n_fields
= bufsplit(entry
, ITAB_FIELDS
- 1, fields
);
332 if (n_fields
!= (ITAB_FIELDS
- 1))
336 * pull the first and second fields apart. this is complicated
337 * since the first field can contain embedded whitespace (so we
338 * must separate the two fields by the last span of whitespace).
340 * first, find the initial span of whitespace. if there isn't one,
341 * then the entry is malformed.
343 category
= strpbrk(fields
[ITAB_NAME
], " \t");
344 if (category
== NULL
)
348 * find the last span of whitespace.
351 while (isspace(*category
))
354 spacep
= strpbrk(category
, " \t");
357 } while (spacep
!= NULL
);
360 * NUL-terminate the first byte of the last span of whitespace, so
361 * that the first field doesn't have any residual trailing
364 spacep
= category
- 1;
365 while (isspace(*spacep
))
368 if (spacep
<= fields
[0])
374 * remove any whitespace from the fields.
376 for (i
= 0; i
< n_fields
; i
++) {
377 while (isspace(*fields
[i
]))
380 fields
[ITAB_CAT
] = category
;
386 * inittab_verify(): verifies that a given inittab entry matches an internal
389 * input: dhcp_symbol_t *: the inittab entry to verify
390 * dhcp_symbol_t *: if non-NULL, a place to store the internal
391 * inittab entry upon return
392 * output: int: ITAB_FAILURE, ITAB_SUCCESS, or ITAB_UNKNOWN
398 inittab_verify(const dhcp_symbol_t
*inittab_ent
, dhcp_symbol_t
*internal_ent
)
402 for (i
= 0; inittab_table
[i
].ds_name
[0] != '\0'; i
++) {
404 if (inittab_ent
->ds_category
!= inittab_table
[i
].ds_category
)
407 if (inittab_ent
->ds_code
== inittab_table
[i
].ds_code
) {
408 if (internal_ent
!= NULL
)
409 *internal_ent
= inittab_table
[i
];
411 if (inittab_table
[i
].ds_type
!= inittab_ent
->ds_type
||
412 inittab_table
[i
].ds_gran
!= inittab_ent
->ds_gran
||
413 inittab_table
[i
].ds_max
!= inittab_ent
->ds_max
)
414 return (ITAB_FAILURE
);
416 return (ITAB_SUCCESS
);
420 return (ITAB_UNKNOWN
);
424 * get_hw_type(): interpret ",hwtype" in the input string, as part of a DUID.
425 * The hwtype string is optional, and must be 0-65535 if
428 * input: char **: pointer to string pointer
429 * int *: error return value
430 * output: int: hardware type, or -1 for empty, or -2 for error.
434 get_hw_type(char **strp
, int *ierrnop
)
440 *ierrnop
= ITAB_BAD_NUMBER
;
443 if (*str
== ',' || *str
== '\0') {
447 hwtype
= strtoul(str
, strp
, 0);
448 if (errno
!= 0 || *strp
== str
|| hwtype
> 65535) {
449 *ierrnop
= ITAB_BAD_NUMBER
;
452 return ((int)hwtype
);
457 * get_mac_addr(): interpret ",macaddr" in the input string, as part of a DUID.
458 * The 'macaddr' may be a hex string (in any standard format),
459 * or the name of a physical interface. If an interface name
460 * is given, then the interface type is extracted as well.
462 * input: const char *: input string
463 * int *: error return value
464 * uint16_t *: hardware type output (network byte order)
465 * int: hardware type input; -1 for empty
466 * uchar_t *: output buffer for MAC address
467 * output: int: length of MAC address, or -1 for error
471 get_mac_addr(const char *str
, int *ierrnop
, uint16_t *hwret
, int hwtype
,
483 if (dlpi_open(str
, &dh
, 0) != DLPI_SUCCESS
) {
487 * Allow MAC addresses with separators matching regexp
490 while ((chr
= *str
++) != '\0') {
492 val
= (val
<< 4) + chr
- '0';
493 } else if (isxdigit(chr
)) {
494 val
= (val
<< 4) + chr
-
495 (isupper(chr
) ? 'A' : 'a') + 10;
496 } else if (isspace(chr
) && dig
== 0) {
498 } else if (chr
== ':' || chr
== '-' ||
511 if (dlpi_bind(dh
, DLPI_ANY_SAP
, NULL
) !=
512 DLPI_SUCCESS
|| dlpi_info(dh
, &dlinfo
, 0) !=
517 maclen
= dlinfo
.di_physaddrlen
;
518 (void) memcpy(outbuf
, dlinfo
.di_physaddr
, maclen
);
521 hwtype
= dlpi_arptype(dlinfo
.di_mactype
);
526 *hwret
= htons(hwtype
);
530 *ierrnop
= ITAB_BAD_NUMBER
;
535 * inittab_encode_e(): converts a string representation of a given datatype into
536 * binary; used for encoding ascii values into a form that
537 * can be put in DHCP packets to be sent on the wire.
539 * input: const dhcp_symbol_t *: the entry describing the value option
540 * const char *: the value to convert
541 * uint16_t *: set to the length of the binary data returned
542 * boolean_t: if false, return a full DHCP option
543 * int *: error return value
544 * output: uchar_t *: a dynamically allocated byte array with converted data
548 inittab_encode_e(const dhcp_symbol_t
*ie
, const char *value
, uint16_t *lengthp
,
549 boolean_t just_payload
, int *ierrnop
)
553 uchar_t n_entries
= 0;
556 uchar_t
*result
= NULL
;
559 uint8_t type_size
= inittab_type_to_size(ie
);
561 uint_t vallen
, reslen
;
562 dhcpv6_option_t
*d6o
;
567 if (type_size
== 0) {
568 *ierrnop
= ITAB_SYNTAX_ERROR
;
572 switch (ie
->ds_type
) {
574 n_entries
= strlen(value
); /* no NUL */
578 vallen
= strlen(value
);
579 n_entries
= vallen
/ 2;
580 n_entries
+= vallen
% 2;
585 * Maximum (worst-case) encoded length is one byte more than
586 * the number of characters on input.
588 n_entries
= strlen(value
) + 1;
592 /* Worst case is ":::::" */
593 n_entries
= strlen(value
);
594 if (n_entries
< DLPI_PHYSADDR_MAX
)
595 n_entries
= DLPI_PHYSADDR_MAX
;
596 n_entries
+= sizeof (duid_llt_t
);
601 * figure out the number of entries by counting the spaces
602 * in the value string
604 for (valuep
= value
; valuep
++ != NULL
; n_entries
++)
605 valuep
= strchr(valuep
, ' ');
610 * if we're gonna return a complete option, then include the
611 * option length and code in the size of the packet we allocate
614 hlen
= ie
->ds_dhcpv6
? sizeof (*d6o
) : 2;
616 length
= n_entries
* type_size
;
617 if (hlen
+ length
> 0)
618 result
= malloc(hlen
+ length
);
620 if ((optstart
= result
) != NULL
&& !just_payload
)
623 switch (ie
->ds_type
) {
627 if (optstart
== NULL
) {
628 *ierrnop
= ITAB_NOMEM
;
632 (void) memcpy(optstart
, value
, length
);
636 if (optstart
== NULL
) {
637 *ierrnop
= ITAB_NOMEM
;
642 * Note that this encoder always presents the trailing 0-octet
643 * when dealing with a list. This means that you can't have
644 * non-fully-qualified members anywhere but at the end of a
645 * list (or as the only member of the list).
648 while (*valuep
!= '\0') {
654 * Skip over whitespace that delimits list members.
656 if (isascii(*valuep
) && isspace(*valuep
)) {
663 while ((inchr
= *valuep
) != '\0') {
666 * Just copy non-ASCII text directly to the
667 * output string. This simplifies the use of
668 * other ctype macros below, as, unlike the
669 * special isascii function, they don't handle
672 if (!isascii(inchr
)) {
679 * Handle any of \D, \DD, or \DDD for
682 if (isdigit(inchr
)) {
683 val
= val
* 10 + inchr
- '0';
690 } else if (dig
> 0) {
692 * User terminated \D or \DD
693 * with non-digit. An error,
694 * but we can assume they mean
695 * to treat as \00D or \0DD.
700 /* Fall through and copy character */
702 } else if (inchr
== '\\') {
705 } else if (inchr
== '.') {
707 * End of component. Write the length
708 * prefix. If the component is zero
709 * length (i.e., ".."), the just omit
712 *flen
= (optstart
- flen
) - 1;
716 } else if (isspace(inchr
)) {
718 * Unescaped space; end of domain name
726 * Handle trailing escape sequence. If string ends
727 * with \, then assume user wants \ at end of encoded
728 * string. If it ends with \D or \DD, assume \00D or
732 *optstart
++ = dig
> 0 ? val
: '\\';
733 *flen
= (optstart
- flen
) - 1;
735 * If user specified FQDN with trailing '.', then above
736 * will result in zero for the last component length.
737 * We're done, and optstart already points to the start
738 * of the next in list. Otherwise, we need to write a
739 * single zero byte to end the entry, if there are more
740 * entries that will be decoded.
742 while (isascii(*valuep
) && isspace(*valuep
))
744 if (*flen
> 0 && *valuep
!= '\0')
747 length
= (optstart
- result
) - hlen
;
751 if (optstart
== NULL
) {
752 *ierrnop
= ITAB_NOMEM
;
757 type
= strtoul(value
, &currp
, 0);
758 if (errno
!= 0 || value
== currp
|| type
> 65535 ||
759 (*currp
!= ',' && *currp
!= '\0')) {
761 *ierrnop
= ITAB_BAD_NUMBER
;
765 case DHCPV6_DUID_LLT
: {
771 if ((hwtype
= get_hw_type(&currp
, ierrnop
)) == -2) {
775 if (*currp
++ != ',') {
777 *ierrnop
= ITAB_BAD_NUMBER
;
780 if (*currp
== ',' || *currp
== '\0') {
781 tstamp
= time(NULL
) - DUID_TIME_BASE
;
783 tstamp
= strtoul(currp
, &cp2
, 0);
784 if (errno
!= 0 || currp
== cp2
) {
786 *ierrnop
= ITAB_BAD_NUMBER
;
791 maclen
= get_mac_addr(currp
, ierrnop
,
792 &dllt
.dllt_hwtype
, hwtype
,
793 optstart
+ sizeof (dllt
));
798 dllt
.dllt_dutype
= htons(type
);
799 dllt
.dllt_time
= htonl(tstamp
);
800 (void) memcpy(optstart
, &dllt
, sizeof (dllt
));
801 length
= maclen
+ sizeof (dllt
);
804 case DHCPV6_DUID_EN
: {
808 if (*currp
++ != ',') {
810 *ierrnop
= ITAB_BAD_NUMBER
;
813 enterp
= strtoul(currp
, &cp2
, 0);
814 DHCPV6_SET_ENTNUM(&den
, enterp
);
815 if (errno
!= 0 || currp
== cp2
||
816 enterp
!= DHCPV6_GET_ENTNUM(&den
) ||
817 (*cp2
!= ',' && *cp2
!= '\0')) {
819 *ierrnop
= ITAB_BAD_NUMBER
;
824 vallen
= strlen(cp2
);
825 reslen
= (vallen
+ 1) / 2;
826 if (hexascii_to_octet(cp2
, vallen
,
827 optstart
+ sizeof (den
), &reslen
) != 0) {
829 *ierrnop
= ITAB_BAD_NUMBER
;
832 den
.den_dutype
= htons(type
);
833 (void) memcpy(optstart
, &den
, sizeof (den
));
834 length
= reslen
+ sizeof (den
);
837 case DHCPV6_DUID_LL
: {
842 if ((hwtype
= get_hw_type(&currp
, ierrnop
)) == -2) {
846 maclen
= get_mac_addr(currp
, ierrnop
, &dll
.dll_hwtype
,
847 hwtype
, optstart
+ sizeof (dll
));
852 dll
.dll_dutype
= htons(type
);
853 (void) memcpy(optstart
, &dll
, sizeof (dll
));
854 length
= maclen
+ sizeof (dll
);
860 vallen
= strlen(currp
);
861 reslen
= (vallen
+ 1) / 2;
862 if (hexascii_to_octet(currp
, vallen
, optstart
+ 2,
865 *ierrnop
= ITAB_BAD_NUMBER
;
868 optstart
[0] = type
>> 8;
877 if (optstart
== NULL
) {
878 *ierrnop
= ITAB_BAD_OCTET
;
883 /* Call libinetutil function to decode */
884 if (hexascii_to_octet(value
, vallen
, optstart
, &reslen
) != 0) {
886 *ierrnop
= ITAB_BAD_OCTET
;
894 if (optstart
== NULL
) {
895 *ierrnop
= ITAB_BAD_IPADDR
;
898 if (n_entries
% ie
->ds_gran
!= 0) {
899 *ierrnop
= ITAB_BAD_GRAN
;
900 inittab_msg("inittab_encode: number of entries "
901 "not compatible with option granularity");
906 for (valuep
= value
, i
= 0; i
< n_entries
; i
++, valuep
++) {
908 currp
= strchr(valuep
, ' ');
911 if (inet_pton(ie
->ds_type
== DSYM_IP
? AF_INET
:
912 AF_INET6
, valuep
, optstart
) != 1) {
913 *ierrnop
= ITAB_BAD_IPADDR
;
914 inittab_msg("inittab_encode: bogus ip address");
920 if (valuep
== NULL
) {
921 if (i
< (n_entries
- 1)) {
922 *ierrnop
= ITAB_NOT_ENOUGH_IP
;
923 inittab_msg("inittab_encode: too few "
930 optstart
+= type_size
;
934 case DSYM_NUMBER
: /* FALLTHRU */
935 case DSYM_UNUMBER8
: /* FALLTHRU */
936 case DSYM_SNUMBER8
: /* FALLTHRU */
937 case DSYM_UNUMBER16
: /* FALLTHRU */
938 case DSYM_SNUMBER16
: /* FALLTHRU */
939 case DSYM_UNUMBER24
: /* FALLTHRU */
940 case DSYM_UNUMBER32
: /* FALLTHRU */
941 case DSYM_SNUMBER32
: /* FALLTHRU */
942 case DSYM_UNUMBER64
: /* FALLTHRU */
945 if (optstart
== NULL
) {
946 *ierrnop
= ITAB_BAD_NUMBER
;
950 is_signed
= (ie
->ds_type
== DSYM_SNUMBER64
||
951 ie
->ds_type
== DSYM_SNUMBER32
||
952 ie
->ds_type
== DSYM_SNUMBER16
||
953 ie
->ds_type
== DSYM_SNUMBER8
);
955 if (encode_number(n_entries
, type_size
, is_signed
, 0, value
,
956 optstart
, ierrnop
) == B_FALSE
) {
963 if (ie
->ds_type
== DSYM_BOOL
)
964 *ierrnop
= ITAB_BAD_BOOLEAN
;
966 *ierrnop
= ITAB_SYNTAX_ERROR
;
968 inittab_msg("inittab_encode: unsupported type `%d'",
976 * if just_payload is false, then we need to add the option
977 * code and length fields in.
981 /* LINTED: alignment */
982 d6o
= (dhcpv6_option_t
*)result
;
983 d6o
->d6o_code
= htons(ie
->ds_code
);
984 d6o
->d6o_len
= htons(length
);
986 result
[0] = ie
->ds_code
;
992 *lengthp
= length
+ hlen
;
998 * inittab_decode_e(): converts a binary representation of a given datatype into
999 * a string; used for decoding DHCP options in a packet off
1000 * the wire into ascii
1002 * input: dhcp_symbol_t *: the entry describing the payload option
1003 * uchar_t *: the payload to convert
1004 * uint16_t: the payload length (only used if just_payload is true)
1005 * boolean_t: if false, payload is assumed to be a DHCP option
1006 * int *: set to extended error code if error occurs.
1007 * output: char *: a dynamically allocated string containing the converted data
1011 inittab_decode_e(const dhcp_symbol_t
*ie
, const uchar_t
*payload
,
1012 uint16_t length
, boolean_t just_payload
, int *ierrnop
)
1014 char *resultp
, *result
= NULL
;
1016 struct in_addr in_addr
;
1017 in6_addr_t in6_addr
;
1018 uint8_t type_size
= inittab_type_to_size(ie
);
1019 boolean_t is_signed
;
1023 if (type_size
== 0) {
1024 *ierrnop
= ITAB_SYNTAX_ERROR
;
1028 if (!just_payload
) {
1029 if (ie
->ds_dhcpv6
) {
1030 dhcpv6_option_t d6o
;
1032 (void) memcpy(&d6o
, payload
, sizeof (d6o
));
1033 length
= ntohs(d6o
.d6o_len
);
1034 payload
+= sizeof (d6o
);
1036 length
= payload
[1];
1042 * figure out the number of elements to convert. note that
1043 * for ds_type NUMBER, the granularity is really 1 since the
1044 * value of ds_gran is the number of bytes in the number.
1046 if (ie
->ds_type
== DSYM_NUMBER
)
1047 n_entries
= MIN(ie
->ds_max
, length
/ type_size
);
1049 n_entries
= MIN(ie
->ds_max
* ie
->ds_gran
, length
/ type_size
);
1052 n_entries
= length
/ type_size
;
1054 if ((length
% type_size
) != 0) {
1055 inittab_msg("inittab_decode: length of string not compatible "
1056 "with option type `%i'", ie
->ds_type
);
1057 *ierrnop
= ITAB_BAD_STRING
;
1061 switch (ie
->ds_type
) {
1065 result
= malloc(n_entries
+ 1);
1066 if (result
== NULL
) {
1067 *ierrnop
= ITAB_NOMEM
;
1071 (void) memcpy(result
, payload
, n_entries
);
1072 result
[n_entries
] = '\0';
1078 * A valid, decoded RFC 1035 domain string or sequence of
1079 * strings is always the same size as the encoded form, but we
1080 * allow for RFC 1035 \DDD and \\ and \. escaping.
1082 * Decoding stops at the end of the input or the first coding
1083 * violation. Coding violations result in discarding the
1084 * offending list entry entirely. Note that we ignore the 255
1085 * character overall limit on domain names.
1087 if ((result
= malloc(4 * length
+ 1)) == NULL
) {
1088 *ierrnop
= ITAB_NOMEM
;
1092 while (length
> 0) {
1097 while (length
> 0) {
1100 /* Upper two bits of length must be zero */
1101 if ((slen
& 0xc0) != 0 || slen
> length
) {
1106 if (resultp
!= dstart
)
1112 if (!isascii(*payload
) ||
1113 !isgraph(*payload
)) {
1114 (void) snprintf(resultp
, 5,
1116 *(unsigned char *)payload
);
1120 if (*payload
== '.' ||
1123 *resultp
++ = *payload
++;
1128 if (resultp
!= dstart
&& length
> 0)
1137 * First, determine the type of DUID. We need at least two
1138 * octets worth of data to grab the type code. Once we have
1139 * that, the number of octets required for representation
1140 * depends on the type.
1144 *ierrnop
= ITAB_BAD_GRAN
;
1147 type
= (payload
[0] << 8) + payload
[1];
1149 case DHCPV6_DUID_LLT
: {
1152 if (length
< sizeof (dllt
)) {
1153 *ierrnop
= ITAB_BAD_GRAN
;
1156 (void) memcpy(&dllt
, payload
, sizeof (dllt
));
1157 payload
+= sizeof (dllt
);
1158 length
-= sizeof (dllt
);
1159 n_entries
= sizeof ("1,65535,4294967295,") +
1161 if ((result
= malloc(n_entries
)) == NULL
) {
1162 *ierrnop
= ITAB_NOMEM
;
1165 (void) snprintf(result
, n_entries
, "%d,%u,%u,", type
,
1166 ntohs(dllt
.dllt_hwtype
), ntohl(dllt
.dllt_time
));
1169 case DHCPV6_DUID_EN
: {
1172 if (length
< sizeof (den
)) {
1173 *ierrnop
= ITAB_BAD_GRAN
;
1176 (void) memcpy(&den
, payload
, sizeof (den
));
1177 payload
+= sizeof (den
);
1178 length
-= sizeof (den
);
1179 n_entries
= sizeof ("2,4294967295,") + length
* 2;
1180 if ((result
= malloc(n_entries
)) == NULL
) {
1181 *ierrnop
= ITAB_NOMEM
;
1184 (void) snprintf(result
, n_entries
, "%d,%u,", type
,
1185 DHCPV6_GET_ENTNUM(&den
));
1188 case DHCPV6_DUID_LL
: {
1191 if (length
< sizeof (dll
)) {
1192 *ierrnop
= ITAB_BAD_GRAN
;
1195 (void) memcpy(&dll
, payload
, sizeof (dll
));
1196 payload
+= sizeof (dll
);
1197 length
-= sizeof (dll
);
1198 n_entries
= sizeof ("3,65535,") + length
* 3;
1199 if ((result
= malloc(n_entries
)) == NULL
) {
1200 *ierrnop
= ITAB_NOMEM
;
1203 (void) snprintf(result
, n_entries
, "%d,%u,", type
,
1204 ntohs(dll
.dll_hwtype
));
1208 n_entries
= sizeof ("0,") + length
* 2;
1209 if ((result
= malloc(n_entries
)) == NULL
) {
1210 *ierrnop
= ITAB_NOMEM
;
1213 (void) snprintf(result
, n_entries
, "%d,", type
);
1216 resultp
= result
+ strlen(result
);
1217 n_entries
-= strlen(result
);
1218 if (type
== DHCPV6_DUID_LLT
|| type
== DHCPV6_DUID_LL
) {
1220 resultp
+= snprintf(resultp
, 3, "%02X",
1224 while (length
-- > 0) {
1225 resultp
+= snprintf(resultp
, 4, ":%02X",
1229 while (length
-- > 0) {
1230 resultp
+= snprintf(resultp
, 3, "%02X",
1238 result
= malloc(n_entries
* (sizeof ("0xNN") + 1));
1239 if (result
== NULL
) {
1240 *ierrnop
= ITAB_NOMEM
;
1246 if (n_entries
> 0) {
1247 resultp
+= sprintf(resultp
, "0x%02X", *payload
++);
1250 while (n_entries
-- > 0)
1251 resultp
+= sprintf(resultp
, " 0x%02X", *payload
++);
1257 if ((length
/ type_size
) % ie
->ds_gran
!= 0) {
1258 *ierrnop
= ITAB_BAD_GRAN
;
1259 inittab_msg("inittab_decode: number of entries "
1260 "not compatible with option granularity");
1264 result
= malloc(n_entries
* (ie
->ds_type
== DSYM_IP
?
1265 INET_ADDRSTRLEN
: INET6_ADDRSTRLEN
));
1266 if (result
== NULL
) {
1267 *ierrnop
= ITAB_NOMEM
;
1271 for (resultp
= result
; n_entries
!= 0; n_entries
--) {
1272 if (ie
->ds_type
== DSYM_IP
) {
1273 (void) memcpy(&in_addr
.s_addr
, payload
,
1275 (void) strcpy(resultp
, inet_ntoa(in_addr
));
1277 (void) memcpy(&in6_addr
, payload
,
1279 (void) inet_ntop(AF_INET6
, &in6_addr
, resultp
,
1282 resultp
+= strlen(resultp
);
1285 payload
+= type_size
;
1290 case DSYM_NUMBER
: /* FALLTHRU */
1291 case DSYM_UNUMBER8
: /* FALLTHRU */
1292 case DSYM_SNUMBER8
: /* FALLTHRU */
1293 case DSYM_UNUMBER16
: /* FALLTHRU */
1294 case DSYM_SNUMBER16
: /* FALLTHRU */
1295 case DSYM_UNUMBER32
: /* FALLTHRU */
1296 case DSYM_SNUMBER32
: /* FALLTHRU */
1297 case DSYM_UNUMBER64
: /* FALLTHRU */
1298 case DSYM_SNUMBER64
:
1300 is_signed
= (ie
->ds_type
== DSYM_SNUMBER64
||
1301 ie
->ds_type
== DSYM_SNUMBER32
||
1302 ie
->ds_type
== DSYM_SNUMBER16
||
1303 ie
->ds_type
== DSYM_SNUMBER8
);
1305 result
= malloc(n_entries
* ITAB_MAX_NUMBER_LEN
);
1306 if (result
== NULL
) {
1307 *ierrnop
= ITAB_NOMEM
;
1311 if (decode_number(n_entries
, type_size
, is_signed
, ie
->ds_gran
,
1312 payload
, result
, ierrnop
) == B_FALSE
) {
1319 inittab_msg("inittab_decode: unsupported type `%d'",
1328 * inittab_encode(): converts a string representation of a given datatype into
1329 * binary; used for encoding ascii values into a form that
1330 * can be put in DHCP packets to be sent on the wire.
1332 * input: dhcp_symbol_t *: the entry describing the value option
1333 * const char *: the value to convert
1334 * uint16_t *: set to the length of the binary data returned
1335 * boolean_t: if false, return a full DHCP option
1336 * output: uchar_t *: a dynamically allocated byte array with converted data
1340 inittab_encode(const dhcp_symbol_t
*ie
, const char *value
, uint16_t *lengthp
,
1341 boolean_t just_payload
)
1345 return (inittab_encode_e(ie
, value
, lengthp
, just_payload
, &ierrno
));
1349 * inittab_decode(): converts a binary representation of a given datatype into
1350 * a string; used for decoding DHCP options in a packet off
1351 * the wire into ascii
1353 * input: dhcp_symbol_t *: the entry describing the payload option
1354 * uchar_t *: the payload to convert
1355 * uint16_t: the payload length (only used if just_payload is true)
1356 * boolean_t: if false, payload is assumed to be a DHCP option
1357 * output: char *: a dynamically allocated string containing the converted data
1361 inittab_decode(const dhcp_symbol_t
*ie
, const uchar_t
*payload
, uint16_t length
,
1362 boolean_t just_payload
)
1366 return (inittab_decode_e(ie
, payload
, length
, just_payload
, &ierrno
));
1370 * inittab_msg(): prints diagnostic messages if INITTAB_DEBUG is set
1372 * const char *: a printf-like format string
1373 * ...: arguments to the format string
1379 inittab_msg(const char *fmt
, ...)
1381 enum { INITTAB_MSG_CHECK
, INITTAB_MSG_RETURN
, INITTAB_MSG_OUTPUT
};
1385 static int action
= INITTAB_MSG_CHECK
;
1388 * check DHCP_INITTAB_DEBUG the first time in; thereafter, use
1389 * the the cached result (stored in `action').
1393 case INITTAB_MSG_CHECK
:
1395 if (getenv("DHCP_INITTAB_DEBUG") == NULL
) {
1396 action
= INITTAB_MSG_RETURN
;
1400 action
= INITTAB_MSG_OUTPUT
;
1402 /* FALLTHRU into INITTAB_MSG_OUTPUT */
1404 case INITTAB_MSG_OUTPUT
:
1408 (void) snprintf(buf
, sizeof (buf
), "inittab: %s\n", fmt
);
1409 (void) vfprintf(stderr
, buf
, ap
);
1414 case INITTAB_MSG_RETURN
:
1421 * decode_number(): decodes a sequence of numbers from binary into ascii;
1422 * binary is coming off of the network, so it is in nbo
1424 * input: uint8_t: the number of "granularity" numbers to decode
1425 * uint8_t: the length of each number
1426 * boolean_t: whether the numbers should be considered signed
1427 * uint8_t: the number of numbers per granularity
1428 * const uint8_t *: where to decode the numbers from
1429 * char *: where to decode the numbers to
1430 * output: boolean_t: true on successful conversion, false on failure
1434 decode_number(uint8_t n_entries
, uint8_t size
, boolean_t is_signed
,
1435 uint8_t granularity
, const uint8_t *from
, char *to
, int *ierrnop
)
1441 if (granularity
!= 0) {
1442 if ((granularity
% n_entries
) != 0) {
1443 inittab_msg("decode_number: number of entries "
1444 "not compatible with option granularity");
1445 *ierrnop
= ITAB_BAD_GRAN
;
1450 for (; n_entries
!= 0; n_entries
--, from
+= size
) {
1455 to
+= sprintf(to
, is_signed
? "%d" : "%u", *from
);
1459 (void) memcpy(&uint16
, from
, 2);
1460 to
+= sprintf(to
, is_signed
? "%hd" : "%hu",
1466 (void) memcpy((uchar_t
*)&uint32
+ 1, from
, 3);
1467 to
+= sprintf(to
, is_signed
? "%ld" : "%lu",
1472 (void) memcpy(&uint32
, from
, 4);
1473 to
+= sprintf(to
, is_signed
? "%ld" : "%lu",
1478 (void) memcpy(&uint64
, from
, 8);
1479 to
+= sprintf(to
, is_signed
? "%lld" : "%llu",
1484 *ierrnop
= ITAB_BAD_NUMBER
;
1485 inittab_msg("decode_number: unknown integer size `%d'",
1498 * encode_number(): encodes a sequence of numbers from ascii into binary;
1499 * number will end up on the wire so it needs to be in nbo
1501 * input: uint8_t: the number of "granularity" numbers to encode
1502 * uint8_t: the length of each number
1503 * boolean_t: whether the numbers should be considered signed
1504 * uint8_t: the number of numbers per granularity
1505 * const uint8_t *: where to encode the numbers from
1506 * char *: where to encode the numbers to
1507 * int *: set to extended error code if error occurs.
1508 * output: boolean_t: true on successful conversion, false on failure
1511 static boolean_t
/* ARGSUSED */
1512 encode_number(uint8_t n_entries
, uint8_t size
, boolean_t is_signed
,
1513 uint8_t granularity
, const char *from
, uint8_t *to
, int *ierrnop
)
1521 if (granularity
!= 0) {
1522 if ((granularity
% n_entries
) != 0) {
1523 *ierrnop
= ITAB_BAD_GRAN
;
1524 inittab_msg("encode_number: number of entries "
1525 "not compatible with option granularity");
1530 for (i
= 0; i
< n_entries
; i
++, from
++, to
+= size
) {
1533 * totally obscure c factoid: it is legal to pass a
1534 * string representing a negative number to strtoul().
1535 * in this case, strtoul() will return an unsigned
1536 * long that if cast to a long, would represent the
1537 * negative number. we take advantage of this to
1538 * cut down on code here.
1545 *to
= strtoul(from
, &endptr
, 0);
1546 if (errno
!= 0 || from
== endptr
) {
1552 uint16
= htons(strtoul(from
, &endptr
, 0));
1553 if (errno
!= 0 || from
== endptr
) {
1556 (void) memcpy(to
, &uint16
, 2);
1560 uint32
= htonl(strtoul(from
, &endptr
, 0));
1561 if (errno
!= 0 || from
== endptr
) {
1564 (void) memcpy(to
, (uchar_t
*)&uint32
+ 1, 3);
1568 uint32
= htonl(strtoul(from
, &endptr
, 0));
1569 if (errno
!= 0 || from
== endptr
) {
1572 (void) memcpy(to
, &uint32
, 4);
1576 uint64
= htonll(strtoull(from
, &endptr
, 0));
1577 if (errno
!= 0 || from
== endptr
) {
1580 (void) memcpy(to
, &uint64
, 8);
1584 inittab_msg("encode_number: unsupported integer "
1589 from
= strchr(from
, ' ');
1597 *ierrnop
= ITAB_BAD_NUMBER
;
1598 inittab_msg("encode_number: cannot convert to integer");
1603 * inittab_type_to_size(): given an inittab entry, returns size of one entry of
1606 * input: dhcp_symbol_t *: an entry of the given type
1607 * output: uint8_t: the size in bytes of an entry of that type
1611 inittab_type_to_size(const dhcp_symbol_t
*ie
)
1613 switch (ie
->ds_type
) {
1624 case DSYM_SNUMBER16
:
1625 case DSYM_UNUMBER16
:
1629 case DSYM_UNUMBER24
:
1633 case DSYM_SNUMBER32
:
1634 case DSYM_UNUMBER32
:
1639 case DSYM_SNUMBER64
:
1640 case DSYM_UNUMBER64
:
1646 return (ie
->ds_gran
);
1650 return (sizeof (in6_addr_t
));
1657 * itabcode_to_dsymcode(): maps an inittab category code to its dsym
1660 * input: uchar_t: the inittab category code
1661 * output: dsym_category_t: the dsym category code
1664 static dsym_category_t
1665 itabcode_to_dsymcode(uchar_t itabcode
)
1670 for (i
= 0; i
< ITAB_CAT_COUNT
; i
++)
1671 if (category_map
[i
].cme_itabcode
== itabcode
)
1672 return (category_map
[i
].cme_dsymcode
);
1674 return (DSYM_BAD_CAT
);
1678 * category_to_code(): maps a category name to its numeric representation
1680 * input: const char *: the category name
1681 * output: uchar_t: its internal code (numeric representation)
1685 category_to_code(const char *category
)
1689 for (i
= 0; i
< ITAB_CAT_COUNT
; i
++)
1690 if (strcasecmp(category_map
[i
].cme_name
, category
) == 0)
1691 return (category_map
[i
].cme_itabcode
);
1697 * our internal table of DHCP option values, used by inittab_verify()
1699 static dhcp_symbol_t inittab_table
[] =
1701 { DSYM_INTERNAL
, 1024, "Hostname", DSYM_BOOL
, 0, 0 },
1702 { DSYM_INTERNAL
, 1025, "LeaseNeg", DSYM_BOOL
, 0, 0 },
1703 { DSYM_INTERNAL
, 1026, "EchoVC", DSYM_BOOL
, 0, 0 },
1704 { DSYM_INTERNAL
, 1027, "BootPath", DSYM_ASCII
, 1, 128 },
1705 { DSYM_FIELD
, 0, "Opcode", DSYM_UNUMBER8
, 1, 1 },
1706 { DSYM_FIELD
, 1, "Htype", DSYM_UNUMBER8
, 1, 1 },
1707 { DSYM_FIELD
, 2, "HLen", DSYM_UNUMBER8
, 1, 1 },
1708 { DSYM_FIELD
, 3, "Hops", DSYM_UNUMBER8
, 1, 1 },
1709 { DSYM_FIELD
, 4, "Xid", DSYM_UNUMBER32
, 1, 1 },
1710 { DSYM_FIELD
, 8, "Secs", DSYM_UNUMBER16
, 1, 1 },
1711 { DSYM_FIELD
, 10, "Flags", DSYM_OCTET
, 1, 2 },
1712 { DSYM_FIELD
, 12, "Ciaddr", DSYM_IP
, 1, 1 },
1713 { DSYM_FIELD
, 16, "Yiaddr", DSYM_IP
, 1, 1 },
1714 { DSYM_FIELD
, 20, "BootSrvA", DSYM_IP
, 1, 1 },
1715 { DSYM_FIELD
, 24, "Giaddr", DSYM_IP
, 1, 1 },
1716 { DSYM_FIELD
, 28, "Chaddr", DSYM_OCTET
, 1, 16 },
1717 { DSYM_FIELD
, 44, "BootSrvN", DSYM_ASCII
, 1, 64 },
1718 { DSYM_FIELD
, 108, "BootFile", DSYM_ASCII
, 1, 128 },
1719 { DSYM_FIELD
, 236, "Magic", DSYM_OCTET
, 1, 4 },
1720 { DSYM_FIELD
, 240, "Options", DSYM_OCTET
, 1, 60 },
1721 { DSYM_STANDARD
, 1, "Subnet", DSYM_IP
, 1, 1 },
1722 { DSYM_STANDARD
, 2, "UTCoffst", DSYM_SNUMBER32
, 1, 1 },
1723 { DSYM_STANDARD
, 3, "Router", DSYM_IP
, 1, 0 },
1724 { DSYM_STANDARD
, 4, "Timeserv", DSYM_IP
, 1, 0 },
1725 { DSYM_STANDARD
, 5, "IEN116ns", DSYM_IP
, 1, 0 },
1726 { DSYM_STANDARD
, 6, "DNSserv", DSYM_IP
, 1, 0 },
1727 { DSYM_STANDARD
, 7, "Logserv", DSYM_IP
, 1, 0 },
1728 { DSYM_STANDARD
, 8, "Cookie", DSYM_IP
, 1, 0 },
1729 { DSYM_STANDARD
, 9, "Lprserv", DSYM_IP
, 1, 0 },
1730 { DSYM_STANDARD
, 10, "Impress", DSYM_IP
, 1, 0 },
1731 { DSYM_STANDARD
, 11, "Resource", DSYM_IP
, 1, 0 },
1732 { DSYM_STANDARD
, 12, "Hostname", DSYM_ASCII
, 1, 0 },
1733 { DSYM_STANDARD
, 13, "Bootsize", DSYM_UNUMBER16
, 1, 1 },
1734 { DSYM_STANDARD
, 14, "Dumpfile", DSYM_ASCII
, 1, 0 },
1735 { DSYM_STANDARD
, 15, "DNSdmain", DSYM_ASCII
, 1, 0 },
1736 { DSYM_STANDARD
, 16, "Swapserv", DSYM_IP
, 1, 1 },
1737 { DSYM_STANDARD
, 17, "Rootpath", DSYM_ASCII
, 1, 0 },
1738 { DSYM_STANDARD
, 18, "ExtendP", DSYM_ASCII
, 1, 0 },
1739 { DSYM_STANDARD
, 19, "IpFwdF", DSYM_UNUMBER8
, 1, 1 },
1740 { DSYM_STANDARD
, 20, "NLrouteF", DSYM_UNUMBER8
, 1, 1 },
1741 { DSYM_STANDARD
, 21, "PFilter", DSYM_IP
, 2, 0 },
1742 { DSYM_STANDARD
, 22, "MaxIpSiz", DSYM_UNUMBER16
, 1, 1 },
1743 { DSYM_STANDARD
, 23, "IpTTL", DSYM_UNUMBER8
, 1, 1 },
1744 { DSYM_STANDARD
, 24, "PathTO", DSYM_UNUMBER32
, 1, 1 },
1745 { DSYM_STANDARD
, 25, "PathTbl", DSYM_UNUMBER16
, 1, 0 },
1746 { DSYM_STANDARD
, 26, "MTU", DSYM_UNUMBER16
, 1, 1 },
1747 { DSYM_STANDARD
, 27, "SameMtuF", DSYM_UNUMBER8
, 1, 1 },
1748 { DSYM_STANDARD
, 28, "Broadcst", DSYM_IP
, 1, 1 },
1749 { DSYM_STANDARD
, 29, "MaskDscF", DSYM_UNUMBER8
, 1, 1 },
1750 { DSYM_STANDARD
, 30, "MaskSupF", DSYM_UNUMBER8
, 1, 1 },
1751 { DSYM_STANDARD
, 31, "RDiscvyF", DSYM_UNUMBER8
, 1, 1 },
1752 { DSYM_STANDARD
, 32, "RSolictS", DSYM_IP
, 1, 1 },
1753 { DSYM_STANDARD
, 33, "StaticRt", DSYM_IP
, 2, 0 },
1754 { DSYM_STANDARD
, 34, "TrailerF", DSYM_UNUMBER8
, 1, 1 },
1755 { DSYM_STANDARD
, 35, "ArpTimeO", DSYM_UNUMBER32
, 1, 1 },
1756 { DSYM_STANDARD
, 36, "EthEncap", DSYM_UNUMBER8
, 1, 1 },
1757 { DSYM_STANDARD
, 37, "TcpTTL", DSYM_UNUMBER8
, 1, 1 },
1758 { DSYM_STANDARD
, 38, "TcpKaInt", DSYM_UNUMBER32
, 1, 1 },
1759 { DSYM_STANDARD
, 39, "TcpKaGbF", DSYM_UNUMBER8
, 1, 1 },
1760 { DSYM_STANDARD
, 40, "NISdmain", DSYM_ASCII
, 1, 0 },
1761 { DSYM_STANDARD
, 41, "NISservs", DSYM_IP
, 1, 0 },
1762 { DSYM_STANDARD
, 42, "NTPservs", DSYM_IP
, 1, 0 },
1763 { DSYM_STANDARD
, 43, "Vendor", DSYM_OCTET
, 1, 0 },
1764 { DSYM_STANDARD
, 44, "NetBNms", DSYM_IP
, 1, 0 },
1765 { DSYM_STANDARD
, 45, "NetBDsts", DSYM_IP
, 1, 0 },
1766 { DSYM_STANDARD
, 46, "NetBNdT", DSYM_UNUMBER8
, 1, 1 },
1767 { DSYM_STANDARD
, 47, "NetBScop", DSYM_ASCII
, 1, 0 },
1768 { DSYM_STANDARD
, 48, "XFontSrv", DSYM_IP
, 1, 0 },
1769 { DSYM_STANDARD
, 49, "XDispMgr", DSYM_IP
, 1, 0 },
1770 { DSYM_STANDARD
, 50, "ReqIP", DSYM_IP
, 1, 1 },
1771 { DSYM_STANDARD
, 51, "LeaseTim", DSYM_UNUMBER32
, 1, 1 },
1772 { DSYM_STANDARD
, 52, "OptOvrld", DSYM_UNUMBER8
, 1, 1 },
1773 { DSYM_STANDARD
, 53, "DHCPType", DSYM_UNUMBER8
, 1, 1 },
1774 { DSYM_STANDARD
, 54, "ServerID", DSYM_IP
, 1, 1 },
1775 { DSYM_STANDARD
, 55, "ReqList", DSYM_OCTET
, 1, 0 },
1776 { DSYM_STANDARD
, 56, "Message", DSYM_ASCII
, 1, 0 },
1777 { DSYM_STANDARD
, 57, "DHCP_MTU", DSYM_UNUMBER16
, 1, 1 },
1778 { DSYM_STANDARD
, 58, "T1Time", DSYM_UNUMBER32
, 1, 1 },
1779 { DSYM_STANDARD
, 59, "T2Time", DSYM_UNUMBER32
, 1, 1 },
1780 { DSYM_STANDARD
, 60, "ClassID", DSYM_ASCII
, 1, 0 },
1781 { DSYM_STANDARD
, 61, "ClientID", DSYM_OCTET
, 1, 0 },
1782 { DSYM_STANDARD
, 62, "NW_dmain", DSYM_ASCII
, 1, 0 },
1783 { DSYM_STANDARD
, 63, "NWIPOpts", DSYM_OCTET
, 1, 128 },
1784 { DSYM_STANDARD
, 64, "NIS+dom", DSYM_ASCII
, 1, 0 },
1785 { DSYM_STANDARD
, 65, "NIS+serv", DSYM_IP
, 1, 0 },
1786 { DSYM_STANDARD
, 66, "TFTPsrvN", DSYM_ASCII
, 1, 64 },
1787 { DSYM_STANDARD
, 67, "OptBootF", DSYM_ASCII
, 1, 128 },
1788 { DSYM_STANDARD
, 68, "MblIPAgt", DSYM_IP
, 1, 0 },
1789 { DSYM_STANDARD
, 69, "SMTPserv", DSYM_IP
, 1, 0 },
1790 { DSYM_STANDARD
, 70, "POP3serv", DSYM_IP
, 1, 0 },
1791 { DSYM_STANDARD
, 71, "NNTPserv", DSYM_IP
, 1, 0 },
1792 { DSYM_STANDARD
, 72, "WWWservs", DSYM_IP
, 1, 0 },
1793 { DSYM_STANDARD
, 73, "Fingersv", DSYM_IP
, 1, 0 },
1794 { DSYM_STANDARD
, 74, "IRCservs", DSYM_IP
, 1, 0 },
1795 { DSYM_STANDARD
, 75, "STservs", DSYM_IP
, 1, 0 },
1796 { DSYM_STANDARD
, 76, "STDAservs", DSYM_IP
, 1, 0 },
1797 { DSYM_STANDARD
, 77, "UserClas", DSYM_ASCII
, 1, 0 },
1798 { DSYM_STANDARD
, 78, "SLP_DA", DSYM_OCTET
, 1, 0 },
1799 { DSYM_STANDARD
, 79, "SLP_SS", DSYM_OCTET
, 1, 0 },
1800 { DSYM_STANDARD
, 82, "AgentOpt", DSYM_OCTET
, 1, 0 },
1801 { DSYM_STANDARD
, 89, "FQDN", DSYM_OCTET
, 1, 0 },
1802 { 0, 0, "", 0, 0, 0 }