|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.2 root 7: //
8: // ホストネットワークの AF_PACKET ドライバ
9: // (Linux 専用)
10: //
1.1 root 11:
12: #include "netdriver_afpacket.h"
13: #include "autofd.h"
1.1.1.2 root 14: #include "hostnet.h"
1.1 root 15: #include <ifaddrs.h>
16: #include <sys/socket.h>
17: #include <sys/ioctl.h>
18: #include <net/if.h>
19: #include <net/if_arp.h>
1.1.1.2 root 20: #include <linux/ethtool.h>
21: #include <linux/sockios.h>
1.1 root 22:
23: // コンストラクタ
1.1.1.2 root 24: NetDriverAFPacket::NetDriverAFPacket(HostDevice *hostdev_,
1.1 root 25: const std::string& ifname_)
1.1.1.4 ! root 26: : inherited(hostdev_, "afpacket")
1.1 root 27: {
28: ifname = ifname_;
29: if (ifname.empty()) {
30: ifname = "auto";
31: }
32: }
33:
34: // デストラクタ
35: NetDriverAFPacket::~NetDriverAFPacket()
36: {
37: Close();
38: }
39:
1.1.1.2 root 40: // ドライバ初期化
1.1 root 41: bool
1.1.1.4 ! root 42: NetDriverAFPacket::InitDriver(bool startup)
1.1 root 43: {
44: packet_mreq mreq {};
45: int len;
46: socklen_t so_len;
47:
48: putmsg(1, "trying afpacket...");
49: putmsg(1, "argument: ifname=\"%s\"", ifname.c_str());
50:
51: sll.sll_family = AF_PACKET;
52: sll.sll_protocol = htons(ETH_P_ALL);
53: sll.sll_ifindex = SelectInterface();
54: if (sll.sll_ifindex == 0) {
1.1.1.2 root 55: // errmsg はセットしてある
1.1 root 56: goto abort;
57: }
58:
59: // bind するまでの期間、このソケットにはシステムのすべての
60: // インタフェースからのパケットが着信することに注意。
61: fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
62: if (fd < 0) {
63: errmsg = string_format("%s: %s", "socket(AF_PACKET)", strerror(errno));
64: putmsg(1, "%s", errmsg.c_str());
65: if (errno == EPERM) {
66: // Linux では setcap(8) が必要。分かりづらいのでメッセージを出すが
67: // その例示より先に errmsg を表示したい。
68: warnx("%s", errmsg.c_str());
69: errmsg.clear();
70:
71: fprintf(stderr,
72: " ** Set CAP_NET_RAW capability to the nono executable file.\n"
73: " ** ex) sudo setcap cap_net_raw=ep path/to/%s\n",
74: getprogname());
75: }
76: return false;
77: }
78:
79: if (bind(fd, (const sockaddr *)&sll, sizeof(sll)) != 0) {
80: errmsg = string_format("bind(%s): %s", ifname.c_str(), strerror(errno));
81: goto abort;
82: }
83:
1.1.1.2 root 84: #if defined(SIOCETHTOOL) && defined(ETHTOOL_GGRO)
85: // GRO が設定されていると再構成されたジャンボパケットが届いてしまうが、
86: // VM (というか当時の NIC) にそんなものが届いても困るので、
87: // GRO が設定されていたらエラー終了する。
88: //
89: // 確認 /sbin/ethtool -k <ifname> | generic-receive-offload
90: // 設定 sudo ethtool -K <ifname> generic-receive-offload <on|off>
91: struct ifreq ifr;
92: struct ethtool_value ethval;
93:
94: memset(&ifr, 0, sizeof(ifr));
95: memset(ðval, 0, sizeof(ethval));
96: strlcpy(ifr.ifr_name, ifname.c_str(), sizeof(ifr.ifr_name));
97: ethval.cmd = ETHTOOL_GGRO;
98: ifr.ifr_data = (char *)ðval;
99: if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
100: if (errno == EOPNOTSUPP) {
101: // この ioctl 自体をサポートしていなければ
102: // たぶんオフローディングは設定されていないはず。
103: ethval.data = 1;
104: } else {
105: errmsg = string_format("SIOCETHTOOL(%s): %s",
106: ifname.c_str(), strerror(errno));
107: goto abort;
108: }
109: }
110:
111: if (ethval.data != 0) {
112: errmsg = "GRO is enabled";
113: putmsg(1, "%s", errmsg.c_str());
114: warnx("%s", errmsg.c_str());
115: errmsg.clear();
116: fprintf(stderr,
117: " ** Disable GRO(generic-receive-offload)"
118: " to avoid bother network problem.\n"
119: " ** ex) sudo ethtool -K %s gro off\n", ifname.c_str());
120: return false;
121: }
122: #endif
123:
1.1 root 124: // PROMISC セット
125: mreq.mr_ifindex = sll.sll_ifindex;
126: mreq.mr_type = PACKET_MR_PROMISC;
127: if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
128: &mreq, sizeof(mreq)) != 0) {
129: errmsg = string_format("%s: %s", "set PROMISC", strerror(errno));
130: goto abort;
131: }
132:
133: so_len = sizeof(len);
134: if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, &so_len) != 0) {
135: errmsg = string_format("%s: %s",
136: "getsockopt(SO_RCVBUF)", strerror(errno));
137: goto abort;
138: }
139:
1.1.1.2 root 140: if (hostdev->AddOuter(fd) < 0) {
141: errmsg = string_format("AddOuter(fd=%d): %s", (int)fd, strerror(errno));
142: goto abort;
143: }
144:
1.1 root 145: putmsg(1, "opened for %s", ifname.c_str());
146:
147: // AFPacket では ifup スクリプトは実行しない
148:
149: return true;
150:
151: abort:
1.1.1.2 root 152: if (errmsg.empty() == false) {
153: putmsg(1, "%s", errmsg.c_str());
154: }
1.1 root 155: Close();
156: return false;
157: }
158:
159: // クローズ
160: // (private)
161: void
162: NetDriverAFPacket::Close()
163: {
1.1.1.2 root 164: if (fd.Valid()) {
165: hostdev->DelOuter(fd);
166: fd.Close();
1.1 root 167: }
168: }
169:
170: // モニタ (ドライバ依存情報のみ)
171: void
1.1.1.3 root 172: NetDriverAFPacket::MonitorUpdateMD(TextScreen& screen, int y)
1.1 root 173: {
1.1.1.3 root 174: screen.Print(0, y, "Interface: %s", ifname.c_str());
1.1 root 175: }
176:
177: // パケットを送信する
1.1.1.2 root 178: void
179: NetDriverAFPacket::Write(const void *buf, int buflen)
1.1 root 180: {
1.1.1.2 root 181: ssize_t n;
1.1 root 182:
1.1.1.2 root 183: n = sendto(fd, buf, buflen, 0, (const sockaddr *)&sll, sizeof(sll));
184: if (n < 0) {
185: putmsg(0, "write: %s", strerror(errno));
186: return;
187: }
188: if (n < buflen) {
189: putmsg(0, "write: short");
190: return;
1.1 root 191: }
192: }
193:
194: // パケットを受信する
1.1.1.2 root 195: int
196: NetDriverAFPacket::Read(NetPacket *p)
1.1 root 197: {
1.1.1.2 root 198: int n;
1.1 root 199:
1.1.1.2 root 200: n = recv(fd, p->data(), p->size(), 0);
201: if (n < 0) {
202: putmsg(0, "recv: %s", strerror(errno));
203: return NODATA;
1.1 root 204: }
1.1.1.2 root 205: p->length = n;
206: return 0;
1.1 root 207: }
208:
209: // インタフェース番号を返す。
210: // auto のときは選択したインタフェース名を ifname にセットする。
211: // インタフェースが決定できないときは errmsg をセットして 0 を返す。
212: int
213: NetDriverAFPacket::SelectInterface()
214: {
215: int ifindex = 0;
216: struct ifaddrs *ifa_list, *ifa;
217:
218: if (ifname == "auto") {
219: // インタフェースを探す
220: int level = 1;
221:
222: if (getifaddrs(&ifa_list) == -1) {
1.1.1.4 ! root 223: errmsg = string_format("getifaddrs: %s", strerror(errno));
1.1 root 224: return 0;
225: }
226:
227: for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
228: if ((ifa->ifa_flags & IFF_UP) == 0) {
229: continue;
230: }
231: if (ifa->ifa_addr == NULL) {
232: continue;
233: }
234: // AF_PACKET をサポートするインタフェースを探す
235: if (ifa->ifa_addr->sa_family != AF_PACKET) {
236: continue;
237: }
238:
239: // ETHERNET を探す
240: sockaddr_ll *sa = (sockaddr_ll *)ifa->ifa_addr;
241: if (sa->sll_hatype != ARPHRD_ETHER) {
242: continue;
243: }
244:
245: if (ifindex == 0) {
246: // 最初に見つかったものを採用
247: ifname = ifa->ifa_name;
248: ifindex = sa->sll_ifindex;
249: } else {
250: // インタフェース候補が 2個以上ある場合はログレベル 0 にしたい
251: level = 0;
252: break;
253: }
254: }
255: freeifaddrs(ifa_list);
256:
257: if (ifindex == 0) {
258: errmsg = "auto could not find a suitable interface";
259: return ifindex;
260: }
261:
262: putmsg(level, "auto-selected ifname \"%s\"", ifname.c_str());
263:
264: } else {
265: ifindex = if_nametoindex(ifname.c_str());
266: if (ifindex == 0) {
267: errmsg = string_format("interface %s not found", ifname.c_str());
268: }
269: }
270: return ifindex;
271: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.