xref: /netbsd-src/usr.sbin/bootp/bootptest/getether.c (revision 5b28f239895d55856221c590945769250e289f5f)
1*5b28f239Srillig /*	$NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig Exp $	*/
29493bb79Slukem 
39493bb79Slukem #include <sys/cdefs.h>
49493bb79Slukem #ifndef lint
5*5b28f239Srillig __RCSID("$NetBSD: getether.c,v 1.10 2024/09/08 09:36:53 rillig Exp $");
69493bb79Slukem #endif
73fe138c1Sperry 
85e9d01f2Sgwr /*
95e9d01f2Sgwr  * getether.c : get the ethernet address of an interface
105e9d01f2Sgwr  *
115e9d01f2Sgwr  * All of this code is quite system-specific.  As you may well
125e9d01f2Sgwr  * guess, it took a good bit of detective work to figure out!
135e9d01f2Sgwr  *
145e9d01f2Sgwr  * If you figure out how to do this on another system,
155e9d01f2Sgwr  * please let me know.  <gwr@mc.com>
165e9d01f2Sgwr  */
175e9d01f2Sgwr 
185e9d01f2Sgwr #include <sys/types.h>
195e9d01f2Sgwr #include <sys/socket.h>
205e9d01f2Sgwr 
215e9d01f2Sgwr #include <ctype.h>
229493bb79Slukem #include <string.h>
23be45f4d0Stls #include <strings.h>
245e9d01f2Sgwr #include <syslog.h>
259493bb79Slukem #include <unistd.h>
265e9d01f2Sgwr 
275e9d01f2Sgwr #include "report.h"
285e9d01f2Sgwr #define EALEN 6
295e9d01f2Sgwr 
30131109e4Swiz extern int getether(char *, char *);
319493bb79Slukem 
325e9d01f2Sgwr #if defined(ultrix) || (defined(__osf__) && defined(__alpha))
335e9d01f2Sgwr /*
345e9d01f2Sgwr  * This is really easy on Ultrix!  Thanks to
355e9d01f2Sgwr  * Harald Lundberg <hl@tekla.fi> for this code.
365e9d01f2Sgwr  *
375e9d01f2Sgwr  * The code here is not specific to the Alpha, but that was the
385e9d01f2Sgwr  * only symbol we could find to identify DEC's version of OSF.
395e9d01f2Sgwr  * (Perhaps we should just define DEC in the Makefile... -gwr)
405e9d01f2Sgwr  */
415e9d01f2Sgwr 
425e9d01f2Sgwr #include <sys/ioctl.h>
435e9d01f2Sgwr #include <net/if.h>				/* struct ifdevea */
445e9d01f2Sgwr 
459493bb79Slukem int
46131109e4Swiz getether(char *ifname, char *eap)
475e9d01f2Sgwr {
485e9d01f2Sgwr 	int rc = -1;
495e9d01f2Sgwr 	int fd;
505e9d01f2Sgwr 	struct ifdevea phys;
5147a6a58fSitojun 
525e9d01f2Sgwr 	bzero(&phys, sizeof(phys));
5347a6a58fSitojun 	strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name));
545e9d01f2Sgwr 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
555e9d01f2Sgwr 		report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
565e9d01f2Sgwr 		return -1;
575e9d01f2Sgwr 	}
585e9d01f2Sgwr 	if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) {
595e9d01f2Sgwr 		report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed");
605e9d01f2Sgwr 	} else {
615e9d01f2Sgwr 		bcopy(&phys.current_pa[0], eap, EALEN);
625e9d01f2Sgwr 		rc = 0;
635e9d01f2Sgwr 	}
645e9d01f2Sgwr 	close(fd);
655e9d01f2Sgwr 	return rc;
665e9d01f2Sgwr }
675e9d01f2Sgwr 
685e9d01f2Sgwr #define	GETETHER
695e9d01f2Sgwr #endif /* ultrix|osf1 */
705e9d01f2Sgwr 
715e9d01f2Sgwr 
725e9d01f2Sgwr #ifdef	SUNOS
735e9d01f2Sgwr 
745e9d01f2Sgwr #include <sys/sockio.h>
755e9d01f2Sgwr #include <sys/time.h>			/* needed by net_if.h */
765e9d01f2Sgwr #include <net/nit_if.h>			/* for NIOCBIND */
775e9d01f2Sgwr #include <net/if.h>				/* for struct ifreq */
785e9d01f2Sgwr 
79131109e4Swiz /* ifname: interface name from ifconfig structure */
80131109e4Swiz /* eap: Ether address (output) */
81131109e4Swiz getether(char *ifname, char *eap)
825e9d01f2Sgwr {
835e9d01f2Sgwr 	int rc = -1;
845e9d01f2Sgwr 
855e9d01f2Sgwr 	struct ifreq ifrnit;
865e9d01f2Sgwr 	int nit;
875e9d01f2Sgwr 
885e9d01f2Sgwr 	bzero((char *) &ifrnit, sizeof(ifrnit));
895e9d01f2Sgwr 	strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ);
905e9d01f2Sgwr 
915e9d01f2Sgwr 	nit = open("/dev/nit", 0);
925e9d01f2Sgwr 	if (nit < 0) {
935e9d01f2Sgwr 		report(LOG_ERR, "getether: open /dev/nit: %s",
945e9d01f2Sgwr 			   get_errmsg());
955e9d01f2Sgwr 		return rc;
965e9d01f2Sgwr 	}
975e9d01f2Sgwr 	do {
985e9d01f2Sgwr 		if (ioctl(nit, NIOCBIND, &ifrnit) < 0) {
995e9d01f2Sgwr 			report(LOG_ERR, "getether: NIOCBIND on nit");
1005e9d01f2Sgwr 			break;
1015e9d01f2Sgwr 		}
1025e9d01f2Sgwr 		if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) {
1035e9d01f2Sgwr 			report(LOG_ERR, "getether: SIOCGIFADDR on nit");
1045e9d01f2Sgwr 			break;
1055e9d01f2Sgwr 		}
1065e9d01f2Sgwr 		bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN);
1075e9d01f2Sgwr 		rc = 0;
1085e9d01f2Sgwr 	} while (0);
1095e9d01f2Sgwr 	close(nit);
1105e9d01f2Sgwr 	return rc;
1115e9d01f2Sgwr }
1125e9d01f2Sgwr 
1135e9d01f2Sgwr #define	GETETHER
1145e9d01f2Sgwr #endif /* SUNOS */
1155e9d01f2Sgwr 
1165e9d01f2Sgwr 
1175e9d01f2Sgwr #if defined(__386BSD__) || defined(__NetBSD__)
1185e9d01f2Sgwr /* Thanks to John Brezak <brezak@ch.hp.com> for this code. */
1195e9d01f2Sgwr #include <sys/ioctl.h>
1205e9d01f2Sgwr #include <net/if.h>
1215e9d01f2Sgwr #include <net/if_dl.h>
1225e9d01f2Sgwr #include <net/if_types.h>
1235e9d01f2Sgwr 
124131109e4Swiz /* ifname: interface name from ifconfig structure */
125131109e4Swiz /* eap: Ether address (output) */
1269493bb79Slukem int
127131109e4Swiz getether(char *ifname, char *eap)
1285e9d01f2Sgwr {
1295e9d01f2Sgwr 	int fd, rc = -1;
130aae9c2a0Swiz 	int n;
1319493bb79Slukem 	struct ifreq ibuf[16];
1325e9d01f2Sgwr 	struct ifconf ifc;
133aae9c2a0Swiz 	struct ifreq *ifrp, *ifend;
1345e9d01f2Sgwr 
1355e9d01f2Sgwr 	/* Fetch the interface configuration */
1365e9d01f2Sgwr 	fd = socket(AF_INET, SOCK_DGRAM, 0);
1375e9d01f2Sgwr 	if (fd < 0) {
1385e9d01f2Sgwr 		report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg());
1395e9d01f2Sgwr 		return (fd);
1405e9d01f2Sgwr 	}
1415e9d01f2Sgwr 	ifc.ifc_len = sizeof(ibuf);
1425e9d01f2Sgwr 	ifc.ifc_buf = (caddr_t) ibuf;
1435e9d01f2Sgwr 	if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 ||
1447affd655Slukem 		ifc.ifc_len < (int)sizeof(struct ifreq)) {
145d8302e2dSis 		report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg());
1465e9d01f2Sgwr 		goto out;
1475e9d01f2Sgwr 	}
1485e9d01f2Sgwr 	/* Search interface configuration list for link layer address. */
1495e9d01f2Sgwr 	ifrp = ibuf;
1505e9d01f2Sgwr 	ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len);
1515e9d01f2Sgwr 	while (ifrp < ifend) {
1525e9d01f2Sgwr 		/* Look for interface */
1535e9d01f2Sgwr 		if (strcmp(ifname, ifrp->ifr_name) == 0 &&
1545e9d01f2Sgwr 			ifrp->ifr_addr.sa_family == AF_LINK &&
1555e9d01f2Sgwr 		((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) {
1565e9d01f2Sgwr 			bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN);
1575e9d01f2Sgwr 			rc = 0;
1585e9d01f2Sgwr 			break;
1595e9d01f2Sgwr 		}
1605e9d01f2Sgwr 		/* Bump interface config pointer */
1615e9d01f2Sgwr 		n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
1627affd655Slukem 		if (n < (int)sizeof(*ifrp))
1635e9d01f2Sgwr 			n = sizeof(*ifrp);
1645e9d01f2Sgwr 		ifrp = (struct ifreq *) ((char *) ifrp + n);
1655e9d01f2Sgwr 	}
1665e9d01f2Sgwr 
1675e9d01f2Sgwr   out:
1685e9d01f2Sgwr 	close(fd);
1695e9d01f2Sgwr 	return (rc);
1705e9d01f2Sgwr }
1715e9d01f2Sgwr 
1725e9d01f2Sgwr #define	GETETHER
1735e9d01f2Sgwr #endif /* __NetBSD__ */
1745e9d01f2Sgwr 
1755e9d01f2Sgwr 
1765e9d01f2Sgwr #ifdef	SVR4
1775e9d01f2Sgwr /*
1785e9d01f2Sgwr  * This is for "Streams TCP/IP" by Lachman Associates.
1795e9d01f2Sgwr  * They sure made this cumbersome!  -gwr
1805e9d01f2Sgwr  */
1815e9d01f2Sgwr 
1825e9d01f2Sgwr #include <sys/sockio.h>
1835e9d01f2Sgwr #include <sys/dlpi.h>
1845e9d01f2Sgwr #include <stropts.h>
1855e9d01f2Sgwr #ifndef NULL
1865e9d01f2Sgwr #define NULL 0
1875e9d01f2Sgwr #endif
1885e9d01f2Sgwr 
189131109e4Swiz /* ifname: interface name from ifconfig structure */
190131109e4Swiz /* eap: Ether address (output) */
191131109e4Swiz getether(char *ifname, char *eap)
1925e9d01f2Sgwr {
1935e9d01f2Sgwr 	int rc = -1;
1945e9d01f2Sgwr 	char devname[32];
1955e9d01f2Sgwr 	char tmpbuf[sizeof(union DL_primitives) + 16];
1965e9d01f2Sgwr 	struct strbuf cbuf;
1975e9d01f2Sgwr 	int fd, flags;
1985e9d01f2Sgwr 	union DL_primitives *dlp;
1995e9d01f2Sgwr 	char *enaddr;
2005e9d01f2Sgwr 	int unit = -1;				/* which unit to attach */
2015e9d01f2Sgwr 
20247a6a58fSitojun 	snprintf(devname, sizeof(devname), "/dev/%s", ifname);
2035e9d01f2Sgwr 	fd = open(devname, 2);
2045e9d01f2Sgwr 	if (fd < 0) {
2055e9d01f2Sgwr 		/* Try without the trailing digit. */
2065e9d01f2Sgwr 		char *p = devname + 5;
2075e9d01f2Sgwr 		while (isalpha(*p))
2085e9d01f2Sgwr 			p++;
2095e9d01f2Sgwr 		if (isdigit(*p)) {
2105e9d01f2Sgwr 			unit = *p - '0';
2115e9d01f2Sgwr 			*p = '\0';
2125e9d01f2Sgwr 		}
2135e9d01f2Sgwr 		fd = open(devname, 2);
2145e9d01f2Sgwr 		if (fd < 0) {
2155e9d01f2Sgwr 			report(LOG_ERR, "getether: open %s: %s",
2165e9d01f2Sgwr 				   devname, get_errmsg());
2175e9d01f2Sgwr 			return rc;
2185e9d01f2Sgwr 		}
2195e9d01f2Sgwr 	}
2205e9d01f2Sgwr #ifdef	DL_ATTACH_REQ
2215e9d01f2Sgwr 	/*
2225e9d01f2Sgwr 	 * If this is a "Style 2" DLPI, then we must "attach" first
2235e9d01f2Sgwr 	 * to tell the driver which unit (board, port) we want.
2245e9d01f2Sgwr 	 * For now, decide this based on the device name.
2255e9d01f2Sgwr 	 * (Should do "info_req" and check dl_provider_style ...)
2265e9d01f2Sgwr 	 */
2275e9d01f2Sgwr 	if (unit >= 0) {
2285e9d01f2Sgwr 		memset(tmpbuf, 0, sizeof(tmpbuf));
2295e9d01f2Sgwr 		dlp = (union DL_primitives *) tmpbuf;
2305e9d01f2Sgwr 		dlp->dl_primitive = DL_ATTACH_REQ;
2315e9d01f2Sgwr 		dlp->attach_req.dl_ppa = unit;
2325e9d01f2Sgwr 		cbuf.buf = tmpbuf;
2335e9d01f2Sgwr 		cbuf.len = DL_ATTACH_REQ_SIZE;
2345e9d01f2Sgwr 		if (putmsg(fd, &cbuf, NULL, 0) < 0) {
2355e9d01f2Sgwr 			report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg());
2365e9d01f2Sgwr 			goto out;
2375e9d01f2Sgwr 		}
2385e9d01f2Sgwr 		/* Recv the ack. */
2395e9d01f2Sgwr 		cbuf.buf = tmpbuf;
2405e9d01f2Sgwr 		cbuf.maxlen = sizeof(tmpbuf);
2415e9d01f2Sgwr 		flags = 0;
2425e9d01f2Sgwr 		if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
2435e9d01f2Sgwr 			report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg());
2445e9d01f2Sgwr 			goto out;
2455e9d01f2Sgwr 		}
2465e9d01f2Sgwr 		/*
2475e9d01f2Sgwr 		 * Check the type, etc.
2485e9d01f2Sgwr 		 */
2495e9d01f2Sgwr 		if (dlp->dl_primitive == DL_ERROR_ACK) {
2505e9d01f2Sgwr 			report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d",
2515e9d01f2Sgwr 				   dlp->error_ack.dl_errno,
2525e9d01f2Sgwr 				   dlp->error_ack.dl_unix_errno);
2535e9d01f2Sgwr 			goto out;
2545e9d01f2Sgwr 		}
2555e9d01f2Sgwr 		if (dlp->dl_primitive != DL_OK_ACK) {
2565e9d01f2Sgwr 			report(LOG_ERR, "getether: attach: not OK or ERROR");
2575e9d01f2Sgwr 			goto out;
2585e9d01f2Sgwr 		}
2595e9d01f2Sgwr 	} /* unit >= 0 */
2605e9d01f2Sgwr #endif	/* DL_ATTACH_REQ */
2615e9d01f2Sgwr 
2625e9d01f2Sgwr 	/*
2635e9d01f2Sgwr 	 * Get the Ethernet address the same way the ARP module
2645e9d01f2Sgwr 	 * does when it is pushed onto a new stream (bind).
265*5b28f239Srillig 	 * One should instead be able just do a dl_info_req
2665e9d01f2Sgwr 	 * but many drivers do not supply the hardware address
2675e9d01f2Sgwr 	 * in the response to dl_info_req (they MUST supply it
2685e9d01f2Sgwr 	 * for dl_bind_ack because the ARP module requires it).
2695e9d01f2Sgwr 	 */
2705e9d01f2Sgwr 	memset(tmpbuf, 0, sizeof(tmpbuf));
2715e9d01f2Sgwr 	dlp = (union DL_primitives *) tmpbuf;
2725e9d01f2Sgwr 	dlp->dl_primitive = DL_BIND_REQ;
2735e9d01f2Sgwr 	dlp->bind_req.dl_sap = 0x8FF;	/* XXX - Unused SAP */
2745e9d01f2Sgwr 	cbuf.buf = tmpbuf;
2755e9d01f2Sgwr 	cbuf.len = DL_BIND_REQ_SIZE;
2765e9d01f2Sgwr 	if (putmsg(fd, &cbuf, NULL, 0) < 0) {
2775e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg());
2785e9d01f2Sgwr 		goto out;
2795e9d01f2Sgwr 	}
2805e9d01f2Sgwr 	/* Recv the ack. */
2815e9d01f2Sgwr 	cbuf.buf = tmpbuf;
2825e9d01f2Sgwr 	cbuf.maxlen = sizeof(tmpbuf);
2835e9d01f2Sgwr 	flags = 0;
2845e9d01f2Sgwr 	if (getmsg(fd, &cbuf, NULL, &flags) < 0) {
2855e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg());
2865e9d01f2Sgwr 		goto out;
2875e9d01f2Sgwr 	}
2885e9d01f2Sgwr 	/*
2895e9d01f2Sgwr 	 * Check the type, etc.
2905e9d01f2Sgwr 	 */
2915e9d01f2Sgwr 	if (dlp->dl_primitive == DL_ERROR_ACK) {
2925e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d",
2935e9d01f2Sgwr 			   dlp->error_ack.dl_errno,
2945e9d01f2Sgwr 			   dlp->error_ack.dl_unix_errno);
2955e9d01f2Sgwr 		goto out;
2965e9d01f2Sgwr 	}
2975e9d01f2Sgwr 	if (dlp->dl_primitive != DL_BIND_ACK) {
2985e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: not OK or ERROR");
2995e9d01f2Sgwr 		goto out;
3005e9d01f2Sgwr 	}
3015e9d01f2Sgwr 	if (dlp->bind_ack.dl_addr_offset == 0) {
3025e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: ack has no address");
3035e9d01f2Sgwr 		goto out;
3045e9d01f2Sgwr 	}
3055e9d01f2Sgwr 	if (dlp->bind_ack.dl_addr_length < EALEN) {
3065e9d01f2Sgwr 		report(LOG_ERR, "getether: bind: ack address truncated");
3075e9d01f2Sgwr 		goto out;
3085e9d01f2Sgwr 	}
3095e9d01f2Sgwr 	/*
3105e9d01f2Sgwr 	 * Copy the Ethernet address out of the message.
3115e9d01f2Sgwr 	 */
3125e9d01f2Sgwr 	enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset;
3135e9d01f2Sgwr 	memcpy(eap, enaddr, EALEN);
3145e9d01f2Sgwr 	rc = 0;
3155e9d01f2Sgwr 
3165e9d01f2Sgwr   out:
3175e9d01f2Sgwr 	close(fd);
3185e9d01f2Sgwr 	return rc;
3195e9d01f2Sgwr }
3205e9d01f2Sgwr 
3215e9d01f2Sgwr #define	GETETHER
3225e9d01f2Sgwr #endif /* SVR4 */
3235e9d01f2Sgwr 
3245e9d01f2Sgwr 
3255e9d01f2Sgwr #ifdef	linux
3265e9d01f2Sgwr /*
3275e9d01f2Sgwr  * This is really easy on Linux!  This version (for linux)
3285e9d01f2Sgwr  * written by Nigel Metheringham <nigelm@ohm.york.ac.uk>
3295e9d01f2Sgwr  *
3305e9d01f2Sgwr  * The code is almost identical to the Ultrix code - however
3315e9d01f2Sgwr  * the names are different to confuse the innocent :-)
3325e9d01f2Sgwr  * Most of this code was stolen from the Ultrix bit above.
3335e9d01f2Sgwr  */
3345e9d01f2Sgwr 
3355e9d01f2Sgwr #include <sys/ioctl.h>
3365e9d01f2Sgwr #include <net/if.h>	       	/* struct ifreq */
3375e9d01f2Sgwr 
3385e9d01f2Sgwr /* In a properly configured system this should be either sys/socketio.h
3395e9d01f2Sgwr    or sys/sockios.h, but on my distribution these don't line up correctly */
3405e9d01f2Sgwr #include <linux/sockios.h>	/* Needed for IOCTL defs */
3415e9d01f2Sgwr 
342131109e4Swiz getether(char *ifname, char *eap)
3435e9d01f2Sgwr {
3445e9d01f2Sgwr 	int rc = -1;
3455e9d01f2Sgwr 	int fd;
3465e9d01f2Sgwr 	struct ifreq phys;
34747a6a58fSitojun 
3485e9d01f2Sgwr 	bzero(&phys, sizeof(phys));
34947a6a58fSitojun 	strncpy(phys.ifr_name, ifname, sizeof(phys.ifr_name));
3505e9d01f2Sgwr 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
3515e9d01f2Sgwr 		report(LOG_ERR, "getether: socket(INET,DGRAM) failed");
3525e9d01f2Sgwr 		return -1;
3535e9d01f2Sgwr 	}
3545e9d01f2Sgwr 	if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) {
3555e9d01f2Sgwr 		report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed");
3565e9d01f2Sgwr 	} else {
3575e9d01f2Sgwr 		bcopy(phys.ifr_hwaddr, eap, EALEN);
3585e9d01f2Sgwr 		rc = 0;
3595e9d01f2Sgwr 	}
3605e9d01f2Sgwr 	close(fd);
3615e9d01f2Sgwr 	return rc;
3625e9d01f2Sgwr }
3635e9d01f2Sgwr 
3645e9d01f2Sgwr #define	GETETHER
3655e9d01f2Sgwr #endif	/* linux */
3665e9d01f2Sgwr 
3675e9d01f2Sgwr 
3685e9d01f2Sgwr /* If we don't know how on this system, just return an error. */
3695e9d01f2Sgwr #ifndef	GETETHER
370131109e4Swiz getether(char *ifname, char *eap)
3715e9d01f2Sgwr {
3725e9d01f2Sgwr 	return -1;
3735e9d01f2Sgwr }
3745e9d01f2Sgwr 
3755e9d01f2Sgwr #endif /* !GETETHER */
3765e9d01f2Sgwr 
3775e9d01f2Sgwr /*
3785e9d01f2Sgwr  * Local Variables:
3795e9d01f2Sgwr  * tab-width: 4
3805e9d01f2Sgwr  * c-indent-level: 4
3815e9d01f2Sgwr  * c-argdecl-indent: 4
3825e9d01f2Sgwr  * c-continued-statement-offset: 4
3835e9d01f2Sgwr  * c-continued-brace-offset: -4
3845e9d01f2Sgwr  * c-label-offset: -4
3855e9d01f2Sgwr  * c-brace-offset: 0
3865e9d01f2Sgwr  * End:
3875e9d01f2Sgwr  */
388