我的代码
https://github.com/liuqun/demo-linux-tun-virtual-netcard/blob/master/demo-1-ioctl/src/main.c
以下代码节选自"openV屁N"的源码tun.c文件:
openV屁N封装了一组读写函数如下:
open_tun(dev_name, dev_type, dev_node, tuntap上下文)
打开Tun虚拟网卡close_tun(tuntap上下文)
n_bytes = write_tun(tuntap上下文, buf, len)
n_bytes = read_tun(tuntap上下文, buf, len)
int write_tun(struct tuntap *tt, uint8_t *buf, int len)
{
return write(tt->fd, buf, len);
}
int read_tun(struct tuntap *tt, uint8_t *buf, int len)
{
return read(tt->fd, buf, len);
}
void open_tun(const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt)
{
struct ifreq ifr;
/*
* We handle --dev null specially, we do not open /dev/null for this.
*/
if (tt->type == DEV_TYPE_NULL)
{
open_null(tt);
}
else
{
/*
* Process --dev-node
*/
const char *node = dev_node;
if (!node)
{
node = "/dev/net/tun";
}
/*
* Open the interface
*/
if ((tt->fd = open(node, O_RDWR)) < 0)
{
msg(M_ERR, "ERROR: Cannot open TUN/TAP dev %s", node);
}
/*
* Process --tun-ipv6
*/
CLEAR(ifr);
ifr.ifr_flags = IFF_NO_PI;
#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
ifr.ifr_flags |= IFF_ONE_QUEUE;
#endif
/*
* Figure out if tun or tap device
*/
if (tt->type == DEV_TYPE_TUN)
{
ifr.ifr_flags |= IFF_TUN;
}
else if (tt->type == DEV_TYPE_TAP)
{
ifr.ifr_flags |= IFF_TAP;
}
else
{
msg(M_FATAL, "I don't recognize device %s as a tun or tap device",
dev);
}
/*
* Set an explicit name, if --dev is not tun or tap
*/
if (strcmp(dev, "tun") && strcmp(dev, "tap"))
{
strncpynt(ifr.ifr_name, dev, IFNAMSIZ);
}
/*
* Use special ioctl that configures tun/tap device with the parms
* we set in ifr
*/
if (ioctl(tt->fd, TUNSETIFF, (void *) &ifr) < 0)
{
msg(M_ERR, "ERROR: Cannot ioctl TUNSETIFF %s", dev);
}
msg(M_INFO, "TUN/TAP device %s opened", ifr.ifr_name);
/*
* Try making the TX send queue bigger
*/
#if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN)
if (tt->options.txqueuelen)
{
struct ifreq netifr;
int ctl_fd;
if ((ctl_fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0)
{
CLEAR(netifr);
strncpynt(netifr.ifr_name, ifr.ifr_name, IFNAMSIZ);
netifr.ifr_qlen = tt->options.txqueuelen;
if (ioctl(ctl_fd, SIOCSIFTXQLEN, (void *) &netifr) >= 0)
{
msg(D_OSBUF, "TUN/TAP TX queue length set to %d", tt->options.txqueuelen);
}
else
{
msg(M_WARN | M_ERRNO, "Note: Cannot set tx queue length on %s", ifr.ifr_name);
}
close(ctl_fd);
}
else
{
msg(M_WARN | M_ERRNO, "Note: Cannot open control socket on %s", ifr.ifr_name);
}
}
#endif /* if defined(IFF_ONE_QUEUE) && defined(SIOCSIFTXQLEN) */
set_nonblock(tt->fd);
set_cloexec(tt->fd);
tt->actual_name = string_alloc(ifr.ifr_name, NULL);
}
return;
}
void close_tun(struct tuntap *tt)
{
if (tt)
{
if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
{
struct argv argv = argv_new();
struct gc_arena gc = gc_new();
#ifdef ENABLE_IPROUTE
if (is_tun_p2p(tt))
{
argv_printf(&argv,
"%s addr del dev %s local %s peer %s",
iproute_path,
tt->actual_name,
print_in_addr_t(tt->local, 0, &gc),
print_in_addr_t(tt->remote_netmask, 0, &gc)
);
}
else
{
argv_printf(&argv,
"%s addr del dev %s %s/%d",
iproute_path,
tt->actual_name,
print_in_addr_t(tt->local, 0, &gc),
netmask_to_netbits2(tt->remote_netmask)
);
}
#else /* ifdef ENABLE_IPROUTE */
argv_printf(&argv,
"%s %s 0.0.0.0",
IFCONFIG_PATH,
tt->actual_name
);
#endif /* ifdef ENABLE_IPROUTE */
argv_msg(M_INFO, &argv);
open_execve_check(&argv, NULL, 0, "Linux ip addr del failed");
if (tt->did_ifconfig_ipv6_setup)
{
const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc);
#ifdef ENABLE_IPROUTE
argv_printf(&argv, "%s -6 addr del %s/%d dev %s",
iproute_path,
ifconfig_ipv6_local,
tt->netbits_ipv6,
tt->actual_name
);
argv_msg(M_INFO, &argv);
open_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
#else /* ifdef ENABLE_IPROUTE */
argv_printf(&argv,
"%s %s del %s/%d",
IFCONFIG_PATH,
tt->actual_name,
ifconfig_ipv6_local,
tt->netbits_ipv6
);
argv_msg(M_INFO, &argv);
open_execve_check(&argv, NULL, 0, "Linux ifconfig inet6 del failed");
#endif
}
argv_reset(&argv);
gc_free(&gc);
}
close_tun_generic(tt);
free(tt);
}
}
#define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF)
#define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF)
/*
* Define a TUN/TAP dev.
*/
struct tuntap
{
int type; /* DEV_TYPE_x as defined in proto.h */
int topology; /* one of the TOP_x values */
bool did_ifconfig_setup;
bool did_ifconfig_ipv6_setup;
bool did_ifconfig;
bool persistent_if; /* if existed before, keep on program end */
struct tuntap_options options; /* options set on command line */
char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */
/* number of TX buffers */
int txqueuelen;
/* ifconfig parameters */
in_addr_t local;
in_addr_t remote_netmask;
in_addr_t broadcast;
struct in6_addr local_ipv6;
struct in6_addr remote_ipv6;
int netbits_ipv6;
#ifdef _WIN32
HANDLE hand;
struct overlapped_io reads;
struct overlapped_io writes;
struct rw_handle rw_handle;
/* used for setting interface address via IP Helper API
* or DHCP masquerade */
bool ipapi_context_defined;
ULONG ipapi_context;
ULONG ipapi_instance;
in_addr_t adapter_netmask;
/* Windows adapter index for TAP-Windows adapter,
* ~0 if undefined */
DWORD adapter_index;
int standby_iter;
#else /* ifdef _WIN32 */
int fd; /* file descriptor for TUN/TAP dev */
#endif
#ifdef TARGET_SOLARIS
int ip_fd;
#endif
#ifdef HAVE_NET_IF_UTUN_H
bool is_utun;
#endif
/* used for printing status info only */
unsigned int rwflags_debug;
/* Some TUN/TAP drivers like to be ioctled for mtu
* after open */
int post_open_mtu;
};
节选自Linux内核文档提供的一段Tun/Tap用户层样例代码(多处细节已经调整)
https://www.kernel.org/doc/Documentation/networking/tuntap.txt
// 样例代码
#include
#include
int open_tun_and_select_type(char devName[IFNAMSIZ], int devType)//devName字符串指针不能为NULL,但可以是全0x00即'\0'
{
struct ifreq ifr;
int fd;
int err;
if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
return old_style__open_tun_and_select_type(devName, devType);
memset(&ifr, 0, sizeof(ifr));
/* Flags: IFF_TUN - TUN device (no Ethernet headers)
* IFF_TAP - TAP device
*
* IFF_NO_PI - Do not provide packet information
*/
ifr.ifr_flags = (short) devType;
if( *devName ) {
memcpy(ifr.ifr_name, devName, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
}
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
close(fd);
return err;
}
memcpy(devName, ifr.ifr_name, IFNAMSIZ);
return fd;
}
struct ifreq
结构体的定义位于
, 细节如下:#define IFNAMSIZ 16
// 该结构体用于修改网卡的各种参数, 包括MAC地址/IP地址/MTU最大值/Metric跃点数等
// 之前的程序我们仅仅用到 ifr.ifr_flags=IFF_TUN切换Tun/Tap网卡的类型
struct ifreq {
char ifr_name[IFNAMSIZ]; /* Interface name */
union {
struct sockaddr ifr_addr;
struct sockaddr ifr_dstaddr;
struct sockaddr ifr_broadaddr;
struct sockaddr ifr_netmask;
struct sockaddr ifr_hwaddr;
short ifr_flags;
int ifr_ifindex;
int ifr_metric;
int ifr_mtu;
struct ifmap ifr_map;
char ifr_slave[IFNAMSIZ];
char ifr_newname[IFNAMSIZ];
char *ifr_data;
};
};
tun_read() / tun_write()读写操作的数据包结构如下(摘自Linux内核文档):
frame_format:
- Flags [2 bytes]
- Proto [2 bytes]
- Raw protocol(IP, IPv6, etc) frame.
https://www.kernel.org/doc/Documentation/networking/tuntap.txt