File 0016-infiniband-support.patch of Package dhcp.24175

References: bnc#870535,bsc#909189,bsc#910984

From 92250da05c2e4e3f1f60d627b52fe3f4f14828d3 Mon Sep 17 00:00:00 2001
From: Marius Tomaschewski <mt@suse.de>
Date: Mon, 14 Sep 2015 13:42:34 +0200
Subject: [PATCH] infiniband-support

Squashed rework based on the following commits:

commit 8f5918fa4319fc8173ca6e75d6e2ab8c379e980d
Author: Marius Tomaschewski <mt@suse.de>
Date:   Mon Sep 14 12:26:12 2015 +0200

    dhcp 4.3.3 ip over ib support fixes (bsc#910984)

    - verify client-identifier in responses as defined by RFC6864
    - generate RFC4361 client-identifier for infiniband as required by
      RFC4390; config file can still override it using send statement.
    - fixed to set the ib hwaddr and guard copying/compares to chaddr;
      it is used for client-identifier, improved xid and other things.
    - fixed lpf send_packet to not break unicast on fallback socket.

commit 3173fd96fe8873f7924f8b5c3f2a6a34ebe1c90c
Author: root <root@reg-ovm-036-004.lab.mtl.com>
Date:   Tue Mar 25 23:30:18 2014 +0200

    dhcp 4.3.2 improved xid

    References: bnc#870535

commit 7e27105baa2a86469efa17fe70667baa8bcc627f
Author: Jiri Slaby <jslaby@suse.cz>
Date:   Wed Dec 10 13:48:03 2014 +0100

    dhcp-4.2.x-handle-ifa_addr-NULL.909189

    Fix to not crash in interface discovery when the interface
    address is NULL. Bug has been introduced by the infiniband
    support patch (bsc#870535).

    References: bsc#909189,bnc#870535

commit 7818a340f84c0fe3c750baa721fb4221e2e2d443
Author: root <root@reg-ovm-036-004.lab.mtl.com>
Date:   Tue Mar 25 23:15:58 2014 +0200

    dhcp 4.3.2 lpf ip over ib support

    References: bnc#870535

diff --git a/client/dhclient.c b/client/dhclient.c
index 0c77ae2..b438629 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -104,6 +104,40 @@ static int check_option_values(struct universe *universe, unsigned int opt,
 static void dhclient_ddns_cb_free(dhcp_ddns_cb_t *ddns_cb,
                                    char* file, int line);
 
+static void form_interface_duid(struct data_string *duid, struct interface_info *ip, const char *file, int line);
+
+static int  check_client_identifier(struct option_state *packet_options, struct option_state *client_options)
+{
+	struct option_cache *client_oc;
+	struct option_cache *packet_oc;
+
+	/* RFC6864 check:
+	 *
+	 *   When a client receives a DHCP message containing a 'client
+	 *   identifier' option, the client MUST compare that client
+	 *   identifier to the one it is configured to send.
+	 *   If the two client identifiers do not match, the client MUST
+	 *   silently discard the message.
+	 */
+
+	/* no identifier received: not RFC6864 server ... */
+	if (!packet_options || !(packet_oc = lookup_option(&dhcp_universe, packet_options,
+							DHO_DHCP_CLIENT_IDENTIFIER)))
+		return 1;
+
+	/* client does not remember them in some cases ... */
+	if (!client_options || !(client_oc = lookup_option(&dhcp_universe, client_options,
+							DHO_DHCP_CLIENT_IDENTIFIER)))
+		return 1;
+
+	/* when we've sent one, the identifiers MUST match */
+	if (client_oc->data.len != packet_oc->data.len ||
+	    memcmp(client_oc->data.data, packet_oc->data.data, client_oc->data.len))
+		return -1;
+
+	return 0;
+}
+
 #ifndef UNIT_TEST
 int
 main(int argc, char **argv) {
@@ -635,6 +669,26 @@ main(int argc, char **argv) {
 		}
 	}
 
+	/* We create a backup seed before rediscovering interfaces in order to
+	   have a seed built using all of the available interfaces
+	   It's interesting if required interfaces doesn't let us defined
+	   a really unique seed due to a lack of valid HW addr later
+	   (this is the case with DHCP over IB)
+	   We only use the last device as using a sum could broke the
+	   uniqueness of the seed among multiple nodes
+	 */
+	unsigned backup_seed = 0;
+	for (ip = interfaces; ip; ip = ip -> next) {
+		int junk;
+		if ( ip -> hw_address.hlen <= sizeof seed )
+		  continue;
+		memcpy (&junk,
+			&ip -> hw_address.hbuf [ip -> hw_address.hlen -
+						sizeof seed], sizeof seed);
+		backup_seed = junk;
+	}
+
+
 	/* At this point, all the interfaces that the script thinks
 	   are relevant should be running, so now we once again call
 	   discover_interfaces(), and this time ask it to actually set
@@ -649,19 +703,42 @@ main(int argc, char **argv) {
 	   Not much entropy, but we're booting, so we're not likely to
 	   find anything better. */
 	seed = 0;
+	int seed_flag = 0;
 	for (ip = interfaces; ip; ip = ip->next) {
 		int junk;
+		if (ip->hw_address.hlen <= sizeof seed)
+			continue;
 		memcpy(&junk,
 		       &ip->hw_address.hbuf[ip->hw_address.hlen -
 					    sizeof seed], sizeof seed);
 		seed += junk;
+		seed_flag = 1;
 	}
-	srandom(seed + cur_time + (unsigned)getpid());
+	if ( seed_flag == 0 ) {
+		if ( backup_seed != 0 ) {
+			seed = backup_seed;
+			log_info ("xid: rand init seed (0x%x) built using all"
+				  " available interfaces",seed);
+		}
+		else {
+			seed = cur_time^((unsigned) gethostid()) ;
+			log_info ("xid: warning: no netdev with useable HWADDR found"
+				  " for seed's uniqueness enforcement");
+			log_info ("xid: rand init seed (0x%x) built using gethostid",
+				  seed);
+                }
+                /* we only use seed and no current time as a broadcast reply */
+                /* will certainly be used by the hwaddrless interface */
+                srandom(seed);
+        }
+        else
+		srandom(seed + cur_time + (unsigned)getpid());
 
 
 	/*
 	 * Establish a default DUID.  We always do so for v6 and
-	 * do so if desired for v4 via the -D or -i options
+	 * do so if desired for v4 via the -D or -i options or
+	 * when an infiniband interface is involved.
 	 */
 	if ((local_family == AF_INET6) ||
 	    ((local_family == AF_INET) && (duid_v4 == 1))) {
@@ -672,6 +749,20 @@ main(int argc, char **argv) {
 			form_duid(&default_duid, MDL);
 			write_duid(&default_duid);
 		}
+	} else {
+	    for (ip = interfaces; ip; ip = ip->next) {
+		    if (!ip->client || ip->hw_address.hbuf[0] != HTYPE_INFINIBAND)
+			    continue;
+
+		    if (default_duid.len == 0) {
+			    if (default_duid.buffer != NULL)
+				    data_string_forget(&default_duid, MDL);
+
+			    form_duid(&default_duid, MDL);
+			    write_duid(&default_duid);
+			    break;
+		    }
+	    }
 	}
 
 	/* Start a configuration state machine for each interface. */
@@ -1143,15 +1234,22 @@ void dhcpack (packet)
 		if (client -> xid == packet -> raw -> xid)
 			break;
 	}
-	if (!client ||
-	    (packet -> interface -> hw_address.hlen - 1 !=
-	     packet -> raw -> hlen) ||
-	    (memcmp (&packet -> interface -> hw_address.hbuf [1],
-		     packet -> raw -> chaddr, packet -> raw -> hlen))) {
+
+	if (!client || (packet -> raw -> hlen &&
+	    ((packet -> interface -> hw_address.hlen - 1 !=
+	      packet -> raw -> hlen) ||
+	     (memcmp (&packet -> interface -> hw_address.hbuf [1],
+		      packet -> raw -> chaddr, packet -> raw -> hlen))))) {
 #if defined (DEBUG)
 		log_debug ("DHCPACK in wrong transaction.");
 #endif
 		return;
+	} else
+	if (check_client_identifier(packet->options, client->sent_options) < 0) {
+#if defined (DEBUG)
+		log_debug ("response client identifier does not match ours");
+#endif
+		return;
 	}
 
 	if (client -> state != S_REBOOTING &&
@@ -1164,7 +1262,7 @@ void dhcpack (packet)
 		return;
 	}
 
-	log_info ("DHCPACK from %s", piaddr (packet -> client_addr));
+	log_info ("DHCPACK from %s (xid=0x%x)", piaddr (packet -> client_addr), client -> xid);
 
 	lease = packet_to_lease (packet, client);
 	if (!lease) {
@@ -1629,15 +1727,21 @@ void dhcpoffer (packet)
 	/* If we're not receptive to an offer right now, or if the offer
 	   has an unrecognizable transaction id, then just drop it. */
 	if (!client ||
-	    client -> state != S_SELECTING ||
-	    (packet -> interface -> hw_address.hlen - 1 !=
-	     packet -> raw -> hlen) ||
-	    (memcmp (&packet -> interface -> hw_address.hbuf [1],
-		     packet -> raw -> chaddr, packet -> raw -> hlen))) {
+	    client -> state != S_SELECTING || (packet -> raw -> hlen &&
+	    ((packet -> interface -> hw_address.hlen - 1 !=
+	      packet -> raw -> hlen) ||
+	     (memcmp (&packet -> interface -> hw_address.hbuf [1],
+		      packet -> raw -> chaddr, packet -> raw -> hlen))))) {
 #if defined (DEBUG)
 		log_debug ("%s in wrong transaction.", name);
 #endif
 		return;
+	} else
+	if (check_client_identifier(packet->options, client->sent_options) < 0) {
+#if defined (DEBUG)
+		log_debug ("response client identifier does not match ours");
+#endif
+		return;
 	}
 
 	sprintf (obuf, "%s from %s", name, piaddr (packet -> client_addr));
@@ -1875,11 +1979,11 @@ void dhcpnak (packet)
 
 	/* If we're not receptive to an offer right now, or if the offer
 	   has an unrecognizable transaction id, then just drop it. */
-	if (!client ||
-	    (packet -> interface -> hw_address.hlen - 1 !=
-	     packet -> raw -> hlen) ||
-	    (memcmp (&packet -> interface -> hw_address.hbuf [1],
-		     packet -> raw -> chaddr, packet -> raw -> hlen))) {
+	if (!client || (packet -> raw -> hlen &&
+	    ((packet -> interface -> hw_address.hlen - 1 !=
+	      packet -> raw -> hlen) ||
+	     (memcmp (&packet -> interface -> hw_address.hbuf [1],
+		      packet -> raw -> chaddr, packet -> raw -> hlen))))) {
 #if defined (DEBUG)
 		log_debug ("DHCPNAK in wrong transaction.");
 #endif
@@ -1894,9 +1998,15 @@ void dhcpnak (packet)
 		log_debug ("DHCPNAK in wrong state.");
 #endif
 		return;
+	} else
+	if (check_client_identifier(packet->options, client->sent_options) < 0) {
+#if defined (DEBUG)
+		log_debug ("response client identifier does not match ours");
+#endif
+		return;
 	}
 
-	log_info ("DHCPNAK from %s", piaddr (packet -> client_addr));
+	log_info ("DHCPNAK from %s (xid=0x%x)", piaddr (packet -> client_addr), client -> xid);
 
 	if (!client -> active) {
 #if defined (DEBUG)
@@ -2023,10 +2133,10 @@ void send_discover (cpp)
 		client -> packet.secs = htons (65535);
 	client -> secs = client -> packet.secs;
 
-	log_info ("DHCPDISCOVER on %s to %s port %d interval %ld",
-	      client -> name ? client -> name : client -> interface -> name,
-	      inet_ntoa (sockaddr_broadcast.sin_addr),
-	      ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval));
+	log_info ("DHCPDISCOVER on %s to %s port %d interval %ld (xid=0x%x)",
+		client -> name ? client -> name : client -> interface -> name,
+		inet_ntoa (sockaddr_broadcast.sin_addr),
+		ntohs (sockaddr_broadcast.sin_port), (long)(client -> interval), client -> xid);
 
 	/* Send out a packet. */
 	result = send_packet(client->interface, NULL, &client->packet,
@@ -2295,10 +2405,10 @@ void send_request (cpp)
 			client -> packet.secs = htons (65535);
 	}
 
-	log_info ("DHCPREQUEST on %s to %s port %d",
+	log_info ("DHCPREQUEST on %s to %s port %d (xid=0x%x)",
 	      client -> name ? client -> name : client -> interface -> name,
 	      inet_ntoa (destination.sin_addr),
-	      ntohs (destination.sin_port));
+	      ntohs (destination.sin_port), client -> xid);
 
 	if (destination.sin_addr.s_addr != INADDR_BROADCAST &&
 	    fallback_interface) {
@@ -2338,10 +2448,10 @@ void send_decline (cpp)
 
 	int result;
 
-	log_info ("DHCPDECLINE on %s to %s port %d",
+	log_info ("DHCPDECLINE on %s to %s port %d (xid=0x%x)",
 	      client->name ? client->name : client->interface->name,
 	      inet_ntoa(sockaddr_broadcast.sin_addr),
-	      ntohs(sockaddr_broadcast.sin_port));
+	      ntohs (sockaddr_broadcast.sin_port), client -> xid);
 
 	/* Send out a packet. */
 	result = send_packet(client->interface, NULL, &client->packet,
@@ -2384,10 +2494,10 @@ void send_release (cpp)
 		return;
 	}
 
-	log_info ("DHCPRELEASE on %s to %s port %d",
+	log_info ("DHCPRELEASE on %s to %s port %d (xid=0x%x)",
 	      client -> name ? client -> name : client -> interface -> name,
 	      inet_ntoa (destination.sin_addr),
-	      ntohs (destination.sin_port));
+	      ntohs (destination.sin_port), client -> xid);
 
 	if (fallback_interface) {
 		result = send_packet(fallback_interface, NULL, &client->packet,
@@ -2507,10 +2617,17 @@ make_client_options(struct client_state *client, struct client_lease *lease,
 	 * This can be overridden by including a client id in the configuration
 	 * file.
 	 */
- 	if (duid_v4 == 1) {
+ 	if (duid_v4 == 1 || client->interface->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
 		struct data_string client_identifier;
 		int hw_idx, hw_len;
 
+		if (default_duid.len == 0) {
+			if (default_duid.buffer != NULL)
+				data_string_forget(&default_duid, MDL);
+
+			form_interface_duid(&default_duid, client->interface, MDL);
+		}
+
 		memset(&client_identifier, 0, sizeof(client_identifier));
 		client_identifier.len = 1 + 4 + default_duid.len;
 		if (!buffer_allocate(&client_identifier.buffer,
@@ -2600,12 +2717,13 @@ void make_discover (client, lease)
 
 	client -> packet.op = BOOTREQUEST;
 	client -> packet.htype = client -> interface -> hw_address.hbuf [0];
-	client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+	if (client -> interface -> hw_address.hlen - 1 <= sizeof(client -> packet.chaddr))
+	    client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
 	client -> packet.hops = 0;
 	client -> packet.xid = random ();
 	client -> packet.secs = 0; /* filled in by send_discover. */
 
-	if (can_receive_unicast_unconfigured (client -> interface))
+	if (can_receive_unicast_unconfigured (client -> interface) && client -> packet.hlen)
 		client -> packet.flags = 0;
 	else
 		client -> packet.flags = htons (BOOTP_BROADCAST);
@@ -2617,7 +2735,7 @@ void make_discover (client, lease)
 	memset (&(client -> packet.siaddr),
 		0, sizeof client -> packet.siaddr);
 	client -> packet.giaddr = giaddr;
-	if (client -> interface -> hw_address.hlen > 0)
+	if (client -> interface -> hw_address.hlen > 1 && client -> packet.hlen)
 	    memcpy (client -> packet.chaddr,
 		    &client -> interface -> hw_address.hbuf [1],
 		    (unsigned)(client -> interface -> hw_address.hlen - 1));
@@ -2673,7 +2791,8 @@ void make_request (client, lease)
 
 	client -> packet.op = BOOTREQUEST;
 	client -> packet.htype = client -> interface -> hw_address.hbuf [0];
-	client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+	if (client -> interface -> hw_address.hlen - 1 <= sizeof(client -> packet.chaddr))
+	    client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
 	client -> packet.hops = 0;
 	client -> packet.xid = client -> xid;
 	client -> packet.secs = 0; /* Filled in by send_request. */
@@ -2705,7 +2824,7 @@ void make_request (client, lease)
 	else
 		memset (&client -> packet.giaddr, 0,
 			sizeof client -> packet.giaddr);
-	if (client -> interface -> hw_address.hlen > 0)
+	if (client -> interface -> hw_address.hlen > 1 && client -> packet.hlen)
 	    memcpy (client -> packet.chaddr,
 		    &client -> interface -> hw_address.hbuf [1],
 		    (unsigned)(client -> interface -> hw_address.hlen - 1));
@@ -2747,7 +2866,8 @@ void make_decline (client, lease)
 
 	client -> packet.op = BOOTREQUEST;
 	client -> packet.htype = client -> interface -> hw_address.hbuf [0];
-	client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+	if (client -> interface -> hw_address.hlen - 1 <= sizeof(client -> packet.chaddr))
+		client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
 	client -> packet.hops = 0;
 	client -> packet.xid = client -> xid;
 	client -> packet.secs = 0; /* Filled in by send_request. */
@@ -2764,9 +2884,10 @@ void make_decline (client, lease)
 	memset (&client -> packet.siaddr, 0,
 		sizeof client -> packet.siaddr);
 	client -> packet.giaddr = giaddr;
-	memcpy (client -> packet.chaddr,
-		&client -> interface -> hw_address.hbuf [1],
-		client -> interface -> hw_address.hlen);
+	if (client -> interface -> hw_address.hlen > 1 && client -> packet.hlen)
+	    memcpy (client -> packet.chaddr,
+		    &client -> interface -> hw_address.hbuf [1],
+		    client -> interface -> hw_address.hlen - 1);
 
 #ifdef DEBUG_PACKET
 	dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
@@ -2808,7 +2929,8 @@ void make_release (client, lease)
 
 	client -> packet.op = BOOTREQUEST;
 	client -> packet.htype = client -> interface -> hw_address.hbuf [0];
-	client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
+	if (client -> interface -> hw_address.hlen - 1 <= sizeof(client -> packet.chaddr))
+	    client -> packet.hlen = client -> interface -> hw_address.hlen - 1;
 	client -> packet.hops = 0;
 	client -> packet.xid = random ();
 	client -> packet.secs = 0;
@@ -2820,9 +2942,10 @@ void make_release (client, lease)
 	memset (&client -> packet.siaddr, 0,
 		sizeof client -> packet.siaddr);
 	client -> packet.giaddr = giaddr;
-	memcpy (client -> packet.chaddr,
-		&client -> interface -> hw_address.hbuf [1],
-		client -> interface -> hw_address.hlen);
+	if (client -> interface -> hw_address.hlen > 1 && client -> packet.hlen)
+	    memcpy (client -> packet.chaddr,
+		    &client -> interface -> hw_address.hbuf [1],
+		    client -> interface -> hw_address.hlen);
 
 #ifdef DEBUG_PACKET
 	dump_raw ((unsigned char *)&client -> packet, client -> packet_length);
@@ -2975,17 +3098,13 @@ write_options(struct client_state *client, struct option_state *options,
  * is not how it is intended.  Upcoming rearchitecting the client should
  * address this "one daemon model."
  */
-void
-form_duid(struct data_string *duid, const char *file, int line)
+static void
+form_interface_duid(struct data_string *duid, struct interface_info *ip, const char *file, int line)
 {
-	struct interface_info *ip;
 	int len;
 	char *str;
 
-	/* For now, just use the first interface on the list. */
-	ip = interfaces;
-
-	if (ip == NULL)
+	if (ip == NULL || duid == NULL)
 		log_fatal("Impossible condition at %s:%d.", MDL);
 
 	if ((ip->hw_address.hlen == 0) ||
@@ -3033,6 +3152,13 @@ form_duid(struct data_string *duid, const char *file, int line)
 	}
 }
 
+void
+form_duid(struct data_string *duid, const char *file, int line)
+{
+	/* For now, just use the first interface on the list. */
+	form_interface_duid(duid, interfaces, file, line);
+}
+
 /* Write the default DUID to the lease store. */
 static isc_result_t
 write_duid(struct data_string *duid)
@@ -4368,7 +4494,8 @@ client_dns_update(struct client_state *client, dhcp_ddns_cb_t *ddns_cb)
 					  NULL, client,
 					  client->sent_options, NULL,
 					  &global_scope, oc, MDL)) {
-			if ((std_dhcid == 1) && (duid_v4 == 1) &&
+			if ((std_dhcid == 1) && (duid_v4 == 1 ||
+			     client->interface->hw_address.hbuf[0] == HTYPE_INFINIBAND) &&
 			    (client_identifier.data[0] == 255)) {
 				/*
 				 * This appears to be an embedded DUID,
diff --git a/common/bpf.c b/common/bpf.c
index df9facc..23d416a 100644
--- a/common/bpf.c
+++ b/common/bpf.c
@@ -117,7 +117,7 @@ int if_register_bpf (info)
 		log_fatal ("Can't attach interface %s to bpf device %s: %m",
 		       info -> name, filename);
 
-	get_hw_addr(info->name, &info->hw_address);
+	get_hw_addr(info);
 
 	return sock;
 }
@@ -199,11 +199,44 @@ struct bpf_insn dhcp_bpf_filter [] = {
 	BPF_STMT(BPF_RET+BPF_K, 0),
 };
 
+/* Packet filter program for DHCP over Infiniband.
+ * It is a generic UDP in IP packet filter (initialized to port 67 in [6].k).
+ *
+ * XXX
+ * Changes to the filter program may require changes to the constant offsets
+ * used in lpf_gen_filter_setup to patch the port in the BPF program!
+ * XXX
+ */
+struct bpf_insn dhcp_ib_bpf_filter [] = {
+	/* Make sure it's a UDP packet... */
+	BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 9),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
+
+	/* Make sure this isn't a fragment... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 6),
+	BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
+
+	/* Get the IP header length... */
+	BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0),
+
+	/* Make sure it's to the right port... */
+	BPF_STMT(BPF_LD + BPF_H + BPF_IND, 2),
+	BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
+
+	/* If we passed all the tests, ask for the whole packet. */
+	BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
+
+	/* Otherwise, drop it. */
+	BPF_STMT(BPF_RET + BPF_K, 0),
+};
+
 #if defined (DEC_FDDI)
 struct bpf_insn *bpf_fddi_filter;
 #endif
 
 int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
+int dhcp_ib_bpf_filter_len = sizeof dhcp_ib_bpf_filter / sizeof (struct bpf_insn);
+
 #if defined (HAVE_TR_SUPPORT)
 struct bpf_insn dhcp_bpf_tr_filter [] = {
         /* accept all token ring packets due to variable length header */
@@ -550,7 +583,9 @@ void maybe_setup_fallback ()
 
 #if defined(USE_BPF_RECEIVE) || defined(USE_BPF_HWADDR)
 void
-get_hw_addr(const char *name, struct hardware *hw) {
+get_hw_addr(struct interface_info *info) {
+	const char *name = info->name;
+	struct hardware *hw = &info->hw_address;
 	struct ifaddrs *ifa;
 	struct ifaddrs *p;
 	struct sockaddr_dl *sa;
diff --git a/common/dlpi.c b/common/dlpi.c
index 944f21c..5cc2f3b 100644
--- a/common/dlpi.c
+++ b/common/dlpi.c
@@ -1330,7 +1330,9 @@ void maybe_setup_fallback ()
 #endif /* USE_DLPI_SEND */
 
 void 
-get_hw_addr(const char *name, struct hardware *hw) {
+get_hw_addr(struct interface_info *info) {
+	const char *name = info->name;
+	struct hardware *hw = &info->hw_address;
 	int sock, unit;
 	long buf[DLPI_MAXDLBUF];
         union DL_primitives *dlp;
diff --git a/common/lpf.c b/common/lpf.c
index 8582f31..745d3c0 100644
--- a/common/lpf.c
+++ b/common/lpf.c
@@ -47,12 +47,22 @@
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <net/if.h>
+#include <ifaddrs.h>
 #endif
 
 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
 /* Reinitializes the specified interface after an address change.   This
    is not required for packet-filter APIs. */
 
+/* Default broadcast address for IPoIB */
+static unsigned char default_ib_bcast_addr[20] = {
+	0x00, 0xff, 0xff, 0xff,
+	0xff, 0x12, 0x40, 0x1b,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00,
+	0xff, 0xff, 0xff, 0xff
+};
+
 #ifdef USE_LPF_SEND
 void if_reinitialize_send (info)
 	struct interface_info *info;
@@ -80,10 +90,25 @@ int if_register_lpf (info)
 		struct sockaddr common;
 		} sa;
 	struct ifreq ifr;
+	int type;
+	int protocol;
 
 	/* Make an LPF socket. */
-	if ((sock = socket(PF_PACKET, SOCK_RAW,
-			   htons((short)ETH_P_ALL))) < 0) {
+	get_hw_addr(info);
+
+	if (info->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
+		/*
+		 * Infiniband provides a 4-octet encapsulation
+		 * header [08 00 00 00] only, so just omit it.
+		 */
+		type = SOCK_DGRAM;
+		protocol = ETHERTYPE_IP;
+	} else {
+		type = SOCK_RAW;
+		protocol = ETH_P_ALL;
+	}
+
+	if ((sock = socket(PF_PACKET, type, htons((short)protocol))) < 0) {
 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
 		    errno == EAFNOSUPPORT || errno == EINVAL) {
@@ -107,6 +132,7 @@ int if_register_lpf (info)
 	memset (&sa, 0, sizeof sa);
 	sa.ll.sll_family = AF_PACKET;
 	sa.ll.sll_ifindex = ifr.ifr_ifindex;
+	sa.ll.sll_protocol = htons(protocol);
 	if (bind (sock, &sa.common, sizeof sa)) {
 		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
 		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
@@ -122,8 +148,6 @@ int if_register_lpf (info)
 
 	}
 
-	get_hw_addr(info->name, &info->hw_address);
-
 	return sock;
 }
 #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
@@ -178,6 +202,8 @@ void if_deregister_send (info)
    in bpf includes... */
 extern struct sock_filter dhcp_bpf_filter [];
 extern int dhcp_bpf_filter_len;
+extern struct sock_filter dhcp_ib_bpf_filter [];
+extern int dhcp_ib_bpf_filter_len;
 
 #if defined (HAVE_TR_SUPPORT)
 extern struct sock_filter dhcp_bpf_tr_filter [];
@@ -185,6 +211,8 @@ extern int dhcp_bpf_tr_filter_len;
 static void lpf_tr_filter_setup (struct interface_info *);
 #endif
 
+static void lpf_ib_filter_setup (struct interface_info *);
+
 static void lpf_gen_filter_setup (struct interface_info *);
 
 void if_register_receive (info)
@@ -206,12 +234,14 @@ void if_register_receive (info)
 	}
 #endif
 
-
 #if defined (HAVE_TR_SUPPORT)
 	if (info -> hw_address.hbuf [0] == HTYPE_IEEE802)
 		lpf_tr_filter_setup (info);
 	else
 #endif
+	if (info -> hw_address.hbuf [0] == HTYPE_INFINIBAND)
+		lpf_ib_filter_setup (info);
+	else
 		lpf_gen_filter_setup (info);
 
 	if (!quiet_interface_discovery)
@@ -276,6 +306,38 @@ static void lpf_gen_filter_setup (info)
 	}
 }
 
+static void lpf_ib_filter_setup (info)
+	struct interface_info *info;
+{
+	struct sock_fprog p;
+
+	memset(&p, 0, sizeof(p));
+
+	/* Set up the bpf filter program structure */
+	p.len = dhcp_ib_bpf_filter_len;
+	p.filter = dhcp_ib_bpf_filter;
+
+	/* Patch the server port into the LPF  program...
+	   XXX changes to filter program may require changes
+	   to the insn number(s) used below! XXX */
+	dhcp_ib_bpf_filter [6].k = ntohs ((short)local_port);
+
+	if (setsockopt (info -> rfdesc, SOL_SOCKET, SO_ATTACH_FILTER, &p,
+			sizeof p) < 0) {
+		if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
+		    errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
+		    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!");
+		}
+		log_fatal ("Can't install packet filter program: %m");
+	}
+}
+
 #if defined (HAVE_TR_SUPPORT)
 static void lpf_tr_filter_setup (info)
 	struct interface_info *info;
@@ -315,6 +377,54 @@ static void lpf_tr_filter_setup (info)
 #endif /* USE_LPF_RECEIVE */
 
 #ifdef USE_LPF_SEND
+ssize_t send_packet_ib(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;
+{
+	unsigned ibufp = 0;
+	double ih [1536 / sizeof (double)];
+	unsigned char *buf = (unsigned char *)ih;
+	ssize_t result;
+
+	union sockunion {
+		struct sockaddr sa;
+		struct sockaddr_ll sll;
+		struct sockaddr_storage ss;
+	} su;
+
+	assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
+				to->sin_addr.s_addr, to->sin_port,
+				(unsigned char *)raw, len);
+	memcpy (buf + ibufp, raw, len);
+
+	memset(&su, 0, sizeof(su));
+	su.sll.sll_family = AF_PACKET;
+	su.sll.sll_protocol = htons(ETHERTYPE_IP);
+
+	if (!(su.sll.sll_ifindex = if_nametoindex(interface->name))) {
+		errno = ENOENT;
+		log_error ("send_packet_ib: %m - failed to get if index");
+		return -1;
+	}
+
+	su.sll.sll_hatype = htons(HTYPE_INFINIBAND);
+	su.sll.sll_halen = sizeof(interface->bcast_addr);
+	memcpy(&su.sll.sll_addr, interface->bcast_addr, 20);
+
+	result = sendto(interface->wfdesc, buf, ibufp + len, 0,
+			&su.sa, sizeof(su));
+
+	if (result < 0)
+		log_error ("send_packet_ib: %m");
+
+	return result;
+}
+
 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
 	struct interface_info *interface;
 	struct packet *packet;
@@ -335,6 +445,10 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
 		return send_fallback (interface, packet, raw,
 				      len, from, to, hto);
 
+	if (interface->hw_address.hbuf[0] == HTYPE_INFINIBAND)
+		return send_packet_ib(interface, packet, raw,
+				      len, from, to, hto);
+
 	if (hto == NULL && interface->anycast_mac_addr.hlen)
 		hto = &interface->anycast_mac_addr;
 
@@ -439,7 +553,15 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
 
 	bufix = 0;
 	/* Decode the physical header... */
-	offset = decode_hw_header (interface, ibuf, bufix, hfrom);
+	if (interface->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
+		/* ETHERTYPE_IP packet, no HW header to decode */
+		if (hfrom) {
+			memset(hfrom, 0, sizeof(*hfrom));
+			hfrom->hbuf[0] = HTYPE_INFINIBAND;
+		}
+	} else {
+		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
@@ -507,33 +629,44 @@ void maybe_setup_fallback ()
 
 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
 void
-get_hw_addr(const char *name, struct hardware *hw) {
-	int sock;
-	struct ifreq tmp;
-	struct sockaddr *sa;
+get_hw_addr(struct interface_info *info)
+{
+	const char *name = info->name;
+	struct hardware *hw = &info->hw_address;
+	struct ifaddrs *ifaddrs;
+	struct ifaddrs *ifa;
+	struct sockaddr_ll *sll = NULL;
 
-	if (strlen(name) >= sizeof(tmp.ifr_name)) {
-		log_fatal("Device name too long: \"%s\"", name);
-	}
+	if (getifaddrs(&ifaddrs) == -1)
+		log_fatal("Failed to get interfaces");
+
+	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+
+		if (!ifa->ifa_addr)
+			continue;
+
+		if (ifa->ifa_addr->sa_family != AF_PACKET)
+			continue;
 
-	sock = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sock < 0) {
-		log_fatal("Can't create socket for \"%s\": %m", name);
+		if (ifa->ifa_flags & IFF_LOOPBACK)
+			continue;
+
+		if (strcmp(ifa->ifa_name, name) == 0) {
+			sll = (struct sockaddr_ll *)(void *)ifa->ifa_addr;
+			break;
+		}
 	}
 
-	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);
+	if (sll == NULL || sll->sll_halen >= sizeof(hw->hbuf)) {
+		freeifaddrs(ifaddrs);
+		log_fatal("Failed to get HW address for %s\n", name);
 	}
 
-	sa = &tmp.ifr_hwaddr;
-	switch (sa->sa_family) {
+	switch (sll->sll_hatype) {
 		case ARPHRD_ETHER:
 			hw->hlen = 7;
 			hw->hbuf[0] = HTYPE_ETHER;
-			memcpy(&hw->hbuf[1], sa->sa_data, 6);
+			memcpy(&hw->hbuf[1], sll->sll_addr, 6);
 			break;
 		case ARPHRD_IEEE802:
 #ifdef ARPHRD_IEEE802_TR
@@ -541,18 +674,37 @@ get_hw_addr(const char *name, struct hardware *hw) {
 #endif /* ARPHRD_IEEE802_TR */
 			hw->hlen = 7;
 			hw->hbuf[0] = HTYPE_IEEE802;
-			memcpy(&hw->hbuf[1], sa->sa_data, 6);
+			memcpy(&hw->hbuf[1], sll->sll_addr, 6);
 			break;
 		case ARPHRD_FDDI:
 			hw->hlen = 7;
 			hw->hbuf[0] = HTYPE_FDDI;
-			memcpy(&hw->hbuf[1], sa->sa_data, 6);
+			memcpy(&hw->hbuf[1], sll->sll_addr, 6);
+			break;
+		case ARPHRD_INFINIBAND:
+			/* For Infiniband, save the broadcast address and store
+			 * the port GUID into the hardware address.
+			 */
+			if (ifa->ifa_flags & IFF_BROADCAST) {
+				struct sockaddr_ll *bll;
+
+				bll = (struct sockaddr_ll *)ifa->ifa_broadaddr;
+				memcpy(&info->bcast_addr, bll->sll_addr, sll->sll_halen);
+			} else {
+				memcpy(&info->bcast_addr, default_ib_bcast_addr,
+				       sizeof(default_ib_bcast_addr));
+			}
+
+			hw->hlen = sll->sll_halen + 1;
+			hw->hbuf[0] = HTYPE_INFINIBAND;
+			memcpy(&hw->hbuf[1], sll->sll_addr, sll->sll_halen);
 			break;
 		default:
+			freeifaddrs(ifaddrs);
 			log_fatal("Unsupported device type %ld for \"%s\"",
-				  (long int)sa->sa_family, name);
+				  (long int)sll->sll_family, name);
 	}
 
-	close(sock);
+	freeifaddrs(ifaddrs);
 }
 #endif
diff --git a/common/print.c b/common/print.c
index dfe0593..aa7b8e2 100644
--- a/common/print.c
+++ b/common/print.c
@@ -162,11 +162,11 @@ char *print_hw_addr (htype, hlen, data)
 	const int hlen;
 	const unsigned char *data;
 {
-	static char habuf [49];
+	static char habuf [64];
 	char *s;
 	int i;
 
-	if (hlen <= 0)
+	if (hlen <= 0 || hlen >= sizeof(habuf))
 		habuf [0] = 0;
 	else {
 		s = habuf;
diff --git a/common/socket.c b/common/socket.c
index e8851b4..2c6fb1c 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -328,7 +328,7 @@ void if_register_send (info)
 	info->wfdesc = if_register_socket(info, AF_INET, 0, NULL);
 	/* If this is a normal IPv4 address, get the hardware address. */
 	if (strcmp(info->name, "fallback") != 0)
-		get_hw_addr(info->name, &info->hw_address);
+		get_hw_addr(info);
 #if defined (USE_SOCKET_FALLBACK)
 	/* Fallback only registers for send, but may need to receive as
 	   well. */
@@ -391,7 +391,7 @@ void if_register_receive (info)
 #endif /* IP_PKTINFO... */
 	/* If this is a normal IPv4 address, get the hardware address. */
 	if (strcmp(info->name, "fallback") != 0)
-		get_hw_addr(info->name, &info->hw_address);
+		get_hw_addr(info);
 
 	if (!quiet_interface_discovery)
 		log_info ("Listening on Socket/%s%s%s",
@@ -505,7 +505,7 @@ if_register6(struct interface_info *info, int do_multicast) {
 	if (req_multi)
 		if_register_multicast(info);
 
-	get_hw_addr(info->name, &info->hw_address);
+	get_hw_addr(info);
 
 	if (!quiet_interface_discovery) {
 		if (info->shared_network != NULL) {
@@ -561,7 +561,7 @@ if_register_linklocal6(struct interface_info *info) {
 	info->rfdesc = sock;
 	info->wfdesc = sock;
 
-	get_hw_addr(info->name, &info->hw_address);
+	get_hw_addr(info);
 
 	if (!quiet_interface_discovery) {
 		if (info->shared_network != NULL) {
@@ -1145,7 +1145,9 @@ void maybe_setup_fallback ()
 #if defined(sun) && defined(USE_V4_PKTINFO)
 /* This code assumes the existence of SIOCGLIFHWADDR */
 void
-get_hw_addr(const char *name, struct hardware *hw) {
+get_hw_addr(struct interface_info *info) {
+	const char *name = info->name;
+	struct hardware *hw =  &info->hw_address;
 	struct sockaddr_dl *dladdrp;
 	int sock, i;
 	struct lifreq lifr;
diff --git a/includes/dhcpd.h b/includes/dhcpd.h
index f0f4b20..0a04f04 100644
--- a/includes/dhcpd.h
+++ b/includes/dhcpd.h
@@ -1321,6 +1321,7 @@ struct interface_info {
 	struct shared_network *shared_network;
 				/* Networks connected to this interface. */
 	struct hardware hw_address;	/* Its physical address. */
+	u_int8_t bcast_addr[HARDWARE_ADDR_LEN];	/* broadcast hw address */
 	struct in_addr *addresses;	/* Addresses associated with this
 					 * interface.
 					 */
@@ -2542,7 +2543,7 @@ void print_dns_status (int, struct dhcp_ddns_cb *, isc_result_t);
 #endif
 const char *print_time(TIME);
 
-void get_hw_addr(const char *name, struct hardware *hw);
+void get_hw_addr(struct interface_info *info);
 
 /* socket.c */
 #if defined (USE_SOCKET_SEND) || defined (USE_SOCKET_RECEIVE) \
diff --git a/server/dhcp.c b/server/dhcp.c
index 1f007e2..0f53ac2 100644
--- a/server/dhcp.c
+++ b/server/dhcp.c
@@ -1806,11 +1806,12 @@ void echo_client_id(packet, lease, in_options, out_options)
 
 	/* Check if echo-client-id is enabled */
 	oc = lookup_option(&server_universe, in_options, SV_ECHO_CLIENT_ID);
-	if (oc && evaluate_boolean_option_cache(&ignorep, packet, lease,
+	if ((packet->raw && packet->raw->hlen == 0) ||
+	    (oc && evaluate_boolean_option_cache(&ignorep, packet, lease,
                                                 NULL, packet->options,
 						in_options,
                                                 (lease ? &lease->scope : NULL),
-						oc, MDL)) {
+						oc, MDL))) {
 		struct data_string client_id;
 		unsigned int opcode = DHO_DHCP_CLIENT_IDENTIFIER;
 
@@ -3592,9 +3593,11 @@ void dhcp_reply (lease)
 	} else
 		bufs |= 2; /* XXX */
 
-	memcpy (raw.chaddr,
-		&lease -> hardware_addr.hbuf [1], sizeof raw.chaddr);
-	raw.hlen = lease -> hardware_addr.hlen - 1;
+	if (lease -> hardware_addr.hlen - 1 <= sizeof(raw.chaddr))
+	    raw.hlen = lease -> hardware_addr.hlen - 1;
+	if (lease -> hardware_addr.hlen > 1 && raw.hlen)
+	    memcpy (raw.chaddr,
+		    &lease -> hardware_addr.hbuf [1], raw.hlen);
 	raw.htype = lease -> hardware_addr.hbuf [0];
 
 	/* See if this is a Microsoft client that NUL-terminates its
diff --git a/server/dhcpleasequery.c b/server/dhcpleasequery.c
index 0766b84..c9a1e8b 100644
--- a/server/dhcpleasequery.c
+++ b/server/dhcpleasequery.c
@@ -295,7 +295,7 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 							  assoc_ips, 
 							  nassoc_ips);
 
-		} else {
+		} else if (packet->raw->hlen) {
 
 			if (packet->raw->hlen+1 > sizeof(h.hbuf)) {
 				log_info("%s: hardware length too long, "
@@ -405,11 +405,13 @@ dhcpleasequery(struct packet *packet, int ms_nulltp) {
 		 * Set the hardware address fields.
 		 */
 
-		packet->raw->hlen = lease->hardware_addr.hlen - 1;
 		packet->raw->htype = lease->hardware_addr.hbuf[0];
-		memcpy(packet->raw->chaddr, 
-		       &lease->hardware_addr.hbuf[1], 
-		       sizeof(packet->raw->chaddr));
+		if (lease->hardware_addr.hlen - 1 <= sizeof(packet->raw->chaddr))
+		    packet->raw->hlen = lease->hardware_addr.hlen - 1;
+		if (lease->hardware_addr.hlen > 1 && packet->raw->hlen)
+		    memcpy(packet->raw->chaddr,
+			    &lease->hardware_addr.hbuf[1],
+			    packet->raw->hlen);
 
 		/*
 		 * Set client identifier option.
diff --git a/server/mdb.c b/server/mdb.c
index 9a7da80..539a233 100644
--- a/server/mdb.c
+++ b/server/mdb.c
@@ -619,6 +619,9 @@ int find_hosts_by_haddr (struct host_decl **hp, int htype,
 		return ret;
 #endif
 
+	if (!hlen || hlen > HARDWARE_ADDR_LEN)
+		return 0;
+
 	h.hlen = hlen + 1;
 	h.hbuf [0] = htype;
 	memcpy (&h.hbuf [1], haddr, hlen);
-- 
2.1.4

openSUSE Build Service is sponsored by