d61ad35b1193b59ffa456a9359e23719a574d8e5
[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.Collection;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.Optional;
18 import java.util.stream.Collectors;
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.ActionList;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCase;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
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     @SuppressWarnings("unchecked")
185     private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
186         // Try to get IP protocol from IP match
187         final Optional<Uint8> protocol = Optional
188                 .ofNullable(message.getMatch())
189                 .flatMap(m -> Optional.ofNullable(m.getIpMatch()))
190                 .flatMap(ipm -> Optional.ofNullable(ipm.getIpProtocol()));
191
192         // Update instructions if needed and then serialize all instructions
193         Optional.ofNullable(message.getInstructions())
194                 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
195                 .ifPresent(is -> is.stream()
196                         .filter(Objects::nonNull)
197                         .sorted(OrderComparator.build())
198                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
199                                 .rev131026.Instruction::getInstruction)
200                         .filter(Objects::nonNull)
201                         .map(i -> protocol.flatMap(p -> updateInstruction(i, p)).orElse(i))
202                         .forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF13_VERSION_ID, registry,
203                                 outBuffer)));
204     }
205
206     /**
207      * Determine type of instruction and update it's actions if it is apply-actions instruction.
208      *
209      * @param instruction instruction
210      * @param protocol    protocol
211      * @return updated instruction or empty
212      */
213     private static Optional<Instruction> updateInstruction(final Instruction instruction, final Uint8 protocol) {
214         if (instruction instanceof ApplyActionsCase) {
215             return Optional
216                     .ofNullable(((ApplyActionsCase) instruction).getApplyActions())
217                     .flatMap(aa -> Optional.ofNullable(aa.getAction()))
218                     .map(as -> new ApplyActionsCaseBuilder()
219                             .setApplyActions(new ApplyActionsBuilder()
220                                     .setAction(as
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().getInstruction()
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.getAction()))
288                             .map(a -> a.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                                                 .build()
319                                                 : action);
320
321                                         return actions.stream();
322                                     }))
323                             .map(as -> new InstructionBuilder(i)
324                                     .setInstruction(new ApplyActionsCaseBuilder()
325                                             .setApplyActions(new ApplyActionsBuilder()
326                                                     .setAction(as.collect(Collectors.toList()))
327                                                     .build())
328                                             .build())
329                                     .build())
330                             .orElse(i)
331                             : i;
332                 }).collect(Collectors.toList());
333     }
334
335     /**
336      * Create integer bit mask from
337      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
338      *
339      * @param flags flow mod flags
340      * @return bit mask
341      */
342     private static int createFlowModFlagsBitmask(final FlowModFlags flags) {
343         return ByteBufUtils.fillBitMask(0,
344                 flags.isSENDFLOWREM(),
345                 flags.isCHECKOVERLAP(),
346                 flags.isRESETCOUNTS(),
347                 flags.isNOPKTCOUNTS(),
348                 flags.isNOBYTCOUNTS());
349     }
350
351     /**
352      * Determine if flow contains vlan match.
353      *
354      * @param flow flow
355      * @return true if flow contains vlan match
356      */
357     private static boolean isVlanMatchPresent(final Flow flow) {
358         return Optional
359                 .ofNullable(flow.getMatch())
360                 .flatMap(m -> Optional.ofNullable(m.getVlanMatch()))
361                 .isPresent();
362     }
363
364     /**
365      * Determine if flow contains
366      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight
367      * .flow.types.rev131026.instruction.instruction.ApplyActionsCase} instruction with
368      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}
369      * action.
370      *
371      * @param flow OpenFlowPlugin flow
372      * @return true if flow contains SetVlanIdAction
373      */
374     private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
375         return Optional
376                 .ofNullable(flow.getInstructions())
377                 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
378                 .flatMap(is -> is.stream()
379                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
380                                 .rev131026.Instruction::getInstruction)
381                         .filter(ApplyActionsCase.class::isInstance)
382                         .map(i -> ((ApplyActionsCase) i).getApplyActions())
383                         .filter(Objects::nonNull)
384                         .map(ActionList::getAction)
385                         .filter(Objects::nonNull)
386                         .flatMap(Collection::stream)
387                         .map(Action::getAction)
388                         .filter(SetVlanIdActionCase.class::isInstance)
389                         .findFirst())
390                 .isPresent();
391     }
392
393     @Override
394     protected byte getMessageType() {
395         return 14;
396     }
397
398     @Override
399     public void injectSerializerRegistry(final SerializerRegistry serializerRegistry) {
400         registry = serializerRegistry;
401     }
402 }