/* * Copyright (c) 2016 Pantheon Technologies s.r.o. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.impl.protocol.serialization.match; import io.netty.buffer.ByteBuf; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import org.opendaylight.openflowjava.protocol.api.extensibility.HeaderSerializer; import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer; import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry; import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector; import org.opendaylight.openflowjava.protocol.api.keys.MatchEntrySerializerKey; import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializer; import org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerRegistry; import org.opendaylight.openflowplugin.extension.api.ConverterExtensionKey; import org.opendaylight.openflowplugin.openflow.md.core.extension.ExtensionResolvers; import org.opendaylight.openflowplugin.openflow.md.core.session.OFSessionUtil; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.augments.rev150225.oxm.container.match.entry.value.ExperimenterIdCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.ExperimenterClass; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.ExtensionKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.general.extension.list.grouping.ExtensionList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MatchSerializer implements OFSerializer<Match>, HeaderSerializer<Match>, MatchEntrySerializerRegistry, SerializerRegistryInjector { private static final Logger LOG = LoggerFactory.getLogger(MatchSerializer.class); private static final byte OXM_MATCH_TYPE_CODE = 1; private final Map<org.opendaylight.openflowplugin.api.openflow.protocol.serialization. MatchEntrySerializerKey, MatchEntrySerializer> entryRegistry = new LinkedHashMap<>(); private SerializerRegistry registry; @Override public void serialize(Match match, ByteBuf outBuffer) { // Save start index in buffer int matchStartIndex = outBuffer.writerIndex(); // With OpenflowPlugin models, we cannot check difference between OXM and Standard match type // so all matches will be OXM outBuffer.writeShort(OXM_MATCH_TYPE_CODE); // Save length of match type int matchLengthIndex = outBuffer.writerIndex(); outBuffer.writeShort(EncodeConstants.EMPTY_LENGTH); // Small hack to be able to serialize only match entryRegistry externally serializeHeader(match, outBuffer); // Length of ofp_match (excluding padding) int matchLength = outBuffer.writerIndex() - matchStartIndex; outBuffer.setShort(matchLengthIndex, matchLength); // If we have any remaining padding, write it at end int paddingRemainder = matchLength % EncodeConstants.PADDING; if (paddingRemainder != 0) { outBuffer.writeZero(EncodeConstants.PADDING - paddingRemainder); } } @Override public void serializeHeader(Match match, ByteBuf outBuffer) { if (match == null) { LOG.debug("Match is null, skipping serialization of match entries"); return; } // Serialize match entries entryRegistry.forEach((key, value) -> { if (value.matchTypeCheck(match)) { value.serialize(match, outBuffer); } }); // Serialize match extensions ExtensionResolvers .getMatchExtensionResolver() .getExtension(match) .flatMap(extensions -> Optional.ofNullable(extensions.getExtensionList())) .ifPresent(extensionList -> serializeExtensionList(extensionList, outBuffer)); } private void serializeExtensionList(final List<ExtensionList> extensionList, final ByteBuf outBuffer) { // TODO: Remove also extension converters extensionList.forEach(extension -> { final ConverterExtensionKey<? extends ExtensionKey> converterExtensionKey = new ConverterExtensionKey<>(extension.getExtensionKey(), OFConstants.OFP_VERSION_1_3); Optional.ofNullable(OFSessionUtil.getExtensionConvertorProvider()) .flatMap(provider -> Optional.ofNullable(provider.<MatchEntry>getConverter(converterExtensionKey))) .map(matchEntryConvertorToOFJava -> { final MatchEntry entry = matchEntryConvertorToOFJava.convert(extension.getExtension()); final MatchEntrySerializerKey<?, ?> key = new MatchEntrySerializerKey<>( EncodeConstants.OF13_VERSION_ID, entry.getOxmClass(), entry.getOxmMatchField()); // If entry is experimenter, set experimenter ID to key if (entry.getOxmClass().equals(ExperimenterClass.class)) { key.setExperimenterId(ExperimenterIdCase.class.cast(entry.getMatchEntryValue()) .getExperimenter().getExperimenter().getValue()); } final OFSerializer<MatchEntry> entrySerializer = registry.getSerializer(key); entrySerializer.serialize(entry, outBuffer); return entry; }) .orElseGet(() -> { LOG.warn("Serializer for match entry {} for version {} not found.", extension.getExtension().getImplementedInterface(), OFConstants.OFP_VERSION_1_3); return null; }); }); } @Override public void injectSerializerRegistry(SerializerRegistry serializerRegistry) { registry = serializerRegistry; } @Override public void registerEntrySerializer(org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerKey key, MatchEntrySerializer serializer) { if (Objects.isNull(key) || Objects.isNull(serializer)) { throw new IllegalArgumentException("MatchEntrySerializerKey or Serializer is null"); } final MatchEntrySerializer seInRegistry = entryRegistry.put(key, serializer); if (seInRegistry != null) { LOG.debug("Serializer for key {} overwritten. Old serializer: {}, new serializer: {}", key, seInRegistry.getClass().getName(), serializer.getClass().getName()); } } @Override public boolean unregisterEntrySerializer(org.opendaylight.openflowplugin.api.openflow.protocol.serialization.MatchEntrySerializerKey key) { return Objects.nonNull(entryRegistry.remove(key)); } }