1/*
2 * Copyright (C) 2015 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
17package com.android.server.connectivity;
18
19import android.system.OsConstants;
20import android.net.ConnectivityManager;
21import android.net.NetworkUtils;
22import android.net.util.IpUtils;
23
24import java.net.Inet4Address;
25import java.net.Inet6Address;
26import java.net.InetAddress;
27import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
29
30import static android.net.ConnectivityManager.PacketKeepalive.*;
31
32/**
33 * Represents the actual packets that are sent by the
34 * {@link android.net.ConnectivityManager.PacketKeepalive} API.
35 *
36 * @hide
37 */
38public class KeepalivePacketData {
39    /** Protocol of the packet to send; one of the OsConstants.ETH_P_* values. */
40    public final int protocol;
41
42    /** Source IP address */
43    public final InetAddress srcAddress;
44
45    /** Destination IP address */
46    public final InetAddress dstAddress;
47
48    /** Source port */
49    public final int srcPort;
50
51    /** Destination port */
52    public final int dstPort;
53
54    /** Destination MAC address. Can change if routing changes. */
55    public byte[] dstMac;
56
57    /** Packet data. A raw byte string of packet data, not including the link-layer header. */
58    public final byte[] data;
59
60    private static final int IPV4_HEADER_LENGTH = 20;
61    private static final int UDP_HEADER_LENGTH = 8;
62
63    protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
64            InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
65        this.srcAddress = srcAddress;
66        this.dstAddress = dstAddress;
67        this.srcPort = srcPort;
68        this.dstPort = dstPort;
69        this.data = data;
70
71        // Check we have two IP addresses of the same family.
72        if (srcAddress == null || dstAddress == null ||
73                !srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) {
74            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
75        }
76
77        // Set the protocol.
78        if (this.dstAddress instanceof Inet4Address) {
79            this.protocol = OsConstants.ETH_P_IP;
80        } else if (this.dstAddress instanceof Inet6Address) {
81            this.protocol = OsConstants.ETH_P_IPV6;
82        } else {
83            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
84        }
85
86        // Check the ports.
87        if (!IpUtils.isValidUdpOrTcpPort(srcPort) || !IpUtils.isValidUdpOrTcpPort(dstPort)) {
88            throw new InvalidPacketException(ERROR_INVALID_PORT);
89        }
90    }
91
92    public static class InvalidPacketException extends Exception {
93        final public int error;
94        public InvalidPacketException(int error) {
95            this.error = error;
96        }
97    }
98
99    /**
100     * Creates an IPsec NAT-T keepalive packet with the specified parameters.
101     */
102    public static KeepalivePacketData nattKeepalivePacket(
103            InetAddress srcAddress, int srcPort,
104            InetAddress dstAddress, int dstPort) throws InvalidPacketException {
105
106        if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
107            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
108        }
109
110        if (dstPort != NATT_PORT) {
111            throw new InvalidPacketException(ERROR_INVALID_PORT);
112        }
113
114        int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
115        ByteBuffer buf = ByteBuffer.allocate(length);
116        buf.order(ByteOrder.BIG_ENDIAN);
117        buf.putShort((short) 0x4500);             // IP version and TOS
118        buf.putShort((short) length);
119        buf.putInt(0);                            // ID, flags, offset
120        buf.put((byte) 64);                       // TTL
121        buf.put((byte) OsConstants.IPPROTO_UDP);
122        int ipChecksumOffset = buf.position();
123        buf.putShort((short) 0);                  // IP checksum
124        buf.put(srcAddress.getAddress());
125        buf.put(dstAddress.getAddress());
126        buf.putShort((short) srcPort);
127        buf.putShort((short) dstPort);
128        buf.putShort((short) (length - 20));      // UDP length
129        int udpChecksumOffset = buf.position();
130        buf.putShort((short) 0);                  // UDP checksum
131        buf.put((byte) 0xff);                     // NAT-T keepalive
132        buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
133        buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
134
135        return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
136    }
137}
138