/*
*/
/*
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <strings.h>
#include <libdllink.h>
#include <kstat2.h>
#include "coverage.h"
#include "dpif-netdev.h"
#include "dynamic-string.h"
#include "fatal-signal.h"
#include "hash.h"
#include "hmap.h"
#include "netdev-provider.h"
#include "netdev-vport.h"
#include "ofpbuf.h"
#include "openflow/openflow.h"
#include "ovs-atomic.h"
#include "packets.h"
#include "poll-loop.h"
#include "shash.h"
#include "socket-util.h"
#include "sset.h"
#include "timer.h"
#include "unaligned.h"
#include "vlog.h"
#include "netdev-solaris.h"
#include "util-solaris.h"
#include "dpif-solaris.h"
/*
* Enable vlog() for this module
*/
/*
* Per-network device state.
*/
struct netdev_solaris {
/* Protects all members below. */
/*
* Bit mask of cached properties. These can be cached because
* nothing besides vswitchd is altering the properties.
*/
unsigned int cache_valid;
/*
* Network device features (e.g., speed, duplex, etc)
*/
int get_features_error;
};
/*
* Bits indicating what network device configuration is cached.
* See cache_valid field above.
*/
enum {
};
/*
* Used to track state of IP plumbing on network devices.
*/
/*
* Cached socket descriptors.
*/
static int sock4;
static int sock6;
/*
* Cached kstat2_handle_t. Re-use unless an error causes it to be
* closed. In that case, cache the handle on the next open.
*/
static int netdev_solaris_init(void);
/* Maintaining a mapping of every netdev->name to its bridge name. */
/*
* An instance of a traffic control class.
*
* Always associated with a particular network device.
*
* Each TC implementation subclasses this with whatever additional data
* it needs.
*/
struct tc {
/*
* Contains "struct tc_queues"s.
* Read by generic TC layer.
* Written only by TC implementation.
*/
};
/*
* One traffic control queue.
*
* Each TC implementation subclasses this with whatever additional
* data it needs.
*/
struct tc_queue {
};
/*
* A particular kind of traffic control.
*
* Each implementation generally maps to one particular Linux qdisc class.
*
* The functions below return 0 if successful or a positive errno value on
* failure, except where otherwise noted. All of them must be provided,
* except where otherwise noted.
*/
struct tc_ops {
/*
* Name used by kernel in the TCA_KIND attribute of tcmsg, e.g. "htb".
* This is null for tc_ops_default since there is no appropriate
* value.
*/
const char *linux_name;
/*
* Name used in OVS database, e.g. "linux-htb". Must be nonnull.
*/
const char *ovs_name;
/*
* Number of supported OpenFlow queues, 0 for qdiscs that have no
* queues. The queues are numbered 0 through n_queues - 1.
*/
unsigned int n_queues;
/*
* Called to install this TC class on 'netdev'. The implementation
* should make the calls required to set up 'netdev' with the right
* qdisc and configure it according to 'details'. The implementation
* may assume that the current qdisc is the default; that is, there
* is no need for it to delete the current qdisc before installing
* itself.
*
* The contents of 'details' should be documented as valid for
* 'ovs_name' in the "other_config" column in the "QoS" table in
* vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
*
* This function must return 0 if and only if it sets 'netdev->tc'
* to an initialized 'struct tc'.
*
* This function should always be nonnull.
*/
/*
* Called when the netdev code determines that this TC class's qdisc
* is installed on 'netdev', but we didn't install it ourselves and
* so don't know any of the details.
*
* There is nothing for this function to do on Solaris.
*
* This function must return 0 if and only if it sets 'netdev->tc'
* to an initialized 'struct tc'.
*/
/*
* Destroys the data structures allocated by the implementation as
* part of 'tc'. (This includes destroying 'tc->queues' by calling
* tc_destroy(tc).
*
* This function may be null if 'tc' is trivial.
*/
/*
* Retrieves details of 'netdev->tc' configuration into 'details'.
*
* The contents of 'details' should be documented as valid for
* 'ovs_name' in the "other_config" column in the "QoS" table in
* vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
*
* This function may be null if 'tc' is not configurable.
*/
/*
* Reconfigures 'netdev->tc' according to 'details'.
*
* The contents of 'details' should be documented as valid for
* 'ovs_name' in the "other_config" column in the "QoS" table in
* vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
*
* This function may be null if 'tc' is not configurable.
*/
/*
* Retrieves details of 'queue' on 'netdev->tc' into 'details'.
* 'queue' is one of the 'struct tc_queue's within
* 'netdev->tc->queues'.
*
* The contents of 'details' should be documented as valid for
* 'ovs_name' in the "other_config" column in the "Queue" table in
* vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
*
* This function may be null if 'tc' does not have queues
* ('n_queues' is 0).
*/
/*
* Configures or reconfigures 'queue_id' on 'netdev->tc' according to
* 'details'. The caller ensures that 'queue_id' is less than
* 'n_queues'.
*
* The contents of 'details' should be documented as valid for
* 'ovs_name' in the "other_config" column in the "Queue" table in
* vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)).
*
* This function may be null if 'tc' does not have queues or its
* queues are not configurable.
*/
/*
* Deletes 'queue' from 'netdev->tc'. 'queue' is one of the 'struct
* tc_queue's within 'netdev->tc->queues'.
*
* This function may be null if 'tc' does not have queues or its queues
* cannot be deleted.
*/
/*
* Obtains stats for 'queue' from 'netdev->tc'. 'queue' is one of the
* 'struct tc_queue's within 'netdev->tc->queues'.
*
* On success, initializes '*stats'.
*
* This function may be null if 'tc' does not have queues or if it
* cannot report queue statistics.
*/
/*
* Extracts queue stats from 'nlmsg', which is a response to a
* RTM_GETTCLASS message, and passes them to 'cb' along with 'aux'.
*
* This function may be null if 'tc' does not have queues or if it
* cannot report queue statistics.
*/
void *aux);
};
static void
{
}
static void
{
}
&tc_ops_htb, /* Hierarchy token bucket */
&tc_ops_default, /* Default qdisc */
};
static const struct tc_ops *
{
return (ops);
}
}
return (NULL);
}
static struct tc_queue *
{
return (queue);
}
}
return (NULL);
}
static struct tc_queue *
{
}
static int
unsigned int handle OVS_UNUSED)
{
return (0);
}
static int
{
}
}
return (0);
}
/*
* If 'netdev''s qdisc type and parameters are not yet known, queries the
* kernel to determine what they are. Returns 0 if successful, otherwise a
* positive errno value.
*/
static int
{
int load_error;
int error;
return (0);
}
/*
* Either it's a built-in qdisc, or it's a qdisc set up by some
* other entity that doesn't have a handle 1:0. We will assume
* that it's the system default qdisc.
*/
ops = &tc_ops_default;
error = 0;
/* Instantiate it. */
}
static bool
{
}
static struct netdev_solaris *
{
}
char *
{
}
static int
{
char *astring;
int sock;
int proto;
int error;
astring = "IPv4";
} else {
astring = "IPv6";
}
return (0);
if (error != 0) {
return (error);
}
return (0);
}
static int
{
char *astring;
int sock;
int proto;
int error;
astring = "IPv4";
} else {
astring = "IPv6";
}
return (0);
}
if (error != 0) {
return (0);
}
error);
}
return (0);
}
struct solaris_netdev_sdmap {
const char *sns_val;
};
static int
{
{"10G-f", NETDEV_F_10GB_FD},
{"1G-f", NETDEV_F_1GB_FD},
{"1G-h", NETDEV_F_1GB_HD},
{"100M-f", NETDEV_F_100MB_FD},
{"100M-h", NETDEV_F_100MB_HD},
{"10M-f", NETDEV_F_100MB_FD},
{"10M-h", NETDEV_F_10MB_HD},
{"40G-f", NETDEV_F_40GB_FD},
{"100G-f", NETDEV_F_100GB_FD},
{"1T-f", NETDEV_F_1TB_FD},
{NULL, NETDEV_F_OTHER},
};
int i;
int error;
if (error != 0) {
VLOG_ERR("Unable to retrieve feature speed-duplex for %s "
"device", netdev_name);
return (error);
}
}
return (0);
}
static int
const char *netdev_name)
{
int error = 0;
int speed;
bool full;
goto exit;
/*
* Supported features
*
* Unsupported features:
* NETDEV_F_COPPER
* NETDEV_F_FIBER
* NETDEV_F_PAUSE;
* NETDEV_F_PAUSE_ASYM;
*/
goto exit;
/*
* Advertised features.
*
* Unsupported features:
* NETDEV_F_COPPER
* NETDEV_F_FIBER
* NETDEV_F_PAUSE;
* NETDEV_F_PAUSE_ASYM;
*/
netdev->advertised = 0;
goto exit;
/* Current settings. */
sizeof (buffer));
if (error != 0) {
VLOG_ERR("Unable to retrieve speed for %s device",
goto exit;
}
sizeof (buffer));
if (error != 0) {
VLOG_ERR("Unable to retrieve duplex for %s device",
goto exit;
}
speed /= 1000000;
if (speed == 10) {
} else if (speed == 100) {
} else if (speed == 1000) {
} else if (speed == 10000) {
} else if (speed == 40000) {
} else if (speed == 100000) {
} else if (speed == 1000000) {
} else {
}
exit:
return (error);
}
static int
netdev_solaris_init(void)
{
int error;
if (sock4 < 0) {
VLOG_ERR("failed to create AF_INET socket (%s)",
return (error);
}
if (sock6 < 0) {
VLOG_ERR("failed to create AF_INET6 socket (%s)",
return (error);
}
error = solaris_init_rad();
return (error);
}
static void
netdev_solaris_run(void)
{
}
static void
netdev_solaris_wait(void)
{
}
static struct netdev *
netdev_solaris_alloc(void)
{
}
static void
{
VLOG_DBG("netdev_solaris_add_port_to_bridge_mapping: adding a mapping "
}
static int
{
int error;
bool b_error = false;
curr_lower, sizeof (curr_lower));
if (error) {
VLOG_ERR("netdev_solaris_unconfigure_uplink couldn't obtain "
return (0);
}
/*
* The uplink which we are trying to remove has bridge vnic on
* top of it. We need to migrate bridge vnic to a different
* uplink or to ovs.etherstub0 if uplink_port_list is empty.
*/
VLOG_DBG("netdev_solaris_unconfigure_uplink: unconfiguring the "
"uplink (%s) which is the bridge %s's lowerlink (%s)",
if (b_error) {
VLOG_ERR("netdev_solaris_unconfigure_uplink: couldn't "
"obtain netdev_to_migrate for bridge vnic %s",
return (0);
}
if (netdev_to_migrate == NULL)
else
VLOG_DBG("netdev_solaris_unconfigure_uplink migrating bridge "
"vnic to %s", new_lower);
if (error != 0) {
VLOG_ERR("failed to unconfigure %s as uplink for %s: "
return (0);
}
/*
* reset internal port's physname and refresh its port channel.
*/
}
return (0);
}
static int
const char *brname)
{
int error;
const char *netdev_existing_class;
/*
* Normally, we would expect to see that the bridge VNIC has already
* been created by a call to netdev_solaris_construct() to create the
* bridge netdev. However, on a service restart ovs-vswitchd sometimes
* adds the lower-link port prior to adding the bridge's internal port.
* As a result, we need to create the bridge VNIC here if it does not
* already exist.
*/
curr_lower, sizeof (curr_lower));
/*
* No implicit VNIC exists, create it now first over
* NETDEV_IMPL_ETHERSTUB and later migrate onto new_uplink. Not
* creating it directly over new_uplink as it may fail(for vnet)
* if it doesn't support creating vnic with "auto" mac-addr-type
*/
if (error == 0) {
if (error == 0) {
} else {
VLOG_ERR("Failed to migrate %s vnic over %s:%s",
}
} else {
VLOG_ERR("Failed to create vnic for %s: %s",
}
goto exit;
} else if (error != 0) {
VLOG_ERR("Failed to get the lower-link for %s: %s",
goto exit;
}
/*
* If the lower-link is already set correctly, then return with
* success.
*/
VLOG_DBG("netdev_solaris_configure_uplink lower-link(%s) is "
error = 0;
goto exit;
}
/*
* Bridge vnic is on ovs.etherstub0. We have to migrate the
* bridge vnic to the uplink
*/
VLOG_DBG("bridge vnic %s is on %s, migrating it to %s",
if (error != 0) {
VLOG_ERR("failed to configure %s as uplink: %s",
goto exit;
}
} else {
/*
* Bridge vnic is already on an uplink. Now we see if the
* uplink to be added is other than etherstub or not.
* If the bridge vnic is currently residing on an etherstub
* and the one to be configured is not etherstub,
* then we migrate the bridge vnic.
*/
if (!i_netdev) {
VLOG_ERR("netdev_solaris_configure_uplink error "
"in fetching lower-link netdev for %s", curr_lower);
goto exit;
}
/* i_netdev->ref_cnt--; */
if (error != 0) {
VLOG_ERR("failed to configure %s as uplink: %s",
goto exit;
}
}
}
exit:
/*
* reset internal port's physname and refresh its port channel.
*/
if (migrated)
return (error);
}
static boolean_t
{
int sock;
int proto;
int error;
char *astring;
astring = "IPv4";
} else {
astring = "IPv6";
}
if (error == 0) {
VLOG_DBG("netdev_is_if_plumbed %s device encountered "
}
return (error == 0);
}
int
{
int error;
if (error != 0) {
VLOG_ERR("netdev_create_impl_etherstub: failed to create %s: "
} else {
if (error != 0) {
VLOG_ERR("netdev_create_impl_etherstub: failed to "
"set 'openvswitch' property on %s: %s: ",
}
}
return (error);
}
void
{
int error;
if (error != 0) {
VLOG_ERR("netdev_create_impl_etherstub: failed to "
"set 'openvswitch' property on %s: %s: ",
} else {
if (error != 0) {
VLOG_ERR("netdev_delete_impl_etherstub: failed to "
"delete %s: %s", NETDEV_IMPL_ETHERSTUB,
}
}
}
{
int error;
return (B_FALSE);
/*
* If the implicit etherstub exists, but the openvswitch
* property is not set, then something has gone wrong.
*/
VLOG_FATAL("%s does not have openvswitch property set",
}
return (B_TRUE);
}
/*
* Creates system and internal devices. Really very little to
* do here given that neither system or internal devices must
* exist in the kernel yet.
*/
static int
{
int error;
VLOG_DBG("netdev_solaris_construct system device %s",
} else {
VLOG_DBG("netdev_solaris_construct internal device %s",
}
netdev->implicitly_plumbed = 0;
/*
* loopback is illegal.
*/
VLOG_ERR("%s: cannot add a loopback device",
return (EINVAL);
}
if (error != 0) {
VLOG_ERR("failed to configure %s as uplink: "
return (error);
}
} else if (error != 0) {
VLOG_ERR("Failed to get the lower-link for %s: %s",
}
}
return (0);
}
static void
{
int error;
/*
* If implicitly plumbed, then unplumb it.
*/
if (error != 0) {
VLOG_ERR("failed to delete %s: %s",
}
} else {
}
}
}
static void
{
}
/*
* Attempts to set 'netdev''s MAC address to 'mac'. Returns 0 if successful,
* otherwise a positive errno value.
*/
static int
{
int error = 0;
goto exit;
}
/*
* In case there is some kind of failure, invalidate
* the cached value.
*/
/*
* MAC addresses are datalink properties.
*/
ETH_ADDR_ARGS(mac));
B_TRUE);
if (error != 0) {
VLOG_ERR("set etheraddr %s on %s device failed: %s",
goto exit;
}
exit:
return (error);
}
static int
{
int error = 0;
goto exit;
/*
* Get the datalink class for this link.
*/
if (error != 0) {
VLOG_ERR("Unable to retrieve linkclass for %s device, %d",
netdev_name, error);
goto exit;
}
exit:
return (error);
}
/*
* Determines if this netdev is an uplink for the bridge.
*/
static bool
{
if (netdev_solaris_get_dlclass(netdev_) != 0)
return (false);
}
static int
{
int error = 0;
/*
* Only the bridge ethernet address can be changed outside the
* knowledge of vswitchd (when the uplink is added and the bridge
* VNIC is moved to the uplink).
*/
goto exit;
}
/*
* MAC addresses are datalink properties.
*/
if (error != 0) {
VLOG_ERR("Unable to retrieve etheraddr for %s device",
goto exit;
}
goto exit;
}
exit:
return (error);
}
static int
const char *netdev_name)
{
int mtu;
int error = 0;
return (0);
}
/*
* MTU is a datalink property
*/
sizeof (buffer));
if (error != 0) {
VLOG_ERR("Unable to retrieve mtu for %s device",
return (error);
}
if (mtu == 0) {
return (error);
}
return (0);
}
/*
* Returns the maximum size of transmitted (and received) packets on 'netdev',
* in bytes, not including the hardware header; thus, this is typically 1500
* bytes for Ethernet devices.
*/
static int
{
int error = 0;
if (!netdev_) {
VLOG_DBG("netdev_solaris_get_mtu null netdevs");
return (error);
}
return (error);
}
/*
* Sets the maximum size of transmitted (MTU) for given device.
*/
static int
{
int error = 0;
goto exit;
/*
* In case there is some kind of failure, invalidate
* the cached value.
*/
/*
* MTU is a datalink property.
*/
if (error != 0) {
VLOG_ERR("set mtu on %s device failed: %s",
goto exit;
}
exit:
return (error);
}
/*
* Returns the ifindex of 'netdev', if successful, as a positive number.
* On failure, returns a negative errno.
*/
static int
{
int ifindex;
int error = 0;
goto exit;
}
if (ifindex <= 0) {
goto exit;
}
exit:
}
/*
* Retrieves current device stats for 'netdev-solaris'.
*/
static int
struct netdev_stats *stats)
{
char *name;
int error = 0;
/*
* Initialize statistics only supported on uplink.
*/
stats->rx_length_errors = 0;
stats->rx_missed_errors = 0;
stats->rx_over_errors = 0;
/*
* Unsupported on Solaris.
*/
if (!kstat2_handle_initialized) {
VLOG_DBG("netdev_solaris_get_stats initializing handle");
if (!kstat_handle_init(&nd_khandle)) {
error = -1;
goto done;
}
} else if (!kstat_handle_update(nd_khandle)) {
VLOG_DBG("netdev_solaris_get_stats error updating stats");
error = -1;
goto done;
}
name = (char *)netdev_name;
instance = 0;
&zid);
name = kstat_name;
}
if (stat != KSTAT2_S_OK) {
} else {
if (is_uplink) {
} else {
}
}
if (stat != KSTAT2_S_OK) {
} else {
if (is_uplink) {
} else {
}
}
if (!is_uplink)
goto done;
/*
* Can we do anything for aggr?
*/
goto done;
VLOG_WARN("Failed to retrieve devname of uplink\n");
error = -1;
goto done;
}
&instance)) {
VLOG_DBG("netdev_solaris_get_stats error getting "
error = -1;
goto done;
}
if (stat != KSTAT2_S_OK) {
error = -1;
goto done;
}
done:
return (error);
}
/*
* Stores the features supported by 'netdev' into of '*current', '*advertised',
* '*supported', and '*peer'. Each value is a bitmap of NETDEV_* bits.
* Returns 0 if successful, otherwise a positive errno value.
*/
static int
enum netdev_features *current,
enum netdev_features *advertised,
enum netdev_features *supported,
enum netdev_features *peer)
{
const char *physname;
int error = 0;
if (netdev_solaris_is_uplink(netdev_)) {
} else {
sizeof (buffer));
if (error != 0)
goto exit;
}
if (error != 0)
goto exit;
*peer = 0;
exit:
return (error);
}
/*
* Attempts to set input rate limiting (policing) policy. Returns 0 if
* successful, otherwise a positive errno value.
*/
static int
{
int error = 0;
goto out;
VLOG_DBG("netdev_solaris_set_policing setting "
"ingress_policing_rate %d kbps on device %s",
VLOG_ERR("set ingress_policing_rate "
"%d kbps on %s failed: %s",
goto out;
}
out:
return (error);
}
static int
{
/*
* XXXSolaris we will support only HTB-equivalent which will translate
*/
}
return (0);
}
static int
{
VLOG_DBG("netdev_solaris_get_qos_capabilities device %s",
if (!ops)
return (EOPNOTSUPP);
/*
* Arbit number for now. In theory we are not limited for bandwidth
* limit. But, with shares and priority this can be revisited.
*/
return (error);
}
static int
{
int error = 0;
if (!error) {
}
return (error);
}
static int
{
int error = 0;
if (!new_ops) {
return (EOPNOTSUPP);
}
if (error) {
VLOG_DBG("netdev_solaris_set_qos qdisc querry failed");
goto exit;
}
0;
} else {
if (error) {
VLOG_DBG("netdev_solaris_set_qos error deleting %s",
type);
goto exit;
}
/* Install new qdisc. */
error);
}
exit:
return (error);
}
static int
{
int error = 0;
if (!error) {
ENOENT);
}
error);
return (error);
}
static int
{
int error = 0;
if (!error) {
EINVAL);
} else {
}
return (error);
}
static int
unsigned int queue_id)
{
int error = 0;
if (!error) {
: ENOENT);
} else {
}
}
return (error);
}
static int
unsigned int queue_id OVS_UNUSED,
{
int error = 0;
return (error);
}
struct netdev_solaris_queue_state {
unsigned int *queues;
};
static int
void **statep)
{
int error = 0;
if (!error) {
size_t i;
i = 0;
}
} else {
error = EOPNOTSUPP;
}
} else {
VLOG_DBG("netdev_solaris_queue_dump_start: no qdisc");
}
return (error);
}
static int
{
if (!state) {
VLOG_DBG("netdev_solaris_queue_dump_next %s: null state\n",
return (EOF);
}
if (queue) {
break;
}
details);
break;
}
}
return (error);
}
static int
void *state_)
{
if (state) {
}
return (0);
}
static int
{
int error = 0;
return (error);
}
/*
* If 'netdev' has an assigned IPv4 address, sets '*address' to that
* address and '*netmask' to the associated netmask. Otherwise, returns
* errno.
*/
static int
{
int error = 0;
goto exit;
}
if (error != 0)
goto exit;
/*
* In the future, a RAD IP module might be a good provider
* of this information. For now, use the SIOCGLIFADDR.
*/
goto exit;
}
goto exit;
}
} else {
}
exit:
return (error);
}
static int
{
int error = 0;
if (error != 0)
goto exit;
/*
* In the future, a RAD IP module might be a good provider
* of this information. For now, use the SIOCSLIFADDR.
*/
goto exit;
}
goto done;
goto done;
}
done:
exit:
return (error);
}
/*
* If 'netdev' has an assigned IPv6 address, sets '*address' to that
* address. Otherwise, returns errno.
*/
static int
{
int error = 0;
if (!IN6_IS_ADDR_UNSPECIFIED(address))
else
goto exit;
}
if (error != 0)
goto exit;
/*
* In the future, a RAD IP module might be a good provider
* of this information. For now, use the SIOCGLIFADDR.
*/
goto exit;
}
if (!IN6_IS_ADDR_UNSPECIFIED(address))
else
exit:
return (error);
}
sizeof (long))
typedef struct rtmsg {
} rtmsg_t;
static int
{
case AF_INET:
return (sizeof (struct sockaddr_in));
case AF_LINK:
return (sizeof (struct sockaddr_dl));
case AF_INET6:
return (sizeof (struct sockaddr_in6));
default:
return (sizeof (struct sockaddr));
}
}
/*
* Adds 'router' as a default IP gateway.
*/
static int
{
int rtsock_fd;
int error = 0;
int l;
if (rtsock_fd == -1) {
VLOG_ERR("failed to create PF_ROUTE socket (%s)",
return (error);
}
l = ROUNDUP_LONG(sizeof (struct sockaddr_in));
cp += l;
cp += l;
cp += l;
}
return (error);
}
/*
* Looks up the next hop for 'host' in the host's routing table. If
* successful, stores the next hop gateway's address (0 if 'host' is on a
* directly connected network) in '*next_hop' and a copy of the name of the
* device to reach 'host' in '*netdev_name', and returns 0. The caller is
* responsible for freeing '*netdev_name' (by calling free()).
*/
static int
{
int rtsock_fd;
static int seq;
int saved_errno;
int error = 0;
int rlen;
int l;
int i;
if (rtsock_fd == -1) {
VLOG_ERR("failed to create PF_ROUTE socket (%s)",
return (error);
}
l = ROUNDUP_LONG(sizeof (struct sockaddr_in));
cp += l;
l = ROUNDUP_LONG(sizeof (struct sockaddr_dl));
cp += l;
return (errno);
}
*netdev_name = NULL;
do {
saved_errno = errno;
if (ssz <= 0) {
if (ssz < 0) {
return (saved_errno);
}
return (EPIPE);
}
for (i = 1; i; i <<= 1) {
ALIGNED_CAST(const struct sockaddr_in *,
sa);
}
ALIGNED_CAST(const struct sockaddr_dl *,
sa);
}
}
}
return (ENXIO);
}
if (!gateway) {
}
*netdev_name = ifname;
return (0);
}
static int
{
/*
* It looks like this is used to populate a column,
* OVSREC_INTERFACE_COL_STATUS in the OVSDB. It doesn't appear
* to be required though. If we wanted to return the driver name
* then we could return that using libdladm.
*/
return (error);
}
/*
* Looks up the ARP table entry for 'ip' on 'netdev'. If one exists and can be
* successfully retrieved, it stores the corresponding MAC address in 'mac' and
* returns 0. Otherwise, it returns a positive errno value; in particular,
* ENXIO indicates that there is not ARP table entry for 'ip' on 'netdev'.
*/
static int
{
int error = 0;
goto out;
}
goto out;
}
out:
return (error);
}
static int
{
}
if (lifrflags & IFF_PROMISC) {
}
if (lifrflags & IFF_LOOPBACK) {
}
return (nd_flags);
}
static int64_t
{
}
if (nd_flags & NETDEV_PROMISC) {
lifrflags |= IFF_PROMISC;
}
if (nd_flags & NETDEV_LOOPBACK) {
}
return (lifrflags);
}
/*
* Retrieves the current set of flags on 'netdev' into '*old_flags'. Then,
* turns off the flags that are set to 1 in 'off' and turns on the flags
* that are set to 1 in 'on'. (No bit will be set to 1 in both 'off' and
* 'on'; that is, off & on == 0.)
*/
static int
{
int error = 0;
goto exit;
}
if (new_lifr_flags == old_lifr_flags)
goto exit;
goto exit;
}
exit:
return (error);
}
static int
{
return (0);
}
{ \
NAME, \
CONSTRUCT, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
GET_STATS, \
SET_STATS, \
GET_FEATURES, \
NULL, \
GET_STATUS, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL \
}
"system",
NULL, /* set_stats */
"internal",
NULL, /* set_stats */
NULL, /* get_features */
/* Solaris HTB traffic control class */
struct htb {
};
struct htb_class {
};
/*
* Create an HTB qdisc.
*
* Equivalent to "tc qdisc add dev <dev> root handle 1: htb default 1".
*/
static int
{
return (0);
}
static struct htb *
{
}
static void
{
}
static int
unsigned int parent OVS_UNUSED,
{
return (0);
}
static void
{
const char *max_rate_s;
const char *physname;
int error = 0;
/*
* Initialize in case of early return.
*/
if (netdev_solaris_is_uplink(netdev_)) {
} else {
sizeof (buffer));
if (error != 0)
return;
}
100 * 1000 * 1000) / 8;
}
}
static int
{
int error;
if (!error) {
if (!error) {
}
}
return (error);
}
static void
{
}
tc_destroy(tc);
}
static int
{
return (0);
}
static int
{
int error;
/* Solaris: don't care about the handles */
if (!error) {
}
return (error);
}
static struct htb_class *
{
}
static int
{
VLOG_DBG("htb_class_get device done");
return (0);
}
/* Solaris: currently, min-rate, burst and priority are not supported */
static int
{
/* max-rate */
VLOG_DBG("htb_parse_class_details__ device max_rate is %u",
return (0);
}
static void
{
if (queue) {
} else {
}
}
static int
{
int error;
if (error) {
return (error);
}
if (error) {
return (error);
}
return (0);
}
static int
{
int error;
if (!error) {
}
return (error);
}
/*
* Used to get existing configuration for qdiscs in the kernel, not used in
* Solaris.
*/
static int
{
return (0);
}
"htb", /* linux_name */
"linux-htb", /* ovs_name */
HTB_N_QUEUES, /* n_queues */
NULL,
};
/*
* The default traffic control class.
*
* This class represents the default, unnamed Linux qdisc. It corresponds to
* the "" (empty string) QoS type in the OVS database.
*/
static void
{
/*
* Nothing but a tc class implementation is allowed to write to a tc.
* This class never does that, so we can legitimately use a const tc
* object.
*/
}
static int
{
return (0);
}
static int
{
return (0);
}
NULL, /* linux_name */
"", /* ovs_name */
0, /* n_queues */
NULL, /* tc_destroy */
NULL, /* qdisc_get */
NULL, /* qdisc_set */
NULL, /* class_get */
NULL, /* class_set */
NULL, /* class_delete */
NULL, /* class_get_stats */
NULL /* class_dump_stats */
};