b1820141d89b535aa6ac6ab607c80bbb2f64b610
[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 com.google.common.base.MoreObjects;
11 import com.google.common.base.Preconditions;
12 import io.netty.buffer.ByteBuf;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.Objects;
16 import java.util.Optional;
17 import java.util.stream.Collectors;
18 import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
19 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry;
20 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector;
21 import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
22 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
23 import org.opendaylight.openflowjava.util.ByteBufUtils;
24 import org.opendaylight.openflowplugin.api.OFConstants;
25 import org.opendaylight.openflowplugin.impl.protocol.serialization.util.InstructionUtil;
26 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.OrderComparator;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.ActionList;
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.common.Uint16;
59 import org.opendaylight.yangtools.yang.common.Uint32;
60 import org.opendaylight.yangtools.yang.common.Uint64;
61 import org.opendaylight.yangtools.yang.common.Uint8;
62
63 /**
64  * Translates FlowMod messages.
65  * OF protocol versions: 1.3
66  */
67 public class FlowMessageSerializer extends AbstractMessageSerializer<FlowMessage> implements
68         SerializerRegistryInjector {
69     private static final FlowCookie DEFAULT_COOKIE = new FlowCookie(OFConstants.DEFAULT_COOKIE);
70     private static final FlowCookie DEFAULT_COOKIE_MASK = new FlowCookie(OFConstants.DEFAULT_COOKIE_MASK);
71     private static final Uint8 DEFAULT_TABLE_ID = Uint8.ZERO;
72     private static final Uint16 DEFAULT_IDLE_TIMEOUT = Uint16.ZERO;
73     private static final Uint16 DEFAULT_HARD_TIMEOUT = Uint16.ZERO;
74     private static final Uint16 DEFAULT_PRIORITY = OFConstants.DEFAULT_FLOW_PRIORITY;
75     private static final Uint32 DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
76     private static final Uint64 DEFAULT_OUT_PORT = Uint64.valueOf(OFConstants.OFPP_ANY);
77     private static final Uint32 DEFAULT_OUT_GROUP = OFConstants.OFPG_ANY;
78     private static final byte PADDING_IN_FLOW_MOD_MESSAGE = 2;
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         outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookie(), DEFAULT_COOKIE).getValue().longValue());
120         outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookieMask(), DEFAULT_COOKIE_MASK).getValue()
121                 .longValue());
122         outBuffer.writeByte(MoreObjects.firstNonNull(message.getTableId(), DEFAULT_TABLE_ID).toJava());
123         outBuffer.writeByte(message.getCommand().getIntValue());
124         outBuffer.writeShort(MoreObjects.firstNonNull(message.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT).toJava());
125         outBuffer.writeShort(MoreObjects.firstNonNull(message.getHardTimeout(), DEFAULT_HARD_TIMEOUT).toJava());
126         outBuffer.writeShort(MoreObjects.firstNonNull(message.getPriority(), DEFAULT_PRIORITY).toJava());
127         outBuffer.writeInt(MoreObjects.firstNonNull(message.getBufferId(), DEFAULT_BUFFER_ID).intValue());
128         outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutPort(), DEFAULT_OUT_PORT).intValue());
129         outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutGroup(), DEFAULT_OUT_GROUP).intValue());
130         outBuffer.writeShort(createFlowModFlagsBitmask(MoreObjects.firstNonNull(message.getFlags(), DEFAULT_FLAGS)));
131         outBuffer.writeZero(PADDING_IN_FLOW_MOD_MESSAGE);
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(
146                 new FlowMessageBuilder(message)
147                         .setMatch(new MatchBuilder(message.getMatch())
148                                 .setVlanMatch(VLAN_MATCH_FALSE)
149                                 .build())
150                         .setInstructions(new InstructionsBuilder()
151                                 .setInstruction(updateSetVlanIdAction(message))
152                                 .build())
153                         .build(),
154                 outBuffer);
155
156         writeFlow(
157                 new FlowMessageBuilder(message)
158                         .setMatch(new MatchBuilder(message.getMatch())
159                                 .setVlanMatch(VLAN_MATCH_TRUE)
160                                 .build())
161                         .build(),
162                 outBuffer);
163     }
164
165     /**
166      * Serialize OpenFlowPlugin match to raw bytes.
167      *
168      * @param message   OpenFlow flow mod message
169      * @param outBuffer output buffer
170      */
171     private void writeMatch(final FlowMessage message, final ByteBuf outBuffer) {
172         Preconditions.checkNotNull(registry).<Match, OFSerializer<Match>>getSerializer(
173                 new MessageTypeKey<>(message.getVersion().toJava(), Match.class)).serialize(message.getMatch(),
174                     outBuffer);
175
176     }
177
178     /**
179      * Serialize OpenFlowPlugin instructions and set ip protocol of set-tp-src and set-tp-dst actions of need.
180      *
181      * @param message   OpenFlow flow mod message
182      * @param outBuffer output buffer
183      */
184     private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
185         // Try to get IP protocol from IP match
186         final Optional<Uint8> protocol = Optional
187                 .ofNullable(message.getMatch())
188                 .flatMap(m -> Optional.ofNullable(m.getIpMatch()))
189                 .flatMap(ipm -> Optional.ofNullable(ipm.getIpProtocol()));
190
191         // Update instructions if needed and then serialize all instructions
192         Optional.ofNullable(message.getInstructions())
193                 .flatMap(is -> Optional.ofNullable(is.nonnullInstruction()))
194                 .ifPresent(is -> is.values().stream()
195                         .filter(Objects::nonNull)
196                         .sorted(OrderComparator.build())
197                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
198                                 .rev131026.Instruction::getInstruction)
199                         .filter(Objects::nonNull)
200                         .map(i -> protocol.flatMap(p -> updateInstruction(i, p)).orElse(i))
201                         .forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF13_VERSION_ID, registry,
202                                 outBuffer)));
203     }
204
205     /**
206      * Determine type of instruction and update it's actions if it is apply-actions instruction.
207      *
208      * @param instruction instruction
209      * @param protocol    protocol
210      * @return updated instruction or empty
211      */
212     private static Optional<Instruction> updateInstruction(final Instruction instruction, final Uint8 protocol) {
213         if (instruction instanceof ApplyActionsCase) {
214             return Optional
215                     .ofNullable(((ApplyActionsCase) instruction).getApplyActions())
216                     .flatMap(aa -> Optional.ofNullable(aa.nonnullAction()))
217                     .map(as -> new ApplyActionsCaseBuilder()
218                             .setApplyActions(new ApplyActionsBuilder()
219                                     .setAction(as
220                                             .values()
221                                             .stream()
222                                             .filter(Objects::nonNull)
223                                             .map(a -> updateSetTpActions(a, protocol))
224                                             .collect(Collectors.toList()))
225                                     .build())
226                             .build());
227         }
228
229         return Optional.empty();
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) {
241             final SetTpSrcActionCase actionCase = (SetTpSrcActionCase) action.getAction();
242
243             return new ActionBuilder(action)
244                     .setAction(new SetTpSrcActionCaseBuilder(actionCase)
245                             .setSetTpSrcAction(new SetTpSrcActionBuilder(
246                                     actionCase.getSetTpSrcAction())
247                                     .setIpProtocol(protocol)
248                                     .build())
249                             .build())
250                     .build();
251         } else if (action.getAction() instanceof SetTpDstActionCase) {
252             final SetTpDstActionCase actionCase = (SetTpDstActionCase) action.getAction();
253
254             return new ActionBuilder(action)
255                     .setAction(new SetTpDstActionCaseBuilder(actionCase)
256                             .setSetTpDstAction(new SetTpDstActionBuilder(
257                                     actionCase.getSetTpDstAction())
258                                     .setIpProtocol(protocol)
259                                     .build())
260                             .build())
261                     .build();
262         }
263
264         // Return original action if no modifications are needed
265         return action;
266     }
267
268     /**
269      * Create copy of instructions of original flow but insert
270      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase}
271      * before each
272      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
273      *
274      * @param message OpenFlowPlugin flow mod message
275      * @return list of instructions
276      */
277     private static List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list
278             .Instruction> updateSetVlanIdAction(final FlowMessage message) {
279         return message.getInstructions().nonnullInstruction().values()
280                 .stream()
281                 .map(i -> {
282                     final int[] offset = {0};
283
284                     return i.getInstruction() instanceof ApplyActionsCase
285                             ? Optional
286                             .ofNullable(((ApplyActionsCase) i.getInstruction()).getApplyActions())
287                             .flatMap(as -> Optional.ofNullable(as.nonnullAction()))
288                             .map(a -> a.values().stream()
289                                     .sorted(OrderComparator.build())
290                                     .flatMap(action -> {
291                                         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112
292                                                 .action.list.Action> actions = new ArrayList<>();
293
294                                         // If current action is SetVlanId, insert PushVlan action before it and
295                                         // update order
296                                         if (action.getAction() instanceof SetVlanIdActionCase) {
297                                             actions.add(new ActionBuilder()
298                                                     .setAction(new PushVlanActionCaseBuilder()
299                                                             .setPushVlanAction(new PushVlanActionBuilder()
300                                                                     .setCfi(PUSH_CFI)
301                                                                     .setVlanId(((SetVlanIdActionCase) action
302                                                                         .getAction()).getSetVlanIdAction()
303                                                                             .getVlanId())
304                                                                     .setEthernetType(PUSH_VLAN)
305                                                                     .setTag(PUSH_TAG)
306                                                                     .build())
307                                                             .build())
308                                                     .withKey(action.key())
309                                                     .setOrder(action.getOrder() + offset[0])
310                                                     .build());
311
312                                             offset[0]++;
313                                         }
314
315                                         // Update offset of action if there is any inserted PushVlan actions
316                                         actions.add(offset[0] > 0
317                                                 ? new ActionBuilder(action).setOrder(action.getOrder() + offset[0])
318                                                 .withKey(new ActionKey(action.getOrder() + offset[0]))
319                                                 .build()
320                                                 : action);
321
322                                         return actions.stream();
323                                     }))
324                             .map(as -> new InstructionBuilder(i)
325                                     .setInstruction(new ApplyActionsCaseBuilder()
326                                             .setApplyActions(new ApplyActionsBuilder()
327                                                     .setAction(as.collect(Collectors.toList()))
328                                                     .build())
329                                             .build())
330                                     .build())
331                             .orElse(i)
332                             : i;
333                 }).collect(Collectors.toList());
334     }
335
336     /**
337      * Create integer bit mask from
338      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
339      *
340      * @param flags flow mod flags
341      * @return bit mask
342      */
343     private static int createFlowModFlagsBitmask(final FlowModFlags flags) {
344         return ByteBufUtils.fillBitMask(0,
345                 flags.isSENDFLOWREM(),
346                 flags.isCHECKOVERLAP(),
347                 flags.isRESETCOUNTS(),
348                 flags.isNOPKTCOUNTS(),
349                 flags.isNOBYTCOUNTS());
350     }
351
352     /**
353      * Determine if flow contains vlan match.
354      *
355      * @param flow flow
356      * @return true if flow contains vlan match
357      */
358     private static boolean isVlanMatchPresent(final Flow flow) {
359         return Optional
360                 .ofNullable(flow.getMatch())
361                 .flatMap(m -> Optional.ofNullable(m.getVlanMatch()))
362                 .isPresent();
363     }
364
365     /**
366      * Determine if flow contains
367      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight
368      * .flow.types.rev131026.instruction.instruction.ApplyActionsCase} instruction with
369      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}
370      * action.
371      *
372      * @param flow OpenFlowPlugin flow
373      * @return true if flow contains SetVlanIdAction
374      */
375     private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
376         return Optional
377                 .ofNullable(flow.getInstructions())
378                 .flatMap(is -> Optional.ofNullable(is.nonnullInstruction()))
379                 .flatMap(is -> is.values().stream()
380                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
381                                 .rev131026.Instruction::getInstruction)
382                         .filter(ApplyActionsCase.class::isInstance)
383                         .map(i -> ((ApplyActionsCase) i).getApplyActions())
384                         .filter(Objects::nonNull)
385                         .map(ActionList::nonnullAction)
386                         .filter(Objects::nonNull)
387                         .flatMap(map -> map.values().stream())
388                         .map(Action::getAction)
389                         .filter(SetVlanIdActionCase.class::isInstance)
390                         .findFirst())
391                 .isPresent();
392     }
393
394     @Override
395     protected byte getMessageType() {
396         return 14;
397     }
398
399     @Override
400     public void injectSerializerRegistry(final SerializerRegistry serializerRegistry) {
401         registry = serializerRegistry;
402     }
403 }