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.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
41 public 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 public static String macToString(byte[] mac) {
51 StringBuilder b = new StringBuilder();
52 for (int i = 0; i < mac.length; i++) {
53 b.append(String.format("%02X%s", mac[i], i < mac.length - 1 ? ":" : ""));
61 * @return nodeConnectorId - encoded in custom TLV of given lldp
62 * @see LLDPDiscoveryUtils#lldpToNodeConnectorRef(byte[], boolean)
64 public static NodeConnectorRef lldpToNodeConnectorRef(byte[] payload) {
65 return lldpToNodeConnectorRef(payload, false);
70 * @param useExtraAuthenticatorCheck make it more secure (CVE-2015-1611 CVE-2015-1612)
71 * @return nodeConnectorId - encoded in custom TLV of given lldp
73 public static NodeConnectorRef lldpToNodeConnectorRef(byte[] payload, boolean useExtraAuthenticatorCheck) {
74 NodeConnectorRef nodeConnectorRef = null;
76 if (isLLDP(payload)) {
77 Ethernet ethPkt = new Ethernet();
79 ethPkt.deserialize(payload, 0, payload.length * NetUtils.NUM_BITS_IN_A_BYTE);
80 } catch (Exception e) {
81 LOG.warn("Failed to decode LLDP packet {}", e);
82 return nodeConnectorRef;
85 LLDP lldp = (LLDP) ethPkt.getPayload();
88 NodeId srcNodeId = null;
89 NodeConnectorId srcNodeConnectorId = null;
91 final LLDPTLV systemIdTLV = lldp.getSystemNameId();
92 if (systemIdTLV != null) {
93 String srcNodeIdString = new String(systemIdTLV.getValue(), Charset.defaultCharset());
94 srcNodeId = new NodeId(srcNodeIdString);
96 throw new Exception("Node id wasn't specified via systemNameId in LLDP packet.");
99 final LLDPTLV nodeConnectorIdLldptlv = lldp.getCustomTLV(
100 new CustomTLVKey(BitBufferHelper.getInt(LLDPTLV.OFOUI), LLDPTLV.CUSTOM_TLV_SUB_TYPE_NODE_CONNECTOR_ID[0]));
101 if (nodeConnectorIdLldptlv != null) {
102 srcNodeConnectorId = new NodeConnectorId(LLDPTLV.getCustomString(
103 nodeConnectorIdLldptlv.getValue(), nodeConnectorIdLldptlv.getLength()));
105 throw new Exception("Node connector wasn't specified via Custom TLV in LLDP packet.");
108 if (useExtraAuthenticatorCheck) {
109 boolean secure = checkExtraAuthenticator(lldp, srcNodeConnectorId);
111 LOG.warn("SECURITY ALERT: there is probably a LLDP spoofing attack in progress.");
112 throw new Exception("Attack. LLDP packet with inconsistent extra authenticator field was received.");
116 InstanceIdentifier<NodeConnector> srcInstanceId = InstanceIdentifier.builder(Nodes.class)
117 .child(Node.class, new NodeKey(srcNodeId))
118 .child(NodeConnector.class, new NodeConnectorKey(srcNodeConnectorId))
120 nodeConnectorRef = new NodeConnectorRef(srcInstanceId);
121 } catch (Exception e) {
122 LOG.debug("Caught exception while parsing out lldp optional and custom fields: {}", e.getMessage(), e);
125 return nodeConnectorRef;
129 * @param nodeConnectorId
130 * @return extra authenticator for lldp security
131 * @throws NoSuchAlgorithmException
133 public static byte[] getValueForLLDPPacketIntegrityEnsuring(final NodeConnectorId nodeConnectorId) throws NoSuchAlgorithmException {
135 if(LLDPActivator.getLldpSecureKey() !=null && !LLDPActivator.getLldpSecureKey().isEmpty()) {
136 finalKey = LLDPActivator.getLldpSecureKey();
138 finalKey = ManagementFactory.getRuntimeMXBean().getName();
140 final String pureValue = nodeConnectorId + finalKey;
142 final byte[] pureBytes = pureValue.getBytes();
143 HashFunction hashFunction = Hashing.md5();
144 Hasher hasher = hashFunction.newHasher();
145 HashCode hashedValue = hasher.putBytes(pureBytes).hash();
146 return hashedValue.asBytes();
151 * @param srcNodeConnectorId
152 * @throws NoSuchAlgorithmException
154 private static boolean checkExtraAuthenticator(LLDP lldp, NodeConnectorId srcNodeConnectorId) throws NoSuchAlgorithmException {
155 final LLDPTLV hashLldptlv = lldp.getCustomTLV(
156 new CustomTLVKey(BitBufferHelper.getInt(LLDPTLV.OFOUI), LLDPTLV.CUSTOM_TLV_SUB_TYPE_CUSTOM_SEC[0]));
157 boolean secAuthenticatorOk = false;
158 if (hashLldptlv != null) {
159 byte[] rawTlvValue = hashLldptlv.getValue();
160 byte[] lldpCustomSecurityHash = ArrayUtils.subarray(rawTlvValue, 4, rawTlvValue.length);
161 byte[] calculatedHash = getValueForLLDPPacketIntegrityEnsuring(srcNodeConnectorId);
162 secAuthenticatorOk = Arrays.equals(calculatedHash, lldpCustomSecurityHash);
164 LOG.debug("Custom security hint wasn't specified via Custom TLV in LLDP packet.");
167 return secAuthenticatorOk;
170 private static boolean isLLDP(final byte[] packet) {
171 if (Objects.isNull(packet) || packet.length < MINIMUM_LLDP_SIZE) {
175 final ByteBuffer bb = ByteBuffer.wrap(packet);
177 short ethernetType = bb.getShort(ETHERNET_TYPE_OFFSET);
179 if (ethernetType == ETHERNET_TYPE_VLAN) {
180 ethernetType = bb.getShort(ETHERNET_VLAN_OFFSET);
183 return ethernetType == ETHERNET_TYPE_LLDP;