mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 09:58:01 +00:00
To easily allow both in- and out-of-tree building of the Python wrapper for the OVS JSON parser (e.g. w/ pip), move json.h to include/openvswitch. This also requires moving lib/{hmap,shash}.h. Both hmap.h and shash.h were #include-ing "util.h" even though the headers themselves did not use anything from there, but rather from include/openvswitch/util.h. Fixing that required including util.h in several C files mostly due to OVS_NOT_REACHED and things like xmalloc. Signed-off-by: Terry Wilson <twilson@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
514 lines
15 KiB
C
514 lines
15 KiB
C
/*
|
|
* Copyright (c) 2014, 2016 VMware, 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:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <config.h>
|
|
#include <errno.h>
|
|
#include <iphlpapi.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include "coverage.h"
|
|
#include "fatal-signal.h"
|
|
#include "netdev-provider.h"
|
|
#include "openvswitch/ofpbuf.h"
|
|
#include "packets.h"
|
|
#include "poll-loop.h"
|
|
#include "openvswitch/shash.h"
|
|
#include "svec.h"
|
|
#include "openvswitch/vlog.h"
|
|
#include "odp-netlink.h"
|
|
#include "netlink-socket.h"
|
|
#include "netlink.h"
|
|
|
|
VLOG_DEFINE_THIS_MODULE(netdev_windows);
|
|
static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(9999, 5);
|
|
|
|
enum {
|
|
VALID_ETHERADDR = 1 << 0,
|
|
VALID_MTU = 1 << 1,
|
|
VALID_IFFLAG = 1 << 5,
|
|
};
|
|
|
|
/* Caches the information of a netdev. */
|
|
struct netdev_windows {
|
|
struct netdev up;
|
|
int32_t dev_type;
|
|
uint32_t port_no;
|
|
|
|
unsigned int change_seq;
|
|
|
|
unsigned int cache_valid;
|
|
int ifindex;
|
|
struct eth_addr mac;
|
|
uint32_t mtu;
|
|
unsigned int ifi_flags;
|
|
};
|
|
|
|
/* Utility structure for netdev commands. */
|
|
struct netdev_windows_netdev_info {
|
|
/* Generic Netlink header. */
|
|
uint8_t cmd;
|
|
|
|
/* Information that is relevant to ovs. */
|
|
uint32_t dp_ifindex;
|
|
uint32_t port_no;
|
|
uint32_t ovs_type;
|
|
|
|
/* General information of a network device. */
|
|
const char *name;
|
|
struct eth_addr mac_address;
|
|
uint32_t mtu;
|
|
uint32_t ifi_flags;
|
|
};
|
|
|
|
static int query_netdev(const char *devname,
|
|
struct netdev_windows_netdev_info *reply,
|
|
struct ofpbuf **bufp);
|
|
static struct netdev *netdev_windows_alloc(void);
|
|
static int netdev_windows_init_(void);
|
|
|
|
/* Generic Netlink family numbers for OVS.
|
|
*
|
|
* Initialized by netdev_windows_init_(). */
|
|
static int ovs_win_netdev_family;
|
|
struct nl_sock *ovs_win_netdev_sock;
|
|
|
|
|
|
static bool
|
|
is_netdev_windows_class(const struct netdev_class *netdev_class)
|
|
{
|
|
return netdev_class->alloc == netdev_windows_alloc;
|
|
}
|
|
|
|
static struct netdev_windows *
|
|
netdev_windows_cast(const struct netdev *netdev_)
|
|
{
|
|
ovs_assert(is_netdev_windows_class(netdev_get_class(netdev_)));
|
|
return CONTAINER_OF(netdev_, struct netdev_windows, up);
|
|
}
|
|
|
|
static int
|
|
netdev_windows_init_(void)
|
|
{
|
|
int error = 0;
|
|
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
|
|
|
|
if (ovsthread_once_start(&once)) {
|
|
error = nl_lookup_genl_family(OVS_WIN_NETDEV_FAMILY,
|
|
&ovs_win_netdev_family);
|
|
if (error) {
|
|
VLOG_ERR("Generic Netlink family '%s' does not exist. "
|
|
"The Open vSwitch kernel module is probably not loaded.",
|
|
OVS_WIN_NETDEV_FAMILY);
|
|
}
|
|
if (!error) {
|
|
/* XXX: Where to close this socket? */
|
|
error = nl_sock_create(NETLINK_GENERIC, &ovs_win_netdev_sock);
|
|
}
|
|
|
|
ovsthread_once_done(&once);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static struct netdev *
|
|
netdev_windows_alloc(void)
|
|
{
|
|
struct netdev_windows *netdev = xzalloc(sizeof *netdev);
|
|
return netdev ? &netdev->up : NULL;
|
|
}
|
|
|
|
static uint32_t
|
|
dp_to_netdev_ifi_flags(uint32_t dp_flags)
|
|
{
|
|
uint32_t nd_flags = 0;
|
|
|
|
if (dp_flags && OVS_WIN_NETDEV_IFF_UP) {
|
|
nd_flags |= NETDEV_UP;
|
|
}
|
|
|
|
if (dp_flags && OVS_WIN_NETDEV_IFF_PROMISC) {
|
|
nd_flags |= NETDEV_PROMISC;
|
|
}
|
|
|
|
return nd_flags;
|
|
}
|
|
|
|
static int
|
|
netdev_windows_system_construct(struct netdev *netdev_)
|
|
{
|
|
struct netdev_windows *netdev = netdev_windows_cast(netdev_);
|
|
struct netdev_windows_netdev_info info;
|
|
struct ofpbuf *buf;
|
|
int ret;
|
|
|
|
/* Query the attributes and runtime status of the netdev. */
|
|
ret = query_netdev(netdev_get_name(&netdev->up), &info, &buf);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
ofpbuf_delete(buf);
|
|
|
|
netdev->change_seq = 1;
|
|
netdev->dev_type = info.ovs_type;
|
|
netdev->port_no = info.port_no;
|
|
|
|
netdev->mac = info.mac_address;
|
|
netdev->cache_valid = VALID_ETHERADDR;
|
|
netdev->ifindex = -EOPNOTSUPP;
|
|
|
|
netdev->mtu = info.mtu;
|
|
netdev->cache_valid |= VALID_MTU;
|
|
|
|
netdev->ifi_flags = dp_to_netdev_ifi_flags(info.ifi_flags);
|
|
netdev->cache_valid |= VALID_IFFLAG;
|
|
|
|
VLOG_DBG("construct device %s, ovs_type: %u.",
|
|
netdev_get_name(&netdev->up), info.ovs_type);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
netdev_windows_netdev_to_ofpbuf(struct netdev_windows_netdev_info *info,
|
|
struct ofpbuf *buf)
|
|
{
|
|
struct ovs_header *ovs_header;
|
|
int error = EINVAL;
|
|
|
|
nl_msg_put_genlmsghdr(buf, 0, ovs_win_netdev_family,
|
|
NLM_F_REQUEST | NLM_F_ECHO,
|
|
info->cmd, OVS_WIN_NETDEV_VERSION);
|
|
|
|
ovs_header = ofpbuf_put_uninit(buf, sizeof *ovs_header);
|
|
ovs_header->dp_ifindex = info->dp_ifindex;
|
|
|
|
if (info->name) {
|
|
nl_msg_put_string(buf, OVS_WIN_NETDEV_ATTR_NAME, info->name);
|
|
error = 0;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
static void
|
|
netdev_windows_info_init(struct netdev_windows_netdev_info *info)
|
|
{
|
|
memset(info, 0, sizeof *info);
|
|
}
|
|
|
|
static int
|
|
netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info,
|
|
struct ofpbuf *buf)
|
|
{
|
|
static const struct nl_policy ovs_netdev_policy[] = {
|
|
[OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 },
|
|
[OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 },
|
|
[OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ },
|
|
[OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) },
|
|
[OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 },
|
|
[OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 },
|
|
};
|
|
|
|
netdev_windows_info_init(info);
|
|
|
|
struct ofpbuf b = ofpbuf_const_initializer(&b, buf->data, buf->size);
|
|
struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
|
|
struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl);
|
|
struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)];
|
|
if (!nlmsg || !genl || !ovs_header
|
|
|| nlmsg->nlmsg_type != ovs_win_netdev_family
|
|
|| !nl_policy_parse(&b, 0, ovs_netdev_policy, a,
|
|
ARRAY_SIZE(ovs_netdev_policy))) {
|
|
return EINVAL;
|
|
}
|
|
|
|
info->cmd = genl->cmd;
|
|
info->dp_ifindex = ovs_header->dp_ifindex;
|
|
info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]);
|
|
info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]);
|
|
info->name = nl_attr_get_string(a[OVS_WIN_NETDEV_ATTR_NAME]);
|
|
memcpy(&info->mac_address, nl_attr_get_unspec(a[OVS_WIN_NETDEV_ATTR_MAC_ADDR],
|
|
sizeof(info->mac_address)), sizeof(info->mac_address));
|
|
info->mtu = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_MTU]);
|
|
info->ifi_flags = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_IF_FLAGS]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
query_netdev(const char *devname,
|
|
struct netdev_windows_netdev_info *info,
|
|
struct ofpbuf **bufp)
|
|
{
|
|
int error = 0;
|
|
struct ofpbuf *request_buf;
|
|
|
|
ovs_assert(info != NULL);
|
|
netdev_windows_info_init(info);
|
|
|
|
error = netdev_windows_init_();
|
|
if (error) {
|
|
if (info) {
|
|
*bufp = NULL;
|
|
netdev_windows_info_init(info);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
request_buf = ofpbuf_new(1024);
|
|
info->cmd = OVS_WIN_NETDEV_CMD_GET;
|
|
info->name = devname;
|
|
error = netdev_windows_netdev_to_ofpbuf(info, request_buf);
|
|
if (error) {
|
|
ofpbuf_delete(request_buf);
|
|
return error;
|
|
}
|
|
|
|
error = nl_transact(NETLINK_GENERIC, request_buf, bufp);
|
|
ofpbuf_delete(request_buf);
|
|
|
|
if (info) {
|
|
if (!error) {
|
|
error = netdev_windows_netdev_from_ofpbuf(info, *bufp);
|
|
}
|
|
if (error) {
|
|
netdev_windows_info_init(info);
|
|
ofpbuf_delete(*bufp);
|
|
*bufp = NULL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
netdev_windows_destruct(struct netdev *netdev_)
|
|
{
|
|
|
|
}
|
|
|
|
static void
|
|
netdev_windows_dealloc(struct netdev *netdev_)
|
|
{
|
|
struct netdev_windows *netdev = netdev_windows_cast(netdev_);
|
|
free(netdev);
|
|
}
|
|
|
|
static int
|
|
netdev_windows_get_etheraddr(const struct netdev *netdev_,
|
|
struct eth_addr *mac)
|
|
{
|
|
struct netdev_windows *netdev = netdev_windows_cast(netdev_);
|
|
|
|
ovs_assert((netdev->cache_valid & VALID_ETHERADDR) != 0);
|
|
if (netdev->cache_valid & VALID_ETHERADDR) {
|
|
*mac = netdev->mac;
|
|
} else {
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
netdev_windows_get_mtu(const struct netdev *netdev_, int *mtup)
|
|
{
|
|
struct netdev_windows *netdev = netdev_windows_cast(netdev_);
|
|
|
|
ovs_assert((netdev->cache_valid & VALID_MTU) != 0);
|
|
if (netdev->cache_valid & VALID_MTU) {
|
|
*mtup = netdev->mtu;
|
|
} else {
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This functionality is not really required by the datapath.
|
|
* But vswitchd bringup expects this to be implemented. */
|
|
static int
|
|
netdev_windows_set_etheraddr(const struct netdev *netdev_,
|
|
const struct eth_addr mac)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* This functionality is not really required by the datapath.
|
|
* But vswitchd bringup expects this to be implemented. */
|
|
static int
|
|
netdev_windows_update_flags(struct netdev *netdev_,
|
|
enum netdev_flags off,
|
|
enum netdev_flags on,
|
|
enum netdev_flags *old_flagsp)
|
|
{
|
|
struct netdev_windows *netdev = netdev_windows_cast(netdev_);
|
|
|
|
ovs_assert((netdev->cache_valid & VALID_IFFLAG) != 0);
|
|
if (netdev->cache_valid & VALID_IFFLAG) {
|
|
*old_flagsp = netdev->ifi_flags;
|
|
/* Setting the interface flags is not supported. */
|
|
} else {
|
|
return EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Looks up in the ARP table entry for a given 'ip'. If it is found, the
|
|
* corresponding MAC address will be copied in 'mac' and return 0. If no
|
|
* matching entry is found or an error occurs it will log it and return ENXIO.
|
|
*/
|
|
static int
|
|
netdev_windows_arp_lookup(const struct netdev *netdev,
|
|
ovs_be32 ip, struct eth_addr *mac)
|
|
{
|
|
PMIB_IPNETTABLE arp_table = NULL;
|
|
/* The buffer length of all ARP entries */
|
|
uint32_t buffer_length = 0;
|
|
uint32_t ret_val = 0;
|
|
uint32_t counter = 0;
|
|
|
|
ret_val = GetIpNetTable(arp_table, &buffer_length, false);
|
|
|
|
if (ret_val != ERROR_INSUFFICIENT_BUFFER ) {
|
|
VLOG_ERR("Call to GetIpNetTable failed with error: %s",
|
|
ovs_format_message(ret_val));
|
|
return ENXIO;
|
|
}
|
|
|
|
arp_table = (MIB_IPNETTABLE *) malloc(buffer_length);
|
|
|
|
if (arp_table == NULL) {
|
|
VLOG_ERR("Could not allocate memory for all the interfaces");
|
|
return ENXIO;
|
|
}
|
|
|
|
ret_val = GetIpNetTable(arp_table, &buffer_length, false);
|
|
|
|
if (ret_val == NO_ERROR) {
|
|
for (counter = 0; counter < arp_table->dwNumEntries; counter++) {
|
|
if (arp_table->table[counter].dwAddr == ip) {
|
|
memcpy(mac, arp_table->table[counter].bPhysAddr, ETH_ADDR_LEN);
|
|
|
|
free(arp_table);
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
VLOG_ERR("Call to GetIpNetTable failed with error: %s",
|
|
ovs_format_message(ret_val));
|
|
}
|
|
|
|
free(arp_table);
|
|
return ENXIO;
|
|
}
|
|
|
|
static int
|
|
netdev_windows_get_next_hop(const struct in_addr *host,
|
|
struct in_addr *next_hop,
|
|
char **netdev_name)
|
|
{
|
|
uint32_t ret_val = 0;
|
|
/* The buffer length of all addresses */
|
|
uint32_t buffer_length = 1000;
|
|
PIP_ADAPTER_ADDRESSES all_addr = NULL;
|
|
PIP_ADAPTER_ADDRESSES cur_addr = NULL;
|
|
|
|
ret_val = GetAdaptersAddresses(AF_INET,
|
|
GAA_FLAG_INCLUDE_PREFIX |
|
|
GAA_FLAG_INCLUDE_GATEWAYS,
|
|
NULL, all_addr, &buffer_length);
|
|
|
|
if (ret_val != ERROR_INSUFFICIENT_BUFFER ) {
|
|
VLOG_ERR("Call to GetAdaptersAddresses failed with error: %s",
|
|
ovs_format_message(ret_val));
|
|
return ENXIO;
|
|
}
|
|
|
|
all_addr = (IP_ADAPTER_ADDRESSES *) malloc(buffer_length);
|
|
|
|
if (all_addr == NULL) {
|
|
VLOG_ERR("Could not allocate memory for all the interfaces");
|
|
return ENXIO;
|
|
}
|
|
|
|
ret_val = GetAdaptersAddresses(AF_INET,
|
|
GAA_FLAG_INCLUDE_PREFIX |
|
|
GAA_FLAG_INCLUDE_GATEWAYS,
|
|
NULL, all_addr, &buffer_length);
|
|
|
|
if (ret_val == NO_ERROR) {
|
|
cur_addr = all_addr;
|
|
while (cur_addr) {
|
|
if(cur_addr->FirstGatewayAddress &&
|
|
cur_addr->FirstGatewayAddress->Address.lpSockaddr) {
|
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *)
|
|
cur_addr->FirstGatewayAddress->Address.lpSockaddr;
|
|
next_hop->s_addr = ipv4->sin_addr.S_un.S_addr;
|
|
*netdev_name = xstrdup((char *)cur_addr->FriendlyName);
|
|
|
|
free(all_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cur_addr = cur_addr->Next;
|
|
}
|
|
} else {
|
|
VLOG_ERR("Call to GetAdaptersAddresses failed with error: %s",
|
|
ovs_format_message(ret_val));
|
|
}
|
|
|
|
if (all_addr) {
|
|
free(all_addr);
|
|
}
|
|
return ENXIO;
|
|
}
|
|
|
|
static int
|
|
netdev_windows_internal_construct(struct netdev *netdev_)
|
|
{
|
|
return netdev_windows_system_construct(netdev_);
|
|
}
|
|
|
|
|
|
#define NETDEV_WINDOWS_CLASS(NAME, CONSTRUCT) \
|
|
{ \
|
|
.type = NAME, \
|
|
.is_pmd = false, \
|
|
.alloc = netdev_windows_alloc, \
|
|
.construct = CONSTRUCT, \
|
|
.destruct = netdev_windows_destruct, \
|
|
.dealloc = netdev_windows_dealloc, \
|
|
.get_etheraddr = netdev_windows_get_etheraddr, \
|
|
.set_etheraddr = netdev_windows_set_etheraddr, \
|
|
.update_flags = netdev_windows_update_flags, \
|
|
.get_next_hop = netdev_windows_get_next_hop, \
|
|
.arp_lookup = netdev_windows_arp_lookup, \
|
|
}
|
|
|
|
const struct netdev_class netdev_windows_class =
|
|
NETDEV_WINDOWS_CLASS(
|
|
"system",
|
|
netdev_windows_system_construct);
|
|
|
|
const struct netdev_class netdev_internal_class =
|
|
NETDEV_WINDOWS_CLASS(
|
|
"internal",
|
|
netdev_windows_internal_construct);
|