/* * Copyright (c) 2014 Cisco Systems, Inc. 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.groupbasedpolicy.renderer.ofoverlay.flow; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty; import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch; import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup; import org.opendaylight.groupbasedpolicy.resolver.EgKey; import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant; import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; 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.match.EthernetMatchBuilder; 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.layer._3.match.ArpMatchBuilder; 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.Ipv6MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Sets; import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*; /** * Manage the table that maps the destination address to the next hop * for the path as well as applies any relevant routing transformations. * @author readams */ public class DestinationMapper extends FlowTable { protected static final Logger LOG = LoggerFactory.getLogger(DestinationMapper.class); public static final short TABLE_ID = 2; /** * This is the MAC address of the magical router in the sky */ public static final MacAddress ROUTER_MAC = new MacAddress("88:f0:31:b5:12:b5"); public static final MacAddress MULTICAST_MAC = new MacAddress("01:00:00:00:00:00"); public DestinationMapper(OfTable.OfTableCtx ctx) { super(ctx); } @Override public short getTableId() { return TABLE_ID; } @Override public void sync(ReadWriteTransaction t, InstanceIdentifier tiid, Map flowMap, NodeId nodeId, PolicyInfo policyInfo, Dirty dirty) throws Exception { dropFlow(t, tiid, flowMap, Integer.valueOf(1), null); HashSet visitedEgs = new HashSet<>(); HashSet visitedFds = new HashSet<>(); for (EgKey epg : ctx.epManager.getGroupsForNode(nodeId)) { Set peers = Sets.union(Collections.singleton(epg), policyInfo.getPeers(epg)); for (EgKey peer : peers) { syncEPG(t, tiid, flowMap, nodeId, policyInfo, peer, visitedEgs, visitedFds); } } } // set up next-hop destinations for all the endpoints in the endpoint // group on the node private void syncEPG(ReadWriteTransaction t, InstanceIdentifier
tiid, Map flowMap, NodeId nodeId, PolicyInfo policyInfo, EgKey key, HashSet visitedEgs, HashSet visitedFds) throws Exception { if (visitedEgs.contains(key)) return; visitedEgs.add(key); IndexedTenant tenant = ctx.policyResolver.getTenant(key.getTenantId()); EndpointGroup eg = tenant.getEndpointGroup(key.getEgId()); L2FloodDomain fd = tenant.resolveL2FloodDomain(eg.getNetworkDomain()); Collection sns = tenant.resolveSubnets(eg.getNetworkDomain()); L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain()); int l3Id = 0; if (l3c != null) l3Id = ctx.policyManager.getContextOrdinal(key.getTenantId(), l3c.getId()); Collection egEps = ctx.epManager .getEndpointsForGroup(key); for (Endpoint e : egEps) { if (e.getTenant() == null || e.getEndpointGroup() == null) continue; OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class); if (ofc == null || ofc.getNodeId() == null) continue; syncEP(t, tiid, flowMap, nodeId, policyInfo, e, ofc, tenant, key); } if (fd == null) return; Integer fdId = ctx.policyManager.getContextOrdinal(key.getTenantId(), fd.getId()); if (visitedFds.contains(fdId)) return; visitedFds.add(fdId); FlowId flowId = new FlowId(new StringBuilder() .append("broadcast|") .append(fdId).toString()); if (visit(flowMap, flowId.getValue())) { MatchBuilder mb = new MatchBuilder() .setEthernetMatch(new EthernetMatchBuilder() .setEthernetDestination(new EthernetDestinationBuilder() .setAddress(MULTICAST_MAC) .setMask(MULTICAST_MAC) .build()) .build()); addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class,Long.valueOf(fdId))); FlowBuilder flow = base() .setPriority(Integer.valueOf(140)) .setId(flowId) .setMatch(mb.build()) .setInstructions(instructions(applyActionIns(nxMoveRegTunIdAction(NxmNxReg0.class, false), groupAction(Long.valueOf(fdId))))); writeFlow(t, tiid, flow.build()); } for (Subnet sn : sns) { writeRouterArpFlow(t, tiid, flowMap, nodeId, sn, l3Id); } } private void writeRouterArpFlow(ReadWriteTransaction t, InstanceIdentifier
tiid, Map flowMap, NodeId nodeId, Subnet sn, int l3Id) { if (sn != null && sn.getVirtualRouterIp() != null) { if (sn.getVirtualRouterIp().getIpv4Address() != null) { String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue(); FlowId flowId = new FlowId(new StringBuffer() .append("routerarp|") .append(sn.getId().getValue()) .append("|") .append(ikey) .append("|") .append(l3Id) .toString()); if (visit(flowMap, flowId.getValue())) { MatchBuilder mb = new MatchBuilder() .setEthernetMatch(ethernetMatch(null, null, ARP)) .setLayer3Match(new ArpMatchBuilder() .setArpOp(Integer.valueOf(1)) .setArpTargetTransportAddress(new Ipv4Prefix(ikey+"/32")) .build()); addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(l3Id))); BigInteger routerMac = new BigInteger(1, bytesFromHexString(ROUTER_MAC .getValue())); FlowBuilder flowb = base() .setPriority(150) .setId(flowId) .setMatch(mb.build()) .setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(), setDlSrcAction(ROUTER_MAC), nxLoadArpOpAction(BigInteger.valueOf(2L)), nxMoveArpShaToArpThaAction(), nxLoadArpShaAction(routerMac), nxMoveArpSpaToArpTpaAction(), nxLoadArpSpaAction(ikey), outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT"))))); writeFlow(t, tiid, flowb.build()); } } else { LOG.warn("IPv6 virtual router {} for subnet {} not supported", sn.getVirtualRouterIp(), sn.getId().getValue()); } } } private void syncEP(ReadWriteTransaction t, InstanceIdentifier
tiid, Map flowMap, NodeId nodeId, PolicyInfo policyInfo, Endpoint e, OfOverlayContext ofc, IndexedTenant tenant, EgKey key) throws Exception { ArrayList instructions = new ArrayList<>(); ArrayList l3instructions = new ArrayList<>(); List applyActions = new ArrayList<>(); List l3ApplyActions = new ArrayList<>(); int order = 0; EndpointGroup eg = tenant.getEndpointGroup(e.getEndpointGroup()); L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain()); L2BridgeDomain bd = tenant.resolveL2BridgeDomain(eg.getNetworkDomain()); int egId = 0, bdId = 0, l3Id = 0, cgId = 0; egId = ctx.policyManager.getContextOrdinal(e.getTenant(), e.getEndpointGroup()); if (bd != null) bdId = ctx.policyManager.getContextOrdinal(e.getTenant(), bd.getId()); if (l3c != null) l3Id = ctx.policyManager.getContextOrdinal(e.getTenant(), l3c.getId()); List conds = ctx.epManager.getCondsForEndpoint(e); ConditionGroup cg = policyInfo.getEgCondGroup(new EgKey(e.getTenant(), e.getEndpointGroup()), conds); cgId = ctx.policyManager.getCondGroupOrdinal(cg); Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(egId)); Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(cgId)); Action setNextHop; String nextHop; if (LocationType.External.equals(ofc.getLocationType())) { // XXX - TODO - perform NAT and send to the external network nextHop = "external"; LOG.warn("External endpoints not yet supported"); return; } else { Action setDlSrc = setDlSrcAction(ROUTER_MAC); Action decTtl = decNwTtlAction(); if (Objects.equals(ofc.getNodeId(), nodeId)) { // this is a local endpoint; send to the approppriate local // port nextHop = ofc.getNodeConnectorId().getValue(); long portNum; try { portNum = getOfPortNum(ofc.getNodeConnectorId()); } catch (NumberFormatException ex) { LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex); return; } setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum)); Action setDlDst = setDlDstAction(e.getMacAddress()); l3ApplyActions.add(setDlSrc); l3ApplyActions.add(setDlDst); l3ApplyActions.add(decTtl); order +=1; } else { // this endpoint is on a different switch; send to the // appropriate tunnel IpAddress tunDst = ctx.switchManager.getTunnelIP(ofc.getNodeId()); NodeConnectorId tunPort = ctx.switchManager.getTunnelPort(nodeId); if (tunDst == null) return; if (tunPort == null) return; Action tundstAction; if (tunDst.getIpv4Address() != null) { nextHop = tunDst.getIpv4Address().getValue(); tundstAction = nxLoadTunIPv4Action(nextHop, false); } else if (tunDst.getIpv6Address() != null) { // nextHop = tunDst.getIpv6Address().getValue(); LOG.error("IPv6 tunnel destination {} for {} not supported", tunDst.getIpv6Address().getValue(), ofc.getNodeId()); return; } else { // this shouldn't happen LOG.error("Tunnel IP for {} invalid", ofc.getNodeId()); return; } long portNum; try { portNum = getOfPortNum(tunPort); } catch (NumberFormatException ex) { LOG.warn("Could not parse port number {}", ofc.getNodeConnectorId(), ex); return; } setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum)); Action tunIdAction = nxMoveRegTunIdAction(NxmNxReg0.class, false); applyActions.add(tunIdAction); applyActions.add(tundstAction); l3ApplyActions.add(setDlSrc); l3ApplyActions.add(decTtl); order +=1; } } applyActions.add(setdEPG); applyActions.add(setdCG); applyActions.add(setNextHop); Instruction applyActionsIns = new InstructionBuilder() .setOrder(order++) .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()]))) .build(); instructions.add(applyActionsIns); applyActions.addAll(l3ApplyActions); applyActionsIns = new InstructionBuilder() .setOrder(order++) .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()]))) .build(); l3instructions.add(applyActionsIns); Instruction gotoTable = new InstructionBuilder() .setOrder(order++) .setInstruction(gotoTableIns((short)(getTableId()+1))) .build(); instructions.add(gotoTable); l3instructions.add(gotoTable); FlowId flowid = new FlowId(new StringBuilder() .append(bdId) .append("|l2|") .append(e.getMacAddress().getValue()) .append("|") .append(nextHop) .toString()); if (visit(flowMap, flowid.getValue())) { MatchBuilder mb = new MatchBuilder() .setEthernetMatch(ethernetMatch(null, e.getMacAddress(), null)); addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(bdId))); FlowBuilder flowb = base() .setId(flowid) .setPriority(Integer.valueOf(50)) .setMatch(mb.build()) .setInstructions(new InstructionsBuilder() .setInstruction(instructions) .build()); writeFlow(t, tiid, flowb.build()); } if (e.getL3Address() == null) return; for (L3Address l3a : e.getL3Address()) { if (l3a.getIpAddress() == null || l3a.getL3Context() == null) continue; Layer3Match m = null; Long etherType = null; String ikey = null; if (l3a.getIpAddress().getIpv4Address() != null) { ikey = l3a.getIpAddress().getIpv4Address().getValue() + "/32"; etherType = IPv4; m = new Ipv4MatchBuilder() .setIpv4Destination(new Ipv4Prefix(ikey)) .build(); } else if (l3a.getIpAddress().getIpv6Address() != null) { ikey = l3a.getIpAddress().getIpv6Address().getValue() + "/128"; etherType = IPv6; m = new Ipv6MatchBuilder() .setIpv6Destination(new Ipv6Prefix(ikey)) .build(); } else continue; flowid = new FlowId(new StringBuilder() .append(l3a.getL3Context().getValue()) .append("|l3|") .append(ikey) .append("|") .append(nextHop) .toString()); if (visit(flowMap, flowid.getValue())) { MatchBuilder mb = new MatchBuilder() .setEthernetMatch(ethernetMatch(null, ROUTER_MAC, etherType)) .setLayer3Match(m); addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(l3Id))); FlowBuilder flowb = base() .setId(flowid) .setPriority(Integer.valueOf(132)) .setMatch(mb.build()) .setInstructions(new InstructionsBuilder() .setInstruction(l3instructions) .build()); writeFlow(t, tiid, flowb.build()); } } } static byte[] bytesFromHexString(String values) { String target = ""; if (values != null) { target = values; } String[] octets = target.split(":"); byte[] ret = new byte[octets.length]; for (int i = 0; i < octets.length; i++) { ret[i] = Integer.valueOf(octets[i], 16).byteValue(); } return ret; } }