Annotation of nono/host/netdriver_afpacket.cpp, revision 1.1.1.4

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(&ethval, 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 *)&ethval;
                     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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.