lpf.c 15.7 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1 2 3 4 5 6
/* lpf.c

   Linux packet filter code, contributed by Brian Murrel at Interlinx
   Support Services in Vancouver, B.C. */

/*
7
 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
8
 * Copyright (c) 1996-2003 by Internet Software Consortium
Ted Lemon's avatar
Ted Lemon committed
9
 *
10 11 12
 * 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/.
Ted Lemon's avatar
Ted Lemon committed
13
 *
14 15 16 17 18 19 20
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Ted Lemon's avatar
Ted Lemon committed
21
 *
22 23 24 25
 *   Internet Systems Consortium, Inc.
 *   950 Charter Street
 *   Redwood City, CA 94063
 *   <info@isc.org>
26
 *   https://www.isc.org/
Ted Lemon's avatar
Ted Lemon committed
27 28 29 30 31
 */

#include "dhcpd.h"
#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
#include <sys/uio.h>
32
#include <errno.h>
Ted Lemon's avatar
Ted Lemon committed
33

34 35 36
#include <asm/types.h>
#include <linux/filter.h>
#include <linux/if_ether.h>
37
#include <linux/if_packet.h>
Ted Lemon's avatar
Ted Lemon committed
38 39 40 41
#include <netinet/in_systm.h>
#include "includes/netinet/ip.h"
#include "includes/netinet/udp.h"
#include "includes/netinet/if_ether.h"
42 43 44 45
#endif

#if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
#include <sys/ioctl.h>
46
#include <sys/socket.h>
David Hankins's avatar
David Hankins committed
47
#include <net/if.h>
48
#endif
Ted Lemon's avatar
Ted Lemon committed
49

50
#if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
Ted Lemon's avatar
Ted Lemon committed
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
/* Reinitializes the specified interface after an address change.   This
   is not required for packet-filter APIs. */

#ifdef USE_LPF_SEND
void if_reinitialize_send (info)
	struct interface_info *info;
{
}
#endif

#ifdef USE_LPF_RECEIVE
void if_reinitialize_receive (info)
	struct interface_info *info;
{
}
#endif

/* Called by get_interface_list for each interface that's discovered.
   Opens a packet filter for each interface and adds it to the select
   mask. */

int if_register_lpf (info)
	struct interface_info *info;
{
	int sock;
76 77 78 79 80
	union {
		struct sockaddr_ll ll;
		struct sockaddr common;
		} sa;
	struct ifreq ifr;
Ted Lemon's avatar
Ted Lemon committed
81 82

	/* Make an LPF socket. */
83
	if ((sock = socket(PF_PACKET, SOCK_RAW,
84
			   htons((short)ETH_P_ALL))) < 0) {
85 86
		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
87 88 89 90 91 92 93 94
		    errno == EAFNOSUPPORT || errno == EINVAL) {
			log_error ("socket: %m - make sure");
			log_error ("CONFIG_PACKET (Packet socket) %s",
				   "and CONFIG_FILTER");
			log_error ("(Socket Filtering) are enabled %s",
				   "in your kernel");
			log_fatal ("configuration!");
		}
Ted Lemon's avatar
Ted Lemon committed
95
		log_fatal ("Open a socket for LPF: %m");
96
	}
Ted Lemon's avatar
Ted Lemon committed
97

98 99 100 101 102 103
	memset (&ifr, 0, sizeof ifr);
	strncpy (ifr.ifr_name, (const char *)info -> ifp, sizeof ifr.ifr_name);
	ifr.ifr_name[IFNAMSIZ-1] = '\0';
	if (ioctl (sock, SIOCGIFINDEX, &ifr))
		log_fatal ("Failed to get interface index: %m");

Ted Lemon's avatar
Ted Lemon committed
104 105
	/* Bind to the interface name */
	memset (&sa, 0, sizeof sa);
106 107 108
	sa.ll.sll_family = AF_PACKET;
	sa.ll.sll_ifindex = ifr.ifr_ifindex;
	if (bind (sock, &sa.common, sizeof sa)) {
109 110
		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
111 112 113 114 115 116 117 118
		    errno == EAFNOSUPPORT || errno == EINVAL) {
			log_error ("socket: %m - make sure");
			log_error ("CONFIG_PACKET (Packet socket) %s",
				   "and CONFIG_FILTER");
			log_error ("(Socket Filtering) are enabled %s",
				   "in your kernel");
			log_fatal ("configuration!");
		}
Ted Lemon's avatar
Ted Lemon committed
119
		log_fatal ("Bind socket to interface: %m");
120

121
	}
Ted Lemon's avatar
Ted Lemon committed
122

123 124
	get_hw_addr(info->name, &info->hw_address);

Ted Lemon's avatar
Ted Lemon committed
125 126 127 128 129 130 131 132 133 134 135
	return sock;
}
#endif /* USE_LPF_SEND || USE_LPF_RECEIVE */

#ifdef USE_LPF_SEND
void if_register_send (info)
	struct interface_info *info;
{
	/* If we're using the lpf API for sending and receiving,
	   we don't need to register this interface twice. */
#ifndef USE_LPF_RECEIVE
136
	info -> wfdesc = if_register_lpf (info);
Ted Lemon's avatar
Ted Lemon committed
137 138 139 140
#else
	info -> wfdesc = info -> rfdesc;
#endif
	if (!quiet_interface_discovery)
141
		log_info ("Sending on   LPF/%s/%s%s%s",
Ted Lemon's avatar
Ted Lemon committed
142
		      info -> name,
Ted Lemon's avatar
Ted Lemon committed
143 144 145
		      print_hw_addr (info -> hw_address.hbuf [0],
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
146
		      (info -> shared_network ? "/" : ""),
Ted Lemon's avatar
Ted Lemon committed
147
		      (info -> shared_network ?
148
		       info -> shared_network -> name : ""));
Ted Lemon's avatar
Ted Lemon committed
149
}
150 151 152 153 154 155 156 157 158 159 160

void if_deregister_send (info)
	struct interface_info *info;
{
	/* don't need to close twice if we are using lpf for sending and
	   receiving */
#ifndef USE_LPF_RECEIVE
	/* for LPF this is simple, packet filters are removed when sockets
	   are closed */
	close (info -> wfdesc);
#endif
161
	info -> wfdesc = -1;
162
	if (!quiet_interface_discovery)
163
		log_info ("Disabling output on LPF/%s/%s%s%s",
164 165 166 167 168 169 170 171
		      info -> name,
		      print_hw_addr (info -> hw_address.hbuf [0],
				     info -> hw_address.hlen - 1,
				     &info -> hw_address.hbuf [1]),
		      (info -> shared_network ? "/" : ""),
		      (info -> shared_network ?
		       info -> shared_network -> name : ""));
}
Ted Lemon's avatar
Ted Lemon committed
172 173 174 175 176
#endif /* USE_LPF_SEND */

#ifdef USE_LPF_RECEIVE
/* Defined in bpf.c.   We can't extern these in dhcpd.h without pulling
   in bpf includes... */
177
extern struct sock_filter dhcp_bpf_filter [];
Ted Lemon's avatar
Ted Lemon committed
178
extern int dhcp_bpf_filter_len;
179

Francis Dupont's avatar
Francis Dupont committed
180 181 182 183 184
#if defined(RELAY_PORT)
extern struct sock_filter dhcp_bpf_relay_filter [];
extern int dhcp_bpf_relay_filter_len;
#endif

185
#if defined (HAVE_TR_SUPPORT)
Ted Lemon's avatar
Ted Lemon committed
186 187
extern struct sock_filter dhcp_bpf_tr_filter [];
extern int dhcp_bpf_tr_filter_len;
188 189
static void lpf_tr_filter_setup (struct interface_info *);
#endif
Ted Lemon's avatar
Ted Lemon committed
190

Ted Lemon's avatar
Ted Lemon committed
191 192
static void lpf_gen_filter_setup (struct interface_info *);

Ted Lemon's avatar
Ted Lemon committed
193 194 195 196 197 198
void if_register_receive (info)
	struct interface_info *info;
{
	/* Open a LPF device and hang it on this interface... */
	info -> rfdesc = if_register_lpf (info);

199 200 201 202 203 204 205 206 207 208 209 210 211 212
#ifdef PACKET_AUXDATA
	{
	int val = 1;

	if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA,
		       &val, sizeof(val)) < 0) {
		if (errno != ENOPROTOOPT) {
			log_fatal ("Failed to set auxiliary packet data: %m");
		}
	}
	}
#endif


213
#if defined (HAVE_TR_SUPPORT)
Ted Lemon's avatar
Ted Lemon committed
214
	if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
Ted Lemon's avatar
Ted Lemon committed
215 216
		lpf_tr_filter_setup (info);
	else
217
#endif
Ted Lemon's avatar
Ted Lemon committed
218 219 220
		lpf_gen_filter_setup (info);

	if (!quiet_interface_discovery)
Ted Lemon's avatar
Ted Lemon committed
221 222
		log_info ("Listening on LPF/%s/%s%s%s",
			  info -> name,
Ted Lemon's avatar
Ted Lemon committed
223 224 225
			  print_hw_addr (info -> hw_address.hbuf [0],
					 info -> hw_address.hlen - 1,
					 &info -> hw_address.hbuf [1]),
Ted Lemon's avatar
Ted Lemon committed
226 227 228
			  (info -> shared_network ? "/" : ""),
			  (info -> shared_network ?
			   info -> shared_network -> name : ""));
Ted Lemon's avatar
Ted Lemon committed
229 230
}

231 232 233 234 235 236
void if_deregister_receive (info)
	struct interface_info *info;
{
	/* for LPF this is simple, packet filters are removed when sockets
	   are closed */
	close (info -> rfdesc);
237
	info -> rfdesc = -1;
238
	if (!quiet_interface_discovery)
239
		log_info ("Disabling input on LPF/%s/%s%s%s",
240 241 242 243 244 245 246 247 248
			  info -> name,
			  print_hw_addr (info -> hw_address.hbuf [0],
					 info -> hw_address.hlen - 1,
					 &info -> hw_address.hbuf [1]),
			  (info -> shared_network ? "/" : ""),
			  (info -> shared_network ?
			   info -> shared_network -> name : ""));
}

Ted Lemon's avatar
Ted Lemon committed
249 250 251 252 253
static void lpf_gen_filter_setup (info)
	struct interface_info *info;
{
	struct sock_fprog p;

254 255
	memset(&p, 0, sizeof(p));

Ted Lemon's avatar
Ted Lemon committed
256 257
	/* Set up the bpf filter program structure.    This is defined in
	   bpf.c */
258 259
	p.len = dhcp_bpf_filter_len;
	p.filter = dhcp_bpf_filter;
Ted Lemon's avatar
Ted Lemon committed
260 261 262 263

        /* Patch the server port into the LPF  program...
	   XXX changes to filter program may require changes
	   to the insn number(s) used below! XXX */
Francis Dupont's avatar
Francis Dupont committed
264 265 266 267 268 269 270 271 272 273 274 275 276
#if defined(RELAY_PORT)
	if (relay_port) {
		/*
		 * If user defined relay UDP port, we need to filter
		 * also on the user UDP port.
		 */
		p.len = dhcp_bpf_relay_filter_len;
		p.filter = dhcp_bpf_relay_filter;

		dhcp_bpf_relay_filter [10].k = ntohs (relay_port);
	}
#endif
	dhcp_bpf_filter [8].k = ntohs (local_port);
Ted Lemon's avatar
Ted Lemon committed
277 278

	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
279 280 281
			sizeof p) < 0) {
		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
282 283 284 285 286 287 288 289
		    errno == EAFNOSUPPORT) {
			log_error ("socket: %m - make sure");
			log_error ("CONFIG_PACKET (Packet socket) %s",
				   "and CONFIG_FILTER");
			log_error ("(Socket Filtering) are enabled %s",
				   "in your kernel");
			log_fatal ("configuration!");
		}
Ted Lemon's avatar
Ted Lemon committed
290
		log_fatal ("Can't install packet filter program: %m");
Ted Lemon's avatar
Ted Lemon committed
291 292 293
	}
}

294
#if defined (HAVE_TR_SUPPORT)
Ted Lemon's avatar
Ted Lemon committed
295 296 297 298 299
static void lpf_tr_filter_setup (info)
	struct interface_info *info;
{
	struct sock_fprog p;

300 301
	memset(&p, 0, sizeof(p));

Ted Lemon's avatar
Ted Lemon committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
	/* Set up the bpf filter program structure.    This is defined in
	   bpf.c */
	p.len = dhcp_bpf_tr_filter_len;
	p.filter = dhcp_bpf_tr_filter;

        /* Patch the server port into the LPF  program...
	   XXX changes to filter program may require changes
	   XXX to the insn number(s) used below!
	   XXX Token ring filter is null - when/if we have a filter 
	   XXX that's not, we'll need this code.
	   XXX dhcp_bpf_filter [?].k = ntohs (local_port); */

	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
			sizeof p) < 0) {
		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
318 319 320 321 322 323 324 325
		    errno == EAFNOSUPPORT) {
			log_error ("socket: %m - make sure");
			log_error ("CONFIG_PACKET (Packet socket) %s",
				   "and CONFIG_FILTER");
			log_error ("(Socket Filtering) are enabled %s",
				   "in your kernel");
			log_fatal ("configuration!");
		}
Ted Lemon's avatar
Ted Lemon committed
326
		log_fatal ("Can't install packet filter program: %m");
327
	}
Ted Lemon's avatar
Ted Lemon committed
328
}
329
#endif /* HAVE_TR_SUPPORT */
Ted Lemon's avatar
Ted Lemon committed
330 331 332 333 334 335 336 337 338 339 340 341
#endif /* USE_LPF_RECEIVE */

#ifdef USE_LPF_SEND
ssize_t send_packet (interface, packet, raw, len, from, to, hto)
	struct interface_info *interface;
	struct packet *packet;
	struct dhcp_packet *raw;
	size_t len;
	struct in_addr from;
	struct sockaddr_in *to;
	struct hardware *hto;
{
342 343 344 345
	unsigned hbufp = 0, ibufp = 0;
	double hh [16];
	double ih [1536 / sizeof (double)];
	unsigned char *buf = (unsigned char *)ih;
346
	int result;
347
	int fudge;
Ted Lemon's avatar
Ted Lemon committed
348 349 350 351 352

	if (!strcmp (interface -> name, "fallback"))
		return send_fallback (interface, packet, raw,
				      len, from, to, hto);

353 354 355
	if (hto == NULL && interface->anycast_mac_addr.hlen)
		hto = &interface->anycast_mac_addr;

Ted Lemon's avatar
Ted Lemon committed
356
	/* Assemble the headers... */
357 358 359 360 361
	assemble_hw_header (interface, (unsigned char *)hh, &hbufp, hto);
	fudge = hbufp % 4;	/* IP header must be word-aligned. */
	memcpy (buf + fudge, (unsigned char *)hh, hbufp);
	ibufp = hbufp + fudge;
	assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
Ted Lemon's avatar
Ted Lemon committed
362 363
				to -> sin_addr.s_addr, to -> sin_port,
				(unsigned char *)raw, len);
364
	memcpy (buf + ibufp, raw, len);
365
	result = write(interface->wfdesc, buf + fudge, ibufp + len - fudge);
366
	if (result < 0)
Ted Lemon's avatar
Ted Lemon committed
367
		log_error ("send_packet: %m");
368
	return result;
Ted Lemon's avatar
Ted Lemon committed
369 370 371 372 373 374 375 376 377 378 379 380 381
}
#endif /* USE_LPF_SEND */

#ifdef USE_LPF_RECEIVE
ssize_t receive_packet (interface, buf, len, from, hfrom)
	struct interface_info *interface;
	unsigned char *buf;
	size_t len;
	struct sockaddr_in *from;
	struct hardware *hfrom;
{
	int length = 0;
	int offset = 0;
382
	int csum_ready = 1;
383
	unsigned char ibuf [1536];
384
	unsigned bufix = 0;
385
	unsigned paylen;
386 387 388 389
	struct iovec iov = {
		.iov_base = ibuf,
		.iov_len = sizeof ibuf,
	};
390 391 392 393 394 395
#ifdef PACKET_AUXDATA
	/*
	 * We only need cmsgbuf if we are getting the aux data and we
	 * only get the auxdata if it is actually defined
	 */
	unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
396 397 398 399 400 401
	struct msghdr msg = {
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = cmsgbuf,
		.msg_controllen = sizeof(cmsgbuf),
	};
402 403 404 405 406 407 408 409
#else
	struct msghdr msg = {
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = NULL,
		.msg_controllen = 0,
	};
#endif /* PACKET_AUXDATA */
410 411

	length = recvmsg (interface->rfdesc, &msg, 0);
Ted Lemon's avatar
Ted Lemon committed
412 413 414
	if (length <= 0)
		return length;

415 416
#ifdef PACKET_AUXDATA
	{
417 418 419 420 421 422 423 424 425 426 427 428
	/*  Use auxiliary packet data to:
	 *
	 *  a. Weed out extraneous VLAN-tagged packets - If the NIC driver is
	 *  handling VLAN encapsulation (i.e. stripping/adding VLAN tags),
	 *  then an inbound VLAN packet will be seen twice: Once by
	 *  the parent interface (e.g. eth0) with a VLAN tag != 0; and once
	 *  by the vlan interface (e.g. eth0.n) with a VLAN tag of 0 (i.e none).
	 *  We want to discard the packet sent to the parent and thus respond
	 *  only over the vlan interface.  (Drivers for Intel PRO/1000 series
	 *  NICs perform VLAN encapsulation, while drivers for PCnet series
	 *  do not, for example. The linux kernel makes stripped vlan info
	 *  visible to user space via CMSG/auxdata, this appears to not be
429 430
	 *  true for BSD OSs.).  NOTE: this is only supported on linux flavors
	 *  which define the tpacket_auxdata.tp_vlan_tci.
431 432 433
	 *
	 *  b. Determine if checksum is valid for use. It may not be if
	 *  checksum offloading is enabled on the interface.  */
434 435 436 437 438 439
	struct cmsghdr *cmsg;

	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
		if (cmsg->cmsg_level == SOL_PACKET &&
		    cmsg->cmsg_type == PACKET_AUXDATA) {
			struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
440
#ifdef VLAN_TCI_PRESENT
441 442 443
			/* Discard packets with stripped vlan id */
			/* VLAN ID is only bottom 12-bits of TCI */
			if (aux->tp_vlan_tci & 0x0fff)
444
				return 0;
445
#endif
446

447 448 449 450 451 452
			csum_ready = ((aux->tp_status & TP_STATUS_CSUMNOTREADY)
				      ? 0 : 1);
		}
	}

	}
453
#endif /* PACKET_AUXDATA */
454

455
	bufix = 0;
Ted Lemon's avatar
Ted Lemon committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469
	/* Decode the physical header... */
	offset = decode_hw_header (interface, ibuf, bufix, hfrom);

	/* If a physical layer checksum failed (dunno of any
	   physical layer that supports this, but WTH), skip this
	   packet. */
	if (offset < 0) {
		return 0;
	}

	bufix += offset;
	length -= offset;

	/* Decode the IP and UDP headers... */
470
	offset = decode_udp_ip_header (interface, ibuf, bufix, from,
471
				       (unsigned)length, &paylen, csum_ready);
Ted Lemon's avatar
Ted Lemon committed
472 473 474 475 476 477 478 479

	/* If the IP or UDP checksum was bad, skip the packet... */
	if (offset < 0)
		return 0;

	bufix += offset;
	length -= offset;

480 481 482
	if (length < paylen)
		log_fatal("Internal inconsistency at %s:%d.", MDL);

Ted Lemon's avatar
Ted Lemon committed
483
	/* Copy out the data in the packet... */
484 485
	memcpy(buf, &ibuf[bufix], paylen);
	return paylen;
Ted Lemon's avatar
Ted Lemon committed
486 487
}

Ted Lemon's avatar
Ted Lemon committed
488 489
int can_unicast_without_arp (ip)
	struct interface_info *ip;
Ted Lemon's avatar
Ted Lemon committed
490 491 492 493
{
	return 1;
}

Ted Lemon's avatar
Ted Lemon committed
494 495
int can_receive_unicast_unconfigured (ip)
	struct interface_info *ip;
496 497 498 499
{
	return 1;
}

500 501 502 503 504 505
int supports_multiple_interfaces (ip)
	struct interface_info *ip;
{
	return 1;
}

Ted Lemon's avatar
Ted Lemon committed
506 507
void maybe_setup_fallback ()
{
Ted Lemon's avatar
Ted Lemon committed
508
	isc_result_t status;
509 510
	struct interface_info *fbi = (struct interface_info *)0;
	if (setup_fallback (&fbi, MDL)) {
Ted Lemon's avatar
Ted Lemon committed
511
		if_register_fallback (fbi);
512
		status = omapi_register_io_object ((omapi_object_t *)fbi,
Ted Lemon's avatar
Ted Lemon committed
513 514 515
						   if_readsocket, 0,
						   fallback_discard, 0, 0);
		if (status != ISC_R_SUCCESS)
David Hankins's avatar
David Hankins committed
516
			log_fatal ("Can't register I/O handle for \"%s\": %s",
Ted Lemon's avatar
Ted Lemon committed
517
				   fbi -> name, isc_result_totext (status));
518
		interface_dereference (&fbi, MDL);
Ted Lemon's avatar
Ted Lemon committed
519 520
	}
}
521
#endif
David Hankins's avatar
David Hankins committed
522

523
#if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
David Hankins's avatar
David Hankins committed
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
void
get_hw_addr(const char *name, struct hardware *hw) {
	int sock;
	struct ifreq tmp;
	struct sockaddr *sa;

	if (strlen(name) >= sizeof(tmp.ifr_name)) {
		log_fatal("Device name too long: \"%s\"", name);
	}

	sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		log_fatal("Can't create socket for \"%s\": %m", name);
	}

	memset(&tmp, 0, sizeof(tmp));
	strcpy(tmp.ifr_name, name);
	if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) {
		log_fatal("Error getting hardware address for \"%s\": %m", 
			  name);
	}

	sa = &tmp.ifr_hwaddr;
	switch (sa->sa_family) {
		case ARPHRD_ETHER:
			hw->hlen = 7;
			hw->hbuf[0] = HTYPE_ETHER;
			memcpy(&hw->hbuf[1], sa->sa_data, 6);
			break;
		case ARPHRD_IEEE802:
554
#ifdef ARPHRD_IEEE802_TR
David Hankins's avatar
David Hankins committed
555
		case ARPHRD_IEEE802_TR:
556
#endif /* ARPHRD_IEEE802_TR */
David Hankins's avatar
David Hankins committed
557 558 559 560 561
			hw->hlen = 7;
			hw->hbuf[0] = HTYPE_IEEE802;
			memcpy(&hw->hbuf[1], sa->sa_data, 6);
			break;
		case ARPHRD_FDDI:
Shawn Routhier's avatar
Shawn Routhier committed
562
			hw->hlen = 7;
David Hankins's avatar
David Hankins committed
563
			hw->hbuf[0] = HTYPE_FDDI;
Shawn Routhier's avatar
Shawn Routhier committed
564
			memcpy(&hw->hbuf[1], sa->sa_data, 6);
David Hankins's avatar
David Hankins committed
565 566 567
			break;
		default:
			log_fatal("Unsupported device type %ld for \"%s\"",
568
				  (long int)sa->sa_family, name);
David Hankins's avatar
David Hankins committed
569 570 571 572
	}

	close(sock);
}
Ted Lemon's avatar
Ted Lemon committed
573
#endif