1 From de8468cf16c0fa9d01412446fcf1d44ccd0fd411 Mon Sep 17 00:00:00 2001
2 From: Pavel Zhukov <pzhukov@redhat.com>
3 Date: Thu, 21 Feb 2019 10:34:21 +0100
4 Subject: [PATCH 14/21] IPoIB support (#660681)
7 (Submitted to dhcp-bugs@isc.org - [ISC-Bugs #24249])
9 client/dhclient.c | 32 +++++++
10 common/bpf.c | 32 +++++++
11 common/discover.c | 4 +-
12 common/lpf.c | 262 +++++++++++++++++++++++++++++++++++++++++++++++++-----
13 common/socket.c | 8 +-
14 includes/dhcpd.h | 6 +-
15 6 files changed, 315 insertions(+), 29 deletions(-)
17 diff --git a/client/dhclient.c b/client/dhclient.c
18 index 301132c..dc9080e 100644
19 --- a/client/dhclient.c
20 +++ b/client/dhclient.c
21 @@ -205,6 +205,8 @@ static const char use_v6command[] = "Command not used for DHCPv4: %s";
23 #define DHCLIENT_USAGEH "{--version|--help|-h}"
25 +static void setup_ib_interface(struct interface_info *ip);
28 usage(const char *sfmt, const char *sarg)
30 @@ -1191,6 +1193,13 @@ main(int argc, char **argv) {
32 srandom(seed + cur_time + (unsigned)getpid());
34 + /* Setup specific Infiniband options */
35 + for (ip = interfaces; ip; ip = ip->next) {
37 + (ip->hw_address.hbuf[0] == HTYPE_INFINIBAND)) {
38 + setup_ib_interface(ip);
43 * Establish a default DUID. We always do so for v6 and
44 @@ -1486,6 +1495,29 @@ int find_subnet (struct subnet **sp,
48 +static void setup_ib_interface(struct interface_info *ip)
52 + /* Set the broadcast flag */
53 + ip->client->config->bootp_broadcast_always = 1;
56 + * Find out if a dhcp-client-identifier option was specified either
57 + * in the config file or on the command line
59 + for (g = ip->client->config->on_transmission; g != NULL; g = g->next) {
60 + if ((g->statements != NULL) &&
61 + (strcmp(g->statements->data.option->option->name,
62 + "dhcp-client-identifier") == 0)) {
67 + /* No client ID specified */
68 + log_fatal("dhcp-client-identifier must be specified for InfiniBand");
73 * Each routine is called from the dhclient_state_machine() in one of
74 diff --git a/common/bpf.c b/common/bpf.c
75 index ffbd09a..568e3d9 100644
78 @@ -237,11 +237,43 @@ int dhcp_bpf_relay_filter_len =
79 sizeof dhcp_bpf_relay_filter / sizeof (struct bpf_insn);
82 +/* Packet filter program for DHCP over Infiniband.
85 + * Changes to the filter program may require changes to the constant offsets
86 + * used in lpf_gen_filter_setup to patch the port in the BPF program!
89 +struct bpf_insn dhcp_ib_bpf_filter [] = {
90 + /* Packet filter for Infiniband */
91 + /* Make sure it's a UDP packet... */
92 + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 9),
93 + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6),
95 + /* Make sure this isn't a fragment... */
96 + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 6),
97 + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0),
99 + /* Get the IP header length... */
100 + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 0),
102 + /* Make sure it's to the right port... */
103 + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 2),
104 + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1),
106 + /* If we passed all the tests, ask for the whole packet. */
107 + BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
109 + /* Otherwise, drop it. */
110 + BPF_STMT(BPF_RET + BPF_K, 0),
113 #if defined (DEC_FDDI)
114 struct bpf_insn *bpf_fddi_filter = NULL;
117 int dhcp_bpf_filter_len = sizeof dhcp_bpf_filter / sizeof (struct bpf_insn);
118 +int dhcp_ib_bpf_filter_len = sizeof dhcp_ib_bpf_filter / sizeof (struct bpf_insn);
119 #if defined (HAVE_TR_SUPPORT)
120 struct bpf_insn dhcp_bpf_tr_filter [] = {
121 /* accept all token ring packets due to variable length header */
122 diff --git a/common/discover.c b/common/discover.c
123 index 6ef8852..65881fc 100644
124 --- a/common/discover.c
125 +++ b/common/discover.c
126 @@ -894,7 +894,7 @@ discover_interfaces(int state) {
127 if_register_send(tmp);
129 /* get_hw_addr() was called by register. */
130 - get_hw_addr(tmp->name, &tmp->hw_address);
135 @@ -907,7 +907,7 @@ discover_interfaces(int state) {
136 so now we have to call it explicitly
137 to not leave the hardware address unknown
138 (some code expects it cannot be. */
139 - get_hw_addr(tmp->name, &tmp->hw_address);
142 if_register_linklocal6(tmp);
144 diff --git a/common/lpf.c b/common/lpf.c
145 index b0ed01c..b732a86 100644
149 #include <sys/ioctl.h>
150 #include <sys/socket.h>
152 +#include <ifaddrs.h>
154 +/* Default broadcast address for IPoIB */
155 +static unsigned char default_ib_bcast_addr[20] = {
156 + 0x00, 0xff, 0xff, 0xff,
157 + 0xff, 0x12, 0x40, 0x1b,
158 + 0x00, 0x00, 0x00, 0x00,
159 + 0x00, 0x00, 0x00, 0x00,
160 + 0xff, 0xff, 0xff, 0xff
165 #if defined (USE_LPF_SEND) || defined (USE_LPF_RECEIVE)
166 @@ -78,10 +89,20 @@ int if_register_lpf (info)
167 struct sockaddr common;
174 + if (info->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
176 + protocol = ETHERTYPE_IP;
179 + protocol = ETH_P_ALL;
182 /* Make an LPF socket. */
183 - if ((sock = socket(PF_PACKET, SOCK_RAW,
184 - htons((short)ETH_P_ALL))) < 0) {
185 + if ((sock = socket(PF_PACKET, type, htons((short)protocol))) < 0) {
186 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
187 errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
188 errno == EAFNOSUPPORT || errno == EINVAL) {
189 @@ -104,6 +125,7 @@ int if_register_lpf (info)
190 /* Bind to the interface name */
191 memset (&sa, 0, sizeof sa);
192 sa.ll.sll_family = AF_PACKET;
193 + sa.ll.sll_protocol = htons(protocol);
194 sa.ll.sll_ifindex = ifr.ifr_ifindex;
195 if (bind (sock, &sa.common, sizeof sa)) {
196 if (errno == ENOPROTOOPT || errno == EPROTONOSUPPORT ||
197 @@ -120,8 +142,6 @@ int if_register_lpf (info)
201 - get_hw_addr(info->name, &info->hw_address);
205 #endif /* USE_LPF_SEND || USE_LPF_RECEIVE */
206 @@ -176,6 +196,8 @@ void if_deregister_send (info)
207 in bpf includes... */
208 extern struct sock_filter dhcp_bpf_filter [];
209 extern int dhcp_bpf_filter_len;
210 +extern struct sock_filter dhcp_ib_bpf_filter [];
211 +extern int dhcp_ib_bpf_filter_len;
213 #if defined(RELAY_PORT)
214 extern struct sock_filter dhcp_bpf_relay_filter [];
215 @@ -199,11 +221,12 @@ void if_register_receive (info)
216 #ifdef PACKET_AUXDATA
220 - if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA,
221 - &val, sizeof(val)) < 0) {
222 - if (errno != ENOPROTOOPT) {
223 - log_fatal ("Failed to set auxiliary packet data: %m");
224 + if (info->hw_address.hbuf[0] != HTYPE_INFINIBAND) {
225 + if (setsockopt(info->rfdesc, SOL_PACKET, PACKET_AUXDATA,
226 + &val, sizeof(val)) < 0) {
227 + if (errno != ENOPROTOOPT) {
228 + log_fatal ("Failed to set auxiliary packet data: %m");
233 @@ -330,6 +353,54 @@ static void lpf_tr_filter_setup (info)
234 #endif /* USE_LPF_RECEIVE */
237 +ssize_t send_packet_ib(interface, packet, raw, len, from, to, hto)
238 + struct interface_info *interface;
239 + struct packet *packet;
240 + struct dhcp_packet *raw;
242 + struct in_addr from;
243 + struct sockaddr_in *to;
244 + struct hardware *hto;
246 + unsigned ibufp = 0;
247 + double ih [1536 / sizeof (double)];
248 + unsigned char *buf = (unsigned char *)ih;
252 + struct sockaddr sa;
253 + struct sockaddr_ll sll;
254 + struct sockaddr_storage ss;
257 + assemble_udp_ip_header (interface, buf, &ibufp, from.s_addr,
258 + to->sin_addr.s_addr, to->sin_port,
259 + (unsigned char *)raw, len);
260 + memcpy (buf + ibufp, raw, len);
262 + memset(&su, 0, sizeof(su));
263 + su.sll.sll_family = AF_PACKET;
264 + su.sll.sll_protocol = htons(ETHERTYPE_IP);
266 + if (!(su.sll.sll_ifindex = if_nametoindex(interface->name))) {
268 + log_error ("send_packet_ib: %m - failed to get if index");
272 + su.sll.sll_hatype = htons(HTYPE_INFINIBAND);
273 + su.sll.sll_halen = sizeof(interface->bcast_addr);
274 + memcpy(&su.sll.sll_addr, interface->bcast_addr, 20);
276 + result = sendto(interface->wfdesc, buf, ibufp + len, 0,
277 + &su.sa, sizeof(su));
280 + log_error ("send_packet_ib: %m");
285 ssize_t send_packet (interface, packet, raw, len, from, to, hto)
286 struct interface_info *interface;
287 struct packet *packet;
288 @@ -350,6 +421,11 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
289 return send_fallback (interface, packet, raw,
292 + if (interface->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
293 + return send_packet_ib(interface, packet, raw, len, from,
297 if (hto == NULL && interface->anycast_mac_addr.hlen)
298 hto = &interface->anycast_mac_addr;
300 @@ -370,6 +446,42 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
301 #endif /* USE_LPF_SEND */
303 #ifdef USE_LPF_RECEIVE
304 +ssize_t receive_packet_ib (interface, buf, len, from, hfrom)
305 + struct interface_info *interface;
306 + unsigned char *buf;
308 + struct sockaddr_in *from;
309 + struct hardware *hfrom;
313 + unsigned char ibuf [1536];
314 + unsigned bufix = 0;
317 + length = read(interface->rfdesc, ibuf, sizeof(ibuf));
322 + offset = decode_udp_ip_header(interface, ibuf, bufix, from,
323 + (unsigned)length, &paylen, 0);
331 + if (length < paylen)
332 + log_fatal("Internal inconsistency at %s:%d.", MDL);
334 + /* Copy out the data in the packet... */
335 + memcpy(buf, &ibuf[bufix], paylen);
337 + return (ssize_t)paylen;
340 ssize_t receive_packet (interface, buf, len, from, hfrom)
341 struct interface_info *interface;
343 @@ -408,6 +520,10 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
345 #endif /* PACKET_AUXDATA */
347 + if (interface->hw_address.hbuf[0] == HTYPE_INFINIBAND) {
348 + return receive_packet_ib(interface, buf, len, from, hfrom);
351 length = recvmsg (interface->rfdesc, &msg, 0);
354 @@ -521,11 +637,33 @@ void maybe_setup_fallback ()
357 #if defined (USE_LPF_RECEIVE) || defined (USE_LPF_HWADDR)
359 -get_hw_addr(const char *name, struct hardware *hw) {
360 +struct sockaddr_ll *
361 +get_ll (struct ifaddrs *ifaddrs, struct ifaddrs **ifa, char *name)
363 + for (*ifa = ifaddrs; *ifa != NULL; *ifa = (*ifa)->ifa_next) {
364 + if ((*ifa)->ifa_addr == NULL)
367 + if ((*ifa)->ifa_addr->sa_family != AF_PACKET)
370 + if ((*ifa)->ifa_flags & IFF_LOOPBACK)
373 + if (strcmp((*ifa)->ifa_name, name) == 0)
374 + return (struct sockaddr_ll *)(void *)(*ifa)->ifa_addr;
380 +struct sockaddr_ll *
381 +ioctl_get_ll(char *name)
385 - struct sockaddr *sa;
386 + struct sockaddr *sa = NULL;
387 + struct sockaddr_ll *sll = NULL;
389 if (strlen(name) >= sizeof(tmp.ifr_name)) {
390 log_fatal("Device name too long: \"%s\"", name);
391 @@ -539,16 +677,61 @@ get_hw_addr(const char *name, struct hardware *hw) {
392 memset(&tmp, 0, sizeof(tmp));
393 strcpy(tmp.ifr_name, name);
394 if (ioctl(sock, SIOCGIFHWADDR, &tmp) < 0) {
395 - log_fatal("Error getting hardware address for \"%s\": %m",
396 + log_fatal("Error getting hardware address for \"%s\": %m",
401 sa = &tmp.ifr_hwaddr;
402 - switch (sa->sa_family) {
403 + // needs to be freed outside this function
404 + sll = dmalloc (sizeof (struct sockaddr_ll), MDL);
406 + log_fatal("Unable to allocate memory for link layer address");
407 + memcpy(&sll->sll_hatype, &sa->sa_family, sizeof (sll->sll_hatype));
408 + memcpy(sll->sll_addr, sa->sa_data, sizeof (sll->sll_addr));
409 + switch (sll->sll_hatype) {
410 + case ARPHRD_INFINIBAND:
411 + sll->sll_halen = HARDWARE_ADDR_LEN_IOCTL;
420 +get_hw_addr(struct interface_info *info)
422 + struct hardware *hw = &info->hw_address;
423 + char *name = info->name;
424 + struct ifaddrs *ifaddrs = NULL;
425 + struct ifaddrs *ifa = NULL;
426 + struct sockaddr_ll *sll = NULL;
427 + int sll_allocated = 0;
429 + char *colon = NULL;
431 + if (getifaddrs(&ifaddrs) == -1)
432 + log_fatal("Failed to get interfaces");
434 + if ((sll = get_ll(ifaddrs, &ifa, name)) == NULL) {
436 + * We were unable to get link-layer address for name.
437 + * Fall back to ioctl(SIOCGIFHWADDR).
439 + sll = ioctl_get_ll(name);
443 + // shouldn't happen
444 + log_fatal("Unexpected internal error");
447 + switch (sll->sll_hatype) {
450 hw->hbuf[0] = HTYPE_ETHER;
451 - memcpy(&hw->hbuf[1], sa->sa_data, 6);
452 + memcpy(&hw->hbuf[1], sll->sll_addr, 6);
455 #ifdef ARPHRD_IEEE802_TR
456 @@ -556,18 +739,50 @@ get_hw_addr(const char *name, struct hardware *hw) {
457 #endif /* ARPHRD_IEEE802_TR */
459 hw->hbuf[0] = HTYPE_IEEE802;
460 - memcpy(&hw->hbuf[1], sa->sa_data, 6);
461 + memcpy(&hw->hbuf[1], sll->sll_addr, 6);
465 hw->hbuf[0] = HTYPE_FDDI;
466 - memcpy(&hw->hbuf[1], sa->sa_data, 6);
467 + memcpy(&hw->hbuf[1], sll->sll_addr, 6);
469 + case ARPHRD_INFINIBAND:
470 + dup = strdup(name);
471 + /* Aliased infiniband interface is special case where
472 + * neither get_ll() nor ioctl_get_ll() get's correct hw
473 + * address, so we have to truncate the :0 and run
474 + * get_ll() again for the rest.
476 + if ((colon = strchr(dup, ':')) != NULL) {
478 + if ((sll = get_ll(ifaddrs, &ifa, dup)) == NULL)
479 + log_fatal("Error getting hardware address for \"%s\": %m", name);
482 + /* For Infiniband, save the broadcast address and store
483 + * the port GUID into the hardware address.
485 + if (ifa && (ifa->ifa_flags & IFF_BROADCAST)) {
486 + struct sockaddr_ll *bll;
488 + bll = (struct sockaddr_ll *)ifa->ifa_broadaddr;
489 + memcpy(&info->bcast_addr, bll->sll_addr, 20);
491 + memcpy(&info->bcast_addr, default_ib_bcast_addr,
495 + hw->hlen = HARDWARE_ADDR_LEN_IOCTL + 1;
496 + hw->hbuf[0] = HTYPE_INFINIBAND;
497 + memcpy(&hw->hbuf[1],
498 + &sll->sll_addr[sll->sll_halen - HARDWARE_ADDR_LEN_IOCTL],
499 + HARDWARE_ADDR_LEN_IOCTL);
501 #if defined(ARPHRD_PPP)
503 if (local_family != AF_INET6)
504 - log_fatal("Unsupported device type %d for \"%s\"",
505 - sa->sa_family, name);
506 + log_fatal("local_family != AF_INET6 for \"%s\"",
509 hw->hbuf[0] = HTYPE_RESERVED;
510 /* 0xdeadbeef should never occur on the wire,
511 @@ -580,10 +795,13 @@ get_hw_addr(const char *name, struct hardware *hw) {
515 - log_fatal("Unsupported device type %ld for \"%s\"",
516 - (long int)sa->sa_family, name);
517 + freeifaddrs(ifaddrs);
518 + log_fatal("Unsupported device type %hu for \"%s\"",
519 + sll->sll_hatype, name);
525 + freeifaddrs(ifaddrs);
528 diff --git a/common/socket.c b/common/socket.c
529 index 483eb9c..6e1caac 100644
530 --- a/common/socket.c
531 +++ b/common/socket.c
532 @@ -350,7 +350,7 @@ void if_register_send (info)
533 info->wfdesc = if_register_socket(info, AF_INET, 0, NULL);
534 /* If this is a normal IPv4 address, get the hardware address. */
535 if (strcmp(info->name, "fallback") != 0)
536 - get_hw_addr(info->name, &info->hw_address);
538 #if defined (USE_SOCKET_FALLBACK)
539 /* Fallback only registers for send, but may need to receive as
541 @@ -413,7 +413,7 @@ void if_register_receive (info)
542 #endif /* IP_PKTINFO... */
543 /* If this is a normal IPv4 address, get the hardware address. */
544 if (strcmp(info->name, "fallback") != 0)
545 - get_hw_addr(info->name, &info->hw_address);
548 if (!quiet_interface_discovery)
549 log_info ("Listening on Socket/%s%s%s",
550 @@ -567,7 +567,7 @@ if_register6(struct interface_info *info, int do_multicast) {
552 if_register_multicast(info);
554 - get_hw_addr(info->name, &info->hw_address);
557 if (!quiet_interface_discovery) {
558 if (info->shared_network != NULL) {
559 @@ -623,7 +623,7 @@ if_register_linklocal6(struct interface_info *info) {
563 - get_hw_addr(info->name, &info->hw_address);
566 if (!quiet_interface_discovery) {
567 if (info->shared_network != NULL) {
568 diff --git a/includes/dhcpd.h b/includes/dhcpd.h
569 index faa9251..0c1a0aa 100644
570 --- a/includes/dhcpd.h
571 +++ b/includes/dhcpd.h
572 @@ -485,6 +485,9 @@ struct packet {
574 #define HARDWARE_ADDR_LEN 20
576 +/* ioctl limits hardware addresses to 8 bytes */
577 +#define HARDWARE_ADDR_LEN_IOCTL 8
581 u_int8_t hbuf[HARDWARE_ADDR_LEN + 1];
582 @@ -1365,6 +1368,7 @@ struct interface_info {
583 struct shared_network *shared_network;
584 /* Networks connected to this interface. */
585 struct hardware hw_address; /* Its physical address. */
586 + u_int8_t bcast_addr[20]; /* Infiniband broadcast address */
587 struct in_addr *addresses; /* Addresses associated with this
590 @@ -2633,7 +2637,7 @@ void print_dns_status (int, struct dhcp_ddns_cb *, isc_result_t);
592 const char *print_time(TIME);
594 -void get_hw_addr(const char *name, struct hardware *hw);
595 +void get_hw_addr(struct interface_info *info);
596 char *buf_to_hex (const unsigned char *s, unsigned len,
597 const char *file, int line);
598 char *format_lease_id(const unsigned char *s, unsigned len, int format,