blob: f1a4b5fa76ba6550171b9c5e5b9b5b86acc0bbc0 [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;
24
25
26/**
27 * LLDP packets OpenVirteX uses for discovery of physical network topology.
28 * Refer to IEEE Std 802.1ABTM-2009 for more information.
29 *
30 */
31@SuppressWarnings("rawtypes")
32public class OVXLLDP extends LLDP {
33
34 // ON.Lab OUI and OVX name for organizationally specific TLVs
35 public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
36 public static final String OVX_NAME = "OpenVirteX";
37 public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
38 0x01};
39 public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
40 (byte) 0xc2, 0x00, 0x00, 0x0e};
41 public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
42 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
43 public static final short ETHERTYPE_VLAN = (short) 0x8100;
44
45 // TLV constants: type, size and subtype
46 // Organizationally specific TLV also have packet offset and contents of TLV
47 // header
48 private static final byte CHASSIS_TLV_TYPE = 1;
49 private static final byte CHASSIS_TLV_SIZE = 7;
50 private static final byte CHASSIS_TLV_SUBTYPE = 4;
51
52 private static final byte PORT_TLV_TYPE = 2;
53 private static final byte PORT_TLV_SIZE = 7;
54 private static final byte PORT_TLV_SUBTYPE = 2;
55
56 private static final byte TTL_TLV_TYPE = 3;
57 private static final byte TTL_TLV_SIZE = 2;
58
59 private static final byte NAME_TLV_TYPE = 127;
60 // 4 = OUI (3) + subtype (1)
61 private static final byte NAME_TLV_SIZE = (byte) (4 + OVXLLDP.OVX_NAME.length());
62 private static final byte NAME_TLV_SUBTYPE = 1;
63 private static final short NAME_TLV_OFFSET = 32;
64 private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
65 // Contents of full name TLV
66 private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
67 .putShort(NAME_TLV_HEADER).put(ONLAB_OUI).put(NAME_TLV_SUBTYPE)
68 .put(OVX_NAME.getBytes()).array();
69
70 private static final byte DPID_TLV_TYPE = 127;
71 private static final byte DPID_TLV_SIZE = (byte) (12); // 12 = OUI (3) + subtype
72 // (1) + dpid (8)
73 private static final byte DPID_TLV_SUBTYPE = 2;
74 private static final short DPID_TLV_HEADER = (short) ((DPID_TLV_TYPE << 9) | DPID_TLV_SIZE);
75 // Contents of dpid TLV
76 // Note that this does *not* contain the actual dpid since we cannot match
77 // on it
78 private static final byte[] DPID_TLV = ByteBuffer.allocate(DPID_TLV_SIZE + 2 - 8)
79 .putShort(DPID_TLV_HEADER).put(ONLAB_OUI).put(DPID_TLV_SUBTYPE)
80 .array();
81
82 // Pre-built contents of both organizationally specific TLVs
83 private static final byte[] OUI_TLV = ArrayUtils.addAll(NAME_TLV, DPID_TLV);
84
85 // Default switch, port number and TTL
86 private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 0x00, 0x00 };
88 private static final short DEFAULT_PORT = 0;
89 private static final short DEFAULT_TTL = 120; // in seconds
90
91 // Minimum and OVX-generated LLDP packet sizes
92 private static final short MINIMUM_LLDP_SIZE = 61;
93 // Add 12 for 2-byte header of each TLV and a single EndOfLLDPTLV
94 private static final short OVX_LLDP_SIZE = (short) (CHASSIS_TLV_SIZE
95 + PORT_TLV_SIZE + TTL_TLV_SIZE + NAME_TLV_SIZE + DPID_TLV_SIZE + 12);
96
97 // Field offsets in OVX-generated LLDP
98 private static final short ETHERTYPE_OFFSET = 12;
99 private static final short PORT_OFFSET = 26;
100 private static final short DPID_OFFSET = 54;
101
102 // Private member fields
103 // Byte arrays for TLV information string
104 private ByteBuffer bb;
105 private final byte[] chassisId = new byte[CHASSIS_TLV_SIZE];
106 private final byte[] portId = new byte[PORT_TLV_SIZE];
107 private final byte[] ttl = new byte[TTL_TLV_SIZE];
108 private final byte[] ouiName = new byte[NAME_TLV_SIZE];
109 private final byte[] ouiDpid = new byte[DPID_TLV_SIZE];
110
111 // TLVs
112 private final LLDPTLV chassisTLV;
113 private final LLDPTLV portTLV;
114 private final LLDPTLV ttlTLV;
115 private final LLDPTLV ouiNameTLV;
116 private final LLDPTLV ouiDpidTLV;
117 private final List<LLDPTLV> optionalTLVList;
118
119 /**
120 * Instantiates a new OVX LDDP message.
121 */
122 public OVXLLDP() {
123 // Create TLVs
124 this.chassisTLV = new LLDPTLV();
125 this.portTLV = new LLDPTLV();
126 this.ttlTLV = new LLDPTLV();
127 this.ouiNameTLV = new LLDPTLV();
128 this.ouiDpidTLV = new LLDPTLV();
129 this.optionalTLVList = new LinkedList<LLDPTLV>();
130 this.optionalTLVList.add(this.ouiNameTLV);
131 this.optionalTLVList.add(this.ouiDpidTLV);
132
133 // Add TLVs to LLDP packet
134 this.setChassisId(this.chassisTLV);
135 this.setPortId(this.portTLV);
136 this.setTtl(this.ttlTLV);
137 this.setOptionalTLVList(this.optionalTLVList);
138
139 // Set TLVs to default values
140 this.setChassisTLV(DEFAULT_DPID);
141 this.setPortTLV(DEFAULT_PORT);
142 this.setTTLTLV(DEFAULT_TTL);
143 this.setOUIName(OVXLLDP.OVX_NAME);
144 this.setOUIDpid(DEFAULT_DPID);
145 }
146
147 /**
148 * Sets chassis TLV. Note that we can only put 6 bytes in the chassis ID, so
149 * we use another organizationally specific TLV to put the full dpid (see
150 * setOUIDpid()).
151 *
152 * @param dpid the switch DPID
153 */
154 private void setChassisTLV(final byte[] dpid) {
155 this.bb = ByteBuffer.wrap(this.chassisId);
156 this.bb.put(CHASSIS_TLV_SUBTYPE);
157 for (int i = 2; i < 8; i++) {
158 bb.put(dpid[i]);
159 }
160 this.chassisTLV.setLength(CHASSIS_TLV_SIZE);
161 this.chassisTLV.setType(CHASSIS_TLV_TYPE);
162 this.chassisTLV.setValue(this.chassisId);
163 }
164
165 /**
166 * Sets port TLV.
167 *
168 * @param portNumber the port number
169 */
170 private void setPortTLV(final long portNumber) {
171 this.bb = ByteBuffer.wrap(this.portId);
172 this.bb.put(PORT_TLV_SUBTYPE);
173 this.bb.putLong(portNumber);
174
175 this.portTLV.setLength(PORT_TLV_SIZE);
176 this.portTLV.setType(PORT_TLV_TYPE);
177 this.portTLV.setValue(this.portId);
178 }
179
180 /**
181 * Sets Time To Live TLV.
182 *
183 * @param time the time to live
184 */
185 private void setTTLTLV(final short time) {
186 this.bb = ByteBuffer.wrap(this.ttl);
187 this.bb.putShort(time);
188
189 this.ttlTLV.setLength(TTL_TLV_SIZE);
190 this.ttlTLV.setType(TTL_TLV_TYPE);
191 this.ttlTLV.setValue(this.ttl);
192 }
193
194 /**
195 * Set. organizationally specific TLV for OVX name (subtype 1).
196 *
197 * @param name the name
198 */
199 private void setOUIName(final String name) {
200 this.bb = ByteBuffer.wrap(ouiName);
201 this.bb.put(OVXLLDP.ONLAB_OUI);
202 this.bb.put(NAME_TLV_SUBTYPE);
203 this.bb.put(name.getBytes());
204
205 this.ouiNameTLV.setLength(NAME_TLV_SIZE);
206 this.ouiNameTLV.setType(NAME_TLV_TYPE);
207 this.ouiNameTLV.setValue(ouiName);
208 }
209
210 /**
211 * Sets organizationally specific TLV for OVX full dpid (subtype 2).
212 *
213 * @param dpid the switch DPID
214 */
215 private void setOUIDpid(final byte[] dpid) {
216 this.bb = ByteBuffer.wrap(ouiDpid);
217 this.bb.put(OVXLLDP.ONLAB_OUI);
218 this.bb.put(DPID_TLV_SUBTYPE);
219 this.bb.put(dpid);
220
221 this.ouiDpidTLV.setLength(DPID_TLV_SIZE);
222 this.ouiDpidTLV.setType(DPID_TLV_TYPE);
223 this.ouiDpidTLV.setValue(ouiDpid);
224 }
225
226 /**
227 * Sets switch DPID in LLDP packet.
228 *
229 * @param sw the switch instance
230 */
231 public void setSwitch(long dp) {
232 final byte[] dpid = ByteBuffer.allocate(8).putLong(dp)
233 .array();
234 this.setChassisTLV(dpid);
235 this.setOUIDpid(dpid);
236 }
237
238 /**
239 * Sets port in LLDP packet.
240 *
241 * @param port the port instance
242 */
243 public void setPort(long port) {
244 long portNumber = port;
245 this.setPortTLV(portNumber);
246 }
247
248 /**
249 * Serializes full LLDP packet to byte array. Need to set both switch and
250 * port before you can serialize.
251 */
252 @Override
253 public byte[] serialize() {
254 return super.serialize();
255 }
256
257 /**
258 * Checks if LLDP packet has correct size, LLDP multicast address, and
259 * ethertype. Packet assumed to have Ethernet header.
260 *
261 * @param packet
262 * @return true if packet is LLDP, false otherwise
263 */
264 public static boolean isLLDP(final byte[] packet) {
265 // Does packet exist and does it have the mininum size?
266 if (packet == null || packet.length < MINIMUM_LLDP_SIZE) {
267 return false;
268 }
269
270 // Packet has LLDP multicast destination address?
271 final ByteBuffer bb = ByteBuffer.wrap(packet);
272 final byte[] dst = new byte[6];
273 bb.get(dst);
274
275 if (!(Arrays.equals(dst, OVXLLDP.LLDP_NICIRA)
276 || Arrays.equals(dst, OVXLLDP.LLDP_MULTICAST) || Arrays.equals(
277 dst, OVXLLDP.BDDP_MULTICAST))) {
278
279 return false;
280 }
281
282 // Fetch ethertype, skip VLAN tag if it's there
283 short etherType = bb.getShort(ETHERTYPE_OFFSET);
284 if (etherType == ETHERTYPE_VLAN) {
285 etherType = bb.getShort(ETHERTYPE_OFFSET + 4);
286 }
287
288 // Check ethertype
289 if (etherType == Ethernet.TYPE_LLDP) {
290 return true;
291 }
292 if (etherType == Ethernet.TYPE_BSN) {
293 return true;
294 }
295
296 return false;
297
298 }
299
300 /**
301 * Checks if packet has size of OVX-generated LLDP, and correctness of two
302 * organizationally specific TLVs that use ON.Lab's OUI. Assumes packet is
303 * valid LLDP packet
304 *
305 * @param packet
306 * @return
307 */
308 public static boolean isOVXLLDP(byte[] packet) {
309 if (packet.length < OVX_LLDP_SIZE) {
310 return false;
311 }
312
313 // Extra offset due to VLAN tag
314 final ByteBuffer bb = ByteBuffer.wrap(packet);
315 int offset = 0;
316 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
317 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
318 offset = 4;
319 }
320
321 // Compare packet's organizationally specific TLVs to the expected
322 // values
323 for (int i = 0; i < OUI_TLV.length; i++) {
324 if (packet[NAME_TLV_OFFSET + offset + i] != OUI_TLV[i]) {
325 return false;
326 }
327 }
328
329 return true;
330 }
331
332 /**
333 * Extracts dpid and port from OVX-generated LLDP packet.
334 *
335 * @param packet
336 * @return Dpid and port
337 */
338 /* public static long parseLLDP(final byte[] packet) {
339 final ByteBuffer bb = ByteBuffer.wrap(packet);
340
341 // Extra offset due to VLAN tag
342 int offset = 0;
343 if (bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_LLDP
344 && bb.getShort(ETHERTYPE_OFFSET) != Ethernet.TYPE_BSN) {
345 offset = 4;
346 }
347
348 final short port = bb.getLong(PORT_OFFSET + offset);
349 final long dpid = bb.getLong(DPID_OFFSET + offset);
350
351 return dpid
352 }
353 */
354}