blob: 9efe64b36b6ccb30b68c58aa16eac2d5cbd815d9 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onlab.packet;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Class representing EAPOL MKPDU Basic Parameter Set.
* IEEE 802.1X Clause 11; Figure 11-8
*/
public class EAPOLMkpduBasicParameterSet extends BasePacket implements EAPOLMkpduParameterSet {
// Parameter Set fields.
private byte mkaVersion;
private byte keyServerPriority;
private boolean keyServer;
private boolean macSecDesired;
private byte capability;
private short bodyLength;
private SCI sci;
private byte[] mi;
private int mn;
private byte[] algAgility;
private byte[] ckn;
private byte[] padding;
// Various fixed parameter widths. IEEE 802.1X Table 11-6.
public static final int FIELD_ALGAG_LENGTH = 4;
public static final int TOTAL_BPS_BODY_LENGTH = 32;
// Basic Parameter Set field masks.
public static final byte KEYSERVER_MASK = (byte) 0x80;
public static final byte KEYSERVER_OFFSET = (byte) 0x07;
public static final byte MACSEC_DESIRED_MASK = (byte) 0x70;
public static final byte MACSEC_DESIRED_OFFSET = (byte) 0x06;
public static final byte MACSEC_CAPABILITY_MASK = (byte) 0x30;
public static final byte MACSEC_CAPABILITY_OFFSET = (byte) 0x04;
/**
* MKA Secure Channel Identifier.
*/
public static class SCI {
private byte[] address;
private short port;
public static final int SYSTEM_IDENTIFIER_LENGTH = 6;
public static final int PORT_OFFSET = 6;
/**
* Validate SCI has <MAC (6 bytes)><Port (2 bytes)> length.
*
* @param sci ,byte[]
* @return true ,boolean
*/
private boolean validateSCI(byte[] sci) {
if (sci != null && sci.length < EAPOLMkpduBasicParameterSet.FIELD_SCI_LENGTH) {
throw new IllegalArgumentException(
"Invalid SCI argument. Enough bytes are not provided."
);
}
return true;
}
/**
* Validate System Identifier.
*
* @param address , byte[]
* @return true , boolean
*/
private boolean validateAddress(byte[] address) {
if (address != null && address.length < SCI.SYSTEM_IDENTIFIER_LENGTH) {
throw new IllegalArgumentException(
"Invalid System Identifier argument. Expects 6 bytes eg. MAC address.");
}
return true;
}
/**
* To set SCI from MAC address and port stream.
*
* @param sci , type byte[]
*/
public SCI(byte[] sci) {
validateSCI(sci);
address = Arrays.copyOfRange(sci, 0, SYSTEM_IDENTIFIER_LENGTH);
port = (short) ((((short) (sci[PORT_OFFSET] & 0xFF)) << 8)
| ((short) (sci[PORT_OFFSET + 1] & 0xFF)));
}
/**
* To set SCI from MAC address and port number.
*
* @param address ,type byte[]
* @param port ,type short
* @throws IllegalArgumentException Exceptions
*/
public SCI(byte[] address, short port) throws IllegalArgumentException {
validateAddress(address);
this.address = address;
this.port = port;
}
/**
* To set address.
*
* @param address , type byte[]
* @throws IllegalArgumentException if address is not set
*/
public void setAdddress(byte[] address) throws IllegalArgumentException {
validateAddress(address);
this.address = address;
}
/**
* To return address.
*
* @return address , type byte[]
*/
public byte[] address() {
return address;
}
/**
* TO set Port.
*
* @param port , type short
*/
public void setPort(short port) {
this.port = port;
}
/**
* To return Port.
*
* @return port , type short
*/
public short port() {
return port;
}
/**
* Convert to byte array.
*
* @return bb.array() ,type byte[]
*/
public byte[] array() {
byte[] data = new byte[address.length + 2];
ByteBuffer bb = ByteBuffer.wrap(data);
bb.put(address);
bb.putShort(port);
return bb.array();
}
}
// Basic Parameter Set fixed header portion size.
public static final short BPS_FIXED_PART_SIZE_UPTO_LENGTH_FIELD = 4;
public static final short BPS_FIXED_PART_TOTAL_SIZE = 32;
/**
* To set MKA Version.
*
* @param version , type byte
*/
public void setMkaVersion(byte version) {
this.mkaVersion = version;
}
/**
* To get MKA Version.
*
* @return mkaVersion , type byte
*/
public byte getMkaVersion() {
return mkaVersion;
}
/**
* To set Key Server Priority.
*
* @param priority , type byte
*/
public void setKeyServerPriority(byte priority) {
this.keyServerPriority = priority;
}
/**
* To get Key Server Priority.
*
* @return keyServerPriority, type byte
*/
public byte getKeyServerPriority() {
return keyServerPriority;
}
/**
* To set Key Server.
*
* @param isKeyServer , type boolean
*/
public void setKeyServer(boolean isKeyServer) {
this.keyServer = isKeyServer;
}
/**
* To get Key Server.
*
* @return keyServer, type boolean
*/
public boolean getKeyServer() {
return keyServer;
}
/**
* To set MACSec Desired.
*
* @param desired , type boolean
*/
public void setMacSecDesired(boolean desired) {
this.macSecDesired = desired;
}
/**
* To get MACSec Desired.
*
* @return macSecDesired , type boolean
*/
public boolean getMacSecDesired() {
return macSecDesired;
}
/**
* To set MACSec Capacity.
*
* @param capability ,type byte
*/
public void setMacSecCapability(byte capability) {
this.capability = capability;
}
/**
* To get MACSec Capacity.
*
* @return capability, type byte
*/
public byte getMacSecCapacity() {
return capability;
}
/**
* To set body length.
*
* @param length , type short
*/
public void setBodyLength(short length) {
this.bodyLength = length;
}
public short getBodyLength() {
return bodyLength;
}
/**
* To set SCI.
*
* @param sci , byte[]
*/
public void setSci(byte[] sci) {
this.sci = new SCI(sci);
}
/**
* To set SCI.
*
* @param sci , SCI
*/
public void setSci(SCI sci) {
// TODO: Ensure sci valid.
this.sci = sci;
}
/**
* To get SCI.
*
* @return sci, type SCI
*/
public SCI getSci() {
return sci;
}
/**
* To set Member Identifier.
*
* @param mi , type byte[]
* @throws IllegalArgumentException if mi is not set.
*/
public void setActorMI(byte[] mi) throws IllegalArgumentException {
if (mi != null && mi.length < EAPOLMkpduParameterSet.FIELD_MI_LENGTH) {
throw new IllegalArgumentException("Actor Message Identifier doesn't have enough length.");
}
this.mi = mi;
}
/**
* To get Member Identifier.
*
* @return mi, type byte[]
*/
public byte[] getActorMI() {
return mi;
}
/**
* To set Member Identifier.
*
* @param mn , type byte[]
* @throws IllegalArgumentException if mn is not set.
*/
public void setActorMN(byte[] mn) throws IllegalArgumentException {
if (mn != null && mn.length < EAPOLMkpduParameterSet.FIELD_MN_LENGTH) {
throw new IllegalArgumentException("Actor Message Number doesn't have enough length.");
}
final ByteBuffer bf = ByteBuffer.wrap(mn);
this.mn = bf.getInt();
}
/**
* To set Member Identifier.
*
* @param mn , type int
*/
public void setActorMN(int mn) {
this.mn = mn;
}
/**
* To get Member Identifier.
*
* @return mn, type int
*/
public int getActorMN() {
return mn;
}
/**
* To set Algorithm Agility.
*
* @param algAgility , type byte[]
* @throws IllegalArgumentException if algAgility is not set or in incorrect format
*/
public void setAlgAgility(byte[] algAgility) throws IllegalArgumentException {
if (algAgility != null && algAgility.length < EAPOLMkpduBasicParameterSet.FIELD_ALGAG_LENGTH) {
throw new IllegalArgumentException("Algorithm Agility doesn't have enough length.");
}
this.algAgility = algAgility;
}
/**
* To get Algorithm Agility.
*
* @return algAgility, type byte[]
*/
public byte[] getAlgAgility() {
return algAgility;
}
/**
* To set CAK name.
*
* @param ckn , type byte[]
*/
public void setCKN(byte[] ckn) {
int cakNameLength = bodyLength - EAPOLMkpduBasicParameterSet.TOTAL_BPS_BODY_LENGTH;
if (ckn != null && ckn.length < cakNameLength) {
throw new IllegalArgumentException("CAK name doesn't have enough length.");
}
this.ckn = ckn;
}
/**
* To get CAK name.
*
* @return ckn , type byte[]
*/
public byte[] getCKN() {
return ckn;
}
/**
* To set padding.
*
* @param padding , type byte[]
*/
public void setPadding(byte[] padding) {
this.padding = padding;
}
/**
* Deserializer function for Basic Parameter Set.
*
* @return deserializer function
*/
public static Deserializer<EAPOLMkpduBasicParameterSet> deserializer() {
return (data, offset, length) -> {
// Ensure buffer has enough details.
if (length < TOTAL_BPS_BODY_LENGTH) {
return null;
}
// Various tools for deserialization.
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
EAPOLMkpduBasicParameterSet basicParameterSet = new EAPOLMkpduBasicParameterSet();
// Deserialize Basic Parameter Set fields.
basicParameterSet.setMkaVersion(bb.get());
basicParameterSet.setKeyServerPriority(bb.get());
byte[] mbField = new byte[1];
mbField[0] = bb.get();
basicParameterSet.setKeyServer(((mbField[0] & EAPOLMkpduBasicParameterSet.KEYSERVER_MASK) > 0) ?
true : false);
basicParameterSet.setMacSecDesired(((mbField[0]
& EAPOLMkpduBasicParameterSet.MACSEC_DESIRED_MASK) > 0) ?
true : false);
basicParameterSet.setMacSecCapability((byte) ((mbField[0]
& EAPOLMkpduBasicParameterSet.MACSEC_CAPABILITY_MASK)
>> EAPOLMkpduBasicParameterSet.MACSEC_CAPABILITY_OFFSET));
short bodyLength = (short) (((short) (mbField[0] & EAPOLMkpduParameterSet.BODY_LENGTH_MSB_MASK))
<< EAPOLMkpduBasicParameterSet.BODY_LENGTH_MSB_SHIFT);
bodyLength |= (short) (bb.get());
basicParameterSet.setBodyLength(bodyLength);
mbField = new byte[EAPOLMkpduParameterSet.FIELD_SCI_LENGTH];
bb.get(mbField, 0, EAPOLMkpduParameterSet.FIELD_SCI_LENGTH);
basicParameterSet.setSci(mbField);
mbField = new byte[EAPOLMkpduParameterSet.FIELD_MI_LENGTH];
bb.get(mbField, 0, EAPOLMkpduParameterSet.FIELD_MI_LENGTH);
basicParameterSet.setActorMI(mbField);
mbField = new byte[EAPOLMkpduParameterSet.FIELD_MN_LENGTH];
bb.get(mbField, 0, EAPOLMkpduParameterSet.FIELD_MN_LENGTH);
basicParameterSet.setActorMN(mbField);
mbField = new byte[EAPOLMkpduBasicParameterSet.FIELD_ALGAG_LENGTH];
bb.get(mbField, 0, EAPOLMkpduBasicParameterSet.FIELD_ALGAG_LENGTH);
basicParameterSet.setAlgAgility(mbField);
int cakNameLength = basicParameterSet.getBodyLength() + EAPOLMkpduParameterSet.BODY_LENGTH_OCTET_OFFSET -
EAPOLMkpduBasicParameterSet.TOTAL_BPS_BODY_LENGTH;
mbField = new byte[cakNameLength];
bb.get(mbField, 0, cakNameLength);
basicParameterSet.setCKN(mbField);
int padLength = basicParameterSet.getBodyLength() + EAPOLMkpduParameterSet.BODY_LENGTH_OCTET_OFFSET -
(EAPOLMkpduBasicParameterSet.TOTAL_BPS_BODY_LENGTH + cakNameLength);
if (padLength > 0) {
mbField = new byte[padLength];
bb.get(mbField, 0, padLength);
basicParameterSet.setPadding(mbField);
}
return basicParameterSet;
};
}
@Override
public byte[] serialize() {
short paddedLength = getTotalLength();
// Serialize Basic Parameter Set. IEEE 802.1x, Figure 11.8
ByteBuffer data = ByteBuffer.wrap(new byte[paddedLength]);
// Octet 1,2
data.put(mkaVersion);
data.put(keyServerPriority);
// Octet 3
byte octet3 = (byte) ((byte) ((keyServer) ? 0x01 : 0x00) << KEYSERVER_OFFSET);
octet3 |= (byte) ((byte) ((macSecDesired) ? 0x01 : 0x00) << MACSEC_DESIRED_OFFSET);
octet3 |= capability << MACSEC_CAPABILITY_OFFSET;
// Remove header length upto "Length" field from total packet length.
paddedLength -= BPS_FIXED_PART_SIZE_UPTO_LENGTH_FIELD;
octet3 |= (byte) (paddedLength >> BODY_LENGTH_MSB_SHIFT & BODY_LENGTH_MSB_MASK);
data.put(octet3);
// Octet 4
data.put((byte) paddedLength);
// Octet 5-12
data.put(sci.array());
// Octet 13-24
data.put(mi);
// Octet 25-28
data.putInt(mn);
// Octet 29-32
data.put(algAgility);
// Octet 33-
data.put(ckn);
// TODO: Filling Padding if needed.
return data.array();
}
@Override
public byte getParameterSetType() {
return PARAMETERSET_TYPE_BASIC;
}
@Override
public short getTotalLength() {
/*
* Total size calculation.
* 4 byte aligned padded length calculation.
* ie. padded_length = (length + 3) & ~3
*/
short paddedLength = (short) (((BPS_FIXED_PART_TOTAL_SIZE + ckn.length) + 0x03) & ~0x03);
return paddedLength;
}
}