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
8 package org.opendaylight.openflowplugin.impl.protocol.serialization.messages;
10 import static java.util.Objects.requireNonNull;
11 import static java.util.Objects.requireNonNullElse;
13 import io.netty.buffer.ByteBuf;
14 import java.util.ArrayList;
16 import java.util.Objects;
17 import java.util.stream.Stream;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
20 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry;
21 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector;
22 import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
23 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
24 import org.opendaylight.openflowjava.util.ByteBufUtils;
25 import org.opendaylight.openflowplugin.api.OFConstants;
26 import org.opendaylight.openflowplugin.impl.protocol.serialization.util.InstructionUtil;
27 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.OrderComparator;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCase;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCase;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessageBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
58 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
59 import org.opendaylight.yangtools.yang.common.Uint16;
60 import org.opendaylight.yangtools.yang.common.Uint32;
61 import org.opendaylight.yangtools.yang.common.Uint64;
62 import org.opendaylight.yangtools.yang.common.Uint8;
65 * Translates FlowMod messages.
66 * OF protocol versions: 1.3
68 public class FlowMessageSerializer extends AbstractMessageSerializer<FlowMessage> implements
69 SerializerRegistryInjector {
70 private static final FlowCookie DEFAULT_COOKIE = new FlowCookie(OFConstants.DEFAULT_COOKIE);
71 private static final FlowCookie DEFAULT_COOKIE_MASK = new FlowCookie(OFConstants.DEFAULT_COOKIE_MASK);
72 private static final Uint8 DEFAULT_TABLE_ID = Uint8.ZERO;
73 private static final Uint16 DEFAULT_IDLE_TIMEOUT = Uint16.ZERO;
74 private static final Uint16 DEFAULT_HARD_TIMEOUT = Uint16.ZERO;
75 private static final Uint16 DEFAULT_PRIORITY = OFConstants.DEFAULT_FLOW_PRIORITY;
76 private static final Uint32 DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
77 private static final Uint64 DEFAULT_OUT_PORT = Uint64.valueOf(OFConstants.OFPP_ANY);
78 private static final Uint32 DEFAULT_OUT_GROUP = OFConstants.OFPG_ANY;
79 private static final FlowModFlags DEFAULT_FLAGS = new FlowModFlags(false, false, false, false, false);
80 private static final Uint16 PUSH_VLAN = Uint16.valueOf(0x8100);
81 private static final Integer PUSH_TAG = PUSH_VLAN.toJava();
82 private static final VlanCfi PUSH_CFI = new VlanCfi(1);
84 private static final VlanMatch VLAN_MATCH_FALSE = new VlanMatchBuilder()
85 .setVlanId(new VlanIdBuilder()
86 .setVlanIdPresent(false)
87 .setVlanId(new VlanId(Uint16.ZERO))
91 private static final VlanMatch VLAN_MATCH_TRUE = new VlanMatchBuilder()
92 .setVlanId(new VlanIdBuilder()
93 .setVlanIdPresent(true)
94 .setVlanId(new VlanId(Uint16.ZERO))
98 private SerializerRegistry registry;
101 public void serialize(final FlowMessage message, final ByteBuf outBuffer) {
102 if (!isVlanMatchPresent(message) && isSetVlanIdActionCasePresent(message)) {
103 writeVlanFlow(message, outBuffer);
105 writeFlow(message, outBuffer);
110 * Serialize flow message. Needs to be separated from main serialize method to prevent recursion
111 * when serializing SetVlanId flows.
113 * @param message flow message
114 * @param outBuffer output buffer
116 private void writeFlow(final FlowMessage message, final ByteBuf outBuffer) {
117 final int index = outBuffer.writerIndex();
118 super.serialize(message, outBuffer);
119 // FIXME: use Uint/ByteBuf utilities to skip toJava()/longValue()/intValue()
120 outBuffer.writeLong(requireNonNullElse(message.getCookie(), DEFAULT_COOKIE).getValue().longValue());
121 outBuffer.writeLong(requireNonNullElse(message.getCookieMask(), DEFAULT_COOKIE_MASK).getValue().longValue());
122 outBuffer.writeByte(requireNonNullElse(message.getTableId(), DEFAULT_TABLE_ID).toJava());
123 outBuffer.writeByte(message.getCommand().getIntValue());
124 outBuffer.writeShort(requireNonNullElse(message.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT).toJava());
125 outBuffer.writeShort(requireNonNullElse(message.getHardTimeout(), DEFAULT_HARD_TIMEOUT).toJava());
126 outBuffer.writeShort(requireNonNullElse(message.getPriority(), DEFAULT_PRIORITY).toJava());
127 outBuffer.writeInt(requireNonNullElse(message.getBufferId(), DEFAULT_BUFFER_ID).intValue());
128 outBuffer.writeInt(requireNonNullElse(message.getOutPort(), DEFAULT_OUT_PORT).intValue());
129 outBuffer.writeInt(requireNonNullElse(message.getOutGroup(), DEFAULT_OUT_GROUP).intValue());
130 outBuffer.writeShort(createFlowModFlagsBitmask(requireNonNullElse(message.getFlags(), DEFAULT_FLAGS)));
131 outBuffer.writeShort(0);
132 writeMatch(message, outBuffer);
133 writeInstructions(message, outBuffer);
134 outBuffer.setShort(index + 2, outBuffer.writerIndex() - index);
138 * Instead of serializing this flow normally, we need to split it to two parts if flow contains
139 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
141 * @param message flow mod message
142 * @param outBuffer output buffer
144 private void writeVlanFlow(final FlowMessage message, final ByteBuf outBuffer) {
145 writeFlow(new FlowMessageBuilder(message)
146 .setMatch(new MatchBuilder(message.getMatch()).setVlanMatch(VLAN_MATCH_FALSE).build())
147 .setInstructions(new InstructionsBuilder().setInstruction(updateSetVlanIdAction(message)).build())
148 .build(), outBuffer);
149 writeFlow(new FlowMessageBuilder(message)
150 .setMatch(new MatchBuilder(message.getMatch()).setVlanMatch(VLAN_MATCH_TRUE).build())
151 .build(), outBuffer);
155 * Serialize OpenFlowPlugin match to raw bytes.
157 * @param message OpenFlow flow mod message
158 * @param outBuffer output buffer
160 private void writeMatch(final FlowMessage message, final ByteBuf outBuffer) {
161 requireNonNull(registry).<Match, OFSerializer<Match>>getSerializer(
162 new MessageTypeKey<>(message.getVersion(), Match.class)).serialize(message.getMatch(), outBuffer);
166 * Serialize OpenFlowPlugin instructions and set ip protocol of set-tp-src and set-tp-dst actions of need.
168 * @param message OpenFlow flow mod message
169 * @param outBuffer output buffer
171 private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
172 final var instructions = message.getInstructions();
173 if (instructions == null) {
178 // Extract all instructions ...
179 Stream<Instruction> flowInstructions = instructions.nonnullInstruction().values().stream()
180 .filter(Objects::nonNull)
181 .sorted(OrderComparator.build())
182 .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Instruction::getInstruction)
183 .filter(Objects::nonNull);
185 // ... updated them if needed ...
186 final Uint8 protocol = extractProtocol(message);
187 if (protocol != null) {
188 flowInstructions = flowInstructions.map(insn -> updateInstruction(insn, protocol));
191 // ... and serialize them
192 flowInstructions.forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF_VERSION_1_3, registry,
196 // Try to get IP protocol from IP match
197 private static @Nullable Uint8 extractProtocol(final FlowMessage message) {
198 final var match = message.getMatch();
200 final var ipMatch = match.getIpMatch();
201 if (ipMatch != null) {
202 return ipMatch.getIpProtocol();
209 * Determine type of instruction and update it's actions if it is apply-actions instruction.
211 * @param instruction instruction
212 * @param protocol protocol
213 * @return updated or original instruction
215 private static Instruction updateInstruction(final Instruction instruction, final Uint8 protocol) {
216 if (instruction instanceof ApplyActionsCase applyActionsCase) {
217 final var actions = applyActionsCase.getApplyActions();
218 if (actions != null) {
219 return new ApplyActionsCaseBuilder()
220 .setApplyActions(new ApplyActionsBuilder()
221 .setAction(actions.nonnullAction().values().stream()
222 .filter(Objects::nonNull)
223 .map(a -> updateSetTpActions(a, protocol))
224 .collect(BindingMap.toOrderedMap()))
233 * If action is set-tp-src or set-tp-dst, inject IP protocol into it, otherwise return original action.
235 * @param action OpenFlow action
236 * @param protocol IP protocol
237 * @return updated OpenFlow action
239 private static Action updateSetTpActions(final Action action, final Uint8 protocol) {
240 if (action.getAction() instanceof SetTpSrcActionCase actionCase) {
241 return new ActionBuilder(action)
242 .setAction(new SetTpSrcActionCaseBuilder(actionCase)
243 .setSetTpSrcAction(new SetTpSrcActionBuilder(
244 actionCase.getSetTpSrcAction())
245 .setIpProtocol(protocol)
249 } else if (action.getAction() instanceof SetTpDstActionCase actionCase) {
250 return new ActionBuilder(action)
251 .setAction(new SetTpDstActionCaseBuilder(actionCase)
252 .setSetTpDstAction(new SetTpDstActionBuilder(actionCase.getSetTpDstAction())
253 .setIpProtocol(protocol)
259 // Return original action if no modifications are needed
264 * Create copy of instructions of original flow but insert
265 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase}
267 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
269 * @param message OpenFlowPlugin flow mod message
270 * @return list of instructions
273 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey,
274 org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction>
275 updateSetVlanIdAction(final FlowMessage message) {
276 return message.getInstructions().nonnullInstruction().values().stream()
277 .map(FlowMessageSerializer::updateSetVlanIdAction)
278 .collect(BindingMap.toOrderedMap());
281 private static org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction
282 updateSetVlanIdAction(final org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction
283 .list.Instruction insn) {
284 if (insn.getInstruction() instanceof ApplyActionsCase applyActionsCase) {
285 final var applyActions = applyActionsCase.getApplyActions();
286 if (applyActions != null) {
287 final var actions = applyActions.getAction();
288 if (actions != null && !actions.isEmpty()) {
289 final int[] offset = { 0 };
291 return new InstructionBuilder(insn)
292 .setInstruction(new ApplyActionsCaseBuilder()
293 .setApplyActions(new ApplyActionsBuilder()
294 .setAction(actions.values().stream()
295 .sorted(OrderComparator.build())
297 final var actionList = new ArrayList<org.opendaylight.yang.gen.v1.urn
298 .opendaylight.action.types.rev131112.action.list.Action>();
300 // If current action is SetVlanId, insert PushVlan action before it and update
302 if (action.getAction() instanceof SetVlanIdActionCase setVlanIdActionCase) {
303 actionList.add(new ActionBuilder()
304 .setAction(new PushVlanActionCaseBuilder()
305 .setPushVlanAction(new PushVlanActionBuilder()
307 .setVlanId(setVlanIdActionCase.getSetVlanIdAction().getVlanId())
308 .setEthernetType(PUSH_VLAN)
312 .withKey(action.key())
313 .setOrder(action.getOrder() + offset[0])
318 // Update offset of action if there is any inserted PushVlan actions
319 actionList.add(offset[0] <= 0 ? action : new ActionBuilder(action)
320 .setOrder(action.getOrder() + offset[0])
321 .withKey(new ActionKey(action.getOrder() + offset[0]))
323 return actionList.stream();
324 }).collect(BindingMap.toOrderedMap()))
335 * Create integer bit mask from
336 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
338 * @param flags flow mod flags
341 private static int createFlowModFlagsBitmask(final FlowModFlags flags) {
342 return ByteBufUtils.fillBitMask(0,
343 flags.getSENDFLOWREM(),
344 flags.getCHECKOVERLAP(),
345 flags.getRESETCOUNTS(),
346 flags.getNOPKTCOUNTS(),
347 flags.getNOBYTCOUNTS());
351 * Determine if flow contains vlan match.
354 * @return true if flow contains vlan match
356 private static boolean isVlanMatchPresent(final Flow flow) {
357 final var match = flow.getMatch();
358 return match != null && match.getVlanMatch() != null;
362 * Determine if flow contains
363 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight
364 * .flow.types.rev131026.instruction.instruction.ApplyActionsCase} instruction with
365 * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}
368 * @param flow OpenFlowPlugin flow
369 * @return true if flow contains SetVlanIdAction
371 private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
372 final var instructions = flow.getInstructions();
373 return instructions != null && instructions.nonnullInstruction().values().stream()
374 .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Instruction::getInstruction)
375 .filter(ApplyActionsCase.class::isInstance)
376 .map(i -> ((ApplyActionsCase) i).getApplyActions())
377 .filter(Objects::nonNull)
378 .flatMap(actionList -> actionList.nonnullAction().values().stream())
379 .map(Action::getAction)
380 .anyMatch(SetVlanIdActionCase.class::isInstance);
384 protected byte getMessageType() {
389 public void injectSerializerRegistry(final SerializerRegistry serializerRegistry) {
390 registry = serializerRegistry;