blob: 0b7d8fbcebc7604bca558b55f6d840f518a759a6 [file] [log] [blame]
Jonathan Hart299d1132014-06-27 09:25:28 -07001/*******************************************************************************
2 * Copyright 2014 Open Networking Laboratory
3 *
4 * 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
7 *
8 * 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.
15 ******************************************************************************/
16package net.onrc.onos.core.packet;
17
18import java.nio.ByteBuffer;
19import java.util.Arrays;
20import java.util.LinkedList;
21import java.util.List;
22
23import net.onrc.onos.core.util.SwitchPort;
24
Yuta HIGUCHI498e1532014-08-20 21:30:28 -070025import org.apache.commons.lang3.ArrayUtils;
Jonathan Hart299d1132014-06-27 09:25:28 -070026
27import com.google.common.base.Charsets;
28
29/**
30 * LLDP packets ONOS uses for discovery of physical network topology.
31 * Refer to IEEE Std 802.1ABTM-2009 for more information.
32 *
33 */
34public class OnosLldp extends LLDP {
35
36 // ON.Lab OUI and ONOS name for organizationally specific TLVs
37 static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
38 public static final String ONOS_NAME = "ONOSVirteX";
39 static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
40 0x01};
41 static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
42 (byte) 0xc2, 0x00, 0x00, 0x0e};
43 static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
44 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
45 public static final short ETHERTYPE_VLAN = (short) 0x8100;
46
47 // TLV constants: type, size and subtype
48 // Organizationally specific TLV also have packet offset and contents of TLV
49 // header
50 private static final byte CHASSIS_TLV_TYPE = 1;
51 private static final byte CHASSIS_TLV_SIZE = 7;
52 private static final byte CHASSIS_TLV_SUBTYPE = 4;
53
54 private static final byte PORT_TLV_TYPE = 2;
55 private static final byte PORT_TLV_SIZE = 3;
56 private static final byte PORT_TLV_SUBTYPE = 2;
57
58 private static final byte TTL_TLV_TYPE = 3;
59 private static final byte TTL_TLV_SIZE = 2;
60
61 private static final byte NAME_TLV_TYPE = 127;
62 // 4 = OUI (3) + subtype (1)
63 private static final byte NAME_TLV_SIZE = (byte) (4 + OnosLldp.ONOS_NAME.length());
64 private static final byte NAME_TLV_SUBTYPE = 1;
65 private static final short NAME_TLV_OFFSET = 32;
66 private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | (NAME_TLV_SIZE & 0xff));
67 // Contents of full name TLV
68 private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
69 .putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
70 .put(ONOS_NAME.getBytes(Charsets.UTF_8)).array();
71
72 private static final byte DPID_TLV_TYPE = 127;
73 private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
74 // (1) + dpid (8)
75 private static final byte DPID_TLV_SUBTYPE = 2;
76 private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
77 // Contents of dpid TLV
78 // Note that this does *not* contain the actual dpid since we cannot match
79 // on it
80 private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
81 .putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
82 .array();
83
84 // Pre-built contents of both organizationally specific TLVs
85 private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
86
87 // Default switch, port number and TTL
88 private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00 };
90 private static final short DEFAULT_PORT = 0;
91 private static final short DEFAULT_TTL = 120; // in seconds
92
93 // Minimum and ONOS-generated LLDP packet sizes
94 private static final short MINIMUM_LLDP_SIZE = 61;
95 // Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
96 private static final short ONOS_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
97 + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
98
99 // Direction TLVs are used to indicate if the LLDPs were sent
100 // periodically or in response to a received LLDP
101 private static final byte TLV_DIRECTION_TYPE = 0x73;
102 private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte
103 private static final byte[] TLV_DIRECTION_VALUE_FORWARD = {0x01};
104 private static final byte[] TLV_DIRECTION_VALUE_REVERSE = {0x02};
105 private static final LLDPTLV FORWARD_TLV
106 = new LLDPTLV().
107 setType(TLV_DIRECTION_TYPE).
108 setLength(TLV_DIRECTION_LENGTH).
109 setValue(TLV_DIRECTION_VALUE_FORWARD);
110
111 private static final LLDPTLV REVERSE_TLV
112 = new LLDPTLV().
113 setType(TLV_DIRECTION_TYPE).
114 setLength(TLV_DIRECTION_LENGTH).
115 setValue(TLV_DIRECTION_VALUE_REVERSE);
116
117 // Field offsets in ONOS-generated LLDP
118 private static final short ETHERTYPE_OFFSET = 12;
119 private static final short PORT_OFFSET = 26;
120 private static final short DPID_OFFSET = 54;
121
122 // Private member fields
123 // Byte arrays for TLV information string
124 private byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
125 private byte[] portId = new byte[PORT_TLV_SIZE];
126 private byte[] ttl = new byte[TTL_TLV_SIZE];
127 private byte[] ouiName = new byte[NAME_TLV_SIZE];
128 private byte[] ouiDpid = new byte[DPID_TLV_SIZE];
129
130 // TLVs
131 private LLDPTLV chassisTLV;
132 private LLDPTLV portTLV;
133 private LLDPTLV ttlTLV;
134 private LLDPTLV ouiNameTLV;
135 private LLDPTLV ouiDpidTLV;
136 private List<LLDPTLV> optionalTLVList;
137
138 /**
139 * Instantiates a new ONOS LDDP message.
140 */
141 public OnosLldp() {
142 // Create TLVs
143 this.chassisTLV = new LLDPTLV();
144 this.portTLV = new LLDPTLV();
145 this.ttlTLV = new LLDPTLV();
146 this.ouiNameTLV = new LLDPTLV();
147 this.ouiDpidTLV = new LLDPTLV();
148 this.optionalTLVList = new LinkedList<LLDPTLV>();
149 this.optionalTLVList.add(this.ouiNameTLV);
150 this.optionalTLVList.add(this.ouiDpidTLV);
151
152 // Add TLVs to LLDP packet
153 this.setChassisId(this.chassisTLV);
154 this.setPortId(this.portTLV);
155 this.setTtl(this.ttlTLV);
156 this.setOptionalTLVList(this.optionalTLVList);
157
158 // Set TLVs to default values
159 this.setChassisTLV(DEFAULT_DPID);
160 this.setPortTLV(DEFAULT_PORT);
161 this.setTTLTLV(DEFAULT_TTL);
162 this.setOUIName(OnosLldp.ONOS_NAME);
163 this.setOUIDpid(DEFAULT_DPID);
164 }
165
166 /**
167 * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
168 * we use another organizationally specific TLV to put the full dpid (see
169 * setOUIDpid()).
170 *
171 * @param dpid the switch DPID
172 */
173 private void setChassisTLV(final byte[] dpid) {
174 ByteBuffer bb = ByteBuffer.wrap(this.chassisId);
175 bb.put(CHASSIS_TLV_SUBTYPE);
176 for (int i = 2; i < 8; i++) {
177 bb.put(dpid[i]);
178 }
179
180 this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
181 this.chassisTLV.setType(CHASSIS_TLV_TYPE);
182 this.chassisTLV.setValue(this.chassisId);
183 }
184
185 /**
186 * Sets port TLV.
187 *
188 * @param portNumber the port number
189 */
190 private void setPortTLV(final short portNumber) {
191 ByteBuffer bb = ByteBuffer.wrap(this.portId);
192 bb.put(PORT_TLV_SUBTYPE);
193 bb.putShort(portNumber);
194
195 this.portTLV.setLength(PORT_TLV_SIZE);
196 this.portTLV.setType(PORT_TLV_TYPE);
197 this.portTLV.setValue(this.portId);
198 }
199
200 /**
201 * Sets Time To Live TLV.
202 *
203 * @param time the time to live
204 */
205 private void setTTLTLV(final short time) {
206 ByteBuffer bb = ByteBuffer.wrap(this.ttl);
207 bb.putShort(time);
208
209 this.ttlTLV.setLength(TTL_TLV_SIZE);
210 this.ttlTLV.setType(TTL_TLV_TYPE);
211 this.ttlTLV.setValue(this.ttl);
212 }
213
214 /**
215 * Sets organizationally specific TLV for ONOS name (subtype 1).
216 *
217 * @param name the name
218 */
219 private void setOUIName(final String name) {
220 ByteBuffer bb = ByteBuffer.wrap(ouiName);
221 bb.put(OnosLldp.ONLAB_OUI);
222 bb.put(NAME_TLV_SUBTYPE);
223 bb.put(name.getBytes(Charsets.UTF_8));
224
225 this.ouiNameTLV.setLength(NAME_TLV_SIZE);
226 this.ouiNameTLV.setType(NAME_TLV_TYPE);
227 this.ouiNameTLV.setValue(ouiName);
228 }
229
230 /**
231 * Sets organizationally specific TLV for ONOS full dpid (subtype 2).
232 *
233 * @param dpid the switch DPID
234 */
235 private void setOUIDpid(final byte[] dpid) {
236 ByteBuffer bb = ByteBuffer.wrap(ouiDpid);
237 bb.put(OnosLldp.ONLAB_OUI);
238 bb.put(DPID_TLV_SUBTYPE);
239 bb.put(dpid);
240
241 this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
242 this.ouiDpidTLV.setType(DPID_TLV_TYPE);
243 this.ouiDpidTLV.setValue(ouiDpid);
244 }
245
246 /**
247 * Sets switch DPID in LLDP packet.
248 *
Yuta HIGUCHIccab05d2014-07-26 22:42:28 -0700249 * @param dpid the switch dpid
Jonathan Hart299d1132014-06-27 09:25:28 -0700250 */
251 public void setSwitch(Long dpid) {
252 final byte[] byteDpid = ByteBuffer.allocate(8).putLong(dpid)
253 .array();
254 this.setChassisTLV(byteDpid);
255 this.setOUIDpid(byteDpid);
256 }
257
258
259 /**
260 * Sets the port number in LLDP packet.
261 *
Yuta HIGUCHIccab05d2014-07-26 22:42:28 -0700262 * @param portNumber the port number
Jonathan Hart299d1132014-06-27 09:25:28 -0700263 */
264 public void setPort(short portNumber) {
265 this.setPortTLV(portNumber);
266 }
267
268 /**
269 * Sets whether this is a forward or reverse LLDP.
270 *
271 * @param isReverse true if reverse, false if forward
272 */
273 public void setReverse(boolean isReverse) {
274 optionalTLVList.add((isReverse) ? REVERSE_TLV : FORWARD_TLV);
275 }
276
277 /**
278 * Serializes full LLDP packet to byte array.
279 *
280 * @return the serialized packet
281 */
Yuta HIGUCHIccab05d2014-07-26 22:42:28 -0700282 @Override
Jonathan Hart299d1132014-06-27 09:25:28 -0700283 public byte[] serialize() {
284 return super.serialize();
285 }
286
287 /**
288 * Checks if LLDP packet has correct size, LLDP multicast address, and
289 * ethertype. Packet assumed to have Ethernet header.
290 *
291 * @param packet full packet starting from the Ethernet header
292 * @return true if packet is LLDP, false otherwise
293 */
294 public static boolean isLLDP(final byte[] packet) {
295 // Does packet exist and does it have the mininum size?
296 if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
297 return false;
298 }
299
300 // Packet has LLDP multicast destination address?
301 final ByteBuffer bb = ByteBuffer.wrap(packet);
302 final byte[] dst = new byte[6];
303 bb.get(dst);
304
305 if (!(Arrays.equals(dst, OnosLldp.LLDP_NICIRA)
306 || Arrays.equals(dst, OnosLldp.LLDP_MULTICAST) || Arrays.equals(
307 dst, OnosLldp.BDDP_MULTICAST))) {
308
309 return false;
310 }
311
312 // Fetch ethertype, skip VLAN tag if it's there
313 short etherType = bb.getShort(ETHERTYPE_OFFSET);
314 if (etherType == ETHERTYPE_VLAN) {
315 etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
316 }
317
318 // Check ethertype
319 if (etherType == Ethernet.TYPE_LLDP) {
320 return true;
321 }
322 if (etherType == Ethernet.TYPE_BSN) {
323 return true;
324 }
325
326 return false;
327
328 }
329
330 /**
331 * Checks if packet has size of ONOS-generated LLDP, and correctness of two
332 * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
333 * valid LLDP packet
334 *
335 * @param packet full packet starting from the Ethernet header
336 * @return true if this is an ONOS-generated LLDP, otherwise false
337 */
338 public static boolean isOnosLldp(byte[] packet) {
339 if (packet.length < ONOS_LLDP_SIZE) {
340 return false;
341 }
342
343 // Extra offset due to VLAN tag
344 final ByteBuffer bb = ByteBuffer.wrap(packet);
345 int offset = 0;
346 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
347 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
348 offset = 4;
349 }
350
351 // Compare packet's organizationally specific TLVs to the expected
352 // values
353 for (int i = 0; i < OUI_TLV.length; i++) {
354 if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
355 return false;
356 }
357 }
358
359 return true;
360 }
361
362 /**
363 * Extracts dpid and port from ONOS-generated LLDP packet.
364 *
365 * @param packet full packet started at the Ethernet header
366 * @return switchport switch and port info from the DPID and Port TLVs
367 */
368 public static SwitchPort extractSwitchPort(final byte[] packet) {
369 final ByteBuffer bb = ByteBuffer.wrap(packet);
370
371 // Extra offset due to VLAN tag
372 int offset = 0;
373 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
374 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
375 offset = 4;
376 }
377
378 final short port = bb.getShort(PORT_OFFSET + offset);
379 final long dpid = bb.getLong(DPID_OFFSET + offset);
380
381 return new SwitchPort(dpid, port);
382 }
383
384 /**
385 * Checks if the LLDP is a reverse LLDP (i.e. sent in response to receiving
386 * an LLDP on the link). This information is stored in the Direction TLV.
387 *
388 * @param lldp parsed LLDP packet
389 * @return true if the LLDP is a reverse LLDP, otherwise false
390 */
391 public static boolean isReverse(final LLDP lldp) {
392 for (LLDPTLV lldpTlv : lldp.getOptionalTLVList()) {
393 if ((lldpTlv.getType() == TLV_DIRECTION_TYPE) &&
394 Arrays.equals(lldpTlv.getValue(), TLV_DIRECTION_VALUE_REVERSE)) {
395 return true;
396 }
397 }
398
399 return false;
400 }
401
402 @Override
403 public boolean equals(Object other) {
404 if (other == this) {
405 return true;
406 }
407 if (!super.equals(other)) {
408 return false;
409 }
410 //
411 // NOTE: Subclasses are are considered as change of identity, hence
412 // equals() will return false if the class type doesn't match.
413 //
414 if (getClass() != other.getClass()) {
415 return false;
416 }
417
418 OnosLldp otherLldp = (OnosLldp) other;
419
420 if (!this.chassisTLV.equals(otherLldp.chassisTLV)) {
421 return false;
422 }
423
424 if (!this.portTLV.equals(otherLldp.portTLV)) {
425 return false;
426 }
427
428 if (!this.ttlTLV.equals(otherLldp.ttlTLV)) {
429 return false;
430 }
431
432 if (!this.ouiNameTLV.equals(otherLldp.ouiNameTLV)) {
433 return false;
434 }
435
436 if (!this.ouiDpidTLV.equals(otherLldp.ouiDpidTLV)) {
437 return false;
438 }
439
440 return true;
441 }
442
443 @Override
444 public int hashCode() {
445 final int prime = 31;
446 int result = 1;
447 result = prime * result + chassisTLV.hashCode();
448 result = prime * result + portTLV.hashCode();
449 result = prime * result + ttlTLV.hashCode();
450 result = prime * result + ouiNameTLV.hashCode();
451 result = prime * result + ouiDpidTLV.hashCode();
452
453 return result;
454 }
455}