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 }