Merge changes I71545bee,Ic26cddab,I57b36e66,I71367761,I07e7aa8c into main
* changes: Use NetlinkUtil to process dump messages in InetDiagMessage. Add mif6ctl struct definition. Add mf6cctl struct definition. Add Mrt6Msg struct for multicast routing support. Add support to dump all multicast routes in NetlinkUtils
This commit is contained in:
commit
907c6e377b
9 changed files with 569 additions and 99 deletions
|
|
@ -63,6 +63,8 @@ import java.nio.ByteOrder;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
|
|
@ -320,9 +322,8 @@ public class InetDiagMessage extends NetlinkMessage {
|
|||
NetlinkUtils.receiveNetlinkAck(fd);
|
||||
}
|
||||
|
||||
private static void sendNetlinkDumpRequest(FileDescriptor fd, int proto, int states, int family)
|
||||
throws InterruptedIOException, ErrnoException {
|
||||
final byte[] dumpMsg = InetDiagMessage.inetDiagReqV2(
|
||||
private static byte [] makeNetlinkDumpRequest(int proto, int states, int family) {
|
||||
return InetDiagMessage.inetDiagReqV2(
|
||||
proto,
|
||||
null /* id */,
|
||||
family,
|
||||
|
|
@ -331,51 +332,29 @@ public class InetDiagMessage extends NetlinkMessage {
|
|||
0 /* pad */,
|
||||
0 /* idiagExt */,
|
||||
states);
|
||||
NetlinkUtils.sendMessage(fd, dumpMsg, 0, dumpMsg.length, IO_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private static int processNetlinkDumpAndDestroySockets(FileDescriptor dumpFd,
|
||||
private static int processNetlinkDumpAndDestroySockets(byte[] dumpReq,
|
||||
FileDescriptor destroyFd, int proto, Predicate<InetDiagMessage> filter)
|
||||
throws InterruptedIOException, ErrnoException {
|
||||
int destroyedSockets = 0;
|
||||
|
||||
while (true) {
|
||||
final ByteBuffer buf = NetlinkUtils.recvMessage(
|
||||
dumpFd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
|
||||
|
||||
while (buf.remaining() > 0) {
|
||||
final int position = buf.position();
|
||||
final NetlinkMessage nlMsg = NetlinkMessage.parse(buf, NETLINK_INET_DIAG);
|
||||
if (nlMsg == null) {
|
||||
// Move to the position where parse started for error log.
|
||||
buf.position(position);
|
||||
Log.e(TAG, "Failed to parse netlink message: " + hexify(buf));
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlMsg.getHeader().nlmsg_type == NLMSG_DONE) {
|
||||
return destroyedSockets;
|
||||
}
|
||||
|
||||
if (!(nlMsg instanceof InetDiagMessage)) {
|
||||
Log.wtf(TAG, "Received unexpected netlink message: " + nlMsg);
|
||||
continue;
|
||||
}
|
||||
|
||||
final InetDiagMessage diagMsg = (InetDiagMessage) nlMsg;
|
||||
if (filter.test(diagMsg)) {
|
||||
try {
|
||||
sendNetlinkDestroyRequest(destroyFd, proto, diagMsg);
|
||||
destroyedSockets++;
|
||||
} catch (InterruptedIOException | ErrnoException e) {
|
||||
if (!(e instanceof ErrnoException
|
||||
&& ((ErrnoException) e).errno == ENOENT)) {
|
||||
Log.e(TAG, "Failed to destroy socket: diagMsg=" + diagMsg + ", " + e);
|
||||
}
|
||||
throws SocketException, InterruptedIOException, ErrnoException {
|
||||
AtomicInteger destroyedSockets = new AtomicInteger(0);
|
||||
Consumer<InetDiagMessage> handleNlDumpMsg = (diagMsg) -> {
|
||||
if (filter.test(diagMsg)) {
|
||||
try {
|
||||
sendNetlinkDestroyRequest(destroyFd, proto, diagMsg);
|
||||
destroyedSockets.getAndIncrement();
|
||||
} catch (InterruptedIOException | ErrnoException e) {
|
||||
if (!(e instanceof ErrnoException
|
||||
&& ((ErrnoException) e).errno == ENOENT)) {
|
||||
Log.e(TAG, "Failed to destroy socket: diagMsg=" + diagMsg + ", " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NetlinkUtils.<InetDiagMessage>getAndProcessNetlinkDumpMessages(dumpReq,
|
||||
NETLINK_INET_DIAG, InetDiagMessage.class, handleNlDumpMsg);
|
||||
return destroyedSockets.get();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -433,31 +412,28 @@ public class InetDiagMessage extends NetlinkMessage {
|
|||
|
||||
private static void destroySockets(int proto, int states, Predicate<InetDiagMessage> filter)
|
||||
throws ErrnoException, SocketException, InterruptedIOException {
|
||||
FileDescriptor dumpFd = null;
|
||||
FileDescriptor destroyFd = null;
|
||||
|
||||
try {
|
||||
dumpFd = NetlinkUtils.createNetLinkInetDiagSocket();
|
||||
destroyFd = NetlinkUtils.createNetLinkInetDiagSocket();
|
||||
connectToKernel(dumpFd);
|
||||
connectToKernel(destroyFd);
|
||||
|
||||
for (int family : List.of(AF_INET, AF_INET6)) {
|
||||
byte[] req = makeNetlinkDumpRequest(proto, states, family);
|
||||
|
||||
try {
|
||||
sendNetlinkDumpRequest(dumpFd, proto, states, family);
|
||||
} catch (InterruptedIOException | ErrnoException e) {
|
||||
Log.e(TAG, "Failed to send netlink dump request: " + e);
|
||||
continue;
|
||||
}
|
||||
final int destroyedSockets = processNetlinkDumpAndDestroySockets(
|
||||
dumpFd, destroyFd, proto, filter);
|
||||
Log.d(TAG, "Destroyed " + destroyedSockets + " sockets"
|
||||
final int destroyedSockets = processNetlinkDumpAndDestroySockets(
|
||||
req, destroyFd, proto, filter);
|
||||
Log.d(TAG, "Destroyed " + destroyedSockets + " sockets"
|
||||
+ ", proto=" + stringForProtocol(proto)
|
||||
+ ", family=" + stringForAddressFamily(family)
|
||||
+ ", states=" + states);
|
||||
} catch (SocketException | InterruptedIOException | ErrnoException e) {
|
||||
Log.e(TAG, "Failed to send netlink dump request or receive messages: " + e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closeSocketQuietly(dumpFd);
|
||||
closeSocketQuietly(destroyFd);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ import static android.system.OsConstants.SO_RCVTIMEO;
|
|||
import static android.system.OsConstants.SO_SNDTIMEO;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.hexify;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE;
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.RTNL_FAMILY_IP6MR;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
|
||||
|
||||
import android.net.ParseException;
|
||||
import android.net.util.SocketUtils;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
|
@ -314,49 +314,20 @@ public class NetlinkUtils {
|
|||
|
||||
private NetlinkUtils() {}
|
||||
|
||||
/**
|
||||
* Sends a netlink dump request and processes the returned dump messages
|
||||
*
|
||||
* @param <T> extends NetlinkMessage
|
||||
* @param dumpRequestMessage netlink dump request message to be sent
|
||||
* @param nlFamily netlink family
|
||||
* @param msgClass expected class of the netlink message
|
||||
* @param func function defined by caller to handle the dump messages
|
||||
* @throws SocketException when fails to create socket
|
||||
* @throws InterruptedIOException when fails to read the dumpFd
|
||||
* @throws ErrnoException when fails to send dump request
|
||||
* @throws ParseException when message can't be parsed
|
||||
*/
|
||||
public static <T extends NetlinkMessage> void getAndProcessNetlinkDumpMessages(
|
||||
byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass,
|
||||
private static <T extends NetlinkMessage> void getAndProcessNetlinkDumpMessagesWithFd(
|
||||
FileDescriptor fd, byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass,
|
||||
Consumer<T> func)
|
||||
throws SocketException, InterruptedIOException, ErrnoException, ParseException {
|
||||
// Create socket and send dump request
|
||||
final FileDescriptor fd;
|
||||
try {
|
||||
fd = netlinkSocketForProto(nlFamily);
|
||||
} catch (ErrnoException e) {
|
||||
Log.e(TAG, "Failed to create netlink socket " + e);
|
||||
throw e.rethrowAsSocketException();
|
||||
}
|
||||
throws SocketException, InterruptedIOException, ErrnoException {
|
||||
// connecToKernel throws ErrnoException and SocketException, should be handled by caller
|
||||
connectToKernel(fd);
|
||||
|
||||
try {
|
||||
connectToKernel(fd);
|
||||
} catch (ErrnoException | SocketException e) {
|
||||
Log.e(TAG, "Failed to connect netlink socket to kernel " + e);
|
||||
closeSocketQuietly(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sendMessage(fd, dumpRequestMessage, 0, dumpRequestMessage.length, IO_TIMEOUT_MS);
|
||||
} catch (InterruptedIOException | ErrnoException e) {
|
||||
Log.e(TAG, "Failed to send dump request " + e);
|
||||
closeSocketQuietly(fd);
|
||||
throw e;
|
||||
}
|
||||
// sendMessage throws InterruptedIOException and ErrnoException,
|
||||
// should be handled by caller
|
||||
sendMessage(fd, dumpRequestMessage, 0, dumpRequestMessage.length, IO_TIMEOUT_MS);
|
||||
|
||||
while (true) {
|
||||
// recvMessage throws ErrnoException, InterruptedIOException
|
||||
// should be handled by caller
|
||||
final ByteBuffer buf = recvMessage(
|
||||
fd, NetlinkUtils.DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
|
||||
|
||||
|
|
@ -367,17 +338,15 @@ public class NetlinkUtils {
|
|||
// Move to the position where parse started for error log.
|
||||
buf.position(position);
|
||||
Log.e(TAG, "Failed to parse netlink message: " + hexify(buf));
|
||||
closeSocketQuietly(fd);
|
||||
throw new ParseException("Failed to parse netlink message");
|
||||
break;
|
||||
}
|
||||
|
||||
if (nlMsg.getHeader().nlmsg_type == NLMSG_DONE) {
|
||||
closeSocketQuietly(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msgClass.isInstance(nlMsg)) {
|
||||
Log.e(TAG, "Received unexpected netlink message: " + nlMsg);
|
||||
Log.wtf(TAG, "Received unexpected netlink message: " + nlMsg);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -386,6 +355,91 @@ public class NetlinkUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends a netlink dump request and processes the returned dump messages
|
||||
*
|
||||
* @param <T> extends NetlinkMessage
|
||||
* @param dumpRequestMessage netlink dump request message to be sent
|
||||
* @param nlFamily netlink family
|
||||
* @param msgClass expected class of the netlink message
|
||||
* @param func function defined by caller to handle the dump messages
|
||||
* @throws SocketException when fails to connect socket to kernel
|
||||
* @throws InterruptedIOException when fails to read the dumpFd
|
||||
* @throws ErrnoException when fails to create dump fd, send dump request
|
||||
* or receive messages
|
||||
*/
|
||||
public static <T extends NetlinkMessage> void getAndProcessNetlinkDumpMessages(
|
||||
byte[] dumpRequestMessage, int nlFamily, Class<T> msgClass,
|
||||
Consumer<T> func)
|
||||
throws SocketException, InterruptedIOException, ErrnoException {
|
||||
// Create socket
|
||||
final FileDescriptor fd = netlinkSocketForProto(nlFamily);
|
||||
try {
|
||||
getAndProcessNetlinkDumpMessagesWithFd(fd, dumpRequestMessage, nlFamily,
|
||||
msgClass, func);
|
||||
} finally {
|
||||
closeSocketQuietly(fd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a RTM_GETROUTE message for dumping multicast IPv6 routes from kernel.
|
||||
*/
|
||||
private static byte[] newIpv6MulticastRouteDumpRequest() {
|
||||
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
|
||||
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETROUTE;
|
||||
nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
final short shortZero = 0;
|
||||
|
||||
// family must be RTNL_FAMILY_IP6MR to dump IPv6 multicast routes.
|
||||
// dstLen, srcLen, tos and scope must be zero in FIB dump request.
|
||||
// protocol, flags must be 0, and type must be RTN_MULTICAST (if not 0) for multicast
|
||||
// dump request.
|
||||
// table or RTA_TABLE attributes can be used to dump a specific routing table.
|
||||
// RTA_OIF attribute can be used to dump only routes containing given oif.
|
||||
// Here no attributes are set so the kernel can return all multicast routes.
|
||||
final StructRtMsg rtMsg =
|
||||
new StructRtMsg(RTNL_FAMILY_IP6MR /* family */, shortZero /* dstLen */,
|
||||
shortZero /* srcLen */, shortZero /* tos */, shortZero /* table */,
|
||||
shortZero /* protocol */, shortZero /* scope */, shortZero /* type */,
|
||||
0L /* flags */);
|
||||
final RtNetlinkRouteMessage msg =
|
||||
new RtNetlinkRouteMessage(nlmsghdr, rtMsg);
|
||||
|
||||
final int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructRtMsg.STRUCT_SIZE;
|
||||
nlmsghdr.nlmsg_len = spaceRequired;
|
||||
final byte[] bytes = new byte[NetlinkConstants.alignedLengthOf(spaceRequired)];
|
||||
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
msg.pack(byteBuffer);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of IPv6 multicast route messages from kernel.
|
||||
*/
|
||||
public static List<RtNetlinkRouteMessage> getIpv6MulticastRoutes() {
|
||||
final byte[] dumpMsg = newIpv6MulticastRouteDumpRequest();
|
||||
List<RtNetlinkRouteMessage> routes = new ArrayList<>();
|
||||
Consumer<RtNetlinkRouteMessage> handleNlDumpMsg = (msg) -> {
|
||||
if (msg.getRtmFamily() == RTNL_FAMILY_IP6MR) {
|
||||
// Sent rtmFamily RTNL_FAMILY_IP6MR in dump request to make sure ipv6
|
||||
// multicast routes are included in netlink reply messages, the kernel
|
||||
// may also reply with other kind of routes, so we filter them out here.
|
||||
routes.add(msg);
|
||||
}
|
||||
};
|
||||
try {
|
||||
NetlinkUtils.<RtNetlinkRouteMessage>getAndProcessNetlinkDumpMessages(
|
||||
dumpMsg, NETLINK_ROUTE, RtNetlinkRouteMessage.class,
|
||||
handleNlDumpMsg);
|
||||
} catch (SocketException | InterruptedIOException | ErrnoException e) {
|
||||
Log.e(TAG, "Failed to dump multicast routes");
|
||||
return routes;
|
||||
}
|
||||
|
||||
return routes;
|
||||
}
|
||||
|
||||
private static void closeSocketQuietly(final FileDescriptor fd) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.net.module.util.structs;
|
||||
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import java.net.Inet6Address;
|
||||
import java.util.Set;
|
||||
|
||||
/*
|
||||
* Implements the mf6cctl structure which is used to add a multicast forwarding
|
||||
* cache, see /usr/include/linux/mroute6.h
|
||||
*/
|
||||
public class StructMf6cctl extends Struct {
|
||||
// struct sockaddr_in6 mf6cc_origin, added the fields directly as Struct
|
||||
// doesn't support nested Structs
|
||||
@Field(order = 0, type = Type.U16)
|
||||
public final int originFamily; // AF_INET6
|
||||
@Field(order = 1, type = Type.U16)
|
||||
public final int originPort; // Transport layer port # of origin
|
||||
@Field(order = 2, type = Type.U32)
|
||||
public final long originFlowinfo; // IPv6 flow information
|
||||
@Field(order = 3, type = Type.ByteArray, arraysize = 16)
|
||||
public final byte[] originAddress; //the IPv6 address of origin
|
||||
@Field(order = 4, type = Type.U32)
|
||||
public final long originScopeId; // scope id, not used
|
||||
|
||||
// struct sockaddr_in6 mf6cc_mcastgrp
|
||||
@Field(order = 5, type = Type.U16)
|
||||
public final int groupFamily; // AF_INET6
|
||||
@Field(order = 6, type = Type.U16)
|
||||
public final int groupPort; // Transport layer port # of multicast group
|
||||
@Field(order = 7, type = Type.U32)
|
||||
public final long groupFlowinfo; // IPv6 flow information
|
||||
@Field(order = 8, type = Type.ByteArray, arraysize = 16)
|
||||
public final byte[] groupAddress; //the IPv6 address of multicast group
|
||||
@Field(order = 9, type = Type.U32)
|
||||
public final long groupScopeId; // scope id, not used
|
||||
|
||||
@Field(order = 10, type = Type.U16, padding = 2)
|
||||
public final int mf6ccParent; // incoming interface
|
||||
@Field(order = 11, type = Type.ByteArray, arraysize = 32)
|
||||
public final byte[] mf6ccIfset; // outgoing interfaces
|
||||
|
||||
public StructMf6cctl(final Inet6Address origin, final Inet6Address group,
|
||||
final int mf6ccParent, final Set<Integer> oifset) {
|
||||
this(AF_INET6, 0, (long) 0, origin.getAddress(), (long) 0, AF_INET6,
|
||||
0, (long) 0, group.getAddress(), (long) 0, mf6ccParent,
|
||||
getMf6ccIfsetBytes(oifset));
|
||||
}
|
||||
|
||||
private StructMf6cctl(int originFamily, int originPort, long originFlowinfo,
|
||||
byte[] originAddress, long originScopeId, int groupFamily, int groupPort,
|
||||
long groupFlowinfo, byte[] groupAddress, long groupScopeId, int mf6ccParent,
|
||||
byte[] mf6ccIfset) {
|
||||
this.originFamily = originFamily;
|
||||
this.originPort = originPort;
|
||||
this.originFlowinfo = originFlowinfo;
|
||||
this.originAddress = originAddress;
|
||||
this.originScopeId = originScopeId;
|
||||
this.groupFamily = groupFamily;
|
||||
this.groupPort = groupPort;
|
||||
this.groupFlowinfo = groupFlowinfo;
|
||||
this.groupAddress = groupAddress;
|
||||
this.groupScopeId = groupScopeId;
|
||||
this.mf6ccParent = mf6ccParent;
|
||||
this.mf6ccIfset = mf6ccIfset;
|
||||
}
|
||||
|
||||
private static byte[] getMf6ccIfsetBytes(final Set<Integer> oifs)
|
||||
throws IllegalArgumentException {
|
||||
byte[] mf6ccIfset = new byte[32];
|
||||
for (int oif : oifs) {
|
||||
int idx = oif / 8;
|
||||
if (idx >= 32) {
|
||||
// invalid oif index, too big to fit in mf6ccIfset
|
||||
throw new IllegalArgumentException("Invalid oif index" + oif);
|
||||
}
|
||||
int offset = oif % 8;
|
||||
mf6ccIfset[idx] |= (byte) (1 << offset);
|
||||
}
|
||||
return mf6ccIfset;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.net.module.util.structs;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
|
||||
/*
|
||||
* Implements the mif6ctl structure which is used to add a multicast routing
|
||||
* interface, see /usr/include/linux/mroute6.h
|
||||
*/
|
||||
public class StructMif6ctl extends Struct {
|
||||
@Field(order = 0, type = Type.U16)
|
||||
public final int mif6cMifi; // Index of MIF
|
||||
@Field(order = 1, type = Type.U8)
|
||||
public final short mif6cFlags; // MIFF_ flags
|
||||
@Field(order = 2, type = Type.U8)
|
||||
public final short vifcThreshold; // ttl limit
|
||||
@Field(order = 3, type = Type.U16)
|
||||
public final int mif6cPifi; //the index of the physical IF
|
||||
@Field(order = 4, type = Type.U32, padding = 2)
|
||||
public final long vifcRateLimit; // Rate limiter values (NI)
|
||||
|
||||
public StructMif6ctl(final int mif6cMifi, final short mif6cFlags, final short vifcThreshold,
|
||||
final int mif6cPifi, final long vifcRateLimit) {
|
||||
this.mif6cMifi = mif6cMifi;
|
||||
this.mif6cFlags = mif6cFlags;
|
||||
this.vifcThreshold = vifcThreshold;
|
||||
this.mif6cPifi = mif6cPifi;
|
||||
this.vifcRateLimit = vifcRateLimit;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.net.module.util.structs;
|
||||
|
||||
import com.android.net.module.util.Struct;
|
||||
import java.net.Inet6Address;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
public class StructMrt6Msg extends Struct {
|
||||
public static final byte MRT6MSG_NOCACHE = 1;
|
||||
|
||||
@Field(order = 0, type = Type.S8)
|
||||
public final byte mbz;
|
||||
@Field(order = 1, type = Type.S8)
|
||||
public final byte msgType; // message type
|
||||
@Field(order = 2, type = Type.U16, padding = 4)
|
||||
public final int mif; // mif received on
|
||||
@Field(order = 3, type = Type.Ipv6Address)
|
||||
public final Inet6Address src;
|
||||
@Field(order = 4, type = Type.Ipv6Address)
|
||||
public final Inet6Address dst;
|
||||
|
||||
public StructMrt6Msg(final byte mbz, final byte msgType, final int mif,
|
||||
final Inet6Address source, final Inet6Address destination) {
|
||||
this.mbz = mbz; // kernel should set it to 0
|
||||
this.msgType = msgType;
|
||||
this.mif = mif;
|
||||
this.src = source;
|
||||
this.dst = destination;
|
||||
}
|
||||
|
||||
public static StructMrt6Msg parse(ByteBuffer byteBuffer) {
|
||||
byteBuffer.order(ByteOrder.nativeOrder());
|
||||
return Struct.parse(StructMrt6Msg.class, byteBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ import static android.system.OsConstants.AF_INET6;
|
|||
import static android.system.OsConstants.AF_UNSPEC;
|
||||
import static android.system.OsConstants.EACCES;
|
||||
import static android.system.OsConstants.NETLINK_ROUTE;
|
||||
|
||||
import static com.android.net.module.util.netlink.NetlinkConstants.RTNL_FAMILY_IP6MR;
|
||||
import static com.android.net.module.util.netlink.NetlinkUtils.DEFAULT_RECV_BUFSIZE;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
|
||||
import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
|
||||
|
|
@ -191,4 +191,17 @@ public class NetlinkUtilsTest {
|
|||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIpv6MulticastRoutes_doesNotThrow() {
|
||||
var multicastRoutes = NetlinkUtils.getIpv6MulticastRoutes();
|
||||
|
||||
for (var route : multicastRoutes) {
|
||||
assertNotNull(route);
|
||||
assertEquals("Route is not IP6MR: " + route,
|
||||
RTNL_FAMILY_IP6MR, route.getRtmFamily());
|
||||
assertNotNull("Route doesn't contain source: " + route, route.getSource());
|
||||
assertNotNull("Route doesn't contain destination: " + route, route.getDestination());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
package com.android.net.module.util.structs;
|
||||
|
||||
import static android.system.OsConstants.AF_INET6;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.net.InetAddresses;
|
||||
import android.util.ArraySet;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StructMf6cctlTest {
|
||||
private static final byte[] MSG_BYTES = new byte[] {
|
||||
10, 0, /* AF_INET6 */
|
||||
0, 0, /* originPort */
|
||||
0, 0, 0, 0, /* originFlowinfo */
|
||||
32, 1, 13, -72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* originAddress */
|
||||
0, 0, 0, 0, /* originScopeId */
|
||||
10, 0, /* AF_INET6 */
|
||||
0, 0, /* groupPort */
|
||||
0, 0, 0, 0, /* groupFlowinfo*/
|
||||
-1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 52, /*groupAddress*/
|
||||
0, 0, 0, 0, /* groupScopeId*/
|
||||
1, 0, /* mf6ccParent */
|
||||
0, 0, /* padding */
|
||||
0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* mf6ccIfset */
|
||||
};
|
||||
|
||||
private static final int OIF = 10;
|
||||
private static final byte[] OIFSET_BYTES = new byte[] {
|
||||
0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
private static final Inet6Address SOURCE =
|
||||
(Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
|
||||
private static final Inet6Address DESTINATION =
|
||||
(Inet6Address) InetAddresses.parseNumericAddress("ff05::1234");
|
||||
|
||||
@Test
|
||||
public void testConstructor() {
|
||||
final Set<Integer> oifset = new ArraySet<>();
|
||||
oifset.add(OIF);
|
||||
|
||||
StructMf6cctl mf6cctl = new StructMf6cctl(SOURCE, DESTINATION,
|
||||
1 /* mf6ccParent */, oifset);
|
||||
|
||||
assertTrue(Arrays.equals(SOURCE.getAddress(), mf6cctl.originAddress));
|
||||
assertTrue(Arrays.equals(DESTINATION.getAddress(), mf6cctl.groupAddress));
|
||||
assertEquals(1, mf6cctl.mf6ccParent);
|
||||
assertArrayEquals(OIFSET_BYTES, mf6cctl.mf6ccIfset);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructor_tooBigOifIndex_throwsIllegalArgumentException()
|
||||
throws UnknownHostException {
|
||||
final Set<Integer> oifset = new ArraySet<>();
|
||||
oifset.add(1000);
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new StructMf6cctl(SOURCE, DESTINATION, 1, oifset));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMf6cctl() {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(MSG_BYTES);
|
||||
buf.order(ByteOrder.nativeOrder());
|
||||
StructMf6cctl mf6cctl = StructMf6cctl.parse(StructMf6cctl.class, buf);
|
||||
|
||||
assertEquals(AF_INET6, mf6cctl.originFamily);
|
||||
assertEquals(AF_INET6, mf6cctl.groupFamily);
|
||||
assertArrayEquals(SOURCE.getAddress(), mf6cctl.originAddress);
|
||||
assertArrayEquals(DESTINATION.getAddress(), mf6cctl.groupAddress);
|
||||
assertEquals(1, mf6cctl.mf6ccParent);
|
||||
assertArrayEquals("mf6ccIfset = " + Arrays.toString(mf6cctl.mf6ccIfset),
|
||||
OIFSET_BYTES, mf6cctl.mf6ccIfset);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToBytes() {
|
||||
final Set<Integer> oifset = new ArraySet<>();
|
||||
oifset.add(OIF);
|
||||
|
||||
StructMf6cctl mf6cctl = new StructMf6cctl(SOURCE, DESTINATION,
|
||||
1 /* mf6ccParent */, oifset);
|
||||
byte[] bytes = mf6cctl.writeToBytes();
|
||||
|
||||
assertArrayEquals("bytes = " + Arrays.toString(bytes), MSG_BYTES, bytes);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package com.android.net.module.util.structs;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.util.ArraySet;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StructMif6ctlTest {
|
||||
private static final byte[] MSG_BYTES = new byte[] {
|
||||
1, 0, /* mif6cMifi */
|
||||
0, /* mif6cFlags */
|
||||
1, /* vifcThreshold*/
|
||||
20, 0, /* mif6cPifi */
|
||||
0, 0, 0, 0, /* vifcRateLimit */
|
||||
0, 0 /* padding */
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testConstructor() {
|
||||
StructMif6ctl mif6ctl = new StructMif6ctl(10 /* mif6cMifi */,
|
||||
(short) 11 /* mif6cFlags */,
|
||||
(short) 12 /* vifcThreshold */,
|
||||
13 /* mif6cPifi */,
|
||||
14L /* vifcRateLimit */);
|
||||
|
||||
assertEquals(10, mif6ctl.mif6cMifi);
|
||||
assertEquals(11, mif6ctl.mif6cFlags);
|
||||
assertEquals(12, mif6ctl.vifcThreshold);
|
||||
assertEquals(13, mif6ctl.mif6cPifi);
|
||||
assertEquals(14, mif6ctl.vifcRateLimit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMif6ctl() {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(MSG_BYTES);
|
||||
buf.order(ByteOrder.nativeOrder());
|
||||
StructMif6ctl mif6ctl = StructMif6ctl.parse(StructMif6ctl.class, buf);
|
||||
|
||||
assertEquals(1, mif6ctl.mif6cMifi);
|
||||
assertEquals(0, mif6ctl.mif6cFlags);
|
||||
assertEquals(1, mif6ctl.vifcThreshold);
|
||||
assertEquals(20, mif6ctl.mif6cPifi);
|
||||
assertEquals(0, mif6ctl.vifcRateLimit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToBytes() {
|
||||
StructMif6ctl mif6ctl = new StructMif6ctl(1 /* mif6cMifi */,
|
||||
(short) 0 /* mif6cFlags */,
|
||||
(short) 1 /* vifcThreshold */,
|
||||
20 /* mif6cPifi */,
|
||||
(long) 0 /* vifcRateLimit */);
|
||||
|
||||
byte[] bytes = mif6ctl.writeToBytes();
|
||||
|
||||
assertArrayEquals("bytes = " + Arrays.toString(bytes), MSG_BYTES, bytes);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.android.net.module.util.structs;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.net.InetAddresses;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
import com.android.net.module.util.Struct;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StructMrt6MsgTest {
|
||||
|
||||
private static final byte[] MSG_BYTES = new byte[] {
|
||||
0, /* mbz = 0 */
|
||||
1, /* message type = MRT6MSG_NOCACHE */
|
||||
1, 0, /* mif u16 = 1 */
|
||||
0, 0, 0, 0, /* padding */
|
||||
32, 1, 13, -72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* source=2001:db8::1 */
|
||||
-1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 52, /* destination=ff05::1234 */
|
||||
};
|
||||
|
||||
private static final Inet6Address SOURCE =
|
||||
(Inet6Address) InetAddresses.parseNumericAddress("2001:db8::1");
|
||||
private static final Inet6Address GROUP =
|
||||
(Inet6Address) InetAddresses.parseNumericAddress("ff05::1234");
|
||||
|
||||
@Test
|
||||
public void testParseMrt6Msg() {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(MSG_BYTES);
|
||||
StructMrt6Msg mrt6Msg = StructMrt6Msg.parse(buf);
|
||||
|
||||
assertEquals(1, mrt6Msg.mif);
|
||||
assertEquals(StructMrt6Msg.MRT6MSG_NOCACHE, mrt6Msg.msgType);
|
||||
assertEquals(SOURCE, mrt6Msg.src);
|
||||
assertEquals(GROUP, mrt6Msg.dst);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteToBytes() {
|
||||
StructMrt6Msg msg = new StructMrt6Msg((byte) 0 /* mbz must be 0 */,
|
||||
StructMrt6Msg.MRT6MSG_NOCACHE,
|
||||
1 /* mif */,
|
||||
SOURCE,
|
||||
GROUP);
|
||||
byte[] bytes = msg.writeToBytes();
|
||||
|
||||
assertArrayEquals(MSG_BYTES, bytes);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue