/* * Copyright (c) 2015 Yale University 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.alto.basic.endpointcostservice.flow; import org.opendaylight.alto.basic.endpointcostservice.helper.IPPrefixHelper; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.TypedAddressData; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestination; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSource; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetType; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ipv6.match.fields.Ipv6ExtHeader; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ipv6.match.fields.Ipv6Label; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Icmpv4Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Icmpv6Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.IpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer4Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Metadata; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.ProtocolMatchFields; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.TcpFlagsMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Tunnel; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch; 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.Ipv4Match; 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.TunnelIpv4Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.SctpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.protocol.match.fields.Pbb; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.UnknownHostException; public class FlowEntryMatcher { private static final Logger log = LoggerFactory .getLogger(FlowEntryMatcher.class); /** * @param match is the original fields should be matched. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean match(Match match, MatchFields matchFields) { log.info("Flow Entry Matching Start"); return (match == null) || ((matchInPort(match.getInPort(), matchFields) && matchInPhyPort(match.getInPhyPort(), matchFields) && matchIp(match.getIpMatch(), matchFields) && matchVlan(match.getVlanMatch(), matchFields) && matchTunnel(match.getTunnel(), matchFields) && matchMetadata(match.getMetadata(), matchFields) && matchEthernet(match.getEthernetMatch(), matchFields) && matchLayer3(match.getLayer3Match(), matchFields) && matchLayer4(match.getLayer4Match(), matchFields) && matchIcmpv4Match(match.getIcmpv4Match(), matchFields) && matchIcmpv6Match(match.getIcmpv6Match(), matchFields) && matchProtocolMatchFields(match.getProtocolMatchFields(), matchFields) && matchTcpFlag(match.getTcpFlagsMatch(), matchFields))); } /** * Match if port is equal. * @param port is the port filed in a flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchInPort(NodeConnectorId port, MatchFields matchFields) { if (port == null) return true; String flowInPort = port.getValue(); return flowInPort.equals(matchFields.inPort); } /** * Match if port is a physical port. * @param port is the port field in a flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchInPhyPort(NodeConnectorId port, MatchFields matchFields) { return (port == null); } /** * Match if IP is equal. * @param ipMatch is the IP address in the flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchIp(IpMatch ipMatch, MatchFields matchFields) { return (ipMatch == null) || (ipMatch.getIpProtocol() == null && ipMatch.getIpDscp() == null && ipMatch.getIpEcn() == null && ipMatch.getIpProto() == null); } /** * Match if VLAN is equal. * @param match is the VLAN field in flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchVlan(VlanMatch match, MatchFields matchFields) { return (match == null) || (match.getVlanPcp() == null && matchVlanId(match.getVlanId())); } private boolean matchVlanId(VlanId id) { return (id == null) || (!id.isVlanIdPresent()); } /** * Match if tunnel is equal. * @param tunnel is the tunnel field in flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchTunnel(Tunnel tunnel, MatchFields matchFields) { return (tunnel == null) || (tunnel.getTunnelId() == null && tunnel.getTunnelMask() == null); } /** * Match if metadata is equal. * @param meta is the metadata in flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchMetadata(Metadata meta, MatchFields matchFields) { return (meta == null) || (meta.getMetadata() == null && meta.getMetadataMask() == null); } /** * Match if ethernet is equal. * @param match is the ethernet in flow entry. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchEthernet(EthernetMatch match, MatchFields matchFields) { return (match == null) || (matchEthernetSrc(match.getEthernetSource(), matchFields) && matchEthernetDst(match.getEthernetDestination(), matchFields) && matchEthernetType(match.getEthernetType(), matchFields)); } private boolean matchEthernetSrc(EthernetSource ethSrc, MatchFields matchFields) { return (ethSrc == null) || matchMacAddressWithMask(ethSrc.getAddress(), matchFields.srcMac, ethSrc.getMask()); } private boolean matchEthernetDst(EthernetDestination ethDest, MatchFields matchFields) { return (ethDest == null) || matchMacAddressWithMask(ethDest.getAddress(), matchFields.dstMac, ethDest.getMask()); } private String normalizeMacAddress(MacAddress mac) { return mac.getValue().replaceAll(":|-", "").toLowerCase(); } private long getMaskedMacAddress(String macAddress, String mask) { long macLong = (Long.parseLong(macAddress,16)); long maskLong = Long.parseLong(mask,16); return macLong & maskLong; } /** * Match the MAC address with a mask. * @param macA MAC address A. * @param macB MAC address B. * @param mask to XOR with MAC address. * @return the result of match. */ public boolean matchMacAddressWithMask(MacAddress macA, MacAddress macB, MacAddress mask) { return (mask == null && normalizeMacAddress(macA).equals(normalizeMacAddress(macB))) || (mask != null && getMaskedMacAddress(normalizeMacAddress(macA), normalizeMacAddress(mask)) == getMaskedMacAddress(normalizeMacAddress(macB), normalizeMacAddress(mask))); } private boolean matchEthernetType(EthernetType ethernetType, MatchFields matchFields) { return (ethernetType == null) || (matchFields.ethernetType != null && ethernetType.getType().getValue().longValue() == matchFields.ethernetType.longValue()); } /** * Match if layer 3 routing information is equal. * @param match is the layer 3 match. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchLayer3(Layer3Match match, MatchFields matchFields) { if (match == null) { return true; } else if (match instanceof Ipv4Match) { return matchIpv4((Ipv4Match) match, matchFields); } else if (match instanceof Ipv6Match) { return matchIpv6((Ipv6Match) match, matchFields); } else if (match instanceof ArpMatch) { return matchArp((ArpMatch) match, matchFields); } else if (match instanceof TunnelIpv4Match) { return matchTunnelIpv4((TunnelIpv4Match) match, matchFields); } return false; } /** * Match by IPv4 address. * @param match is the original IPv4 address. * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchIpv4(Ipv4Match match, MatchFields matchFields) { return (match == null) || (matchIpv4Address(match.getIpv4Source(), matchFields.srcIp) && matchIpv4Address(match.getIpv4Destination(), matchFields.dstIp)); } /** * @param prefix * @param address * @return the result of match. */ public boolean matchIpv4Address(Ipv4Prefix prefix, TypedAddressData address) { try { return (prefix == null) || new IPPrefixHelper(prefix).match(address); } catch (UnknownHostException e) { e.printStackTrace(); return false; } } /** * @param match * @param matchFields * @return the result of match. */ public boolean matchIpv6(Ipv6Match match, MatchFields matchFields) { return (match == null) || (matchIpv6Address(match.getIpv6Source(), matchFields.srcIp) && matchIpv6Address(match.getIpv6Destination(), matchFields.dstIp) && matchIpv6ExtHeader(match.getIpv6ExtHeader()) && matchIpv6Label(match.getIpv6Label()) && (match.getIpv6NdSll() == null) && (match.getIpv6NdTarget() == null) && (match.getIpv6NdTll() == null)); } /** * @param ipv6ExtHeader * @return the result of match. */ public boolean matchIpv6ExtHeader(Ipv6ExtHeader ipv6ExtHeader) { return (ipv6ExtHeader == null) || (ipv6ExtHeader.getIpv6Exthdr() == null && ipv6ExtHeader.getIpv6ExthdrMask() == null); } /** * @param ipv6Label * @return the result of match. */ public boolean matchIpv6Label(Ipv6Label ipv6Label) { return (ipv6Label == null) || (ipv6Label.getIpv6Flabel() == null && ipv6Label.getFlabelMask() == null); } /** * @param prefix * @param address * @return the result of match. */ public boolean matchIpv6Address(Ipv6Prefix prefix, TypedAddressData address) { try { return (prefix == null) || new IPPrefixHelper(prefix).match(address); } catch (UnknownHostException e) { e.printStackTrace(); return false; } } /** * Match by ARP protocol. * @param match * @param matchFields are the target fields should be compared. * @return the result of match. */ public boolean matchArp(ArpMatch match, MatchFields matchFields) { return (match == null) || (match.getArpOp() == null && match.getArpSourceHardwareAddress() == null && match.getArpSourceTransportAddress() == null && match.getArpTargetHardwareAddress() == null && match.getArpTargetTransportAddress() == null); } /** * Match by tunnel IPv4. * @param match * @param matchFields * @return the result of match. */ public boolean matchTunnelIpv4(TunnelIpv4Match match, MatchFields matchFields) { return (match == null) || (match.getTunnelIpv4Source() == null && match.getTunnelIpv4Destination() == null); } /** * Match by layer 4 routing information. * @param match * @param matchFields * @return the result of match. */ public boolean matchLayer4(Layer4Match match, MatchFields matchFields) { if (match == null) { return true; } else if (match instanceof UdpMatch) { return mathcUdp((UdpMatch) match); } else if (match instanceof TcpMatch) { return matchTcp((TcpMatch) match); } else if (match instanceof SctpMatch) { return matchSctp((SctpMatch) match); } return false; } /** * Match by UDP. * @param match * @return the result of match. */ public boolean mathcUdp(UdpMatch match) { return (match == null) || (match.getUdpSourcePort() == null && match.getUdpDestinationPort() == null); } /** * Match by TCP. * @param match * @return the result of match. */ public boolean matchTcp(TcpMatch match) { return (match == null) || (match.getTcpSourcePort() == null && match.getTcpDestinationPort() == null); } /** * Match by SCTP. * @param match * @return the result of match. */ public boolean matchSctp(SctpMatch match) { return (match == null) || (match.getSctpSourcePort() == null && match.getSctpDestinationPort() == null); } /** * Match by ICMP. * @param match * @param matchFields * @return the result of match. */ public boolean matchIcmpv4Match(Icmpv4Match match, MatchFields matchFields) { return (match == null) || (match.getIcmpv4Code() == null && match.getIcmpv4Type() == null); } /** * Match by ICMPv6. * @param match * @param matchFields * @return the result of match. */ public boolean matchIcmpv6Match(Icmpv6Match match, MatchFields matchFields) { return (match == null) || (match.getIcmpv6Code() == null && match.getIcmpv6Type() == null); } /** * Match by multiple protocol fields. * @param match * @param matchFields * @return the result of match. */ public boolean matchProtocolMatchFields(ProtocolMatchFields match, MatchFields matchFields) { return (match == null) || (match.getMplsBos() == null && match.getMplsLabel() == null && match.getMplsTc() == null && matchPbb(match.getPbb(), matchFields)); } /** * Match by PBB protocol. * @param pbb * @param matchFields * @return the result of match. */ public boolean matchPbb(Pbb pbb, MatchFields matchFields) { return (pbb == null) || (pbb.getPbbIsid() == null && pbb.getPbbMask() == null); } /** * Match by TCP flag. * @param match * @param matchFields * @return the result of match. */ public boolean matchTcpFlag(TcpFlagsMatch match, MatchFields matchFields) { return (match == null) || (match.getTcpFlags() == null); } }