]>
Commit | Line | Data |
---|---|---|
078eedf4 NR |
1 | /* |
2 | * Copyright (c) 2014 VMware, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <stdlib.h> | |
18 | #include <config.h> | |
19 | #include <errno.h> | |
20 | ||
21 | #include <net/if.h> | |
22 | ||
23 | #include "coverage.h" | |
24 | #include "fatal-signal.h" | |
25 | #include "netdev-provider.h" | |
26 | #include "ofpbuf.h" | |
3bd0fd39 | 27 | #include "packets.h" |
078eedf4 NR |
28 | #include "poll-loop.h" |
29 | #include "shash.h" | |
30 | #include "svec.h" | |
31 | #include "vlog.h" | |
32 | #include "odp-netlink.h" | |
33 | #include "netlink-socket.h" | |
34 | #include "netlink.h" | |
35 | ||
36 | VLOG_DEFINE_THIS_MODULE(netdev_windows); | |
37 | static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5); | |
38 | ||
39 | enum { | |
40 | VALID_ETHERADDR = 1 << 0, | |
41 | VALID_MTU = 1 << 1, | |
42 | VALID_IFFLAG = 1 << 5, | |
43 | }; | |
44 | ||
45 | /* Caches the information of a netdev. */ | |
46 | struct netdev_windows { | |
47 | struct netdev up; | |
48 | int32_t dev_type; | |
49 | uint32_t port_no; | |
50 | ||
51 | unsigned int change_seq; | |
52 | ||
53 | unsigned int cache_valid; | |
54 | int ifindex; | |
55 | uint8_t mac[ETH_ADDR_LEN]; | |
56 | uint32_t mtu; | |
57 | unsigned int ifi_flags; | |
58 | }; | |
59 | ||
60 | /* Utility structure for netdev commands. */ | |
61 | struct netdev_windows_netdev_info { | |
62 | /* Generic Netlink header. */ | |
63 | uint8_t cmd; | |
64 | ||
65 | /* Information that is relevant to ovs. */ | |
66 | uint32_t dp_ifindex; | |
67 | uint32_t port_no; | |
68 | uint32_t ovs_type; | |
69 | ||
70 | /* General information of a network device. */ | |
71 | const char *name; | |
72 | uint8_t mac_address[ETH_ADDR_LEN]; | |
73 | uint32_t mtu; | |
74 | uint32_t ifi_flags; | |
75 | }; | |
76 | ||
3e648bc5 NR |
77 | static int query_netdev(const char *devname, |
78 | struct netdev_windows_netdev_info *reply, | |
79 | struct ofpbuf **bufp); | |
0bfecde1 NR |
80 | static struct netdev *netdev_windows_alloc(void); |
81 | static int netdev_windows_init_(void); | |
3e648bc5 NR |
82 | |
83 | /* Generic Netlink family numbers for OVS. | |
84 | * | |
0bfecde1 | 85 | * Initialized by netdev_windows_init_(). */ |
3e648bc5 NR |
86 | static int ovs_win_netdev_family; |
87 | struct nl_sock *ovs_win_netdev_sock; | |
88 | ||
89 | ||
90 | static bool | |
91 | is_netdev_windows_class(const struct netdev_class *netdev_class) | |
92 | { | |
0bfecde1 | 93 | return netdev_class->alloc == netdev_windows_alloc; |
3e648bc5 NR |
94 | } |
95 | ||
96 | static struct netdev_windows * | |
97 | netdev_windows_cast(const struct netdev *netdev_) | |
98 | { | |
99 | ovs_assert(is_netdev_windows_class(netdev_get_class(netdev_))); | |
100 | return CONTAINER_OF(netdev_, struct netdev_windows, up); | |
101 | } | |
102 | ||
078eedf4 | 103 | static int |
0bfecde1 | 104 | netdev_windows_init_(void) |
078eedf4 | 105 | { |
3e648bc5 NR |
106 | int error = 0; |
107 | static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; | |
108 | ||
109 | if (ovsthread_once_start(&once)) { | |
110 | error = nl_lookup_genl_family(OVS_WIN_NETDEV_FAMILY, | |
111 | &ovs_win_netdev_family); | |
112 | if (error) { | |
113 | VLOG_ERR("Generic Netlink family '%s' does not exist. " | |
114 | "The Open vSwitch kernel module is probably not loaded.", | |
115 | OVS_WIN_NETDEV_FAMILY); | |
116 | } | |
117 | if (!error) { | |
118 | /* XXX: Where to close this socket? */ | |
119 | error = nl_sock_create(NETLINK_GENERIC, &ovs_win_netdev_sock); | |
120 | } | |
121 | ||
122 | ovsthread_once_done(&once); | |
123 | } | |
124 | ||
125 | return error; | |
078eedf4 NR |
126 | } |
127 | ||
128 | static struct netdev * | |
129 | netdev_windows_alloc(void) | |
130 | { | |
3e648bc5 NR |
131 | struct netdev_windows *netdev = xzalloc(sizeof *netdev); |
132 | return netdev ? &netdev->up : NULL; | |
078eedf4 NR |
133 | } |
134 | ||
ee4dac3b NR |
135 | static uint32_t |
136 | dp_to_netdev_ifi_flags(uint32_t dp_flags) | |
137 | { | |
138 | uint32_t nd_flags = 0; | |
139 | ||
140 | if (dp_flags && OVS_WIN_NETDEV_IFF_UP) { | |
141 | nd_flags |= NETDEV_UP; | |
142 | } | |
143 | ||
144 | if (dp_flags && OVS_WIN_NETDEV_IFF_PROMISC) { | |
145 | nd_flags |= NETDEV_PROMISC; | |
146 | } | |
147 | ||
148 | return nd_flags; | |
149 | } | |
150 | ||
078eedf4 NR |
151 | static int |
152 | netdev_windows_system_construct(struct netdev *netdev_) | |
153 | { | |
3e648bc5 NR |
154 | struct netdev_windows *netdev = netdev_windows_cast(netdev_); |
155 | uint8_t mac[ETH_ADDR_LEN]; | |
156 | struct netdev_windows_netdev_info info; | |
157 | struct ofpbuf *buf; | |
158 | int ret; | |
159 | ||
160 | /* Query the attributes and runtime status of the netdev. */ | |
161 | ret = query_netdev(netdev_get_name(&netdev->up), &info, &buf); | |
162 | if (ret) { | |
163 | return ret; | |
164 | } | |
165 | ofpbuf_delete(buf); | |
166 | ||
167 | netdev->change_seq = 1; | |
168 | netdev->dev_type = info.ovs_type; | |
169 | netdev->port_no = info.port_no; | |
170 | ||
171 | memcpy(netdev->mac, info.mac_address, ETH_ADDR_LEN); | |
172 | netdev->cache_valid = VALID_ETHERADDR; | |
173 | netdev->ifindex = -EOPNOTSUPP; | |
174 | ||
175 | netdev->mtu = info.mtu; | |
176 | netdev->cache_valid |= VALID_MTU; | |
177 | ||
ee4dac3b | 178 | netdev->ifi_flags = dp_to_netdev_ifi_flags(info.ifi_flags); |
3e648bc5 NR |
179 | netdev->cache_valid |= VALID_IFFLAG; |
180 | ||
181 | VLOG_DBG("construct device %s, ovs_type: %u.", | |
182 | netdev_get_name(&netdev->up), info.ovs_type); | |
183 | return 0; | |
184 | } | |
185 | ||
186 | static int | |
187 | netdev_windows_netdev_to_ofpbuf(struct netdev_windows_netdev_info *info, | |
188 | struct ofpbuf *buf) | |
189 | { | |
190 | struct ovs_header *ovs_header; | |
191 | int error = EINVAL; | |
192 | ||
193 | nl_msg_put_genlmsghdr(buf, 0, ovs_win_netdev_family, | |
194 | NLM_F_REQUEST | NLM_F_ECHO, | |
195 | info->cmd, OVS_WIN_NETDEV_VERSION); | |
196 | ||
197 | ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header); | |
198 | ovs_header->dp_ifindex = info->dp_ifindex; | |
199 | ||
200 | if (info->name) { | |
201 | nl_msg_put_string(buf, OVS_WIN_NETDEV_ATTR_NAME, info->name); | |
202 | error = 0; | |
203 | } | |
204 | ||
205 | return error; | |
206 | } | |
207 | ||
208 | static void | |
209 | netdev_windows_info_init(struct netdev_windows_netdev_info *info) | |
210 | { | |
211 | memset(info, 0, sizeof *info); | |
212 | } | |
213 | ||
214 | static int | |
215 | netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info, | |
216 | struct ofpbuf *buf) | |
217 | { | |
218 | static const struct nl_policy ovs_netdev_policy[] = { | |
219 | [OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 }, | |
220 | [OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 }, | |
221 | [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, | |
222 | [OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) }, | |
223 | [OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 }, | |
224 | [OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 }, | |
225 | }; | |
226 | ||
227 | struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)]; | |
228 | struct ovs_header *ovs_header; | |
229 | struct nlmsghdr *nlmsg; | |
230 | struct genlmsghdr *genl; | |
231 | struct ofpbuf b; | |
232 | ||
233 | netdev_windows_info_init(info); | |
234 | ||
235 | ofpbuf_use_const(&b, ofpbuf_data(buf), ofpbuf_size(buf)); | |
236 | nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); | |
237 | genl = ofpbuf_try_pull(&b, sizeof *genl); | |
238 | ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); | |
239 | if (!nlmsg || !genl || !ovs_header | |
240 | || nlmsg->nlmsg_type != ovs_win_netdev_family | |
241 | || !nl_policy_parse(&b, 0, ovs_netdev_policy, a, | |
242 | ARRAY_SIZE(ovs_netdev_policy))) { | |
243 | return EINVAL; | |
244 | } | |
245 | ||
246 | info->cmd = genl->cmd; | |
247 | info->dp_ifindex = ovs_header->dp_ifindex; | |
248 | info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]); | |
249 | info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]); | |
250 | info->name = nl_attr_get_string(a[OVS_WIN_NETDEV_ATTR_NAME]); | |
ee4dac3b NR |
251 | memcpy(info->mac_address, nl_attr_get_unspec(a[OVS_WIN_NETDEV_ATTR_MAC_ADDR], |
252 | sizeof(info->mac_address)), sizeof(info->mac_address)); | |
3e648bc5 NR |
253 | info->mtu = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_MTU]); |
254 | info->ifi_flags = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_IF_FLAGS]); | |
255 | ||
256 | return 0; | |
257 | } | |
258 | ||
259 | static int | |
260 | query_netdev(const char *devname, | |
261 | struct netdev_windows_netdev_info *info, | |
262 | struct ofpbuf **bufp) | |
263 | { | |
264 | int error = 0; | |
265 | struct ofpbuf *request_buf; | |
266 | ||
267 | ovs_assert(info != NULL); | |
268 | netdev_windows_info_init(info); | |
269 | ||
0bfecde1 | 270 | error = netdev_windows_init_(); |
3e648bc5 NR |
271 | if (error) { |
272 | if (info) { | |
273 | *bufp = NULL; | |
274 | netdev_windows_info_init(info); | |
275 | } | |
276 | return error; | |
277 | } | |
278 | ||
279 | request_buf = ofpbuf_new(1024); | |
280 | info->cmd = OVS_WIN_NETDEV_CMD_GET; | |
281 | info->name = devname; | |
282 | error = netdev_windows_netdev_to_ofpbuf(info, request_buf); | |
283 | if (error) { | |
284 | ofpbuf_delete(request_buf); | |
285 | return error; | |
286 | } | |
287 | ||
288 | error = nl_transact(NETLINK_GENERIC, request_buf, bufp); | |
289 | ofpbuf_delete(request_buf); | |
290 | ||
291 | if (info) { | |
292 | if (!error) { | |
293 | error = netdev_windows_netdev_from_ofpbuf(info, *bufp); | |
294 | } | |
295 | if (error) { | |
296 | netdev_windows_info_init(info); | |
297 | ofpbuf_delete(*bufp); | |
298 | *bufp = NULL; | |
299 | } | |
300 | } | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static void | |
306 | netdev_windows_destruct(struct netdev *netdev_) | |
307 | { | |
308 | ||
078eedf4 NR |
309 | } |
310 | ||
311 | static void | |
312 | netdev_windows_dealloc(struct netdev *netdev_) | |
313 | { | |
3e648bc5 NR |
314 | struct netdev_windows *netdev = netdev_windows_cast(netdev_); |
315 | free(netdev); | |
078eedf4 NR |
316 | } |
317 | ||
318 | static int | |
3bd0fd39 WSH |
319 | netdev_windows_get_etheraddr(const struct netdev *netdev_, |
320 | uint8_t mac[ETH_ADDR_LEN]) | |
078eedf4 | 321 | { |
3e648bc5 NR |
322 | struct netdev_windows *netdev = netdev_windows_cast(netdev_); |
323 | ||
324 | ovs_assert((netdev->cache_valid & VALID_ETHERADDR) != 0); | |
325 | if (netdev->cache_valid & VALID_ETHERADDR) { | |
326 | memcpy(mac, netdev->mac, ETH_ADDR_LEN); | |
327 | } else { | |
328 | return EINVAL; | |
329 | } | |
330 | return 0; | |
078eedf4 NR |
331 | } |
332 | ||
333 | static int | |
334 | netdev_windows_get_mtu(const struct netdev *netdev_, int *mtup) | |
335 | { | |
3e648bc5 NR |
336 | struct netdev_windows *netdev = netdev_windows_cast(netdev_); |
337 | ||
338 | ovs_assert((netdev->cache_valid & VALID_MTU) != 0); | |
339 | if (netdev->cache_valid & VALID_MTU) { | |
340 | *mtup = netdev->mtu; | |
341 | } else { | |
342 | return EINVAL; | |
343 | } | |
344 | return 0; | |
078eedf4 | 345 | } |
6cac056e AS |
346 | |
347 | /* This functionality is not really required by the datapath. | |
348 | * But vswitchd bringup expects this to be implemented. */ | |
349 | static int | |
3bd0fd39 WSH |
350 | netdev_windows_set_etheraddr(const struct netdev *netdev_, |
351 | uint8_t mac[ETH_ADDR_LEN]) | |
6cac056e AS |
352 | { |
353 | return 0; | |
354 | } | |
355 | ||
6cac056e AS |
356 | /* This functionality is not really required by the datapath. |
357 | * But vswitchd bringup expects this to be implemented. */ | |
358 | static int | |
ee4dac3b NR |
359 | netdev_windows_update_flags(struct netdev *netdev_, |
360 | enum netdev_flags off, | |
361 | enum netdev_flags on, | |
362 | enum netdev_flags *old_flagsp) | |
6cac056e | 363 | { |
ee4dac3b NR |
364 | struct netdev_windows *netdev = netdev_windows_cast(netdev_); |
365 | ||
366 | ovs_assert((netdev->cache_valid & VALID_IFFLAG) != 0); | |
367 | if (netdev->cache_valid & VALID_IFFLAG) { | |
368 | *old_flagsp = netdev->ifi_flags; | |
369 | /* Setting the interface flags is not supported. */ | |
370 | } else { | |
371 | return EINVAL; | |
372 | } | |
6cac056e AS |
373 | return 0; |
374 | } | |
375 | ||
078eedf4 NR |
376 | static int |
377 | netdev_windows_internal_construct(struct netdev *netdev_) | |
378 | { | |
379 | return netdev_windows_system_construct(netdev_); | |
380 | } | |
381 | ||
382 | ||
383 | #define NETDEV_WINDOWS_CLASS(NAME, CONSTRUCT) \ | |
384 | { \ | |
385 | .type = NAME, \ | |
078eedf4 NR |
386 | .alloc = netdev_windows_alloc, \ |
387 | .construct = CONSTRUCT, \ | |
3e648bc5 | 388 | .destruct = netdev_windows_destruct, \ |
078eedf4 NR |
389 | .dealloc = netdev_windows_dealloc, \ |
390 | .get_etheraddr = netdev_windows_get_etheraddr, \ | |
6cac056e | 391 | .set_etheraddr = netdev_windows_set_etheraddr, \ |
ee4dac3b | 392 | .update_flags = netdev_windows_update_flags, \ |
078eedf4 NR |
393 | } |
394 | ||
395 | const struct netdev_class netdev_windows_class = | |
396 | NETDEV_WINDOWS_CLASS( | |
397 | "system", | |
398 | netdev_windows_system_construct); | |
399 | ||
400 | const struct netdev_class netdev_internal_class = | |
401 | NETDEV_WINDOWS_CLASS( | |
402 | "internal", | |
403 | netdev_windows_internal_construct); |