blob: 814e5d3676ce52b807bc552b646eabb2207b8225 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
alshabibc4901cd2014-09-05 16:50:40 -07009 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
Thomas Vachuska24c849c2014-10-27 09:53:05 -070012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
alshabibc4901cd2014-09-05 16:50:40 -070019package org.onlab.packet;
20
21import java.nio.ByteBuffer;
22import java.util.Arrays;
23import java.util.LinkedList;
24import java.util.List;
25
26import org.apache.commons.lang.ArrayUtils;
alshabibdf652ad2014-09-09 11:53:19 -070027import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
alshabibc4901cd2014-09-05 16:50:40 -070029
30
31/**
32 * LLDP packets OpenVirteX uses for discovery of physical network topology.
33 * Refer to IEEE Std 802.1ABTM-2009 for more information.
34 *
35 */
alshabib7911a052014-10-16 17:49:37 -070036@Deprecated
alshabib289652c2014-09-07 19:09:28 -070037public class ONLabLddp extends LLDP {
alshabibc4901cd2014-09-05 16:50:40 -070038
alshabib7235d092014-09-09 16:46:27 -070039 private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
alshabibc4901cd2014-09-05 16:50:40 -070040 // ON.Lab OUI and OVX name for organizationally specific TLVs
41 public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
42 public static final String OVX_NAME = "OpenVirteX";
43 public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
44 0x01};
45 public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
46 (byte) 0xc2, 0x00, 0x00, 0x0e};
47 public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
48 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
49 public static final short ETHERTYPE_VLAN = (short) 0x8100;
50
51 // TLV constants: type, size and subtype
52 // Organizationally specific TLV also have packet offset and contents of TLV
53 // header
54 private static final byte CHASSIS_TLV_TYPE = 1;
55 private static final byte CHASSIS_TLV_SIZE = 7;
56 private static final byte CHASSIS_TLV_SUBTYPE = 4;
57
58 private static final byte PORT_TLV_TYPE = 2;
alshabibdf652ad2014-09-09 11:53:19 -070059 private static final byte PORT_TLV_SIZE = 5;
alshabibc4901cd2014-09-05 16:50:40 -070060 private static final byte PORT_TLV_SUBTYPE = 2;
61
62 private static final byte TTL_TLV_TYPE = 3;
63 private static final byte TTL_TLV_SIZE = 2;
64
65 private static final byte NAME_TLV_TYPE = 127;
66 // 4 = OUI (3) + subtype (1)
alshabib289652c2014-09-07 19:09:28 -070067 private static final byte NAME_TLV_SIZE = (byte) (4 + ONLabLddp.OVX_NAME.length());
alshabibc4901cd2014-09-05 16:50:40 -070068 private static final byte NAME_TLV_SUBTYPE = 1;
alshabibdf652ad2014-09-09 11:53:19 -070069 private static final short NAME_TLV_OFFSET = 34;
alshabibc4901cd2014-09-05 16:50:40 -070070 private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
71 // Contents of full name TLV
72 private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
73 .putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
74 .put(OVX_NAME.getBytes()).array();
75
76 private static final byte DPID_TLV_TYPE = 127;
77 private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
78 // (1) + dpid (8)
79 private static final byte DPID_TLV_SUBTYPE = 2;
80 private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
81 // Contents of dpid TLV
82 // Note that this does *not* contain the actual dpid since we cannot match
83 // on it
84 private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
85 .putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
86 .array();
87
88 // Pre-built contents of both organizationally specific TLVs
89 private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
90
91 // Default switch, port number and TTL
92 private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 0x00, 0x00 };
alshabibdf652ad2014-09-09 11:53:19 -070094 private static final int DEFAULT_PORT = 0;
alshabibc4901cd2014-09-05 16:50:40 -070095 private static final short DEFAULT_TTL = 120; // in seconds
96
97 // Minimum and OVX-generated LLDP packet sizes
98 private static final short MINIMUM_LLDP_SIZE = 61;
99 // Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
100 private static final short OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
101 + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
102
103 // Field offsets in OVX-generated LLDP
104 private static final short ETHERTYPE_OFFSET = 12;
105 private static final short PORT_OFFSET = 26;
alshabibdf652ad2014-09-09 11:53:19 -0700106 private static final short DPID_OFFSET = 56;
alshabibc4901cd2014-09-05 16:50:40 -0700107
108 // Private member fields
109 // Byte arrays for TLV information string
110 private ByteBuffer bb;
111 private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
112 private final byte[] portId = new byte[PORT_TLV_SIZE];
113 private final byte[] ttl = new byte[TTL_TLV_SIZE];
114 private final byte[] ouiName = new byte[NAME_TLV_SIZE];
115 private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
116
117 // TLVs
118 private final LLDPTLV chassisTLV;
119 private final LLDPTLV portTLV;
120 private final LLDPTLV ttlTLV;
121 private final LLDPTLV ouiNameTLV;
122 private final LLDPTLV ouiDpidTLV;
123 private final List<LLDPTLV> optionalTLVList;
124
125 /**
126 * Instantiates a new OVX LDDP message.
127 */
alshabib289652c2014-09-07 19:09:28 -0700128 public ONLabLddp() {
alshabibc4901cd2014-09-05 16:50:40 -0700129 // Create TLVs
130 this.chassisTLV = new LLDPTLV();
131 this.portTLV = new LLDPTLV();
132 this.ttlTLV = new LLDPTLV();
133 this.ouiNameTLV = new LLDPTLV();
134 this.ouiDpidTLV = new LLDPTLV();
135 this.optionalTLVList = new LinkedList<LLDPTLV>();
136 this.optionalTLVList.add(this.ouiNameTLV);
137 this.optionalTLVList.add(this.ouiDpidTLV);
138
139 // Add TLVs to LLDP packet
140 this.setChassisId(this.chassisTLV);
141 this.setPortId(this.portTLV);
142 this.setTtl(this.ttlTLV);
143 this.setOptionalTLVList(this.optionalTLVList);
144
145 // Set TLVs to default values
146 this.setChassisTLV(DEFAULT_DPID);
147 this.setPortTLV(DEFAULT_PORT);
148 this.setTTLTLV(DEFAULT_TTL);
alshabib289652c2014-09-07 19:09:28 -0700149 this.setOUIName(ONLabLddp.OVX_NAME);
alshabibc4901cd2014-09-05 16:50:40 -0700150 this.setOUIDpid(DEFAULT_DPID);
151 }
152
153 /**
154 * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
155 * we use another organizationally specific TLV to put the full dpid (see
156 * setOUIDpid()).
157 *
158 * @param dpid the switch DPID
159 */
160 private void setChassisTLV(final byte[] dpid) {
161 this.bb = ByteBuffer.wrap(this.chassisId);
162 this.bb.put(CHASSIS_TLV_SUBTYPE);
163 for (int i = 2; i < 8; i++) {
164 bb.put(dpid[i]);
165 }
166 this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
167 this.chassisTLV.setType(CHASSIS_TLV_TYPE);
168 this.chassisTLV.setValue(this.chassisId);
169 }
170
171 /**
172 * Sets port TLV.
173 *
174 * @param portNumber the port number
175 */
alshabibdf652ad2014-09-09 11:53:19 -0700176 private void setPortTLV(final int portNumber) {
alshabibc4901cd2014-09-05 16:50:40 -0700177 this.bb = ByteBuffer.wrap(this.portId);
178 this.bb.put(PORT_TLV_SUBTYPE);
alshabibdf652ad2014-09-09 11:53:19 -0700179 this.bb.putInt(portNumber);
alshabibc4901cd2014-09-05 16:50:40 -0700180
181 this.portTLV.setLength(PORT_TLV_SIZE);
182 this.portTLV.setType(PORT_TLV_TYPE);
183 this.portTLV.setValue(this.portId);
184 }
185
186 /**
187 * Sets Time To Live TLV.
188 *
189 * @param time the time to live
190 */
191 private void setTTLTLV(final short time) {
192 this.bb = ByteBuffer.wrap(this.ttl);
193 this.bb.putShort(time);
194
195 this.ttlTLV.setLength(TTL_TLV_SIZE);
196 this.ttlTLV.setType(TTL_TLV_TYPE);
197 this.ttlTLV.setValue(this.ttl);
198 }
199
200 /**
201 * Set. organizationally specific TLV for OVX name (subtype 1).
202 *
203 * @param name the name
204 */
205 private void setOUIName(final String name) {
206 this.bb = ByteBuffer.wrap(ouiName);
alshabib289652c2014-09-07 19:09:28 -0700207 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700208 this.bb.put(NAME_TLV_SUBTYPE);
209 this.bb.put(name.getBytes());
210
211 this.ouiNameTLV.setLength(NAME_TLV_SIZE);
212 this.ouiNameTLV.setType(NAME_TLV_TYPE);
213 this.ouiNameTLV.setValue(ouiName);
214 }
215
216 /**
217 * Sets organizationally specific TLV for OVX full dpid (subtype 2).
218 *
219 * @param dpid the switch DPID
220 */
221 private void setOUIDpid(final byte[] dpid) {
222 this.bb = ByteBuffer.wrap(ouiDpid);
alshabib289652c2014-09-07 19:09:28 -0700223 this.bb.put(ONLabLddp.ONLAB_OUI);
alshabibc4901cd2014-09-05 16:50:40 -0700224 this.bb.put(DPID_TLV_SUBTYPE);
225 this.bb.put(dpid);
226
227 this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
228 this.ouiDpidTLV.setType(DPID_TLV_TYPE);
229 this.ouiDpidTLV.setValue(ouiDpid);
230 }
231
232 /**
233 * Sets switch DPID in LLDP packet.
234 *
tom5f18cf32014-09-13 14:10:57 -0700235 * @param dp the switch instance
alshabibc4901cd2014-09-05 16:50:40 -0700236 */
237 public void setSwitch(long dp) {
238 final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
239 .array();
240 this.setChassisTLV(dpid);
241 this.setOUIDpid(dpid);
242 }
243
244 /**
245 * Sets port in LLDP packet.
246 *
247 * @param port the port instance
248 */
alshabibdf652ad2014-09-09 11:53:19 -0700249 public void setPort(int port) {
tom5f18cf32014-09-13 14:10:57 -0700250 this.setPortTLV(port);
alshabibc4901cd2014-09-05 16:50:40 -0700251 }
252
253 /**
254 * Serializes full LLDP packet to byte array. Need to set both switch and
255 * port before you can serialize.
256 */
257 @Override
258 public byte[] serialize() {
259 return super.serialize();
260 }
261
262 /**
263 * Checks if LLDP packet has correct size, LLDP multicast address, and
264 * ethertype. Packet assumed to have Ethernet header.
265 *
tom5f18cf32014-09-13 14:10:57 -0700266 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700267 * @return true if packet is LLDP, false otherwise
268 */
269 public static boolean isLLDP(final byte[] packet) {
270 // Does packet exist and does it have the mininum size?
271 if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
272 return false;
273 }
274
275 // Packet has LLDP multicast destination address?
276 final ByteBuffer bb = ByteBuffer.wrap(packet);
277 final byte[] dst = new byte[6];
278 bb.get(dst);
279
alshabib289652c2014-09-07 19:09:28 -0700280 if (!(Arrays.equals(dst, ONLabLddp.LLDP_NICIRA)
281 || Arrays.equals(dst, ONLabLddp.LLDP_MULTICAST) || Arrays.equals(
282 dst, ONLabLddp.BDDP_MULTICAST))) {
alshabibc4901cd2014-09-05 16:50:40 -0700283
284 return false;
285 }
286
287 // Fetch ethertype, skip VLAN tag if it's there
288 short etherType = bb.getShort(ETHERTYPE_OFFSET);
289 if (etherType == ETHERTYPE_VLAN) {
290 etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
291 }
292
293 // Check ethertype
294 if (etherType == Ethernet.TYPE_LLDP) {
295 return true;
296 }
297 if (etherType == Ethernet.TYPE_BSN) {
298 return true;
299 }
300
301 return false;
302
303 }
304
305 /**
306 * Checks if packet has size of OVX-generated LLDP, and correctness of two
307 * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
308 * valid LLDP packet
309 *
tom5f18cf32014-09-13 14:10:57 -0700310 * @param packet packet data
311 * @return eth type or -1
alshabibc4901cd2014-09-05 16:50:40 -0700312 */
alshabib9ee68172014-09-09 14:45:14 -0700313 public static short isOVXLLDP(byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700314 if (packet.length < OVX_LLDP_SIZE) {
alshabib9ee68172014-09-09 14:45:14 -0700315 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700316 }
317
318 // Extra offset due to VLAN tag
319 final ByteBuffer bb = ByteBuffer.wrap(packet);
320 int offset = 0;
alshabib9ee68172014-09-09 14:45:14 -0700321 short ethType = bb.getShort(ETHERTYPE_OFFSET);
322 if (ethType != Ethernet.TYPE_LLDP
323 && ethType != Ethernet.TYPE_BSN) {
alshabibc4901cd2014-09-05 16:50:40 -0700324 offset = 4;
alshabib9ee68172014-09-09 14:45:14 -0700325 ethType = bb.getShort(ETHERTYPE_OFFSET + offset);
326 if (ethType != Ethernet.TYPE_LLDP
327 && ethType != Ethernet.TYPE_BSN) {
328 return -1;
329 }
alshabibc4901cd2014-09-05 16:50:40 -0700330 }
331
332 // Compare packet's organizationally specific TLVs to the expected
333 // values
334 for (int i = 0; i < OUI_TLV.length; i++) {
335 if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
alshabib9ee68172014-09-09 14:45:14 -0700336 return -1;
alshabibc4901cd2014-09-05 16:50:40 -0700337 }
338 }
339
alshabib9ee68172014-09-09 14:45:14 -0700340 return ethType;
alshabibc4901cd2014-09-05 16:50:40 -0700341 }
342
343 /**
344 * Extracts dpid and port from OVX-generated LLDP packet.
345 *
tom5f18cf32014-09-13 14:10:57 -0700346 * @param packet packet data
alshabibc4901cd2014-09-05 16:50:40 -0700347 * @return Dpid and port
348 */
alshabibdf652ad2014-09-09 11:53:19 -0700349 public static DPIDandPort parseLLDP(final byte[] packet) {
alshabibc4901cd2014-09-05 16:50:40 -0700350 final ByteBuffer bb = ByteBuffer.wrap(packet);
351
352 // Extra offset due to VLAN tag
353 int offset = 0;
354 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
355 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
356 offset = 4;
357 }
358
alshabibdf652ad2014-09-09 11:53:19 -0700359 final int port = bb.getInt(PORT_OFFSET + offset);
alshabibc4901cd2014-09-05 16:50:40 -0700360 final long dpid = bb.getLong(DPID_OFFSET + offset);
361
alshabibdf652ad2014-09-09 11:53:19 -0700362 return new DPIDandPort(dpid, port);
alshabibc4901cd2014-09-05 16:50:40 -0700363 }
alshabibdf652ad2014-09-09 11:53:19 -0700364
365 public static class DPIDandPort {
366
367 private final long dpid;
368 private final int port;
369
370 public DPIDandPort(long dpid, int port) {
371 this.dpid = dpid;
372 this.port = port;
373 }
374
375 public long getDpid() {
376 return this.dpid;
377 }
378
379 public int getPort() {
380 return this.port;
381 }
382
383 }
384
alshabibc4901cd2014-09-05 16:50:40 -0700385}