IEEE 802.1x EAPOL-MKA packet support.
Change-Id: I61bc45226d5b84445d4fafa969150c4a707ab3bc
diff --git a/utils/misc/src/main/java/org/onlab/packet/EAPOLMkpdu.java b/utils/misc/src/main/java/org/onlab/packet/EAPOLMkpdu.java
new file mode 100644
index 0000000..fb67401
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/packet/EAPOLMkpdu.java
@@ -0,0 +1,226 @@
+
+/*
+ * 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.
+ protected Map<Byte, IPacket> parameterSets = new LinkedHashMap<>();
+
+ /*
+ * Parameter Serialization Order.
+ * IEEE 802.1x Clause 11.11.3.
+ */
+ public 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.
+
+ public 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;
+ }
+
+}
+