2 * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.openflowplugin.impl.protocol.serialization.messages;
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import io.netty.buffer.ByteBuf;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.stream.Collectors;
21 import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
22 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry;
23 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector;
24 import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
25 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
26 import org.opendaylight.openflowjava.util.ByteBufUtils;
27 import org.opendaylight.openflowplugin.api.OFConstants;
28 import org.opendaylight.openflowplugin.impl.protocol.serialization.util.InstructionUtil;
29 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.OrderComparator;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.ActionList;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCase;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessageBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
62 * Translates FlowMod messages.
63 * OF protocol versions: 1.3
65 public class FlowMessageSerializer extends AbstractMessageSerializer<FlowMessage> implements
66 SerializerRegistryInjector {
67 private static final FlowCookie DEFAULT_COOKIE = new FlowCookie(OFConstants.DEFAULT_COOKIE);
68 private static final FlowCookie DEFAULT_COOKIE_MASK = new FlowCookie(OFConstants.DEFAULT_COOKIE_MASK);
69 private static final Short DEFAULT_TABLE_ID = (short) 0;
70 private static final Integer DEFAULT_IDLE_TIMEOUT = 0;
71 private static final Integer DEFAULT_HARD_TIMEOUT = 0;
72 private static final Integer DEFAULT_PRIORITY = OFConstants.DEFAULT_FLOW_PRIORITY;
73 private static final Long DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
74 private static final BigInteger DEFAULT_OUT_PORT = BigInteger.valueOf(OFConstants.OFPP_ANY);
75 private static final Long DEFAULT_OUT_GROUP = OFConstants.OFPG_ANY;
76 private static final byte PADDING_IN_FLOW_MOD_MESSAGE = 2;
77 private static final FlowModFlags DEFAULT_FLAGS = new FlowModFlags(false, false, false, false, false);
78 private static final Integer PUSH_VLAN = 0x8100;
80 private static final VlanMatch VLAN_MATCH_FALSE = new VlanMatchBuilder()
81 .setVlanId(new VlanIdBuilder()
82 .setVlanIdPresent(false)
83 .setVlanId(new VlanId(0))
87 private static final VlanMatch VLAN_MATCH_TRUE = new VlanMatchBuilder()
88 .setVlanId(new VlanIdBuilder()
89 .setVlanIdPresent(true)
90 .setVlanId(new VlanId(0))
94 private SerializerRegistry registry;
97 public void serialize(FlowMessage message, ByteBuf outBuffer) {
98 if (!isVlanMatchPresent(message) && isSetVlanIdActionCasePresent(message)) {
99 writeVlanFlow(message, outBuffer);
101 writeFlow(message, outBuffer);
106 * Serialize flow message. Needs to be separated from main serialize method to prevent recursion
107 * when serializing SetVlanId flows.
109 * @param message flow message
110 * @param outBuffer output buffer
112 private void writeFlow(final FlowMessage message, final ByteBuf outBuffer) {
113 final int index = outBuffer.writerIndex();
114 super.serialize(message, outBuffer);
115 outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookie(), DEFAULT_COOKIE).getValue().longValue());
116 outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookieMask(), DEFAULT_COOKIE_MASK).getValue()
118 outBuffer.writeByte(MoreObjects.firstNonNull(message.getTableId(), DEFAULT_TABLE_ID));
119 outBuffer.writeByte(message.getCommand().getIntValue());
120 outBuffer.writeShort(MoreObjects.firstNonNull(message.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT));
121 outBuffer.writeShort(MoreObjects.firstNonNull(message.getHardTimeout(), DEFAULT_HARD_TIMEOUT));
122 outBuffer.writeShort(MoreObjects.firstNonNull(message.getPriority(), DEFAULT_PRIORITY));
123 outBuffer.writeInt(MoreObjects.firstNonNull(message.getBufferId(), DEFAULT_BUFFER_ID).intValue());
124 outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutPort(), DEFAULT_OUT_PORT).intValue());
125 outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutGroup(), DEFAULT_OUT_GROUP).intValue());
126 outBuffer.writeShort(createFlowModFlagsBitmask(MoreObjects.firstNonNull(message.getFlags(), DEFAULT_FLAGS)));
127 outBuffer.writeZero(PADDING_IN_FLOW_MOD_MESSAGE);
128 writeMatch(message, outBuffer);
129 writeInstructions(message, outBuffer);
130 outBuffer.setShort(index + 2, outBuffer.writerIndex() - index);
134 * Instead of serializing this flow normally, we need to split it to two parts if flow contains
135 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
137 * @param message flow mod message
138 * @param outBuffer output buffer
140 private void writeVlanFlow(final FlowMessage message, final ByteBuf outBuffer) {
142 new FlowMessageBuilder(message)
143 .setMatch(new MatchBuilder(message.getMatch())
144 .setVlanMatch(VLAN_MATCH_FALSE)
146 .setInstructions(new InstructionsBuilder()
147 .setInstruction(updateSetVlanIdAction(message))
153 new FlowMessageBuilder(message)
154 .setMatch(new MatchBuilder(message.getMatch())
155 .setVlanMatch(VLAN_MATCH_TRUE)
162 * Serialize OpenFlowPlugin match to raw bytes.
164 * @param message OpenFlow flow mod message
165 * @param outBuffer output buffer
167 private void writeMatch(final FlowMessage message, final ByteBuf outBuffer) {
168 Preconditions.checkNotNull(registry).<Match, OFSerializer<Match>>getSerializer(
169 new MessageTypeKey<>(message.getVersion(), Match.class)).serialize(message.getMatch(), outBuffer);
174 * Serialize OpenFlowPlugin instructions and set ip protocol of set-tp-src and set-tp-dst actions of need.
176 * @param message OpenFlow flow mod message
177 * @param outBuffer output buffer
179 @SuppressWarnings("unchecked")
180 private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
181 // Try to get IP protocol from IP match
182 final Optional<Short> protocol = Optional
183 .ofNullable(message.getMatch())
184 .flatMap(m -> Optional.ofNullable(m.getIpMatch()))
185 .flatMap(ipm -> Optional.ofNullable(ipm.getIpProtocol()));
187 // Update instructions if needed and then serialize all instructions
188 Optional.ofNullable(message.getInstructions())
189 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
192 .filter(Objects::nonNull)
193 .sorted(OrderComparator.build())
194 .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
195 .rev131026.Instruction::getInstruction)
196 .filter(Objects::nonNull)
197 .map(i -> protocol.flatMap(p -> updateInstruction(i, p)).orElse(i))
198 .forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF13_VERSION_ID, registry,
203 * Determine type of instruction and update it's actions if it is apply-actions instruction.
205 * @param instruction instruction
206 * @param protocol protocol
207 * @return updated instruction or empty
209 private static Optional<Instruction> updateInstruction(final Instruction instruction, final Short protocol) {
210 if (ApplyActionsCase.class.isInstance(instruction)) {
212 .ofNullable(ApplyActionsCase.class.cast(instruction).getApplyActions())
213 .flatMap(aa -> Optional.ofNullable(aa.getAction()))
214 .map(as -> new ApplyActionsCaseBuilder()
215 .setApplyActions(new ApplyActionsBuilder()
218 .filter(Objects::nonNull)
219 .map(a -> updateSetTpActions(a, protocol))
220 .collect(Collectors.toList()))
225 return Optional.empty();
229 * If action is set-tp-src or set-tp-dst, inject IP protocol into it, otherwise return original action.
231 * @param action OpenFlow action
232 * @param protocol IP protocol
233 * @return updated OpenFlow action
235 private static Action updateSetTpActions(Action action, Short protocol) {
236 if (SetTpSrcActionCase.class.isInstance(action.getAction())) {
237 final SetTpSrcActionCase actionCase = SetTpSrcActionCase.class.cast(action.getAction());
239 return new ActionBuilder(action)
240 .setAction(new SetTpSrcActionCaseBuilder(actionCase)
241 .setSetTpSrcAction(new SetTpSrcActionBuilder(
242 actionCase.getSetTpSrcAction())
243 .setIpProtocol(protocol)
247 } else if (SetTpDstActionCase.class.isInstance(action.getAction())) {
248 final SetTpDstActionCase actionCase = SetTpDstActionCase.class.cast(action.getAction());
250 return new ActionBuilder(action)
251 .setAction(new SetTpDstActionCaseBuilder(actionCase)
252 .setSetTpDstAction(new SetTpDstActionBuilder(
253 actionCase.getSetTpDstAction())
254 .setIpProtocol(protocol)
260 // Return original action if no modifications are needed
265 * Create copy of instructions of original flow but insert
266 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase}
268 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
270 * @param message OpenFlowPlugin flow mod message
271 * @return list of instructions
273 private static List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list
274 .Instruction> updateSetVlanIdAction(final FlowMessage message) {
275 return message.getInstructions().getInstruction()
278 final int[] offset = {0};
280 return ApplyActionsCase.class.isInstance(i.getInstruction())
282 .ofNullable(ApplyActionsCase.class.cast(i.getInstruction()).getApplyActions())
283 .flatMap(as -> Optional.ofNullable(as.getAction()))
285 .sorted(OrderComparator.build())
287 final List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112
288 .action.list.Action> actions = new ArrayList<>();
290 // If current action is SetVlanId, insert PushVlan action before it and
292 if (SetVlanIdActionCase.class.isInstance(action.getAction())) {
293 actions.add(new ActionBuilder()
294 .setAction(new PushVlanActionCaseBuilder()
295 .setPushVlanAction(new PushVlanActionBuilder()
296 .setCfi(new VlanCfi(1))
297 .setVlanId(SetVlanIdActionCase.class.cast(action
298 .getAction()).getSetVlanIdAction()
300 .setEthernetType(PUSH_VLAN)
304 .setKey(action.getKey())
305 .setOrder(action.getOrder() + offset[0])
311 // Update offset of action if there is any inserted PushVlan actions
312 actions.add(offset[0] > 0
313 ? new ActionBuilder(action).setOrder(action.getOrder() + offset[0])
317 return actions.stream();
319 .map(as -> new InstructionBuilder(i)
320 .setInstruction(new ApplyActionsCaseBuilder()
321 .setApplyActions(new ApplyActionsBuilder()
322 .setAction(as.collect(Collectors.toList()))
328 }).collect(Collectors.toList());
332 * Create integer bit mask from
333 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
335 * @param flags flow mod flags
338 private static int createFlowModFlagsBitmask(final FlowModFlags flags) {
339 return ByteBufUtils.fillBitMask(0,
340 flags.isSENDFLOWREM(),
341 flags.isCHECKOVERLAP(),
342 flags.isRESETCOUNTS(),
343 flags.isNOPKTCOUNTS(),
344 flags.isNOBYTCOUNTS());
348 * Determine if flow contains vlan match.
351 * @return true if flow contains vlan match
353 private static boolean isVlanMatchPresent(final Flow flow) {
355 .ofNullable(flow.getMatch())
356 .flatMap(m -> Optional.ofNullable(m.getVlanMatch()))
361 * Determine if flow contains
362 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight
363 * .flow.types.rev131026.instruction.instruction.ApplyActionsCase} instruction with
364 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}
367 * @param flow OpenFlowPlugin flow
368 * @return true if flow contains SetVlanIdAction
370 private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
372 .ofNullable(flow.getInstructions())
373 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
376 .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
377 .rev131026.Instruction::getInstruction)
378 .filter(ApplyActionsCase.class::isInstance)
379 .map(i -> ApplyActionsCase.class.cast(i).getApplyActions())
380 .filter(Objects::nonNull)
381 .map(ActionList::getAction)
382 .filter(Objects::nonNull)
383 .flatMap(Collection::stream)
384 .map(Action::getAction)
385 .filter(SetVlanIdActionCase.class::isInstance)
391 protected byte getMessageType() {
396 public void injectSerializerRegistry(SerializerRegistry serializerRegistry) {
397 registry = serializerRegistry;