2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.openflowplugin.applications.topology.lldp.utils;
10 import com.google.common.hash.HashCode;
11 import com.google.common.hash.HashFunction;
12 import com.google.common.hash.Hasher;
13 import com.google.common.hash.Hashing;
14 import java.lang.management.ManagementFactory;
15 import java.nio.ByteBuffer;
16 import java.nio.charset.Charset;
17 import java.security.NoSuchAlgorithmException;
18 import java.util.Arrays;
19 import java.util.Objects;
20 import org.apache.commons.lang3.ArrayUtils;
21 import org.opendaylight.openflowplugin.applications.topology.lldp.LLDPActivator;
22 import org.opendaylight.openflowplugin.libraries.liblldp.BitBufferHelper;
23 import org.opendaylight.openflowplugin.libraries.liblldp.CustomTLVKey;
24 import org.opendaylight.openflowplugin.libraries.liblldp.Ethernet;
25 import org.opendaylight.openflowplugin.libraries.liblldp.LLDP;
26 import org.opendaylight.openflowplugin.libraries.liblldp.LLDPTLV;
27 import org.opendaylight.openflowplugin.libraries.liblldp.NetUtils;
28 import org.opendaylight.openflowplugin.libraries.liblldp.PacketException;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 public final class LLDPDiscoveryUtils {
42 private static final Logger LOG = LoggerFactory.getLogger(LLDPDiscoveryUtils.class);
44 private static final short MINIMUM_LLDP_SIZE = 61;
45 public static final short ETHERNET_TYPE_VLAN = (short) 0x8100;
46 public static final short ETHERNET_TYPE_LLDP = (short) 0x88cc;
47 private static final short ETHERNET_TYPE_OFFSET = 12;
48 private static final short ETHERNET_VLAN_OFFSET = ETHERNET_TYPE_OFFSET + 4;
50 private LLDPDiscoveryUtils() {
53 public static String macToString(byte[] mac) {
54 StringBuilder builder = new StringBuilder();
55 for (int i = 0; i < mac.length; i++) {
56 builder.append(String.format("%02X%s", mac[i], i < mac.length - 1 ? ":" : ""));
59 return builder.toString();
63 * Returns the encoded in custom TLV for the given lldp.
65 * @param payload lldp payload
66 * @return nodeConnectorId - encoded in custom TLV of given lldp
67 * @see LLDPDiscoveryUtils#lldpToNodeConnectorRef(byte[], boolean)
69 public static NodeConnectorRef lldpToNodeConnectorRef(byte[] payload) {
70 return lldpToNodeConnectorRef(payload, false);
74 * Returns the encoded in custom TLV for the given lldp.
76 * @param payload lldp payload
77 * @param useExtraAuthenticatorCheck make it more secure (CVE-2015-1611 CVE-2015-1612)
78 * @return nodeConnectorId - encoded in custom TLV of given lldp
80 @SuppressWarnings("checkstyle:IllegalCatch")
81 public static NodeConnectorRef lldpToNodeConnectorRef(byte[] payload, boolean useExtraAuthenticatorCheck) {
82 NodeConnectorRef nodeConnectorRef = null;
84 if (isLLDP(payload)) {
85 Ethernet ethPkt = new Ethernet();
87 ethPkt.deserialize(payload, 0, payload.length * NetUtils.NUM_BITS_IN_A_BYTE);
88 } catch (PacketException e) {
89 LOG.warn("Failed to decode LLDP packet {}", e);
90 return nodeConnectorRef;
93 LLDP lldp = (LLDP) ethPkt.getPayload();
96 NodeId srcNodeId = null;
97 NodeConnectorId srcNodeConnectorId = null;
99 final LLDPTLV systemIdTLV = lldp.getSystemNameId();
100 if (systemIdTLV != null) {
101 String srcNodeIdString = new String(systemIdTLV.getValue(), Charset.defaultCharset());
102 srcNodeId = new NodeId(srcNodeIdString);
104 throw new Exception("Node id wasn't specified via systemNameId in LLDP packet.");
107 final LLDPTLV nodeConnectorIdLldptlv = lldp.getCustomTLV(new CustomTLVKey(
108 BitBufferHelper.getInt(LLDPTLV.OFOUI), LLDPTLV.CUSTOM_TLV_SUB_TYPE_NODE_CONNECTOR_ID[0]));
109 if (nodeConnectorIdLldptlv != null) {
110 srcNodeConnectorId = new NodeConnectorId(LLDPTLV.getCustomString(
111 nodeConnectorIdLldptlv.getValue(), nodeConnectorIdLldptlv.getLength()));
113 throw new Exception("Node connector wasn't specified via Custom TLV in LLDP packet.");
116 if (useExtraAuthenticatorCheck) {
117 boolean secure = checkExtraAuthenticator(lldp, srcNodeConnectorId);
119 LOG.warn("SECURITY ALERT: there is probably a LLDP spoofing attack in progress.");
121 "Attack. LLDP packet with inconsistent extra authenticator field was received.");
125 InstanceIdentifier<NodeConnector> srcInstanceId = InstanceIdentifier.builder(Nodes.class)
126 .child(Node.class, new NodeKey(srcNodeId))
127 .child(NodeConnector.class, new NodeConnectorKey(srcNodeConnectorId))
129 nodeConnectorRef = new NodeConnectorRef(srcInstanceId);
130 } catch (Exception e) {
131 LOG.debug("Caught exception while parsing out lldp optional and custom fields", e);
134 return nodeConnectorRef;
138 * Gets an extra authenticator for lldp security.
140 * @param nodeConnectorId the NodeConnectorId
141 * @return extra authenticator for lldp security
143 public static byte[] getValueForLLDPPacketIntegrityEnsuring(final NodeConnectorId nodeConnectorId)
144 throws NoSuchAlgorithmException {
146 if (LLDPActivator.getLldpSecureKey() != null && !LLDPActivator.getLldpSecureKey().isEmpty()) {
147 finalKey = LLDPActivator.getLldpSecureKey();
149 finalKey = ManagementFactory.getRuntimeMXBean().getName();
151 final String pureValue = nodeConnectorId + finalKey;
153 final byte[] pureBytes = pureValue.getBytes();
154 HashFunction hashFunction = Hashing.md5();
155 Hasher hasher = hashFunction.newHasher();
156 HashCode hashedValue = hasher.putBytes(pureBytes).hash();
157 return hashedValue.asBytes();
160 private static boolean checkExtraAuthenticator(LLDP lldp, NodeConnectorId srcNodeConnectorId)
161 throws NoSuchAlgorithmException {
162 final LLDPTLV hashLldptlv = lldp.getCustomTLV(
163 new CustomTLVKey(BitBufferHelper.getInt(LLDPTLV.OFOUI), LLDPTLV.CUSTOM_TLV_SUB_TYPE_CUSTOM_SEC[0]));
164 boolean secAuthenticatorOk = false;
165 if (hashLldptlv != null) {
166 byte[] rawTlvValue = hashLldptlv.getValue();
167 byte[] lldpCustomSecurityHash = ArrayUtils.subarray(rawTlvValue, 4, rawTlvValue.length);
168 byte[] calculatedHash = getValueForLLDPPacketIntegrityEnsuring(srcNodeConnectorId);
169 secAuthenticatorOk = Arrays.equals(calculatedHash, lldpCustomSecurityHash);
171 LOG.debug("Custom security hint wasn't specified via Custom TLV in LLDP packet.");
174 return secAuthenticatorOk;
177 private static boolean isLLDP(final byte[] packet) {
178 if (Objects.isNull(packet) || packet.length < MINIMUM_LLDP_SIZE) {
182 final ByteBuffer bb = ByteBuffer.wrap(packet);
184 short ethernetType = bb.getShort(ETHERNET_TYPE_OFFSET);
186 if (ethernetType == ETHERNET_TYPE_VLAN) {
187 ethernetType = bb.getShort(ETHERNET_VLAN_OFFSET);
190 return ethernetType == ETHERNET_TYPE_LLDP;