/* * Copyright (c) 2017 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.util; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeIpv4Arbitrary; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeIpv4Prefix; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeIpv6AddressWithoutMask; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeIpv6Arbitrary; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeIpv6Prefix; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeMacAddress; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeMacAddressMask; import static org.opendaylight.openflowplugin.impl.util.AddressNormalizationUtil.normalizeProtocolAgnosticPort; import com.google.common.collect.ImmutableMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.openflowplugin.extension.api.GroupingLooseResolver; import org.opendaylight.openflowplugin.openflow.md.core.extension.ExtensionResolvers; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpSourceHardwareAddressBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.arp.match.fields.ArpTargetHardwareAddressBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchArbitraryBitMask; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchArbitraryBitMask; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.TunnelIpv4Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.TunnelIpv4MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNotifUpdateFlowStats; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralAugMatchNotifUpdateFlowStatsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.general.rev140714.GeneralExtensionListGrouping; import org.opendaylight.yangtools.yang.binding.Augmentation; /** * Utility class for match normalization */ public final class MatchNormalizationUtil { // Cache normalizers for common OpenFlow versions private static final Map<Short, Set<Function<MatchBuilder, MatchBuilder>>> NORMALIZERS = ImmutableMap .<Short, Set<Function<MatchBuilder, MatchBuilder>>>builder() .put(OFConstants.OFP_VERSION_1_0, createNormalizers(OFConstants.OFP_VERSION_1_0).collect(Collectors.toSet())) .put(OFConstants.OFP_VERSION_1_3, createNormalizers(OFConstants.OFP_VERSION_1_3).collect(Collectors.toSet())) .build(); private MatchNormalizationUtil() { throw new RuntimeException("Creating instance of util classes is prohibited"); } /** * Normalize match. * * @param match the OpenFlow match * @param version the OpenFlow version * @return normalized OpenFlow match */ @Nonnull public static Match normalizeMatch(@Nonnull final Match match, final short version) { final MatchBuilder matchBuilder = new MatchBuilder(match); Optional.ofNullable(NORMALIZERS.get(version)) .orElseGet(() -> createNormalizers(version).collect(Collectors.toSet())) .forEach(normalizer -> normalizer.apply(matchBuilder)); return matchBuilder.build(); } @Nonnull private static Stream<Function<MatchBuilder, MatchBuilder>> createNormalizers(final short version) { return Stream.of( MatchNormalizationUtil::normalizeExtensionMatch, MatchNormalizationUtil::normalizeEthernetMatch, MatchNormalizationUtil::normalizeArpMatch, MatchNormalizationUtil::normalizeTunnelIpv4Match, MatchNormalizationUtil::normalizeIpv4Match, MatchNormalizationUtil::normalizeIpv4MatchArbitraryBitMask, MatchNormalizationUtil::normalizeIpv6Match, MatchNormalizationUtil::normalizeIpv6MatchArbitraryBitMask, (match) -> normalizeInPortMatch(match, version), (match) -> normalizeInPhyPortMatch(match, version)); } @Nonnull @SuppressWarnings("unchecked") private static MatchBuilder normalizeExtensionMatch(@Nonnull final MatchBuilder match) { final GroupingLooseResolver<GeneralExtensionListGrouping> matchExtensionResolver = ExtensionResolvers.getMatchExtensionResolver(); return matchExtensionResolver .getExtension(match.build()) .flatMap(statExt -> Optional.ofNullable(statExt.getExtensionList())) .map(extensionLists -> { matchExtensionResolver.getClasses().forEach(aClass -> match .removeAugmentation((Class<? extends Augmentation<Match>>) aClass)); return match.addAugmentation( GeneralAugMatchNotifUpdateFlowStats.class, new GeneralAugMatchNotifUpdateFlowStatsBuilder() .setExtensionList(extensionLists) .build()); }) .orElse(match); } @Nonnull private static MatchBuilder normalizeInPortMatch(@Nonnull final MatchBuilder match, final short version) { return Optional .ofNullable(match.getInPort()) .flatMap(inPort -> Optional.ofNullable(normalizeProtocolAgnosticPort(inPort, version))) .map(inPortUri -> match.setInPort(new NodeConnectorId(inPortUri))) .orElse(match); } @Nonnull private static MatchBuilder normalizeInPhyPortMatch(@Nonnull final MatchBuilder match, final short version) { return Optional .ofNullable(match.getInPhyPort()) .flatMap(inPhyPort -> Optional.ofNullable(normalizeProtocolAgnosticPort(inPhyPort, version))) .map(inPhyPortUri -> match.setInPhyPort(new NodeConnectorId(inPhyPortUri))) .orElse(match); } @Nonnull private static MatchBuilder normalizeArpMatch(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(ArpMatch.class::isInstance) .map(ArpMatch.class::cast) .map(arp -> match.setLayer3Match(new ArpMatchBuilder(arp) .setArpSourceHardwareAddress(Optional .ofNullable(arp.getArpSourceHardwareAddress()) .map(arpSource -> new ArpSourceHardwareAddressBuilder(arpSource) .setAddress(normalizeMacAddress(arpSource.getAddress())) .setMask(normalizeMacAddress(arpSource.getMask())) .build()) .orElse(arp.getArpSourceHardwareAddress())) .setArpTargetHardwareAddress(Optional .ofNullable(arp.getArpTargetHardwareAddress()) .map(arpTarget -> new ArpTargetHardwareAddressBuilder(arpTarget) .setAddress(normalizeMacAddress(arpTarget.getAddress())) .setMask(normalizeMacAddress(arpTarget.getMask())) .build()) .orElse(arp.getArpTargetHardwareAddress())) .setArpSourceTransportAddress(normalizeIpv4Prefix(arp.getArpSourceTransportAddress())) .setArpTargetTransportAddress(normalizeIpv4Prefix(arp.getArpTargetTransportAddress())) .build()) ) .orElse(match); } @Nonnull private static MatchBuilder normalizeTunnelIpv4Match(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(TunnelIpv4Match.class::isInstance) .map(TunnelIpv4Match.class::cast) .map(tunnelIpv4 -> match.setLayer3Match(new TunnelIpv4MatchBuilder(tunnelIpv4) .setTunnelIpv4Source(normalizeIpv4Prefix(tunnelIpv4.getTunnelIpv4Source())) .setTunnelIpv4Destination(normalizeIpv4Prefix(tunnelIpv4.getTunnelIpv4Destination())) .build())) .orElse(match); } @Nonnull private static MatchBuilder normalizeIpv4Match(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(Ipv4Match.class::isInstance) .map(Ipv4Match.class::cast) .map(ipv4 -> match.setLayer3Match(new Ipv4MatchBuilder(ipv4) .setIpv4Source(normalizeIpv4Prefix(ipv4.getIpv4Source())) .setIpv4Destination(normalizeIpv4Prefix(ipv4.getIpv4Destination())) .build())) .orElse(match); } @Nonnull private static MatchBuilder normalizeIpv4MatchArbitraryBitMask(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(Ipv4MatchArbitraryBitMask.class::isInstance) .map(Ipv4MatchArbitraryBitMask.class::cast) .map(ipv4arbitrary -> match.setLayer3Match(new Ipv4MatchBuilder() .setIpv4Source(normalizeIpv4Arbitrary( ipv4arbitrary.getIpv4SourceAddressNoMask(), ipv4arbitrary.getIpv4SourceArbitraryBitmask())) .setIpv4Destination(normalizeIpv4Arbitrary( ipv4arbitrary.getIpv4DestinationAddressNoMask(), ipv4arbitrary.getIpv4DestinationArbitraryBitmask())) .build())) .orElse(match); } @Nonnull private static MatchBuilder normalizeIpv6Match(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(Ipv6Match.class::isInstance) .map(Ipv6Match.class::cast) .map(ipv6 -> match.setLayer3Match(new Ipv6MatchBuilder(ipv6) .setIpv6NdSll(normalizeMacAddress(ipv6.getIpv6NdSll())) .setIpv6NdTll(normalizeMacAddress(ipv6.getIpv6NdTll())) .setIpv6NdTarget(normalizeIpv6AddressWithoutMask(ipv6.getIpv6NdTarget())) .setIpv6Source(normalizeIpv6Prefix(ipv6.getIpv6Source())) .setIpv6Destination(normalizeIpv6Prefix(ipv6.getIpv6Destination())) .build())) .orElse(match); } @Nonnull private static MatchBuilder normalizeIpv6MatchArbitraryBitMask(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getLayer3Match()) .filter(Ipv6MatchArbitraryBitMask.class::isInstance) .map(Ipv6MatchArbitraryBitMask.class::cast) .map(ipv6Arbitrary -> match.setLayer3Match(new Ipv6MatchBuilder() .setIpv6Source(normalizeIpv6Arbitrary( ipv6Arbitrary.getIpv6SourceAddressNoMask(), ipv6Arbitrary.getIpv6SourceArbitraryBitmask())) .setIpv6Destination(normalizeIpv6Arbitrary( ipv6Arbitrary.getIpv6DestinationAddressNoMask(), ipv6Arbitrary.getIpv6DestinationArbitraryBitmask())) .build())) .orElse(match); } @Nonnull private static MatchBuilder normalizeEthernetMatch(@Nonnull final MatchBuilder match) { return Optional .ofNullable(match.getEthernetMatch()) .map(eth -> match.setEthernetMatch(new EthernetMatchBuilder(eth) .setEthernetSource(Optional .ofNullable(eth.getEthernetSource()) .map(filter -> new EthernetSourceBuilder(filter) .setAddress(normalizeMacAddress(filter.getAddress())) .setMask(normalizeMacAddressMask(filter.getMask())) .build()) .orElse(eth.getEthernetSource())) .setEthernetDestination(Optional .ofNullable(eth.getEthernetDestination()) .map(filter -> new EthernetDestinationBuilder(filter) .setAddress(normalizeMacAddress(filter.getAddress())) .setMask(normalizeMacAddressMask(filter.getMask())) .build()) .orElse(eth.getEthernetDestination())) .build())) .orElse(match); } }