View Javadoc
1   /*
2    * Copyright 2020-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.common;
6   
7   import static oshi.util.Memoizer.memoize;
8   
9   import java.net.InetAddress;
10  import java.net.InterfaceAddress;
11  import java.net.NetworkInterface;
12  import java.net.SocketException;
13  import java.util.ArrayList;
14  import java.util.Arrays;
15  import java.util.Collections;
16  import java.util.Enumeration;
17  import java.util.List;
18  import java.util.Locale;
19  import java.util.Properties;
20  import java.util.function.Supplier;
21  import java.util.stream.Collectors;
22  
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  import oshi.annotation.concurrent.ThreadSafe;
27  import oshi.hardware.NetworkIF;
28  import oshi.util.Constants;
29  import oshi.util.FileUtil;
30  import oshi.util.FormatUtil;
31  import oshi.util.ParseUtil;
32  
33  /**
34   * Network interfaces implementation.
35   */
36  @ThreadSafe
37  public abstract class AbstractNetworkIF implements NetworkIF {
38  
39      private static final Logger LOG = LoggerFactory.getLogger(AbstractNetworkIF.class);
40  
41      private static final String OSHI_VM_MAC_ADDR_PROPERTIES = "oshi.vmmacaddr.properties";
42  
43      private NetworkInterface networkInterface;
44      private String name;
45      private String displayName;
46      private int index;
47      private long mtu;
48      private String mac;
49      private String[] ipv4;
50      private Short[] subnetMasks;
51      private String[] ipv6;
52      private Short[] prefixLengths;
53  
54      private final Supplier<Properties> vmMacAddrProps = memoize(AbstractNetworkIF::queryVmMacAddrProps);
55  
56      /**
57       * Construct a {@link NetworkIF} object backed by the specified {@link NetworkInterface}.
58       *
59       * @param netint The core java {@link NetworkInterface} backing this object.
60       * @throws InstantiationException If a socket exception prevents access to the backing interface.
61       */
62      protected AbstractNetworkIF(NetworkInterface netint) throws InstantiationException {
63          this(netint, netint.getDisplayName());
64      }
65  
66      /**
67       * Construct a {@link NetworkIF} object backed by the specified {@link NetworkInterface}.
68       *
69       * @param netint      The core java {@link NetworkInterface} backing this object.
70       * @param displayName A string to use for the display name in preference to the {@link NetworkInterface} value.
71       * @throws InstantiationException If a socket exception prevents access to the backing interface.
72       */
73      protected AbstractNetworkIF(NetworkInterface netint, String displayName) throws InstantiationException {
74          this.networkInterface = netint;
75          try {
76              this.name = networkInterface.getName();
77              this.displayName = displayName;
78              this.index = networkInterface.getIndex();
79              // Set MTU
80              this.mtu = ParseUtil.unsignedIntToLong(networkInterface.getMTU());
81              // Set MAC
82              byte[] hwmac = networkInterface.getHardwareAddress();
83              if (hwmac != null) {
84                  List<String> octets = new ArrayList<>(6);
85                  for (byte b : hwmac) {
86                      octets.add(String.format(Locale.ROOT, "%02x", b));
87                  }
88                  this.mac = String.join(":", octets);
89              } else {
90                  this.mac = Constants.UNKNOWN;
91              }
92              // Set IP arrays
93              ArrayList<String> ipv4list = new ArrayList<>();
94              ArrayList<Short> subnetMaskList = new ArrayList<>();
95              ArrayList<String> ipv6list = new ArrayList<>();
96              ArrayList<Short> prefixLengthList = new ArrayList<>();
97  
98              for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) {
99                  InetAddress address = interfaceAddress.getAddress();
100                 if (address.getHostAddress().length() > 0) {
101                     if (address.getHostAddress().contains(":")) {
102                         ipv6list.add(address.getHostAddress().split("%")[0]);
103                         prefixLengthList.add(interfaceAddress.getNetworkPrefixLength());
104                     } else {
105                         ipv4list.add(address.getHostAddress());
106                         subnetMaskList.add(interfaceAddress.getNetworkPrefixLength());
107                     }
108                 }
109             }
110 
111             this.ipv4 = ipv4list.toArray(new String[0]);
112             this.subnetMasks = subnetMaskList.toArray(new Short[0]);
113             this.ipv6 = ipv6list.toArray(new String[0]);
114             this.prefixLengths = prefixLengthList.toArray(new Short[0]);
115         } catch (SocketException e) {
116             throw new InstantiationException(e.getMessage());
117         }
118     }
119 
120     /**
121      * Returns network interfaces on this machine.
122      *
123      * @param includeLocalInterfaces include local interfaces in the result
124      * @return A list of network interfaces
125      */
126     protected static List<NetworkInterface> getNetworkInterfaces(boolean includeLocalInterfaces) {
127         List<NetworkInterface> interfaces = getAllNetworkInterfaces();
128 
129         return includeLocalInterfaces ? interfaces
130                 : getAllNetworkInterfaces().stream().parallel().filter(ni -> !isLocalInterface(ni))
131                         .collect(Collectors.toList());
132     }
133 
134     /**
135      * Returns all network interfaces.
136      *
137      * @return A list of network interfaces
138      */
139     private static List<NetworkInterface> getAllNetworkInterfaces() {
140         try {
141             Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
142             return interfaces == null ? Collections.emptyList() : Collections.list(interfaces);
143         } catch (SocketException ex) {
144             LOG.error("Socket exception when retrieving interfaces: {}", ex.getMessage());
145         }
146         return Collections.emptyList();
147     }
148 
149     private static boolean isLocalInterface(NetworkInterface networkInterface) {
150         try {
151             // getHardwareAddress also checks for loopback
152             return networkInterface.getHardwareAddress() == null;
153         } catch (SocketException e) {
154             LOG.error("Socket exception when retrieving interface information for {}: {}", networkInterface,
155                     e.getMessage());
156         }
157         return false;
158     }
159 
160     @Override
161     public NetworkInterface queryNetworkInterface() {
162         return this.networkInterface;
163     }
164 
165     @Override
166     public String getName() {
167         return this.name;
168     }
169 
170     @Override
171     public int getIndex() {
172         return this.index;
173     }
174 
175     @Override
176     public String getDisplayName() {
177         return this.displayName;
178     }
179 
180     @Override
181     public long getMTU() {
182         return this.mtu;
183     }
184 
185     @Override
186     public String getMacaddr() {
187         return this.mac;
188     }
189 
190     @Override
191     public String[] getIPv4addr() {
192         return Arrays.copyOf(this.ipv4, this.ipv4.length);
193     }
194 
195     @Override
196     public Short[] getSubnetMasks() {
197         return Arrays.copyOf(this.subnetMasks, this.subnetMasks.length);
198     }
199 
200     @Override
201     public String[] getIPv6addr() {
202         return Arrays.copyOf(this.ipv6, this.ipv6.length);
203     }
204 
205     @Override
206     public Short[] getPrefixLengths() {
207         return Arrays.copyOf(this.prefixLengths, this.prefixLengths.length);
208     }
209 
210     @Override
211     public boolean isKnownVmMacAddr() {
212         String oui = getMacaddr().length() > 7 ? getMacaddr().substring(0, 8) : getMacaddr();
213         return this.vmMacAddrProps.get().containsKey(oui.toUpperCase(Locale.ROOT));
214     }
215 
216     private static Properties queryVmMacAddrProps() {
217         return FileUtil.readPropertiesFromFilename(OSHI_VM_MAC_ADDR_PROPERTIES);
218     }
219 
220     @Override
221     public String toString() {
222         StringBuilder sb = new StringBuilder();
223         sb.append("Name: ").append(getName());
224         if (!getName().equals(getDisplayName())) {
225             sb.append(" (").append(getDisplayName()).append(")");
226         }
227         if (!getIfAlias().isEmpty()) {
228             sb.append(" [IfAlias=").append(getIfAlias()).append("]");
229         }
230         sb.append("\n");
231         sb.append("  MAC Address: ").append(getMacaddr()).append("\n");
232         sb.append("  MTU: ").append(getMTU()).append(", ").append("Speed: ").append(getSpeed()).append("\n");
233         String[] ipv4withmask = getIPv4addr();
234         if (this.ipv4.length == this.subnetMasks.length) {
235             for (int i = 0; i < this.subnetMasks.length; i++) {
236                 ipv4withmask[i] += "/" + this.subnetMasks[i];
237             }
238         }
239         sb.append("  IPv4: ").append(Arrays.toString(ipv4withmask)).append("\n");
240         String[] ipv6withprefixlength = getIPv6addr();
241         if (this.ipv6.length == this.prefixLengths.length) {
242             for (int j = 0; j < this.prefixLengths.length; j++) {
243                 ipv6withprefixlength[j] += "/" + this.prefixLengths[j];
244             }
245         }
246         sb.append("  IPv6: ").append(Arrays.toString(ipv6withprefixlength)).append("\n");
247         sb.append("  Traffic: received ").append(getPacketsRecv()).append(" packets/")
248                 .append(FormatUtil.formatBytes(getBytesRecv())).append(" (" + getInErrors() + " err, ")
249                 .append(getInDrops() + " drop);");
250         sb.append(" transmitted ").append(getPacketsSent()).append(" packets/")
251                 .append(FormatUtil.formatBytes(getBytesSent())).append(" (" + getOutErrors() + " err, ")
252                 .append(getCollisions() + " coll);");
253         return sb.toString();
254     }
255 }