* $Id: libnet_link_dlpi.c,v 1.19 2005/11/29 22:43:19 carlosc Exp $
* libnet_dlpi.c - dlpi routines
* Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
* Copyright (c) 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* This code contributed by Atanu Ghosh (atanu@cs.ucl.ac.uk),
* University College London.
#include "../include/config.h"
#if defined(HAVE_SOLARIS) && defined(HAVE_SYS_BUFMOD_H)
#include <sys/systeminfo.h>
#ifdef HAVE_SYS_DLPI_EXT_H
#include <sys/dlpi_ext.h>
#include "../include/libnet.h"
#include "../include/bpf.h"
#include "../include/gnuc.h"
#include "../include/os-proto.h"
#define DLPI_DEV_PREFIX "/dev"
static int dlattachreq(int, bpf_u_int32
, int8_t *);
static int dlbindack(int, int8_t *, int8_t *);
static int dlbindreq(int, bpf_u_int32
, int8_t *);
static int dlinfoack(int, int8_t *, int8_t *);
static int dlinforeq(int, int8_t *);
static int dlokack(int, const int8_t *, int8_t *, int8_t *);
static int recv_ack(int, int, const int8_t *, int8_t *, int8_t *);
static int send_request(int, int8_t *, int, int8_t *, int8_t *, int);
static int strioctl(int, int, int, int8_t *);
static int dlpi_kread(int, off_t
, void *, u_int
, int8_t *);
static int get_dlpi_ppa(int, const int8_t *, int, int8_t *);
/* XXX Needed by HP-UX (at least) */
static bpf_u_int32 ctlbuf
[MAXDLBUF
];
libnet_open_link(libnet_t
*l
)
register dl_info_ack_t
*infop
;
bpf_u_int32 buf
[MAXDLBUF
];
* Determine device and ppa
cp
= strpbrk(l
->device
, "0123456789");
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
,
"%s(): %s is missing unit number\n", __func__
, l
->device
);
ppa
= strtol(cp
, &eos
, 10);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
,
"%s(): %s bad unit number\n", __func__
, l
->device
);
memset(&dname
, 0, sizeof(dname
));
strncpy(dname
, l
->device
, sizeof(dname
) - 1);
dname
[sizeof(dname
) - 1] = '\0';
sprintf(dname
, "%s/%s", DLPI_DEV_PREFIX
, l
->device
);
* Map network device to /dev/dlpi unit
l
->fd
= open(cp
, O_RDWR
);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
, "%s(): open(): %s: %s\n",
__func__
, cp
, strerror(errno
));
* Map network interface to /dev/dlpi unit
ppa
= get_dlpi_ppa(l
->fd
, dname
, ppa
, l
->err_buf
);
* Try device without unit number
l
->fd
= open(dname
, O_RDWR
);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
, "%s(): open(): %s: %s\n",
__func__
, dname
, strerror(errno
));
* Try again with unit number
l
->fd
= open(dname2
, O_RDWR
);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
, "%s(): open(): %s: %s\n",
__func__
, dname2
, strerror(errno
));
while (*cp
&& !isdigit((int)*cp
))
* Attach if "style 2" provider
if (dlinforeq(l
->fd
, l
->err_buf
) < 0 ||
dlinfoack(l
->fd
, (int8_t *)buf
, l
->err_buf
) < 0)
infop
= &((union DL_primitives
*)buf
)->info_ack
;
if (infop
->dl_provider_style
== DL_STYLE2
&&
(dlattachreq(l
->fd
, ppa
, l
->err_buf
)
< 0 || dlokack(l
->fd
, "attach", (int8_t *)buf
, l
->err_buf
) < 0))
* Bind HP-UX 9 and HP-UX 10.20
#if defined(HAVE_HPUX9) || defined(HAVE_HPUX10_20) || defined(HAVE_HPUX11) || defined(HAVE_SOLARIS)
if (dlbindreq(l
->fd
, 0, l
->err_buf
) < 0 ||
dlbindack(l
->fd
, (int8_t *)buf
, l
->err_buf
) < 0)
if (dlinforeq(l
->fd
, l
->err_buf
) < 0 ||
dlinfoack(l
->fd
, (int8_t *)buf
, l
->err_buf
) < 0)
infop
= &((union DL_primitives
*)buf
)->info_ack
;
switch (infop
->dl_mac_type
)
l
->link_type
= DLT_EN10MB
;
l
->link_type
= DLT_PRONET
;
sprintf(l
->err_buf
, "%s(): unknown mac type 0x%lu\n", __func__
,
(u_int32_t
) infop
->dl_mac_type
);
* This is a non standard SunOS hack to get the ethernet header.
if (strioctl(l
->fd
, DLIOCRAW
, 0, NULL
) < 0)
sprintf(l
->err_buf
, "%s(): DLIOCRAW: %s\n", __func__
, strerror(errno
));
close(l
->fd
); /* this can fail ok */
send_request(int fd
, int8_t *ptr
, int len
, int8_t *what
, int8_t *ebuf
,
if (putmsg(fd
, &ctl
, (struct strbuf
*) NULL
, flags
) < 0)
sprintf(ebuf
, "%s(): putmsg \"%s\": %s\n", __func__
, what
,
recv_ack(int fd
, int size
, const int8_t *what
, int8_t *bufp
, int8_t *ebuf
)
union DL_primitives
*dlp
;
if (getmsg(fd
, &ctl
, (struct strbuf
*)NULL
, &flags
) < 0)
sprintf(ebuf
, "%s(): %s getmsg: %s\n", __func__
, what
, strerror(errno
));
dlp
= (union DL_primitives
*)ctl
.buf
;
switch (dlp
->dl_primitive
)
switch (dlp
->error_ack
.dl_errno
)
sprintf(ebuf
, "recv_ack: %s bad ppa (device unit)", what
);
sprintf(ebuf
, "recv_ack: %s: %s",
what
, strerror(dlp
->error_ack
.dl_unix_errno
));
"recv_ack: %s: Service not supplied by provider", what
);
sprintf(ebuf
, "recv_ack: %s error 0x%x", what
,
(bpf_u_int32
)dlp
->error_ack
.dl_errno
);
sprintf(ebuf
, "recv_ack: %s unexpected primitive ack 0x%x ",
what
, (bpf_u_int32
)dlp
->dl_primitive
);
sprintf(ebuf
, "recv_ack: %s ack too small (%d < %d)",
dlattachreq(int fd
, bpf_u_int32 ppa
, int8_t *ebuf
)
req
.dl_primitive
= DL_ATTACH_REQ
;
return (send_request(fd
, (int8_t *)&req
, sizeof(req
), "attach", ebuf
, 0));
dlbindreq(int fd
, bpf_u_int32 sap
, int8_t *ebuf
)
memset((int8_t *)&req
, 0, sizeof(req
));
req
.dl_primitive
= DL_BIND_REQ
;
req
.dl_max_conind
= 1; /* XXX magic number */
* 22 is INSAP as per the HP-UX DLPI Programmer's Guide
req
.dl_service_mode
= DL_HP_RAWDLS
;
req
.dl_service_mode
= DL_CLDLS
;
return (send_request(fd
, (int8_t *)&req
, sizeof(req
), "bind", ebuf
, 0));
dlbindack(int fd
, int8_t *bufp
, int8_t *ebuf
)
return (recv_ack(fd
, DL_BIND_ACK_SIZE
, "bind", bufp
, ebuf
));
dlokack(int fd
, const int8_t *what
, int8_t *bufp
, int8_t *ebuf
)
return (recv_ack(fd
, DL_OK_ACK_SIZE
, what
, bufp
, ebuf
));
dlinforeq(int fd
, int8_t *ebuf
)
req
.dl_primitive
= DL_INFO_REQ
;
return (send_request(fd
, (int8_t *)&req
, sizeof(req
), "info", ebuf
,
dlinfoack(int fd
, int8_t *bufp
, int8_t *ebuf
)
return (recv_ack(fd
, DL_INFO_ACK_SIZE
, "info", bufp
, ebuf
));
strioctl(int fd
, int cmd
, int len
, int8_t *dp
)
rc
= ioctl(fd
, I_STR
, &str
);
#if (defined(DL_HP_PPA_ACK_OBS) && !defined(HAVE_HPUX11))
* Under HP-UX 10, we can ask for the ppa
get_dlpi_ppa(register int fd
, register const int8_t *device
, register int unit
,
register dl_hp_ppa_ack_t
*ap
;
register dl_hp_ppa_info_t
*ip
;
register u_int32_t majdev
;
bpf_u_int32 buf
[MAXDLBUF
];
if (stat(device
, &statbuf
) < 0)
sprintf(ebuf
, "stat: %s: %s", device
, strerror(errno
));
majdev
= major(statbuf
.st_rdev
);
memset((int8_t *)&req
, 0, sizeof(req
));
req
.dl_primitive
= DL_HP_PPA_REQ
;
memset((int8_t *)buf
, 0, sizeof(buf
));
if (send_request(fd
, (int8_t *)&req
, sizeof(req
), "hpppa", ebuf
, 0) < 0 ||
recv_ack(fd
, DL_HP_PPA_ACK_SIZE
, "hpppa", (int8_t *)buf
, ebuf
) < 0)
ap
= (dl_hp_ppa_ack_t
*)buf
;
ip
= (dl_hp_ppa_info_t
*)((u_int8_t
*)ap
+ ap
->dl_offset
);
for (i
= 0; i
< ap
->dl_count
; i
++)
if (ip
->dl_mjr_num
== majdev
&& ip
->dl_instance_num
== unit
)
ip
= (dl_hp_ppa_info_t
*)((u_int8_t
*)ip
+ ip
->dl_next_offset
);
sprintf(ebuf
, "can't find PPA for %s", device
);
if (ip
->dl_hdw_state
== HDW_DEAD
)
sprintf(ebuf
, "%s: hardware state: DOWN\n", device
);
return ((int)ip
->dl_ppa
);
* Under HP-UX 9, there is no good way to determine the ppa.
* So punt and read it from /dev/kmem.
static struct nlist nl
[] =
static int8_t path_vmunix
[] = "/hp-ux";
* Determine ppa number that specifies ifname
get_dlpi_ppa(register int fd
, register const int8_t *ifname
, register int unit
,
register const int8_t *cp
;
int8_t if_name
[sizeof(ifnet
.if_name
)], tifname
[32];
cp
= strrchr(ifname
, '/');
if (nlist(path_vmunix
, &nl
) < 0)
sprintf(ebuf
, "nlist %s failed", path_vmunix
);
if (nl
[NL_IFNET
].n_value
== 0)
sprintf(ebuf
, "could't find %s kernel symbol", nl
[NL_IFNET
].n_name
);
kd
= open("/dev/kmem", O_RDONLY
);
sprintf(ebuf
, "kmem open: %s", strerror(errno
));
if (dlpi_kread(kd
, nl
[NL_IFNET
].n_value
, &addr
, sizeof(addr
), ebuf
) < 0)
for (; addr
!= NULL
; addr
= ifnet
.if_next
)
if (dlpi_kread(kd
, (off_t
)addr
, &ifnet
, sizeof(ifnet
), ebuf
) < 0 ||
dlpi_kread(kd
, (off_t
)ifnet
.if_name
,
if_name
, sizeof(if_name
), ebuf
) < 0)
sprintf(tifname
, "%.*s%d",
(int)sizeof(if_name
), if_name
, ifnet
.if_unit
);
if (strcmp(tifname
, ifname
) == 0)
sprintf(ebuf
, "Can't find %s", ifname
);
dlpi_kread(register int fd
, register off_t addr
, register void *buf
,
register u_int len
, register int8_t *ebuf
)
if (lseek(fd
, addr
, SEEK_SET
) < 0)
sprintf(ebuf
, "lseek: %s", strerror(errno
));
sprintf(ebuf
, "read: %s", strerror(errno
));
sprintf(ebuf
, "int16_t read (%d != %d)", cc
, len
);
struct libnet_ether_addr DestEtherAddr
;
u_int16_t EtherFrameType
;
libnet_close_link(libnet_t
*l
)
libnet_write_link(libnet_t
*l
, u_int8_t
*packet
, u_int32_t size
)
dl_hp_rawdata_req_t
*rdata
;
rdata
= (dl_hp_rawdata_req_t
*)ctlbuf
;
rdata
->dl_primitive
= DL_HP_RAWDATA_REQ
;
ctl
.len
= sizeof(dl_hp_rawdata_req_t
);
ctl
.maxlen
= sizeof(dl_hp_rawdata_req_t
);
ctl
.buf
= (int8_t *)rdata
;
c
= putmsg(l
->fd
, &ctl
, &data
, 0);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
,
"libnet_write_link(): %d bytes written (%s)\n", c
,
libnet_write_link(libnet_t
*l
, u_int8_t
*packet
, u_int32_t size
)
c
= putmsg(l
->fd
, NULL
, &data
, 0);
snprintf(l
->err_buf
, LIBNET_ERRBUF_SIZE
,
"libnet_write_link: %d bytes written (%s)\n", c
,
struct libnet_ether_addr
*
libnet_get_hwaddr(libnet_t
*l
)
union DL_primitives
*dlp
;
struct libnet_ether_addr
*eap
;
dlp
= (union DL_primitives
*)buf
;
dlp
->physaddr_req
.dl_primitive
= DL_PHYS_ADDR_REQ
;
dlp
->physaddr_req
.dl_addr_type
= DL_CURR_PHYS_ADDR
;
if (send_request(l
->fd
, (int8_t *)dlp
, DL_PHYS_ADDR_REQ_SIZE
, "physaddr",
if (recv_ack(l
->fd
, DL_PHYS_ADDR_ACK_SIZE
, "physaddr", (int8_t *)dlp
,
eap
= (struct libnet_ether_addr
*)
((int8_t *) dlp
+ dlp
->physaddr_ack
.dl_addr_offset
);