1 /*
2 * junixsocket
3 *
4 * Copyright 2009-2024 Christian Kohlschütter
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.newsclub.net.unix.darwin.system;
19
20 import java.nio.ByteBuffer;
21
22 /**
23 * Some IP protocol-related helper methods.
24 *
25 * @author Christian Kohlschütter
26 */
27 public final class IPUtil {
28 /**
29 * The length (in bytes) of the "domain" header used in loopback packet systems like UTUN_CONTROL.
30 */
31 public static final int DOMAIN_HEADER_LENGTH = 4; // bytes
32
33 /**
34 * The identifier for AF_INET (at least on Darwin).
35 */
36 public static final int DOMAIN_AF_INET = 2;
37
38 /**
39 * The length (in bytes) of an IPv4 header without options.
40 */
41 public static final int IPV4_DEFAULT_HEADER_SIZE = 20; // bytes
42
43 /**
44 * The ICMP protocol.
45 */
46 public static final byte AF_INET_PROTOCOL_ICMP = 1;
47
48 private IPUtil() {
49 throw new IllegalStateException("No instances");
50 }
51
52 /**
53 * Computes the checksum for an IPv4 header, and overwrites any existing checksum with the correct
54 * one.
55 *
56 * @param bb The buffer containing the IPv4 header
57 * @param start The beginning position of the header in the buffer.
58 * @param end The end position (exclusive) of the header in the buffer.
59 * @return The computed 16-bit checksum
60 */
61 public static int checksumIPv4header(ByteBuffer bb, int start, int end) {
62 return checksumIPstyle(bb, start, end, 10);
63 }
64
65 /**
66 * Computes the checksum for an ICMP header, and overwrites any existing checksum with the correct
67 * one.
68 *
69 * Also see <a href="https://datatracker.ietf.org/doc/html/rfc792">RFC 792</a>.
70 *
71 * @param bb The buffer containing the ICMP header
72 * @param start The beginning position of the header in the buffer.
73 * @param end The end position (exclusive) of the header in the buffer.
74 * @return The computed 16-bit checksum
75 */
76 public static int checksumICMPheader(ByteBuffer bb, int start, int end) {
77 return checksumIPstyle(bb, start, end, 2);
78 }
79
80 /**
81 * Computes the 16-bit checksum for some header used in IP networking, and overwrites any existing
82 * checksum with the correct one.
83 *
84 * Also see <a href="https://datatracker.ietf.org/doc/html/rfc1071">RFC 1071</a>.
85 *
86 * @param bb The buffer containing the ICMP header
87 * @param start The beginning position of the header in the buffer.
88 * @param end The end position (exclusive) of the header in the buffer.
89 * @param checksumOffset The offset from start for an existing 16-bit checksum that is to be
90 * ignored.
91 * @return The computed 16-bit checksum
92 */
93 private static int checksumIPstyle(ByteBuffer bb, int start, int end, int checksumOffset) {
94 final int checksumAt = start + checksumOffset;
95 int sum = 0;
96
97 if (checksumOffset >= end) {
98 throw new IllegalArgumentException("checksumOffset");
99 }
100
101 // While we could pretend the checksum is 0 (by ignoring the computation at position
102 // checksumAt), we zero it out here, and later put the correct checksum back in.
103 // This should not only be faster than two for-loops or checking the position prior to adding,
104 // it also puts the correct checksum in place, which can come in handy when composing packets.
105 // It is also the recommended strategy as per RFC 1071. The downside is that we modify the
106 // contents of the buffer, but that's OK since we control the API.
107 bb.putShort(checksumAt, (short) 0);
108
109 for (int i = start; i < end; i += 2) {
110 int v = bb.getShort(i) & 0xFFFF;
111
112 sum += v;
113
114 int overflow = (sum & ~0xFFFF);
115 if (overflow != 0) {
116 // overflow -> add carry and trim to 16-bit
117 sum = (sum + (overflow >>> 16)) & 0xFFFF;
118 }
119 }
120
121 int checksum = (~sum) & 0xFFFF;
122
123 // fix checksum
124 bb.putShort(checksumAt, (short) checksum);
125
126 return checksum;
127 }
128
129 /**
130 * Put (write) an IPv4 header to the given byte buffer, using the given parameters.
131 *
132 * This should write exactly 20 bytes to the buffer. The buffer position then is at the end of the
133 * header.
134 *
135 * @param bb The target byte buffer.
136 * @param payloadLength The length of the payload (excluding the IPv4 header).
137 * @param protocol The protocol identifier.
138 * @param srcIP The source IPv4 address.
139 * @param dstIP The destination IPv4 address.
140 */
141 public static void putIPv4Header(ByteBuffer bb, int payloadLength, byte protocol, int srcIP,
142 int dstIP) {
143 bb.put((byte) 0x45); // IPv4, 5*4=20 bytes header
144 bb.put((byte) 0); // TOS/DSCP
145 bb.putShort((short) (20 + payloadLength)); // total length = header + payload
146 bb.putShort((short) 0); // identification
147 bb.putShort((short) 0); // flags and fragment offset
148 bb.put((byte) 65); // TTL
149 bb.put(protocol); // protocol (e.g., ICMP)
150 bb.putShort((short) 0); // header checksum (placeholder)
151 bb.putInt(srcIP);
152 bb.putInt(dstIP);
153 // end of header (20 bytes)
154 }
155
156 /**
157 * Put (write) an ICMP echo response header to the given byte buffer, using the given parameters.
158 *
159 * @param bb The target byte buffer.
160 * @param echoIdentifier The identifier, from the ICMP echo request.
161 * @param sequenceNumber The sequence number, from the ICMP echo request.
162 * @param payload The payload, from the ICMP echo request.
163 */
164 public static void putICMPEchoResponse(ByteBuffer bb, short echoIdentifier, short sequenceNumber,
165 ByteBuffer payload) {
166 bb.put((byte) 0); // Echo response
167 bb.put((byte) 0); // Echo has no other code
168 bb.putShort((short) 0); // ICMP checksum (placeholder)
169 bb.putShort(echoIdentifier); // ICMP echo identifier
170 bb.putShort(sequenceNumber); // ICMP echo sequence number
171 bb.put(payload);
172 }
173 }