// Get the cancel object CANCEL *VLanGetCancel(VLAN *v) { CANCEL *c; int fd; int yes = 0; // Validate arguments if (v == NULL) { return NULL; } c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; fd = v->fd; #ifndef UNIX_MACOS UnixSetSocketNonBlockingMode(fd, true); #else // UNIX_MACOS UnixSetSocketNonBlockingMode(fd, false); #endif // UNIX_MACOS c->SpecialFlag = true; c->pipe_read = fd; return c; }
// Open Ethernet adapter (Solaris) ETH *OpenEthSolaris(char *name, bool local, bool tapmode, char *tapaddr) { char devname[MAX_SIZE]; UINT devid; int fd; ETH *e; CANCEL *c; struct strioctl sioc; // Validate arguments if (name == NULL || tapmode != false) { return NULL; } // Parse device name if (ParseUnixEthDeviceName(devname, sizeof(devname), &devid, name) == false) { return NULL; } // Open the device fd = open(devname, O_RDWR); if (fd == -1) { // Failed return NULL; } // Attach to the device if (DlipAttatchRequest(fd, devid) == false) { // Failed close(fd); return NULL; } // Verify ACK message if (DlipReceiveAck(fd) == false) { // Failed close(fd); return NULL; } // Bind to SAP if (DlipBindRequest(fd) == false) { // Failed close(fd); return NULL; } // Verify ACK message if (DlipReceiveAck(fd) == false) { // Failed close(fd); return NULL; } // Set to ignore SAP and promiscuous mode if (DlipPromiscuous(fd, DL_PROMISC_SAP) == false) { // Failed close(fd); return NULL; } // Verify ACK message if (DlipReceiveAck(fd) == false) { // Failed close(fd); return NULL; } // Set to the mode to receive self sending packet if (DlipPromiscuous(fd, DL_PROMISC_PHYS) == false) { // Failed close(fd); return NULL; } // Verify ACK message if (DlipReceiveAck(fd) == false) { // Failed close(fd); return NULL; } // Set to raw mode sioc.ic_cmd = DLIOCRAW; sioc.ic_timout = -1; sioc.ic_len = 0; sioc.ic_dp = NULL; if (ioctl(fd, I_STR, &sioc) < 0) { // Failed close(fd); return NULL; } if (ioctl(fd, I_FLUSH, FLUSHR) < 0) { // Failed close(fd); return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; c->SpecialFlag = true; c->pipe_read = fd; e->Cancel = c; e->IfIndex = -1; e->Socket = fd; UnixSetSocketNonBlockingMode(fd, true); // Get control interface e->SocketBsdIf = socket(AF_INET, SOCK_DGRAM, 0); // Get MTU value e->InitialMtu = EthGetMtu(e); return e; }
// Open Ethernet device (Linux) ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr) { ETH *e; struct ifreq ifr; struct sockaddr_ll addr; int s; int index; CANCEL *c; // Validate arguments if (name == NULL) { return NULL; } if (tapmode) { #ifndef NO_VLAN // In tap mode VLAN *v = NewTap(name, tapaddr); if (v == NULL) { return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->Cancel = VLanGetCancel(v); e->IfIndex = 0; e->Socket = INVALID_SOCKET; e->Tap = v; return e; #else // NO_VLAN return NULL; #endif // NO_VLAN } s = UnixEthOpenRawSocket(); if (s == INVALID_SOCKET) { return NULL; } Zero(&ifr, sizeof(ifr)); StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { closesocket(s); return NULL; } index = ifr.ifr_ifindex; Zero(&addr, sizeof(addr)); addr.sll_family = PF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); addr.sll_ifindex = index; if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { closesocket(s); return NULL; } if (local == false) { // Enable promiscious mode Zero(&ifr, sizeof(ifr)); StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { // Failed closesocket(s); return NULL; } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { // Failed closesocket(s); return NULL; } } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->IfIndex = index; e->Socket = s; c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; UnixSetSocketNonBlockingMode(s, true); c->SpecialFlag = true; c->pipe_read = s; e->Cancel = c; // Get MTU e->InitialMtu = EthGetMtu(e); if (tapmode == false) { // Disable hardware offloading UnixDisableInterfaceOffload(name); } return e; }
// Open Ethernet adapter (BPF) ETH *OpenEthBpf(char *name, bool local, bool tapmode, char *tapaddr) { ETH *e; CANCEL *c; char devname[MAX_SIZE]; int n = 0; int fd; int ret; UINT bufsize; struct ifreq ifr; struct timeval to; // Find unused bpf device and open it do{ Format(devname, sizeof(devname), "/dev/bpf%d", n++); fd = open (devname, O_RDWR); if(fd<0){ perror("open"); } }while(fd < 0 && errno == EBUSY); // No free bpf device was found if(fd < 0){ Debug("BPF: No minor number are free.\n"); return NULL; } // Enlarge buffer size n = 524288; // Somehow(In libpcap, this size is 32768) while(true){ // Specify buffer size ioctl(fd, BIOCSBLEN, &n); // Bind to the network device StrCpy(ifr.ifr_name, IFNAMSIZ, name); ret = ioctl(fd, BIOCSETIF, &ifr); if(ret < 0){ if(ret == ENOBUFS && n>1500){ // Inappropriate buffer size // Retry with half buffer size // If buffer size is under 1500 bytes, something goes wrong n /= 2; continue; } Debug("bpf: binding network failed.\n"); close(fd); return NULL; }else{ break; } } bufsize = n; // Set to promiscuous mode if(local == false){ if (ioctl(fd, BIOCPROMISC, NULL) < 0){ printf("bpf: promisc mode failed.\n"); close(fd); return NULL; } } // Set to immediate mode (Return immediately when packet arrives) n = 1; if (ioctl(fd, BIOCIMMEDIATE, &n) < 0){ Debug("BPF: non-block mode failed.\n"); close(fd); return NULL; } // Set receiving self sending packet n = 1; if (ioctl(fd, BIOCGSEESENT, &n) < 0){ Debug("BPF: see sent mode failed.\n"); close(fd); return NULL; } // Header complete mode (Generate whole header of sending packet) n = 1; if (ioctl(fd, BIOCSHDRCMPLT, &n) < 0){ Debug("BPF: Header complete mode failed.\n"); close(fd); return NULL; } // Set timeout delay to 1 second to.tv_sec = 1; to.tv_usec = 0; if (ioctl(fd, BIOCSRTIMEOUT, &to) < 0){ Debug("BPF: Read timeout setting failed.\n"); close(fd); return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->IfIndex = -1; e->Socket = fd; e->BufSize = bufsize; #ifdef BRIDGE_BPF_THREAD e->Queue = NewQueue(); e->QueueSize = 0; e->Cancel = NewCancel(); // Start capture thread e->CaptureThread = NewThread(BpfThread, e); WaitThreadInit(e->CaptureThread); #else // BRIDGE_BPF_THREAD c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; c->SpecialFlag = true; c->pipe_read = fd; e->Cancel = c; e->Buffer = Malloc(bufsize); e->Next = e->Buffer; e->Rest = 0; // Set to non-blocking mode UnixSetSocketNonBlockingMode(fd, true); #endif // BRIDGE_BPF_THREAD // Open interface control socket for FreeBSD e->SocketBsdIf = socket(AF_LOCAL, SOCK_DGRAM, 0); // Get MTU value e->InitialMtu = EthGetMtu(e); return e; }
// アダプタを開く (Solaris) ETH *OpenEthSolaris(char *name, bool local, bool tapmode, char *tapaddr) { char devname[MAX_SIZE]; UINT devid; int fd; ETH *e; CANCEL *c; struct strioctl sioc; // 引数チェック if (name == NULL || tapmode != false) { return NULL; } // デバイス名を解析 if (ParseUnixEthDeviceName(devname, sizeof(devname), &devid, name) == false) { return NULL; } // デバイスを開く fd = open(devname, O_RDWR); if (fd == -1) { // 失敗 return NULL; } // デバイスにアタッチする if (DlipAttatchRequest(fd, devid) == false) { // 失敗 close(fd); return NULL; } // 確認 if (DlipReceiveAck(fd) == false) { // 失敗 close(fd); return NULL; } // SAPにバインドする if (DlipBindRequest(fd) == false) { // 失敗 close(fd); return NULL; } // 確認 if (DlipReceiveAck(fd) == false) { // 失敗 close(fd); return NULL; } // SAPに関わらず受信するモードにセットする if (DlipPromiscuous(fd, DL_PROMISC_SAP) == false) { // 失敗 close(fd); return NULL; } // 確認 if (DlipReceiveAck(fd) == false) { // 失敗 close(fd); return NULL; } // 自分の送信するパケットも受信するモードにセットする if (DlipPromiscuous(fd, DL_PROMISC_PHYS) == false) { // 失敗 close(fd); return NULL; } // 確認 if (DlipReceiveAck(fd) == false) { // 失敗 close(fd); return NULL; } // Raw モードに設定 sioc.ic_cmd = DLIOCRAW; sioc.ic_timout = -1; sioc.ic_len = 0; sioc.ic_dp = NULL; if (ioctl(fd, I_STR, &sioc) < 0) { // 失敗 close(fd); return NULL; } if (ioctl(fd, I_FLUSH, FLUSHR) < 0) { // 失敗 close(fd); return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; c->SpecialFlag = true; c->pipe_read = fd; e->Cancel = c; e->IfIndex = -1; e->Socket = fd; UnixSetSocketNonBlockingMode(fd, true); // 操作用 I/F の取得 e->SocketBsdIf = socket(AF_INET, SOCK_DGRAM, 0); // MTU の取得 e->InitialMtu = EthGetMtu(e); return e; }
// アダプタを開く (BPF) ETH *OpenEthBpf(char *name, bool local, bool tapmode, char *tapaddr) { ETH *e; CANCEL *c; char devname[MAX_SIZE]; int n = 0; int fd; int ret; UINT bufsize; struct ifreq ifr; struct timeval to; // 未使用の bpf デバイスを探して開く do{ Format(devname, sizeof(devname), "/dev/bpf%d", n++); fd = open (devname, O_RDWR); if(fd<0){ perror("open"); } }while(fd < 0 && errno == EBUSY); // 開くことが出来るbpfデバイスが無ければエラー if(fd < 0){ Debug("BPF: No minor number are free.\n"); return NULL; } // バッファサイズを拡大 n = 524288; // なんとなく(libpcapでは32768だった) while(true){ // バッファサイズを指定 ioctl(fd, BIOCSBLEN, &n); // ネットワークをバインド StrCpy(ifr.ifr_name, IFNAMSIZ, name); ret = ioctl(fd, BIOCSETIF, &ifr); if(ret < 0){ if(ret == ENOBUFS && n>1500){ // バッファサイズが不適切 // サイズを半分にしてリトライ // バッファサイズ1500バイト以下でエラーになるのは何かおかしい n /= 2; continue; } Debug("bpf: binding network failed.\n"); close(fd); return NULL; }else{ break; } } bufsize = n; // プロミスキャスモードに設定 if(local == false){ if (ioctl(fd, BIOCPROMISC, NULL) < 0){ printf("bpf: promisc mode failed.\n"); close(fd); return NULL; } } // 即時モードに設定(パケットを受信するとタイムアウトを待たず、すぐにreadがreturnする) n = 1; if (ioctl(fd, BIOCIMMEDIATE, &n) < 0){ Debug("BPF: non-block mode failed.\n"); close(fd); return NULL; } // 自分が送信するパケットも受信する n = 1; if (ioctl(fd, BIOCGSEESENT, &n) < 0){ Debug("BPF: see sent mode failed.\n"); close(fd); return NULL; } // ヘッダ完全モード(送信するパケットのヘッダも自分で生成する) n = 1; if (ioctl(fd, BIOCSHDRCMPLT, &n) < 0){ Debug("BPF: Header complete mode failed.\n"); close(fd); return NULL; } // read のタイムアウト時間を設定(1秒) to.tv_sec = 1; to.tv_usec = 0; if (ioctl(fd, BIOCSRTIMEOUT, &to) < 0){ Debug("BPF: Read timeout setting failed.\n"); close(fd); return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->IfIndex = -1; e->Socket = fd; e->BufSize = bufsize; #ifdef BRIDGE_BPF_THREAD e->Queue = NewQueue(); e->QueueSize = 0; e->Cancel = NewCancel(); // キャプチャ用スレッドの開始 e->CaptureThread = NewThread(BpfThread, e); WaitThreadInit(e->CaptureThread); #else // BRIDGE_BPF_THREAD c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; c->SpecialFlag = true; c->pipe_read = fd; e->Cancel = c; e->Buffer = Malloc(bufsize); e->Next = e->Buffer; e->Rest = 0; // ノンブロッキングモードに設定 UnixSetSocketNonBlockingMode(fd, true); #endif // BRIDGE_BPF_THREAD // FreeBSD 用インターフェイス操作用ソケットを作成 e->SocketBsdIf = socket(AF_LOCAL, SOCK_DGRAM, 0); // MTU の取得 e->InitialMtu = EthGetMtu(e); return e; }
// Open Ethernet device (Linux) ETH *OpenEthLinux(char *name, bool local, bool tapmode, char *tapaddr) { ETH *e; struct ifreq ifr; struct sockaddr_ll addr; int s; int index; bool aux_ok = false; CANCEL *c; // Validate arguments if (name == NULL) { return NULL; } if (tapmode) { #ifndef NO_VLAN // In tap mode VLAN *v = NewTap(name, tapaddr); if (v == NULL) { return NULL; } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->Cancel = VLanGetCancel(v); e->IfIndex = 0; e->Socket = INVALID_SOCKET; e->Tap = v; return e; #else // NO_VLAN return NULL; #endif // NO_VLAN } s = UnixEthOpenRawSocket(); if (s == INVALID_SOCKET) { return NULL; } Zero(&ifr, sizeof(ifr)); StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { closesocket(s); return NULL; } index = ifr.ifr_ifindex; Zero(&addr, sizeof(addr)); addr.sll_family = PF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); addr.sll_ifindex = index; if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { closesocket(s); return NULL; } if (local == false) { // Enable promiscious mode Zero(&ifr, sizeof(ifr)); StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), name); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) { // Failed closesocket(s); return NULL; } ifr.ifr_flags |= IFF_PROMISC; if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) { // Failed closesocket(s); return NULL; } } if (true) { int val = 1; int ss_ret = setsockopt(s, SOL_PACKET, MY_PACKET_AUXDATA, &val, sizeof(val)); if (ss_ret < 0) { Debug("eth(%s): setsockopt: PACKET_AUXDATA failed.\n", name); } else { Debug("eth(%s): setsockopt: PACKET_AUXDATA ok.\n", name); aux_ok = true; } } e = ZeroMalloc(sizeof(ETH)); e->Name = CopyStr(name); e->Title = CopyStr(name); e->IfIndex = index; e->Socket = s; e->Linux_IsAuxDataSupported = aux_ok; c = NewCancel(); UnixDeletePipe(c->pipe_read, c->pipe_write); c->pipe_read = c->pipe_write = -1; UnixSetSocketNonBlockingMode(s, true); c->SpecialFlag = true; c->pipe_read = s; e->Cancel = c; // Get MTU e->InitialMtu = EthGetMtu(e); if (tapmode == false) { if (GetGlobalServerFlag(GSF_LOCALBRIDGE_NO_DISABLE_OFFLOAD) == false) { bool b = false; LockList(eth_offload_list); { if (IsInListStr(eth_offload_list, name) == false) { b = true; Add(eth_offload_list, CopyStr(name)); } } UnlockList(eth_offload_list); if (b) { // Disable hardware offloading UnixDisableInterfaceOffload(name); } } } return e; }