• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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