+ if (!advertised) {
+ advertised = &dummy[1];
+ }
+ if (!supported) {
+ supported = &dummy[2];
+ }
+ if (!peer) {
+ peer = &dummy[3];
+ }
+
+ get_features = netdev->netdev_class->get_features;
+ error = get_features
+ ? get_features(netdev, current, advertised, supported,
+ peer)
+ : EOPNOTSUPP;
+ if (error) {
+ *current = *advertised = *supported = *peer = 0;
+ }
+ return error;
+}
+
+/* Returns the maximum speed of a network connection that has the NETDEV_F_*
+ * bits in 'features', in bits per second. If no bits that indicate a speed
+ * are set in 'features', returns 'default_bps'. */
+uint64_t
+netdev_features_to_bps(enum netdev_features features,
+ uint64_t default_bps)
+{
+ enum {
+ F_1000000MB = NETDEV_F_1TB_FD,
+ F_100000MB = NETDEV_F_100GB_FD,
+ F_40000MB = NETDEV_F_40GB_FD,
+ F_10000MB = NETDEV_F_10GB_FD,
+ F_1000MB = NETDEV_F_1GB_HD | NETDEV_F_1GB_FD,
+ F_100MB = NETDEV_F_100MB_HD | NETDEV_F_100MB_FD,
+ F_10MB = NETDEV_F_10MB_HD | NETDEV_F_10MB_FD
+ };
+
+ return ( features & F_1000000MB ? UINT64_C(1000000000000)
+ : features & F_100000MB ? UINT64_C(100000000000)
+ : features & F_40000MB ? UINT64_C(40000000000)
+ : features & F_10000MB ? UINT64_C(10000000000)
+ : features & F_1000MB ? UINT64_C(1000000000)
+ : features & F_100MB ? UINT64_C(100000000)
+ : features & F_10MB ? UINT64_C(10000000)
+ : default_bps);
+}
+
+/* Returns true if any of the NETDEV_F_* bits that indicate a full-duplex link
+ * are set in 'features', otherwise false. */
+bool
+netdev_features_is_full_duplex(enum netdev_features features)
+{
+ return (features & (NETDEV_F_10MB_FD | NETDEV_F_100MB_FD | NETDEV_F_1GB_FD
+ | NETDEV_F_10GB_FD | NETDEV_F_40GB_FD
+ | NETDEV_F_100GB_FD | NETDEV_F_1TB_FD)) != 0;
+}
+
+/* Set the features advertised by 'netdev' to 'advertise'. Returns 0 if
+ * successful, otherwise a positive errno value. */
+int
+netdev_set_advertisements(struct netdev *netdev,
+ enum netdev_features advertise)
+{
+ return (netdev->netdev_class->set_advertisements
+ ? netdev->netdev_class->set_advertisements(
+ netdev, advertise)
+ : EOPNOTSUPP);
+}
+
+/* If 'netdev' has an assigned IPv4 address, sets '*address' to that address
+ * and '*netmask' to its netmask and returns 0. Otherwise, returns a positive
+ * errno value and sets '*address' to 0 (INADDR_ANY).
+ *
+ * The following error values have well-defined meanings:
+ *
+ * - EADDRNOTAVAIL: 'netdev' has no assigned IPv4 address.
+ *
+ * - EOPNOTSUPP: No IPv4 network stack attached to 'netdev'.
+ *
+ * 'address' or 'netmask' or both may be null, in which case the address or
+ * netmask is not reported. */
+int
+netdev_get_in4(const struct netdev *netdev,
+ struct in_addr *address_, struct in_addr *netmask_)
+{
+ struct in_addr address;
+ struct in_addr netmask;
+ int error;
+
+ error = (netdev->netdev_class->get_in4
+ ? netdev->netdev_class->get_in4(netdev,
+ &address, &netmask)
+ : EOPNOTSUPP);
+ if (address_) {
+ address_->s_addr = error ? 0 : address.s_addr;
+ }
+ if (netmask_) {
+ netmask_->s_addr = error ? 0 : netmask.s_addr;
+ }
+ return error;