blob: 7b15e82612326158f0eb5184518c20b11be7ae65 [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
3* University
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may
6* not use this file except in compliance with the License. You may obtain
7* a copy of the License at
8*
9* http://www.apache.org/licenses/LICENSE-2.0
10*
11* Unless required by applicable law or agreed to in writing, software
12* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations
15* under the License.
16**/
17
18package org.openflow.protocol.factory;
19
20import java.util.ArrayList;
21import java.util.List;
22
23import org.jboss.netty.buffer.ChannelBuffer;
24import org.openflow.protocol.OFMessage;
25import org.openflow.protocol.OFType;
26import org.openflow.protocol.action.OFAction;
27import org.openflow.protocol.action.OFActionType;
28import org.openflow.protocol.statistics.OFStatistics;
29import org.openflow.protocol.statistics.OFStatisticsType;
30import org.openflow.protocol.statistics.OFVendorStatistics;
31import org.openflow.protocol.vendor.OFByteArrayVendorData;
32import org.openflow.protocol.vendor.OFVendorData;
33import org.openflow.protocol.vendor.OFVendorDataType;
34import org.openflow.protocol.vendor.OFVendorId;
35
36
37/**
38 * A basic OpenFlow factory that supports naive creation of both Messages and
39 * Actions.
40 *
41 * @author David Erickson (daviderickson@cs.stanford.edu)
42 * @author Rob Sherwood (rob.sherwood@stanford.edu)
43 *
44 */
45public class BasicFactory implements OFMessageFactory, OFActionFactory,
46 OFStatisticsFactory, OFVendorDataFactory {
47 @Override
48 public OFMessage getMessage(OFType t) {
49 return t.newInstance();
50 }
51
52 @Override
53 public List<OFMessage> parseMessage(ChannelBuffer data) throws MessageParseException {
54 List<OFMessage> msglist = new ArrayList<OFMessage>();
55 OFMessage msg = null;
56
57 while (data.readableBytes() >= OFMessage.MINIMUM_LENGTH) {
58 data.markReaderIndex();
59 msg = this.parseMessageOne(data);
60 if (msg == null) {
61 data.resetReaderIndex();
62 break;
63 }
64 else {
65 msglist.add(msg);
66 }
67 }
68
69 if (msglist.size() == 0) {
70 return null;
71 }
72 return msglist;
73
74 }
75
76 public OFMessage parseMessageOne(ChannelBuffer data) throws MessageParseException {
77 try {
78 OFMessage demux = new OFMessage();
79 OFMessage ofm = null;
80
81 if (data.readableBytes() < OFMessage.MINIMUM_LENGTH)
82 return ofm;
83
84 data.markReaderIndex();
85 demux.readFrom(data);
86 data.resetReaderIndex();
87
88 if (demux.getLengthU() > data.readableBytes())
89 return ofm;
90
91 ofm = getMessage(demux.getType());
92 if (ofm == null)
93 return null;
94
95 if (ofm instanceof OFActionFactoryAware) {
96 ((OFActionFactoryAware)ofm).setActionFactory(this);
97 }
98 if (ofm instanceof OFMessageFactoryAware) {
99 ((OFMessageFactoryAware)ofm).setMessageFactory(this);
100 }
101 if (ofm instanceof OFStatisticsFactoryAware) {
102 ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this);
103 }
104 if (ofm instanceof OFVendorDataFactoryAware) {
105 ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this);
106 }
107 ofm.readFrom(data);
108 if (OFMessage.class.equals(ofm.getClass())) {
109 // advance the position for un-implemented messages
110 data.readerIndex(data.readerIndex()+(ofm.getLengthU() -
111 OFMessage.MINIMUM_LENGTH));
112 }
113
114 return ofm;
115 } catch (Exception e) {
116 /* Write the offending data along with the error message */
117 data.resetReaderIndex();
118 String msg =
119 "Message Parse Error for packet:" + dumpBuffer(data) +
120 "\nException: " + e.toString();
121 data.resetReaderIndex();
122
123 throw new MessageParseException(msg, e);
124 }
125 }
126
127 @Override
128 public OFAction getAction(OFActionType t) {
129 return t.newInstance();
130 }
131
132 @Override
133 public List<OFAction> parseActions(ChannelBuffer data, int length) {
134 return parseActions(data, length, 0);
135 }
136
137 @Override
138 public List<OFAction> parseActions(ChannelBuffer data, int length, int limit) {
139 List<OFAction> results = new ArrayList<OFAction>();
140 OFAction demux = new OFAction();
141 OFAction ofa;
142 int end = data.readerIndex() + length;
143
144 while (limit == 0 || results.size() <= limit) {
145 if ((data.readableBytes() < OFAction.MINIMUM_LENGTH ||
146 (data.readerIndex() + OFAction.MINIMUM_LENGTH) > end))
147 return results;
148
149 data.markReaderIndex();
150 demux.readFrom(data);
151 data.resetReaderIndex();
152
153 if ((demux.getLengthU() > data.readableBytes() ||
154 (data.readerIndex() + demux.getLengthU()) > end))
155 return results;
156
157 ofa = getAction(demux.getType());
158 ofa.readFrom(data);
159 if (OFAction.class.equals(ofa.getClass())) {
160 // advance the position for un-implemented messages
161 data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
162 OFAction.MINIMUM_LENGTH));
163 }
164 results.add(ofa);
165 }
166
167 return results;
168 }
169
170 @Override
171 public OFActionFactory getActionFactory() {
172 return this;
173 }
174
175 @Override
176 public OFStatistics getStatistics(OFType t, OFStatisticsType st) {
177 return st.newInstance(t);
178 }
179
180 @Override
181 public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
182 ChannelBuffer data, int length) {
183 return parseStatistics(t, st, data, length, 0);
184 }
185
186 /**
187 * @param t
188 * OFMessage type: should be one of stats_request or stats_reply
189 * @param st
190 * statistics type of this message, e.g., DESC, TABLE
191 * @param data
192 * buffer to read from
193 * @param length
194 * length of statistics
195 * @param limit
196 * number of statistics to grab; 0 == all
197 *
198 * @return list of statistics
199 */
200
201 @Override
202 public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
203 ChannelBuffer data, int length, int limit) {
204 List<OFStatistics> results = new ArrayList<OFStatistics>();
205 OFStatistics statistics = getStatistics(t, st);
206
207 int start = data.readerIndex();
208 int count = 0;
209
210 while (limit == 0 || results.size() <= limit) {
211 // TODO Create a separate MUX/DEMUX path for vendor stats
212 if (statistics instanceof OFVendorStatistics)
213 ((OFVendorStatistics)statistics).setLength(length);
214
215 /**
216 * can't use data.remaining() here, b/c there could be other data
217 * buffered past this message
218 */
219 if ((length - count) >= statistics.getLength()) {
220 if (statistics instanceof OFActionFactoryAware)
221 ((OFActionFactoryAware)statistics).setActionFactory(this);
222 statistics.readFrom(data);
223 results.add(statistics);
224 count += statistics.getLength();
225 statistics = getStatistics(t, st);
226 } else {
227 if (count < length) {
228 /**
229 * Nasty case: partial/incomplete statistic found even
230 * though we have a full message. Found when NOX sent
231 * agg_stats request with wrong agg statistics length (52
232 * instead of 56)
233 *
234 * just throw the rest away, or we will break framing
235 */
236 data.readerIndex(start + length);
237 }
238 return results;
239 }
240 }
241 return results; // empty; no statistics at all
242 }
243
244
245 @Override
246 public OFVendorData getVendorData(OFVendorId vendorId,
247 OFVendorDataType vendorDataType) {
248 if (vendorDataType == null)
249 return null;
250
251 return vendorDataType.newInstance();
252 }
253
254 /**
255 * Attempts to parse and return the OFVendorData contained in the given
256 * ChannelBuffer, beginning right after the vendor id.
257 * @param vendor the vendor id that was parsed from the OFVendor message.
258 * @param data the ChannelBuffer from which to parse the vendor data
259 * @param length the length to the end of the enclosing message.
260 * @return an OFVendorData instance
261 */
262 public OFVendorData parseVendorData(int vendor, ChannelBuffer data,
263 int length) {
264 OFVendorDataType vendorDataType = null;
265 OFVendorId vendorId = OFVendorId.lookupVendorId(vendor);
266 if (vendorId != null) {
267 data.markReaderIndex();
268 vendorDataType = vendorId.parseVendorDataType(data, length);
269 data.resetReaderIndex();
270 }
271
272 OFVendorData vendorData = getVendorData(vendorId, vendorDataType);
273 if (vendorData == null)
274 vendorData = new OFByteArrayVendorData();
275
276 vendorData.readFrom(data, length);
277
278 return vendorData;
279 }
280
281 public static String dumpBuffer(ChannelBuffer data) {
282 // NOTE: Reads all the bytes in buffer from current read offset.
283 // Set/Reset ReaderIndex if you want to read from a different location
284 int len = data.readableBytes();
285 StringBuffer sb = new StringBuffer();
286 for (int i=0 ; i<len; i++) {
287 if (i%32 == 0) sb.append("\n");
288 if (i%4 == 0) sb.append(" ");
289 sb.append(String.format("%02x", data.getUnsignedByte(i)));
290 }
291 return sb.toString();
292 }
293
294}