netdev-windows: add code to query netdev information
[cascardo/ovs.git] / lib / netdev-windows.c
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"
27 #include "poll-loop.h"
28 #include "shash.h"
29 #include "svec.h"
30 #include "vlog.h"
31 #include "odp-netlink.h"
32 #include "netlink-socket.h"
33 #include "netlink.h"
34
35 VLOG_DEFINE_THIS_MODULE(netdev_windows);
36 static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
37
38 enum {
39     VALID_ETHERADDR         = 1 << 0,
40     VALID_MTU               = 1 << 1,
41     VALID_IFFLAG            = 1 << 5,
42 };
43
44 /* Caches the information of a netdev. */
45 struct netdev_windows {
46     struct netdev up;
47     int32_t dev_type;
48     uint32_t port_no;
49
50     unsigned int change_seq;
51
52     unsigned int cache_valid;
53     int ifindex;
54     uint8_t mac[ETH_ADDR_LEN];
55     uint32_t mtu;
56     unsigned int ifi_flags;
57 };
58
59 /* Utility structure for netdev commands. */
60 struct netdev_windows_netdev_info {
61     /* Generic Netlink header. */
62     uint8_t cmd;
63
64     /* Information that is relevant to ovs. */
65     uint32_t dp_ifindex;
66     uint32_t port_no;
67     uint32_t ovs_type;
68
69     /* General information of a network device. */
70     const char *name;
71     uint8_t mac_address[ETH_ADDR_LEN];
72     uint32_t mtu;
73     uint32_t ifi_flags;
74 };
75
76 static int refresh_port_status(struct netdev_windows *netdev);
77 static int query_netdev(const char *devname,
78                         struct netdev_windows_netdev_info *reply,
79                         struct ofpbuf **bufp);
80 static int netdev_windows_init(void);
81
82 /* Generic Netlink family numbers for OVS.
83  *
84  * Initialized by netdev_windows_init(). */
85 static int ovs_win_netdev_family;
86 struct nl_sock *ovs_win_netdev_sock;
87
88
89 static bool
90 is_netdev_windows_class(const struct netdev_class *netdev_class)
91 {
92     return netdev_class->init == netdev_windows_init;
93 }
94
95 static struct netdev_windows *
96 netdev_windows_cast(const struct netdev *netdev_)
97 {
98     ovs_assert(is_netdev_windows_class(netdev_get_class(netdev_)));
99     return CONTAINER_OF(netdev_, struct netdev_windows, up);
100 }
101
102 static int
103 netdev_windows_init(void)
104 {
105     int error = 0;
106     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
107
108     if (ovsthread_once_start(&once)) {
109         error = nl_lookup_genl_family(OVS_WIN_NETDEV_FAMILY,
110                                       &ovs_win_netdev_family);
111         if (error) {
112             VLOG_ERR("Generic Netlink family '%s' does not exist. "
113                      "The Open vSwitch kernel module is probably not loaded.",
114                      OVS_WIN_NETDEV_FAMILY);
115         }
116         if (!error) {
117             /* XXX: Where to close this socket? */
118             error = nl_sock_create(NETLINK_GENERIC, &ovs_win_netdev_sock);
119         }
120
121         ovsthread_once_done(&once);
122     }
123
124     return error;
125 }
126
127 static struct netdev *
128 netdev_windows_alloc(void)
129 {
130     struct netdev_windows *netdev = xzalloc(sizeof *netdev);
131     return netdev ? &netdev->up : NULL;
132 }
133
134 static int
135 netdev_windows_system_construct(struct netdev *netdev_)
136 {
137     struct netdev_windows *netdev = netdev_windows_cast(netdev_);
138     uint8_t mac[ETH_ADDR_LEN];
139     struct netdev_windows_netdev_info info;
140     struct ofpbuf *buf;
141     int ret;
142
143     /* Query the attributes and runtime status of the netdev. */
144     ret = query_netdev(netdev_get_name(&netdev->up), &info, &buf);
145     if (ret) {
146         return ret;
147     }
148     ofpbuf_delete(buf);
149
150     netdev->change_seq = 1;
151     netdev->dev_type = info.ovs_type;
152     netdev->port_no = info.port_no;
153
154     memcpy(netdev->mac, info.mac_address, ETH_ADDR_LEN);
155     netdev->cache_valid = VALID_ETHERADDR;
156     netdev->ifindex = -EOPNOTSUPP;
157
158     netdev->mtu = info.mtu;
159     netdev->cache_valid |= VALID_MTU;
160
161     netdev->ifi_flags = info.ifi_flags;
162     netdev->cache_valid |= VALID_IFFLAG;
163
164     VLOG_DBG("construct device %s, ovs_type: %u.",
165              netdev_get_name(&netdev->up), info.ovs_type);
166     return 0;
167 }
168
169 static int
170 netdev_windows_netdev_to_ofpbuf(struct netdev_windows_netdev_info *info,
171                                 struct ofpbuf *buf)
172 {
173     struct ovs_header *ovs_header;
174     int error = EINVAL;
175
176     nl_msg_put_genlmsghdr(buf, 0, ovs_win_netdev_family,
177                           NLM_F_REQUEST | NLM_F_ECHO,
178                           info->cmd, OVS_WIN_NETDEV_VERSION);
179
180     ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header);
181     ovs_header->dp_ifindex = info->dp_ifindex;
182
183     if (info->name) {
184         nl_msg_put_string(buf, OVS_WIN_NETDEV_ATTR_NAME, info->name);
185         error = 0;
186     }
187
188     return error;
189 }
190
191 static void
192 netdev_windows_info_init(struct netdev_windows_netdev_info *info)
193 {
194     memset(info, 0, sizeof *info);
195 }
196
197 static int
198 netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info,
199                                   struct ofpbuf *buf)
200 {
201     static const struct nl_policy ovs_netdev_policy[] = {
202         [OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 },
203         [OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 },
204         [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
205         [OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) },
206         [OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 },
207         [OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 },
208     };
209
210     struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)];
211     struct ovs_header *ovs_header;
212     struct nlmsghdr *nlmsg;
213     struct genlmsghdr *genl;
214     struct ofpbuf b;
215
216     netdev_windows_info_init(info);
217
218     ofpbuf_use_const(&b, ofpbuf_data(buf), ofpbuf_size(buf));
219     nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
220     genl = ofpbuf_try_pull(&b, sizeof *genl);
221     ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);
222     if (!nlmsg || !genl || !ovs_header
223         || nlmsg->nlmsg_type != ovs_win_netdev_family
224         || !nl_policy_parse(&b, 0, ovs_netdev_policy, a,
225                             ARRAY_SIZE(ovs_netdev_policy))) {
226         return EINVAL;
227     }
228
229     info->cmd = genl->cmd;
230     info->dp_ifindex = ovs_header->dp_ifindex;
231     info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]);
232     info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]);
233     info->name = nl_attr_get_string(a[OVS_WIN_NETDEV_ATTR_NAME]);
234     memcpy(info->mac_address, nl_attr_get_string(a[OVS_WIN_NETDEV_ATTR_NAME]),
235            sizeof(info->mac_address));
236     info->mtu = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_MTU]);
237     info->ifi_flags = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_IF_FLAGS]);
238
239     return 0;
240 }
241
242 static int
243 query_netdev(const char *devname,
244              struct netdev_windows_netdev_info *info,
245              struct ofpbuf **bufp)
246 {
247     int error = 0;
248     struct ofpbuf *request_buf;
249
250     ovs_assert(info != NULL);
251     netdev_windows_info_init(info);
252
253     error = netdev_windows_init();
254     if (error) {
255         if (info) {
256             *bufp = NULL;
257             netdev_windows_info_init(info);
258         }
259         return error;
260     }
261
262     request_buf = ofpbuf_new(1024);
263     info->cmd = OVS_WIN_NETDEV_CMD_GET;
264     info->name = devname;
265     error = netdev_windows_netdev_to_ofpbuf(info, request_buf);
266     if (error) {
267         ofpbuf_delete(request_buf);
268         return error;
269     }
270
271     error = nl_transact(NETLINK_GENERIC, request_buf, bufp);
272     ofpbuf_delete(request_buf);
273
274     if (info) {
275         if (!error) {
276             error = netdev_windows_netdev_from_ofpbuf(info, *bufp);
277         }
278         if (error) {
279             netdev_windows_info_init(info);
280             ofpbuf_delete(*bufp);
281             *bufp = NULL;
282         }
283     }
284
285     return 0;
286 }
287
288 static void
289 netdev_windows_destruct(struct netdev *netdev_)
290 {
291
292 }
293
294 static void
295 netdev_windows_dealloc(struct netdev *netdev_)
296 {
297     struct netdev_windows *netdev = netdev_windows_cast(netdev_);
298     free(netdev);
299 }
300
301 static int
302 netdev_windows_get_etheraddr(const struct netdev *netdev_, uint8_t mac[6])
303 {
304     struct netdev_windows *netdev = netdev_windows_cast(netdev_);
305
306     ovs_assert((netdev->cache_valid & VALID_ETHERADDR) != 0);
307     if (netdev->cache_valid & VALID_ETHERADDR) {
308         memcpy(mac, netdev->mac, ETH_ADDR_LEN);
309     } else {
310         return EINVAL;
311     }
312     return 0;
313 }
314
315 static int
316 netdev_windows_get_mtu(const struct netdev *netdev_, int *mtup)
317 {
318     struct netdev_windows *netdev = netdev_windows_cast(netdev_);
319
320     ovs_assert((netdev->cache_valid & VALID_MTU) != 0);
321     if (netdev->cache_valid & VALID_MTU) {
322         *mtup = netdev->mtu;
323     } else {
324         return EINVAL;
325     }
326     return 0;
327 }
328 \f
329
330 static int
331 netdev_windows_internal_construct(struct netdev *netdev_)
332 {
333     return netdev_windows_system_construct(netdev_);
334 }
335
336
337 #define NETDEV_WINDOWS_CLASS(NAME, CONSTRUCT)                           \
338 {                                                                       \
339     .type               = NAME,                                         \
340     .init               = netdev_windows_init,                          \
341     .alloc              = netdev_windows_alloc,                         \
342     .construct          = CONSTRUCT,                                    \
343     .destruct           = netdev_windows_destruct,                      \
344     .dealloc            = netdev_windows_dealloc,                       \
345     .get_etheraddr      = netdev_windows_get_etheraddr,                 \
346 }
347
348 const struct netdev_class netdev_windows_class =
349     NETDEV_WINDOWS_CLASS(
350         "system",
351         netdev_windows_system_construct);
352
353 const struct netdev_class netdev_internal_class =
354     NETDEV_WINDOWS_CLASS(
355         "internal",
356         netdev_windows_internal_construct);