1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """KVM hypervisor tap device helpers
32
33 """
34
35 import os
36 import logging
37 import struct
38 import fcntl
39
40 from ganeti import errors
41
42
43
44
45
46 TUNSETIFF = 0x400454ca
47 TUNGETIFF = 0x800454d2
48 TUNGETFEATURES = 0x800454cf
49 IFF_TAP = 0x0002
50 IFF_NO_PI = 0x1000
51 IFF_ONE_QUEUE = 0x2000
52 IFF_VNET_HDR = 0x4000
53 IFF_MULTI_QUEUE = 0x0100
54
55
57 """Retrieves supported TUN features from file descriptor.
58
59 @see: L{_ProbeTapVnetHdr}
60
61 """
62 req = struct.pack("I", 0)
63 try:
64 buf = _ioctl(fd, TUNGETFEATURES, req)
65 except EnvironmentError, err:
66 logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
67 return None
68 else:
69 (flags, ) = struct.unpack("I", buf)
70 return flags
71
72
74 """Check whether to enable the IFF_VNET_HDR flag.
75
76 To do this, _all_ of the following conditions must be met:
77 1. TUNGETFEATURES ioctl() *must* be implemented
78 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
79 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
80 drivers/net/tun.c there is no way to test this until after the tap device
81 has been created using TUNSETIFF, and there is no way to change the
82 IFF_VNET_HDR flag after creating the interface, catch-22! However both
83 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
84 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
85
86 @type fd: int
87 @param fd: the file descriptor of /dev/net/tun
88
89 """
90 flags = _features_fn(fd)
91
92 if flags is None:
93
94 return False
95
96 result = bool(flags & IFF_VNET_HDR)
97
98 if not result:
99 logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
100
101 return result
102
103
105 """Check whether to enable the IFF_MULTI_QUEUE flag.
106
107 This flag was introduced in Linux kernel 3.8.
108
109 @type fd: int
110 @param fd: the file descriptor of /dev/net/tun
111
112 """
113 flags = _features_fn(fd)
114
115 if flags is None:
116
117 return False
118
119 result = bool(flags & IFF_MULTI_QUEUE)
120
121 if not result:
122 logging.warning("Kernel does not support IFF_MULTI_QUEUE, not enabling")
123
124 return result
125
126
127 -def OpenTap(vnet_hdr=True, virtio_net_queues=1, name=""):
128 """Open a new tap device and return its file descriptor.
129
130 This is intended to be used by a qemu-type hypervisor together with the -net
131 tap,fd=<fd> or -net tap,fds=x:y:...:z command line parameter.
132
133 @type vnet_hdr: boolean
134 @param vnet_hdr: Enable the VNET Header
135
136 @type virtio_net_queues: int
137 @param virtio_net_queues: Set number of tap queues but not more than 8,
138 queues only work with virtio-net device;
139 disabled by default (one queue).
140
141 @type name: string
142 @param name: name for the TAP interface being created; if an empty
143 string is passed, the OS will generate a unique name
144
145 @return: (ifname, [tapfds])
146 @rtype: tuple
147
148 """
149 tapfds = []
150
151 for _ in range(virtio_net_queues):
152 try:
153 tapfd = os.open("/dev/net/tun", os.O_RDWR)
154 except EnvironmentError:
155 raise errors.HypervisorError("Failed to open /dev/net/tun")
156
157 flags = IFF_TAP | IFF_NO_PI
158
159 if vnet_hdr and _ProbeTapVnetHdr(tapfd):
160 flags |= IFF_VNET_HDR
161
162
163 if virtio_net_queues > 1 and _ProbeTapMqVirtioNet(tapfd):
164 flags |= IFF_MULTI_QUEUE
165 else:
166 flags |= IFF_ONE_QUEUE
167
168
169 ifr = struct.pack("16sh", name, flags)
170
171 try:
172 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
173 except EnvironmentError, err:
174 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
175 err)
176
177 tapfds.append(tapfd)
178
179
180 ifname = struct.unpack("16sh", res)[0].strip("\x00")
181
182 return (ifname, tapfds)
183