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.ReadOnlyTransaction;
22 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
25 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.RegMatch;
27 import org.opendaylight.groupbasedpolicy.resolver.ConditionGroup;
28 import org.opendaylight.groupbasedpolicy.resolver.EgKey;
29 import org.opendaylight.groupbasedpolicy.resolver.IndexedTenant;
30 import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.EndpointLocation.LocationType;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.EndpointGroup;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2BridgeDomain;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L2FloodDomain;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.L3Context;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.Subnet;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg2;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg3;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg4;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg5;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg7;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
75 import com.google.common.base.Optional;
76 import com.google.common.collect.Sets;
78 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
81 * Manage the table that maps the destination address to the next hop
82 * for the path as well as applies any relevant routing transformations.
85 public class DestinationMapper extends FlowTable {
86 protected static final Logger LOG =
87 LoggerFactory.getLogger(DestinationMapper.class);
89 public static final short TABLE_ID = 2;
91 * This is the MAC address of the magical router in the sky
93 public static final MacAddress ROUTER_MAC =
94 new MacAddress("88:f0:31:b5:12:b5");
95 public static final MacAddress MULTICAST_MAC =
96 new MacAddress("01:00:00:00:00:00");
98 public DestinationMapper(OfTable.OfTableCtx ctx) {
103 public short getTableId() {
108 public void sync(ReadWriteTransaction t,
109 InstanceIdentifier<Table> tiid,
110 Map<String, FlowCtx> flowMap,
111 NodeId nodeId, PolicyInfo policyInfo, Dirty dirty)
113 dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
115 HashSet<EgKey> visitedEgs = new HashSet<>();
116 HashSet<Integer> visitedFds = new HashSet<>();
118 for (EgKey epg : ctx.epManager.getGroupsForNode(nodeId)) {
119 Set<EgKey> peers = Sets.union(Collections.singleton(epg),
120 policyInfo.getPeers(epg));
121 for (EgKey peer : peers) {
122 syncEPG(t, tiid, flowMap, nodeId,
124 visitedEgs, visitedFds);
129 // set up next-hop destinations for all the endpoints in the endpoint
131 private void syncEPG(ReadWriteTransaction t,
132 InstanceIdentifier<Table> tiid,
133 Map<String, FlowCtx> flowMap,
134 NodeId nodeId, PolicyInfo policyInfo,
136 HashSet<EgKey> visitedEgs,
137 HashSet<Integer> visitedFds) throws Exception {
138 if (visitedEgs.contains(key)) return;
141 IndexedTenant tenant = ctx.policyResolver.getTenant(key.getTenantId());
142 EndpointGroup eg = tenant.getEndpointGroup(key.getEgId());
143 L2FloodDomain fd = tenant.resolveL2FloodDomain(eg.getNetworkDomain());
144 Collection<Subnet> sns = tenant.resolveSubnets(eg.getNetworkDomain());
145 L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
149 l3Id = ctx.policyManager.getContextOrdinal(key.getTenantId(),
152 Collection<Endpoint> egEps = ctx.epManager
153 .getEndpointsForGroup(key);
155 for (Endpoint e : egEps) {
156 if (e.getTenant() == null || e.getEndpointGroup() == null)
158 OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
159 if (ofc == null || ofc.getNodeId() == null) continue;
161 syncEP(t, tiid, flowMap, nodeId, policyInfo, e, ofc, tenant, key);
164 if (fd == null) return;
165 Integer fdId = ctx.policyManager.getContextOrdinal(key.getTenantId(),
167 if (visitedFds.contains(fdId)) return;
168 visitedFds.add(fdId);
170 //GroupTable must exist before we start adding flows that direct to it via fdId
171 if(groupExists(nodeId,fdId)) {
173 FlowId flowId = new FlowId(new StringBuilder()
174 .append("broadcast|")
175 .append(fdId).toString());
176 if (visit(flowMap, flowId.getValue())) {
177 MatchBuilder mb = new MatchBuilder()
178 .setEthernetMatch(new EthernetMatchBuilder()
179 .setEthernetDestination(new EthernetDestinationBuilder()
180 .setAddress(MULTICAST_MAC)
181 .setMask(MULTICAST_MAC)
184 addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class,Long.valueOf(fdId)));
186 FlowBuilder flow = base()
187 .setPriority(Integer.valueOf(140))
189 .setMatch(mb.build())
190 .setInstructions(instructions(applyActionIns(nxMoveRegTunIdAction(NxmNxReg0.class, false),
191 groupAction(Long.valueOf(fdId)))));
192 writeFlow(t, tiid, flow.build());
195 for (Subnet sn : sns) {
196 writeRouterArpFlow(t, tiid, flowMap, nodeId, sn, l3Id);
200 private boolean groupExists(NodeId nodeId, Integer fdId) throws Exception {
201 //Fetch existing GroupTables
202 if(ctx.dataBroker==null) return false;
204 ReadOnlyTransaction t = ctx.dataBroker.newReadOnlyTransaction();
205 InstanceIdentifier<Node> niid = createNodePath(nodeId);
207 t.read(LogicalDatastoreType.CONFIGURATION, niid).get();
208 if (!r.isPresent()) return false;
209 FlowCapableNode fcn = r.get().getAugmentation(FlowCapableNode.class);
210 if (fcn == null) return false;
212 if (fcn.getGroup() != null) {
213 for (Group g : fcn.getGroup()) {
214 if (g.getGroupId().getValue().equals(Long.valueOf(fdId))) { //Group Exists.
224 private void writeRouterArpFlow(ReadWriteTransaction t,
225 InstanceIdentifier<Table> tiid,
226 Map<String, FlowCtx> flowMap,
230 if (sn != null && sn.getVirtualRouterIp() != null) {
231 if (sn.getVirtualRouterIp().getIpv4Address() != null) {
232 String ikey = sn.getVirtualRouterIp().getIpv4Address().getValue();
233 FlowId flowId = new FlowId(new StringBuffer()
234 .append("routerarp|")
235 .append(sn.getId().getValue())
241 if (visit(flowMap, flowId.getValue())) {
242 MatchBuilder mb = new MatchBuilder()
243 .setEthernetMatch(ethernetMatch(null, null, ARP))
244 .setLayer3Match(new ArpMatchBuilder()
245 .setArpOp(Integer.valueOf(1))
246 .setArpTargetTransportAddress(new Ipv4Prefix(ikey+"/32"))
248 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
249 Long.valueOf(l3Id)));
250 BigInteger routerMac =
251 new BigInteger(1, bytesFromHexString(ROUTER_MAC
253 FlowBuilder flowb = base()
256 .setMatch(mb.build())
257 .setInstructions(instructions(applyActionIns(nxMoveEthSrcToEthDstAction(),
258 setDlSrcAction(ROUTER_MAC),
259 nxLoadArpOpAction(BigInteger.valueOf(2L)),
260 nxMoveArpShaToArpThaAction(),
261 nxLoadArpShaAction(routerMac),
262 nxMoveArpSpaToArpTpaAction(),
263 nxLoadArpSpaAction(ikey),
264 outputAction(new NodeConnectorId(nodeId.getValue() + ":INPORT")))));
265 writeFlow(t, tiid, flowb.build());
268 LOG.warn("IPv6 virtual router {} for subnet {} not supported",
269 sn.getVirtualRouterIp(), sn.getId().getValue());
274 private void syncEP(ReadWriteTransaction t,
275 InstanceIdentifier<Table> tiid,
276 Map<String, FlowCtx> flowMap,
277 NodeId nodeId, PolicyInfo policyInfo,
278 Endpoint e, OfOverlayContext ofc,
279 IndexedTenant tenant, EgKey key)
281 ArrayList<Instruction> instructions = new ArrayList<>();
282 ArrayList<Instruction> l3instructions = new ArrayList<>();
283 List<Action> applyActions = new ArrayList<>();
284 List<Action> l3ApplyActions = new ArrayList<>();
287 EndpointGroup eg = tenant.getEndpointGroup(e.getEndpointGroup());
288 L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
289 L2BridgeDomain bd = tenant.resolveL2BridgeDomain(eg.getNetworkDomain());
291 int egId = 0, bdId = 0, l3Id = 0, cgId = 0;
293 egId = ctx.policyManager.getContextOrdinal(e.getTenant(),
294 e.getEndpointGroup());
296 bdId = ctx.policyManager.getContextOrdinal(e.getTenant(),
299 l3Id = ctx.policyManager.getContextOrdinal(e.getTenant(),
302 List<ConditionName> conds = ctx.epManager.getCondsForEndpoint(e);
304 policyInfo.getEgCondGroup(new EgKey(e.getTenant(),
305 e.getEndpointGroup()),
307 cgId = ctx.policyManager.getCondGroupOrdinal(cg);
308 Action setdEPG = nxLoadRegAction(NxmNxReg2.class,
309 BigInteger.valueOf(egId));
310 Action setdCG = nxLoadRegAction(NxmNxReg3.class,
311 BigInteger.valueOf(cgId));
314 if (LocationType.External.equals(ofc.getLocationType())) {
315 // XXX - TODO - perform NAT and send to the external network
316 nextHop = "external";
317 LOG.warn("External endpoints not yet supported");
320 Action setDlSrc = setDlSrcAction(ROUTER_MAC);
321 Action decTtl = decNwTtlAction();
323 if (Objects.equals(ofc.getNodeId(), nodeId)) {
324 // this is a local endpoint; send to the approppriate local
326 nextHop = ofc.getNodeConnectorId().getValue();
330 portNum = getOfPortNum(ofc.getNodeConnectorId());
331 } catch (NumberFormatException ex) {
332 LOG.warn("Could not parse port number {}",
333 ofc.getNodeConnectorId(), ex);
337 setNextHop = nxLoadRegAction(NxmNxReg7.class,
338 BigInteger.valueOf(portNum));
340 Action setDlDst = setDlDstAction(e.getMacAddress());
341 l3ApplyActions.add(setDlSrc);
342 l3ApplyActions.add(setDlDst);
343 l3ApplyActions.add(decTtl);
346 // this endpoint is on a different switch; send to the
347 // appropriate tunnel
350 ctx.switchManager.getTunnelIP(ofc.getNodeId());
351 NodeConnectorId tunPort =
352 ctx.switchManager.getTunnelPort(nodeId);
353 if (tunDst == null) return;
354 if (tunPort == null) return;
358 if (tunDst.getIpv4Address() != null) {
359 nextHop = tunDst.getIpv4Address().getValue();
360 tundstAction = nxLoadTunIPv4Action(nextHop, false);
361 } else if (tunDst.getIpv6Address() != null) {
362 // nextHop = tunDst.getIpv6Address().getValue();
363 LOG.error("IPv6 tunnel destination {} for {} not supported",
364 tunDst.getIpv6Address().getValue(),
368 // this shouldn't happen
369 LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
376 portNum = getOfPortNum(tunPort);
377 } catch (NumberFormatException ex) {
378 LOG.warn("Could not parse port number {}",
379 ofc.getNodeConnectorId(), ex);
383 setNextHop = nxLoadRegAction(NxmNxReg7.class,
384 BigInteger.valueOf(portNum));
386 nxMoveRegTunIdAction(NxmNxReg0.class, false);
388 applyActions.add(tunIdAction);
389 applyActions.add(tundstAction);
390 l3ApplyActions.add(setDlSrc);
391 l3ApplyActions.add(decTtl);
395 applyActions.add(setdEPG);
396 applyActions.add(setdCG);
397 applyActions.add(setNextHop);
398 Instruction applyActionsIns = new InstructionBuilder()
400 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
402 instructions.add(applyActionsIns);
404 applyActions.addAll(l3ApplyActions);
405 applyActionsIns = new InstructionBuilder()
407 .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
409 l3instructions.add(applyActionsIns);
411 Instruction gotoTable = new InstructionBuilder()
413 .setInstruction(gotoTableIns((short)(getTableId()+1)))
415 instructions.add(gotoTable);
416 l3instructions.add(gotoTable);
418 FlowId flowid = new FlowId(new StringBuilder()
421 .append(e.getMacAddress().getValue())
425 if (visit(flowMap, flowid.getValue())) {
426 MatchBuilder mb = new MatchBuilder()
427 .setEthernetMatch(ethernetMatch(null,
430 addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(bdId)));
431 FlowBuilder flowb = base()
433 .setPriority(Integer.valueOf(50))
434 .setMatch(mb.build())
435 .setInstructions(new InstructionsBuilder()
436 .setInstruction(instructions)
439 writeFlow(t, tiid, flowb.build());
441 if (e.getL3Address() == null) return;
442 for (L3Address l3a : e.getL3Address()) {
443 if (l3a.getIpAddress() == null || l3a.getL3Context() == null)
445 Layer3Match m = null;
446 Long etherType = null;
448 if (l3a.getIpAddress().getIpv4Address() != null) {
449 ikey = l3a.getIpAddress().getIpv4Address().getValue() + "/32";
451 m = new Ipv4MatchBuilder()
452 .setIpv4Destination(new Ipv4Prefix(ikey))
454 } else if (l3a.getIpAddress().getIpv6Address() != null) {
455 ikey = l3a.getIpAddress().getIpv6Address().getValue() + "/128";
457 m = new Ipv6MatchBuilder()
458 .setIpv6Destination(new Ipv6Prefix(ikey))
463 flowid = new FlowId(new StringBuilder()
464 .append(l3a.getL3Context().getValue())
470 if (visit(flowMap, flowid.getValue())) {
471 MatchBuilder mb = new MatchBuilder()
472 .setEthernetMatch(ethernetMatch(null,
476 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
477 Long.valueOf(l3Id)));
478 FlowBuilder flowb = base()
480 .setPriority(Integer.valueOf(132))
481 .setMatch(mb.build())
482 .setInstructions(new InstructionsBuilder()
483 .setInstruction(l3instructions)
486 writeFlow(t, tiid, flowb.build());
491 static byte[] bytesFromHexString(String values) {
493 if (values != null) {
496 String[] octets = target.split(":");
498 byte[] ret = new byte[octets.length];
499 for (int i = 0; i < octets.length; i++) {
500 ret[i] = Integer.valueOf(octets[i], 16).byteValue();