TUN/TAPデバイスの浅い分析(二)--TUN/TAPのプログラミング
4772 ワード
この記事では、TUN/TAPデバイスのプログラミングについて詳しく説明したいと思います.
実はこの2つの設備のプログラミングについて、基本的には8つの文に属して、みんなは普通このようにします.
デバイスを起動する前に
あるlinuxはtunモジュールをカーネルにコンパイルしていないので、私たちが最初にしなければならないことは、私たちのシステムがTUN/TAPをサポートしているかどうかを確認することです.詳細については、ここを参照してください.http://blog.csdn.net/lishuhuakai/article/details/70305543ああ、この文章はもう余計なことは言わない.
tunモジュールだけでは十分ではありません.前の文書で説明したファイルを作成し、コマンドを実行します.
これにより、/dev/net/ディレクトリの下にtunという名前のファイルが表示されます.もちろんここのtunは任意のあなたの好きな名前に変えることができます.
デバイスの起動
TUNデバイスの場合、一般的には次のように初期化されます.
TAPデバイスを起動したい場合は、上記の
ネットワークアドレスの設定
上のコードはファイルを開き、ファイルの記述子を返しますが、まだ十分ではありません.1枚のNICにとって、ネットワークアドレスを構成しなければなりません.ルーティング情報さえあれば、NICは正常に動作します.
仮想TUN/TAPデバイスの起動に成功すると、コマンドでアドレスを設定できます.
例えばTAPデバイスを例に挙げます.
手動でコマンドを叩くことでtap 0デバイスを構成するのは面倒ですが、システム関数を直接プログラムで呼び出すことができます.
上のコマンドをrun_に直接渡します.cmd関数でよい.
もちろん、このような方法が好きでない場合は、次の関数を使用するなど、他の方法もあります.
上の関数は主に上のコマンドとほぼ同じことをします.
データの送受信
データの送受信は非常に簡単で、戻ってきたファイル記述子を読み取るたびにデータを受信することができ、データが来ないときは、ずっとどこかにブロックされています.もちろん、非ブロックIOを遊んで、データを送信したい場合は、そのファイル記述子に対応するファイルにデータを書き込むだけでいいです.
実はこの2つの設備のプログラミングについて、基本的には8つの文に属して、みんなは普通このようにします.
デバイスを起動する前に
あるlinuxはtunモジュールをカーネルにコンパイルしていないので、私たちが最初にしなければならないことは、私たちのシステムがTUN/TAPをサポートしているかどうかを確認することです.詳細については、ここを参照してください.http://blog.csdn.net/lishuhuakai/article/details/70305543ああ、この文章はもう余計なことは言わない.
tunモジュールだけでは十分ではありません.前の文書で説明したファイルを作成し、コマンドを実行します.
% sudo mknod /dev/net/tun c 10 200 # c ,10 200
これにより、/dev/net/ディレクトリの下にtunという名前のファイルが表示されます.もちろんここのtunは任意のあなたの好きな名前に変えることができます.
デバイスの起動
TUNデバイスの場合、一般的には次のように初期化されます.
int
tun_alloc(char dev[IFNAMSIZ]) // dev
{
struct ifreq ifr;
int fd, err;
if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { //
perror("open");
return -1;
}
bzero(&ifr, sizeof(ifr));
/* Flags : IFF_TUN - TUN
* IFF_TAP - TAP
* IFF_NO_PI -
*/
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; // tun , tap ,
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) { //
perror("ioctl TUNSETIFF");
close(fd);
return err;
}
// , tun , tunX,X 0 , tap
// tapX,X 0
strcpy(dev, ifr.ifr_name); // dev
return fd;
}
TAPデバイスを起動したい場合は、上記の
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
をifr.ifr_flags = IFF_TAP | IFF_NO_PI;
に変更すれば簡単です.では、TAPデバイスを起動します.ネットワークアドレスの設定
上のコードはファイルを開き、ファイルの記述子を返しますが、まだ十分ではありません.1枚のNICにとって、ネットワークアドレスを構成しなければなりません.ルーティング情報さえあれば、NICは正常に動作します.
仮想TUN/TAPデバイスの起動に成功すると、コマンドでアドレスを設定できます.
例えばTAPデバイスを例に挙げます.
% sudo ip link set dev tap0 up # tap0 , , ipconfig tap0 , ip
% sudo ip address add dev tap0 10.0.1.5/24 # tap0 ip
% ifconfig # ifconfig tap0
tap0: flags=4163 mtu 1500
inet 10.0.1.5 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::1872:80ff:fe20:46e2 prefixlen 64 scopeid 0x20
ether 1a:72:80:20:46:e2 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
% ip route show #
default via 192.168.140.2 dev ens33 proto static metric 100
10.0.0.0/24 dev ens39 proto kernel scope link src 10.0.0.130 metric 100
10.0.1.0/24 dev tap0 proto kernel scope link src 10.0.1.5 # ip ,
192.168.140.0/24 dev ens33 proto kernel scope link src 192.168.140.133 metric 100
手動でコマンドを叩くことでtap 0デバイスを構成するのは面倒ですが、システム関数を直接プログラムで呼び出すことができます.
int
run_cmd(char *cmd, ...)
{
va_list ap;
char buf[CMDBUFLEN];
va_start(ap, cmd);
vsnprintf(buf, CMDBUFLEN, cmd, ap);
va_end(ap);
if (debug) { // DEBUG
printf("EXEC: %s
", buf);
}
return system(buf);
}
上のコマンドをrun_に直接渡します.cmd関数でよい.
もちろん、このような方法が好きでない場合は、次の関数を使用するなど、他の方法もあります.
int
set_stack_attribute(char *dev)
{
struct ifreq ifr;
struct sockaddr_in addr;
int sockfd, err = -1;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, tapaddr, &addr.sin_addr);
bzero(&ifr, sizeof(ifr));
strcpy(ifr.ifr_name, dev);
bcopy(&addr, &ifr.ifr_addr, sizeof(addr));
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
return -1;
}
// ifconfig tap0 10.0.1.5 # ip
if ((err = ioctl(sockfd, SIOCSIFADDR, (void *)&ifr)) < 0) {
perror("ioctl SIOSIFADDR");
goto done;
}
/* */
if ((err = ioctl(sockfd, SIOCGIFFLAGS, (void *)&ifr)) < 0) {
perror("ioctl SIOCGIFADDR");
goto done;
}
/* */
ifr.ifr_flags |= IFF_UP;
// ifup tap0 #
if ((err = ioctl(sockfd, SIOCSIFFLAGS, (void *)&ifr)) < 0) {
perror("ioctl SIOCSIFFLAGS");
goto done;
}
inet_pton(AF_INET, "255.255.255.0", &addr.sin_addr);
bcopy(&addr, &ifr.ifr_netmask, sizeof(addr));
// ifconfig tap0 10.0.1.5/24 #
if ((err = ioctl(sockfd, SIOCSIFNETMASK, (void *) &ifr)) < 0) {
perror("ioctl SIOCSIFNETMASK");
goto done;
}
done:
close(sockfd);
return err;
}
上の関数は主に上のコマンドとほぼ同じことをします.
データの送受信
データの送受信は非常に簡単で、戻ってきたファイル記述子を読み取るたびにデータを受信することができ、データが来ないときは、ずっとどこかにブロックされています.もちろん、非ブロックIOを遊んで、データを送信したい場合は、そのファイル記述子に対応するファイルにデータを書き込むだけでいいです.