Ventoy/VBLADE/vblade-master/freebsd.c
2020-04-05 00:08:01 +08:00

314 lines
5.8 KiB
C

/*
* Copyright (c) 2005, Stacey Son <sson (at) verio (dot) net>
* All rights reserved.
*/
// freebsd.c: low level access routines for FreeBSD
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <net/if.h>
#include <sys/stat.h>
#include <sys/disk.h>
#include <sys/select.h>
#include <sys/sysctl.h>
#include <fcntl.h>
#include <errno.h>
#include "dat.h"
#include "fns.h"
#define BPF_DEV "/dev/bpf0"
/* Packet buffer for getpkt() */
static uchar *pktbuf = NULL;
static int pktbufsz = 0;
int
dial(char *eth, int bufcnt)
{
char m;
int fd = -1;
struct bpf_version bv;
u_int v;
unsigned bufsize, linktype;
char device[sizeof BPF_DEV];
struct ifreq ifr;
struct bpf_program *bpf_program = create_bpf_program(shelf, slot);
strncpy(device, BPF_DEV, sizeof BPF_DEV);
/* find a bpf device we can use, check /dev/bpf[0-9] */
for (m = '0'; m <= '9'; m++) {
device[sizeof(BPF_DEV)-2] = m;
if ((fd = open(device, O_RDWR)) > 0)
break;
}
if (fd < 0) {
perror("open");
return -1;
}
if (ioctl(fd, BIOCVERSION, &bv) < 0) {
perror("BIOCVERSION");
goto bad;
}
if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
fprintf(stderr,
"kernel bpf filter out of date\n");
goto bad;
}
/*
* Try finding a good size for the buffer; 65536 may be too
* big, so keep cutting it in half until we find a size
* that works, or run out of sizes to try.
*
*/
for (v = 65536; v != 0; v >>= 1) {
(void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
(void)strncpy(ifr.ifr_name, eth,
sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
break; /* that size worked; we're done */
if (errno != ENOBUFS) {
fprintf(stderr, "BIOCSETIF: %s: %s\n",
eth, strerror(errno));
goto bad;
}
}
if (v == 0) {
fprintf(stderr,
"BIOCSBLEN: %s: No buffer size worked\n", eth);
goto bad;
}
/* Allocate memory for the packet buffer */
pktbufsz = v;
if ((pktbuf = malloc(pktbufsz)) == NULL) {
perror("malloc");
goto bad;
}
/* Don't wait for buffer to be full or timeout */
v = 1;
if (ioctl(fd, BIOCIMMEDIATE, &v) < 0) {
perror("BIOCIMMEDIATE");
goto bad;
}
/* Only read incoming packets */
v = 0;
if (ioctl(fd, BIOCSSEESENT, &v) < 0) {
perror("BIOCSSEESENT");
goto bad;
}
/* Don't complete ethernet hdr */
v = 1;
if (ioctl(fd, BIOCSHDRCMPLT, &v) < 0) {
perror("BIOCSHDRCMPLT");
goto bad;
}
/* Get the data link layer type. */
if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
perror("BIOCGDLT");
goto bad;
}
linktype = v;
/* Get the filter buf size */
if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
perror("BIOCGBLEN");
goto bad;
}
bufsize = v;
if (ioctl(fd, BIOCSETF, (caddr_t)bpf_program) < 0) {
perror("BIOSETF");
goto bad;
}
free_bpf_program(bpf_program);
return(fd);
bad:
free_bpf_program(bpf_program);
close(fd);
return(-1);
}
int
getea(int s, char *eth, uchar *ea)
{
int mib[6];
size_t len;
char *buf, *next, *end;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
mib[0] = CTL_NET; mib[1] = AF_ROUTE;
mib[2] = 0; mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST; mib[5] = 0;
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
return (-1);
}
if (!(buf = (char *) malloc(len))) {
return (-1);
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
free(buf);
return (-1);
}
end = buf + len;
for (next = buf; next < end; next += ifm->ifm_msglen) {
ifm = (struct if_msghdr *)next;
if (ifm->ifm_type == RTM_IFINFO) {
sdl = (struct sockaddr_dl *)(ifm + 1);
if (strncmp(&sdl->sdl_data[0], eth,
sdl->sdl_nlen) == 0) {
memcpy(ea, LLADDR(sdl), ETHER_ADDR_LEN);
break;
}
}
}
free(buf);
return(0);
}
#if 0
int
getsec(int fd, uchar *place, vlong lba, int nsec)
{
return pread(fd, place, nsec * 512, lba * 512);
}
int
putsec(int fd, uchar *place, vlong lba, int nsec)
{
return pwrite(fd, place, nsec * 512, lba * 512);
}
#endif
static int pktn = 0;
static uchar *pktbp = NULL;
int
getpkt(int fd, uchar *buf, int sz)
{
register struct bpf_hdr *bh;
register int pktlen, retlen;
if (pktn <= 0) {
if ((pktn = read(fd, pktbuf, pktbufsz)) < 0) {
perror("read");
exit(1);
}
pktbp = pktbuf;
}
bh = (struct bpf_hdr *) pktbp;
retlen = (int) bh->bh_caplen;
/* This memcpy() is currently needed */
memcpy(buf, (void *)(pktbp + bh->bh_hdrlen),
retlen > sz ? sz : retlen);
pktlen = bh->bh_hdrlen + bh->bh_caplen;
pktbp = pktbp + BPF_WORDALIGN(pktlen);
pktn -= (int) BPF_WORDALIGN(pktlen);
return retlen;
}
int
putpkt(int fd, uchar *buf, int sz)
{
return write(fd, buf, sz);
}
int
getmtu(int fd, char *name)
{
struct ifreq xx;
int s, n, p;
s = socket(AF_INET, SOCK_RAW, 0);
if (s == -1) {
perror("Can't get mtu");
return 1500;
}
xx.ifr_addr.sa_family = AF_INET;
snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name);
n = ioctl(s, SIOCGIFMTU, &xx);
if (n == -1) {
perror("Can't get mtu");
return 1500;
}
close(s);
// FreeBSD bpf writes are capped at one PAGESIZE'd mbuf. As such we must
// limit our sector count. See FreeBSD PR 205164, OpenAoE/vblade #7.
p = getpagesize();
if (xx.ifr_mtu > p) {
return p;
}
return xx.ifr_mtu;
}
vlong
getsize(int fd)
{
off_t media_size;
vlong size;
struct stat s;
int n;
// Try getting disklabel from block dev
if ((n = ioctl(fd, DIOCGMEDIASIZE, &media_size)) != -1) {
size = media_size;
} else {
// must not be a block special dev
if (fstat(fd, &s) == -1) {
perror("getsize");
exit(1);
}
size = s.st_size;
}
printf("ioctl returned %d\n", n);
printf("%lld bytes\n", size);
return size;
}