|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// ホストネットワークの BPF ドライバ
//
#include "netdriver_bpf.h"
#include "autofd.h"
#include "hostnet.h"
#include <fcntl.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
// コンストラクタ
NetDriverBPF::NetDriverBPF(HostDevice *hostdev_, const std::string& ifname_)
: inherited(hostdev_, "bpf")
{
ifname = ifname_;
if (ifname.empty()) {
ifname = "auto";
}
}
// デストラクタ
NetDriverBPF::~NetDriverBPF()
{
Close();
}
// ドライバ初期化
bool
NetDriverBPF::InitDriver(bool startup)
{
struct ifreq ifr;
struct bpf_program prog;
u_int val;
u_int bpfbuflen;
struct bpf_insn insn[] = {
// すべてのパケットを全長受信
BPF_STMT(BPF_RET + BPF_K, (u_int)-1),
};
putmsg(1, "trying bpf...");
putmsg(1, "argument: ifname=\"%s\"", ifname.c_str());
if (ifname == "auto") {
// インタフェース指定がなければそれっぽいのを探すか
ifname = FindInterface();
if (ifname.empty()) {
errmsg = "auto could not find a suitable interface";
goto abort;
}
putmsg(1, "auto-selected ifname: \"%s\"", ifname.c_str());
}
// 最大値は適当
fd = -1;
for (int i = -1; i < 10; i++) {
// i == -1 なら /dev/bpf、0..10 は /dev/bpfN。
// まず /dev/bpf を cloning デバイスだと思って開いてみる。
// そうでなければ従来どおり 0 から順番に試してみる。
if (i == -1) {
devpath = "/dev/bpf";
} else {
devpath = string_format("/dev/bpf%d", i);
}
fd = open(devpath.c_str(), O_RDWR);
if (fd < 0) {
// ファイルがないのを表示したら結構うるさいので除く
if (errno != ENOENT) {
putmsg(1, "open %s: %s", devpath.c_str(), strerror(errno));
}
continue;
}
// 成功したら抜ける
break;
}
if (fd < 0) {
errmsg = "no bpf devices available";
goto abort;
}
// インタフェースにバインド
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
errmsg = string_format("%s(%s): BIOCSETIF: %s",
devpath.c_str(), ifname.c_str(), strerror(errno));
goto abort;
}
if (ioctl(fd, BIOCPROMISC, (void *)0) == -1) {
errmsg = string_format("%s(%s): %s: %s",
devpath.c_str(), ifname.c_str(), "BIOCPROMISC", strerror(errno));
goto abort;
}
val = 1;
if (ioctl(fd, BIOCIMMEDIATE, &val) == -1) {
errmsg = string_format("%s(%s): %s: %s",
devpath.c_str(), ifname.c_str(), "BIOCIMMEDIATE", strerror(errno));
goto abort;
}
if (ioctl(fd, BIOCSHDRCMPLT, &val) == -1) {
errmsg = string_format("%s(%s): %s: %s",
devpath.c_str(), ifname.c_str(), "BIOCSHDRCMPLT", strerror(errno));
goto abort;
}
// フィルタをセット
prog.bf_len = countof(insn);
prog.bf_insns = insn;
if (ioctl(fd, BIOCSETF, &prog) == -1) {
errmsg = string_format("%s(%s): %s: %s",
devpath.c_str(), ifname.c_str(), "BIOCSETF", strerror(errno));
goto abort;
}
// バッファサイズを取得
if (ioctl(fd, BIOCGBLEN, &bpfbuflen) == -1) {
errmsg = string_format("%s(%s): %s: %s",
devpath.c_str(), ifname.c_str(), "BIOCGBLEN", strerror(errno));
goto abort;
}
// 受信用バッファを確保
bpfbuf.resize(bpfbuflen);
putmsg(1, "opened %s for %s", devpath.c_str(), ifname.c_str());
putmsg(1, "warning: communicating with the localhost is not possible");
// bpf では ifup スクリプトは実行しない
if (hostdev->AddOuter(fd) < 0) {
putmsg(0, "AddOuter(fd=%d): %s", (int)fd, strerror(errno));
return false;
}
return true;
abort:
putmsg(1, "%s", errmsg.c_str());
Close();
return false;
}
// クローズ
// (private)
void
NetDriverBPF::Close()
{
if (fd.Valid()) {
hostdev->DelOuter(fd);
fd.Close();
}
}
// モニタ (ドライバ依存情報のみ)
void
NetDriverBPF::MonitorScreenMD(TextScreen& screen, int y)
{
screen.Print(0, y++, "Device : %s", devpath.c_str());
screen.Print(0, y++, "Interface: %s", ifname.c_str());
}
// パケットを送信する
void
NetDriverBPF::Write(const void *buf, int buflen)
{
ssize_t n;
n = write(fd, buf, buflen);
if (n < 0) {
putmsg(0, "write(%d): %s", buflen, strerror(errno));
return;
}
if (n < buflen) {
putmsg(0, "write(%d): short(%zd)", buflen, n);
return;
}
}
// パケットを受信する
int
NetDriverBPF::Read(NetPacket *p)
{
uint8 *buf = bpfbuf.data();
if (bpfptr == NULL) {
bpflen = read(fd, bpfbuf.data(), bpfbuf.size());
if (bpflen < 0) {
putmsg(0, "read: %s", strerror(errno));
return NODATA;
}
bpfptr = buf;
}
// BPF ヘッダを除いた部分が 1 パケット
struct bpf_hdr *bh = (struct bpf_hdr *)bpfptr;
p->Assign(bpfptr + bh->bh_hdrlen, bh->bh_caplen);
// 次のフレームの先頭を指す
int framelen = BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
bpfptr += framelen;
if (bpfptr < buf + bpflen) {
// 次がある
return 1;
} else {
// 次はない
bpfptr = NULL;
return 0;
}
}
// 最初に見付かった使えそうなインタフェース名を返す。
// 物理インタフェースが明らかに1つしかないところで、
// インタフェース名の指定を省略できるようにするため。
std::string
NetDriverBPF::FindInterface() const
{
char name[IFNAMSIZ];
struct ifaddrs *ifa_list, *ifa;
struct sockaddr_dl *dl;
if (getifaddrs(&ifa_list) == -1) {
return "";
}
name[0] = '\0';
for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
dl = (struct sockaddr_dl *)ifa->ifa_addr;
if ((ifa->ifa_flags & IFF_UP) != 0 && dl->sdl_type == IFT_ETHER) {
strlcpy(name, ifa->ifa_name, sizeof(name));
break;
}
}
freeifaddrs(ifa_list);
return std::string(name);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.