最近做和Eip相关的裸金属功能,涉及到一个以前开发中接触较少的领域:Ipv6 和 Ipv6 Cidr。Ipv6的概念网上有很多,但是Cidr的概念不多,而且大部分文章讲的也比较啰嗦,不够简明;这里简单记录一下自己的理解,实用为主。
关于Ipv6 Cidr
为了划分一个可用的Ip地址段,供一个小型局域网络来使用,避免浪费地址(ipv4比较珍贵);通常由机房中的一个大网段划分出来。与Ipv4 Cidr一样,也是一个可用的ip地址集合,其规则为:ip地址/后缀(指明前缀的位数,也叫掩码),cidr 是一种可变长子网掩码技术。比如:fd00::1001:100/120,其对应的全写:fd00:0000:0000:0000:0000:0000:1001:0100/120。
ipv6地址一共128位,对于fd00::1001:100/120,其cidr固定位(红色):fd00:0000:0000:0000:0000:0000:1001:01【00】/120,可用位在中括号中。也就是说,如果你传入的IP地址:fd00:0000:0000:0000:0000:0000:1001:02 00,则不在当前cidr段中。同理:fd00:0000:0000:0000:0000:0000:1001:01 01 则在cidr段中。
实际使用中遇到的问题
1、判断ipv6地址是否在指定cidr段内
2、ipv6 cidr是否合法
3、ipv6及其cidr的简写和全写切换
我在代码中使用了googlecode源码包,maven坐标如下:
<dependency>
<groupId>com.googlecode.java-ipv6</groupId>
<artifactId>java-ipv6</artifactId>
<version>0.16</version>
</dependency>
开发中遇到了一些比较有意思的问题也一并记录:fd00::1:1/120 (这是一个错误的cidr,因为他最后一位【2】被占了;正确的应该是fd00::1:0/120,或fd00::1:1/128)。不多写了,上代码自己看吧,愿詹姆斯·高斯林保佑你。
请求实体:
@Data
@EqualsAndHashCode(callSuper=false)
public class CheckIpv6InfomationRequest extends PagingRequest {
@Expose
@Pattern(regexp = Validation.REGEX_IPV6, message = ErrorCode.INVALID_ARGUMENT)
private String targetIpv6; // ipv6 简写或全写
@Expose
@Pattern(regexp = Validation.REGEX_IPV6_CIDR, message = ErrorCode.INVALID_ARGUMENT)
private String ipv6Cidr; // 不为空则验证targetIpv6是否在cidr段中。
}
String REGEX_IPV6 = "^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$";
String REGEX_IPV6_CIDR = "^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$|^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*\\/(1[01][0-9]|12[0-8]|[0-9]{1,2})$";
响应实体:
@Data
@NoArgsConstructor
public class CheckIpv6InfomationResponse {
@Expose
private String targetIpv6;
@Expose
private String ipv6Cidr;
@Expose
private String ipv6CidrLong;
@Expose
private String targetIpv6Long;
@Expose
private String targetIpv6Short;
@Expose
private String ipv6CidrAddressLong;
@Expose
private String ipv6CidrAddressShort;
@Expose
private String msg;
}
逻辑代码
import com.googlecode.ipv6.IPv6Address;
import com.googlecode.ipv6.IPv6Network;
@GetMapping(value = "/checkIpv6Infomation")
public RestfulResponse<CheckIpv6InfomationResponse> checkIpv6Infomation(
@Validated CheckIpv6InfomationRequest param, HttpServletRequest request) {
CheckIpv6InfomationResponse result = new CheckIpv6InfomationResponse();
String targetIpv6 = param.getTargetIpv6();
String targetIpv6Long = IPv6Address.fromString(targetIpv6).toLongString();
String targetIpv6Short = IPv6Address.fromString(targetIpv6).toString();
String ipv6Cidr = param.getIpv6Cidr();
String ipv6CidrLong = "--"; // 完整的 ipv6 cidr
String ipv6CidrAddressLong = "--";
String ipv6CidrAddressShort = "--";
String msg = "--";
if(StringUtils.isNotBlank(ipv6Cidr)) {
IPv6Network ipv6Network = null; // 例如参数:fd00::1:1/120(这是一个错误的cidr,正确的应该是fd00::1:0/120,或fd00::1:1/128
try {
ipv6Network = IPv6Network.fromString(ipv6Cidr); // 不能被解析,此处会抛异常,则不是标准cidr
} catch (Exception e) {
throw new RestfulException(ErrorCode.IPV6_CIDR_NOT_STANDARD,
"ipv6 cidr not standard", HttpStatus.BAD_REQUEST);
}
ipv6CidrLong = ipv6Network.toLongString(); // 这是绝对正确的cidr,即使你传入了错误的cidr,也会被格式化成为正确的。
String cidrAddress = ipv6CidrLong.substring(0, ipv6CidrLong.indexOf("/")); // fd00::1:1 或全写
String paramIpv6CidrAddress = IPv6Address.fromString(ipv6Cidr.substring(0, ipv6Cidr.indexOf("/"))).toLongString(); // 传入的ipv6 cidr 未必正确,fd00::111:0/8 就是错误的
if(!paramIpv6CidrAddress.equalsIgnoreCase(cidrAddress)) {
result.setTargetIpv6(targetIpv6);
result.setTargetIpv6Long(targetIpv6Long);
result.setTargetIpv6Short(targetIpv6Short);
result.setIpv6Cidr(ipv6Cidr);
result.setIpv6CidrLong("right:" + ipv6CidrLong);
result.setIpv6CidrAddressLong("error:" + paramIpv6CidrAddress);
result.setIpv6CidrAddressShort(ipv6CidrAddressShort);
result.setMsg("ipv6 cidr invalid,right is:" + ipv6Network.toString());
return new RestfulResponse<>(result);
}
ipv6CidrAddressLong = IPv6Address.fromString(cidrAddress).toLongString();
ipv6CidrAddressShort = IPv6Address.fromString(cidrAddress).toString(); // 简写
String addressPrefix16 = ipv6CidrAddressLong.split(":")[7]; // 0001
int address10 = Integer.parseInt(addressPrefix16,16);
int power = 1 << 128 - ipv6Network.getNetmask().asPrefixLength(); // 前缀整除 2的 (128-掩码)次幂,此处验证【1、整段cidr合法性】
int model = address10 % power;
if(model!=0){
throw new RestfulException(ErrorCode.IPV6_CIDR_INVALID,
"ipv6 cidr invalid", HttpStatus.BAD_REQUEST);
}
int mask = ipv6Network.getNetmask().asPrefixLength();
int offset = mask / 4;
String targetIpv6Long_ = targetIpv6Long.replace(":", "").substring(0, offset);
String ipv6CidrAddressLong_ = ipv6CidrAddressLong.replace(":", "").substring(0, offset);
if(!targetIpv6Long_.equals(ipv6CidrAddressLong_)) {
msg = "ipv6:[" + param.getTargetIpv6() + "] not in cidr:[" + param.getIpv6Cidr() + "]";
}else {
msg = "ipv6:[" + param.getTargetIpv6() + "] in cidr:[" + param.getIpv6Cidr() + "]";
}
}
result.setTargetIpv6(targetIpv6);
result.setTargetIpv6Long(targetIpv6Long);
result.setTargetIpv6Short(targetIpv6Short);
result.setIpv6Cidr(ipv6Cidr);
result.setIpv6CidrLong(ipv6CidrLong);
result.setIpv6CidrAddressLong(ipv6CidrAddressLong);
result.setIpv6CidrAddressShort(ipv6CidrAddressShort);
result.setMsg(msg);
return new RestfulResponse<>(result);
}
结果: