1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.networkstack.util; 18 19 import static android.net.apf.ApfConstants.IPV6_SOLICITED_NODES_PREFIX; 20 import static android.os.Build.VERSION.CODENAME; 21 import static android.os.Build.VERSION.SDK_INT; 22 import static android.system.OsConstants.IFA_F_DEPRECATED; 23 import static android.system.OsConstants.IFA_F_TENTATIVE; 24 25 import android.content.Context; 26 import android.net.IpPrefix; 27 import android.net.LinkAddress; 28 import android.net.LinkProperties; 29 import android.net.MacAddress; 30 import android.system.ErrnoException; 31 import android.util.Log; 32 33 import androidx.annotation.ChecksSdkIntAtLeast; 34 import androidx.annotation.NonNull; 35 import androidx.annotation.Nullable; 36 37 import com.android.net.module.util.DeviceConfigUtils; 38 import com.android.net.module.util.HexDump; 39 40 import java.io.FileDescriptor; 41 import java.io.IOException; 42 import java.net.Inet4Address; 43 import java.net.Inet6Address; 44 import java.net.InetAddress; 45 import java.net.UnknownHostException; 46 47 /** 48 * Collection of utilities for the network stack. 49 */ 50 public class NetworkStackUtils { 51 private static final String TAG = "NetworkStackUtils"; 52 53 /** 54 * A list of captive portal detection specifications used in addition to the fallback URLs. 55 * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated 56 * by "@@,@@". 57 */ 58 public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = 59 "captive_portal_fallback_probe_specs"; 60 61 /** 62 * A comma separated list of URLs used for captive portal detection in addition to the 63 * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings. 64 */ 65 public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = 66 "captive_portal_other_fallback_urls"; 67 68 /** 69 * A comma separated list of URLs used for captive portal detection in addition to the HTTP url 70 * associated with the CAPTIVE_PORTAL_HTTP_URL settings. 71 */ 72 public static final String CAPTIVE_PORTAL_OTHER_HTTP_URLS = "captive_portal_other_http_urls"; 73 74 /** 75 * A comma separated list of URLs used for network validation in addition to the HTTPS url 76 * associated with the CAPTIVE_PORTAL_HTTPS_URL settings. 77 */ 78 public static final String CAPTIVE_PORTAL_OTHER_HTTPS_URLS = "captive_portal_other_https_urls"; 79 80 /** 81 * Which User-Agent string to use in the header of the captive portal detection probes. 82 * The User-Agent field is unset when this setting has no value (HttpUrlConnection default). 83 */ 84 public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; 85 86 /** 87 * Whether to use HTTPS for network validation. This is enabled by default and the setting 88 * needs to be set to 0 to disable it. This setting is a misnomer because captive portals 89 * don't actually use HTTPS, but it's consistent with the other settings. 90 */ 91 public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; 92 93 /** 94 * The URL used for HTTPS captive portal detection upon a new connection. 95 * A 204 response code from the server is used for validation. 96 */ 97 public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; 98 99 /** 100 * The URL used for HTTP captive portal detection upon a new connection. 101 * A 204 response code from the server is used for validation. 102 */ 103 public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; 104 105 /** 106 * The URL used for fallback HTTP captive portal detection when previous HTTP 107 * and HTTPS captive portal detection attemps did not return a conclusive answer. 108 */ 109 public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; 110 111 /** 112 * What to do when connecting a network that presents a captive portal. 113 * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. 114 * 115 * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. 116 */ 117 public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; 118 119 /** 120 * Don't attempt to detect captive portals. 121 */ 122 public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; 123 124 /** 125 * When detecting a captive portal, display a notification that 126 * prompts the user to sign in. 127 */ 128 public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; 129 130 /** 131 * When detecting a captive portal, immediately disconnect from the 132 * network and do not reconnect to that network in the future. 133 */ 134 public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; 135 136 /** 137 * DNS probe timeout for network validation. Enough for 3 DNS queries 5 seconds apart. 138 */ 139 public static final int DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT = 12500; 140 141 /** 142 * List of fallback probe specs to use for detecting captive portals. This is an alternative to 143 * fallback URLs that provides more flexibility on detection rules. Empty, so unused by default. 144 */ 145 public static final String[] DEFAULT_CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = 146 new String[] {}; 147 148 /** 149 * The default list of HTTP URLs to use for detecting captive portals. 150 */ 151 public static final String[] DEFAULT_CAPTIVE_PORTAL_HTTP_URLS = 152 new String [] {"http://connectivitycheck.gstatic.com/generate_204"}; 153 154 /** 155 * The default list of HTTPS URLs for network validation, to use for confirming internet 156 * connectivity. 157 */ 158 public static final String[] DEFAULT_CAPTIVE_PORTAL_HTTPS_URLS = 159 new String [] {"https://www.google.com/generate_204"}; 160 161 /** 162 * Minimum module version at which to enable the DHCP Rapid Commit option. 163 */ 164 public static final String DHCP_RAPID_COMMIT_VERSION = "dhcp_rapid_commit_version"; 165 166 /** 167 * Minimum module version at which to enable the IP address conflict detection feature. 168 */ 169 public static final String DHCP_IP_CONFLICT_DETECT_VERSION = "dhcp_ip_conflict_detect_version"; 170 171 /** 172 * Minimum module version at which to enable slow DHCP retransmission approach in renew/rebind 173 * state suggested in RFC2131 section 4.4.5. 174 */ 175 public static final String DHCP_SLOW_RETRANSMISSION_VERSION = 176 "dhcp_slow_retransmission_version"; 177 178 /** 179 * Experiment flag to enable considering DNS probes returning private IP addresses as failed 180 * when attempting to detect captive portals. 181 * 182 * This flag is enabled if !=0 and less than the module APK version. 183 */ 184 public static final String DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION = 185 "dns_probe_private_ip_no_internet"; 186 187 /** 188 * Experiment flag to enable validation metrics sent by NetworkMonitor. 189 * 190 * Metrics are sent by default. They can be disabled by setting the flag to a number greater 191 * than the APK version (for example 999999999). 192 * @see DeviceConfigUtils#isFeatureEnabled(Context, String, String, boolean) 193 */ 194 public static final String VALIDATION_METRICS_VERSION = "validation_metrics_version"; 195 196 /** 197 * Experiment flag to enable "mcast_resolicit" neighbor parameter in IpReachabilityMonitor, 198 * set it to 3 by default. 199 */ 200 public static final String IP_REACHABILITY_MCAST_RESOLICIT_VERSION = 201 "ip_reachability_mcast_resolicit_version"; 202 203 /** 204 * Experiment flag to treat router MAC address changes as a failure only on roam. 205 */ 206 public static final String IP_REACHABILITY_ROUTER_MAC_CHANGE_FAILURE_ONLY_AFTER_ROAM_VERSION = 207 "ip_reachability_router_mac_change_failure_only_after_roam_version"; 208 209 /** 210 * Experiment flag to ignore all NUD failures from kernel organic. 211 */ 212 public static final String IP_REACHABILITY_IGNORE_ORGANIC_NUD_FAILURE_VERSION = 213 "ip_reachability_ignore_organic_nud_failure_version"; 214 215 /** 216 * Experiment flag to enable the feature of polling counters in Apf. 217 */ 218 public static final String APF_POLLING_COUNTERS_VERSION = "apf_polling_counters_version"; 219 220 /** 221 * Experiment flag to enable the feature of ignoring any individual RA section with lifetime 222 * below accept_ra_min_lft sysctl. 223 */ 224 public static final String IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION = 225 "ipclient_ignore_low_ra_lifetime_version"; 226 227 /** 228 * Feature flag to send private DNS resolution queries and probes on a background thread. 229 */ 230 public static final String NETWORKMONITOR_ASYNC_PRIVDNS_RESOLUTION = 231 "networkmonitor_async_privdns_resolution"; 232 233 /** 234 * Experiment flag to populate the IP link address lifetime such as deprecationTime and 235 * expirationtTime. 236 */ 237 public static final String IPCLIENT_POPULATE_LINK_ADDRESS_LIFETIME_VERSION = 238 "ipclient_populate_link_address_lifetime_version"; 239 240 /** 241 * Experiment flag to support parsing PIO P flag(DHCPv6-PD preferred). 242 */ 243 public static final String IPCLIENT_DHCPV6_PD_PREFERRED_FLAG_VERSION = 244 "ipclient_dhcpv6_pd_preferred_flag_version"; 245 246 /** 247 * Experiment flag to replace INetd usage with netlink in IpClient. 248 */ 249 public static final String IPCLIENT_REPLACE_NETD_WITH_NETLINK_VERSION = 250 "ipclient_replace_netd_with_netlink_version"; 251 252 /** 253 * Experiment flag to enable Discovery of Designated Resolvers (DDR). 254 * This flag requires networkmonitor_async_privdns_resolution flag. 255 */ 256 public static final String DNS_DDR_VERSION = "dns_ddr_version"; 257 258 /** 259 * Experiment flag to ignore all NUD failures if we've seen too many NUD failure in a network. 260 */ 261 public static final String IP_REACHABILITY_IGNORE_NUD_FAILURE_VERSION = 262 "ip_reachability_ignore_nud_failure_version"; 263 264 /** 265 * Experiment flag to enable the feature of handle IPv4 ping offload in Apf. 266 */ 267 public static final String APF_HANDLE_PING4_OFFLOAD_VERSION = 268 "apf_handle_ping_offload_version"; 269 270 /** 271 * Experiment flag to enable the feature of handle IPv6 ping offload in Apf. 272 */ 273 public static final String APF_HANDLE_PING6_OFFLOAD_VERSION = 274 "apf_handle_ping6_offload_version"; 275 276 /** 277 * Experiment flag to enable the feature of handle IGMP offload in Apf. 278 */ 279 public static final String APF_HANDLE_IGMP_OFFLOAD_VERSION = 280 "apf_handle_igmp_offload_version"; 281 282 /** 283 * Experiment flag to enable the feature of handle MLD offload in Apf. 284 */ 285 public static final String APF_HANDLE_MLD_OFFLOAD_VERSION = 286 "apf_handle_mld_offload_version"; 287 288 /**** BEGIN Feature Kill Switch Flags ****/ 289 290 /** 291 * Kill switch flag to disable the feature of skipping Tcp socket info polling when light 292 * doze mode is enabled. 293 */ 294 public static final String SKIP_TCP_POLL_IN_LIGHT_DOZE = "skip_tcp_poll_in_light_doze_mode"; 295 296 /** 297 * Experiment flag to enable the feature of re-evaluate when network resumes. 298 */ 299 public static final String REEVALUATE_WHEN_RESUME = "reevaluate_when_resume"; 300 301 /** 302 * Kill switch flag to disable the feature of ignoring Tcp socket info for uids which 303 * networking are blocked. 304 */ 305 public static final String IGNORE_TCP_INFO_FOR_BLOCKED_UIDS = 306 "ignore_tcp_info_for_blocked_uids"; 307 308 /** Kill switch to force disable APF */ 309 public static final String APF_ENABLE = "apf_enable"; 310 311 /** 312 * Kill switch flag to disable the feature of handle arp offload in Apf. 313 * Warning: the following flag String is incorrect. The feature that is not chickened out is 314 * "ARP offload" not "ARP offload force disabled". 315 */ 316 public static final String APF_HANDLE_ARP_OFFLOAD = "apf_handle_arp_offload_force_disable"; 317 318 /** 319 * Kill switch flag to disable the feature of handle nd offload in Apf. 320 */ 321 public static final String APF_HANDLE_ND_OFFLOAD = "apf_handle_nd_offload"; 322 323 /** 324 * Kill switch flag to disable the feature of handle IGMP offload in Apf. 325 */ 326 public static final String APF_HANDLE_IGMP_OFFLOAD = "apf_handle_igmp_offload"; 327 328 /** 329 * Kill switch flag to disable the feature of handle MLD offload in Apf. 330 */ 331 public static final String APF_HANDLE_MLD_OFFLOAD = "apf_handle_mld_offload"; 332 333 /** 334 * Kill switch flag to disable the feature of handle IPv4 ping offload in Apf. 335 */ 336 public static final String APF_HANDLE_PING4_OFFLOAD = "apf_handle_ping4_offload"; 337 338 /** 339 * Kill switch flag to disable the feature of handle IPv6 ping offload in Apf. 340 */ 341 public static final String APF_HANDLE_PING6_OFFLOAD = "apf_handle_ping6_offload"; 342 static { 343 System.loadLibrary("networkstackutilsjni"); 344 } 345 346 /** 347 * Convert IPv4 multicast address to ethernet multicast address in network order. 348 */ ipv4MulticastToEthernetMulticast(@onNull final Inet4Address addr)349 public static MacAddress ipv4MulticastToEthernetMulticast(@NonNull final Inet4Address addr) { 350 final byte[] etherMulticast = new byte[6]; 351 final byte[] ipv4Multicast = addr.getAddress(); 352 etherMulticast[0] = (byte) 0x01; 353 etherMulticast[1] = (byte) 0x00; 354 etherMulticast[2] = (byte) 0x5e; 355 etherMulticast[3] = (byte) (ipv4Multicast[1] & 0x7f); 356 etherMulticast[4] = ipv4Multicast[2]; 357 etherMulticast[5] = ipv4Multicast[3]; 358 return MacAddress.fromBytes(etherMulticast); 359 } 360 361 /** 362 * Convert IPv6 multicast address to ethernet multicast address in network order. 363 */ ipv6MulticastToEthernetMulticast(@onNull final Inet6Address addr)364 public static MacAddress ipv6MulticastToEthernetMulticast(@NonNull final Inet6Address addr) { 365 final byte[] etherMulticast = new byte[6]; 366 final byte[] ipv6Multicast = addr.getAddress(); 367 etherMulticast[0] = (byte) 0x33; 368 etherMulticast[1] = (byte) 0x33; 369 etherMulticast[2] = ipv6Multicast[12]; 370 etherMulticast[3] = ipv6Multicast[13]; 371 etherMulticast[4] = ipv6Multicast[14]; 372 etherMulticast[5] = ipv6Multicast[15]; 373 return MacAddress.fromBytes(etherMulticast); 374 } 375 376 /** 377 * Convert IPv6 unicast or anycast address to solicited node multicast address 378 * per RFC4291 section 2.7.1. 379 */ 380 @Nullable ipv6AddressToSolicitedNodeMulticast( @onNull final Inet6Address addr)381 public static Inet6Address ipv6AddressToSolicitedNodeMulticast( 382 @NonNull final Inet6Address addr) { 383 final byte[] address = new byte[16]; 384 address[0] = (byte) 0xFF; 385 address[1] = (byte) 0x02; 386 address[11] = (byte) 0x01; 387 address[12] = (byte) 0xFF; 388 address[13] = addr.getAddress()[13]; 389 address[14] = addr.getAddress()[14]; 390 address[15] = addr.getAddress()[15]; 391 try { 392 return (Inet6Address) InetAddress.getByAddress(address); 393 } catch (UnknownHostException e) { 394 Log.e(TAG, "Invalid host IP address " + addr.getHostAddress(), e); 395 return null; 396 } 397 } 398 399 /** 400 * Checks if the given IPv6 address is a solicited-node multicast address. 401 * 402 * <p>Solicited-node multicast addresses are used for Neighbor Discovery in IPv6. 403 * They have a specific prefix (FF02::1:FFxx:xxxx) where the last 64 bits are derived 404 * from the interface's link-layer address. This function only checks if the address 405 * has the correct prefix; it does *not* verify the lower 64 bits. 406 */ isIPv6AddressSolicitedNodeMulticast(@onNull final Inet6Address addr)407 public static boolean isIPv6AddressSolicitedNodeMulticast(@NonNull final Inet6Address addr) { 408 for (int i = 0; i < IPV6_SOLICITED_NODES_PREFIX.length; i++) { 409 if (addr.getAddress()[i] != IPV6_SOLICITED_NODES_PREFIX[i]) { 410 return false; 411 } 412 } 413 414 return true; 415 } 416 417 /** 418 * Check whether a link address is IPv6 global preferred unicast address. 419 */ isIPv6GUA(@onNull final LinkAddress address)420 public static boolean isIPv6GUA(@NonNull final LinkAddress address) { 421 return address.isIpv6() && address.isGlobalPreferred(); 422 } 423 424 /** 425 * Convert 48bits MAC address to 64bits link-layer address(EUI64). 426 * 1. insert the 0xFFFE in the middle of mac address 427 * 2. flip the 7th bit(universal/local) of the first byte. 428 */ macAddressToEui64(@onNull final MacAddress hwAddr)429 public static byte[] macAddressToEui64(@NonNull final MacAddress hwAddr) { 430 final byte[] eui64 = new byte[8]; 431 final byte[] mac48 = hwAddr.toByteArray(); 432 System.arraycopy(mac48 /* src */, 0 /* srcPos */, eui64 /* dest */, 0 /* destPos */, 433 3 /* length */); 434 eui64[3] = (byte) 0xFF; 435 eui64[4] = (byte) 0xFE; 436 System.arraycopy(mac48 /* src */, 3 /* srcPos */, eui64 /* dest */, 5 /* destPos */, 437 3 /* length */); 438 eui64[0] = (byte) (eui64[0] ^ 0x02); // flip 7th bit 439 return eui64; 440 } 441 442 /** 443 * Generate an IPv6 address based on the given prefix(/64) and stable interface 444 * identifier(EUI64). 445 */ 446 @Nullable createInet6AddressFromEui64(@onNull final IpPrefix prefix, @NonNull final byte[] eui64)447 public static Inet6Address createInet6AddressFromEui64(@NonNull final IpPrefix prefix, 448 @NonNull final byte[] eui64) { 449 if (prefix.getPrefixLength() > 64) { 450 Log.e(TAG, "Invalid IPv6 prefix length " + prefix.getPrefixLength()); 451 return null; 452 } 453 final byte[] address = new byte[16]; 454 System.arraycopy(prefix.getRawAddress() /* src */, 0 /* srcPos */, address /* dest */, 455 0 /* destPos*/, 8 /* length */); 456 System.arraycopy(eui64 /* src */, 0 /* srcPos */, address /* dest */, 8 /* destPos */, 457 eui64.length); 458 try { 459 return (Inet6Address) InetAddress.getByAddress(address); 460 } catch (UnknownHostException e) { 461 Log.e(TAG, "Invalid IPv6 address " + HexDump.toHexString(address), e); 462 return null; 463 } 464 } 465 466 /** Checks if the device is running on a release version of Android Baklava or newer */ 467 @ChecksSdkIntAtLeast(api = 36 /* BUILD_VERSION_CODES.Baklava */) isAtLeast25Q2()468 public static boolean isAtLeast25Q2() { 469 return SDK_INT >= 36 || (SDK_INT == 35 && isAtLeastPreReleaseCodename("Baklava")); 470 } 471 isAtLeastPreReleaseCodename(@onNull String codename)472 private static boolean isAtLeastPreReleaseCodename(@NonNull String codename) { 473 // Special case "REL", which means the build is not a pre-release build. 474 if ("REL".equals(CODENAME)) { 475 return false; 476 } 477 478 // Otherwise lexically compare them. Return true if the build codename is equal to or 479 // greater than the requested codename. 480 return CODENAME.compareTo(codename) >= 0; 481 } 482 483 /** 484 * Select the preferred IPv6 link-local address based on the rules defined in rfc3484, 485 * Section 5. 486 * <p> 487 * The address selection criteria are as follows: 488 * 1. Select a non-tentative, non-deprecated address, if available. 489 * 2. If no such address exists, select any non-tentative address. 490 */ selectPreferredIPv6LinkLocalAddress(@onNull LinkProperties lp)491 public static Inet6Address selectPreferredIPv6LinkLocalAddress(@NonNull LinkProperties lp) { 492 Inet6Address preferredAddress = null; 493 for (LinkAddress linkAddress : lp.getLinkAddresses()) { 494 final InetAddress inetAddress = linkAddress.getAddress(); 495 final int flags = linkAddress.getFlags(); 496 497 if (!(inetAddress instanceof Inet6Address)) { 498 continue; 499 } 500 501 if (!inetAddress.isLinkLocalAddress()) { 502 continue; 503 } 504 505 if ((flags & IFA_F_TENTATIVE) != 0) { 506 continue; 507 } 508 509 preferredAddress = (Inet6Address) inetAddress; 510 if ((flags & IFA_F_DEPRECATED) == 0L) { 511 return preferredAddress; 512 } 513 } 514 515 return preferredAddress; 516 } 517 518 /** 519 * Attaches a socket filter that accepts DHCP packets to the given socket. 520 */ attachDhcpFilter(FileDescriptor fd)521 public static native void attachDhcpFilter(FileDescriptor fd) throws ErrnoException; 522 523 /** 524 * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket. 525 * @param fd the socket's {@link FileDescriptor}. 526 */ attachRaFilter(FileDescriptor fd)527 public static native void attachRaFilter(FileDescriptor fd) throws ErrnoException; 528 529 /** 530 * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity. 531 * 532 * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges. 533 * 534 * @param fd the socket's {@link FileDescriptor}. 535 */ attachControlPacketFilter(FileDescriptor fd)536 public static native void attachControlPacketFilter(FileDescriptor fd) throws ErrnoException; 537 538 /** 539 * Add an entry into the ARP cache. 540 */ addArpEntry(Inet4Address ipv4Addr, android.net.MacAddress ethAddr, String ifname, FileDescriptor fd)541 public static void addArpEntry(Inet4Address ipv4Addr, android.net.MacAddress ethAddr, 542 String ifname, FileDescriptor fd) throws IOException { 543 addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd); 544 } 545 546 /** 547 * Attaches a socket filter that accepts egress IGMPv2/IGMPv3 reports to the given socket. 548 * 549 * This filter doesn't include IGMPv1 report since device will not send out IGMPv1 report 550 * when the device leaves a multicast address group. 551 * 552 * @param fd the socket's {@link FileDescriptor}. 553 */ attachEgressIgmpReportFilter(FileDescriptor fd)554 public static native void attachEgressIgmpReportFilter(FileDescriptor fd) throws ErrnoException; 555 556 /** 557 * Attaches a socket filter that accepts egress IGMPv2/v3, MLDv1/v2 reports to the given socket. 558 * 559 * This filter doesn't include IGMPv1 report since device will not send out IGMPv1 report 560 * when the device leaves a multicast address group. 561 * 562 * @param fd the socket's {@link FileDescriptor}. 563 */ attachEgressMulticastReportFilter( FileDescriptor fd)564 public static native void attachEgressMulticastReportFilter( 565 FileDescriptor fd) throws ErrnoException; 566 addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname, FileDescriptor fd)567 private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname, 568 FileDescriptor fd) throws IOException; 569 570 } 571