blob: a636a765803e5b79bf9d13104be5b0276071bc2a [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.provider.of.flow.impl;
Jonathan Hart86e59352014-10-22 10:42:16 -070017
alshabib346b5b32015-03-06 00:42:16 -080018import com.google.common.collect.Lists;
Jonathan Hart29afca32015-01-20 18:17:39 -080019import org.onlab.packet.Ip4Address;
Pavlin Radoslavovfebe82c2015-02-11 19:08:15 -080020import org.onlab.packet.Ip6Address;
Sho SHIMIZUe397ffe2015-05-12 16:24:23 -070021import org.onosproject.net.OchSignal;
Charles M.C. Chanfe421812015-01-12 18:20:51 +080022import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080023import org.onosproject.net.flow.FlowRule;
24import org.onosproject.net.flow.TrafficTreatment;
25import org.onosproject.net.flow.instructions.Instruction;
alshabib9af70072015-02-09 14:34:16 -080026import org.onosproject.net.flow.instructions.Instructions;
sangho8995ac52015-02-04 11:29:03 -080027import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
alshabib9af70072015-02-09 14:34:16 -080028import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Brian O'Connorabafb502014-12-02 22:26:20 -080029import org.onosproject.net.flow.instructions.L0ModificationInstruction;
30import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
Sho SHIMIZUe397ffe2015-05-12 16:24:23 -070031import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction;
Brian O'Connorabafb502014-12-02 22:26:20 -080032import org.onosproject.net.flow.instructions.L2ModificationInstruction;
33import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
Jonathan Hart29afca32015-01-20 18:17:39 -080034import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
Brian O'Connorabafb502014-12-02 22:26:20 -080035import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
36import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
Brian O'Connorabafb502014-12-02 22:26:20 -080037import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions;
38import org.onosproject.net.flow.instructions.L3ModificationInstruction;
39import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
Pavlin Radoslavovfebe82c2015-02-11 19:08:15 -080040import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction;
Jonathan Hart86e59352014-10-22 10:42:16 -070041import org.projectfloodlight.openflow.protocol.OFFactory;
42import org.projectfloodlight.openflow.protocol.OFFlowAdd;
43import org.projectfloodlight.openflow.protocol.OFFlowDelete;
44import org.projectfloodlight.openflow.protocol.OFFlowMod;
45import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
46import org.projectfloodlight.openflow.protocol.action.OFAction;
sangho8995ac52015-02-04 11:29:03 -080047import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
Charles M.C. Chanfe421812015-01-12 18:20:51 +080048import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
alshabib9af70072015-02-09 14:34:16 -080049import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
Jonathan Hart86e59352014-10-22 10:42:16 -070050import org.projectfloodlight.openflow.protocol.match.Match;
51import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
52import org.projectfloodlight.openflow.types.CircuitSignalID;
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -080053import org.projectfloodlight.openflow.types.EthType;
Jonathan Hart86e59352014-10-22 10:42:16 -070054import org.projectfloodlight.openflow.types.IPv4Address;
Pavlin Radoslavovfebe82c2015-02-11 19:08:15 -080055import org.projectfloodlight.openflow.types.IPv6Address;
56import org.projectfloodlight.openflow.types.IPv6FlowLabel;
Jonathan Hart86e59352014-10-22 10:42:16 -070057import org.projectfloodlight.openflow.types.MacAddress;
58import org.projectfloodlight.openflow.types.OFBufferId;
sangho8995ac52015-02-04 11:29:03 -080059import org.projectfloodlight.openflow.types.OFGroup;
Jonathan Hart86e59352014-10-22 10:42:16 -070060import org.projectfloodlight.openflow.types.OFPort;
61import org.projectfloodlight.openflow.types.OFVlanVidMatch;
alshabib9af70072015-02-09 14:34:16 -080062import org.projectfloodlight.openflow.types.TableId;
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -080063import org.projectfloodlight.openflow.types.U32;
Jonathan Hart86e59352014-10-22 10:42:16 -070064import org.projectfloodlight.openflow.types.U64;
65import org.projectfloodlight.openflow.types.VlanPcp;
66import org.slf4j.Logger;
67import org.slf4j.LoggerFactory;
68
Jonathan Hart29afca32015-01-20 18:17:39 -080069import java.util.Collections;
70import java.util.LinkedList;
71import java.util.List;
72import java.util.Optional;
73
Sho SHIMIZUc15ce512015-05-26 16:54:08 -070074import static org.onosproject.provider.of.flow.impl.FlowModBuilderHelper.convertChannelSpacing;
75import static org.onosproject.provider.of.flow.impl.FlowModBuilderHelper.convertGridType;
76
Jonathan Hart86e59352014-10-22 10:42:16 -070077/**
78 * Flow mod builder for OpenFlow 1.3+.
79 */
80public class FlowModBuilderVer13 extends FlowModBuilder {
81
alshabib10580802015-02-18 18:30:33 -080082 private final Logger log = LoggerFactory.getLogger(getClass());
Charles M.C. Chanfe421812015-01-12 18:20:51 +080083 private static final int OFPCML_NO_BUFFER = 0xffff;
Jonathan Hart86e59352014-10-22 10:42:16 -070084
85 private final TrafficTreatment treatment;
86
87 /**
88 * Constructor for a flow mod builder for OpenFlow 1.3.
89 *
90 * @param flowRule the flow rule to transform into a flow mod
91 * @param factory the OpenFlow factory to use to build the flow mod
Pavlin Radoslavov119fd5c2014-11-25 19:08:19 -080092 * @param xid the transaction ID
Jonathan Hart86e59352014-10-22 10:42:16 -070093 */
Brian O'Connor427a1762014-11-19 18:40:32 -080094 protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
95 super(flowRule, factory, xid);
Jonathan Hart86e59352014-10-22 10:42:16 -070096
97 this.treatment = flowRule.treatment();
98 }
99
100 @Override
101 public OFFlowAdd buildFlowAdd() {
102 Match match = buildMatch();
alshabib346b5b32015-03-06 00:42:16 -0800103 List<OFAction> deferredActions = buildActions(treatment.deferred());
104 List<OFAction> immediateActions = buildActions(treatment.immediate());
105 List<OFInstruction> instructions = Lists.newLinkedList();
Saurav Dascbe6de32015-03-01 18:30:46 -0800106
Jonathan Hartd4a8bba2014-10-28 12:44:20 -0700107
alshabib346b5b32015-03-06 00:42:16 -0800108 if (treatment.clearedDeferred()) {
109 instructions.add(factory().instructions().clearActions());
110 }
sangho1e575652015-05-14 00:39:53 -0700111 if (immediateActions.size() > 0) {
112 instructions.add(factory().instructions().applyActions(immediateActions));
113 }
alshabib346b5b32015-03-06 00:42:16 -0800114 if (deferredActions.size() > 0) {
115 instructions.add(factory().instructions().writeActions(deferredActions));
116 }
117 if (treatment.tableTransition() != null) {
118 instructions.add(buildTableGoto(treatment.tableTransition()));
Saurav Dascbe6de32015-03-01 18:30:46 -0800119 }
Jonathan Hart86e59352014-10-22 10:42:16 -0700120
121 long cookie = flowRule().id().value();
122
Jonathan Hart86e59352014-10-22 10:42:16 -0700123 OFFlowAdd fm = factory().buildFlowAdd()
Brian O'Connor427a1762014-11-19 18:40:32 -0800124 .setXid(xid)
Jonathan Hart86e59352014-10-22 10:42:16 -0700125 .setCookie(U64.of(cookie))
126 .setBufferId(OFBufferId.NO_BUFFER)
alshabib9af70072015-02-09 14:34:16 -0800127 .setInstructions(instructions)
Jonathan Hart86e59352014-10-22 10:42:16 -0700128 .setMatch(match)
129 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
130 .setPriority(flowRule().priority())
alshabib08d98982015-04-21 16:25:50 -0700131 .setTableId(TableId.of(flowRule().tableId()))
Jonathan Hart86e59352014-10-22 10:42:16 -0700132 .build();
133
134 return fm;
135 }
136
137 @Override
138 public OFFlowMod buildFlowMod() {
139 Match match = buildMatch();
alshabib346b5b32015-03-06 00:42:16 -0800140 List<OFAction> deferredActions = buildActions(treatment.deferred());
141 List<OFAction> immediateActions = buildActions(treatment.immediate());
142 List<OFInstruction> instructions = Lists.newLinkedList();
Jonathan Harteda33872015-02-18 10:22:32 -0800143
alshabib346b5b32015-03-06 00:42:16 -0800144
145 if (immediateActions.size() > 0) {
146 instructions.add(factory().instructions().applyActions(immediateActions));
147 }
148 if (treatment.clearedDeferred()) {
149 instructions.add(factory().instructions().clearActions());
150 }
151 if (deferredActions.size() > 0) {
152 instructions.add(factory().instructions().writeActions(deferredActions));
153 }
154 if (treatment.tableTransition() != null) {
155 instructions.add(buildTableGoto(treatment.tableTransition()));
Saurav Dascbe6de32015-03-01 18:30:46 -0800156 }
Jonathan Hart86e59352014-10-22 10:42:16 -0700157
158 long cookie = flowRule().id().value();
159
Jonathan Hart86e59352014-10-22 10:42:16 -0700160 OFFlowMod fm = factory().buildFlowModify()
Brian O'Connor427a1762014-11-19 18:40:32 -0800161 .setXid(xid)
Jonathan Hart86e59352014-10-22 10:42:16 -0700162 .setCookie(U64.of(cookie))
163 .setBufferId(OFBufferId.NO_BUFFER)
alshabib9af70072015-02-09 14:34:16 -0800164 .setInstructions(instructions)
Jonathan Hart86e59352014-10-22 10:42:16 -0700165 .setMatch(match)
166 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
167 .setPriority(flowRule().priority())
alshabib08d98982015-04-21 16:25:50 -0700168 .setTableId(TableId.of(flowRule().tableId()))
Jonathan Hart86e59352014-10-22 10:42:16 -0700169 .build();
170
171 return fm;
172 }
173
174 @Override
175 public OFFlowDelete buildFlowDel() {
176 Match match = buildMatch();
Jonathan Hart86e59352014-10-22 10:42:16 -0700177
178 long cookie = flowRule().id().value();
179
180 OFFlowDelete fm = factory().buildFlowDelete()
Brian O'Connor427a1762014-11-19 18:40:32 -0800181 .setXid(xid)
Jonathan Hart86e59352014-10-22 10:42:16 -0700182 .setCookie(U64.of(cookie))
183 .setBufferId(OFBufferId.NO_BUFFER)
Jonathan Hart86e59352014-10-22 10:42:16 -0700184 .setMatch(match)
185 .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
186 .setPriority(flowRule().priority())
alshabib08d98982015-04-21 16:25:50 -0700187 .setTableId(TableId.of(flowRule().tableId()))
Jonathan Hart86e59352014-10-22 10:42:16 -0700188 .build();
189
190 return fm;
191 }
192
alshabib346b5b32015-03-06 00:42:16 -0800193 private List<OFAction> buildActions(List<Instruction> treatments) {
Jonathan Hart86e59352014-10-22 10:42:16 -0700194 if (treatment == null) {
Sho SHIMIZU6601dd72015-05-11 19:00:29 -0700195 return Collections.emptyList();
Jonathan Hart86e59352014-10-22 10:42:16 -0700196 }
Sho SHIMIZU6601dd72015-05-11 19:00:29 -0700197
198 boolean tableFound = false;
199 List<OFAction> actions = new LinkedList<>();
alshabib346b5b32015-03-06 00:42:16 -0800200 for (Instruction i : treatments) {
Jonathan Hart86e59352014-10-22 10:42:16 -0700201 switch (i.type()) {
alshabib9af70072015-02-09 14:34:16 -0800202 case DROP:
Sho SHIMIZUb35ed362015-04-28 11:05:19 -0700203 return Collections.emptyList();
alshabib9af70072015-02-09 14:34:16 -0800204 case L0MODIFICATION:
205 actions.add(buildL0Modification(i));
206 break;
207 case L2MODIFICATION:
208 actions.add(buildL2Modification(i));
209 break;
210 case L3MODIFICATION:
211 actions.add(buildL3Modification(i));
212 break;
213 case OUTPUT:
214 OutputInstruction out = (OutputInstruction) i;
215 OFActionOutput.Builder action = factory().actions().buildOutput()
216 .setPort(OFPort.of((int) out.port().toLong()));
217 if (out.port().equals(PortNumber.CONTROLLER)) {
218 action.setMaxLen(OFPCML_NO_BUFFER);
219 }
220 actions.add(action.build());
221 break;
222 case GROUP:
223 GroupInstruction group = (GroupInstruction) i;
224 OFActionGroup.Builder groupBuilder = factory().actions().buildGroup()
225 .setGroup(OFGroup.of(group.groupId().id()));
226 actions.add(groupBuilder.build());
227 break;
alshabib10580802015-02-18 18:30:33 -0800228 case TABLE:
229 //FIXME: should not occur here.
Saurav Dascbe6de32015-03-01 18:30:46 -0800230 tableFound = true;
alshabib10580802015-02-18 18:30:33 -0800231 break;
alshabib9af70072015-02-09 14:34:16 -0800232 default:
233 log.warn("Instruction type {} not yet implemented.", i.type());
Jonathan Hart86e59352014-10-22 10:42:16 -0700234 }
235 }
Saurav Dascbe6de32015-03-01 18:30:46 -0800236 if (tableFound && actions.isEmpty()) {
237 // handles the case where there are no actions, but there is
238 // a goto instruction for the next table
Sho SHIMIZU6601dd72015-05-11 19:00:29 -0700239 return Collections.emptyList();
Saurav Dascbe6de32015-03-01 18:30:46 -0800240 }
Jonathan Hart86e59352014-10-22 10:42:16 -0700241 return actions;
242 }
243
alshabib9af70072015-02-09 14:34:16 -0800244 private OFInstruction buildTableGoto(Instructions.TableTypeTransition i) {
245 OFInstruction instruction = factory().instructions().gotoTable(
alshabibd17abc22015-04-21 18:26:35 -0700246 TableId.of(i.tableId()));
alshabib9af70072015-02-09 14:34:16 -0800247 return instruction;
248 }
249
Jonathan Hart86e59352014-10-22 10:42:16 -0700250 private OFAction buildL0Modification(Instruction i) {
251 L0ModificationInstruction l0m = (L0ModificationInstruction) i;
252 switch (l0m.subtype()) {
Sho SHIMIZU5cd7ce92015-05-11 19:10:38 -0700253 case LAMBDA:
Sho SHIMIZUe397ffe2015-05-12 16:24:23 -0700254 return buildModLambdaInstruction((ModLambdaInstruction) i);
255 case OCH:
256 try {
257 return buildModOchSignalInstruction((ModOchSignalInstruction) i);
258 } catch (UnsupportedGridTypeException | UnsupportedChannelSpacingException e) {
259 log.warn(e.getMessage());
260 break;
261 }
Sho SHIMIZU5cd7ce92015-05-11 19:10:38 -0700262 default:
263 log.warn("Unimplemented action type {}.", l0m.subtype());
264 break;
Jonathan Hart86e59352014-10-22 10:42:16 -0700265 }
266 return null;
267 }
268
Sho SHIMIZUe397ffe2015-05-12 16:24:23 -0700269 private OFAction buildModLambdaInstruction(ModLambdaInstruction instruction) {
270 return factory().actions().circuit(factory().oxms().ochSigidBasic(
271 new CircuitSignalID((byte) 1, (byte) 2, instruction.lambda(), (short) 1)));
272 }
273
274 private OFAction buildModOchSignalInstruction(ModOchSignalInstruction instruction) {
275 OchSignal signal = instruction.lambda();
276 byte gridType = convertGridType(signal.gridType());
277 byte channelSpacing = convertChannelSpacing(signal.channelSpacing());
278
279 return factory().actions().circuit(factory().oxms().ochSigidBasic(
280 new CircuitSignalID(gridType, channelSpacing,
281 (short) signal.spacingMultiplier(), (short) signal.slotGranularity())
282 ));
283 }
284
Jonathan Hart86e59352014-10-22 10:42:16 -0700285 private OFAction buildL2Modification(Instruction i) {
286 L2ModificationInstruction l2m = (L2ModificationInstruction) i;
287 ModEtherInstruction eth;
288 OFOxm<?> oxm = null;
289 switch (l2m.subtype()) {
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800290 case ETH_DST:
291 eth = (ModEtherInstruction) l2m;
292 oxm = factory().oxms().ethDst(MacAddress.of(eth.mac().toLong()));
293 break;
294 case ETH_SRC:
295 eth = (ModEtherInstruction) l2m;
296 oxm = factory().oxms().ethSrc(MacAddress.of(eth.mac().toLong()));
297 break;
298 case VLAN_ID:
299 ModVlanIdInstruction vlanId = (ModVlanIdInstruction) l2m;
300 oxm = factory().oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort()));
301 break;
302 case VLAN_PCP:
303 ModVlanPcpInstruction vlanPcp = (ModVlanPcpInstruction) l2m;
304 oxm = factory().oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp()));
305 break;
306 case MPLS_PUSH:
307 PushHeaderInstructions pushHeaderInstructions =
308 (PushHeaderInstructions) l2m;
309 return factory().actions().pushMpls(EthType.of(pushHeaderInstructions
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800310 .ethernetType()));
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800311 case MPLS_POP:
Jonathan Hart67fc0972015-03-19 15:21:20 -0700312 PushHeaderInstructions popHeaderInstructions =
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800313 (PushHeaderInstructions) l2m;
314 return factory().actions().popMpls(EthType.of(popHeaderInstructions
Yuta HIGUCHI32a53c52015-02-08 01:25:40 -0800315 .ethernetType()));
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800316 case MPLS_LABEL:
317 ModMplsLabelInstruction mplsLabel =
318 (ModMplsLabelInstruction) l2m;
319 oxm = factory().oxms().mplsLabel(U32.of(mplsLabel.label()
320 .longValue()));
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800321 break;
sangho3f97a17d2015-01-29 22:56:29 -0800322 case DEC_MPLS_TTL:
323 return factory().actions().decMplsTtl();
Saurav Dasfbe25c52015-03-04 11:12:00 -0800324 case VLAN_POP:
325 return factory().actions().popVlan();
Jonathan Hart54b406b2015-03-06 16:24:14 -0800326 case VLAN_PUSH:
327 PushHeaderInstructions pushVlanInstruction = (PushHeaderInstructions) l2m;
328 return factory().actions().pushVlan(
329 EthType.of(pushVlanInstruction.ethernetType()));
Praseed Balakrishnan8c67d172014-11-10 10:15:41 -0800330 default:
331 log.warn("Unimplemented action type {}.", l2m.subtype());
332 break;
Jonathan Hart86e59352014-10-22 10:42:16 -0700333 }
334
335 if (oxm != null) {
336 return factory().actions().buildSetField().setField(oxm).build();
337 }
338 return null;
339 }
340
341 private OFAction buildL3Modification(Instruction i) {
342 L3ModificationInstruction l3m = (L3ModificationInstruction) i;
343 ModIPInstruction ip;
Pavlin Radoslavov23e398d2014-11-05 15:17:57 -0800344 Ip4Address ip4;
Pavlin Radoslavovfebe82c2015-02-11 19:08:15 -0800345 Ip6Address ip6;
Jonathan Hart86e59352014-10-22 10:42:16 -0700346 OFOxm<?> oxm = null;
347 switch (l3m.subtype()) {
Sho SHIMIZU5cd7ce92015-05-11 19:10:38 -0700348 case IPV4_SRC:
349 ip = (ModIPInstruction) i;
350 ip4 = ip.ip().getIp4Address();
351 oxm = factory().oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
352 break;
353 case IPV4_DST:
354 ip = (ModIPInstruction) i;
355 ip4 = ip.ip().getIp4Address();
356 oxm = factory().oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
357 break;
358 case IPV6_SRC:
359 ip = (ModIPInstruction) i;
360 ip6 = ip.ip().getIp6Address();
361 oxm = factory().oxms().ipv6Src(IPv6Address.of(ip6.toOctets()));
362 break;
363 case IPV6_DST:
364 ip = (ModIPInstruction) i;
365 ip6 = ip.ip().getIp6Address();
366 oxm = factory().oxms().ipv6Dst(IPv6Address.of(ip6.toOctets()));
367 break;
368 case IPV6_FLABEL:
369 ModIPv6FlowLabelInstruction flowLabelInstruction =
370 (ModIPv6FlowLabelInstruction) i;
371 int flowLabel = flowLabelInstruction.flowLabel();
372 oxm = factory().oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
373 break;
374 case DEC_TTL:
375 return factory().actions().decNwTtl();
376 case TTL_IN:
377 return factory().actions().copyTtlIn();
378 case TTL_OUT:
379 return factory().actions().copyTtlOut();
380 default:
381 log.warn("Unimplemented action type {}.", l3m.subtype());
382 break;
Jonathan Hart86e59352014-10-22 10:42:16 -0700383 }
384
385 if (oxm != null) {
386 return factory().actions().buildSetField().setField(oxm).build();
387 }
388 return null;
389 }
390
391}