1 /* $NetBSD: dlpi.c,v 1.1.1.3 2014/07/12 11:57:43 spz Exp $ */
4 Data Link Provider Interface (DLPI) network interface code. */
7 * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 2004,2007 by Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1996-2003 by Internet Software Consortium
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Internet Systems Consortium, Inc.
25 * Redwood City, CA 94063
27 * https://www.isc.org/
29 * This software was written for Internet Systems Consortium
30 * by Eric James Negaard, <lmdejn@lmd.ericsson.se>. To learn more about
31 * Internet Systems Consortium, see ``https://www.isc.org''.
33 * Joost Mulders has also done considerable work in debugging the DLPI API
34 * support on Solaris and getting this code to work properly on a variety
35 * of different Solaris platforms.
38 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: dlpi.c,v 1.1.1.3 2014/07/12 11:57:43 spz Exp $");
42 * Based largely in part to the existing NIT code in nit.c.
44 * This code has been developed and tested on sparc-based machines running
45 * SunOS 5.5.1, with le and hme network interfaces. It should be pretty
50 * Implementation notes:
52 * I first tried to write this code to the "vanilla" DLPI 2.0 API.
53 * It worked on a Sun Ultra-1 with a hme interface, but didn't work
54 * on Sun SparcStation 5's with "le" interfaces (the packets sent out
55 * via dlpiunitdatareq contained an Ethernet type of 0x0000 instead
56 * of the expected 0x0800).
58 * Therefore I added the "DLPI_RAW" code which is a Sun extension to
59 * the DLPI standard. This code works on both of the above machines.
60 * This is configurable in the OS-dependent include file by defining
63 * It quickly became apparant that I should also use the "pfmod"
64 * STREAMS module to cut down on the amount of user level packet
65 * processing. I don't know how widely available "pfmod" is, so it's
66 * use is conditionally included. This is configurable in the
67 * OS-dependent include file by defining USE_DLPI_PFMOD.
69 * A major quirk on the Sun's at least, is that no packets seem to get
70 * sent out the interface until six seconds after the interface is
71 * first "attached" to [per system reboot] (it's actually from when
72 * the interface is attached, not when it is plumbed, so putting a
73 * sleep into the dhclient-script at PREINIT time doesn't help). I
74 * HAVE tried, without success to poll the fd to see when it is ready
75 * for writing. This doesn't help at all. If the sleeps are not done,
76 * the initial DHCPREQUEST or DHCPDISCOVER never gets sent out, so
77 * I've put them here, when register_send and register_receive are
78 * called (split up into two three-second sleeps between the notices,
79 * so that it doesn't seem like so long when you're watching :-). The
80 * amount of time to sleep is configurable in the OS-dependent include
81 * file by defining DLPI_FIRST_SEND_WAIT to be the number of seconds
86 * The Open Group Technical Standard can be found here:
87 * http://www.opengroup.org/onlinepubs/009618899/index.htm
89 * The HP DLPI Programmer's Guide can be found here:
90 * http://docs.hp.com/en/B2355-90139/index.html
95 #if defined (USE_DLPI_SEND) || defined (USE_DLPI_RECEIVE) || \
96 defined(USE_DLPI_HWADDR)
98 # include <sys/ioctl.h>
99 # include <sys/time.h>
100 # include <sys/dlpi.h>
101 # include <stropts.h>
102 # ifdef USE_DLPI_PFMOD
103 # include <sys/pfmod.h>
108 # include <netinet/in_systm.h>
109 # include "includes/netinet/ip.h"
110 # include "includes/netinet/udp.h"
111 # include "includes/netinet/if_ether.h"
113 # ifdef USE_DLPI_PFMOD
115 # define DLPI_MODNAME "DLPI+RAW+PFMOD"
117 # define DLPI_MODNAME "DLPI+PFMOD"
121 # define DLPI_MODNAME "DLPI+RAW"
123 # define DLPI_MODNAME "DLPI"
128 # define ABS(x) ((x) >= 0 ? (x) : 0-(x))
131 #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW)
132 static int strioctl (int fd
, int cmd
, int timeout
, int len
, char *dp
);
135 #define DLPI_MAXDLBUF 8192 /* Buffer size */
136 #define DLPI_MAXDLADDR 1024 /* Max address size */
137 #define DLPI_DEVDIR "/dev/" /* Device directory */
139 static int dlpiopen(const char *ifname
);
140 static int dlpiunit (char *ifname
);
141 static int dlpiinforeq (int fd
);
142 static int dlpiphysaddrreq (int fd
, unsigned long addrtype
);
143 static int dlpiattachreq (int fd
, unsigned long ppa
);
144 static int dlpibindreq (int fd
, unsigned long sap
, unsigned long max_conind
,
145 unsigned long service_mode
, unsigned long conn_mgmt
,
146 unsigned long xidtest
);
147 #if defined(UNUSED_DLPI_INTERFACE)
148 /* These functions are unused at present, but may be used at a later date.
149 * defined out to avoid compiler warnings about unused static functions.
151 static int dlpidetachreq (int fd
);
152 static int dlpiunbindreq (int fd
);
154 static int dlpiokack (int fd
, char *bufp
);
155 static int dlpiinfoack (int fd
, char *bufp
);
156 static int dlpiphysaddrack (int fd
, char *bufp
);
157 static int dlpibindack (int fd
, char *bufp
);
158 #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE)
159 /* These functions are not used if we're only sourcing the get_hw_addr()
160 * function (for USE_SOCKETS).
162 static int dlpiunitdatareq (int fd
, unsigned char *addr
, int addrlen
,
163 unsigned long minpri
, unsigned long maxpri
,
164 unsigned char *data
, int datalen
);
165 static int dlpiunitdataind (int fd
,
166 unsigned char *dstaddr
,
167 unsigned long *dstaddrlen
,
168 unsigned char *srcaddr
,
169 unsigned long *srcaddrlen
,
170 unsigned long *grpaddr
,
173 #endif /* !USE_DLPI_HWADDR: USE_DLPI_SEND || USE_DLPI_RECEIVE */
174 static int expected (unsigned long prim
, union DL_primitives
*dlp
,
176 static int strgetmsg (int fd
, struct strbuf
*ctlp
, struct strbuf
*datap
,
177 int *flagsp
, char *caller
);
179 /* Reinitializes the specified interface after an address change. This
180 is not required for packet-filter APIs. */
183 void if_reinitialize_send (info
)
184 struct interface_info
*info
;
189 #ifdef USE_DLPI_RECEIVE
190 void if_reinitialize_receive (info
)
191 struct interface_info
*info
;
196 /* Called by get_interface_list for each interface that's discovered.
197 Opens a packet filter for each interface and adds it to the select
200 int if_register_dlpi (info
)
201 struct interface_info
*info
;
205 long buf
[DLPI_MAXDLBUF
];
206 union DL_primitives
*dlp
;
208 dlp
= (union DL_primitives
*)buf
;
210 /* Open a DLPI device */
211 if ((sock
= dlpiopen (info
-> name
)) < 0) {
212 log_fatal ("Can't open DLPI device for %s: %m", info
-> name
);
216 * Submit a DL_INFO_REQ request, to find the dl_mac_type and
219 if (dlpiinforeq(sock
) < 0 || dlpiinfoack(sock
, (char *)buf
) < 0) {
220 log_fatal ("Can't get DLPI MAC type for %s: %m", info
-> name
);
222 switch (dlp
-> info_ack
.dl_mac_type
) {
223 case DL_CSMACD
: /* IEEE 802.3 */
225 info
-> hw_address
.hbuf
[0] = HTYPE_ETHER
;
227 /* adding token ring 5/1999 - mayer@ping.at */
229 info
-> hw_address
.hbuf
[0] = HTYPE_IEEE802
;
232 info
-> hw_address
.hbuf
[0] = HTYPE_FDDI
;
235 log_fatal("%s: unsupported DLPI MAC type %lu", info
->name
,
236 (unsigned long)dlp
->info_ack
.dl_mac_type
);
240 * copy the sap length and broadcast address of this interface
241 * to interface_info. This fixes nothing but seemed nicer than to
242 * assume -2 and ffffff.
244 info
-> dlpi_sap_length
= dlp
-> info_ack
.dl_sap_length
;
245 info
-> dlpi_broadcast_addr
.hlen
=
246 dlp
-> info_ack
.dl_brdcst_addr_length
;
247 memcpy (info
-> dlpi_broadcast_addr
.hbuf
,
248 (char *)dlp
+ dlp
-> info_ack
.dl_brdcst_addr_offset
,
249 dlp
-> info_ack
.dl_brdcst_addr_length
);
252 if (dlp
-> info_ack
.dl_provider_style
== DL_STYLE2
) {
254 * Attach to the device. If this fails, the device
257 unit
= dlpiunit (info
-> name
);
259 if (dlpiattachreq (sock
, unit
) < 0
260 || dlpiokack (sock
, (char *)buf
) < 0) {
261 log_fatal ("Can't attach DLPI device for %s: %m", info
-> name
);
266 * Bind to the IP service access point (SAP), connectionless (CLDLS).
268 if (dlpibindreq (sock
, ETHERTYPE_IP
, 0, DL_CLDLS
, 0, 0) < 0
269 || dlpibindack (sock
, (char *)buf
) < 0) {
270 log_fatal ("Can't bind DLPI device for %s: %m", info
-> name
);
274 * Submit a DL_PHYS_ADDR_REQ request, to find
275 * the hardware address
277 if (dlpiphysaddrreq (sock
, DL_CURR_PHYS_ADDR
) < 0
278 || dlpiphysaddrack (sock
, (char *)buf
) < 0) {
279 log_fatal ("Can't get DLPI hardware address for %s: %m",
283 info
-> hw_address
.hlen
= dlp
-> physaddr_ack
.dl_addr_length
+ 1;
284 memcpy (&info
-> hw_address
.hbuf
[1],
285 (char *)buf
+ dlp
-> physaddr_ack
.dl_addr_offset
,
286 dlp
-> physaddr_ack
.dl_addr_length
);
289 if (strioctl (sock
, DLIOCRAW
, INFTIM
, 0, 0) < 0) {
290 log_fatal ("Can't set DLPI RAW mode for %s: %m",
295 #ifdef USE_DLPI_PFMOD
296 if (ioctl (sock
, I_PUSH
, "pfmod") < 0) {
297 log_fatal ("Can't push packet filter onto DLPI for %s: %m",
305 #if defined(USE_DLPI_PFMOD) || defined(USE_DLPI_RAW)
307 strioctl (fd
, cmd
, timeout
, len
, dp
)
318 sio
.ic_timout
= timeout
;
322 if ((rslt
= ioctl (fd
, I_STR
, &sio
)) < 0) {
328 #endif /* USE_DPI_PFMOD || USE_DLPI_RAW */
331 void if_register_send (info
)
332 struct interface_info
*info
;
334 /* If we're using the DLPI API for sending and receiving,
335 we don't need to register this interface twice. */
336 #ifndef USE_DLPI_RECEIVE
337 # ifdef USE_DLPI_PFMOD
338 struct packetfilt pf
;
341 info
-> wfdesc
= if_register_dlpi (info
);
343 # ifdef USE_DLPI_PFMOD
344 /* Set up an PFMOD filter that rejects everything... */
347 pf
.Pf_Filter
[0] = ENF_PUSHZERO
;
349 /* Install the filter */
350 if (strioctl (info
-> wfdesc
, PFIOCSETF
, INFTIM
,
351 sizeof (pf
), (char *)&pf
) < 0) {
352 log_fatal ("Can't set PFMOD send filter on %s: %m", info
-> name
);
355 # endif /* USE_DLPI_PFMOD */
356 #else /* !defined (USE_DLPI_RECEIVE) */
358 * If using DLPI for both send and receive, simply re-use
359 * the read file descriptor that was set up earlier.
361 info
-> wfdesc
= info
-> rfdesc
;
364 if (!quiet_interface_discovery
)
365 log_info ("Sending on DLPI/%s/%s%s%s",
367 print_hw_addr (info
-> hw_address
.hbuf
[0],
368 info
-> hw_address
.hlen
- 1,
369 &info
-> hw_address
.hbuf
[1]),
370 (info
-> shared_network
? "/" : ""),
371 (info
-> shared_network
?
372 info
-> shared_network
-> name
: ""));
374 #ifdef DLPI_FIRST_SEND_WAIT
375 /* See the implementation notes at the beginning of this file */
376 # ifdef USE_DLPI_RECEIVE
377 sleep (DLPI_FIRST_SEND_WAIT
- (DLPI_FIRST_SEND_WAIT
/ 2));
379 sleep (DLPI_FIRST_SEND_WAIT
);
384 void if_deregister_send (info
)
385 struct interface_info
*info
;
387 /* If we're using the DLPI API for sending and receiving,
388 we don't need to register this interface twice. */
389 #ifndef USE_DLPI_RECEIVE
390 close (info
-> wfdesc
);
394 if (!quiet_interface_discovery
)
395 log_info ("Disabling output on DLPI/%s/%s%s%s",
397 print_hw_addr (info
-> hw_address
.hbuf
[0],
398 info
-> hw_address
.hlen
- 1,
399 &info
-> hw_address
.hbuf
[1]),
400 (info
-> shared_network
? "/" : ""),
401 (info
-> shared_network
?
402 info
-> shared_network
-> name
: ""));
404 #endif /* USE_DLPI_SEND */
406 #ifdef USE_DLPI_RECEIVE
407 /* Packet filter program...
408 XXX Changes to the filter program may require changes to the constant
409 offsets used in if_register_send to patch the NIT program! XXX */
411 void if_register_receive (info
)
412 struct interface_info
*info
;
414 #ifdef USE_DLPI_PFMOD
415 struct packetfilt pf
;
420 /* Open a DLPI device and hang it on this interface... */
421 info
-> rfdesc
= if_register_dlpi (info
);
423 #ifdef USE_DLPI_PFMOD
424 /* Set up the PFMOD filter program. */
425 /* XXX Unlike the BPF filter program, this one won't work if the
426 XXX IP packet is fragmented or if there are options on the IP
431 #if defined (USE_DLPI_RAW)
432 # define ETHER_H_PREFIX (14) /* sizeof (ethernet_header) */
434 * ethertype == ETHERTYPE_IP
437 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHWORD
+ (offset
/ 2);
438 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHLIT
| ENF_CAND
;
439 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = htons (ETHERTYPE_IP
);
441 # define ETHER_H_PREFIX (0)
442 # endif /* USE_DLPI_RAW */
444 * The packets that will be received on this file descriptor
445 * will be IP packets (due to the SAP that was specified in
446 * the dlbind call). There will be no ethernet header.
447 * Therefore, setup the packet filter to check the protocol
448 * field for UDP, and the destination port number equal
449 * to the local port. All offsets are relative to the start
454 * BOOTPS destination port
456 offset
= ETHER_H_PREFIX
+ sizeof (iphdr
) + sizeof (u_int16_t
);
457 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHWORD
+ (offset
/ 2);
458 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHLIT
| ENF_CAND
;
459 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = local_port
;
462 * protocol should be udp. this is a byte compare, test for
465 offset
= ETHER_H_PREFIX
+ ((u_int8_t
*)&(iphdr
.ip_p
) - (u_int8_t
*)&iphdr
);
466 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHWORD
+ (offset
/ 2);
467 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHLIT
| ENF_AND
;
468 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = htons (0x00FF);
469 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = ENF_PUSHLIT
| ENF_CAND
;
470 pf
.Pf_Filter
[pf
.Pf_FilterLen
++] = htons (IPPROTO_UDP
);
472 /* Install the filter... */
473 if (strioctl (info
-> rfdesc
, PFIOCSETF
, INFTIM
,
474 sizeof (pf
), (char *)&pf
) < 0) {
475 log_fatal ("Can't set PFMOD receive filter on %s: %m", info
-> name
);
477 #endif /* USE_DLPI_PFMOD */
479 if (!quiet_interface_discovery
)
480 log_info ("Listening on DLPI/%s/%s%s%s",
482 print_hw_addr (info
-> hw_address
.hbuf
[0],
483 info
-> hw_address
.hlen
- 1,
484 &info
-> hw_address
.hbuf
[1]),
485 (info
-> shared_network
? "/" : ""),
486 (info
-> shared_network
?
487 info
-> shared_network
-> name
: ""));
489 #ifdef DLPI_FIRST_SEND_WAIT
490 /* See the implementation notes at the beginning of this file */
491 # ifdef USE_DLPI_SEND
492 sleep (DLPI_FIRST_SEND_WAIT
/ 2);
494 sleep (DLPI_FIRST_SEND_WAIT
);
499 void if_deregister_receive (info
)
500 struct interface_info
*info
;
502 /* If we're using the DLPI API for sending and receiving,
503 we don't need to register this interface twice. */
504 #ifndef USE_DLPI_SEND
505 close (info
-> rfdesc
);
509 if (!quiet_interface_discovery
)
510 log_info ("Disabling input on DLPI/%s/%s%s%s",
512 print_hw_addr (info
-> hw_address
.hbuf
[0],
513 info
-> hw_address
.hlen
- 1,
514 &info
-> hw_address
.hbuf
[1]),
515 (info
-> shared_network
? "/" : ""),
516 (info
-> shared_network
?
517 info
-> shared_network
-> name
: ""));
519 #endif /* USE_DLPI_RECEIVE */
522 ssize_t
send_packet (interface
, packet
, raw
, len
, from
, to
, hto
)
523 struct interface_info
*interface
;
524 struct packet
*packet
;
525 struct dhcp_packet
*raw
;
528 struct sockaddr_in
*to
;
529 struct hardware
*hto
;
535 double ih
[1536 / sizeof (double)];
536 unsigned char *dbuf
= (unsigned char *)ih
;
538 unsigned char dstaddr
[DLPI_MAXDLADDR
];
542 if (!strcmp (interface
-> name
, "fallback"))
543 return send_fallback (interface
, packet
, raw
,
546 if (hto
== NULL
&& interface
->anycast_mac_addr
.hlen
)
547 hto
= &interface
->anycast_mac_addr
;
551 /* Assemble the headers... */
553 assemble_hw_header (interface
, (unsigned char *)hh
, &dbuflen
, hto
);
554 if (dbuflen
> sizeof hh
)
555 log_fatal ("send_packet: hh buffer too small.\n");
556 fudge
= dbuflen
% 4; /* IP header must be word-aligned. */
557 memcpy (dbuf
+ fudge
, (unsigned char *)hh
, dbuflen
);
560 assemble_udp_ip_header (interface
, dbuf
, &dbuflen
, from
.s_addr
,
561 to
-> sin_addr
.s_addr
, to
-> sin_port
,
562 (unsigned char *)raw
, len
);
564 /* Copy the data into the buffer (yuk). */
565 memcpy (dbuf
+ dbuflen
, raw
, len
);
569 result
= write (interface
-> wfdesc
, dbuf
+ fudge
, dbuflen
- fudge
);
573 * Setup the destination address (DLSAP) in dstaddr
575 * If sap_length < 0 we must deliver the DLSAP as phys+sap.
576 * If sap_length > 0 we must deliver the DLSAP as sap+phys.
578 * sap = Service Access Point == ETHERTYPE_IP
579 * sap + datalink address is called DLSAP in dlpi speak.
582 unsigned char phys
[DLPI_MAXDLADDR
];
583 unsigned char sap
[4];
584 int sap_len
= interface
-> dlpi_sap_length
;
585 int phys_len
= interface
-> hw_address
.hlen
- 1;
587 /* sap = htons (ETHERTYPE_IP) kludge */
588 memset (sap
, 0, sizeof (sap
));
589 # if (BYTE_ORDER == LITTLE_ENDIAN)
597 if (hto
&& hto
-> hlen
== interface
-> hw_address
.hlen
)
598 memcpy ( phys
, (char *) &hto
-> hbuf
[1], phys_len
);
600 memcpy ( phys
, interface
-> dlpi_broadcast_addr
.hbuf
,
601 interface
-> dlpi_broadcast_addr
.hlen
);
604 memcpy ( dstaddr
, phys
, phys_len
);
605 memcpy ( (char *) &dstaddr
[phys_len
], sap
, ABS (sap_len
));
608 memcpy ( dstaddr
, (void *) sap
, sap_len
);
609 memcpy ( (char *) &dstaddr
[sap_len
], phys
, phys_len
);
611 addrlen
= phys_len
+ ABS (sap_len
);
614 result
= dlpiunitdatareq (interface
-> wfdesc
, dstaddr
, addrlen
,
615 0, 0, dbuf
, dbuflen
);
616 #endif /* USE_DLPI_RAW */
618 log_error ("send_packet: %m");
621 #endif /* USE_DLPI_SEND */
623 #ifdef USE_DLPI_RECEIVE
624 ssize_t
receive_packet (interface
, buf
, len
, from
, hfrom
)
625 struct interface_info
*interface
;
628 struct sockaddr_in
*from
;
629 struct hardware
*hfrom
;
631 unsigned char dbuf
[1536];
632 unsigned char srcaddr
[DLPI_MAXDLADDR
];
633 unsigned long srcaddrlen
;
640 length
= read (interface
-> rfdesc
, dbuf
, sizeof (dbuf
));
642 length
= dlpiunitdataind (interface
-> rfdesc
, (unsigned char *)NULL
,
643 (unsigned long *)NULL
, srcaddr
, &srcaddrlen
,
644 (unsigned long *)NULL
, dbuf
, sizeof (dbuf
));
648 log_error("receive_packet: %m");
652 # if !defined (USE_DLPI_RAW)
654 * Copy the sender's hw address into hfrom
655 * If sap_len < 0 the DLSAP is as phys+sap.
656 * If sap_len > 0 the DLSAP is as sap+phys.
658 * sap is discarded here.
661 int sap_len
= interface
-> dlpi_sap_length
;
662 int phys_len
= interface
-> hw_address
.hlen
- 1;
664 if (hfrom
&& (srcaddrlen
== ABS (sap_len
) + phys_len
)) {
665 hfrom
-> hbuf
[0] = interface
-> hw_address
.hbuf
[0];
666 hfrom
-> hlen
= interface
-> hw_address
.hlen
;
669 memcpy ((char *) &hfrom
-> hbuf
[1], srcaddr
, phys_len
);
672 memcpy((char *)&hfrom
->hbuf
[1], srcaddr
+ sap_len
, phys_len
);
676 memset (hfrom
, '\0', sizeof *hfrom
);
680 # endif /* !defined (USE_DLPI_RAW) */
682 /* Decode the IP and UDP headers... */
685 /* Decode the physical header... */
686 offset
= decode_hw_header (interface
, dbuf
, bufix
, hfrom
);
688 /* If a physical layer checksum failed (dunno of any
689 physical layer that supports this, but WTH), skip this
697 offset
= decode_udp_ip_header (interface
, dbuf
, bufix
,
698 from
, length
, &paylen
);
701 * If the IP or UDP checksum was bad, skip the packet...
703 * Note: this happens all the time when writing packets via the
704 * fallback socket. The packet received by streams does not have
705 * the IP or UDP checksums filled in, as those are calculated by
716 log_fatal("Internal inconsistency at %s:%d.", MDL
);
718 /* Copy out the data in the packet... */
719 memcpy(buf
, &dbuf
[bufix
], paylen
);
724 /* Common DLPI routines ...
726 * Written by Eric James Negaard, <lmdejn@lmd.ericsson.se>
728 * Based largely in part to the example code contained in the document
729 * "How to Use the STREAMS Data Link Provider Interface (DLPI)", written
730 * by Neal Nuckolls of SunSoft Internet Engineering.
732 * This code has been developed and tested on sparc-based machines running
733 * SunOS 5.5.1, with le and hme network interfaces. It should be pretty
736 * The usual disclaimers apply. This code works for me. Don't blame me
737 * if it makes your machine or network go down in flames. That taken
738 * into consideration, use this code as you wish. If you make usefull
739 * modifications I'd appreciate hearing about it.
742 #define DLPI_MAXWAIT 15 /* Max timeout */
746 * Parse an interface name and extract the unit number
749 static int dlpiunit (ifname
)
759 /* Advance to the end of the name */
762 /* Back up to the start of the first digit */
763 while ((*(cp
-1) >= '0' && *(cp
-1) <= '9') || *(cp
- 1) == ':') cp
--;
765 /* Convert the unit number */
767 while (*cp
>= '0' && *cp
<= '9') {
769 unit
+= (*cp
++ - '0');
776 * dlpiopen - open the DLPI device for a given interface name
779 dlpiopen(const char *ifname
) {
788 /* Open a DLPI device */
789 if (*ifname
== '/') {
792 /* Prepend the device directory */
793 memcpy (devname
, DLPI_DEVDIR
, strlen (DLPI_DEVDIR
));
794 dp
= &devname
[strlen (DLPI_DEVDIR
)];
797 /* Find the end of the interface name */
801 /* And back up to the first digit (unit number) */
802 while ((*(ep
- 1) >= '0' && *(ep
- 1) <= '9') || *(ep
- 1) == ':')
805 /* Copy everything up to the unit number */
811 return open (devname
, O_RDWR
, 0);
815 * dlpiinforeq - request information about the data link provider.
818 static int dlpiinforeq (fd
)
821 dl_info_req_t info_req
;
825 info_req
.dl_primitive
= DL_INFO_REQ
;
828 ctl
.len
= sizeof (info_req
);
829 ctl
.buf
= (char *)&info_req
;
833 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
837 * dlpiphysaddrreq - request the current physical address.
839 static int dlpiphysaddrreq (fd
, addrtype
)
841 unsigned long addrtype
;
843 dl_phys_addr_req_t physaddr_req
;
847 physaddr_req
.dl_primitive
= DL_PHYS_ADDR_REQ
;
848 physaddr_req
.dl_addr_type
= addrtype
;
851 ctl
.len
= sizeof (physaddr_req
);
852 ctl
.buf
= (char *)&physaddr_req
;
856 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
860 * dlpiattachreq - send a request to attach to a specific unit.
862 static int dlpiattachreq (fd
, ppa
)
866 dl_attach_req_t attach_req
;
870 attach_req
.dl_primitive
= DL_ATTACH_REQ
;
871 attach_req
.dl_ppa
= ppa
;
874 ctl
.len
= sizeof (attach_req
);
875 ctl
.buf
= (char *)&attach_req
;
879 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
883 * dlpibindreq - send a request to bind to a specific SAP address.
885 static int dlpibindreq (fd
, sap
, max_conind
, service_mode
, conn_mgmt
, xidtest
)
887 unsigned long max_conind
;
888 unsigned long service_mode
;
889 unsigned long conn_mgmt
;
890 unsigned long xidtest
;
893 dl_bind_req_t bind_req
;
897 bind_req
.dl_primitive
= DL_BIND_REQ
;
898 bind_req
.dl_sap
= sap
;
899 bind_req
.dl_max_conind
= max_conind
;
900 bind_req
.dl_service_mode
= service_mode
;
901 bind_req
.dl_conn_mgmt
= conn_mgmt
;
902 bind_req
.dl_xidtest_flg
= xidtest
;
905 ctl
.len
= sizeof (bind_req
);
906 ctl
.buf
= (char *)&bind_req
;
910 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
913 #if defined(UNUSED_DLPI_INTERFACE)
915 * dlpiunbindreq - send a request to unbind. This function is not actually
916 * used by ISC DHCP, but is included for completeness in case it is
917 * ever required for new work.
919 static int dlpiunbindreq (fd
)
922 dl_unbind_req_t unbind_req
;
926 unbind_req
.dl_primitive
= DL_UNBIND_REQ
;
929 ctl
.len
= sizeof (unbind_req
);
930 ctl
.buf
= (char *)&unbind_req
;
934 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
939 * dlpidetachreq - send a request to detach. This function is not actually
940 * used by ISC DHCP, but is included for completeness in case it is
941 * ever required for new work.
943 static int dlpidetachreq (fd
)
946 dl_detach_req_t detach_req
;
950 detach_req
.dl_primitive
= DL_DETACH_REQ
;
953 ctl
.len
= sizeof (detach_req
);
954 ctl
.buf
= (char *)&detach_req
;
958 return putmsg (fd
, &ctl
, (struct strbuf
*)NULL
, flags
);
960 #endif /* UNUSED_DLPI_INTERFACE */
964 * dlpibindack - receive an ack to a dlbindreq.
966 static int dlpibindack (fd
, bufp
)
970 union DL_primitives
*dlp
;
974 ctl
.maxlen
= DLPI_MAXDLBUF
;
978 if (strgetmsg (fd
, &ctl
,
979 (struct strbuf
*)NULL
, &flags
, "dlpibindack") < 0) {
983 dlp
= (union DL_primitives
*)ctl
.buf
;
985 if (expected (DL_BIND_ACK
, dlp
, flags
) == -1) {
989 if (ctl
.len
< sizeof (dl_bind_ack_t
)) {
990 /* Returned structure is too short */
998 * dlpiokack - general acknowledgement reception.
1000 static int dlpiokack (fd
, bufp
)
1004 union DL_primitives
*dlp
;
1008 ctl
.maxlen
= DLPI_MAXDLBUF
;
1012 if (strgetmsg (fd
, &ctl
,
1013 (struct strbuf
*)NULL
, &flags
, "dlpiokack") < 0) {
1017 dlp
= (union DL_primitives
*)ctl
.buf
;
1019 if (expected (DL_OK_ACK
, dlp
, flags
) == -1) {
1023 if (ctl
.len
< sizeof (dl_ok_ack_t
)) {
1024 /* Returned structure is too short */
1032 * dlpiinfoack - receive an ack to a dlinforeq.
1034 static int dlpiinfoack (fd
, bufp
)
1038 union DL_primitives
*dlp
;
1042 ctl
.maxlen
= DLPI_MAXDLBUF
;
1046 if (strgetmsg (fd
, &ctl
, (struct strbuf
*)NULL
, &flags
,
1047 "dlpiinfoack") < 0) {
1051 dlp
= (union DL_primitives
*) ctl
.buf
;
1053 if (expected (DL_INFO_ACK
, dlp
, flags
) == -1) {
1057 if (ctl
.len
< sizeof (dl_info_ack_t
)) {
1058 /* Returned structure is too short */
1066 * dlpiphysaddrack - receive an ack to a dlpiphysaddrreq.
1068 int dlpiphysaddrack (fd
, bufp
)
1072 union DL_primitives
*dlp
;
1076 ctl
.maxlen
= DLPI_MAXDLBUF
;
1080 if (strgetmsg (fd
, &ctl
, (struct strbuf
*)NULL
, &flags
,
1081 "dlpiphysaddrack") < 0) {
1085 dlp
= (union DL_primitives
*)ctl
.buf
;
1087 if (expected (DL_PHYS_ADDR_ACK
, dlp
, flags
) == -1) {
1091 if (ctl
.len
< sizeof (dl_phys_addr_ack_t
)) {
1092 /* Returned structure is too short */
1099 #if defined(USE_DLPI_SEND) || defined(USE_DLPI_RECEIVE)
1100 int dlpiunitdatareq (fd
, addr
, addrlen
, minpri
, maxpri
, dbuf
, dbuflen
)
1102 unsigned char *addr
;
1104 unsigned long minpri
;
1105 unsigned long maxpri
;
1106 unsigned char *dbuf
;
1109 long buf
[DLPI_MAXDLBUF
];
1110 union DL_primitives
*dlp
;
1111 struct strbuf ctl
, data
;
1113 /* Set up the control information... */
1114 dlp
= (union DL_primitives
*)buf
;
1115 dlp
-> unitdata_req
.dl_primitive
= DL_UNITDATA_REQ
;
1116 dlp
-> unitdata_req
.dl_dest_addr_length
= addrlen
;
1117 dlp
-> unitdata_req
.dl_dest_addr_offset
= sizeof (dl_unitdata_req_t
);
1118 dlp
-> unitdata_req
.dl_priority
.dl_min
= minpri
;
1119 dlp
-> unitdata_req
.dl_priority
.dl_max
= maxpri
;
1121 /* Append the destination address */
1122 memcpy ((char *)buf
+ dlp
-> unitdata_req
.dl_dest_addr_offset
,
1126 ctl
.len
= dlp
-> unitdata_req
.dl_dest_addr_offset
+ addrlen
;
1127 ctl
.buf
= (char *)buf
;
1130 data
.buf
= (char *)dbuf
;
1133 /* Send the packet down the wire... */
1134 return putmsg (fd
, &ctl
, &data
, 0);
1137 static int dlpiunitdataind (fd
, daddr
, daddrlen
,
1138 saddr
, saddrlen
, grpaddr
, dbuf
, dlen
)
1140 unsigned char *daddr
;
1141 unsigned long *daddrlen
;
1142 unsigned char *saddr
;
1143 unsigned long *saddrlen
;
1144 unsigned long *grpaddr
;
1145 unsigned char *dbuf
;
1148 long buf
[DLPI_MAXDLBUF
];
1149 union DL_primitives
*dlp
;
1150 struct strbuf ctl
, data
;
1154 /* Set up the msg_buf structure... */
1155 dlp
= (union DL_primitives
*)buf
;
1156 dlp
-> unitdata_ind
.dl_primitive
= DL_UNITDATA_IND
;
1158 ctl
.maxlen
= DLPI_MAXDLBUF
;
1160 ctl
.buf
= (char *)buf
;
1164 data
.buf
= (char *)dbuf
;
1166 result
= getmsg (fd
, &ctl
, &data
, &flags
);
1169 log_debug("dlpiunitdataind: %m");
1173 if (ctl
.len
< sizeof (dl_unitdata_ind_t
) ||
1174 dlp
-> unitdata_ind
.dl_primitive
!= DL_UNITDATA_IND
) {
1178 if (data
.len
<= 0) {
1182 /* Copy sender info */
1185 (char *)buf
+ dlp
-> unitdata_ind
.dl_src_addr_offset
,
1186 dlp
-> unitdata_ind
.dl_src_addr_length
);
1189 *saddrlen
= dlp
-> unitdata_ind
.dl_src_addr_length
;
1192 /* Copy destination info */
1195 (char *)buf
+ dlp
-> unitdata_ind
.dl_dest_addr_offset
,
1196 dlp
-> unitdata_ind
.dl_dest_addr_length
);
1199 *daddrlen
= dlp
-> unitdata_ind
.dl_dest_addr_length
;
1203 *grpaddr
= dlp
-> unitdata_ind
.dl_group_address
;
1208 #endif /* !USE_DLPI_HWADDR: USE_DLPI_RECEIVE || USE_DLPI_SEND */
1211 * expected - see if we got what we wanted.
1213 static int expected (prim
, dlp
, msgflags
)
1215 union DL_primitives
*dlp
;
1218 if (msgflags
!= RS_HIPRI
) {
1219 /* Message was not M_PCPROTO */
1223 if (dlp
->dl_primitive
!= prim
) {
1224 /* Incorrect/unexpected return message */
1232 * strgetmsg - get a message from a stream, with timeout.
1234 static int strgetmsg (fd
, ctlp
, datap
, flagsp
, caller
)
1235 struct strbuf
*ctlp
, *datap
;
1248 pfd
.events
= POLLPRI
; /* We're only interested in knowing
1249 * when we can receive the next high
1254 now
= time (&starttime
);
1255 while (now
<= starttime
+ DLPI_MAXWAIT
) {
1256 to_msec
= ((starttime
+ DLPI_MAXWAIT
) - now
) * 1000;
1257 count
= poll (&pfd
, 1, to_msec
);
1260 /* log_fatal ("strgetmsg: timeout"); */
1262 } else if (count
< 0) {
1263 if (errno
== EAGAIN
|| errno
== EINTR
) {
1267 /* log_fatal ("poll: %m"); */
1276 * Set flags argument and issue getmsg ().
1279 if ((result
= getmsg (fd
, ctlp
, datap
, flagsp
)) < 0) {
1284 * Check for MOREDATA and/or MORECTL.
1286 if (result
& (MORECTL
|MOREDATA
)) {
1291 * Check for at least sizeof (long) control data portion.
1293 if (ctlp
-> len
< sizeof (long)) {
1300 #if defined(USE_DLPI_SEND)
1301 int can_unicast_without_arp (ip
)
1302 struct interface_info
*ip
;
1307 int can_receive_unicast_unconfigured (ip
)
1308 struct interface_info
*ip
;
1313 int supports_multiple_interfaces (ip
)
1314 struct interface_info
*ip
;
1319 void maybe_setup_fallback ()
1321 isc_result_t status
;
1322 struct interface_info
*fbi
= (struct interface_info
*)0;
1323 if (setup_fallback (&fbi
, MDL
)) {
1324 if_register_fallback (fbi
);
1325 status
= omapi_register_io_object ((omapi_object_t
*)fbi
,
1327 fallback_discard
, 0, 0);
1328 if (status
!= ISC_R_SUCCESS
)
1329 log_fatal ("Can't register I/O handle for %s: %s",
1330 fbi
-> name
, isc_result_totext (status
));
1331 interface_dereference (&fbi
, MDL
);
1334 #endif /* USE_DLPI_SEND */
1337 get_hw_addr(const char *name
, struct hardware
*hw
) {
1339 long buf
[DLPI_MAXDLBUF
];
1340 union DL_primitives
*dlp
;
1342 dlp
= (union DL_primitives
*)buf
;
1345 * Open a DLPI device.
1347 sock
= dlpiopen(name
);
1349 log_fatal("Can't open DLPI device for %s: %m", name
);
1353 * Submit a DL_INFO_REQ request, to find the dl_mac_type and
1356 if (dlpiinforeq(sock
) < 0) {
1357 log_fatal("Can't request DLPI MAC type for %s: %m", name
);
1359 if (dlpiinfoack(sock
, (char *)buf
) < 0) {
1360 log_fatal("Can't get DLPI MAC type for %s: %m", name
);
1362 switch (dlp
->info_ack
.dl_mac_type
) {
1363 case DL_CSMACD
: /* IEEE 802.3 */
1365 hw
->hbuf
[0] = HTYPE_ETHER
;
1368 hw
->hbuf
[0] = HTYPE_IEEE802
;
1371 hw
->hbuf
[0] = HTYPE_FDDI
;
1374 log_fatal("%s: unsupported DLPI MAC type %lu", name
,
1375 (unsigned long)dlp
->info_ack
.dl_mac_type
);
1378 if (dlp
->info_ack
.dl_provider_style
== DL_STYLE2
) {
1380 * Attach to the device. If this fails, the device
1383 unit
= dlpiunit((char *)name
);
1385 if (dlpiattachreq(sock
, unit
) < 0 ||
1386 dlpiokack(sock
, (char *)buf
) < 0) {
1387 log_fatal("Can't attach DLPI device for %s: %m",
1393 * Submit a DL_PHYS_ADDR_REQ request, to find
1394 * the hardware address.
1396 if (dlpiphysaddrreq(sock
, DL_CURR_PHYS_ADDR
) < 0) {
1397 log_fatal("Can't request DLPI hardware address for %s: %m",
1400 if (dlpiphysaddrack(sock
, (char *)buf
) < 0) {
1401 log_fatal("Can't get DLPI hardware address for %s: %m",
1404 if (dlp
->physaddr_ack
.dl_addr_length
< sizeof(hw
->hbuf
)) {
1406 (char *)buf
+ dlp
->physaddr_ack
.dl_addr_offset
,
1407 dlp
->physaddr_ack
.dl_addr_length
);
1408 hw
->hlen
= dlp
->physaddr_ack
.dl_addr_length
+ 1;
1411 (char *)buf
+ dlp
->physaddr_ack
.dl_addr_offset
,
1412 sizeof(hw
->hbuf
)-1);
1413 hw
->hlen
= sizeof(hw
->hbuf
);
1418 #endif /* USE_DLPI_SEND || USE_DLPI_RECEIVE || USE_DLPI_HWADDR */