Remove Optional.ofNullable().map()
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / protocol / serialization / messages / FlowMessageSerializer.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.openflowplugin.impl.protocol.serialization.messages;
9
10 import static java.util.Objects.requireNonNull;
11 import static java.util.Objects.requireNonNullElse;
12
13 import io.netty.buffer.ByteBuf;
14 import java.util.ArrayList;
15 import java.util.Map;
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;
63
64 /**
65  * Translates FlowMod messages.
66  * OF protocol versions: 1.3
67  */
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);
83
84     private static final VlanMatch VLAN_MATCH_FALSE = new VlanMatchBuilder()
85             .setVlanId(new VlanIdBuilder()
86                     .setVlanIdPresent(false)
87                     .setVlanId(new VlanId(Uint16.ZERO))
88                     .build())
89             .build();
90
91     private static final VlanMatch VLAN_MATCH_TRUE = new VlanMatchBuilder()
92             .setVlanId(new VlanIdBuilder()
93                     .setVlanIdPresent(true)
94                     .setVlanId(new VlanId(Uint16.ZERO))
95                     .build())
96             .build();
97
98     private SerializerRegistry registry;
99
100     @Override
101     public void serialize(final FlowMessage message, final ByteBuf outBuffer) {
102         if (!isVlanMatchPresent(message) && isSetVlanIdActionCasePresent(message)) {
103             writeVlanFlow(message, outBuffer);
104         } else {
105             writeFlow(message, outBuffer);
106         }
107     }
108
109     /**
110      * Serialize flow message. Needs to be separated from main serialize method to prevent recursion
111      * when serializing SetVlanId flows.
112      *
113      * @param message   flow message
114      * @param outBuffer output buffer
115      */
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);
135     }
136
137     /**
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}.
140      *
141      * @param message   flow mod message
142      * @param outBuffer output buffer
143      */
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);
152     }
153
154     /**
155      * Serialize OpenFlowPlugin match to raw bytes.
156      *
157      * @param message   OpenFlow flow mod message
158      * @param outBuffer output buffer
159      */
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);
163     }
164
165     /**
166      * Serialize OpenFlowPlugin instructions and set ip protocol of set-tp-src and set-tp-dst actions of need.
167      *
168      * @param message   OpenFlow flow mod message
169      * @param outBuffer output buffer
170      */
171     private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
172         final var instructions = message.getInstructions();
173         if (instructions == null) {
174             // Nothing to do
175             return;
176         }
177
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);
184
185         // ... updated them if needed ...
186         final Uint8 protocol = extractProtocol(message);
187         if (protocol != null) {
188             flowInstructions = flowInstructions.map(insn -> updateInstruction(insn, protocol));
189         }
190
191         // ... and serialize them
192         flowInstructions.forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF_VERSION_1_3, registry,
193             outBuffer));
194     }
195
196     // Try to get IP protocol from IP match
197     private static @Nullable Uint8 extractProtocol(final FlowMessage message) {
198         final var match = message.getMatch();
199         if (match != null) {
200             final var ipMatch = match.getIpMatch();
201             if (ipMatch != null) {
202                 return ipMatch.getIpProtocol();
203             }
204         }
205         return null;
206     }
207
208     /**
209      * Determine type of instruction and update it's actions if it is apply-actions instruction.
210      *
211      * @param instruction instruction
212      * @param protocol    protocol
213      * @return updated or original instruction
214      */
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()))
225                         .build())
226                     .build();
227             }
228         }
229         return instruction;
230     }
231
232     /**
233      * If action is set-tp-src or set-tp-dst, inject IP protocol into it, otherwise return original action.
234      *
235      * @param action   OpenFlow action
236      * @param protocol IP protocol
237      * @return updated OpenFlow action
238      */
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)
246                         .build())
247                     .build())
248                 .build();
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)
254                         .build())
255                     .build())
256                 .build();
257         }
258
259         // Return original action if no modifications are needed
260         return action;
261     }
262
263     /**
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}
266      * before each
267      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
268      *
269      * @param message OpenFlowPlugin flow mod message
270      * @return list of instructions
271      */
272     private static Map<
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());
279     }
280
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 };
290
291                     return new InstructionBuilder(insn)
292                         .setInstruction(new ApplyActionsCaseBuilder()
293                             .setApplyActions(new ApplyActionsBuilder()
294                                 .setAction(actions.values().stream()
295                                     .sorted(OrderComparator.build())
296                                     .flatMap(action -> {
297                                         final var actionList = new ArrayList<org.opendaylight.yang.gen.v1.urn
298                                             .opendaylight.action.types.rev131112.action.list.Action>();
299
300                                         // If current action is SetVlanId, insert PushVlan action before it and update
301                                         // order
302                                         if (action.getAction() instanceof SetVlanIdActionCase setVlanIdActionCase) {
303                                             actionList.add(new ActionBuilder()
304                                                 .setAction(new PushVlanActionCaseBuilder()
305                                                     .setPushVlanAction(new PushVlanActionBuilder()
306                                                         .setCfi(PUSH_CFI)
307                                                         .setVlanId(setVlanIdActionCase.getSetVlanIdAction().getVlanId())
308                                                         .setEthernetType(PUSH_VLAN)
309                                                         .setTag(PUSH_TAG)
310                                                         .build())
311                                                     .build())
312                                                 .withKey(action.key())
313                                                 .setOrder(action.getOrder() + offset[0])
314                                                 .build());
315                                             offset[0]++;
316                                         }
317
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]))
322                                             .build());
323                                         return actionList.stream();
324                                     }).collect(BindingMap.toOrderedMap()))
325                                 .build())
326                             .build())
327                         .build();
328                 }
329             }
330         }
331         return insn;
332     }
333
334     /**
335      * Create integer bit mask from
336      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
337      *
338      * @param flags flow mod flags
339      * @return bit mask
340      */
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());
348     }
349
350     /**
351      * Determine if flow contains vlan match.
352      *
353      * @param flow flow
354      * @return true if flow contains vlan match
355      */
356     private static boolean isVlanMatchPresent(final Flow flow) {
357         final var match = flow.getMatch();
358         return match != null && match.getVlanMatch() != null;
359     }
360
361     /**
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}
366      * action.
367      *
368      * @param flow OpenFlowPlugin flow
369      * @return true if flow contains SetVlanIdAction
370      */
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);
381     }
382
383     @Override
384     protected byte getMessageType() {
385         return 14;
386     }
387
388     @Override
389     public void injectSerializerRegistry(final SerializerRegistry serializerRegistry) {
390         registry = serializerRegistry;
391     }
392 }