X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetdev-linux.c;h=b8d24ed999cad953785f8862d8aa544812221c43;hb=2158888d8d8f3b4c00dcf979390a19fa7fcf7942;hp=ccc3f845c9c2a96afcdf5d93171936d498c29911;hpb=a4af00400a835eb87569ba40e21874c05e872c0f;p=sliver-openvswitch.git diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index ccc3f845c..b8d24ed99 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "netlink.h" #include "ofpbuf.h" #include "openflow/openflow.h" +#include "openvswitch/internal_dev.h" #include "openvswitch/gre.h" #include "packets.h" #include "poll-loop.h" @@ -58,10 +60,6 @@ #include "shash.h" #include "svec.h" -#ifndef GRE_IOCTL_ONLY -#include -#endif - #define THIS_MODULE VLM_netdev_linux #include "vlog.h" @@ -84,7 +82,8 @@ enum { VALID_IN6 = 1 << 3, VALID_MTU = 1 << 4, VALID_CARRIER = 1 << 5, - VALID_IS_INTERNAL = 1 << 6 + VALID_IS_PSEUDO = 1 << 6, /* Represents is_internal and is_tap. */ + VALID_POLICING = 1 << 7 }; struct tap_state { @@ -97,13 +96,18 @@ struct netdev_dev_linux { struct shash_node *shash_node; unsigned int cache_valid; + /* The following are figured out "on demand" only. They are only valid + * when the corresponding VALID_* bit in 'cache_valid' is set. */ int ifindex; uint8_t etheraddr[ETH_ADDR_LEN]; struct in_addr address, netmask; struct in6_addr in6; int mtu; int carrier; - bool is_internal; + bool is_internal; /* Is this an openvswitch internal device? */ + bool is_tap; /* Is this a tuntap device? */ + uint32_t kbits_rate; /* Policing data. */ + uint32_t kbits_burst; union { struct tap_state tap; @@ -118,25 +122,6 @@ struct netdev_linux { /* An AF_INET socket (used for ioctl operations). */ static int af_inet_sock = -1; -struct gre_config { - uint32_t local_ip; - uint32_t remote_ip; - uint32_t in_key; - uint32_t out_key; - bool have_in_key; - bool have_out_key; - bool in_csum; - bool out_csum; -}; - -static struct { - union { - struct nl_sock *nl_sock; - int ioctl_fd; - }; - bool use_ioctl; -} gre_descriptors; - struct netdev_linux_notifier { struct netdev_notifier notifier; struct list node; @@ -150,7 +135,8 @@ static struct rtnetlink_notifier netdev_linux_poll_notifier; * additional log messages. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20); -static int destroy_gre(const char *name); +static int netdev_linux_init(void); + static int netdev_linux_do_ethtool(const char *name, struct ethtool_cmd *, int cmd, const char *cmd_name); static int netdev_linux_do_ioctl(const char *name, struct ifreq *, int cmd, @@ -169,22 +155,30 @@ static int set_etheraddr(const char *netdev_name, int hwaddr_family, const uint8_t[ETH_ADDR_LEN]); static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats); static int get_stats_via_proc(const char *netdev_name, struct netdev_stats *stats); +static int get_rtnl_sock(struct nl_sock **); + +static bool +is_netdev_linux_class(const struct netdev_class *netdev_class) +{ + return netdev_class->init == netdev_linux_init; +} static struct netdev_dev_linux * netdev_dev_linux_cast(const struct netdev_dev *netdev_dev) { - const char *type = netdev_dev_get_type(netdev_dev); - assert(!strcmp(type, "system") || !strcmp(type, "tap") - || !strcmp(type, "gre")); + const struct netdev_class *netdev_class = netdev_dev_get_class(netdev_dev); + assert(is_netdev_linux_class(netdev_class)); + return CONTAINER_OF(netdev_dev, struct netdev_dev_linux, netdev_dev); } static struct netdev_linux * netdev_linux_cast(const struct netdev *netdev) { - const char *type = netdev_get_type(netdev); - assert(!strcmp(type, "system") || !strcmp(type, "tap") - || !strcmp(type, "gre")); + struct netdev_dev *netdev_dev = netdev_get_dev(netdev); + const struct netdev_class *netdev_class = netdev_dev_get_class(netdev_dev); + assert(is_netdev_linux_class(netdev_class)); + return CONTAINER_OF(netdev, struct netdev_linux, netdev); } @@ -216,14 +210,19 @@ netdev_linux_wait(void) static void netdev_linux_cache_cb(const struct rtnetlink_change *change, - void *aux UNUSED) + void *aux OVS_UNUSED) { struct netdev_dev_linux *dev; if (change) { struct netdev_dev *base_dev = netdev_dev_from_name(change->ifname); if (base_dev) { - dev = netdev_dev_linux_cast(base_dev); - dev->cache_valid = 0; + const struct netdev_class *netdev_class = + netdev_dev_get_class(base_dev); + + if (is_netdev_linux_class(netdev_class)) { + dev = netdev_dev_linux_cast(base_dev); + dev->cache_valid = 0; + } } } else { struct shash device_shash; @@ -239,337 +238,9 @@ netdev_linux_cache_cb(const struct rtnetlink_change *change, } } -/* The arguments are marked as unused to prevent warnings on platforms where - * the Netlink interface isn't supported. */ -static int -setup_gre_netlink(const char *name UNUSED, struct gre_config *config UNUSED, - bool create UNUSED) -{ -#ifdef GRE_IOCTL_ONLY - return EOPNOTSUPP; -#else - int error; - struct ofpbuf request, *reply; - unsigned int nl_flags; - struct ifinfomsg ifinfomsg; - struct nlattr *linkinfo_hdr; - struct nlattr *info_data_hdr; - uint16_t iflags = 0; - uint16_t oflags = 0; - uint8_t pmtudisc = 0; - - VLOG_DBG("%s: attempting to create gre device using netlink", name); - - if (!gre_descriptors.nl_sock) { - error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, - &gre_descriptors.nl_sock); - if (error) { - VLOG_WARN("couldn't create netlink socket: %s", strerror(error)); - goto error; - } - } - - ofpbuf_init(&request, 0); - - nl_flags = NLM_F_REQUEST; - if (create) { - nl_flags |= NLM_F_CREATE|NLM_F_EXCL; - } - - /* We over-reserve space, because we do some pointer arithmetic - * and don't want the buffer address shifting under us. */ - nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 2048, RTM_NEWLINK, - nl_flags); - - memset(&ifinfomsg, 0, sizeof ifinfomsg); - ifinfomsg.ifi_family = AF_UNSPEC; - nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); - - linkinfo_hdr = ofpbuf_tail(&request); - nl_msg_put_unspec(&request, IFLA_LINKINFO, NULL, 0); - - nl_msg_put_unspec(&request, IFLA_INFO_KIND, "gretap", 6); - - info_data_hdr = ofpbuf_tail(&request); - nl_msg_put_unspec(&request, IFLA_INFO_DATA, NULL, 0); - - /* Set flags */ - if (config->have_in_key) { - iflags |= GRE_KEY; - } - if (config->have_out_key) { - oflags |= GRE_KEY; - } - - if (config->in_csum) { - iflags |= GRE_CSUM; - } - if (config->out_csum) { - oflags |= GRE_CSUM; - } - - /* Add options */ - nl_msg_put_u32(&request, IFLA_GRE_IKEY, config->in_key); - nl_msg_put_u32(&request, IFLA_GRE_OKEY, config->out_key); - nl_msg_put_u16(&request, IFLA_GRE_IFLAGS, iflags); - nl_msg_put_u16(&request, IFLA_GRE_OFLAGS, oflags); - nl_msg_put_u32(&request, IFLA_GRE_LOCAL, config->local_ip); - nl_msg_put_u32(&request, IFLA_GRE_REMOTE, config->remote_ip); - nl_msg_put_u8(&request, IFLA_GRE_PMTUDISC, pmtudisc); - nl_msg_put_u8(&request, IFLA_GRE_TTL, 0); - nl_msg_put_u8(&request, IFLA_GRE_TOS, 0); - - info_data_hdr->nla_len = (char *)ofpbuf_tail(&request) - - (char *)info_data_hdr; - linkinfo_hdr->nla_len = (char *)ofpbuf_tail(&request) - - (char *)linkinfo_hdr; - - nl_msg_put_string(&request, IFLA_IFNAME, name); - - error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); - ofpbuf_uninit(&request); - if (error) { - VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); - goto error; - } - ofpbuf_delete(reply); - -error: - return error; -#endif -} - -static int -setup_gre_ioctl(const char *name, struct gre_config *config, bool create) -{ - struct ip_tunnel_parm p; - struct ifreq ifr; - - VLOG_DBG("%s: attempting to create gre device using ioctl", name); - - memset(&p, 0, sizeof p); - - strncpy(p.name, name, IFNAMSIZ); - - p.iph.version = 4; - p.iph.ihl = 5; - p.iph.protocol = IPPROTO_GRE; - p.iph.saddr = config->local_ip; - p.iph.daddr = config->remote_ip; - - if (config->have_in_key) { - p.i_flags |= GRE_KEY; - p.i_key = config->in_key; - } - if (config->have_out_key) { - p.o_flags |= GRE_KEY; - p.o_key = config->out_key; - } - - if (config->in_csum) { - p.i_flags |= GRE_CSUM; - } - if (config->out_csum) { - p.o_flags |= GRE_CSUM; - } - - strncpy(ifr.ifr_name, create ? GRE_IOCTL_DEVICE : name, IFNAMSIZ); - ifr.ifr_ifru.ifru_data = (void *)&p; - - if (!gre_descriptors.ioctl_fd) { - gre_descriptors.ioctl_fd = socket(AF_INET, SOCK_DGRAM, 0); - if (gre_descriptors.ioctl_fd < 0) { - VLOG_WARN("couldn't create gre ioctl socket: %s", strerror(errno)); - gre_descriptors.ioctl_fd = 0; - return errno; - } - } - - if (ioctl(gre_descriptors.ioctl_fd, create ? SIOCADDGRETAP : SIOCCHGGRETAP, - &ifr) < 0) { - VLOG_WARN("couldn't do gre ioctl: %s", strerror(errno)); - return errno; - } - - return 0; -} - -/* The arguments are marked as unused to prevent warnings on platforms where - * the Netlink interface isn't supported. */ -static bool -check_gre_device_netlink(const char *name UNUSED) -{ -#ifdef GRE_IOCTL_ONLY - return false; -#else - static const struct nl_policy getlink_policy[] = { - [IFLA_LINKINFO] = { .type = NL_A_NESTED, .optional = false }, - }; - - static const struct nl_policy linkinfo_policy[] = { - [IFLA_INFO_KIND] = { .type = NL_A_STRING, .optional = false }, - }; - - int error; - bool ret = false; - struct ofpbuf request, *reply; - struct ifinfomsg ifinfomsg; - struct nlattr *getlink_attrs[ARRAY_SIZE(getlink_policy)]; - struct nlattr *linkinfo_attrs[ARRAY_SIZE(linkinfo_policy)]; - struct ofpbuf linkinfo; - const char *device_kind; - - ofpbuf_init(&request, 0); - - nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, - NLMSG_LENGTH(sizeof ifinfomsg), RTM_GETLINK, - NLM_F_REQUEST); - - memset(&ifinfomsg, 0, sizeof ifinfomsg); - ifinfomsg.ifi_family = AF_UNSPEC; - ifinfomsg.ifi_index = do_get_ifindex(name); - nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); - - error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); - ofpbuf_uninit(&request); - if (error) { - VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); - return false; - } - - if (!nl_policy_parse(reply, NLMSG_HDRLEN + sizeof(struct ifinfomsg), - getlink_policy, getlink_attrs, - ARRAY_SIZE(getlink_policy))) { - VLOG_WARN("received bad rtnl message (getlink policy)"); - goto error; - } - - linkinfo.data = (void *)nl_attr_get(getlink_attrs[IFLA_LINKINFO]); - linkinfo.size = nl_attr_get_size(getlink_attrs[IFLA_LINKINFO]); - if (!nl_policy_parse(&linkinfo, 0, linkinfo_policy, - linkinfo_attrs, ARRAY_SIZE(linkinfo_policy))) { - VLOG_WARN("received bad rtnl message (linkinfo policy)"); - goto error; - } - - device_kind = nl_attr_get_string(linkinfo_attrs[IFLA_INFO_KIND]); - ret = !strcmp(device_kind, "gretap"); - -error: - ofpbuf_delete(reply); - return ret; -#endif -} - -static bool -check_gre_device_ioctl(const char *name) -{ - struct ethtool_drvinfo drvinfo; - int error; - - memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(name, (struct ethtool_cmd *)&drvinfo, - ETHTOOL_GDRVINFO, "ETHTOOL_GDRVINFO"); - - return !error && !strcmp(drvinfo.driver, "ip_gre") - && !strcmp(drvinfo.bus_info, "gretap"); -} - -static int -setup_gre(const char *name, const struct shash *args, bool create) -{ - int error; - struct in_addr in_addr; - struct shash_node *node; - struct gre_config config; - - memset(&config, 0, sizeof config); - config.in_csum = true; - config.out_csum = true; - - SHASH_FOR_EACH (node, args) { - if (!strcmp(node->name, "remote_ip")) { - if (lookup_ip(node->data, &in_addr)) { - VLOG_WARN("bad 'remote_ip' for gre device %s ", name); - } else { - config.remote_ip = in_addr.s_addr; - } - } else if (!strcmp(node->name, "local_ip")) { - if (lookup_ip(node->data, &in_addr)) { - VLOG_WARN("bad 'local_ip' for gre device %s ", name); - } else { - config.local_ip = in_addr.s_addr; - } - } else if (!strcmp(node->name, "key")) { - config.have_in_key = true; - config.have_out_key = true; - config.in_key = htonl(atoi(node->data)); - config.out_key = htonl(atoi(node->data)); - } else if (!strcmp(node->name, "in_key")) { - config.have_in_key = true; - config.in_key = htonl(atoi(node->data)); - } else if (!strcmp(node->name, "out_key")) { - config.have_out_key = true; - config.out_key = htonl(atoi(node->data)); - } else if (!strcmp(node->name, "csum")) { - if (!strcmp(node->data, "false")) { - config.in_csum = false; - config.out_csum = false; - } - } else { - VLOG_WARN("unknown gre argument '%s'", node->name); - } - } - - if (!config.remote_ip) { - VLOG_WARN("gre type requires valid 'remote_ip' argument"); - error = EINVAL; - goto error; - } - - if (!gre_descriptors.use_ioctl) { - error = setup_gre_netlink(name, &config, create); - if (error == EOPNOTSUPP) { - gre_descriptors.use_ioctl = true; - } - } - if (gre_descriptors.use_ioctl) { - error = setup_gre_ioctl(name, &config, create); - } - - if (create && error == EEXIST) { - bool gre_device; - - if (gre_descriptors.use_ioctl) { - gre_device = check_gre_device_ioctl(name); - } else { - gre_device = check_gre_device_netlink(name); - } - - if (!gre_device) { - goto error; - } - - VLOG_WARN("replacing existing gre device %s", name); - error = destroy_gre(name); - if (error) { - goto error; - } - - if (gre_descriptors.use_ioctl) { - error = setup_gre_ioctl(name, &config, create); - } else { - error = setup_gre_netlink(name, &config, create); - } - } - -error: - return error; -} - /* Creates the netdev device of 'type' with 'name'. */ static int -netdev_linux_create_system(const char *name, const char *type UNUSED, +netdev_linux_create_system(const char *name, const char *type OVS_UNUSED, const struct shash *args, struct netdev_dev **netdev_devp) { struct netdev_dev_linux *netdev_dev; @@ -602,7 +273,7 @@ netdev_linux_create_system(const char *name, const char *type UNUSED, * buffers, across all readers. Therefore once data is read it will * be unavailable to other reads for tap devices. */ static int -netdev_linux_create_tap(const char *name, const char *type UNUSED, +netdev_linux_create_tap(const char *name, const char *type OVS_UNUSED, const struct shash *args, struct netdev_dev **netdev_devp) { struct netdev_dev_linux *netdev_dev; @@ -651,120 +322,6 @@ error: return error; } -static int -if_up(const char *name) -{ - struct ifreq ifr; - - strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name); - ifr.ifr_flags = IFF_UP; - - if (ioctl(af_inet_sock, SIOCSIFFLAGS, &ifr) == -1) { - VLOG_DBG_RL(&rl, "%s: failed to bring device up: %s", - name, strerror(errno)); - return errno; - } - - return 0; -} - -static int -netdev_linux_create_gre(const char *name, const char *type UNUSED, - const struct shash *args, struct netdev_dev **netdev_devp) -{ - struct netdev_dev_linux *netdev_dev; - int error; - - netdev_dev = xzalloc(sizeof *netdev_dev); - - error = setup_gre(name, args, true); - if (error) { - goto error; - } - - error = if_up(name); - if (error) { - goto error; - } - - netdev_dev_init(&netdev_dev->netdev_dev, name, &netdev_gre_class); - *netdev_devp = &netdev_dev->netdev_dev; - return 0; - -error: - free(netdev_dev); - return error; -} - -static int -netdev_linux_reconfigure_gre(struct netdev_dev *netdev_dev_, - const struct shash *args) -{ - const char *name = netdev_dev_get_name(netdev_dev_); - - return setup_gre(name, args, false); -} - -/* The arguments are marked as unused to prevent warnings on platforms where - * the Netlink interface isn't supported. */ -static int -destroy_gre_netlink(const char *name UNUSED) -{ -#ifdef GRE_IOCTL_ONLY - return EOPNOTSUPP; -#else - int error; - struct ofpbuf request, *reply; - struct ifinfomsg ifinfomsg; - int ifindex; - - ofpbuf_init(&request, 0); - - nl_msg_put_nlmsghdr(&request, gre_descriptors.nl_sock, 0, RTM_DELLINK, - NLM_F_REQUEST); - - memset(&ifinfomsg, 0, sizeof ifinfomsg); - ifinfomsg.ifi_family = AF_UNSPEC; - nl_msg_put(&request, &ifinfomsg, sizeof ifinfomsg); - - ifindex = do_get_ifindex(name); - nl_msg_put_u32(&request, IFLA_LINK, ifindex); - - nl_msg_put_string(&request, IFLA_IFNAME, name); - - error = nl_sock_transact(gre_descriptors.nl_sock, &request, &reply); - ofpbuf_uninit(&request); - if (error) { - VLOG_WARN("couldn't transact netlink socket: %s", strerror(error)); - goto error; - } - ofpbuf_delete(reply); - -error: - return 0; -#endif -} - -static int -destroy_gre_ioctl(const char *name) -{ - struct ip_tunnel_parm p; - struct ifreq ifr; - - memset(&p, 0, sizeof p); - strncpy(p.name, name, IFNAMSIZ); - - strncpy(ifr.ifr_name, name, IFNAMSIZ); - ifr.ifr_ifru.ifru_data = (void *)&p; - - if (ioctl(gre_descriptors.ioctl_fd, SIOCDELGRETAP, &ifr) < 0) { - VLOG_WARN("couldn't do gre ioctl: %s\n", strerror(errno)); - return errno; - } - - return 0; -} - static void destroy_tap(struct netdev_dev_linux *netdev_dev) { @@ -775,16 +332,6 @@ destroy_tap(struct netdev_dev_linux *netdev_dev) } } -static int -destroy_gre(const char *name) -{ - if (gre_descriptors.use_ioctl) { - return destroy_gre_ioctl(name); - } else { - return destroy_gre_netlink(name); - } -} - /* Destroys the netdev device 'netdev_dev_'. */ static void netdev_linux_destroy(struct netdev_dev *netdev_dev_) @@ -800,11 +347,9 @@ netdev_linux_destroy(struct netdev_dev *netdev_dev_) } } else if (!strcmp(type, "tap")) { destroy_tap(netdev_dev); - } else if (!strcmp(type, "gre")) { - destroy_gre(netdev_dev_get_name(&netdev_dev->netdev_dev)); } - free(netdev_dev_); + free(netdev_dev); } static int @@ -1205,6 +750,35 @@ check_for_working_netlink_stats(void) } } +/* Brings the 'is_internal' and 'is_tap' members of 'netdev_dev' up-to-date. */ +static void +netdev_linux_update_is_pseudo(struct netdev_dev_linux *netdev_dev) +{ + if (!(netdev_dev->cache_valid & VALID_IS_PSEUDO)) { + const char *name = netdev_dev_get_name(&netdev_dev->netdev_dev); + const char *type = netdev_dev_get_type(&netdev_dev->netdev_dev); + + netdev_dev->is_tap = !strcmp(type, "tap"); + netdev_dev->is_internal = false; + if (!netdev_dev->is_tap) { + struct ethtool_drvinfo drvinfo; + int error; + + memset(&drvinfo, 0, sizeof drvinfo); + error = netdev_linux_do_ethtool(name, + (struct ethtool_cmd *)&drvinfo, + ETHTOOL_GDRVINFO, + "ETHTOOL_GDRVINFO"); + + if (!error && !strcmp(drvinfo.driver, "openvswitch")) { + netdev_dev->is_internal = true; + } + } + + netdev_dev->cache_valid |= VALID_IS_PSEUDO; + } +} + /* Retrieves current device stats for 'netdev'. * * XXX All of the members of struct netdev_stats are 64 bits wide, but on @@ -1222,28 +796,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, COVERAGE_INC(netdev_get_stats); - if (!(netdev_dev->cache_valid & VALID_IS_INTERNAL)) { - netdev_dev->is_internal = !strcmp(netdev_get_type(netdev_), - "tap"); - - if (!netdev_dev->is_internal) { - struct ethtool_drvinfo drvinfo; - - memset(&drvinfo, 0, sizeof drvinfo); - error = netdev_linux_do_ethtool(netdev_get_name(netdev_), - (struct ethtool_cmd *)&drvinfo, - ETHTOOL_GDRVINFO, - "ETHTOOL_GDRVINFO"); - - if (!error) { - netdev_dev->is_internal = !strcmp(drvinfo.driver, - "openvswitch"); - } - } - - netdev_dev->cache_valid |= VALID_IS_INTERNAL; - } - + netdev_linux_update_is_pseudo(netdev_dev); if (netdev_dev->is_internal) { collect_stats = &raw_stats; } @@ -1266,7 +819,7 @@ netdev_linux_get_stats(const struct netdev *netdev_, * will appear to be swapped relative to the other ports since we are the * one sending the data, not a remote computer. For consistency, we swap * them back here. */ - if (netdev_dev->is_internal) { + if (!error && (netdev_dev->is_internal || netdev_dev->is_tap)) { stats->rx_packets = raw_stats.tx_packets; stats->tx_packets = raw_stats.rx_packets; stats->rx_bytes = raw_stats.tx_bytes; @@ -1293,6 +846,41 @@ netdev_linux_get_stats(const struct netdev *netdev_, return error; } +static int +netdev_linux_set_stats(struct netdev *netdev, + const struct netdev_stats *stats) +{ + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev)); + struct internal_dev_stats dp_dev_stats; + struct ifreq ifr; + + /* We must reject this call if 'netdev' is not an Open vSwitch internal + * port, because the ioctl that we are about to execute is in the "device + * private ioctls" range, which means that executing it on a device that + * is not the type we expect could do any random thing. + * + * (Amusingly, these ioctl numbers are commented "THESE IOCTLS ARE + * _DEPRECATED_ AND WILL DISAPPEAR IN 2.5.X" in linux/sockios.h. I guess + * DaveM is a little behind on that.) */ + netdev_linux_update_is_pseudo(netdev_dev); + if (!netdev_dev->is_internal) { + return EOPNOTSUPP; + } + + /* This actually only sets the *offset* that the dp_dev applies, but in our + * usage for fake bond devices the dp_dev never has any traffic of it own + * so it has the same effect. */ + dp_dev_stats.rx_packets = stats->rx_packets; + dp_dev_stats.rx_bytes = stats->rx_bytes; + dp_dev_stats.tx_packets = stats->tx_packets; + dp_dev_stats.tx_bytes = stats->tx_bytes; + ifr.ifr_data = (void *) &dp_dev_stats; + return netdev_linux_do_ioctl(netdev_get_name(netdev), &ifr, + INTERNAL_DEV_SET_STATS, + "INTERNAL_DEV_SET_STATS"); +} + /* Stores the features supported by 'netdev' into each of '*current', * '*advertised', '*supported', and '*peer' that are non-null. Each value is a * bitmap of "enum ofp_port_features" bits, in host byte order. Returns 0 if @@ -1530,35 +1118,88 @@ done: #define POLICE_ADD_CMD "/sbin/tc qdisc add dev %s handle ffff: ingress" #define POLICE_CONFIG_CMD "/sbin/tc filter add dev %s parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate %dkbit burst %dk mtu 65535 drop flowid :1" -/* We redirect stderr to /dev/null because we often want to remove all - * traffic control configuration on a port so its in a known state. If - * this done when there is no such configuration, tc complains, so we just - * always ignore it. + +/* Remove ingress policing from 'netdev'. Returns 0 if successful, otherwise a + * positive errno value. + * + * This function is equivalent to running + * /sbin/tc qdisc del dev %s handle ffff: ingress + * but it is much, much faster. */ -#define POLICE_DEL_CMD "/sbin/tc qdisc del dev %s handle ffff: ingress 2>/dev/null" +static int +netdev_linux_remove_policing(struct netdev *netdev) +{ + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev)); + const char *netdev_name = netdev_get_name(netdev); + + struct ofpbuf request; + struct ofpbuf *reply; + struct tcmsg *tcmsg; + struct nl_sock *rtnl_sock; + int ifindex; + int error; + + error = get_ifindex(netdev, &ifindex); + if (error) { + return error; + } + + error = get_rtnl_sock(&rtnl_sock); + if (error) { + return error; + } + + ofpbuf_init(&request, 0); + nl_msg_put_nlmsghdr(&request, rtnl_sock, sizeof *tcmsg, + RTM_DELQDISC, NLM_F_REQUEST); + tcmsg = ofpbuf_put_zeros(&request, sizeof *tcmsg); + tcmsg->tcm_family = AF_UNSPEC; + tcmsg->tcm_ifindex = ifindex; + tcmsg->tcm_handle = 0xffff0000; + tcmsg->tcm_parent = TC_H_INGRESS; + nl_msg_put_string(&request, TCA_KIND, "ingress"); + nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0); + error = nl_sock_transact(rtnl_sock, &request, &reply); + ofpbuf_uninit(&request); + ofpbuf_delete(reply); + if (error && error != ENOENT) { + VLOG_WARN_RL(&rl, "%s: removing policing failed: %s", + netdev_name, strerror(error)); + return error; + } + + netdev_dev->kbits_rate = 0; + netdev_dev->kbits_burst = 0; + netdev_dev->cache_valid |= VALID_POLICING; + return 0; +} /* Attempts to set input rate limiting (policing) policy. */ static int netdev_linux_set_policing(struct netdev *netdev, uint32_t kbits_rate, uint32_t kbits_burst) { + struct netdev_dev_linux *netdev_dev = + netdev_dev_linux_cast(netdev_get_dev(netdev)); const char *netdev_name = netdev_get_name(netdev); char command[1024]; COVERAGE_INC(netdev_set_policing); - if (kbits_rate) { - if (!kbits_burst) { - /* Default to 1000 kilobits if not specified. */ - kbits_burst = 1000; - } - /* xxx This should be more careful about only adding if it - * xxx actually exists, as opposed to always deleting it. */ - snprintf(command, sizeof(command), POLICE_DEL_CMD, netdev_name); - if (system(command) == -1) { - VLOG_WARN_RL(&rl, "%s: problem removing policing", netdev_name); - } + kbits_burst = (!kbits_rate ? 0 /* Force to 0 if no rate specified. */ + : !kbits_burst ? 1000 /* Default to 1000 kbits if 0. */ + : kbits_burst); /* Stick with user-specified value. */ + + if (netdev_dev->cache_valid & VALID_POLICING + && netdev_dev->kbits_rate == kbits_rate + && netdev_dev->kbits_burst == kbits_burst) { + /* Assume that settings haven't changed since we last set them. */ + return 0; + } + netdev_linux_remove_policing(netdev); + if (kbits_rate) { snprintf(command, sizeof(command), POLICE_ADD_CMD, netdev_name); if (system(command) != 0) { VLOG_WARN_RL(&rl, "%s: problem adding policing", netdev_name); @@ -1572,11 +1213,10 @@ netdev_linux_set_policing(struct netdev *netdev, netdev_name); return -1; } - } else { - snprintf(command, sizeof(command), POLICE_DEL_CMD, netdev_name); - if (system(command) == -1) { - VLOG_WARN_RL(&rl, "%s: problem removing policing", netdev_name); - } + + netdev_dev->kbits_rate = kbits_rate; + netdev_dev->kbits_burst = kbits_burst; + netdev_dev->cache_valid |= VALID_POLICING; } return 0; @@ -1709,7 +1349,7 @@ do_set_addr(struct netdev *netdev, /* Adds 'router' as a default IP gateway. */ static int -netdev_linux_add_router(struct netdev *netdev UNUSED, struct in_addr router) +netdev_linux_add_router(struct netdev *netdev OVS_UNUSED, struct in_addr router) { struct in_addr any = { INADDR_ANY }; struct rtentry rt; @@ -1876,7 +1516,7 @@ poll_notify(struct list *list) static void netdev_linux_poll_cb(const struct rtnetlink_change *change, - void *aux UNUSED) + void *aux OVS_UNUSED) { if (change) { struct list *list = shash_find_data(&netdev_linux_notifiers, @@ -1976,6 +1616,7 @@ const struct netdev_class netdev_linux_class = { netdev_linux_get_ifindex, netdev_linux_get_carrier, netdev_linux_get_stats, + netdev_linux_set_stats, netdev_linux_get_features, netdev_linux_set_advertisements, @@ -2024,6 +1665,7 @@ const struct netdev_class netdev_tap_class = { netdev_linux_get_ifindex, netdev_linux_get_carrier, netdev_linux_get_stats, + NULL, /* set_stats */ netdev_linux_get_features, netdev_linux_set_advertisements, @@ -2043,53 +1685,6 @@ const struct netdev_class netdev_tap_class = { netdev_linux_poll_remove, }; -const struct netdev_class netdev_gre_class = { - "gre", - - netdev_linux_init, - netdev_linux_run, - netdev_linux_wait, - - netdev_linux_create_gre, - netdev_linux_destroy, - netdev_linux_reconfigure_gre, - - netdev_linux_open, - netdev_linux_close, - - NULL, /* enumerate */ - - netdev_linux_recv, - netdev_linux_recv_wait, - netdev_linux_drain, - - netdev_linux_send, - netdev_linux_send_wait, - - netdev_linux_set_etheraddr, - netdev_linux_get_etheraddr, - netdev_linux_get_mtu, - netdev_linux_get_ifindex, - netdev_linux_get_carrier, - netdev_linux_get_stats, - - netdev_linux_get_features, - netdev_linux_set_advertisements, - netdev_linux_get_vlan_vid, - netdev_linux_set_policing, - - netdev_linux_get_in4, - netdev_linux_set_in4, - netdev_linux_get_in6, - netdev_linux_add_router, - netdev_linux_get_next_hop, - netdev_linux_arp_lookup, - - netdev_linux_update_flags, - - netdev_linux_poll_add, - netdev_linux_poll_remove, -}; static int get_stats_via_netlink(int ifindex, struct netdev_stats *stats) @@ -2104,8 +1699,7 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) .min_len = sizeof(struct rtnl_link_stats) }, }; - - static struct nl_sock *rtnl_sock; + struct nl_sock *rtnl_sock; struct ofpbuf request; struct ofpbuf *reply; struct ifinfomsg *ifi; @@ -2113,13 +1707,9 @@ get_stats_via_netlink(int ifindex, struct netdev_stats *stats) struct nlattr *attrs[ARRAY_SIZE(rtnlgrp_link_policy)]; int error; - if (!rtnl_sock) { - error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &rtnl_sock); - if (error) { - VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s", - strerror(error)); - return error; - } + error = get_rtnl_sock(&rtnl_sock); + if (error) { + return error; } ofpbuf_init(&request, 0); @@ -2383,3 +1973,26 @@ netdev_linux_get_ipv4(const struct netdev *netdev, struct in_addr *ip, } return error; } + +/* Obtains a Netlink routing socket that is not subscribed to any multicast + * groups. Returns 0 if successful, otherwise a positive errno value. Stores + * the socket in '*rtnl_sockp' if successful, otherwise a null pointer. */ +static int +get_rtnl_sock(struct nl_sock **rtnl_sockp) +{ + static struct nl_sock *sock; + int error; + + if (!sock) { + error = nl_sock_create(NETLINK_ROUTE, 0, 0, 0, &sock); + if (error) { + VLOG_ERR_RL(&rl, "failed to create rtnetlink socket: %s", + strerror(error)); + } + } else { + error = 0; + } + + *rtnl_sockp = sock; + return error; +}