blob: 37213d0e5cb485a89d374feac455470137b0afa0 [file] [log] [blame]
alshabibc4901cd2014-09-05 16:50:40 -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 org.onlab.packet;
17
18import java.nio.ByteBuffer;
19import java.util.Arrays;
20import java.util.LinkedList;
21import java.util.List;
22
23import org.apache.commons.lang.ArrayUtils;
alshabibdf652ad2014-09-09 11:53:19 -070024import org.slf4j.Logger;
25import org.slf4j.LoggerFactory;
alshabibc4901cd2014-09-05 16:50:40 -070026
27
28/**
29 * LLDP packets OpenVirteX uses for discovery of physical network topology.
30 * Refer to IEEE Std 802.1ABTM-2009 for more information.
31 *
32 */
alshabib289652c2014-09-07 19:09:28 -070033public class ONLabLddp extends LLDP {
alshabibc4901cd2014-09-05 16:50:40 -070034
alshabib7235d092014-09-09 16:46:27 -070035 private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
alshabibc4901cd2014-09-05 16:50:40 -070036 // ON.Lab OUI and OVX name for organizationally specific TLVs
37 public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
38 public static final String OVX_NAME = "OpenVirteX";
39 public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
40 0x01};
41 public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
42 (byte) 0xc2, 0x00, 0x00, 0x0e};
43 public 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;
alshabibdf652ad2014-09-09 11:53:19 -070055 private static final byte PORT_TLV_SIZE = 5;
alshabibc4901cd2014-09-05 16:50:40 -070056 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)
alshabib289652c2014-09-07 19:09:28 -070063 private static final byte NAME_TLV_SIZE = (byte) (4 + ONLabLddp.OVX_NAME.length());
alshabibc4901cd2014-09-05 16:50:40 -070064 private static final byte NAME_TLV_SUBTYPE = 1;
alshabibdf652ad2014-09-09 11:53:19 -070065 private static final short NAME_TLV_OFFSET = 34;
alshabibc4901cd2014-09-05 16:50:40 -070066 private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
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(OVX_NAME.getBytes()).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 };
alshabibdf652ad2014-09-09 11:53:19 -070090 private static final int DEFAULT_PORT = 0;
alshabibc4901cd2014-09-05 16:50:40 -070091 private static final short DEFAULT_TTL = 120; // in seconds
92
93 // Minimum and OVX-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 OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
97 + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
98
99 // Field offsets in OVX-generated LLDP
100 private static final short ETHERTYPE_OFFSET = 12;
101 private static final short PORT_OFFSET = 26;
alshabibdf652ad2014-09-09 11:53:19 -0700102 private static final short DPID_OFFSET = 56;
alshabibc4901cd2014-09-05 16:50:40 -0700103
104 // Private member fields
105 // Byte arrays for TLV information string
106 private ByteBuffer bb;
107 private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
108 private final byte[] portId = new byte[PORT_TLV_SIZE];
109 private final byte[] ttl = new byte[TTL_TLV_SIZE];
110 private final byte[] ouiName = new byte[NAME_TLV_SIZE];
111 private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
112
113 // TLVs
114 private final LLDPTLV chassisTLV;
115 private final LLDPTLV portTLV;
116 private final LLDPTLV ttlTLV;
117 private final LLDPTLV ouiNameTLV;
118 private final LLDPTLV ouiDpidTLV;
119 private final List<LLDPTLV> optionalTLVList;
120
121 /**
122 * Instantiates a new OVX LDDP message.
123 */
alshabib289652c2014-09-07 19:09:28 -0700124 public ONLabLddp() {
alshabibc4901cd2014-09-05 16:50:40 -0700125 // Create TLVs
126 this.chassisTLV = new LLDPTLV();
127 this.portTLV = new LLDPTLV();
128 this.ttlTLV = new LLDPTLV();
129 this.ouiNameTLV = new LLDPTLV();
130 this.ouiDpidTLV = new LLDPTLV();
131 this.optionalTLVList = new LinkedList<LLDPTLV>();
132 this.optionalTLVList.add(this.ouiNameTLV);
133 this.optionalTLVList.add(this.ouiDpidTLV);
134
135 // Add TLVs to LLDP packet
136 this.setChassisId(this.chassisTLV);
137 this.setPortId(this.portTLV);
138 this.setTtl(this.ttlTLV);
139 this.setOptionalTLVList(this.optionalTLVList);
140
141 // Set TLVs to default values
142 this.setChassisTLV(DEFAULT_DPID);
143 this.setPortTLV(DEFAULT_PORT);
144 this.setTTLTLV(DEFAULT_TTL);
alshabib289652c2014-09-07 19:09:28 -0700145 this.setOUIName(ONLabLddp.OVX_NAME);
alshabibc4901cd2014-09-05 16:50:40 -0700146 this.setOUIDpid(DEFAULT_DPID);
147 }
148
149 /**
150 * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
151 * we use another organizationally specific TLV to put the full dpid (see
152 * setOUIDpid()).
153 *
154 * @param dpid the switch DPID
155 */
156 private void setChassisTLV(final byte[] dpid) {
157 this.bb = ByteBuffer.wrap(this.chassisId);
158 this.bb.put(CHASSIS_TLV_SUBTYPE);
159 for (int i = 2; i < 8; i++) {
160 bb.put(dpid[i]);
161 }
162 this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
163 this.chassisTLV.setType(CHASSIS_TLV_TYPE);
164 this.chassisTLV.setValue(this.chassisId);
165 }
166
167 /**
168 * Sets port TLV.
169 *
170 * @param portNumber the port number
171 */
alshabibdf652ad2014-09-09 11:53:19 -0700172 private void setPortTLV(final int portNumber) {
alshabibc4901cd2014-09-05 16:50:40 -0700173 this.bb = ByteBuffer.wrap(this.portId);
174 this.bb.put(PORT_TLV_SUBTYPE);
alshabibdf652ad2014-09-09 11:53:19 -0700175 this.bb.putInt(portNumber);
alshabibc4901cd2014-09-05 16:50:40 -0700176
177 this.portTLV.setLength(PORT_TLV_SIZE);
178 this.portTLV.setType(PORT_TLV_TYPE);
179 this.portTLV.setValue(this.portId);
180 }
181
182 /**
183 * Sets Time To Live TLV.
184 *
185 * @param time the time to live
186 */
187 private void setTTLTLV(final short time) {
188 this.bb = ByteBuffer.wrap(this.ttl);
189 this.bb.putShort(time);
190
191 this.ttlTLV.setLength(TTL_TLV_SIZE);
192 this.ttlTLV.setType(TTL_TLV_TYPE);
193 this.ttlTLV.setValue(this.ttl);
194 }
195
196 /**
197 * Set. organizationally specific TLV for OVX name (subtype 1).
198 *
199 * @param name the name
200 */
201 private void setOUIName(final String name) {
202 this.bb = ByteBuffer.wrap(ouiName);
alshabib289652c2014-09-07 19:09:28 -0700203 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700204 this.bb.put(NAME_TLV_SUBTYPE);
205 this.bb.put(name.getBytes());
206
207 this.ouiNameTLV.setLength(NAME_TLV_SIZE);
208 this.ouiNameTLV.setType(NAME_TLV_TYPE);
209 this.ouiNameTLV.setValue(ouiName);
210 }
211
212 /**
213 * Sets organizationally specific TLV for OVX full dpid (subtype 2).
214 *
215 * @param dpid the switch DPID
216 */
217 private void setOUIDpid(final byte[] dpid) {
218 this.bb = ByteBuffer.wrap(ouiDpid);
alshabib289652c2014-09-07 19:09:28 -0700219 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700220 this.bb.put(DPID_TLV_SUBTYPE);
221 this.bb.put(dpid);
222
223 this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
224 this.ouiDpidTLV.setType(DPID_TLV_TYPE);
225 this.ouiDpidTLV.setValue(ouiDpid);
226 }
227
228 /**
229 * Sets switch DPID in LLDP packet.
230 *
tom5f18cf32014-09-13 14:10:57 -0700231 * @param dp the switch instance
alshabibc4901cd2014-09-05 16:50:40 -0700232 */
233 public void setSwitch(long dp) {
234 final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
235 .array();
236 this.setChassisTLV(dpid);
237 this.setOUIDpid(dpid);
238 }
239
240 /**
241 * Sets port in LLDP packet.
242 *
243 * @param port the port instance
244 */
alshabibdf652ad2014-09-09 11:53:19 -0700245 public void setPort(int port) {
tom5f18cf32014-09-13 14:10:57 -0700246 this.setPortTLV(port);
alshabibc4901cd2014-09-05 16:50:40 -0700247 }
248
249 /**
250 * Serializes full LLDP packet to byte array. Need to set both switch and
251 * port before you can serialize.
252 */
253 @Override
254 public byte[] serialize() {
255 return super.serialize();
256 }
257
258 /**
259 * Checks if LLDP packet has correct size, LLDP multicast address, and
260 * ethertype. Packet assumed to have Ethernet header.
261 *
tom5f18cf32014-09-13 14:10:57 -0700262 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700263 * @return true if packet is LLDP, false otherwise
264 */
265 public static boolean isLLDP(final byte[] packet) {
266 // Does packet exist and does it have the mininum size?
267 if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
268 return false;
269 }
270
271 // Packet has LLDP multicast destination address?
272 final ByteBuffer bb = ByteBuffer.wrap(packet);
273 final byte[] dst = new byte[6];
274 bb.get(dst);
275
alshabib289652c2014-09-07 19:09:28 -0700276 if (!(Arrays.equals(dst, ONLabLddp.LLDP_NICIRA)
277 || Arrays.equals(dst, ONLabLddp.LLDP_MULTICAST) || Arrays.equals(
278 dst, ONLabLddp.BDDP_MULTICAST))) {
alshabibc4901cd2014-09-05 16:50:40 -0700279
280 return false;
281 }
282
283 // Fetch ethertype, skip VLAN tag if it's there
284 short etherType = bb.getShort(ETHERTYPE_OFFSET);
285 if (etherType == ETHERTYPE_VLAN) {
286 etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
287 }
288
289 // Check ethertype
290 if (etherType == Ethernet.TYPE_LLDP) {
291 return true;
292 }
293 if (etherType == Ethernet.TYPE_BSN) {
294 return true;
295 }
296
297 return false;
298
299 }
300
301 /**
302 * Checks if packet has size of OVX-generated LLDP, and correctness of two
303 * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
304 * valid LLDP packet
305 *
tom5f18cf32014-09-13 14:10:57 -0700306 * @param packet packet data
307 * @return eth type or -1
alshabibc4901cd2014-09-05 16:50:40 -0700308 */
alshabib9ee68172014-09-09 14:45:14 -0700309 public static short isOVXLLDP(byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700310 if (packet.length < OVX_LLDP_SIZE) {
alshabib9ee68172014-09-09 14:45:14 -0700311 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700312 }
313
314 // Extra offset due to VLAN tag
315 final ByteBuffer bb = ByteBuffer.wrap(packet);
316 int offset = 0;
alshabib9ee68172014-09-09 14:45:14 -0700317 short ethType = bb.getShort(ETHERTYPE_OFFSET);
318 if (ethType != Ethernet.TYPE_LLDP
319 && ethType != Ethernet.TYPE_BSN) {
alshabibc4901cd2014-09-05 16:50:40 -0700320 offset = 4;
alshabib9ee68172014-09-09 14:45:14 -0700321 ethType = bb.getShort(ETHERTYPE_OFFSET + offset);
322 if (ethType != Ethernet.TYPE_LLDP
323 && ethType != Ethernet.TYPE_BSN) {
324 return -1;
325 }
alshabibc4901cd2014-09-05 16:50:40 -0700326 }
327
328 // Compare packet's organizationally specific TLVs to the expected
329 // values
330 for (int i = 0; i < OUI_TLV.length; i++) {
331 if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
alshabib9ee68172014-09-09 14:45:14 -0700332 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700333 }
334 }
335
alshabib9ee68172014-09-09 14:45:14 -0700336 return ethType;
alshabibc4901cd2014-09-05 16:50:40 -0700337 }
338
339 /**
340 * Extracts dpid and port from OVX-generated LLDP packet.
341 *
tom5f18cf32014-09-13 14:10:57 -0700342 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700343 * @return Dpid and port
344 */
alshabibdf652ad2014-09-09 11:53:19 -0700345 public static DPIDandPort parseLLDP(final byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700346 final ByteBuffer bb = ByteBuffer.wrap(packet);
347
348 // Extra offset due to VLAN tag
349 int offset = 0;
350 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
351 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
352 offset = 4;
353 }
354
alshabibdf652ad2014-09-09 11:53:19 -0700355 final int port = bb.getInt(PORT_OFFSET + offset);
alshabibc4901cd2014-09-05 16:50:40 -0700356 final long dpid = bb.getLong(DPID_OFFSET + offset);
357
alshabibdf652ad2014-09-09 11:53:19 -0700358 return new DPIDandPort(dpid, port);
alshabibc4901cd2014-09-05 16:50:40 -0700359 }
alshabibdf652ad2014-09-09 11:53:19 -0700360
361 public static class DPIDandPort {
362
363 private final long dpid;
364 private final int port;
365
366 public DPIDandPort(long dpid, int port) {
367 this.dpid = dpid;
368 this.port = port;
369 }
370
371 public long getDpid() {
372 return this.dpid;
373 }
374
375 public int getPort() {
376 return this.port;
377 }
378
379 }
380
alshabibc4901cd2014-09-05 16:50:40 -0700381}