package io.trygvis.rules.network; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.checkerframework.checker.nullness.compatqual.NonNullType; import javax.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.regex.Pattern; @JsonSerialize(using = Ipv4Cidr.Serializer.class) public class Ipv4Cidr implements Comparable { public final int network; public final int bits; public Ipv4Cidr(int network, int bits) { if (bits < 0 || bits > 32) { throw new IllegalArgumentException("bits must be [0, 32]"); } int hostBits = 32 - bits; int netmask = (-1 >> hostBits) << hostBits; int x = network & ~netmask; if (x != 0) { throw new IllegalArgumentException("The host part of the address must be 0."); } this.network = network; this.bits = bits; } @Override public String toString() { return "%d.%d.%d.%d/%d".formatted( network >> 24 & 0xff, network >> 16 & 0xff, network >> 8 & 0xff, network & 0xff, bits); } private String formatIpv4(int address) { return "%d.%d.%d.%d".formatted(address >> 24 & 0xff, address >> 16 & 0xff, address >> 8 & 0xff, address & 0xff); } public List partition(int bits) { if (bits <= 0 || bits <= this.bits || bits > 32) { throw new IllegalArgumentException("Invalid new network size"); } var list = new ArrayList(); int count = 1 << (bits - this.bits); for (int i = 0; i < count; i++) { var network = this.network | (i << (32 - bits)); list.add(new Ipv4Cidr(network, bits)); } return list; } public List addresses() { int size = 1 << 32 - bits; var end = network + size; var addresses = new ArrayList(size); for (int address = network; address < end; address++) { addresses.add(new Ipv4Address(address)); } return addresses; } @Override public int compareTo(Ipv4Cidr o) { if (this == o) { return 0; } var ret = network - o.network; if (ret != 0) { return ret; } return bits - o.bits; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Ipv4Cidr ipv4Cidr = (Ipv4Cidr) o; return network == ipv4Cidr.network && bits == ipv4Cidr.bits; } @Override public int hashCode() { return Objects.hash(network, bits); } private static final Pattern pattern = Pattern.compile("([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})/([0-9]{1,3})"); public static Ipv4Cidr parseCidr(String cidr) { var matcher = pattern.matcher(cidr); if (!matcher.matches()) { throw new IllegalArgumentException("Not a CIDR: " + cidr); } var b1 = matcher.group(1); var b2 = matcher.group(2); var b3 = matcher.group(3); var b4 = matcher.group(4); int network = parse(b1) << 24 | parse(b2) << 16 | parse(b3) << 8 | parse(b4); // System.out.printf("network = %x%n", network); var l = matcher.group(5); var bits = Integer.parseInt(l); // System.out.printf("netmask = %08x%n", netmask); return new Ipv4Cidr(network, bits); } private static int parse(String s) { var i = Integer.parseInt(s); if (i > 255) { throw new IllegalArgumentException("Not a CIDR"); } return i; } public static class Serializer extends JsonSerializer { @Override public void serialize(Ipv4Cidr value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeStartObject(); gen.writeObjectField("value", value.toString()); gen.writeEndObject(); } } }