blob: 7978d37b92af416eaf50564934cd2f01a1e45d16 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
alshabibc4901cd2014-09-05 16:50:40 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
alshabibc4901cd2014-09-05 16:50:40 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska24c849c2014-10-27 09:53:05 -070015 */
alshabibc4901cd2014-09-05 16:50:40 -070016package org.onlab.packet;
17
18import java.nio.ByteBuffer;
Ray Milkey241b96a2014-11-17 13:08:20 -080019import java.nio.charset.StandardCharsets;
alshabibc4901cd2014-09-05 16:50:40 -070020import java.util.Arrays;
21import java.util.LinkedList;
22import java.util.List;
23
24import org.apache.commons.lang.ArrayUtils;
alshabibdf652ad2014-09-09 11:53:19 -070025import org.slf4j.Logger;
26import org.slf4j.LoggerFactory;
alshabibc4901cd2014-09-05 16:50:40 -070027
28
29/**
30 * LLDP packets OpenVirteX uses for discovery of physical network topology.
31 * Refer to IEEE Std 802.1ABTM-2009 for more information.
32 *
33 */
alshabib7911a052014-10-16 17:49:37 -070034@Deprecated
alshabib289652c2014-09-07 19:09:28 -070035public class ONLabLddp extends LLDP {
alshabibc4901cd2014-09-05 16:50:40 -070036
alshabib7235d092014-09-09 16:46:27 -070037 private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
alshabibc4901cd2014-09-05 16:50:40 -070038 // ON.Lab OUI and OVX name for organizationally specific TLVs
39 public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
40 public static final String OVX_NAME = "OpenVirteX";
41 public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
42 0x01};
43 public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
44 (byte) 0xc2, 0x00, 0x00, 0x0e};
45 public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
46 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
47 public static final short ETHERTYPE_VLAN = (short) 0x8100;
48
49 // TLV constants: type, size and subtype
50 // Organizationally specific TLV also have packet offset and contents of TLV
51 // header
52 private static final byte CHASSIS_TLV_TYPE = 1;
53 private static final byte CHASSIS_TLV_SIZE = 7;
54 private static final byte CHASSIS_TLV_SUBTYPE = 4;
55
56 private static final byte PORT_TLV_TYPE = 2;
alshabibdf652ad2014-09-09 11:53:19 -070057 private static final byte PORT_TLV_SIZE = 5;
alshabibc4901cd2014-09-05 16:50:40 -070058 private static final byte PORT_TLV_SUBTYPE = 2;
59
60 private static final byte TTL_TLV_TYPE = 3;
61 private static final byte TTL_TLV_SIZE = 2;
62
63 private static final byte NAME_TLV_TYPE = 127;
64 // 4 = OUI (3) + subtype (1)
alshabib289652c2014-09-07 19:09:28 -070065 private static final byte NAME_TLV_SIZE = (byte) (4 + ONLabLddp.OVX_NAME.length());
alshabibc4901cd2014-09-05 16:50:40 -070066 private static final byte NAME_TLV_SUBTYPE = 1;
alshabibdf652ad2014-09-09 11:53:19 -070067 private static final short NAME_TLV_OFFSET = 34;
alshabibc4901cd2014-09-05 16:50:40 -070068 private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
69 // Contents of full name TLV
70 private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
71 .putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
Ray Milkey241b96a2014-11-17 13:08:20 -080072 .put(OVX_NAME.getBytes(StandardCharsets.UTF_8)).array();
alshabibc4901cd2014-09-05 16:50:40 -070073
74 private static final byte DPID_TLV_TYPE = 127;
75 private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
76 // (1) + dpid (8)
77 private static final byte DPID_TLV_SUBTYPE = 2;
78 private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
79 // Contents of dpid TLV
80 // Note that this does *not* contain the actual dpid since we cannot match
81 // on it
82 private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
83 .putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
84 .array();
85
86 // Pre-built contents of both organizationally specific TLVs
87 private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
88
89 // Default switch, port number and TTL
90 private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00 };
alshabibdf652ad2014-09-09 11:53:19 -070092 private static final int DEFAULT_PORT = 0;
alshabibc4901cd2014-09-05 16:50:40 -070093 private static final short DEFAULT_TTL = 120; // in seconds
94
95 // Minimum and OVX-generated LLDP packet sizes
96 private static final short MINIMUM_LLDP_SIZE = 61;
97 // Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
98 private static final short OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
99 + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
100
101 // Field offsets in OVX-generated LLDP
102 private static final short ETHERTYPE_OFFSET = 12;
103 private static final short PORT_OFFSET = 26;
alshabibdf652ad2014-09-09 11:53:19 -0700104 private static final short DPID_OFFSET = 56;
alshabibc4901cd2014-09-05 16:50:40 -0700105
106 // Private member fields
107 // Byte arrays for TLV information string
108 private ByteBuffer bb;
109 private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
110 private final byte[] portId = new byte[PORT_TLV_SIZE];
111 private final byte[] ttl = new byte[TTL_TLV_SIZE];
112 private final byte[] ouiName = new byte[NAME_TLV_SIZE];
113 private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
114
115 // TLVs
116 private final LLDPTLV chassisTLV;
117 private final LLDPTLV portTLV;
118 private final LLDPTLV ttlTLV;
119 private final LLDPTLV ouiNameTLV;
120 private final LLDPTLV ouiDpidTLV;
121 private final List<LLDPTLV> optionalTLVList;
122
123 /**
124 * Instantiates a new OVX LDDP message.
125 */
alshabib289652c2014-09-07 19:09:28 -0700126 public ONLabLddp() {
alshabibc4901cd2014-09-05 16:50:40 -0700127 // Create TLVs
128 this.chassisTLV = new LLDPTLV();
129 this.portTLV = new LLDPTLV();
130 this.ttlTLV = new LLDPTLV();
131 this.ouiNameTLV = new LLDPTLV();
132 this.ouiDpidTLV = new LLDPTLV();
133 this.optionalTLVList = new LinkedList<LLDPTLV>();
134 this.optionalTLVList.add(this.ouiNameTLV);
135 this.optionalTLVList.add(this.ouiDpidTLV);
136
137 // Add TLVs to LLDP packet
138 this.setChassisId(this.chassisTLV);
139 this.setPortId(this.portTLV);
140 this.setTtl(this.ttlTLV);
141 this.setOptionalTLVList(this.optionalTLVList);
142
143 // Set TLVs to default values
144 this.setChassisTLV(DEFAULT_DPID);
145 this.setPortTLV(DEFAULT_PORT);
146 this.setTTLTLV(DEFAULT_TTL);
alshabib289652c2014-09-07 19:09:28 -0700147 this.setOUIName(ONLabLddp.OVX_NAME);
alshabibc4901cd2014-09-05 16:50:40 -0700148 this.setOUIDpid(DEFAULT_DPID);
149 }
150
151 /**
152 * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
153 * we use another organizationally specific TLV to put the full dpid (see
154 * setOUIDpid()).
155 *
156 * @param dpid the switch DPID
157 */
158 private void setChassisTLV(final byte[] dpid) {
159 this.bb = ByteBuffer.wrap(this.chassisId);
160 this.bb.put(CHASSIS_TLV_SUBTYPE);
161 for (int i = 2; i < 8; i++) {
162 bb.put(dpid[i]);
163 }
164 this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
165 this.chassisTLV.setType(CHASSIS_TLV_TYPE);
166 this.chassisTLV.setValue(this.chassisId);
167 }
168
169 /**
170 * Sets port TLV.
171 *
172 * @param portNumber the port number
173 */
alshabibdf652ad2014-09-09 11:53:19 -0700174 private void setPortTLV(final int portNumber) {
alshabibc4901cd2014-09-05 16:50:40 -0700175 this.bb = ByteBuffer.wrap(this.portId);
176 this.bb.put(PORT_TLV_SUBTYPE);
alshabibdf652ad2014-09-09 11:53:19 -0700177 this.bb.putInt(portNumber);
alshabibc4901cd2014-09-05 16:50:40 -0700178
179 this.portTLV.setLength(PORT_TLV_SIZE);
180 this.portTLV.setType(PORT_TLV_TYPE);
181 this.portTLV.setValue(this.portId);
182 }
183
184 /**
185 * Sets Time To Live TLV.
186 *
187 * @param time the time to live
188 */
189 private void setTTLTLV(final short time) {
190 this.bb = ByteBuffer.wrap(this.ttl);
191 this.bb.putShort(time);
192
193 this.ttlTLV.setLength(TTL_TLV_SIZE);
194 this.ttlTLV.setType(TTL_TLV_TYPE);
195 this.ttlTLV.setValue(this.ttl);
196 }
197
198 /**
199 * Set. organizationally specific TLV for OVX name (subtype 1).
200 *
201 * @param name the name
202 */
203 private void setOUIName(final String name) {
204 this.bb = ByteBuffer.wrap(ouiName);
alshabib289652c2014-09-07 19:09:28 -0700205 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700206 this.bb.put(NAME_TLV_SUBTYPE);
Ray Milkey241b96a2014-11-17 13:08:20 -0800207 this.bb.put(name.getBytes(StandardCharsets.UTF_8));
alshabibc4901cd2014-09-05 16:50:40 -0700208
209 this.ouiNameTLV.setLength(NAME_TLV_SIZE);
210 this.ouiNameTLV.setType(NAME_TLV_TYPE);
211 this.ouiNameTLV.setValue(ouiName);
212 }
213
214 /**
215 * Sets organizationally specific TLV for OVX full dpid (subtype 2).
216 *
217 * @param dpid the switch DPID
218 */
219 private void setOUIDpid(final byte[] dpid) {
220 this.bb = ByteBuffer.wrap(ouiDpid);
alshabib289652c2014-09-07 19:09:28 -0700221 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700222 this.bb.put(DPID_TLV_SUBTYPE);
223 this.bb.put(dpid);
224
225 this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
226 this.ouiDpidTLV.setType(DPID_TLV_TYPE);
227 this.ouiDpidTLV.setValue(ouiDpid);
228 }
229
230 /**
231 * Sets switch DPID in LLDP packet.
232 *
tom5f18cf32014-09-13 14:10:57 -0700233 * @param dp the switch instance
alshabibc4901cd2014-09-05 16:50:40 -0700234 */
235 public void setSwitch(long dp) {
236 final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
237 .array();
238 this.setChassisTLV(dpid);
239 this.setOUIDpid(dpid);
240 }
241
242 /**
243 * Sets port in LLDP packet.
244 *
245 * @param port the port instance
246 */
alshabibdf652ad2014-09-09 11:53:19 -0700247 public void setPort(int port) {
tom5f18cf32014-09-13 14:10:57 -0700248 this.setPortTLV(port);
alshabibc4901cd2014-09-05 16:50:40 -0700249 }
250
251 /**
252 * Serializes full LLDP packet to byte array. Need to set both switch and
253 * port before you can serialize.
254 */
255 @Override
256 public byte[] serialize() {
257 return super.serialize();
258 }
259
260 /**
261 * Checks if LLDP packet has correct size, LLDP multicast address, and
262 * ethertype. Packet assumed to have Ethernet header.
263 *
tom5f18cf32014-09-13 14:10:57 -0700264 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700265 * @return true if packet is LLDP, false otherwise
266 */
267 public static boolean isLLDP(final byte[] packet) {
268 // Does packet exist and does it have the mininum size?
269 if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
270 return false;
271 }
272
273 // Packet has LLDP multicast destination address?
274 final ByteBuffer bb = ByteBuffer.wrap(packet);
275 final byte[] dst = new byte[6];
276 bb.get(dst);
277
alshabib289652c2014-09-07 19:09:28 -0700278 if (!(Arrays.equals(dst, ONLabLddp.LLDP_NICIRA)
279 || Arrays.equals(dst, ONLabLddp.LLDP_MULTICAST) || Arrays.equals(
280 dst, ONLabLddp.BDDP_MULTICAST))) {
alshabibc4901cd2014-09-05 16:50:40 -0700281
282 return false;
283 }
284
285 // Fetch ethertype, skip VLAN tag if it's there
286 short etherType = bb.getShort(ETHERTYPE_OFFSET);
287 if (etherType == ETHERTYPE_VLAN) {
288 etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
289 }
290
291 // Check ethertype
292 if (etherType == Ethernet.TYPE_LLDP) {
293 return true;
294 }
295 if (etherType == Ethernet.TYPE_BSN) {
296 return true;
297 }
298
299 return false;
300
301 }
302
303 /**
304 * Checks if packet has size of OVX-generated LLDP, and correctness of two
305 * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
306 * valid LLDP packet
307 *
tom5f18cf32014-09-13 14:10:57 -0700308 * @param packet packet data
309 * @return eth type or -1
alshabibc4901cd2014-09-05 16:50:40 -0700310 */
alshabib9ee68172014-09-09 14:45:14 -0700311 public static short isOVXLLDP(byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700312 if (packet.length < OVX_LLDP_SIZE) {
alshabib9ee68172014-09-09 14:45:14 -0700313 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700314 }
315
316 // Extra offset due to VLAN tag
317 final ByteBuffer bb = ByteBuffer.wrap(packet);
318 int offset = 0;
alshabib9ee68172014-09-09 14:45:14 -0700319 short ethType = bb.getShort(ETHERTYPE_OFFSET);
320 if (ethType != Ethernet.TYPE_LLDP
321 && ethType != Ethernet.TYPE_BSN) {
alshabibc4901cd2014-09-05 16:50:40 -0700322 offset = 4;
alshabib9ee68172014-09-09 14:45:14 -0700323 ethType = bb.getShort(ETHERTYPE_OFFSET + offset);
324 if (ethType != Ethernet.TYPE_LLDP
325 && ethType != Ethernet.TYPE_BSN) {
326 return -1;
327 }
alshabibc4901cd2014-09-05 16:50:40 -0700328 }
329
330 // Compare packet's organizationally specific TLVs to the expected
331 // values
332 for (int i = 0; i < OUI_TLV.length; i++) {
333 if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
alshabib9ee68172014-09-09 14:45:14 -0700334 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700335 }
336 }
337
alshabib9ee68172014-09-09 14:45:14 -0700338 return ethType;
alshabibc4901cd2014-09-05 16:50:40 -0700339 }
340
341 /**
342 * Extracts dpid and port from OVX-generated LLDP packet.
343 *
tom5f18cf32014-09-13 14:10:57 -0700344 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700345 * @return Dpid and port
346 */
alshabibdf652ad2014-09-09 11:53:19 -0700347 public static DPIDandPort parseLLDP(final byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700348 final ByteBuffer bb = ByteBuffer.wrap(packet);
349
350 // Extra offset due to VLAN tag
351 int offset = 0;
352 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
353 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
354 offset = 4;
355 }
356
alshabibdf652ad2014-09-09 11:53:19 -0700357 final int port = bb.getInt(PORT_OFFSET + offset);
alshabibc4901cd2014-09-05 16:50:40 -0700358 final long dpid = bb.getLong(DPID_OFFSET + offset);
359
alshabibdf652ad2014-09-09 11:53:19 -0700360 return new DPIDandPort(dpid, port);
alshabibc4901cd2014-09-05 16:50:40 -0700361 }
alshabibdf652ad2014-09-09 11:53:19 -0700362
363 public static class DPIDandPort {
364
365 private final long dpid;
366 private final int port;
367
368 public DPIDandPort(long dpid, int port) {
369 this.dpid = dpid;
370 this.port = port;
371 }
372
373 public long getDpid() {
374 return this.dpid;
375 }
376
377 public int getPort() {
378 return this.port;
379 }
380
381 }
382
alshabibc4901cd2014-09-05 16:50:40 -0700383}