|
1 /* |
|
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. |
|
3 */ |
|
4 |
|
5 /* |
|
6 * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. |
|
7 * |
|
8 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
9 * you may not use this file except in compliance with the License. |
|
10 * You may obtain a copy of the License at: |
|
11 * |
|
12 * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 * |
|
14 * Unless required by applicable law or agreed to in writing, software |
|
15 * distributed under the License is distributed on an "AS IS" BASIS, |
|
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
17 * See the License for the specific language governing permissions and |
|
18 * limitations under the License. |
|
19 */ |
|
20 |
|
21 #include <config.h> |
|
22 |
|
23 #include <errno.h> |
|
24 #include <fcntl.h> |
|
25 #include <arpa/inet.h> |
|
26 #include <inttypes.h> |
|
27 #include <sys/types.h> |
|
28 #include <sys/ioctl.h> |
|
29 #include <sys/socket.h> |
|
30 #include <netpacket/packet.h> |
|
31 #include <net/if.h> |
|
32 #include <net/if_arp.h> |
|
33 #include <net/route.h> |
|
34 #include <netinet/in.h> |
|
35 #include <poll.h> |
|
36 #include <stdlib.h> |
|
37 #include <string.h> |
|
38 #include <unistd.h> |
|
39 |
|
40 #include <sys/sockio.h> |
|
41 #include <strings.h> |
|
42 |
|
43 #include <libdllink.h> |
|
44 #include <kstat2.h> |
|
45 |
|
46 #include "coverage.h" |
|
47 #include "dpif-netdev.h" |
|
48 #include "dynamic-string.h" |
|
49 #include "fatal-signal.h" |
|
50 #include "hash.h" |
|
51 #include "hmap.h" |
|
52 #include "netdev-provider.h" |
|
53 #include "netdev-vport.h" |
|
54 #include "ofpbuf.h" |
|
55 #include "openflow/openflow.h" |
|
56 #include "ovs-atomic.h" |
|
57 #include "packets.h" |
|
58 #include "poll-loop.h" |
|
59 #include "shash.h" |
|
60 #include "socket-util.h" |
|
61 #include "sset.h" |
|
62 #include "timer.h" |
|
63 #include "unaligned.h" |
|
64 #include "vlog.h" |
|
65 |
|
66 #include "netdev-solaris.h" |
|
67 #include "util-solaris.h" |
|
68 |
|
69 /* |
|
70 * Enable vlog() for this module |
|
71 */ |
|
72 VLOG_DEFINE_THIS_MODULE(netdev_solaris); |
|
73 |
|
74 /* |
|
75 * Per-network device state. |
|
76 */ |
|
77 struct netdev_solaris { |
|
78 struct netdev up; |
|
79 |
|
80 /* Protects all members below. */ |
|
81 struct ovs_mutex mutex; |
|
82 |
|
83 /* |
|
84 * Bit mask of cached properties. These can be cached because |
|
85 * nothing besides vswitchd is altering the properties. |
|
86 */ |
|
87 unsigned int cache_valid; |
|
88 |
|
89 /* |
|
90 * Network device features (e.g., speed, duplex, etc) |
|
91 */ |
|
92 enum netdev_features current; |
|
93 enum netdev_features advertised; |
|
94 enum netdev_features supported; |
|
95 int get_features_error; |
|
96 |
|
97 int ifindex; /* interface index */ |
|
98 struct in_addr in4; /* IPv4 address */ |
|
99 struct in_addr netmask; /* IPv4 netmask */ |
|
100 struct in6_addr in6; /* IPv6 address */ |
|
101 unsigned int plumbed; /* per-family plumbed */ |
|
102 unsigned int implicitly_plumbed; /* per-family implicitly plumbed */ |
|
103 |
|
104 char class[DLADM_PROP_VAL_MAX]; /* datalink class */ |
|
105 char brname[MAXLINKNAMELEN]; /* bridge name */ |
|
106 int mtu; /* datalink MTU */ |
|
107 uint8_t etheraddr[ETH_ADDR_LEN]; /* MAC address */ |
|
108 |
|
109 struct tc *tc; /* traffic control */ |
|
110 }; |
|
111 |
|
112 /* |
|
113 * Bits indicating what network device configuration is cached. |
|
114 * See cache_valid field above. |
|
115 */ |
|
116 enum { |
|
117 VALID_IFINDEX = 1 << 0, |
|
118 VALID_ETHERADDR = 1 << 1, |
|
119 VALID_IN4 = 1 << 2, |
|
120 VALID_IN6 = 1 << 3, |
|
121 VALID_MTU = 1 << 4, |
|
122 VALID_LINKCLASS = 1 << 5, |
|
123 VALID_FEATURES = 1 << 6 |
|
124 }; |
|
125 |
|
126 /* |
|
127 * When network devices are constructed, the internal devices (i.e., bridges) |
|
128 * are initially created over this etherstub and are moved when uplink ports |
|
129 * are added to the bridge. |
|
130 */ |
|
131 #define NETDEV_IMPL_ETHERSTUB "ovs.etherstub0" |
|
132 |
|
133 /* |
|
134 * Used to track state of IP plumbing on network devices. |
|
135 */ |
|
136 #define SOLARIS_IPV4 0x01 |
|
137 #define SOLARIS_IPV6 0x02 |
|
138 |
|
139 /* |
|
140 * Cached socket descriptors. |
|
141 */ |
|
142 static int sock4; |
|
143 static int sock6; |
|
144 |
|
145 /* |
|
146 * Cached kstat2_handle_t. Re-use unless an error causes it to be |
|
147 * closed. In that case, cache the handle on the next open. |
|
148 */ |
|
149 static kstat2_handle_t nd_khandle; |
|
150 static boolean_t kstat2_handle_initialized = B_FALSE; |
|
151 static struct ovs_mutex kstat_mutex = OVS_MUTEX_INITIALIZER; |
|
152 |
|
153 static int netdev_solaris_init(void); |
|
154 static struct netdev_solaris *netdev_solaris_cast(const struct netdev *); |
|
155 |
|
156 /* |
|
157 * An instance of a traffic control class. |
|
158 * |
|
159 * Always associated with a particular network device. |
|
160 * |
|
161 * Each TC implementation subclasses this with whatever additional data |
|
162 * it needs. |
|
163 */ |
|
164 struct tc { |
|
165 const struct tc_ops *ops; |
|
166 |
|
167 /* |
|
168 * Contains "struct tc_queues"s. |
|
169 * Read by generic TC layer. |
|
170 * Written only by TC implementation. |
|
171 */ |
|
172 struct hmap queues; |
|
173 }; |
|
174 |
|
175 #define TC_INITIALIZER(TC, OPS) { OPS, HMAP_INITIALIZER(&(TC)->queues) } |
|
176 |
|
177 /* |
|
178 * One traffic control queue. |
|
179 * |
|
180 * Each TC implementation subclasses this with whatever additional |
|
181 * data it needs. |
|
182 */ |
|
183 struct tc_queue { |
|
184 struct hmap_node hmap_node; /* In struct tc's "queues" hmap. */ |
|
185 unsigned int queue_id; /* OpenFlow queue ID. */ |
|
186 long long int created; /* Time queue was created, in msecs. */ |
|
187 }; |
|
188 |
|
189 /* |
|
190 * A particular kind of traffic control. |
|
191 * |
|
192 * Each implementation generally maps to one particular Linux qdisc class. |
|
193 * |
|
194 * The functions below return 0 if successful or a positive errno value on |
|
195 * failure, except where otherwise noted. All of them must be provided, |
|
196 * except where otherwise noted. |
|
197 */ |
|
198 struct tc_ops { |
|
199 /* |
|
200 * Name used by kernel in the TCA_KIND attribute of tcmsg, e.g. "htb". |
|
201 * This is null for tc_ops_default since there is no appropriate |
|
202 * value. |
|
203 */ |
|
204 const char *linux_name; |
|
205 |
|
206 /* |
|
207 * Name used in OVS database, e.g. "linux-htb". Must be nonnull. |
|
208 */ |
|
209 const char *ovs_name; |
|
210 |
|
211 /* |
|
212 * Number of supported OpenFlow queues, 0 for qdiscs that have no |
|
213 * queues. The queues are numbered 0 through n_queues - 1. |
|
214 */ |
|
215 unsigned int n_queues; |
|
216 |
|
217 /* |
|
218 * Called to install this TC class on 'netdev'. The implementation |
|
219 * should make the calls required to set up 'netdev' with the right |
|
220 * qdisc and configure it according to 'details'. The implementation |
|
221 * may assume that the current qdisc is the default; that is, there |
|
222 * is no need for it to delete the current qdisc before installing |
|
223 * itself. |
|
224 * |
|
225 * The contents of 'details' should be documented as valid for |
|
226 * 'ovs_name' in the "other_config" column in the "QoS" table in |
|
227 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). |
|
228 * |
|
229 * This function must return 0 if and only if it sets 'netdev->tc' |
|
230 * to an initialized 'struct tc'. |
|
231 * |
|
232 * This function should always be nonnull. |
|
233 */ |
|
234 int (*tc_install)(struct netdev *netdev, const struct smap *details); |
|
235 |
|
236 /* |
|
237 * Called when the netdev code determines that this TC class's qdisc |
|
238 * is installed on 'netdev', but we didn't install it ourselves and |
|
239 * so don't know any of the details. |
|
240 * |
|
241 * There is nothing for this function to do on Solaris. |
|
242 * |
|
243 * This function must return 0 if and only if it sets 'netdev->tc' |
|
244 * to an initialized 'struct tc'. |
|
245 */ |
|
246 int (*tc_load)(struct netdev *netdev, struct ofpbuf *nlmsg); |
|
247 |
|
248 /* |
|
249 * Destroys the data structures allocated by the implementation as |
|
250 * part of 'tc'. (This includes destroying 'tc->queues' by calling |
|
251 * tc_destroy(tc). |
|
252 * |
|
253 * This function may be null if 'tc' is trivial. |
|
254 */ |
|
255 void (*tc_destroy)(struct tc *tc); |
|
256 |
|
257 /* |
|
258 * Retrieves details of 'netdev->tc' configuration into 'details'. |
|
259 * |
|
260 * The contents of 'details' should be documented as valid for |
|
261 * 'ovs_name' in the "other_config" column in the "QoS" table in |
|
262 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). |
|
263 * |
|
264 * This function may be null if 'tc' is not configurable. |
|
265 */ |
|
266 int (*qdisc_get)(const struct netdev *netdev, struct smap *details); |
|
267 |
|
268 /* |
|
269 * Reconfigures 'netdev->tc' according to 'details'. |
|
270 * |
|
271 * The contents of 'details' should be documented as valid for |
|
272 * 'ovs_name' in the "other_config" column in the "QoS" table in |
|
273 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). |
|
274 * |
|
275 * This function may be null if 'tc' is not configurable. |
|
276 */ |
|
277 int (*qdisc_set)(struct netdev *, const struct smap *details); |
|
278 |
|
279 /* |
|
280 * Retrieves details of 'queue' on 'netdev->tc' into 'details'. |
|
281 * 'queue' is one of the 'struct tc_queue's within |
|
282 * 'netdev->tc->queues'. |
|
283 * |
|
284 * The contents of 'details' should be documented as valid for |
|
285 * 'ovs_name' in the "other_config" column in the "Queue" table in |
|
286 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). |
|
287 * |
|
288 * This function may be null if 'tc' does not have queues |
|
289 * ('n_queues' is 0). |
|
290 */ |
|
291 int (*class_get)(const struct netdev *netdev, |
|
292 const struct tc_queue *queue, struct smap *details); |
|
293 |
|
294 /* |
|
295 * Configures or reconfigures 'queue_id' on 'netdev->tc' according to |
|
296 * 'details'. The caller ensures that 'queue_id' is less than |
|
297 * 'n_queues'. |
|
298 * |
|
299 * The contents of 'details' should be documented as valid for |
|
300 * 'ovs_name' in the "other_config" column in the "Queue" table in |
|
301 * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). |
|
302 * |
|
303 * This function may be null if 'tc' does not have queues or its |
|
304 * queues are not configurable. |
|
305 */ |
|
306 int (*class_set)(struct netdev *, unsigned int queue_id, |
|
307 const struct smap *details); |
|
308 |
|
309 /* |
|
310 * Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct |
|
311 * tc_queue's within 'netdev->tc->queues'. |
|
312 * |
|
313 * This function may be null if 'tc' does not have queues or its queues |
|
314 * cannot be deleted. |
|
315 */ |
|
316 int (*class_delete)(struct netdev *, struct tc_queue *queue); |
|
317 |
|
318 /* |
|
319 * Obtains stats for 'queue' from 'netdev->tc'. 'queue' is one of the |
|
320 * 'struct tc_queue's within 'netdev->tc->queues'. |
|
321 * |
|
322 * On success, initializes '*stats'. |
|
323 * |
|
324 * This function may be null if 'tc' does not have queues or if it |
|
325 * cannot report queue statistics. |
|
326 */ |
|
327 int (*class_get_stats)(const struct netdev *netdev, |
|
328 const struct tc_queue *queue, struct netdev_queue_stats *stats); |
|
329 |
|
330 /* |
|
331 * Extracts queue stats from 'nlmsg', which is a response to a |
|
332 * RTM_GETTCLASS message, and passes them to 'cb' along with 'aux'. |
|
333 * |
|
334 * This function may be null if 'tc' does not have queues or if it |
|
335 * cannot report queue statistics. |
|
336 */ |
|
337 int (*class_dump_stats)(const struct netdev *netdev, |
|
338 const struct ofpbuf *nlmsg, netdev_dump_queue_stats_cb *cb, |
|
339 void *aux); |
|
340 }; |
|
341 |
|
342 static void |
|
343 tc_init(struct tc *tc, const struct tc_ops *ops) |
|
344 { |
|
345 tc->ops = ops; |
|
346 hmap_init(&tc->queues); |
|
347 } |
|
348 |
|
349 static void |
|
350 tc_destroy(struct tc *tc) |
|
351 { |
|
352 hmap_destroy(&tc->queues); |
|
353 } |
|
354 |
|
355 static const struct tc_ops tc_ops_htb; |
|
356 static const struct tc_ops tc_ops_default; |
|
357 |
|
358 static const struct tc_ops *const tcs[] = { |
|
359 &tc_ops_htb, /* Hierarchy token bucket */ |
|
360 &tc_ops_default, /* Default qdisc */ |
|
361 NULL |
|
362 }; |
|
363 |
|
364 static const struct tc_ops * |
|
365 tc_lookup_ovs_name(const char *name) |
|
366 { |
|
367 const struct tc_ops *const *opsp; |
|
368 |
|
369 for (opsp = tcs; *opsp != NULL; opsp++) { |
|
370 const struct tc_ops *ops = *opsp; |
|
371 if (strcmp(name, ops->ovs_name) == 0) { |
|
372 return (ops); |
|
373 } |
|
374 } |
|
375 return (NULL); |
|
376 } |
|
377 |
|
378 static struct tc_queue * |
|
379 tc_find_queue__(const struct netdev *netdev_, unsigned int queue_id, |
|
380 size_t hash) |
|
381 { |
|
382 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
383 struct tc_queue *queue; |
|
384 |
|
385 HMAP_FOR_EACH_IN_BUCKET(queue, hmap_node, hash, &netdev->tc->queues) { |
|
386 if (queue->queue_id == queue_id) { |
|
387 return (queue); |
|
388 } |
|
389 } |
|
390 return (NULL); |
|
391 } |
|
392 |
|
393 static struct tc_queue * |
|
394 tc_find_queue(const struct netdev *netdev, unsigned int queue_id) |
|
395 { |
|
396 return (tc_find_queue__(netdev, queue_id, hash_int(queue_id, 0))); |
|
397 } |
|
398 |
|
399 static int |
|
400 tc_delete_class(const struct netdev *netdev OVS_UNUSED, |
|
401 unsigned int handle OVS_UNUSED) |
|
402 { |
|
403 return (0); |
|
404 } |
|
405 |
|
406 static int |
|
407 tc_del_qdisc(struct netdev *netdev_) |
|
408 { |
|
409 const char *netdev_name = netdev_get_name(netdev_); |
|
410 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
411 |
|
412 VLOG_DBG("tc_del_qdisc device %s", netdev_name); |
|
413 if (netdev->tc) { |
|
414 if (netdev->tc->ops->tc_destroy) { |
|
415 netdev->tc->ops->tc_destroy(netdev->tc); |
|
416 } |
|
417 netdev->tc = NULL; |
|
418 } |
|
419 return (0); |
|
420 } |
|
421 |
|
422 /* |
|
423 * If 'netdev''s qdisc type and parameters are not yet known, queries the |
|
424 * kernel to determine what they are. Returns 0 if successful, otherwise a |
|
425 * positive errno value. |
|
426 */ |
|
427 static int |
|
428 tc_query_qdisc(const struct netdev *netdev_) |
|
429 { |
|
430 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
431 const struct tc_ops *ops; |
|
432 int load_error; |
|
433 int error; |
|
434 |
|
435 if (netdev->tc) { |
|
436 return (0); |
|
437 } |
|
438 |
|
439 /* |
|
440 * Either it's a built-in qdisc, or it's a qdisc set up by some |
|
441 * other entity that doesn't have a handle 1:0. We will assume |
|
442 * that it's the system default qdisc. |
|
443 */ |
|
444 ops = &tc_ops_default; |
|
445 error = 0; |
|
446 |
|
447 /* Instantiate it. */ |
|
448 load_error = ops->tc_load(CONST_CAST(struct netdev *, netdev_), NULL); |
|
449 ovs_assert((load_error == 0) == (netdev->tc != NULL)); |
|
450 |
|
451 return (error ? error : load_error); |
|
452 } |
|
453 |
|
454 static bool |
|
455 is_netdev_solaris_class(const struct netdev_class *netdev_class) |
|
456 { |
|
457 return (netdev_class->init == netdev_solaris_init); |
|
458 } |
|
459 |
|
460 static struct netdev_solaris * |
|
461 netdev_solaris_cast(const struct netdev *netdev) |
|
462 { |
|
463 ovs_assert(is_netdev_solaris_class(netdev_get_class(netdev))); |
|
464 |
|
465 return (CONTAINER_OF(netdev, struct netdev_solaris, up)); |
|
466 } |
|
467 |
|
468 static int |
|
469 netdev_solaris_plumb(const struct netdev *netdev_, sa_family_t af) |
|
470 { |
|
471 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
472 const char *netdev_name = netdev_get_name(netdev_); |
|
473 char *astring; |
|
474 int sock; |
|
475 int proto; |
|
476 int error; |
|
477 |
|
478 ovs_assert(af == AF_INET || af == AF_INET6); |
|
479 |
|
480 if (af == AF_INET) { |
|
481 sock = sock4; |
|
482 astring = "IPv4"; |
|
483 proto = SOLARIS_IPV4; |
|
484 } else { |
|
485 sock = sock6; |
|
486 astring = "IPv6"; |
|
487 proto = SOLARIS_IPV6; |
|
488 } |
|
489 |
|
490 if ((netdev->plumbed & proto) != 0) |
|
491 return (0); |
|
492 |
|
493 error = solaris_plumb_if(sock, netdev_name, af); |
|
494 if (error != 0) { |
|
495 VLOG_ERR("%s device could not be plumbed", netdev_name); |
|
496 return (error); |
|
497 } |
|
498 VLOG_DBG("%s device plumbed for %s", netdev_name, astring); |
|
499 |
|
500 netdev->implicitly_plumbed |= proto; |
|
501 netdev->plumbed |= proto; |
|
502 return (0); |
|
503 } |
|
504 |
|
505 static int |
|
506 netdev_solaris_unplumb(const struct netdev *netdev_, sa_family_t af) |
|
507 { |
|
508 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
509 const char *netdev_name = netdev_get_name(netdev_); |
|
510 char *astring; |
|
511 int sock; |
|
512 int proto; |
|
513 int error; |
|
514 |
|
515 ovs_assert(af == AF_INET || af == AF_INET6); |
|
516 |
|
517 if (af == AF_INET) { |
|
518 sock = sock4; |
|
519 astring = "IPv4"; |
|
520 proto = SOLARIS_IPV4; |
|
521 } else { |
|
522 sock = sock6; |
|
523 astring = "IPv6"; |
|
524 proto = SOLARIS_IPV6; |
|
525 } |
|
526 |
|
527 if ((netdev->implicitly_plumbed & proto) == 0) |
|
528 return (0); |
|
529 |
|
530 error = solaris_unplumb_if(sock, netdev_name, af); |
|
531 if (error != 0) { |
|
532 VLOG_ERR("%s device could not be unplumbed", netdev_name); |
|
533 return (error); |
|
534 } |
|
535 VLOG_ERR("%s device unplumbed for %s", netdev_name, astring); |
|
536 netdev->implicitly_plumbed &= ~proto; |
|
537 netdev->plumbed &= ~proto; |
|
538 |
|
539 return (0); |
|
540 } |
|
541 |
|
542 struct solaris_netdev_sdmap { |
|
543 const char *sns_val; |
|
544 enum netdev_features sns_feature; |
|
545 }; |
|
546 |
|
547 static int |
|
548 netdev_solaris_chk_speed_duplex(const char *netdev_name, |
|
549 const char *field_name, enum netdev_features *features) |
|
550 { |
|
551 struct solaris_netdev_sdmap map [] = { |
|
552 {"10G-f", NETDEV_F_10GB_FD}, |
|
553 {"1G-f", NETDEV_F_1GB_FD}, |
|
554 {"1G-h", NETDEV_F_1GB_HD}, |
|
555 {"100M-f", NETDEV_F_100MB_FD}, |
|
556 {"100M-h", NETDEV_F_100MB_HD}, |
|
557 {"10M-f", NETDEV_F_100MB_FD}, |
|
558 {"10M-h", NETDEV_F_10MB_HD}, |
|
559 {"40G-f", NETDEV_F_40GB_FD}, |
|
560 {"100G-f", NETDEV_F_100GB_FD}, |
|
561 {"1T-f", NETDEV_F_1TB_FD}, |
|
562 {NULL, NETDEV_F_OTHER}, |
|
563 }; |
|
564 struct solaris_netdev_sdmap *snsp; |
|
565 char buffer[DLADM_PROP_VAL_MAX]; |
|
566 int i; |
|
567 int error; |
|
568 |
|
569 error = solaris_get_dlprop(netdev_name, "speed-duplex", field_name, |
|
570 buffer, sizeof (buffer)); |
|
571 if (error != 0) { |
|
572 VLOG_ERR("Unable to retrieve feature speed-duplex for %s " |
|
573 "device", netdev_name); |
|
574 return (error); |
|
575 } |
|
576 snsp = map; |
|
577 for (i = 0; snsp[i].sns_val != NULL; i++) { |
|
578 if (strstr(buffer, snsp[i].sns_val) != NULL) |
|
579 *features |= snsp[i].sns_feature; |
|
580 } |
|
581 return (0); |
|
582 } |
|
583 |
|
584 static int |
|
585 netdev_solaris_read_features(struct netdev_solaris *netdev, |
|
586 const char *netdev_name) |
|
587 { |
|
588 char buffer[DLADM_PROP_VAL_MAX]; |
|
589 int error = 0; |
|
590 int speed; |
|
591 bool full; |
|
592 |
|
593 if (netdev->cache_valid & VALID_FEATURES) |
|
594 goto exit; |
|
595 |
|
596 /* |
|
597 * Supported features |
|
598 * |
|
599 * Unsupported features: |
|
600 * NETDEV_F_COPPER |
|
601 * NETDEV_F_FIBER |
|
602 * NETDEV_F_PAUSE; |
|
603 * NETDEV_F_PAUSE_ASYM; |
|
604 */ |
|
605 netdev->supported = 0; |
|
606 if ((error = netdev_solaris_chk_speed_duplex(netdev_name, |
|
607 "possible", &netdev->supported)) != 0) |
|
608 goto exit; |
|
609 /* |
|
610 * Advertised features. |
|
611 * |
|
612 * Unsupported features: |
|
613 * NETDEV_F_COPPER |
|
614 * NETDEV_F_FIBER |
|
615 * NETDEV_F_PAUSE; |
|
616 * NETDEV_F_PAUSE_ASYM; |
|
617 */ |
|
618 netdev->advertised = 0; |
|
619 if ((error = netdev_solaris_chk_speed_duplex(netdev_name, |
|
620 "current", &netdev->advertised)) != 0) |
|
621 goto exit; |
|
622 |
|
623 /* Current settings. */ |
|
624 error = solaris_get_dlprop(netdev_name, "speed", "current", buffer, |
|
625 sizeof (buffer)); |
|
626 if (error != 0) { |
|
627 VLOG_ERR("Unable to retrieve speed for %s device", |
|
628 netdev_name); |
|
629 goto exit; |
|
630 } |
|
631 speed = atoi(buffer); |
|
632 |
|
633 error = solaris_get_dlprop(netdev_name, "duplex", "current", buffer, |
|
634 sizeof (buffer)); |
|
635 if (error != 0) { |
|
636 VLOG_ERR("Unable to retrieve duplex for %s device", |
|
637 netdev_name); |
|
638 goto exit; |
|
639 } |
|
640 full = (strcmp(buffer, "full") == 0); |
|
641 |
|
642 speed /= 1000000; |
|
643 if (speed == 10) { |
|
644 netdev->current = full ? NETDEV_F_10MB_FD : NETDEV_F_10MB_HD; |
|
645 } else if (speed == 100) { |
|
646 netdev->current = full ? NETDEV_F_100MB_FD : NETDEV_F_100MB_HD; |
|
647 } else if (speed == 1000) { |
|
648 netdev->current = full ? NETDEV_F_1GB_FD : NETDEV_F_1GB_HD; |
|
649 } else if (speed == 10000) { |
|
650 netdev->current = NETDEV_F_10GB_FD; |
|
651 } else if (speed == 40000) { |
|
652 netdev->current = NETDEV_F_40GB_FD; |
|
653 } else if (speed == 100000) { |
|
654 netdev->current = NETDEV_F_100GB_FD; |
|
655 } else if (speed == 1000000) { |
|
656 netdev->current = NETDEV_F_1TB_FD; |
|
657 } else { |
|
658 netdev->current = 0; |
|
659 } |
|
660 netdev->advertised |= (netdev->supported & NETDEV_F_AUTONEG); |
|
661 netdev->cache_valid |= VALID_FEATURES; |
|
662 exit: |
|
663 netdev->get_features_error = error; |
|
664 return (error); |
|
665 } |
|
666 |
|
667 static int |
|
668 netdev_solaris_init(void) |
|
669 { |
|
670 int error; |
|
671 |
|
672 sock4 = socket(AF_INET, SOCK_DGRAM, 0); |
|
673 if (sock4 < 0) { |
|
674 error = errno; |
|
675 VLOG_ERR("failed to create AF_INET socket (%s)", |
|
676 ovs_strerror(error)); |
|
677 return (error); |
|
678 } |
|
679 |
|
680 sock6 = socket(AF_INET6, SOCK_DGRAM, 0); |
|
681 if (sock6 < 0) { |
|
682 error = errno; |
|
683 VLOG_ERR("failed to create AF_INET6 socket (%s)", |
|
684 ovs_strerror(error)); |
|
685 return (error); |
|
686 } |
|
687 |
|
688 error = solaris_init_rad(); |
|
689 return (error); |
|
690 } |
|
691 |
|
692 static void |
|
693 netdev_solaris_run(void) |
|
694 { |
|
695 } |
|
696 |
|
697 static void |
|
698 netdev_solaris_wait(void) |
|
699 { |
|
700 } |
|
701 |
|
702 static struct netdev * |
|
703 netdev_solaris_alloc(void) |
|
704 { |
|
705 struct netdev_solaris *netdev = xzalloc(sizeof (*netdev)); |
|
706 |
|
707 return (&netdev->up); |
|
708 } |
|
709 |
|
710 static int |
|
711 netdev_solaris_unconfigure_uplink(const struct netdev *netdev_) |
|
712 { |
|
713 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
714 const char *netdev_name = netdev_get_name(netdev_); |
|
715 int error; |
|
716 |
|
717 VLOG_DBG("netdev_solaris_unconfigure_uplink device %s", netdev_name); |
|
718 |
|
719 error = solaris_modify_vnic(NETDEV_IMPL_ETHERSTUB, netdev->brname); |
|
720 if (error != 0 && error != ENODEV) { |
|
721 VLOG_ERR("failed to unconfigure %s as uplink for %s: " |
|
722 "%s", netdev_name, netdev->brname, |
|
723 ovs_strerror(error)); |
|
724 } |
|
725 return (0); |
|
726 } |
|
727 |
|
728 static int |
|
729 netdev_solaris_configure_uplink(const struct netdev *netdev_, |
|
730 const char *brname) |
|
731 { |
|
732 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
733 const char *netdev_name = netdev_get_name(netdev_); |
|
734 char buffer[DLADM_PROP_VAL_MAX]; |
|
735 int error; |
|
736 |
|
737 VLOG_DBG("netdev_solaris_configure_uplink device %s", netdev_name); |
|
738 |
|
739 /* |
|
740 * Normally, we would expect to see that the bridge VNIC has already |
|
741 * been created by a call to netdev_solaris_construct() to create the |
|
742 * bridge netdev. However, on a service restart ovs-vswitchd sometimes |
|
743 * adds the lower-link port prior to adding the bridge's internal port. |
|
744 * As a result, we need to create the bridge VNIC here if it does not |
|
745 * already exist. |
|
746 */ |
|
747 error = solaris_get_dlprop(brname, "lower-link", "current", |
|
748 buffer, sizeof (buffer)); |
|
749 if (error == ENODEV) { |
|
750 VLOG_DBG("%s vnic being created on %s", |
|
751 brname, netdev_name); |
|
752 error = solaris_create_vnic(netdev_name, brname); |
|
753 if (error != 0) { |
|
754 VLOG_ERR("Failed to create vnic for %s: %s", |
|
755 brname, ovs_strerror(error)); |
|
756 } |
|
757 goto exit; |
|
758 } else if (error != 0) { |
|
759 VLOG_ERR("Failed to get the lower-link for %s: %s", |
|
760 brname, ovs_strerror(error)); |
|
761 goto exit; |
|
762 } |
|
763 |
|
764 /* |
|
765 * If the lower-link is already set correctly, then return with |
|
766 * success. |
|
767 */ |
|
768 if (strcmp(buffer, netdev_name) == 0) { |
|
769 error = 0; |
|
770 goto exit; |
|
771 } |
|
772 |
|
773 /* |
|
774 * If the lower-link is already set to something other than the |
|
775 * etherstub, something is wrong. |
|
776 */ |
|
777 if (strcmp(buffer, NETDEV_IMPL_ETHERSTUB) != 0) { |
|
778 VLOG_ERR("Bridge already has uplink %s", buffer); |
|
779 error = EEXIST; |
|
780 goto exit; |
|
781 } |
|
782 |
|
783 /* |
|
784 * This is the "normal" case, where the bridge VNIC existed and had |
|
785 * the etherstub as its lower-link. Move the VNIC to its uplink. |
|
786 */ |
|
787 error = solaris_modify_vnic(netdev_name, brname); |
|
788 if (error != 0) { |
|
789 VLOG_ERR("failed to configure %s as uplink: %s", |
|
790 netdev_name, ovs_strerror(error)); |
|
791 goto exit; |
|
792 } |
|
793 (void) strlcpy(netdev->brname, brname, sizeof (netdev->brname)); |
|
794 |
|
795 exit: |
|
796 return (error); |
|
797 } |
|
798 |
|
799 static boolean_t |
|
800 solaris_is_if_plumbed(struct netdev *netdev_, sa_family_t af, uint64_t *flags) |
|
801 { |
|
802 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
803 const char *netdev_name = netdev_get_name(netdev_); |
|
804 int sock; |
|
805 int proto; |
|
806 int error; |
|
807 char *astring; |
|
808 |
|
809 ovs_assert(af == AF_INET || af == AF_INET6); |
|
810 |
|
811 if (af == AF_INET) { |
|
812 sock = sock4; |
|
813 astring = "IPv4"; |
|
814 proto = SOLARIS_IPV4; |
|
815 } else { |
|
816 sock = sock6; |
|
817 astring = "IPv6"; |
|
818 proto = SOLARIS_IPV6; |
|
819 } |
|
820 error = solaris_if_enabled(sock, netdev_name, flags); |
|
821 if (error == 0) { |
|
822 netdev->plumbed |= proto; |
|
823 } else if (error != ENXIO) { |
|
824 VLOG_DBG("netdev_is_if_plumbed %s device encountered " |
|
825 "error %d for %s", netdev_name, error, astring); |
|
826 } |
|
827 return (error == 0); |
|
828 } |
|
829 |
|
830 int |
|
831 netdev_create_impl_etherstub(void) |
|
832 { |
|
833 int error; |
|
834 |
|
835 error = solaris_create_etherstub(NETDEV_IMPL_ETHERSTUB); |
|
836 if (error != 0) { |
|
837 VLOG_ERR("netdev_create_impl_etherstub: failed to create %s: " |
|
838 "%s", NETDEV_IMPL_ETHERSTUB, ovs_strerror(error)); |
|
839 |
|
840 } else { |
|
841 boolean_t bval = B_TRUE; |
|
842 |
|
843 error = solaris_set_dlprop_boolean(NETDEV_IMPL_ETHERSTUB, |
|
844 "openvswitch", &bval); |
|
845 if (error != 0) { |
|
846 (void) solaris_delete_etherstub(NETDEV_IMPL_ETHERSTUB); |
|
847 VLOG_ERR("netdev_create_impl_etherstub: failed to " |
|
848 "set 'openvswitch' property on %s: %s: ", |
|
849 NETDEV_IMPL_ETHERSTUB, ovs_strerror(error)); |
|
850 } |
|
851 } |
|
852 return (error); |
|
853 } |
|
854 |
|
855 void |
|
856 netdev_delete_impl_etherstub(void) |
|
857 { |
|
858 boolean_t bval = B_FALSE; |
|
859 int error; |
|
860 |
|
861 error = solaris_set_dlprop_boolean(NETDEV_IMPL_ETHERSTUB, |
|
862 "openvswitch", &bval); |
|
863 if (error != 0) { |
|
864 VLOG_ERR("netdev_create_impl_etherstub: failed to " |
|
865 "set 'openvswitch' property on %s: %s: ", |
|
866 NETDEV_IMPL_ETHERSTUB, ovs_strerror(error)); |
|
867 } else { |
|
868 error = solaris_delete_etherstub(NETDEV_IMPL_ETHERSTUB); |
|
869 if (error != 0) { |
|
870 VLOG_ERR("netdev_delete_impl_etherstub: failed to " |
|
871 "delete %s: %s", NETDEV_IMPL_ETHERSTUB, |
|
872 ovs_strerror(error)); |
|
873 } |
|
874 } |
|
875 } |
|
876 |
|
877 boolean_t |
|
878 netdev_impl_etherstub_exists(void) |
|
879 { |
|
880 return (solaris_etherstub_exists(NETDEV_IMPL_ETHERSTUB)); |
|
881 } |
|
882 |
|
883 /* |
|
884 * Creates system and internal devices. Really very little to |
|
885 * do here given that neither system or internal devices must |
|
886 * exist in the kernel yet. |
|
887 */ |
|
888 static int |
|
889 netdev_solaris_construct(struct netdev *netdev_) |
|
890 { |
|
891 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
892 const char *netdev_name = netdev_get_name(netdev_); |
|
893 char buffer[DLADM_PROP_VAL_MAX]; |
|
894 uint64_t flags; |
|
895 int error; |
|
896 |
|
897 ovs_mutex_init(&netdev->mutex); |
|
898 if (netdev->up.netdev_class != &netdev_internal_class) { |
|
899 VLOG_DBG("netdev_solaris_construct system device %s", |
|
900 netdev_name); |
|
901 } else { |
|
902 VLOG_DBG("netdev_solaris_construct internal device %s", |
|
903 netdev_name); |
|
904 } |
|
905 netdev->implicitly_plumbed = 0; |
|
906 netdev->plumbed = 0; |
|
907 |
|
908 (void) solaris_is_if_plumbed(netdev_, AF_INET, &flags); |
|
909 (void) solaris_is_if_plumbed(netdev_, AF_INET6, &flags); |
|
910 |
|
911 /* |
|
912 * loopback is illegal. |
|
913 */ |
|
914 if (netdev->plumbed != 0 && flags & NETDEV_LOOPBACK) { |
|
915 VLOG_ERR("%s: cannot add a loopback device", |
|
916 netdev_name); |
|
917 return (EINVAL); |
|
918 } |
|
919 |
|
920 if (netdev->up.netdev_class == &netdev_internal_class) { |
|
921 error = solaris_get_dlprop(netdev_name, "lower-link", |
|
922 "current", buffer, sizeof (buffer)); |
|
923 if (error == ENODEV) { |
|
924 error = solaris_create_vnic(NETDEV_IMPL_ETHERSTUB, |
|
925 netdev_name); |
|
926 if (error != 0) { |
|
927 VLOG_ERR("failed to configure %s as uplink: " |
|
928 "%s", netdev_name, ovs_strerror(error)); |
|
929 return (error); |
|
930 } |
|
931 } else if (error != 0) { |
|
932 VLOG_ERR("Failed to get the lower-link for %s: %s", |
|
933 netdev_name, ovs_strerror(error)); |
|
934 } |
|
935 } |
|
936 return (0); |
|
937 } |
|
938 |
|
939 static void |
|
940 netdev_solaris_destruct(struct netdev *netdev_) |
|
941 { |
|
942 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
943 const char *netdev_name = netdev_get_name(netdev_); |
|
944 int error; |
|
945 |
|
946 VLOG_DBG("netdev_solaris_destruct device %s", netdev_name); |
|
947 |
|
948 /* |
|
949 * If implicitly plumbed, then unplumb it. |
|
950 */ |
|
951 (void) netdev_solaris_unplumb(netdev_, AF_INET); |
|
952 (void) netdev_solaris_unplumb(netdev_, AF_INET6); |
|
953 |
|
954 if (netdev->up.netdev_class == &netdev_internal_class) { |
|
955 error = solaris_delete_vnic(netdev_name); |
|
956 if (error != 0) { |
|
957 VLOG_ERR("failed to delete %s: %s", |
|
958 netdev_name, ovs_strerror(error)); |
|
959 } |
|
960 } else { |
|
961 if (netdev->brname[0] != '\0') { |
|
962 netdev_solaris_unconfigure_uplink(netdev_); |
|
963 } |
|
964 } |
|
965 |
|
966 ovs_mutex_destroy(&netdev->mutex); |
|
967 } |
|
968 |
|
969 static void |
|
970 netdev_solaris_dealloc(struct netdev *netdev_) |
|
971 { |
|
972 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
973 |
|
974 free(netdev); |
|
975 } |
|
976 |
|
977 /* |
|
978 * Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful, |
|
979 * otherwise a positive errno value. |
|
980 */ |
|
981 static int |
|
982 netdev_solaris_set_etheraddr(struct netdev *netdev_, |
|
983 const uint8_t mac[ETH_ADDR_LEN]) |
|
984 { |
|
985 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
986 const char *netdev_name = netdev_get_name(netdev_); |
|
987 char buffer[128]; |
|
988 int error = 0; |
|
989 |
|
990 VLOG_DBG("netdev_solaris_set_etheraddr device %s", netdev_name); |
|
991 |
|
992 ovs_mutex_lock(&netdev->mutex); |
|
993 if ((netdev->cache_valid & VALID_ETHERADDR) && |
|
994 (eth_addr_equals(netdev->etheraddr, mac))) { |
|
995 goto exit; |
|
996 } |
|
997 |
|
998 /* |
|
999 * In case there is some kind of failure, invalidate |
|
1000 * the cached value. |
|
1001 */ |
|
1002 netdev->cache_valid &= ~VALID_ETHERADDR; |
|
1003 |
|
1004 /* |
|
1005 * MAC addresses are datalink properties. |
|
1006 */ |
|
1007 (void) snprintf(buffer, sizeof (buffer), ETH_ADDR_FMT, |
|
1008 ETH_ADDR_ARGS(mac)); |
|
1009 error = solaris_set_dlprop_string(netdev_name, "mac-address", buffer); |
|
1010 if (error != 0) { |
|
1011 VLOG_ERR("set etheraddr %s on %s device failed: %s", |
|
1012 buffer, netdev_name, ovs_strerror(error)); |
|
1013 goto exit; |
|
1014 } |
|
1015 |
|
1016 memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN); |
|
1017 netdev->cache_valid |= VALID_ETHERADDR; |
|
1018 netdev_change_seq_changed(netdev_); |
|
1019 |
|
1020 exit: |
|
1021 ovs_mutex_unlock(&netdev->mutex); |
|
1022 return (error); |
|
1023 } |
|
1024 |
|
1025 static int |
|
1026 netdev_solaris_get_dlclass(const struct netdev *netdev_) |
|
1027 { |
|
1028 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1029 const char *netdev_name = netdev_get_name(netdev_); |
|
1030 char buffer[DLADM_PROP_VAL_MAX]; |
|
1031 int error = 0; |
|
1032 |
|
1033 if (netdev->cache_valid & VALID_LINKCLASS) |
|
1034 goto exit; |
|
1035 |
|
1036 /* |
|
1037 * Get the datalink class for this link. |
|
1038 */ |
|
1039 error = solaris_get_dlclass(netdev_name, buffer, sizeof (buffer)); |
|
1040 if (error != 0) { |
|
1041 VLOG_ERR("Unable to retrieve linkclass for %s device, %d", |
|
1042 netdev_name, error); |
|
1043 goto exit; |
|
1044 } |
|
1045 |
|
1046 (void) strlcpy(netdev->class, buffer, sizeof (netdev->class)); |
|
1047 netdev->cache_valid |= VALID_LINKCLASS; |
|
1048 exit: |
|
1049 return (error); |
|
1050 } |
|
1051 |
|
1052 /* |
|
1053 * Determines if this netdev is an uplink for the bridge. |
|
1054 */ |
|
1055 static bool |
|
1056 netdev_solaris_is_uplink(const struct netdev *netdev_) |
|
1057 { |
|
1058 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1059 const char *netdev_name = netdev_get_name(netdev_); |
|
1060 |
|
1061 VLOG_DBG("netdev_solaris_is_uplink device %s", netdev_name); |
|
1062 if (netdev_solaris_get_dlclass(netdev_) != 0) |
|
1063 return (false); |
|
1064 return (solaris_is_uplink_class(netdev->class)); |
|
1065 } |
|
1066 |
|
1067 static int |
|
1068 netdev_solaris_get_etheraddr(const struct netdev *netdev_, |
|
1069 uint8_t mac[ETH_ADDR_LEN]) |
|
1070 { |
|
1071 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1072 const char *netdev_name = netdev_get_name(netdev_); |
|
1073 char buffer[DLADM_PROP_VAL_MAX]; |
|
1074 int error = 0; |
|
1075 |
|
1076 VLOG_DBG("netdev_solaris_get_etheraddr device %s", netdev_name); |
|
1077 |
|
1078 ovs_mutex_lock(&netdev->mutex); |
|
1079 /* |
|
1080 * Only the bridge ethernet address can be changed outside the |
|
1081 * knowledge of vswitchd (when the uplink is added and the bridge |
|
1082 * VNIC is moved to the uplink). |
|
1083 */ |
|
1084 if ((netdev->cache_valid & VALID_ETHERADDR) && |
|
1085 (netdev->up.netdev_class != &netdev_internal_class)) { |
|
1086 memcpy(mac, netdev->etheraddr, ETH_ADDR_LEN); |
|
1087 goto exit; |
|
1088 } |
|
1089 |
|
1090 /* |
|
1091 * MAC addresses are datalink properties. |
|
1092 */ |
|
1093 error = solaris_get_dlprop(netdev_name, "mac-address", "current", |
|
1094 buffer, sizeof (buffer)); |
|
1095 if (error != 0) { |
|
1096 VLOG_ERR("Unable to retrieve etheraddr for %s device", |
|
1097 netdev_name); |
|
1098 goto exit; |
|
1099 } |
|
1100 |
|
1101 if (!eth_addr_from_string(buffer, mac)) { |
|
1102 VLOG_ERR("Invalid etheraddr for %s device", netdev_name); |
|
1103 error = EINVAL; |
|
1104 goto exit; |
|
1105 } |
|
1106 |
|
1107 memcpy(netdev->etheraddr, mac, ETH_ADDR_LEN); |
|
1108 netdev->cache_valid |= VALID_ETHERADDR; |
|
1109 exit: |
|
1110 ovs_mutex_unlock(&netdev->mutex); |
|
1111 return (error); |
|
1112 } |
|
1113 |
|
1114 static int |
|
1115 netdev_solaris_get_mtu__(struct netdev_solaris *netdev, int *mtup, |
|
1116 const char *netdev_name) |
|
1117 { |
|
1118 char buffer[DLADM_PROP_VAL_MAX]; |
|
1119 int mtu; |
|
1120 int error = 0; |
|
1121 |
|
1122 if (netdev->cache_valid & VALID_MTU) { |
|
1123 *mtup = netdev->mtu; |
|
1124 return (0); |
|
1125 } |
|
1126 |
|
1127 /* |
|
1128 * MTU is a datalink property |
|
1129 */ |
|
1130 error = solaris_get_dlprop(netdev_name, "mtu", "current", buffer, |
|
1131 sizeof (buffer)); |
|
1132 if (error != 0) { |
|
1133 VLOG_ERR("Unable to retrieve mtu for %s device", |
|
1134 netdev_name); |
|
1135 return (error); |
|
1136 } |
|
1137 |
|
1138 mtu = atoi(buffer); |
|
1139 if (mtu == 0) { |
|
1140 VLOG_ERR("Invalid mtu for %s device", netdev_name); |
|
1141 error = EINVAL; |
|
1142 return (error); |
|
1143 } |
|
1144 |
|
1145 netdev->mtu = *mtup = mtu; |
|
1146 netdev->cache_valid |= VALID_MTU; |
|
1147 return (0); |
|
1148 } |
|
1149 |
|
1150 /* |
|
1151 * Returns the maximum size of transmitted (and received) packets on 'netdev', |
|
1152 * in bytes, not including the hardware header; thus, this is typically 1500 |
|
1153 * bytes for Ethernet devices. |
|
1154 */ |
|
1155 static int |
|
1156 netdev_solaris_get_mtu(const struct netdev *netdev_, int *mtup) |
|
1157 { |
|
1158 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1159 const char *netdev_name = netdev_get_name(netdev_); |
|
1160 int error = 0; |
|
1161 |
|
1162 VLOG_DBG("netdev_solaris_get_mtu device %s", netdev_name); |
|
1163 |
|
1164 if (!netdev_) { |
|
1165 VLOG_DBG("netdev_solaris_get_mtu null netdevs"); |
|
1166 return (error); |
|
1167 } |
|
1168 |
|
1169 ovs_mutex_lock(&netdev->mutex); |
|
1170 error = netdev_solaris_get_mtu__(netdev, mtup, netdev_name); |
|
1171 ovs_mutex_unlock(&netdev->mutex); |
|
1172 return (error); |
|
1173 } |
|
1174 |
|
1175 /* |
|
1176 * Sets the maximum size of transmitted (MTU) for given device. |
|
1177 */ |
|
1178 static int |
|
1179 netdev_solaris_set_mtu(const struct netdev *netdev_ OVS_UNUSED, int mtu) |
|
1180 { |
|
1181 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1182 const char *netdev_name = netdev_get_name(netdev_); |
|
1183 uint64_t ulval = mtu; |
|
1184 int error = 0; |
|
1185 |
|
1186 VLOG_DBG("netdev_solaris_set_mtu device %s", netdev_name); |
|
1187 |
|
1188 ovs_mutex_lock(&netdev->mutex); |
|
1189 if ((netdev->cache_valid & VALID_MTU && netdev->mtu == mtu)) |
|
1190 goto exit; |
|
1191 |
|
1192 /* |
|
1193 * In case there is some kind of failure, invalidate |
|
1194 * the cached value. |
|
1195 */ |
|
1196 netdev->cache_valid &= ~VALID_MTU; |
|
1197 |
|
1198 /* |
|
1199 * MTU is a datalink property. |
|
1200 */ |
|
1201 error = solaris_set_dlprop_ulong(netdev_name, "mtu", &ulval); |
|
1202 if (error != 0) { |
|
1203 VLOG_ERR("set mtu on %s device failed: %s", |
|
1204 netdev_name, ovs_strerror(error)); |
|
1205 goto exit; |
|
1206 } |
|
1207 |
|
1208 netdev->mtu = mtu; |
|
1209 netdev->cache_valid |= VALID_MTU; |
|
1210 netdev_change_seq_changed(netdev_); |
|
1211 |
|
1212 exit: |
|
1213 ovs_mutex_unlock(&netdev->mutex); |
|
1214 return (error); |
|
1215 } |
|
1216 |
|
1217 /* |
|
1218 * Returns the ifindex of 'netdev', if successful, as a positive number. |
|
1219 * On failure, returns a negative errno. |
|
1220 */ |
|
1221 static int |
|
1222 netdev_solaris_get_ifindex(const struct netdev *netdev_) |
|
1223 { |
|
1224 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1225 const char *netdev_name = netdev_get_name(netdev_); |
|
1226 int ifindex; |
|
1227 int error = 0; |
|
1228 |
|
1229 VLOG_DBG("netdev_solaris_get_ifindex device %s", netdev_name); |
|
1230 |
|
1231 ovs_mutex_lock(&netdev->mutex); |
|
1232 if (netdev->cache_valid & VALID_IFINDEX) { |
|
1233 ifindex = netdev->ifindex; |
|
1234 goto exit; |
|
1235 } |
|
1236 |
|
1237 ifindex = (int)if_nametoindex(netdev_name); |
|
1238 if (ifindex <= 0) { |
|
1239 error = errno; |
|
1240 goto exit; |
|
1241 } |
|
1242 |
|
1243 netdev->ifindex = ifindex; |
|
1244 netdev->cache_valid |= VALID_IFINDEX; |
|
1245 |
|
1246 exit: |
|
1247 ovs_mutex_unlock(&netdev->mutex); |
|
1248 return (error ? -error : ifindex); |
|
1249 } |
|
1250 |
|
1251 /* |
|
1252 * Retrieves current device stats for 'netdev-solaris'. |
|
1253 */ |
|
1254 static int |
|
1255 netdev_solaris_get_stats(const struct netdev *netdev_, |
|
1256 struct netdev_stats *stats) |
|
1257 { |
|
1258 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1259 const char *netdev_name = netdev_get_name(netdev_); |
|
1260 kstat2_status_t stat; |
|
1261 kstat2_map_t map; |
|
1262 char kuri[1024]; |
|
1263 char devname[DLADM_PROP_VAL_MAX]; |
|
1264 char modname[DLADM_PROP_VAL_MAX]; |
|
1265 char kstat_name[MAXLINKNAMELEN]; |
|
1266 char *name; |
|
1267 zoneid_t zid; |
|
1268 uint_t instance; |
|
1269 int error = 0; |
|
1270 boolean_t is_uplink; |
|
1271 |
|
1272 VLOG_DBG("netdev_solaris_get_stats device %s", netdev_name); |
|
1273 |
|
1274 is_uplink = netdev_solaris_is_uplink(netdev_); |
|
1275 |
|
1276 /* |
|
1277 * Initialize statistics only supported on uplink. |
|
1278 */ |
|
1279 stats->rx_length_errors = 0; |
|
1280 stats->rx_missed_errors = 0; |
|
1281 stats->rx_over_errors = 0; |
|
1282 |
|
1283 /* |
|
1284 * Unsupported on Solaris. |
|
1285 */ |
|
1286 stats->rx_crc_errors = UINT64_MAX; |
|
1287 stats->rx_frame_errors = UINT64_MAX; |
|
1288 stats->rx_fifo_errors = UINT64_MAX; |
|
1289 stats->tx_aborted_errors = UINT64_MAX; |
|
1290 stats->tx_carrier_errors = UINT64_MAX; |
|
1291 stats->tx_fifo_errors = UINT64_MAX; |
|
1292 stats->tx_heartbeat_errors = UINT64_MAX; |
|
1293 stats->tx_window_errors = UINT64_MAX; |
|
1294 |
|
1295 ovs_mutex_lock(&kstat_mutex); |
|
1296 if (!kstat2_handle_initialized) { |
|
1297 VLOG_DBG("netdev_solaris_get_stats initializing handle"); |
|
1298 if (!kstat_handle_init(&nd_khandle)) { |
|
1299 error = -1; |
|
1300 goto done; |
|
1301 } |
|
1302 kstat2_handle_initialized = B_TRUE; |
|
1303 } else if (!kstat_handle_update(nd_khandle)) { |
|
1304 VLOG_DBG("netdev_solaris_get_stats error updating stats"); |
|
1305 kstat_handle_close(&nd_khandle); |
|
1306 kstat2_handle_initialized = B_FALSE; |
|
1307 error = -1; |
|
1308 goto done; |
|
1309 } |
|
1310 name = (char *)netdev_name; |
|
1311 instance = 0; |
|
1312 if (strchr(netdev_name, '/') != NULL) { |
|
1313 (void) solaris_dlparse_zonelinkname(netdev_name, kstat_name, |
|
1314 &zid); |
|
1315 name = kstat_name; |
|
1316 instance = zid; |
|
1317 } |
|
1318 (void) snprintf(kuri, sizeof (kuri), "kstat:/net/link/%s/%d", |
|
1319 name, instance); |
|
1320 stat = kstat2_lookup_map(nd_khandle, kuri, &map); |
|
1321 if (stat != KSTAT2_S_OK) { |
|
1322 VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri, |
|
1323 kstat2_status_string(stat)); |
|
1324 } else { |
|
1325 if (is_uplink) { |
|
1326 stats->rx_packets = get_nvvt_int(map, "ipackets64"); |
|
1327 stats->tx_packets = get_nvvt_int(map, "opackets64"); |
|
1328 stats->rx_bytes = get_nvvt_int(map, "rbytes"); |
|
1329 stats->tx_bytes = get_nvvt_int(map, "obytes"); |
|
1330 stats->rx_errors = get_nvvt_int(map, "ierrors"); |
|
1331 stats->tx_errors = get_nvvt_int(map, "oerrors"); |
|
1332 } else { |
|
1333 stats->tx_packets = get_nvvt_int(map, "ipackets64"); |
|
1334 stats->rx_packets = get_nvvt_int(map, "opackets64"); |
|
1335 stats->tx_bytes = get_nvvt_int(map, "rbytes"); |
|
1336 stats->rx_bytes = get_nvvt_int(map, "obytes"); |
|
1337 stats->tx_errors = get_nvvt_int(map, "ierrors"); |
|
1338 stats->rx_errors = get_nvvt_int(map, "oerrors"); |
|
1339 } |
|
1340 stats->collisions = get_nvvt_int(map, "collisions"); |
|
1341 stats->multicast = get_nvvt_int(map, "multircv") + |
|
1342 get_nvvt_int(map, "multixmt"); |
|
1343 } |
|
1344 |
|
1345 (void) snprintf(kuri, sizeof (kuri), "kstat:/net/%s/link/%d", |
|
1346 name, instance); |
|
1347 stat = kstat2_lookup_map(nd_khandle, kuri, &map); |
|
1348 if (stat != KSTAT2_S_OK) { |
|
1349 VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri, |
|
1350 kstat2_status_string(stat)); |
|
1351 } else { |
|
1352 if (is_uplink) { |
|
1353 stats->rx_dropped = get_nvvt_int(map, "idrops"); |
|
1354 stats->tx_dropped = get_nvvt_int(map, "odrops"); |
|
1355 } else { |
|
1356 stats->tx_dropped = get_nvvt_int(map, "idrops"); |
|
1357 stats->rx_dropped = get_nvvt_int(map, "odrops"); |
|
1358 } |
|
1359 } |
|
1360 |
|
1361 if (!is_uplink) |
|
1362 goto done; |
|
1363 |
|
1364 /* |
|
1365 * Can we do anything for aggr? |
|
1366 */ |
|
1367 if (strcmp("phys", netdev->class) != 0) |
|
1368 goto done; |
|
1369 |
|
1370 if (solaris_get_devname(netdev_name, devname, sizeof (devname)) != 0) { |
|
1371 VLOG_WARN("Failed to retrieve devname of uplink\n"); |
|
1372 error = -1; |
|
1373 goto done; |
|
1374 } |
|
1375 |
|
1376 if (!dlparse_drvppa(devname, modname, sizeof (modname), |
|
1377 &instance)) { |
|
1378 VLOG_DBG("netdev_solaris_get_stats error getting " |
|
1379 "mod/instance for %s\n", devname); |
|
1380 error = -1; |
|
1381 goto done; |
|
1382 } |
|
1383 |
|
1384 (void) snprintf(kuri, sizeof (kuri), "kstat:/net/%s/statistics/%d", |
|
1385 modname, instance); |
|
1386 stat = kstat2_lookup_map(nd_khandle, kuri, &map); |
|
1387 if (stat != KSTAT2_S_OK) { |
|
1388 VLOG_WARN("kstat2_lookup_map of %s failed: %s", kuri, |
|
1389 kstat2_status_string(stat)); |
|
1390 error = -1; |
|
1391 goto done; |
|
1392 } |
|
1393 stats->rx_length_errors = get_nvvt_int(map, "Recv_Length_Errors"); |
|
1394 stats->rx_missed_errors = get_nvvt_int(map, "Recv_Missed_Packets"); |
|
1395 stats->rx_over_errors = get_nvvt_int(map, "Recv_Oversize"); |
|
1396 |
|
1397 done: |
|
1398 ovs_mutex_unlock(&kstat_mutex); |
|
1399 return (error); |
|
1400 } |
|
1401 |
|
1402 /* |
|
1403 * Stores the features supported by 'netdev' into of '*current', '*advertised', |
|
1404 * '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits. |
|
1405 * Returns 0 if successful, otherwise a positive errno value. |
|
1406 */ |
|
1407 static int |
|
1408 netdev_solaris_get_features(const struct netdev *netdev_, |
|
1409 enum netdev_features *current, |
|
1410 enum netdev_features *advertised, |
|
1411 enum netdev_features *supported, |
|
1412 enum netdev_features *peer) |
|
1413 { |
|
1414 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1415 const char *netdev_name = netdev_get_name(netdev_); |
|
1416 const char *physname; |
|
1417 char buffer[DLADM_PROP_VAL_MAX]; |
|
1418 int error = 0; |
|
1419 |
|
1420 VLOG_DBG("netdev_solaris_get_features device %s", netdev_name); |
|
1421 ovs_mutex_lock(&netdev->mutex); |
|
1422 |
|
1423 if (netdev_solaris_is_uplink(netdev_)) { |
|
1424 physname = netdev_name; |
|
1425 } else { |
|
1426 error = solaris_get_dllower(netdev_name, buffer, |
|
1427 sizeof (buffer)); |
|
1428 if (error != 0) |
|
1429 goto exit; |
|
1430 physname = buffer; |
|
1431 } |
|
1432 |
|
1433 error = netdev_solaris_read_features(netdev, physname); |
|
1434 if (error != 0) |
|
1435 goto exit; |
|
1436 |
|
1437 *current = netdev->current; |
|
1438 *advertised = netdev->advertised; |
|
1439 *supported = netdev->supported; |
|
1440 *peer = 0; |
|
1441 |
|
1442 exit: |
|
1443 ovs_mutex_unlock(&netdev->mutex); |
|
1444 return (error); |
|
1445 } |
|
1446 |
|
1447 /* |
|
1448 * Attempts to set input rate limiting (policing) policy. Returns 0 if |
|
1449 * successful, otherwise a positive errno value. |
|
1450 */ |
|
1451 static int |
|
1452 netdev_solaris_set_policing(struct netdev *netdev_, |
|
1453 uint32_t kbits_rate OVS_UNUSED, uint32_t kbits_burst OVS_UNUSED) |
|
1454 { |
|
1455 const char *netdev_name = netdev_get_name(netdev_); |
|
1456 int error = 0; |
|
1457 |
|
1458 VLOG_DBG("netdev_solaris_set_policing device %s", netdev_name); |
|
1459 |
|
1460 /* XXXSolaris check libdladm:setlinkrop maxbw/priority */ |
|
1461 |
|
1462 return (error); |
|
1463 } |
|
1464 |
|
1465 static int |
|
1466 netdev_solaris_get_qos_types(const struct netdev *netdev_, |
|
1467 struct sset *types) |
|
1468 { |
|
1469 const char *netdev_name = netdev_get_name(netdev_); |
|
1470 const struct tc_ops *const *opsp; |
|
1471 |
|
1472 VLOG_DBG("netdev_solaris_get_qos_types device %s", netdev_name); |
|
1473 |
|
1474 /* |
|
1475 * XXXSolaris we will support only HTB-equivalent which will translate |
|
1476 * to maxbw/priority. |
|
1477 */ |
|
1478 for (opsp = tcs; *opsp != NULL; opsp++) { |
|
1479 const struct tc_ops *ops = *opsp; |
|
1480 |
|
1481 if (ops->tc_install && ops->ovs_name[0] != '\0') |
|
1482 sset_add(types, ops->ovs_name); |
|
1483 } |
|
1484 return (0); |
|
1485 } |
|
1486 |
|
1487 static int |
|
1488 netdev_solaris_get_qos_capabilities(const struct netdev *netdev_, |
|
1489 const char *type, struct netdev_qos_capabilities *caps) |
|
1490 { |
|
1491 const char *netdev_name = netdev_get_name(netdev_); |
|
1492 int error = EOPNOTSUPP; |
|
1493 const struct tc_ops *ops = tc_lookup_ovs_name(type); |
|
1494 |
|
1495 VLOG_DBG("netdev_solaris_get_qos_capabilities device %s", |
|
1496 netdev_name); |
|
1497 |
|
1498 if (!ops) |
|
1499 return (EOPNOTSUPP); |
|
1500 |
|
1501 /* |
|
1502 * Arbit number for now. In theory we are not limited for bandwidth |
|
1503 * limit. But, with shares and priority this can be revisited. |
|
1504 */ |
|
1505 caps->n_queues = ops->n_queues; |
|
1506 |
|
1507 return (error); |
|
1508 } |
|
1509 |
|
1510 static int |
|
1511 netdev_solaris_get_qos(const struct netdev *netdev_, |
|
1512 const char **typep, struct smap *details) |
|
1513 { |
|
1514 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1515 const char *netdev_name = netdev_get_name(netdev_); |
|
1516 int error = 0; |
|
1517 |
|
1518 VLOG_DBG("netdev_solaris_get_qos device %s", netdev_name); |
|
1519 |
|
1520 ovs_mutex_lock(&netdev->mutex); |
|
1521 error = tc_query_qdisc(netdev_); |
|
1522 if (!error) { |
|
1523 *typep = netdev->tc->ops->ovs_name; |
|
1524 error = (netdev->tc->ops->qdisc_get ? |
|
1525 netdev->tc->ops->qdisc_get(netdev_, details) : 0); |
|
1526 } |
|
1527 ovs_mutex_unlock(&netdev->mutex); |
|
1528 return (error); |
|
1529 } |
|
1530 |
|
1531 static int |
|
1532 netdev_solaris_set_qos(struct netdev *netdev_, |
|
1533 const char *type, const struct smap *details) |
|
1534 { |
|
1535 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1536 const char *netdev_name = netdev_get_name(netdev_); |
|
1537 const struct tc_ops *new_ops; |
|
1538 int error = 0; |
|
1539 |
|
1540 VLOG_DBG("netdev_solaris_set_qos device %s", netdev_name); |
|
1541 new_ops = tc_lookup_ovs_name(type); |
|
1542 if (!new_ops) { |
|
1543 VLOG_DBG("netdev_solaris_set_qos type %s not found", type); |
|
1544 return (EOPNOTSUPP); |
|
1545 } |
|
1546 ovs_mutex_lock(&netdev->mutex); |
|
1547 error = tc_query_qdisc(netdev_); |
|
1548 if (error) { |
|
1549 VLOG_DBG("netdev_solaris_set_qos qdisc querry failed"); |
|
1550 goto exit; |
|
1551 } |
|
1552 |
|
1553 if (new_ops == netdev->tc->ops) { |
|
1554 error = new_ops->qdisc_set ? |
|
1555 new_ops->qdisc_set(netdev_, details) : |
|
1556 0; |
|
1557 } else { |
|
1558 error = tc_del_qdisc(netdev_); |
|
1559 if (error) { |
|
1560 VLOG_DBG("netdev_solaris_set_qos error deleting %s", |
|
1561 type); |
|
1562 goto exit; |
|
1563 } |
|
1564 ovs_assert(netdev->tc == NULL); |
|
1565 /* Install new qdisc. */ |
|
1566 error = new_ops->tc_install(netdev_, details); |
|
1567 ovs_assert((error == 0) == (netdev->tc != NULL)); |
|
1568 VLOG_DBG("netdev_solaris_set_qos installed %s, %d", type, |
|
1569 error); |
|
1570 } |
|
1571 exit: |
|
1572 ovs_mutex_unlock(&netdev->mutex); |
|
1573 return (error); |
|
1574 } |
|
1575 |
|
1576 static int |
|
1577 netdev_solaris_get_queue(const struct netdev *netdev_, |
|
1578 unsigned int queue_id, struct smap *details) |
|
1579 { |
|
1580 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1581 const char *netdev_name = netdev_get_name(netdev_); |
|
1582 int error = 0; |
|
1583 |
|
1584 VLOG_DBG("netdev_solaris_get_queue device %s", netdev_name); |
|
1585 |
|
1586 ovs_mutex_lock(&netdev->mutex); |
|
1587 error = tc_query_qdisc(netdev_); |
|
1588 if (!error) { |
|
1589 struct tc_queue *queue = tc_find_queue(netdev_, queue_id); |
|
1590 error = (queue ? |
|
1591 netdev->tc->ops->class_get(netdev_, queue, details) : |
|
1592 ENOENT); |
|
1593 } |
|
1594 ovs_mutex_unlock(&netdev->mutex); |
|
1595 VLOG_DBG("netdev_solaris_get_queue device %s done %d", netdev_name, |
|
1596 error); |
|
1597 return (error); |
|
1598 } |
|
1599 |
|
1600 static int |
|
1601 netdev_solaris_set_queue(struct netdev *netdev_, |
|
1602 unsigned int queue_id, const struct smap *details) |
|
1603 { |
|
1604 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1605 const char *netdev_name = netdev_get_name(netdev_); |
|
1606 int error = 0; |
|
1607 |
|
1608 VLOG_DBG("netdev_solaris_set_queue device %s", netdev_name); |
|
1609 |
|
1610 ovs_mutex_lock(&netdev->mutex); |
|
1611 error = tc_query_qdisc(netdev_); |
|
1612 if (!error) { |
|
1613 error = (queue_id < netdev->tc->ops->n_queues && |
|
1614 netdev->tc->ops->class_set ? |
|
1615 netdev->tc->ops->class_set(netdev_, queue_id, details) : |
|
1616 EINVAL); |
|
1617 } else { |
|
1618 VLOG_DBG("netdev_solaris_set_queue %s: no qdisc", netdev_name); |
|
1619 } |
|
1620 ovs_mutex_unlock(&netdev->mutex); |
|
1621 return (error); |
|
1622 } |
|
1623 |
|
1624 static int |
|
1625 netdev_solaris_delete_queue(struct netdev *netdev_, |
|
1626 unsigned int queue_id) |
|
1627 { |
|
1628 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1629 const char *netdev_name = netdev_get_name(netdev_); |
|
1630 int error = 0; |
|
1631 |
|
1632 VLOG_DBG("netdev_solaris_delete_queue device %s", netdev_name); |
|
1633 |
|
1634 ovs_mutex_lock(&netdev->mutex); |
|
1635 error = tc_query_qdisc(netdev_); |
|
1636 if (!error) { |
|
1637 if (netdev->tc->ops->class_delete) { |
|
1638 struct tc_queue *queue = |
|
1639 tc_find_queue(netdev_, queue_id); |
|
1640 |
|
1641 error = (queue |
|
1642 ? netdev->tc->ops->class_delete(netdev_, queue) |
|
1643 : ENOENT); |
|
1644 } else { |
|
1645 error = EINVAL; |
|
1646 } |
|
1647 } |
|
1648 ovs_mutex_unlock(&netdev->mutex); |
|
1649 return (error); |
|
1650 } |
|
1651 |
|
1652 static int |
|
1653 netdev_solaris_get_queue_stats(const struct netdev *netdev_, |
|
1654 unsigned int queue_id OVS_UNUSED, |
|
1655 struct netdev_queue_stats *stats OVS_UNUSED) |
|
1656 { |
|
1657 const char *netdev_name = netdev_get_name(netdev_); |
|
1658 int error = 0; |
|
1659 |
|
1660 VLOG_DBG("netdev_solaris_set_queue_stats device %s", netdev_name); |
|
1661 |
|
1662 return (error); |
|
1663 } |
|
1664 |
|
1665 struct netdev_solaris_queue_state { |
|
1666 unsigned int *queues; |
|
1667 size_t cur_queue; |
|
1668 size_t n_queues; |
|
1669 }; |
|
1670 |
|
1671 static int |
|
1672 netdev_solaris_queue_dump_start(const struct netdev *netdev_, |
|
1673 void **statep) |
|
1674 { |
|
1675 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1676 const char *netdev_name = netdev_get_name(netdev_); |
|
1677 int error = 0; |
|
1678 |
|
1679 VLOG_DBG("netdev_solaris_queue_dump_start device %s", netdev_name); |
|
1680 |
|
1681 ovs_mutex_lock(&netdev->mutex); |
|
1682 error = tc_query_qdisc(netdev_); |
|
1683 if (!error) { |
|
1684 if (netdev->tc->ops->class_get) { |
|
1685 struct netdev_solaris_queue_state *state; |
|
1686 struct tc_queue *queue; |
|
1687 size_t i; |
|
1688 |
|
1689 *statep = state = xmalloc(sizeof (*state)); |
|
1690 state->n_queues = hmap_count(&netdev->tc->queues); |
|
1691 state->cur_queue = 0; |
|
1692 state->queues = |
|
1693 xmalloc(state->n_queues * sizeof (*state->queues)); |
|
1694 |
|
1695 i = 0; |
|
1696 HMAP_FOR_EACH(queue, hmap_node, &netdev->tc->queues) { |
|
1697 state->queues[i++] = queue->queue_id; |
|
1698 } |
|
1699 } else { |
|
1700 error = EOPNOTSUPP; |
|
1701 } |
|
1702 } else { |
|
1703 VLOG_DBG("netdev_solaris_queue_dump_start: no qdisc"); |
|
1704 } |
|
1705 ovs_mutex_unlock(&netdev->mutex); |
|
1706 |
|
1707 return (error); |
|
1708 } |
|
1709 |
|
1710 static int |
|
1711 netdev_solaris_queue_dump_next(const struct netdev *netdev_, |
|
1712 void *state_, unsigned int *queue_idp, struct smap *details) |
|
1713 { |
|
1714 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1715 const char *netdev_name = netdev_get_name(netdev_); |
|
1716 int error = EOF; |
|
1717 struct netdev_solaris_queue_state *state = state_; |
|
1718 |
|
1719 if (!state) { |
|
1720 VLOG_DBG("netdev_solaris_queue_dump_next %s: null state\n", |
|
1721 netdev_name); |
|
1722 return (EOF); |
|
1723 } |
|
1724 |
|
1725 VLOG_DBG("netdev_solaris_queue_dump_next device %s, %"PRIuSIZE |
|
1726 ", %"PRIuSIZE, netdev_name, state->cur_queue, state->n_queues); |
|
1727 ovs_mutex_lock(&netdev->mutex); |
|
1728 while (state->cur_queue < state->n_queues) { |
|
1729 unsigned int queue_id = state->queues[state->cur_queue++]; |
|
1730 struct tc_queue *queue = tc_find_queue(netdev_, queue_id); |
|
1731 |
|
1732 if (queue) { |
|
1733 *queue_idp = queue_id; |
|
1734 if (!netdev->tc || !netdev->tc->ops || |
|
1735 !netdev->tc->ops->class_get) { |
|
1736 break; |
|
1737 } |
|
1738 error = netdev->tc->ops->class_get(netdev_, queue, |
|
1739 details); |
|
1740 break; |
|
1741 } |
|
1742 } |
|
1743 ovs_mutex_unlock(&netdev->mutex); |
|
1744 return (error); |
|
1745 } |
|
1746 |
|
1747 static int |
|
1748 netdev_solaris_queue_dump_done(const struct netdev *netdev_, |
|
1749 void *state_) |
|
1750 { |
|
1751 const char *netdev_name = netdev_get_name(netdev_); |
|
1752 struct netdev_solaris_queue_state *state = state_; |
|
1753 |
|
1754 VLOG_DBG("netdev_solaris_queue_dump_done device %s", netdev_name); |
|
1755 if (state) { |
|
1756 if (state->queues) |
|
1757 free(state->queues); |
|
1758 free(state); |
|
1759 } |
|
1760 return (0); |
|
1761 } |
|
1762 |
|
1763 static int |
|
1764 netdev_solaris_dump_queue_stats(const struct netdev *netdev_, |
|
1765 netdev_dump_queue_stats_cb *cb OVS_UNUSED, void *aux OVS_UNUSED) |
|
1766 { |
|
1767 const char *netdev_name = netdev_get_name(netdev_); |
|
1768 int error = 0; |
|
1769 |
|
1770 VLOG_DBG("netdev_solaris_queue_dump_stats device %s", netdev_name); |
|
1771 |
|
1772 return (error); |
|
1773 } |
|
1774 |
|
1775 /* |
|
1776 * If 'netdev' has an assigned IPv4 address, sets '*address' to that |
|
1777 * address and '*netmask' to the associated netmask. Otherwise, returns |
|
1778 * errno. |
|
1779 */ |
|
1780 static int |
|
1781 netdev_solaris_get_in4(const struct netdev *netdev_, |
|
1782 struct in_addr *address, struct in_addr *netmask) |
|
1783 { |
|
1784 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1785 const char *netdev_name = netdev_get_name(netdev_); |
|
1786 struct lifreq lifr; |
|
1787 const struct sockaddr_in *sin; |
|
1788 int error = 0; |
|
1789 |
|
1790 VLOG_DBG("netdev_solaris_get_in4 device %s", netdev_name); |
|
1791 |
|
1792 ovs_mutex_lock(&netdev->mutex); |
|
1793 if (netdev->cache_valid & VALID_IN4) { |
|
1794 *address = netdev->in4; |
|
1795 *netmask = netdev->netmask; |
|
1796 goto exit; |
|
1797 } |
|
1798 |
|
1799 error = netdev_solaris_plumb(netdev_, AF_INET); |
|
1800 if (error != 0) |
|
1801 goto exit; |
|
1802 |
|
1803 /* |
|
1804 * In the future, a RAD IP module might be a good provider |
|
1805 * of this information. For now, use the SIOCGLIFADDR. |
|
1806 */ |
|
1807 bzero(&lifr, sizeof (lifr)); |
|
1808 (void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name)); |
|
1809 sin = ALIGNED_CAST(struct sockaddr_in *, &lifr.lifr_addr); |
|
1810 if (ioctl(sock4, SIOCGLIFADDR, &lifr) < 0) { |
|
1811 error = errno; |
|
1812 goto exit; |
|
1813 } |
|
1814 netdev->in4 = sin->sin_addr; |
|
1815 |
|
1816 if (ioctl(sock4, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) { |
|
1817 error = errno; |
|
1818 goto exit; |
|
1819 } |
|
1820 netdev->netmask = sin->sin_addr; |
|
1821 netdev->cache_valid |= VALID_IN4; |
|
1822 |
|
1823 if (netdev->in4.s_addr != INADDR_ANY) { |
|
1824 *address = netdev->in4; |
|
1825 *netmask = netdev->netmask; |
|
1826 } else { |
|
1827 error = EADDRNOTAVAIL; |
|
1828 } |
|
1829 |
|
1830 exit: |
|
1831 ovs_mutex_unlock(&netdev->mutex); |
|
1832 return (error); |
|
1833 } |
|
1834 |
|
1835 static int |
|
1836 netdev_solaris_set_in4(struct netdev *netdev_, struct in_addr address, |
|
1837 struct in_addr netmask) |
|
1838 { |
|
1839 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1840 const char *netdev_name = netdev_get_name(netdev_); |
|
1841 struct lifreq lifr; |
|
1842 struct sockaddr_in *sin; |
|
1843 int error = 0; |
|
1844 |
|
1845 VLOG_DBG("netdev_solaris_set_in4 device %s", netdev_name); |
|
1846 |
|
1847 ovs_mutex_lock(&netdev->mutex); |
|
1848 |
|
1849 error = netdev_solaris_plumb(netdev_, AF_INET); |
|
1850 if (error != 0) |
|
1851 goto exit; |
|
1852 |
|
1853 /* |
|
1854 * In the future, a RAD IP module might be a good provider |
|
1855 * of this information. For now, use the SIOCSLIFADDR. |
|
1856 */ |
|
1857 bzero(&lifr, sizeof (lifr)); |
|
1858 (void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name)); |
|
1859 sin = ALIGNED_CAST(struct sockaddr_in *, &lifr.lifr_addr); |
|
1860 sin->sin_addr = address; |
|
1861 sin->sin_family = AF_INET; |
|
1862 if (ioctl(sock4, SIOCSLIFADDR, &lifr) < 0) { |
|
1863 error = errno; |
|
1864 goto exit; |
|
1865 } |
|
1866 if (address.s_addr == htonl(INADDR_ANY)) |
|
1867 goto done; |
|
1868 |
|
1869 sin->sin_addr = netmask; |
|
1870 if (ioctl(sock4, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) { |
|
1871 error = errno; |
|
1872 goto done; |
|
1873 } |
|
1874 netdev->in4 = address; |
|
1875 netdev->netmask = netmask; |
|
1876 netdev->cache_valid |= VALID_IN4; |
|
1877 |
|
1878 done: |
|
1879 netdev_change_seq_changed(netdev_); |
|
1880 exit: |
|
1881 ovs_mutex_unlock(&netdev->mutex); |
|
1882 return (error); |
|
1883 } |
|
1884 |
|
1885 /* |
|
1886 * If 'netdev' has an assigned IPv6 address, sets '*address' to that |
|
1887 * address. Otherwise, returns errno. |
|
1888 */ |
|
1889 static int |
|
1890 netdev_solaris_get_in6(const struct netdev *netdev_, struct in6_addr *address) |
|
1891 { |
|
1892 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
1893 const char *netdev_name = netdev_get_name(netdev_); |
|
1894 struct lifreq lifr; |
|
1895 const struct sockaddr_in6 *sin6; |
|
1896 int error = 0; |
|
1897 |
|
1898 VLOG_DBG("netdev_solaris_get_in6 device %s", netdev_name); |
|
1899 |
|
1900 ovs_mutex_lock(&netdev->mutex); |
|
1901 if (netdev->cache_valid & VALID_IN6) { |
|
1902 if (!IN6_IS_ADDR_UNSPECIFIED(address)) |
|
1903 *address = netdev->in6; |
|
1904 else |
|
1905 error = EADDRNOTAVAIL; |
|
1906 goto exit; |
|
1907 } |
|
1908 |
|
1909 error = netdev_solaris_plumb(netdev_, AF_INET6); |
|
1910 if (error != 0) |
|
1911 goto exit; |
|
1912 |
|
1913 /* |
|
1914 * In the future, a RAD IP module might be a good provider |
|
1915 * of this information. For now, use the SIOCGLIFADDR. |
|
1916 */ |
|
1917 bzero(&lifr, sizeof (lifr)); |
|
1918 (void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name)); |
|
1919 if (ioctl(sock6, SIOCGLIFADDR, &lifr) < 0) { |
|
1920 error = errno; |
|
1921 goto exit; |
|
1922 } |
|
1923 sin6 = ALIGNED_CAST(struct sockaddr_in6 *, &lifr.lifr_addr); |
|
1924 netdev->in6 = sin6->sin6_addr; |
|
1925 netdev->cache_valid |= VALID_IN6; |
|
1926 |
|
1927 if (!IN6_IS_ADDR_UNSPECIFIED(address)) |
|
1928 *address = netdev->in6; |
|
1929 else |
|
1930 error = EADDRNOTAVAIL; |
|
1931 |
|
1932 exit: |
|
1933 ovs_mutex_unlock(&netdev->mutex); |
|
1934 return (error); |
|
1935 } |
|
1936 |
|
1937 #define ROUNDUP_LONG(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : \ |
|
1938 sizeof (long)) |
|
1939 #define RT_ADVANCE(x, n) ((x) += ROUNDUP_LONG(salen(n))) |
|
1940 #define BUF_SIZE 2048 |
|
1941 |
|
1942 typedef struct rtmsg { |
|
1943 struct rt_msghdr m_rtm; |
|
1944 char m_space[BUF_SIZE]; |
|
1945 } rtmsg_t; |
|
1946 |
|
1947 static int |
|
1948 salen(const struct sockaddr *sa) |
|
1949 { |
|
1950 switch (sa->sa_family) { |
|
1951 case AF_INET: |
|
1952 return (sizeof (struct sockaddr_in)); |
|
1953 case AF_LINK: |
|
1954 return (sizeof (struct sockaddr_dl)); |
|
1955 case AF_INET6: |
|
1956 return (sizeof (struct sockaddr_in6)); |
|
1957 default: |
|
1958 return (sizeof (struct sockaddr)); |
|
1959 } |
|
1960 } |
|
1961 |
|
1962 /* |
|
1963 * Adds 'router' as a default IP gateway. |
|
1964 */ |
|
1965 static int |
|
1966 netdev_solaris_add_router(struct netdev *netdev_, struct in_addr router) |
|
1967 { |
|
1968 const char *netdev_name = netdev_get_name(netdev_); |
|
1969 rtmsg_t m_rtmsg; |
|
1970 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; |
|
1971 char *cp = m_rtmsg.m_space; |
|
1972 struct sockaddr_in gateway; |
|
1973 struct sockaddr_in dest; |
|
1974 struct sockaddr_in mask; |
|
1975 int rtsock_fd; |
|
1976 int error = 0; |
|
1977 int l; |
|
1978 |
|
1979 VLOG_DBG("netdev_solaris_add_router %s", netdev_name); |
|
1980 |
|
1981 rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); |
|
1982 if (rtsock_fd == -1) { |
|
1983 error = errno; |
|
1984 VLOG_ERR("failed to create PF_ROUTE socket (%s)", |
|
1985 ovs_strerror(error)); |
|
1986 return (error); |
|
1987 } |
|
1988 |
|
1989 memset(&gateway, 0, sizeof (gateway)); |
|
1990 gateway.sin_family = AF_INET; |
|
1991 gateway.sin_addr = router; |
|
1992 |
|
1993 memset(&dest, 0, sizeof (dest)); |
|
1994 dest.sin_family = AF_INET; |
|
1995 dest.sin_addr.s_addr = htonl(INADDR_ANY); |
|
1996 |
|
1997 memset(&dest, 0, sizeof (mask)); |
|
1998 mask.sin_family = AF_INET; |
|
1999 mask.sin_addr.s_addr = htonl(INADDR_ANY); |
|
2000 |
|
2001 (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg)); |
|
2002 rtm->rtm_version = RTM_VERSION; |
|
2003 rtm->rtm_type = RTM_ADD; |
|
2004 rtm->rtm_flags = RTF_GATEWAY | RTF_STATIC | RTF_UP; |
|
2005 rtm->rtm_addrs = RTA_GATEWAY | RTA_DST | RTA_NETMASK; |
|
2006 |
|
2007 l = ROUNDUP_LONG(sizeof (struct sockaddr_in)); |
|
2008 (void) memcpy(cp, &dest, l); |
|
2009 cp += l; |
|
2010 (void) memcpy(cp, &gateway, l); |
|
2011 cp += l; |
|
2012 (void) memcpy(cp, &mask, l); |
|
2013 cp += l; |
|
2014 |
|
2015 rtm->rtm_msglen = l = cp - (char *)&m_rtmsg; |
|
2016 if (write(rtsock_fd, (char *)&m_rtmsg, l) != l) { |
|
2017 char buffer[INET_ADDRSTRLEN]; |
|
2018 (void) inet_ntop(AF_INET, &router, buffer, sizeof (buffer)); |
|
2019 error = errno; |
|
2020 VLOG_ERR("failed to add router %s: %s", buffer, |
|
2021 ovs_strerror(error)); |
|
2022 } |
|
2023 close(rtsock_fd); |
|
2024 return (error); |
|
2025 } |
|
2026 |
|
2027 /* |
|
2028 * Looks up the next hop for 'host' in the host's routing table. If |
|
2029 * successful, stores the next hop gateway's address (0 if 'host' is on a |
|
2030 * directly connected network) in '*next_hop' and a copy of the name of the |
|
2031 * device to reach 'host' in '*netdev_name', and returns 0. The caller is |
|
2032 * responsible for freeing '*netdev_name' (by calling free()). |
|
2033 */ |
|
2034 static int |
|
2035 netdev_solaris_get_next_hop(const struct in_addr *host, |
|
2036 struct in_addr *next_hop, char **netdev_name) |
|
2037 { |
|
2038 rtmsg_t m_rtmsg; |
|
2039 struct rt_msghdr *rtm = &m_rtmsg.m_rtm; |
|
2040 char *cp = m_rtmsg.m_space; |
|
2041 int rtsock_fd; |
|
2042 struct sockaddr_in sin; |
|
2043 struct sockaddr_dl sdl; |
|
2044 const pid_t pid = getpid(); |
|
2045 static int seq; |
|
2046 boolean_t gateway = B_FALSE; |
|
2047 ssize_t ssz; |
|
2048 char *ifname = NULL; |
|
2049 int saved_errno; |
|
2050 int error = 0; |
|
2051 int rlen; |
|
2052 int l; |
|
2053 int i; |
|
2054 |
|
2055 memset(&sin, 0, sizeof (sin)); |
|
2056 sin.sin_family = AF_INET; |
|
2057 sin.sin_port = 0; |
|
2058 sin.sin_addr = *host; |
|
2059 |
|
2060 memset(&sdl, 0, sizeof (sdl)); |
|
2061 sdl.sdl_family = AF_LINK; |
|
2062 |
|
2063 rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0); |
|
2064 if (rtsock_fd == -1) { |
|
2065 error = errno; |
|
2066 VLOG_ERR("failed to create PF_ROUTE socket (%s)", |
|
2067 ovs_strerror(error)); |
|
2068 return (error); |
|
2069 } |
|
2070 |
|
2071 (void) memset(&m_rtmsg, 0, sizeof (m_rtmsg)); |
|
2072 rtm->rtm_type = RTM_GET; |
|
2073 rtm->rtm_flags = RTF_HOST|RTF_UP; |
|
2074 rtm->rtm_version = RTM_VERSION; |
|
2075 rtm->rtm_seq = ++seq; |
|
2076 rtm->rtm_addrs = RTA_DST|RTA_IFP; |
|
2077 |
|
2078 l = ROUNDUP_LONG(sizeof (struct sockaddr_in)); |
|
2079 (void) memcpy(cp, &sin, l); |
|
2080 cp += l; |
|
2081 |
|
2082 l = ROUNDUP_LONG(sizeof (struct sockaddr_dl)); |
|
2083 (void) memcpy(cp, &sdl, l); |
|
2084 cp += l; |
|
2085 |
|
2086 rtm->rtm_msglen = l = cp - (char *)&m_rtmsg; |
|
2087 if ((rlen = write(rtsock_fd, (char *)&m_rtmsg, l)) < l) { |
|
2088 error = errno; |
|
2089 VLOG_ERR("failed to get route: %s", ovs_strerror(error)); |
|
2090 close(rtsock_fd); |
|
2091 return (errno); |
|
2092 } |
|
2093 |
|
2094 memset(next_hop, 0, sizeof (*next_hop)); |
|
2095 *netdev_name = NULL; |
|
2096 memset(&m_rtmsg, 0, sizeof (m_rtmsg)); |
|
2097 do { |
|
2098 ssz = read(rtsock_fd, &m_rtmsg, sizeof (m_rtmsg)); |
|
2099 } while (ssz > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid)); |
|
2100 saved_errno = errno; |
|
2101 close(rtsock_fd); |
|
2102 if (ssz <= 0) { |
|
2103 if (ssz < 0) { |
|
2104 return (saved_errno); |
|
2105 } |
|
2106 return (EPIPE); |
|
2107 } |
|
2108 cp = (void *)&m_rtmsg.m_space; |
|
2109 for (i = 1; i; i <<= 1) { |
|
2110 if ((rtm->rtm_addrs & i) != 0) { |
|
2111 const struct sockaddr *sa = (const void *)cp; |
|
2112 |
|
2113 if ((i == RTA_GATEWAY) && sa->sa_family == AF_INET) { |
|
2114 const struct sockaddr_in * const sin = |
|
2115 ALIGNED_CAST(const struct sockaddr_in *, |
|
2116 sa); |
|
2117 |
|
2118 *next_hop = sin->sin_addr; |
|
2119 gateway = B_TRUE; |
|
2120 } |
|
2121 if ((i == RTA_IFP) && sa->sa_family == AF_LINK) { |
|
2122 const struct sockaddr_dl * const sdl = |
|
2123 ALIGNED_CAST(const struct sockaddr_dl *, |
|
2124 sa); |
|
2125 |
|
2126 ifname = xmemdup0(sdl->sdl_data, |
|
2127 sdl->sdl_nlen); |
|
2128 } |
|
2129 RT_ADVANCE(cp, sa); |
|
2130 } |
|
2131 } |
|
2132 if (ifname == NULL) { |
|
2133 return (ENXIO); |
|
2134 } |
|
2135 if (!gateway) { |
|
2136 *next_hop = *host; |
|
2137 } |
|
2138 *netdev_name = ifname; |
|
2139 VLOG_DBG("host " IP_FMT " next-hop " IP_FMT " if %s\n", |
|
2140 IP_ARGS(host->s_addr), IP_ARGS(next_hop->s_addr), *netdev_name); |
|
2141 |
|
2142 return (0); |
|
2143 } |
|
2144 |
|
2145 static int |
|
2146 netdev_solaris_get_status(const struct netdev *netdev_, |
|
2147 struct smap *smap OVS_UNUSED) |
|
2148 { |
|
2149 const char *netdev_name = netdev_get_name(netdev_); |
|
2150 int error = EOPNOTSUPP; |
|
2151 |
|
2152 VLOG_DBG("netdev_solaris_get_status %s", netdev_name); |
|
2153 |
|
2154 /* |
|
2155 * It looks like this is used to populate a column, |
|
2156 * OVSREC_INTERFACE_COL_STATUS in the OVSDB. It doesn't appear |
|
2157 * to be required though. If we wanted to return the driver name |
|
2158 * then we could return that using libdladm. |
|
2159 */ |
|
2160 return (error); |
|
2161 } |
|
2162 |
|
2163 /* |
|
2164 * Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be |
|
2165 * successfully retrieved, it stores the corresponding MAC address in 'mac' and |
|
2166 * returns 0. Otherwise, it returns a positive errno value; in particular, |
|
2167 * ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'. |
|
2168 */ |
|
2169 static int |
|
2170 netdev_solaris_arp_lookup(const struct netdev *netdev_, |
|
2171 ovs_be32 ip OVS_UNUSED, uint8_t mac[ETH_ADDR_LEN] OVS_UNUSED) |
|
2172 { |
|
2173 const char *netdev_name = netdev_get_name(netdev_); |
|
2174 int error = 0; |
|
2175 struct xarpreq ar; |
|
2176 struct sockaddr_in *sin; |
|
2177 |
|
2178 VLOG_DBG("netdev_solaris_arp_lookup %s", netdev_name); |
|
2179 |
|
2180 bzero(&ar, sizeof (ar)); |
|
2181 sin = ALIGNED_CAST(struct sockaddr_in *, &ar.xarp_pa); |
|
2182 sin->sin_addr.s_addr = ip; |
|
2183 sin->sin_family = AF_INET; |
|
2184 ar.xarp_ha.sdl_family = AF_LINK; |
|
2185 |
|
2186 if (ioctl(sock4, SIOCGXARP, &ar) < 0) { |
|
2187 error = errno; |
|
2188 goto out; |
|
2189 } |
|
2190 if (!(ar.xarp_flags & ATF_COM)) { |
|
2191 errno = EOPNOTSUPP; /* XXX */ |
|
2192 goto out; |
|
2193 } |
|
2194 memcpy(mac, LLADDR(&ar.xarp_ha), ETH_ADDR_LEN); |
|
2195 out: |
|
2196 return (error); |
|
2197 } |
|
2198 |
|
2199 static int |
|
2200 lifr_to_nd_flags(int64_t lifrflags) |
|
2201 { |
|
2202 enum netdev_flags nd_flags = 0; |
|
2203 |
|
2204 if (lifrflags & IFF_UP) { |
|
2205 nd_flags |= NETDEV_UP; |
|
2206 } |
|
2207 if (lifrflags & IFF_PROMISC) { |
|
2208 nd_flags |= NETDEV_PROMISC; |
|
2209 } |
|
2210 if (lifrflags & IFF_LOOPBACK) { |
|
2211 nd_flags |= NETDEV_LOOPBACK; |
|
2212 } |
|
2213 return (nd_flags); |
|
2214 } |
|
2215 |
|
2216 static int64_t |
|
2217 nd_to_lifr_flags(enum netdev_flags nd_flags) |
|
2218 { |
|
2219 int64_t lifrflags = 0; |
|
2220 |
|
2221 if (nd_flags & NETDEV_UP) { |
|
2222 lifrflags |= IFF_UP; |
|
2223 } |
|
2224 if (nd_flags & NETDEV_PROMISC) { |
|
2225 lifrflags |= IFF_PROMISC; |
|
2226 } |
|
2227 if (nd_flags & NETDEV_LOOPBACK) { |
|
2228 lifrflags |= IFF_LOOPBACK; |
|
2229 } |
|
2230 return (lifrflags); |
|
2231 } |
|
2232 |
|
2233 /* |
|
2234 * Retrieves the current set of flags on 'netdev' into '*old_flags'. Then, |
|
2235 * turns off the flags that are set to 1 in 'off' and turns on the flags |
|
2236 * that are set to 1 in 'on'. (No bit will be set to 1 in both 'off' and |
|
2237 * 'on'; that is, off & on == 0.) |
|
2238 */ |
|
2239 static int |
|
2240 netdev_solaris_update_flags(struct netdev *netdev_, enum netdev_flags off, |
|
2241 enum netdev_flags on, enum netdev_flags *old_flagsp) |
|
2242 { |
|
2243 const char *netdev_name = netdev_get_name(netdev_); |
|
2244 struct lifreq lifr; |
|
2245 int64_t old_lifr_flags; |
|
2246 int64_t new_lifr_flags; |
|
2247 int error = 0; |
|
2248 |
|
2249 VLOG_DBG("netdev_solaris_update_flags %s", netdev_name); |
|
2250 |
|
2251 bzero(&lifr, sizeof (lifr)); |
|
2252 (void) strncpy(lifr.lifr_name, netdev_name, sizeof (lifr.lifr_name)); |
|
2253 if (ioctl(sock4, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { |
|
2254 error = errno; |
|
2255 goto exit; |
|
2256 } |
|
2257 old_lifr_flags = lifr.lifr_flags; |
|
2258 *old_flagsp = lifr_to_nd_flags(old_lifr_flags); |
|
2259 new_lifr_flags = (old_lifr_flags & ~nd_to_lifr_flags(off)) | |
|
2260 nd_to_lifr_flags(on); |
|
2261 |
|
2262 if (new_lifr_flags == old_lifr_flags) |
|
2263 goto exit; |
|
2264 |
|
2265 lifr.lifr_flags = new_lifr_flags; |
|
2266 if (ioctl(sock4, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) { |
|
2267 error = errno; |
|
2268 goto exit; |
|
2269 } |
|
2270 netdev_change_seq_changed(netdev_); |
|
2271 |
|
2272 exit: |
|
2273 return (error); |
|
2274 } |
|
2275 |
|
2276 static int |
|
2277 netdev_internal_get_stats(const struct netdev *netdev_ OVS_UNUSED, |
|
2278 struct netdev_stats *stats OVS_UNUSED) |
|
2279 { |
|
2280 return (0); |
|
2281 } |
|
2282 |
|
2283 static int |
|
2284 netdev_internal_get_status(const struct netdev *netdev OVS_UNUSED, |
|
2285 struct smap *smap) |
|
2286 { |
|
2287 smap_add(smap, "driver_name", "openvswitch"); |
|
2288 return (0); |
|
2289 } |
|
2290 |
|
2291 #define NETDEV_SOLARIS_CLASS(NAME, CONSTRUCT, GET_STATS, SET_STATS, \ |
|
2292 GET_FEATURES, GET_STATUS) \ |
|
2293 { \ |
|
2294 NAME, \ |
|
2295 netdev_solaris_init, \ |
|
2296 netdev_solaris_run, \ |
|
2297 netdev_solaris_wait, \ |
|
2298 netdev_solaris_alloc, \ |
|
2299 CONSTRUCT, \ |
|
2300 netdev_solaris_destruct, \ |
|
2301 netdev_solaris_dealloc, \ |
|
2302 NULL, \ |
|
2303 NULL, \ |
|
2304 NULL, \ |
|
2305 NULL, \ |
|
2306 NULL, \ |
|
2307 netdev_solaris_set_etheraddr, \ |
|
2308 netdev_solaris_get_etheraddr, \ |
|
2309 netdev_solaris_get_mtu, \ |
|
2310 netdev_solaris_set_mtu, \ |
|
2311 netdev_solaris_get_ifindex, \ |
|
2312 NULL, \ |
|
2313 NULL, \ |
|
2314 NULL, \ |
|
2315 GET_STATS, \ |
|
2316 SET_STATS, \ |
|
2317 GET_FEATURES, \ |
|
2318 NULL, \ |
|
2319 netdev_solaris_set_policing, \ |
|
2320 netdev_solaris_get_qos_types, \ |
|
2321 netdev_solaris_get_qos_capabilities, \ |
|
2322 netdev_solaris_get_qos, \ |
|
2323 netdev_solaris_set_qos, \ |
|
2324 netdev_solaris_get_queue, \ |
|
2325 netdev_solaris_set_queue, \ |
|
2326 netdev_solaris_delete_queue, \ |
|
2327 netdev_solaris_get_queue_stats, \ |
|
2328 netdev_solaris_queue_dump_start, \ |
|
2329 netdev_solaris_queue_dump_next, \ |
|
2330 netdev_solaris_queue_dump_done, \ |
|
2331 netdev_solaris_dump_queue_stats, \ |
|
2332 netdev_solaris_get_in4, \ |
|
2333 netdev_solaris_set_in4, \ |
|
2334 netdev_solaris_get_in6, \ |
|
2335 netdev_solaris_add_router, \ |
|
2336 netdev_solaris_get_next_hop, \ |
|
2337 GET_STATUS, \ |
|
2338 netdev_solaris_arp_lookup, \ |
|
2339 netdev_solaris_update_flags, \ |
|
2340 netdev_solaris_configure_uplink, \ |
|
2341 netdev_solaris_is_uplink, \ |
|
2342 NULL, \ |
|
2343 NULL, \ |
|
2344 NULL, \ |
|
2345 NULL, \ |
|
2346 NULL, \ |
|
2347 NULL, \ |
|
2348 NULL \ |
|
2349 } |
|
2350 |
|
2351 const struct netdev_class netdev_solaris_class = |
|
2352 NETDEV_SOLARIS_CLASS( |
|
2353 "system", |
|
2354 netdev_solaris_construct, |
|
2355 netdev_solaris_get_stats, |
|
2356 NULL, /* set_stats */ |
|
2357 netdev_solaris_get_features, |
|
2358 netdev_solaris_get_status); |
|
2359 |
|
2360 const struct netdev_class netdev_internal_class = |
|
2361 NETDEV_SOLARIS_CLASS( |
|
2362 "internal", |
|
2363 netdev_solaris_construct, |
|
2364 netdev_internal_get_stats, |
|
2365 NULL, /* set_stats */ |
|
2366 NULL, /* get_features */ |
|
2367 netdev_internal_get_status); |
|
2368 |
|
2369 /* Solaris HTB traffic control class */ |
|
2370 #define HTB_N_QUEUES 0xf000 |
|
2371 |
|
2372 struct htb { |
|
2373 struct tc tc; |
|
2374 unsigned int max_rate; /* In bytes/s. */ |
|
2375 }; |
|
2376 |
|
2377 struct htb_class { |
|
2378 struct tc_queue tc_queue; |
|
2379 unsigned int min_rate; /* In bytes/s */ |
|
2380 unsigned int max_rate; /* In bytes/s */ |
|
2381 unsigned int burst; /* In bytes/s -- unused */ |
|
2382 unsigned int priority; /* Lower value is higher priority */ |
|
2383 }; |
|
2384 |
|
2385 |
|
2386 /* |
|
2387 * Create an HTB qdisc. |
|
2388 * |
|
2389 * Equivalent to "tc qdisc add dev <dev> root handle 1: htb default 1". |
|
2390 */ |
|
2391 static int |
|
2392 htb_setup_qdisc__(struct netdev *netdev) |
|
2393 { |
|
2394 VLOG_DBG("htb_setup_qdisc__ device %s", netdev->name); |
|
2395 tc_del_qdisc(netdev); |
|
2396 |
|
2397 return (0); |
|
2398 } |
|
2399 |
|
2400 static struct htb * |
|
2401 htb_get__(const struct netdev *netdev_) |
|
2402 { |
|
2403 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
2404 const char *netdev_name = netdev_get_name(netdev_); |
|
2405 |
|
2406 VLOG_DBG("htb_get__ device %s", netdev_name); |
|
2407 return (CONTAINER_OF(netdev->tc, struct htb, tc)); |
|
2408 } |
|
2409 |
|
2410 static void |
|
2411 htb_install__(struct netdev *netdev_, uint64_t max_rate) |
|
2412 { |
|
2413 const char *netdev_name = netdev_get_name(netdev_); |
|
2414 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
2415 struct htb *htb; |
|
2416 |
|
2417 VLOG_DBG("htb_install__ device %s", netdev_name); |
|
2418 htb = xmalloc(sizeof (*htb)); |
|
2419 tc_init(&htb->tc, &tc_ops_htb); |
|
2420 htb->max_rate = max_rate; |
|
2421 |
|
2422 netdev->tc = &htb->tc; |
|
2423 VLOG_DBG("htb_install__ device %s TC configured ", netdev_name); |
|
2424 } |
|
2425 |
|
2426 static int |
|
2427 htb_setup_class__(struct netdev *netdev, unsigned int handle OVS_UNUSED, |
|
2428 unsigned int parent OVS_UNUSED, |
|
2429 struct htb_class *class OVS_UNUSED) |
|
2430 { |
|
2431 VLOG_DBG("htb_setup_class__ device %s", netdev->name); |
|
2432 |
|
2433 return (0); |
|
2434 } |
|
2435 |
|
2436 static void |
|
2437 htb_parse_qdisc_details__(struct netdev *netdev_, |
|
2438 const struct smap *details, struct htb_class *hc) |
|
2439 { |
|
2440 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
2441 const char *netdev_name = netdev_get_name(netdev_); |
|
2442 const char *max_rate_s; |
|
2443 const char *physname; |
|
2444 char buffer[DLADM_PROP_VAL_MAX]; |
|
2445 int error = 0; |
|
2446 |
|
2447 VLOG_DBG("htb_parse_qdisc_details__ device %s", netdev_name); |
|
2448 |
|
2449 /* |
|
2450 * Initialize in case of early return. |
|
2451 */ |
|
2452 hc->max_rate = 0; |
|
2453 hc->min_rate = 0; |
|
2454 hc->burst = 0; |
|
2455 hc->priority = 0; |
|
2456 |
|
2457 if (netdev_solaris_is_uplink(netdev_)) { |
|
2458 physname = netdev_name; |
|
2459 } else { |
|
2460 error = solaris_get_dllower(netdev_name, buffer, |
|
2461 sizeof (buffer)); |
|
2462 if (error != 0) |
|
2463 return; |
|
2464 physname = buffer; |
|
2465 } |
|
2466 |
|
2467 max_rate_s = smap_get(details, "max-rate"); |
|
2468 hc->max_rate = max_rate_s ? strtoull(max_rate_s, NULL, 10) / 8 : 0; |
|
2469 if (!hc->max_rate) { |
|
2470 enum netdev_features current; |
|
2471 |
|
2472 netdev_solaris_read_features(netdev, physname); |
|
2473 current = !netdev->get_features_error ? netdev->current : 0; |
|
2474 hc->max_rate = netdev_features_to_bps(current, |
|
2475 100 * 1000 * 1000) / 8; |
|
2476 } |
|
2477 } |
|
2478 |
|
2479 static int |
|
2480 htb_tc_install(struct netdev *netdev, const struct smap *details) |
|
2481 { |
|
2482 int error; |
|
2483 |
|
2484 VLOG_DBG("htb_tc_install device %s", netdev->name); |
|
2485 |
|
2486 error = htb_setup_qdisc__(netdev); |
|
2487 if (!error) { |
|
2488 struct htb_class hc; |
|
2489 |
|
2490 htb_parse_qdisc_details__(netdev, details, &hc); |
|
2491 error = htb_setup_class__(netdev, 0, 0, &hc); |
|
2492 if (!error) { |
|
2493 htb_install__(netdev, hc.max_rate); |
|
2494 } |
|
2495 } |
|
2496 return (error); |
|
2497 } |
|
2498 |
|
2499 static void |
|
2500 htb_tc_destroy(struct tc *tc) |
|
2501 { |
|
2502 struct htb *htb = CONTAINER_OF(tc, struct htb, tc); |
|
2503 struct htb_class *hc, *next; |
|
2504 |
|
2505 HMAP_FOR_EACH_SAFE(hc, next, tc_queue.hmap_node, &htb->tc.queues) { |
|
2506 hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); |
|
2507 free(hc); |
|
2508 } |
|
2509 tc_destroy(tc); |
|
2510 free(htb); |
|
2511 } |
|
2512 |
|
2513 static int |
|
2514 htb_qdisc_get(const struct netdev *netdev, struct smap *details) |
|
2515 { |
|
2516 const struct htb *htb = htb_get__(netdev); |
|
2517 |
|
2518 VLOG_DBG("htb_qdisc_get device %s", netdev->name); |
|
2519 smap_add_format(details, "max-rate", "%llu", 8ULL * htb->max_rate); |
|
2520 return (0); |
|
2521 } |
|
2522 |
|
2523 static int |
|
2524 htb_qdisc_set(struct netdev *netdev, const struct smap *details) |
|
2525 { |
|
2526 struct htb_class hc; |
|
2527 int error; |
|
2528 |
|
2529 VLOG_DBG("htb_qdisc_set device %s", netdev->name); |
|
2530 htb_parse_qdisc_details__(netdev, details, &hc); |
|
2531 /* Solaris: don't care about the handles */ |
|
2532 error = htb_setup_class__(netdev, 0, 0, &hc); |
|
2533 if (!error) { |
|
2534 htb_get__(netdev)->max_rate = hc.max_rate; |
|
2535 } |
|
2536 |
|
2537 return (error); |
|
2538 } |
|
2539 |
|
2540 static struct htb_class * |
|
2541 htb_class_cast__(const struct tc_queue *queue) |
|
2542 { |
|
2543 return (CONTAINER_OF(queue, struct htb_class, tc_queue)); |
|
2544 } |
|
2545 |
|
2546 static int |
|
2547 htb_class_get(const struct netdev *netdev, |
|
2548 const struct tc_queue *queue, struct smap *details) |
|
2549 { |
|
2550 const struct htb_class *hc = htb_class_cast__(queue); |
|
2551 |
|
2552 VLOG_DBG("htb_class_get device %s", netdev->name); |
|
2553 |
|
2554 if (hc->max_rate > 0) |
|
2555 smap_add_format(details, "max-rate", "%llu", |
|
2556 8ULL * hc->max_rate); |
|
2557 |
|
2558 VLOG_DBG("htb_class_get device done"); |
|
2559 return (0); |
|
2560 } |
|
2561 |
|
2562 /* Solaris: currently, min-rate, burst and priority are not supported */ |
|
2563 static int |
|
2564 htb_parse_class_details__(struct netdev *netdev, |
|
2565 const struct smap *details, struct htb_class *hc) |
|
2566 { |
|
2567 const struct htb *htb = htb_get__(netdev); |
|
2568 const char *max_rate_s = smap_get(details, "max-rate"); |
|
2569 |
|
2570 VLOG_DBG("htb_parse_class_details__ device %s", netdev->name); |
|
2571 |
|
2572 /* max-rate */ |
|
2573 hc->max_rate = (max_rate_s |
|
2574 ? strtoull(max_rate_s, NULL, 10) / 8 |
|
2575 : htb->max_rate); |
|
2576 VLOG_DBG("htb_parse_class_details__ device max_rate is %u", |
|
2577 hc->max_rate); |
|
2578 |
|
2579 return (0); |
|
2580 } |
|
2581 |
|
2582 static void |
|
2583 htb_update_queue__(struct netdev *netdev, unsigned int queue_id, |
|
2584 const struct htb_class *hc) |
|
2585 { |
|
2586 struct htb *htb = htb_get__(netdev); |
|
2587 size_t hash = hash_int(queue_id, 0); |
|
2588 struct tc_queue *queue; |
|
2589 struct htb_class *hcp; |
|
2590 |
|
2591 VLOG_DBG("htb_update_queue__ %s", netdev->name); |
|
2592 |
|
2593 queue = tc_find_queue__(netdev, queue_id, hash); |
|
2594 if (queue) { |
|
2595 hcp = htb_class_cast__(queue); |
|
2596 } else { |
|
2597 hcp = xmalloc(sizeof (*hcp)); |
|
2598 queue = &hcp->tc_queue; |
|
2599 queue->queue_id = queue_id; |
|
2600 queue->created = time_msec(); |
|
2601 hmap_insert(&htb->tc.queues, &queue->hmap_node, hash); |
|
2602 } |
|
2603 |
|
2604 hcp->max_rate = hc->max_rate; |
|
2605 } |
|
2606 |
|
2607 static int |
|
2608 htb_class_set(struct netdev *netdev, unsigned int queue_id, |
|
2609 const struct smap *details) |
|
2610 { |
|
2611 struct htb_class hc; |
|
2612 int error; |
|
2613 |
|
2614 VLOG_DBG("htb_class_set %s", netdev->name); |
|
2615 |
|
2616 error = htb_parse_class_details__(netdev, details, &hc); |
|
2617 if (error) { |
|
2618 return (error); |
|
2619 } |
|
2620 |
|
2621 error = htb_setup_class__(netdev, 0, 0, &hc); |
|
2622 if (error) { |
|
2623 return (error); |
|
2624 } |
|
2625 |
|
2626 htb_update_queue__(netdev, queue_id, &hc); |
|
2627 return (0); |
|
2628 } |
|
2629 |
|
2630 static int |
|
2631 htb_class_delete(struct netdev *netdev, struct tc_queue *queue) |
|
2632 { |
|
2633 struct htb_class *hc = htb_class_cast__(queue); |
|
2634 struct htb *htb = htb_get__(netdev); |
|
2635 int error; |
|
2636 |
|
2637 VLOG_DBG("htb_class_delete %s", netdev->name); |
|
2638 error = tc_delete_class(netdev, 0); |
|
2639 if (!error) { |
|
2640 hmap_remove(&htb->tc.queues, &hc->tc_queue.hmap_node); |
|
2641 free(hc); |
|
2642 } |
|
2643 return (error); |
|
2644 } |
|
2645 |
|
2646 /* |
|
2647 * Used to get existing configuration for qdiscs in the kernel, not used in |
|
2648 * Solaris. |
|
2649 */ |
|
2650 static int |
|
2651 htb_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) |
|
2652 { |
|
2653 VLOG_DBG("htb_tc_load %s", netdev->name); |
|
2654 |
|
2655 return (0); |
|
2656 } |
|
2657 |
|
2658 static const struct tc_ops tc_ops_htb = { |
|
2659 "htb", /* linux_name */ |
|
2660 "linux-htb", /* ovs_name */ |
|
2661 HTB_N_QUEUES, /* n_queues */ |
|
2662 htb_tc_install, |
|
2663 htb_tc_load, |
|
2664 htb_tc_destroy, |
|
2665 htb_qdisc_get, |
|
2666 htb_qdisc_set, |
|
2667 htb_class_get, |
|
2668 htb_class_set, |
|
2669 htb_class_delete, |
|
2670 NULL, |
|
2671 NULL |
|
2672 }; |
|
2673 |
|
2674 /* |
|
2675 * The default traffic control class. |
|
2676 * |
|
2677 * This class represents the default, unnamed Linux qdisc. It corresponds to |
|
2678 * the "" (empty string) QoS type in the OVS database. |
|
2679 */ |
|
2680 static void |
|
2681 default_install__(struct netdev *netdev_) |
|
2682 { |
|
2683 const char *netdev_name = netdev_get_name(netdev_); |
|
2684 struct netdev_solaris *netdev = netdev_solaris_cast(netdev_); |
|
2685 static const struct tc tc = TC_INITIALIZER(&tc, &tc_ops_default); |
|
2686 |
|
2687 VLOG_DBG("default_install__ device %s", netdev_name); |
|
2688 |
|
2689 /* |
|
2690 * Nothing but a tc class implementation is allowed to write to a tc. |
|
2691 * This class never does that, so we can legitimately use a const tc |
|
2692 * object. |
|
2693 */ |
|
2694 netdev->tc = CONST_CAST(struct tc *, &tc); |
|
2695 } |
|
2696 |
|
2697 static int |
|
2698 default_tc_install(struct netdev *netdev, |
|
2699 const struct smap *details OVS_UNUSED) |
|
2700 { |
|
2701 VLOG_DBG("default_tc_install device %s", netdev->name); |
|
2702 default_install__(netdev); |
|
2703 return (0); |
|
2704 } |
|
2705 |
|
2706 static int |
|
2707 default_tc_load(struct netdev *netdev, struct ofpbuf *nlmsg OVS_UNUSED) |
|
2708 { |
|
2709 VLOG_DBG("default_tc_load device %s", netdev->name); |
|
2710 default_install__(netdev); |
|
2711 return (0); |
|
2712 } |
|
2713 |
|
2714 static const struct tc_ops tc_ops_default = { |
|
2715 NULL, /* linux_name */ |
|
2716 "", /* ovs_name */ |
|
2717 0, /* n_queues */ |
|
2718 default_tc_install, |
|
2719 default_tc_load, |
|
2720 NULL, /* tc_destroy */ |
|
2721 NULL, /* qdisc_get */ |
|
2722 NULL, /* qdisc_set */ |
|
2723 NULL, /* class_get */ |
|
2724 NULL, /* class_set */ |
|
2725 NULL, /* class_delete */ |
|
2726 NULL, /* class_get_stats */ |
|
2727 NULL /* class_dump_stats */ |
|
2728 }; |