/* * 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 android.net.dhcp6; import static android.system.OsConstants.AF_INET6; import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_DGRAM; import static android.system.OsConstants.SOCK_NONBLOCK; import static com.android.net.module.util.NetworkStackConstants.ALL_DHCP_RELAY_AGENTS_AND_SERVERS; import static com.android.net.module.util.NetworkStackConstants.DHCP6_CLIENT_PORT; import static com.android.net.module.util.NetworkStackConstants.DHCP6_SERVER_PORT; import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY; import android.content.Context; import android.net.ip.IpClient; import android.net.util.SocketUtils; import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import androidx.annotation.NonNull; import com.android.internal.util.StateMachine; import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.PacketReader; import java.io.FileDescriptor; import java.io.IOException; import java.net.SocketException; import java.nio.ByteBuffer; /** * A DHCPv6 client. * * So far only support IA_PD (prefix delegation), not for IA_NA/IA_TA yet. * * @hide */ public class Dhcp6Client extends StateMachine { private static final String TAG = Dhcp6Client.class.getSimpleName(); private static final boolean DBG = true; // Internal messages. // Dhcp6Client shares the same handler with IpClient, define the base command range for // both public and private messages used in Dhcp6Client, to avoid commands overlap. private static final int PRIVATE_BASE = IpClient.DHCP6CLIENT_CMD_BASE + 100; private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 1; @NonNull private final Context mContext; // State variables. @NonNull private final StateMachine mController; @NonNull private final String mIfaceName; private Dhcp6Client(@NonNull final Context context, @NonNull final StateMachine controller, @NonNull final InterfaceParams iface) { super(TAG, controller.getHandler()); mContext = context; mController = controller; mIfaceName = iface.name; // TODO: add state machine initialization. } private class Dhcp6PacketHandler extends PacketReader { private FileDescriptor mUdpSock; Dhcp6PacketHandler(Handler handler) { super(handler); } @Override protected void handlePacket(byte[] recvbuf, int length) { try { final Dhcp6Packet packet = Dhcp6Packet.decodePacket(recvbuf, length); if (DBG) Log.d(TAG, "Received packet: " + packet); sendMessage(CMD_RECEIVED_PACKET, packet); } catch (Dhcp6Packet.ParseException e) { Log.e(TAG, "Can't parse DHCPv6 packet: " + e.getMessage()); } } @Override protected FileDescriptor createFd() { try { mUdpSock = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName); Os.bind(mUdpSock, IPV6_ADDR_ANY, DHCP6_CLIENT_PORT); } catch (SocketException | ErrnoException e) { Log.e(TAG, "Error creating udp socket", e); closeFd(mUdpSock); mUdpSock = null; return null; } return mUdpSock; } @Override protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception { try { return Os.read(fd, packetBuffer, 0, packetBuffer.length); } catch (IOException | ErrnoException e) { Log.e(TAG, "Fail to read packet"); throw e; } } public int transmitPacket(final ByteBuffer buf) throws ErrnoException, SocketException { int ret = Os.sendto(mUdpSock, buf.array(), 0 /* byteOffset */, buf.limit() /* byteCount */, 0 /* flags */, ALL_DHCP_RELAY_AGENTS_AND_SERVERS, DHCP6_SERVER_PORT); return ret; } } }