xref: /freebsd-src/libexec/bootpd/dovend.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
144099b7bSPaul Traina /*
244099b7bSPaul Traina  * dovend.c : Inserts all but the first few vendor options.
344099b7bSPaul Traina  */
444099b7bSPaul Traina 
544099b7bSPaul Traina #include <sys/types.h>
644099b7bSPaul Traina 
744099b7bSPaul Traina #include <netinet/in.h>
844099b7bSPaul Traina #include <arpa/inet.h>			/* inet_ntoa */
944099b7bSPaul Traina 
1044099b7bSPaul Traina #include <stdlib.h>
1144099b7bSPaul Traina #include <stdio.h>
1244099b7bSPaul Traina #include <string.h>
1344099b7bSPaul Traina #include <errno.h>
1444099b7bSPaul Traina #include <syslog.h>
1544099b7bSPaul Traina 
1644099b7bSPaul Traina #include "bootp.h"
1744099b7bSPaul Traina #include "bootpd.h"
1844099b7bSPaul Traina #include "report.h"
1944099b7bSPaul Traina #include "dovend.h"
2044099b7bSPaul Traina 
21f19d047aSAlfred Perlstein PRIVATE int insert_generic(struct shared_bindata *, byte **, int *);
2244099b7bSPaul Traina 
2344099b7bSPaul Traina /*
2444099b7bSPaul Traina  * Insert the 2nd part of the options into an option buffer.
2544099b7bSPaul Traina  * Return amount of space used.
2644099b7bSPaul Traina  *
2744099b7bSPaul Traina  * This inserts everything EXCEPT:
2844099b7bSPaul Traina  *   magic cookie, subnet mask, gateway, bootsize, extension file
2944099b7bSPaul Traina  * Those are handled separately (in bootpd.c) to allow this function
3044099b7bSPaul Traina  * to be shared between bootpd and bootpef.
3144099b7bSPaul Traina  *
3244099b7bSPaul Traina  * When an "extension file" is in use, the options inserted by
3344099b7bSPaul Traina  * this function go into the exten_file, not the bootp response.
3444099b7bSPaul Traina  */
3544099b7bSPaul Traina 
3644099b7bSPaul Traina int
dovend_rfc1497(struct host * hp,byte * buf,int len)37*8b356c88SJohn Baldwin dovend_rfc1497(struct host *hp, byte *buf, int len)
3844099b7bSPaul Traina {
3944099b7bSPaul Traina 	int bytesleft = len;
4044099b7bSPaul Traina 	byte *vp = buf;
4144099b7bSPaul Traina 
42be9efd56SKris Kennaway 	static const char noroom[] = "%s: No room for \"%s\" option";
4344099b7bSPaul Traina #define	NEED(LEN, MSG) do                       \
4444099b7bSPaul Traina 		if (bytesleft < (LEN)) {         	    \
4544099b7bSPaul Traina 			report(LOG_NOTICE, noroom,          \
4644099b7bSPaul Traina 				   hp->hostname->string, MSG);  \
4744099b7bSPaul Traina 			return (vp - buf);                  \
4844099b7bSPaul Traina 		} while (0)
4944099b7bSPaul Traina 
5044099b7bSPaul Traina 	/*
5144099b7bSPaul Traina 	 * Note that the following have already been inserted:
5244099b7bSPaul Traina 	 *   magic_cookie, subnet_mask, gateway, bootsize
5344099b7bSPaul Traina 	 *
5444099b7bSPaul Traina 	 * The remaining options are inserted in order of importance.
5544099b7bSPaul Traina 	 * (Of course the importance of each is a matter of opinion.)
5644099b7bSPaul Traina 	 * The option insertion order should probably be configurable.
5744099b7bSPaul Traina 	 *
5844099b7bSPaul Traina 	 * This is the order used in the NetBSD version.  Can anyone
5944099b7bSPaul Traina 	 * explain why the time_offset and swap_server are first?
6044099b7bSPaul Traina 	 * Also, why is the hostname so far down the list?  -gwr
6144099b7bSPaul Traina 	 */
6244099b7bSPaul Traina 
6344099b7bSPaul Traina 	if (hp->flags.time_offset) {
6444099b7bSPaul Traina 		NEED(6, "to");
6544099b7bSPaul Traina 		*vp++ = TAG_TIME_OFFSET;/* -1 byte  */
6644099b7bSPaul Traina 		*vp++ = 4;				/* -1 byte  */
6744099b7bSPaul Traina 		insert_u_long(htonl(hp->time_offset), &vp);	/* -4 bytes */
6844099b7bSPaul Traina 		bytesleft -= 6;
6944099b7bSPaul Traina 	}
7044099b7bSPaul Traina 	/*
7144099b7bSPaul Traina 	 * swap server, root path, dump path
7244099b7bSPaul Traina 	 */
7344099b7bSPaul Traina 	if (hp->flags.swap_server) {
7444099b7bSPaul Traina 		NEED(6, "sw");
7544099b7bSPaul Traina 		/* There is just one SWAP_SERVER, so it is not an iplist. */
7644099b7bSPaul Traina 		*vp++ = TAG_SWAP_SERVER;/* -1 byte  */
7744099b7bSPaul Traina 		*vp++ = 4;				/* -1 byte  */
7844099b7bSPaul Traina 		insert_u_long(hp->swap_server.s_addr, &vp);	/* -4 bytes */
7944099b7bSPaul Traina 		bytesleft -= 6;			/* Fix real count */
8044099b7bSPaul Traina 	}
8144099b7bSPaul Traina 	if (hp->flags.root_path) {
8244099b7bSPaul Traina 		/*
8344099b7bSPaul Traina 		 * Check for room for root_path.  Add 2 to account for
8444099b7bSPaul Traina 		 * TAG_ROOT_PATH and length.
8544099b7bSPaul Traina 		 */
8644099b7bSPaul Traina 		len = strlen(hp->root_path->string);
8744099b7bSPaul Traina 		NEED((len + 2), "rp");
8844099b7bSPaul Traina 		*vp++ = TAG_ROOT_PATH;
8944099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
9044099b7bSPaul Traina 		bcopy(hp->root_path->string, vp, len);
9144099b7bSPaul Traina 		vp += len;
9244099b7bSPaul Traina 		bytesleft -= len + 2;
9344099b7bSPaul Traina 	}
9444099b7bSPaul Traina 	if (hp->flags.dump_file) {
9544099b7bSPaul Traina 		/*
9644099b7bSPaul Traina 		 * Check for room for dump_file.  Add 2 to account for
9744099b7bSPaul Traina 		 * TAG_DUMP_FILE and length.
9844099b7bSPaul Traina 		 */
9944099b7bSPaul Traina 		len = strlen(hp->dump_file->string);
10044099b7bSPaul Traina 		NEED((len + 2), "df");
10144099b7bSPaul Traina 		*vp++ = TAG_DUMP_FILE;
10244099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
10344099b7bSPaul Traina 		bcopy(hp->dump_file->string, vp, len);
10444099b7bSPaul Traina 		vp += len;
10544099b7bSPaul Traina 		bytesleft -= len + 2;
10644099b7bSPaul Traina 	}
10744099b7bSPaul Traina 	/*
10844099b7bSPaul Traina 	 * DNS server and domain
10944099b7bSPaul Traina 	 */
11044099b7bSPaul Traina 	if (hp->flags.domain_server) {
11144099b7bSPaul Traina 		if (insert_ip(TAG_DOMAIN_SERVER,
11244099b7bSPaul Traina 					  hp->domain_server,
11344099b7bSPaul Traina 					  &vp, &bytesleft))
11444099b7bSPaul Traina 			NEED(8, "ds");
11544099b7bSPaul Traina 	}
11644099b7bSPaul Traina 	if (hp->flags.domain_name) {
11744099b7bSPaul Traina 		/*
11844099b7bSPaul Traina 		 * Check for room for domain_name.  Add 2 to account for
11944099b7bSPaul Traina 		 * TAG_DOMAIN_NAME and length.
12044099b7bSPaul Traina 		 */
12144099b7bSPaul Traina 		len = strlen(hp->domain_name->string);
12244099b7bSPaul Traina 		NEED((len + 2), "dn");
12344099b7bSPaul Traina 		*vp++ = TAG_DOMAIN_NAME;
12444099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
12544099b7bSPaul Traina 		bcopy(hp->domain_name->string, vp, len);
12644099b7bSPaul Traina 		vp += len;
12744099b7bSPaul Traina 		bytesleft -= len + 2;
12844099b7bSPaul Traina 	}
12944099b7bSPaul Traina 	/*
13044099b7bSPaul Traina 	 * NIS (YP) server and domain
13144099b7bSPaul Traina 	 */
13244099b7bSPaul Traina 	if (hp->flags.nis_server) {
13344099b7bSPaul Traina 		if (insert_ip(TAG_NIS_SERVER,
13444099b7bSPaul Traina 					  hp->nis_server,
13544099b7bSPaul Traina 					  &vp, &bytesleft))
13634366bc9SEd Maste 			NEED(8, "ys");
13744099b7bSPaul Traina 	}
13844099b7bSPaul Traina 	if (hp->flags.nis_domain) {
13944099b7bSPaul Traina 		/*
14044099b7bSPaul Traina 		 * Check for room for nis_domain.  Add 2 to account for
14144099b7bSPaul Traina 		 * TAG_NIS_DOMAIN and length.
14244099b7bSPaul Traina 		 */
14344099b7bSPaul Traina 		len = strlen(hp->nis_domain->string);
14434366bc9SEd Maste 		NEED((len + 2), "yn");
14544099b7bSPaul Traina 		*vp++ = TAG_NIS_DOMAIN;
14644099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
14744099b7bSPaul Traina 		bcopy(hp->nis_domain->string, vp, len);
14844099b7bSPaul Traina 		vp += len;
14944099b7bSPaul Traina 		bytesleft -= len + 2;
15044099b7bSPaul Traina 	}
15144099b7bSPaul Traina 	/* IEN 116 name server */
15244099b7bSPaul Traina 	if (hp->flags.name_server) {
15344099b7bSPaul Traina 		if (insert_ip(TAG_NAME_SERVER,
15444099b7bSPaul Traina 					  hp->name_server,
15544099b7bSPaul Traina 					  &vp, &bytesleft))
15644099b7bSPaul Traina 			NEED(8, "ns");
15744099b7bSPaul Traina 	}
15844099b7bSPaul Traina 	if (hp->flags.rlp_server) {
15944099b7bSPaul Traina 		if (insert_ip(TAG_RLP_SERVER,
16044099b7bSPaul Traina 					  hp->rlp_server,
16144099b7bSPaul Traina 					  &vp, &bytesleft))
16244099b7bSPaul Traina 			NEED(8, "rl");
16344099b7bSPaul Traina 	}
16444099b7bSPaul Traina 	/* Time server (RFC 868) */
16544099b7bSPaul Traina 	if (hp->flags.time_server) {
16644099b7bSPaul Traina 		if (insert_ip(TAG_TIME_SERVER,
16744099b7bSPaul Traina 					  hp->time_server,
16844099b7bSPaul Traina 					  &vp, &bytesleft))
16944099b7bSPaul Traina 			NEED(8, "ts");
17044099b7bSPaul Traina 	}
17144099b7bSPaul Traina 	/* NTP (time) Server (RFC 1129) */
17244099b7bSPaul Traina 	if (hp->flags.ntp_server) {
17344099b7bSPaul Traina 		if (insert_ip(TAG_NTP_SERVER,
17444099b7bSPaul Traina 					  hp->ntp_server,
17544099b7bSPaul Traina 					  &vp, &bytesleft))
17634366bc9SEd Maste 			NEED(8, "nt");
17744099b7bSPaul Traina 	}
17844099b7bSPaul Traina 	/*
17944099b7bSPaul Traina 	 * I wonder:  If the hostname were "promoted" into the BOOTP
18044099b7bSPaul Traina 	 * response part, might these "extension" files possibly be
18144099b7bSPaul Traina 	 * shared between several clients?
18244099b7bSPaul Traina 	 *
18344099b7bSPaul Traina 	 * Also, why not just use longer BOOTP packets with all the
18444099b7bSPaul Traina 	 * additional length used as option data.  This bootpd version
18544099b7bSPaul Traina 	 * already supports that feature by replying with the same
18644099b7bSPaul Traina 	 * packet length as the client request packet. -gwr
18744099b7bSPaul Traina 	 */
18844099b7bSPaul Traina 	if (hp->flags.name_switch && hp->flags.send_name) {
18944099b7bSPaul Traina 		/*
19044099b7bSPaul Traina 		 * Check for room for hostname.  Add 2 to account for
19144099b7bSPaul Traina 		 * TAG_HOST_NAME and length.
19244099b7bSPaul Traina 		 */
19344099b7bSPaul Traina 		len = strlen(hp->hostname->string);
19444099b7bSPaul Traina #if 0
19544099b7bSPaul Traina 		/*
19644099b7bSPaul Traina 		 * XXX - Too much magic.  The user can always set the hostname
19744099b7bSPaul Traina 		 * to the short version in the bootptab file. -gwr
19844099b7bSPaul Traina 		 */
19944099b7bSPaul Traina 		if ((len + 2) > bytesleft) {
20044099b7bSPaul Traina 			/*
20144099b7bSPaul Traina 			 * Not enough room for full (domain-qualified) hostname, try
20244099b7bSPaul Traina 			 * stripping it down to just the first field (host).
20344099b7bSPaul Traina 			 */
204e08ac58bSPaul Traina 			char *tmpstr = hp->hostname->string;
20544099b7bSPaul Traina 			len = 0;
20644099b7bSPaul Traina 			while (*tmpstr && (*tmpstr != '.')) {
20744099b7bSPaul Traina 				tmpstr++;
20844099b7bSPaul Traina 				len++;
20944099b7bSPaul Traina 			}
21044099b7bSPaul Traina 		}
21144099b7bSPaul Traina #endif
21244099b7bSPaul Traina 		NEED((len + 2), "hn");
21344099b7bSPaul Traina 		*vp++ = TAG_HOST_NAME;
21444099b7bSPaul Traina 		*vp++ = (byte) (len & 0xFF);
21544099b7bSPaul Traina 		bcopy(hp->hostname->string, vp, len);
21644099b7bSPaul Traina 		vp += len;
21744099b7bSPaul Traina 		bytesleft -= len + 2;
21844099b7bSPaul Traina 	}
21944099b7bSPaul Traina 	/*
22044099b7bSPaul Traina 	 * The rest of these are less important, so they go last.
22144099b7bSPaul Traina 	 */
22244099b7bSPaul Traina 	if (hp->flags.lpr_server) {
22344099b7bSPaul Traina 		if (insert_ip(TAG_LPR_SERVER,
22444099b7bSPaul Traina 					  hp->lpr_server,
22544099b7bSPaul Traina 					  &vp, &bytesleft))
22644099b7bSPaul Traina 			NEED(8, "lp");
22744099b7bSPaul Traina 	}
22844099b7bSPaul Traina 	if (hp->flags.cookie_server) {
22944099b7bSPaul Traina 		if (insert_ip(TAG_COOKIE_SERVER,
23044099b7bSPaul Traina 					  hp->cookie_server,
23144099b7bSPaul Traina 					  &vp, &bytesleft))
23244099b7bSPaul Traina 			NEED(8, "cs");
23344099b7bSPaul Traina 	}
23444099b7bSPaul Traina 	if (hp->flags.log_server) {
23544099b7bSPaul Traina 		if (insert_ip(TAG_LOG_SERVER,
23644099b7bSPaul Traina 					  hp->log_server,
23744099b7bSPaul Traina 					  &vp, &bytesleft))
23844099b7bSPaul Traina 			NEED(8, "lg");
23944099b7bSPaul Traina 	}
24044099b7bSPaul Traina 	/*
24144099b7bSPaul Traina 	 * XXX - Add new tags here (to insert options)
24244099b7bSPaul Traina 	 */
24344099b7bSPaul Traina 	if (hp->flags.generic) {
24444099b7bSPaul Traina 		if (insert_generic(hp->generic, &vp, &bytesleft))
24544099b7bSPaul Traina 			NEED(64, "(generic)");
24644099b7bSPaul Traina 	}
24744099b7bSPaul Traina 	/*
24844099b7bSPaul Traina 	 * The end marker is inserted by the caller.
24944099b7bSPaul Traina 	 */
25044099b7bSPaul Traina 	return (vp - buf);
25144099b7bSPaul Traina #undef	NEED
25244099b7bSPaul Traina }								/* dovend_rfc1497 */
25344099b7bSPaul Traina 
25444099b7bSPaul Traina 
25544099b7bSPaul Traina 
25644099b7bSPaul Traina /*
25744099b7bSPaul Traina  * Insert a tag value, a length value, and a list of IP addresses into the
25844099b7bSPaul Traina  * memory buffer indirectly pointed to by "dest".  "tag" is the RFC1048 tag
25944099b7bSPaul Traina  * number to use, "iplist" is a pointer to a list of IP addresses
26044099b7bSPaul Traina  * (struct in_addr_list), and "bytesleft" points to an integer which
26144099b7bSPaul Traina  * indicates the size of the "dest" buffer.
26244099b7bSPaul Traina  *
26344099b7bSPaul Traina  * Return zero if everything fits.
26444099b7bSPaul Traina  *
26544099b7bSPaul Traina  * This is used to fill the vendor-specific area of a bootp packet in
26644099b7bSPaul Traina  * conformance to RFC1048.
26744099b7bSPaul Traina  */
26844099b7bSPaul Traina 
26944099b7bSPaul Traina int
insert_ip(byte tag,struct in_addr_list * iplist,byte ** dest,int * bytesleft)270*8b356c88SJohn Baldwin insert_ip(byte tag, struct in_addr_list *iplist, byte **dest, int *bytesleft)
27144099b7bSPaul Traina {
27244099b7bSPaul Traina 	struct in_addr *addrptr;
27344099b7bSPaul Traina 	unsigned addrcount = 1;
27444099b7bSPaul Traina 	byte *d;
27544099b7bSPaul Traina 
27644099b7bSPaul Traina 	if (iplist == NULL)
27744099b7bSPaul Traina 		return (0);
27844099b7bSPaul Traina 
27944099b7bSPaul Traina 	if (*bytesleft >= 6) {
28044099b7bSPaul Traina 		d = *dest;				/* Save pointer for later */
28144099b7bSPaul Traina 		**dest = tag;
28244099b7bSPaul Traina 		(*dest) += 2;
28344099b7bSPaul Traina 		(*bytesleft) -= 2;		/* Account for tag and length */
28444099b7bSPaul Traina 		addrptr = iplist->addr;
28544099b7bSPaul Traina 		addrcount = iplist->addrcount;
28644099b7bSPaul Traina 		while ((*bytesleft >= 4) && (addrcount > 0)) {
28744099b7bSPaul Traina 			insert_u_long(addrptr->s_addr, dest);
28844099b7bSPaul Traina 			addrptr++;
28944099b7bSPaul Traina 			addrcount--;
29044099b7bSPaul Traina 			(*bytesleft) -= 4;	/* Four bytes per address */
29144099b7bSPaul Traina 		}
29244099b7bSPaul Traina 		d[1] = (byte) ((*dest - d - 2) & 0xFF);
29344099b7bSPaul Traina 	}
29444099b7bSPaul Traina 	return (addrcount);
29544099b7bSPaul Traina }
29644099b7bSPaul Traina 
29744099b7bSPaul Traina 
29844099b7bSPaul Traina 
29944099b7bSPaul Traina /*
30044099b7bSPaul Traina  * Insert generic data into a bootp packet.  The data is assumed to already
30144099b7bSPaul Traina  * be in RFC1048 format.  It is inserted using a first-fit algorithm which
30244099b7bSPaul Traina  * attempts to insert as many tags as possible.  Tags and data which are
30344099b7bSPaul Traina  * too large to fit are skipped; any remaining tags are tried until they
30444099b7bSPaul Traina  * have all been exhausted.
30544099b7bSPaul Traina  * Return zero if everything fits.
30644099b7bSPaul Traina  */
30744099b7bSPaul Traina 
30844099b7bSPaul Traina static int
insert_generic(struct shared_bindata * gendata,byte ** buff,int * bytesleft)309*8b356c88SJohn Baldwin insert_generic(struct shared_bindata *gendata, byte **buff, int *bytesleft)
31044099b7bSPaul Traina {
31144099b7bSPaul Traina 	byte *srcptr;
31244099b7bSPaul Traina 	int length, numbytes;
31344099b7bSPaul Traina 	int skipped = 0;
31444099b7bSPaul Traina 
31544099b7bSPaul Traina 	if (gendata == NULL)
31644099b7bSPaul Traina 		return (0);
31744099b7bSPaul Traina 
31844099b7bSPaul Traina 	srcptr = gendata->data;
31944099b7bSPaul Traina 	length = gendata->length;
32044099b7bSPaul Traina 	while ((length > 0) && (*bytesleft > 0)) {
32144099b7bSPaul Traina 		switch (*srcptr) {
32244099b7bSPaul Traina 		case TAG_END:
32344099b7bSPaul Traina 			length = 0;			/* Force an exit on next iteration */
32444099b7bSPaul Traina 			break;
32544099b7bSPaul Traina 		case TAG_PAD:
32644099b7bSPaul Traina 			*(*buff)++ = *srcptr++;
32744099b7bSPaul Traina 			(*bytesleft)--;
32844099b7bSPaul Traina 			length--;
32944099b7bSPaul Traina 			break;
33044099b7bSPaul Traina 		default:
33144099b7bSPaul Traina 			numbytes = srcptr[1] + 2;
33244099b7bSPaul Traina 			if (*bytesleft < numbytes)
33344099b7bSPaul Traina 				skipped += numbytes;
33444099b7bSPaul Traina 			else {
33544099b7bSPaul Traina 				bcopy(srcptr, *buff, numbytes);
33644099b7bSPaul Traina 				(*buff) += numbytes;
33744099b7bSPaul Traina 				(*bytesleft) -= numbytes;
33844099b7bSPaul Traina 			}
33944099b7bSPaul Traina 			srcptr += numbytes;
34044099b7bSPaul Traina 			length -= numbytes;
34144099b7bSPaul Traina 			break;
34244099b7bSPaul Traina 		}
34344099b7bSPaul Traina 	} /* while */
34444099b7bSPaul Traina 	return (skipped);
34544099b7bSPaul Traina }
34644099b7bSPaul Traina 
34744099b7bSPaul Traina /*
34844099b7bSPaul Traina  * Insert the unsigned long "value" into memory starting at the byte
34944099b7bSPaul Traina  * pointed to by the byte pointer (*dest).  (*dest) is updated to
35044099b7bSPaul Traina  * point to the next available byte.
35144099b7bSPaul Traina  *
35244099b7bSPaul Traina  * Since it is desirable to internally store network addresses in network
35344099b7bSPaul Traina  * byte order (in struct in_addr's), this routine expects longs to be
35444099b7bSPaul Traina  * passed in network byte order.
35544099b7bSPaul Traina  *
35644099b7bSPaul Traina  * However, due to the nature of the main algorithm, the long must be in
35744099b7bSPaul Traina  * host byte order, thus necessitating the use of ntohl() first.
35844099b7bSPaul Traina  */
35944099b7bSPaul Traina 
36044099b7bSPaul Traina void
insert_u_long(u_int32 value,byte ** dest)361*8b356c88SJohn Baldwin insert_u_long(u_int32 value, byte **dest)
36244099b7bSPaul Traina {
36344099b7bSPaul Traina 	byte *temp;
36444099b7bSPaul Traina 	int n;
36544099b7bSPaul Traina 
36644099b7bSPaul Traina 	value = ntohl(value);		/* Must use host byte order here */
36744099b7bSPaul Traina 	temp = (*dest += 4);
36844099b7bSPaul Traina 	for (n = 4; n > 0; n--) {
36944099b7bSPaul Traina 		*--temp = (byte) (value & 0xFF);
37044099b7bSPaul Traina 		value >>= 8;
37144099b7bSPaul Traina 	}
37244099b7bSPaul Traina 	/* Final result is network byte order */
37344099b7bSPaul Traina }
37444099b7bSPaul Traina 
37544099b7bSPaul Traina /*
37644099b7bSPaul Traina  * Local Variables:
37744099b7bSPaul Traina  * tab-width: 4
37844099b7bSPaul Traina  * c-indent-level: 4
37944099b7bSPaul Traina  * c-argdecl-indent: 4
38044099b7bSPaul Traina  * c-continued-statement-offset: 4
38144099b7bSPaul Traina  * c-continued-brace-offset: -4
38244099b7bSPaul Traina  * c-label-offset: -4
38344099b7bSPaul Traina  * c-brace-offset: 0
38444099b7bSPaul Traina  * End:
38544099b7bSPaul Traina  */
386