blob: b76bc9bb1327d7c3981b2a735a3433a95f4f45a8 [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.LinkedHashMap;
import java.util.Map;
/**
* EAPOL MKA (EAPOL MAC Key Agreement Protocol) header.
*/
public class EAPOLMkpdu extends BasePacket {
// Parameter Sets.
private Map<Byte, IPacket> parameterSets = new LinkedHashMap<>();
/*
* Parameter Serialization Order.
* IEEE 802.1x Clause 11.11.3.
*/
private static byte[] parametersetSerializerKeyList = new byte[]{
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC,
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST,
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_POTENTIAL_PEER_LIST,
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_MACSEC_SAK_USE,
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_DISTRIBUTED_SAK,
// TODO: Fill other types.
EAPOLMkpduParameterSet.PARAMETERSET_TYPE_ICV_INDICATOR
};
// Various Parameter Set Deserializers.
private static final Map<Byte, Deserializer<? extends IPacket>> PARAMETERSET_DESERIALIZER_MAP =
new LinkedHashMap<>();
static {
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC,
EAPOLMkpduBasicParameterSet.deserializer());
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST,
EAPOLMkpduPeerListParameterSet.deserializer());
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_POTENTIAL_PEER_LIST,
EAPOLMkpduPeerListParameterSet.deserializer());
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_MACSEC_SAK_USE,
EAPOLMkpduMACSecUseParameterSet.deserializer());
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_DISTRIBUTED_SAK,
EAPOLMkpduDistributedSAKParameterSet.deserializer());
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.put(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_ICV_INDICATOR,
EAPOLMkpduICVIndicatorParameterSet.deserializer());
}
@Override
public byte[] serialize() {
int payloadLength = packetLength();
ByteBuffer payload = ByteBuffer.wrap(new byte[payloadLength]);
//Serialize Parameter Sets.
for (byte b : parametersetSerializerKeyList) {
IPacket packet = parameterSets.get(b);
if (packet != null) {
byte[] data = packet.serialize();
if (data != null) {
payload.put(data);
}
}
}
return payload.array();
}
/**
* Static deserializer for EAPOL-MKA packets.
*
* @return deserializer function
*/
public static Deserializer<EAPOLMkpdu> deserializer() {
return (data, offset, length) -> {
byte parameterSetType;
final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
EAPOLMkpdu mkpdu = new EAPOLMkpdu();
/* Extract Basic ParameterSet;
Special care needed, MKA Version & Peer Type difficult to distinguish. */
Deserializer<? extends IPacket> psDeserializer =
EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.get(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC);
EAPOLMkpduParameterSet ps = (EAPOLMkpduParameterSet) (psDeserializer.deserialize(bb.array(),
bb.position(), bb.remaining()));
if (!mkpdu.addParameterSet(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC, ps)) {
throw new DeserializationException("Error in deserializing packets");
}
// Update buffer position.
bb.position(bb.position() + ps.getTotalLength());
// Extract various remaining Parameter Sets.
while (bb.hasRemaining()) {
parameterSetType = bb.get();
psDeserializer = EAPOLMkpdu.PARAMETERSET_DESERIALIZER_MAP.get(parameterSetType);
ps = (EAPOLMkpduParameterSet) (psDeserializer.deserialize(bb.array(), bb.position(),
bb.remaining()));
// Specially handle Peer List Parameter Sets .
if ((parameterSetType == EAPOLMkpduParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST) ||
(parameterSetType == EAPOLMkpduParameterSet.PARAMETERSET_TYPE_POTENTIAL_PEER_LIST)) {
EAPOLMkpduPeerListParameterSet peerList =
(EAPOLMkpduPeerListParameterSet) ps;
peerList.setPeerListType(parameterSetType);
}
if (!mkpdu.addParameterSet(parameterSetType, ps)) {
throw new DeserializationException("Error in deserializing packets");
}
// Update buffer.
short consumed = ps.getTotalLength();
short remaining = (short) bb.remaining();
// Already one byte shifted, only "consumed-1" is to be shifted.
bb.position(bb.position() + ((remaining > consumed) ? (consumed - 1) : remaining));
}
return mkpdu;
};
}
/**
* Populate various Parameter Sets to store.
*
* @param type short
* @param ps EAPOLMkpduParameterSet
* @return boolean
*/
public boolean addParameterSet(short type, EAPOLMkpduParameterSet ps) {
if (ps == null) {
return false;
}
// Ensure type is valid.
if (!((EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC <= type &&
type <= EAPOLMkpduParameterSet.PARAMETERSET_TYPE_DISTRIBUTED_SAK) ||
(type == EAPOLMkpduParameterSet.PARAMETERSET_TYPE_ICV_INDICATOR))) {
return false;
}
// Update store.
parameterSets.put((byte) type, (IPacket) ps);
return true;
}
/**
* Provide Basic Parameter Set details.
*
* @return EAPOLMkpduBasicParameterSet
*/
public EAPOLMkpduBasicParameterSet getBasicParameterSet() {
IPacket parameterSet = null;
if (parameterSets.containsKey(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC)) {
parameterSet = parameterSets.get(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_BASIC);
}
return (EAPOLMkpduBasicParameterSet) parameterSet;
}
/**
* Provide Live/Potential Peer List details.
*
* @return EAPOLMkpduPeerListParameterSet
*/
public EAPOLMkpduPeerListParameterSet getPeerListParameterSet() {
IPacket parameterSet;
if (parameterSets.containsKey(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST)) {
parameterSet = parameterSets.get(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_LIVE_PEER_LIST);
} else {
parameterSet = parameterSets.get(EAPOLMkpduParameterSet.PARAMETERSET_TYPE_POTENTIAL_PEER_LIST);
}
return (EAPOLMkpduPeerListParameterSet) parameterSet;
}
/**
* Total EAPOL-MKPDU packet length. Cumulative length of Parameter Sets.
*
* @return length
*/
public short packetLength() {
short length = 0;
for (byte k : parameterSets.keySet()) {
EAPOLMkpduParameterSet p = (EAPOLMkpduParameterSet) parameterSets.get(k);
length += p.getTotalLength();
}
return length;
}
/**
* Retrieve Parameter Set based on type.
*
* @param type byte
* @return EAPOLMkpduParameterSet
*/
public EAPOLMkpduParameterSet getParameterSet(byte type) {
EAPOLMkpduParameterSet ps = null;
Map.Entry<Byte, IPacket> entry = parameterSets.entrySet().stream()
.filter((i) -> {
return i.getKey().equals(new Byte(type));
})
.findFirst()
.orElse(null);
if (entry != null) {
ps = (EAPOLMkpduParameterSet) entry.getValue();
}
return ps;
}
}