1d8c9bf93fd239f4986ccfc5164e1caf18d30165
[groupbasedpolicy.git] / groupbasedpolicy / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / DestinationMapper.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
10
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;
17 import java.util.Map;
18 import java.util.Objects;
19 import java.util.Set;
20
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;
68
69 import com.google.common.collect.Sets;
70
71 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.*;
72
73 /**
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.
76  * @author readams
77  */
78 public class DestinationMapper extends FlowTable {
79     protected static final Logger LOG =
80             LoggerFactory.getLogger(DestinationMapper.class);
81
82     public static final short TABLE_ID = 2;
83     /**
84      * This is the MAC address of the magical router in the sky
85      */
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");
90
91     public DestinationMapper(OfTable.OfTableCtx ctx) {
92         super(ctx);
93     }
94
95     @Override
96     public short getTableId() {
97         return TABLE_ID;
98     }
99
100     @Override
101     public void sync(ReadWriteTransaction t, 
102                      InstanceIdentifier<Table> tiid,
103                      Map<String, FlowCtx> flowMap, 
104                      NodeId nodeId, PolicyInfo policyInfo, Dirty dirty)
105                              throws Exception {
106         dropFlow(t, tiid, flowMap, Integer.valueOf(1), null);
107
108         HashSet<EgKey> visitedEgs = new HashSet<>();
109         HashSet<Integer> visitedFds = new HashSet<>();
110
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, 
116                         policyInfo, peer, 
117                         visitedEgs, visitedFds);
118             }
119         }
120     }
121
122     // set up next-hop destinations for all the endpoints in the endpoint
123     // group on the node
124     private void syncEPG(ReadWriteTransaction t, 
125                          InstanceIdentifier<Table> tiid,
126                          Map<String, FlowCtx> flowMap, 
127                          NodeId nodeId, PolicyInfo policyInfo, 
128                          EgKey key,
129                          HashSet<EgKey> visitedEgs,
130                          HashSet<Integer> visitedFds) throws Exception {
131         if (visitedEgs.contains(key)) return;
132         visitedEgs.add(key);
133         
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());
139         int l3Id = 0;
140
141         if (l3c != null)
142             l3Id = ctx.policyManager.getContextOrdinal(key.getTenantId(),
143                                                        l3c.getId());
144
145         Collection<Endpoint> egEps = ctx.epManager
146                 .getEndpointsForGroup(key);
147         
148         for (Endpoint e : egEps) {
149             if (e.getTenant() == null || e.getEndpointGroup() == null)
150                 continue;
151             OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
152             if (ofc == null || ofc.getNodeId() == null) continue;
153             
154             syncEP(t, tiid, flowMap, nodeId, policyInfo, e, ofc, tenant, key);
155         }
156         
157         if (fd == null) return;
158         Integer fdId = ctx.policyManager.getContextOrdinal(key.getTenantId(),
159                                                            fd.getId());
160         if (visitedFds.contains(fdId)) return;
161         visitedFds.add(fdId);
162
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)
172                         .build())
173                     .build());
174             addNxRegMatch(mb, RegMatch.of(NxmNxReg5.class,Long.valueOf(fdId)));
175             
176             FlowBuilder flow = base()
177                 .setPriority(Integer.valueOf(140))
178                 .setId(flowId)
179                 .setMatch(mb.build())
180                 .setInstructions(instructions(applyActionIns(nxMoveRegTunIdAction(NxmNxReg0.class, false),
181                                                              groupAction(Long.valueOf(fdId)))));
182             writeFlow(t, tiid, flow.build());
183         }
184         for (Subnet sn : sns) {
185             writeRouterArpFlow(t, tiid, flowMap, nodeId, sn, l3Id);
186         }
187     }
188
189     private void writeRouterArpFlow(ReadWriteTransaction t,
190                                     InstanceIdentifier<Table> tiid,
191                                     Map<String, FlowCtx> flowMap, 
192                                     NodeId nodeId,
193                                     Subnet sn,
194                                     int l3Id) {
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())
201                     .append("|")
202                     .append(ikey)
203                     .append("|")
204                     .append(l3Id)
205                     .toString());
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"))
212                             .build());
213                     addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class,
214                                                   Long.valueOf(l3Id)));
215                     BigInteger routerMac = 
216                             new BigInteger(1, bytesFromHexString(ROUTER_MAC
217                                                                  .getValue()));
218                     FlowBuilder flowb = base()
219                          .setPriority(150)
220                          .setId(flowId)
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());
231                 }
232             } else {
233                 LOG.warn("IPv6 virtual router {} for subnet {} not supported",
234                          sn.getVirtualRouterIp(), sn.getId().getValue());
235             }
236         }
237     }
238     
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) 
245                                  throws Exception {
246         ArrayList<Instruction> instructions = new ArrayList<>();
247         ArrayList<Instruction> l3instructions = new ArrayList<>();
248         List<Action> applyActions = new ArrayList<>();
249         List<Action> l3ApplyActions = new ArrayList<>();
250
251         int order = 0;
252         EndpointGroup eg = tenant.getEndpointGroup(e.getEndpointGroup());
253         L3Context l3c = tenant.resolveL3Context(eg.getNetworkDomain());
254         L2BridgeDomain bd = tenant.resolveL2BridgeDomain(eg.getNetworkDomain());
255
256         int egId = 0, bdId = 0, l3Id = 0, cgId = 0;
257         
258         egId = ctx.policyManager.getContextOrdinal(e.getTenant(), 
259                                                    e.getEndpointGroup());
260         if (bd != null)
261             bdId = ctx.policyManager.getContextOrdinal(e.getTenant(),
262                                                        bd.getId());
263         if (l3c != null)
264             l3Id = ctx.policyManager.getContextOrdinal(e.getTenant(),
265                                                        l3c.getId());
266
267         List<ConditionName> conds = ctx.epManager.getCondsForEndpoint(e);
268         ConditionGroup cg = 
269                 policyInfo.getEgCondGroup(new EgKey(e.getTenant(), 
270                                                     e.getEndpointGroup()), 
271                                           conds);
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));
277         Action setNextHop;
278         String nextHop;
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");
283             return;
284         } else {            
285             Action setDlSrc = setDlSrcAction(ROUTER_MAC);
286             Action decTtl = decNwTtlAction();
287
288             if (Objects.equals(ofc.getNodeId(), nodeId)) {
289                 // this is a local endpoint; send to the approppriate local 
290                 // port
291                 nextHop = ofc.getNodeConnectorId().getValue();
292
293                 long portNum;
294                 try {
295                     portNum = getOfPortNum(ofc.getNodeConnectorId());
296                 } catch (NumberFormatException ex) {
297                     LOG.warn("Could not parse port number {}", 
298                              ofc.getNodeConnectorId(), ex);
299                     return;
300                 }
301                 
302                 setNextHop = nxLoadRegAction(NxmNxReg7.class, 
303                                              BigInteger.valueOf(portNum));
304
305                 Action setDlDst = setDlDstAction(e.getMacAddress());
306                 l3ApplyActions.add(setDlSrc);
307                 l3ApplyActions.add(setDlDst);
308                 l3ApplyActions.add(decTtl);
309                 order +=1;
310             } else {
311                 // this endpoint is on a different switch; send to the 
312                 // appropriate tunnel
313
314                 IpAddress tunDst = 
315                         ctx.switchManager.getTunnelIP(ofc.getNodeId());
316                 NodeConnectorId tunPort =
317                         ctx.switchManager.getTunnelPort(nodeId);
318                 if (tunDst == null) return;
319                 if (tunPort == null) return;
320
321                 Action tundstAction;
322
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(),
330                               ofc.getNodeId());
331                     return;
332                 } else {
333                     // this shouldn't happen
334                     LOG.error("Tunnel IP for {} invalid", ofc.getNodeId());
335                     return;
336                 }
337
338
339                 long portNum;
340                 try {
341                     portNum = getOfPortNum(tunPort);
342                 } catch (NumberFormatException ex) {
343                     LOG.warn("Could not parse port number {}", 
344                              ofc.getNodeConnectorId(), ex);
345                     return;
346                 }
347                 
348                 setNextHop = nxLoadRegAction(NxmNxReg7.class, 
349                                              BigInteger.valueOf(portNum));
350                 Action tunIdAction = 
351                         nxMoveRegTunIdAction(NxmNxReg0.class, false);
352
353                 applyActions.add(tunIdAction);
354                 applyActions.add(tundstAction);
355                 l3ApplyActions.add(setDlSrc);
356                 l3ApplyActions.add(decTtl);
357                 order +=1;
358             }
359         }
360         applyActions.add(setdEPG);
361         applyActions.add(setdCG);
362         applyActions.add(setNextHop);
363         Instruction applyActionsIns = new InstructionBuilder()
364             .setOrder(order++)
365             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
366             .build();
367         instructions.add(applyActionsIns);
368
369         applyActions.addAll(l3ApplyActions);
370         applyActionsIns = new InstructionBuilder()
371             .setOrder(order++)
372             .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
373             .build();
374         l3instructions.add(applyActionsIns);
375         
376         Instruction gotoTable = new InstructionBuilder()
377             .setOrder(order++)
378             .setInstruction(gotoTableIns((short)(getTableId()+1)))
379             .build();
380         instructions.add(gotoTable);
381         l3instructions.add(gotoTable);
382
383         FlowId flowid = new FlowId(new StringBuilder()
384             .append(bdId)
385             .append("|l2|")
386             .append(e.getMacAddress().getValue())
387             .append("|")
388             .append(nextHop)
389             .toString());
390         if (visit(flowMap, flowid.getValue())) {
391             MatchBuilder mb = new MatchBuilder()
392                 .setEthernetMatch(ethernetMatch(null, 
393                                                 e.getMacAddress(), 
394                                                 null));
395             addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(bdId)));
396             FlowBuilder flowb = base()
397                 .setId(flowid)
398                 .setPriority(Integer.valueOf(50))
399                 .setMatch(mb.build())
400                 .setInstructions(new InstructionsBuilder()
401                     .setInstruction(instructions)
402                     .build());
403
404             writeFlow(t, tiid, flowb.build());
405         }
406         if (e.getL3Address() == null) return;
407         for (L3Address l3a : e.getL3Address()) {
408             if (l3a.getIpAddress() == null || l3a.getL3Context() == null)
409                 continue;
410             Layer3Match m = null;
411             Long etherType = null;
412             String ikey = null;
413             if (l3a.getIpAddress().getIpv4Address() != null) {
414                 ikey = l3a.getIpAddress().getIpv4Address().getValue() + "/32";
415                 etherType = IPv4;
416                 m = new Ipv4MatchBuilder()
417                     .setIpv4Destination(new Ipv4Prefix(ikey))
418                     .build();
419             } else if (l3a.getIpAddress().getIpv6Address() != null) {
420                 ikey = l3a.getIpAddress().getIpv6Address().getValue() + "/128";
421                 etherType = IPv6;
422                 m = new Ipv6MatchBuilder()
423                     .setIpv6Destination(new Ipv6Prefix(ikey))
424                     .build();
425             } else
426                 continue;
427
428             flowid = new FlowId(new StringBuilder()
429                 .append(l3a.getL3Context().getValue())
430                 .append("|l3|")
431                 .append(ikey)
432                 .append("|")
433                 .append(nextHop)
434                 .toString());
435             if (visit(flowMap, flowid.getValue())) {
436                 MatchBuilder mb = new MatchBuilder()
437                     .setEthernetMatch(ethernetMatch(null, 
438                                                     ROUTER_MAC, 
439                                                     etherType))
440                     .setLayer3Match(m);
441                 addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, 
442                                               Long.valueOf(l3Id)));
443                 FlowBuilder flowb = base()
444                     .setId(flowid)
445                     .setPriority(Integer.valueOf(132))
446                     .setMatch(mb.build())
447                     .setInstructions(new InstructionsBuilder()
448                         .setInstruction(l3instructions)
449                         .build());
450
451                 writeFlow(t, tiid, flowb.build());
452             }
453         }
454     }
455     
456     static byte[] bytesFromHexString(String values) {
457         String target = "";
458         if (values != null) {
459             target = values;
460         }
461         String[] octets = target.split(":");
462
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();
466         }
467         return ret;
468     }
469 }