2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
11 import java.math.BigInteger;
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.HashSet;
16 import java.util.List;
18 import java.util.Objects;
21 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
22 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
23 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
24 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
25 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
26 import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
27 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 import com.google.common.collect.Sets;
71 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
74 * Manage the table that maps the destination address to the next hop
75 * for the path as well as applies any relevant routing transformations.
78 public class DestinationMapper extends FlowTable {
79 protected static final Logger LOG =
80 LoggerFactory.getLogger(DestinationMapper.class);
82 public static final short TABLE_ID = 2;
84 * This is the MAC address of the magical router in the sky
86 public static final MacAddress ROUTER_MAC =
87 new MacAddress("88:f0:31:b5:12:b5");
88 public static final MacAddress MULTICAST_MAC =
89 new MacAddress("01:00:00:00:00:00");
91 public DestinationMapper(OfTable.OfTableCtx ctx) {
96 public short getTableId() {
101 public void sync(ReadWriteTransaction t,
102 InstanceIdentifier<Table> tiid,
103 Map<String, FlowCtx> flowMap,
104 NodeId nodeId, PolicyInfo policyInfo, Dirty dirty)
106 dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
108 HashSet<EgKey> visitedEgs = new HashSet<>();
109 HashSet<Integer> visitedFds = new HashSet<>();
111 for (EgKey epg : ctx.epManager.getGroupsForNode(nodeId)) {
112 Set<EgKey> peers = Sets.union(Collections.singleton(epg),
113 policyInfo.getPeers(epg));
114 for (EgKey peer : peers) {
115 syncEPG(t, tiid, flowMap, nodeId,
117 visitedEgs, visitedFds);
122 // set up next-hop destinations for all the endpoints in the endpoint
124 private void syncEPG(ReadWriteTransaction t,
125 InstanceIdentifier<Table> tiid,
126 Map<String, FlowCtx> flowMap,
127 NodeId nodeId, PolicyInfo policyInfo,
129 HashSet<EgKey> visitedEgs,
130 HashSet<Integer> visitedFds) throws Exception {
131 if (visitedEgs.contains(key)) return;
134 IndexedTenant tenant = ctx.policyResolver.getTenant(key.getTenantId());
135 EndpointGroup eg = tenant.getEndpointGroup(key.getEgId());
136 L2FloodDomain fd = tenant.resolveL2FloodDomain(eg.getNetworkDomain());
137 Collection<Subnet> sns = tenant.resolveSubnets(eg.getNetworkDomain());
138 L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
142 l3Id = ctx.policyManager.getContextOrdinal(key.getTenantId(),
145 Collection<Endpoint> egEps = ctx.epManager
146 .getEndpointsForGroup(key);
148 for (Endpoint e : egEps) {
149 if (e.getTenant() == null || e.getEndpointGroup() == null)
151 OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
152 if (ofc == null || ofc.getNodeId() == null) continue;
154 syncEP(t, tiid, flowMap, nodeId, policyInfo, e, ofc, tenant, key);
157 if (fd == null) return;
158 Integer fdId = ctx.policyManager.getContextOrdinal(key.getTenantId(),
160 if (visitedFds.contains(fdId)) return;
161 visitedFds.add(fdId);
163 FlowId flowId = new FlowId(new StringBuilder()
164 .append("broadcast|")
165 .append(fdId).toString());
166 if (visit(flowMap, flowId.getValue())) {
167 MatchBuilder mb = new MatchBuilder()
168 .setEthernetMatch(new EthernetMatchBuilder()
169 .setEthernetDestination(new EthernetDestinationBuilder()
170 .setAddress(MULTICAST_MAC)
171 .setMask(MULTICAST_MAC)
174 addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class,Long.valueOf(fdId)));
176 FlowBuilder flow = base()
177 .setPriority(Integer.valueOf(140))
179 .setMatch(mb.build())
180 .setInstructions(instructions(applyActionIns(nxMoveRegTunIdAction(NxmNxReg0.class, false),
181 groupAction(Long.valueOf(fdId)))));
182 writeFlow(t, tiid, flow.build());
184 for (Subnet sn : sns) {
185 writeRouterArpFlow(t, tiid, flowMap, nodeId, sn, l3Id);
189 private void writeRouterArpFlow(ReadWriteTransaction t,
190 InstanceIdentifier<Table> tiid,
191 Map<String, FlowCtx> flowMap,
195 if (sn != null && sn.getVirtualRouterIp() != null) {
196 if (sn.getVirtualRouterIp().getIpv4Address() != null) {
197 String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue();
198 FlowId flowId = new FlowId(new StringBuffer()
199 .append("routerarp|")
200 .append(sn.getId().getValue())
206 if (visit(flowMap, flowId.getValue())) {
207 MatchBuilder mb = new MatchBuilder()
208 .setEthernetMatch(ethernetMatch(null, null, ARP))
209 .setLayer3Match(new ArpMatchBuilder()
210 .setArpOp(Integer.valueOf(1))
211 .setArpTargetTransportAddress(new Ipv4Prefix(ikey+"/32"))
213 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
214 Long.valueOf(l3Id)));
215 BigInteger routerMac =
216 new BigInteger(1, bytesFromHexString(ROUTER_MAC
218 FlowBuilder flowb = base()
221 .setMatch(mb.build())
222 .setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(),
223 setDlSrcAction(ROUTER_MAC),
224 nxLoadArpOpAction(BigInteger.valueOf(2L)),
225 nxMoveArpShaToArpThaAction(),
226 nxLoadArpShaAction(routerMac),
227 nxMoveArpSpaToArpTpaAction(),
228 nxLoadArpSpaAction(ikey),
229 outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT")))));
230 writeFlow(t, tiid, flowb.build());
233 LOG.warn("IPv6 virtual router {} for subnet {} not supported",
234 sn.getVirtualRouterIp(), sn.getId().getValue());
239 private void syncEP(ReadWriteTransaction t,
240 InstanceIdentifier<Table> tiid,
241 Map<String, FlowCtx> flowMap,
242 NodeId nodeId, PolicyInfo policyInfo,
243 Endpoint e, OfOverlayContext ofc,
244 IndexedTenant tenant, EgKey key)
246 ArrayList<Instruction> instructions = new ArrayList<>();
247 ArrayList<Instruction> l3instructions = new ArrayList<>();
248 List<Action> applyActions = new ArrayList<>();
249 List<Action> l3ApplyActions = new ArrayList<>();
252 EndpointGroup eg = tenant.getEndpointGroup(e.getEndpointGroup());
253 L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
254 L2BridgeDomain bd = tenant.resolveL2BridgeDomain(eg.getNetworkDomain());
256 int egId = 0, bdId = 0, l3Id = 0, cgId = 0;
258 egId = ctx.policyManager.getContextOrdinal(e.getTenant(),
259 e.getEndpointGroup());
261 bdId = ctx.policyManager.getContextOrdinal(e.getTenant(),
264 l3Id = ctx.policyManager.getContextOrdinal(e.getTenant(),
267 List<ConditionName> conds = ctx.epManager.getCondsForEndpoint(e);
269 policyInfo.getEgCondGroup(new EgKey(e.getTenant(),
270 e.getEndpointGroup()),
272 cgId = ctx.policyManager.getCondGroupOrdinal(cg);
273 Action setdEPG = nxLoadRegAction(NxmNxReg2.class,
274 BigInteger.valueOf(egId));
275 Action setdCG = nxLoadRegAction(NxmNxReg3.class,
276 BigInteger.valueOf(cgId));
279 if (LocationType.External.equals(ofc.getLocationType())) {
280 // XXX - TODO - perform NAT and send to the external network
281 nextHop = "external";
282 LOG.warn("External endpoints not yet supported");
285 Action setDlSrc = setDlSrcAction(ROUTER_MAC);
286 Action decTtl = decNwTtlAction();
288 if (Objects.equals(ofc.getNodeId(), nodeId)) {
289 // this is a local endpoint; send to the approppriate local
291 nextHop = ofc.getNodeConnectorId().getValue();
295 portNum = getOfPortNum(ofc.getNodeConnectorId());
296 } catch (NumberFormatException ex) {
297 LOG.warn("Could not parse port number {}",
298 ofc.getNodeConnectorId(), ex);
302 setNextHop = nxLoadRegAction(NxmNxReg7.class,
303 BigInteger.valueOf(portNum));
305 Action setDlDst = setDlDstAction(e.getMacAddress());
306 l3ApplyActions.add(setDlSrc);
307 l3ApplyActions.add(setDlDst);
308 l3ApplyActions.add(decTtl);
311 // this endpoint is on a different switch; send to the
312 // appropriate tunnel
315 ctx.switchManager.getTunnelIP(ofc.getNodeId());
316 NodeConnectorId tunPort =
317 ctx.switchManager.getTunnelPort(nodeId);
318 if (tunDst == null) return;
319 if (tunPort == null) return;
323 if (tunDst.getIpv4Address() != null) {
324 nextHop = tunDst.getIpv4Address().getValue();
325 tundstAction = nxLoadTunIPv4Action(nextHop, false);
326 } else if (tunDst.getIpv6Address() != null) {
327 // nextHop = tunDst.getIpv6Address().getValue();
328 LOG.error("IPv6 tunnel destination {} for {} not supported",
329 tunDst.getIpv6Address().getValue(),
333 // this shouldn't happen
334 LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
341 portNum = getOfPortNum(tunPort);
342 } catch (NumberFormatException ex) {
343 LOG.warn("Could not parse port number {}",
344 ofc.getNodeConnectorId(), ex);
348 setNextHop = nxLoadRegAction(NxmNxReg7.class,
349 BigInteger.valueOf(portNum));
351 nxMoveRegTunIdAction(NxmNxReg0.class, false);
353 applyActions.add(tunIdAction);
354 applyActions.add(tundstAction);
355 l3ApplyActions.add(setDlSrc);
356 l3ApplyActions.add(decTtl);
360 applyActions.add(setdEPG);
361 applyActions.add(setdCG);
362 applyActions.add(setNextHop);
363 Instruction applyActionsIns = new InstructionBuilder()
365 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
367 instructions.add(applyActionsIns);
369 applyActions.addAll(l3ApplyActions);
370 applyActionsIns = new InstructionBuilder()
372 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
374 l3instructions.add(applyActionsIns);
376 Instruction gotoTable = new InstructionBuilder()
378 .setInstruction(gotoTableIns((short)(getTableId()+1)))
380 instructions.add(gotoTable);
381 l3instructions.add(gotoTable);
383 FlowId flowid = new FlowId(new StringBuilder()
386 .append(e.getMacAddress().getValue())
390 if (visit(flowMap, flowid.getValue())) {
391 MatchBuilder mb = new MatchBuilder()
392 .setEthernetMatch(ethernetMatch(null,
395 addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(bdId)));
396 FlowBuilder flowb = base()
398 .setPriority(Integer.valueOf(50))
399 .setMatch(mb.build())
400 .setInstructions(new InstructionsBuilder()
401 .setInstruction(instructions)
404 writeFlow(t, tiid, flowb.build());
406 if (e.getL3Address() == null) return;
407 for (L3Address l3a : e.getL3Address()) {
408 if (l3a.getIpAddress() == null || l3a.getL3Context() == null)
410 Layer3Match m = null;
411 Long etherType = null;
413 if (l3a.getIpAddress().getIpv4Address() != null) {
414 ikey = l3a.getIpAddress().getIpv4Address().getValue() + "/32";
416 m = new Ipv4MatchBuilder()
417 .setIpv4Destination(new Ipv4Prefix(ikey))
419 } else if (l3a.getIpAddress().getIpv6Address() != null) {
420 ikey = l3a.getIpAddress().getIpv6Address().getValue() + "/128";
422 m = new Ipv6MatchBuilder()
423 .setIpv6Destination(new Ipv6Prefix(ikey))
428 flowid = new FlowId(new StringBuilder()
429 .append(l3a.getL3Context().getValue())
435 if (visit(flowMap, flowid.getValue())) {
436 MatchBuilder mb = new MatchBuilder()
437 .setEthernetMatch(ethernetMatch(null,
441 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
442 Long.valueOf(l3Id)));
443 FlowBuilder flowb = base()
445 .setPriority(Integer.valueOf(132))
446 .setMatch(mb.build())
447 .setInstructions(new InstructionsBuilder()
448 .setInstruction(l3instructions)
451 writeFlow(t, tiid, flowb.build());
456 static byte[] bytesFromHexString(String values) {
458 if (values != null) {
461 String[] octets = target.split(":");
463 byte[] ret = new byte[octets.length];
464 for (int i = 0; i < octets.length; i++) {
465 ret[i] = Integer.valueOf(octets[i], 16).byteValue();