465f0c5c9363040f867fc381d111f92fc91e7207
[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 "packets.h"
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
77 static int query_netdev(const char *devname,
78                         struct netdev_windows_netdev_info *reply,
79                         struct ofpbuf **bufp);
80 static struct netdev *netdev_windows_alloc(void);
81 static int netdev_windows_init_(void);
82
83 /* Generic Netlink family numbers for OVS.
84  *
85  * Initialized by netdev_windows_init_(). */
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 {
93     return netdev_class->alloc == netdev_windows_alloc;
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
103 static int
104 netdev_windows_init_(void)
105 {
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;
126 }
127
128 static struct netdev *
129 netdev_windows_alloc(void)
130 {
131     struct netdev_windows *netdev = xzalloc(sizeof *netdev);
132     return netdev ? &netdev->up : NULL;
133 }
134
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
151 static int
152 netdev_windows_system_construct(struct netdev *netdev_)
153 {
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
178     netdev->ifi_flags = dp_to_netdev_ifi_flags(info.ifi_flags);
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]);
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));
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
270     error = netdev_windows_init_();
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
309 }
310
311 static void
312 netdev_windows_dealloc(struct netdev *netdev_)
313 {
314     struct netdev_windows *netdev = netdev_windows_cast(netdev_);
315     free(netdev);
316 }
317
318 static int
319 netdev_windows_get_etheraddr(const struct netdev *netdev_,
320                              uint8_t mac[ETH_ADDR_LEN])
321 {
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;
331 }
332
333 static int
334 netdev_windows_get_mtu(const struct netdev *netdev_, int *mtup)
335 {
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;
345 }
346
347 /* This functionality is not really required by the datapath.
348  * But vswitchd bringup expects this to be implemented. */
349 static int
350 netdev_windows_set_etheraddr(const struct netdev *netdev_,
351                              uint8_t mac[ETH_ADDR_LEN])
352 {
353     return 0;
354 }
355
356 /* This functionality is not really required by the datapath.
357  * But vswitchd bringup expects this to be implemented. */
358 static int
359 netdev_windows_update_flags(struct netdev *netdev_,
360                             enum netdev_flags off,
361                             enum netdev_flags on,
362                             enum netdev_flags *old_flagsp)
363 {
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     }
373     return 0;
374 }
375
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,                                         \
386     .alloc              = netdev_windows_alloc,                         \
387     .construct          = CONSTRUCT,                                    \
388     .destruct           = netdev_windows_destruct,                      \
389     .dealloc            = netdev_windows_dealloc,                       \
390     .get_etheraddr      = netdev_windows_get_etheraddr,                 \
391     .set_etheraddr      = netdev_windows_set_etheraddr,                 \
392     .update_flags       = netdev_windows_update_flags,                  \
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);