2 * Copyright (c) 2015 - 2016 Ericsson India Global Services Pvt Ltd. 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
8 package org.opendaylight.vpnservice.fibmanager;
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
15 import java.math.BigInteger;
16 import java.net.InetAddress;
17 import java.net.UnknownHostException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Future;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
26 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
27 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
28 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.vpnmanager.api.IVpnManager;
31 import org.opendaylight.vpnservice.mdsalutil.AbstractDataChangeListener;
32 import org.opendaylight.vpnservice.itm.globals.ITMConstants;
33 import org.opendaylight.vpnservice.mdsalutil.ActionInfo;
34 import org.opendaylight.vpnservice.mdsalutil.ActionType;
35 import org.opendaylight.vpnservice.mdsalutil.FlowEntity;
36 import org.opendaylight.vpnservice.mdsalutil.InstructionInfo;
37 import org.opendaylight.vpnservice.mdsalutil.InstructionType;
38 import org.opendaylight.vpnservice.mdsalutil.MDSALUtil;
39 import org.opendaylight.vpnservice.mdsalutil.MatchFieldType;
40 import org.opendaylight.vpnservice.mdsalutil.MatchInfo;
41 import org.opendaylight.vpnservice.mdsalutil.MetaDataUtil;
42 import org.opendaylight.vpnservice.mdsalutil.NwConstants;
43 import org.opendaylight.vpnservice.mdsalutil.interfaces.IMdsalApiManager;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.OpState;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.PrefixToInterface;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnInstanceOpData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.VpnToExtraroute;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.VpnIds;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.VpnIdsKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.prefix.to._interface.vpn.ids.PrefixesKey;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntryKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.itm.rpcs.rev151217.ItmRpcService;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.Vpn;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.VpnKey;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.Extraroute;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.ExtrarouteBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.to.extraroute.vpn.ExtrarouteKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.FibEntries;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTables;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.fibentries.VrfTablesKey;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.fibmanager.rev150330.vrfentries.VrfEntry;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeBase;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rev150331.TunnelTypeMplsOverGre;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceInputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetEgressActionsForInterfaceOutput;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetTunnelTypeInputBuilder;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.GetTunnelTypeOutput;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.interfacemgr.rpcs.rev151003.OdlInterfaceRpcService;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.vpnservice.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
74 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.Adjacencies;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.adjacency.list.Adjacency;
77 import org.opendaylight.yangtools.concepts.ListenerRegistration;
78 import org.opendaylight.yangtools.yang.binding.DataObject;
79 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
80 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
81 import org.opendaylight.yangtools.yang.common.RpcResult;
82 import org.slf4j.Logger;
83 import org.slf4j.LoggerFactory;
85 public class FibManager extends AbstractDataChangeListener<VrfEntry> implements AutoCloseable{
86 private static final Logger LOG = LoggerFactory.getLogger(FibManager.class);
87 private static final String FLOWID_PREFIX = "L3.";
88 private ListenerRegistration<DataChangeListener> listenerRegistration;
89 private final DataBroker broker;
90 private IMdsalApiManager mdsalManager;
91 private IVpnManager vpnmanager;
92 private NexthopManager nextHopManager;
93 private ItmRpcService itmManager;
94 private OdlInterfaceRpcService interfaceManager;
95 private static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
96 private static final BigInteger COOKIE_VM_FIB_TABLE = new BigInteger("8000003", 16);
97 private static final int DEFAULT_FIB_FLOW_PRIORITY = 10;
98 private static final BigInteger METADATA_MASK_CLEAR = new BigInteger("000000FFFFFFFFFF", 16);
99 private static final BigInteger CLEAR_METADATA = BigInteger.valueOf(0);
100 public static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
103 public FibManager(final DataBroker db) {
104 super(VrfEntry.class);
106 registerListener(db);
110 public void close() throws Exception {
111 if (listenerRegistration != null) {
113 listenerRegistration.close();
114 } catch (final Exception e) {
115 LOG.error("Error when cleaning up DataChangeListener.", e);
117 listenerRegistration = null;
119 LOG.info("Fib Manager Closed");
122 public void setNextHopManager(NexthopManager nextHopManager) {
123 this.nextHopManager = nextHopManager;
126 public void setMdsalManager(IMdsalApiManager mdsalManager) {
127 this.mdsalManager = mdsalManager;
130 public void setVpnmanager(IVpnManager vpnmanager) {
131 this.vpnmanager = vpnmanager;
134 public void setITMRpcService(ItmRpcService itmManager) {
135 this.itmManager = itmManager;
138 public void setInterfaceManager(OdlInterfaceRpcService ifManager) {
139 this.interfaceManager = ifManager;
142 private void registerListener(final DataBroker db) {
144 listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
145 getWildCardPath(), FibManager.this, DataChangeScope.SUBTREE);
146 } catch (final Exception e) {
147 LOG.error("FibManager DataChange listener registration fail!", e);
148 throw new IllegalStateException("FibManager registration Listener failed.", e);
153 private InstanceIdentifier<VrfEntry> getWildCardPath() {
154 return InstanceIdentifier.create(FibEntries.class).child(VrfTables.class).child(VrfEntry.class);
159 protected void add(final InstanceIdentifier<VrfEntry> identifier,
160 final VrfEntry vrfEntry) {
161 LOG.trace("key: " + identifier + ", value=" + vrfEntry );
162 createFibEntries(identifier, vrfEntry);
166 protected void remove(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry) {
167 LOG.trace("key: " + identifier + ", value=" + vrfEntry);
168 deleteFibEntries(identifier, vrfEntry);
172 protected void update(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update) {
173 LOG.trace("key: " + identifier + ", original=" + original + ", update=" + update );
174 createFibEntries(identifier, update);
177 private void createFibEntries(final InstanceIdentifier<VrfEntry> identifier,
178 final VrfEntry vrfEntry) {
179 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
180 Preconditions.checkNotNull(vrfTableKey, "VrfTablesKey cannot be null or empty!");
181 Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
183 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
184 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
185 Preconditions.checkNotNull(vpnInstance.getVpnId(), "Vpn Instance with rd " + vpnInstance.getVrfId() + "has null vpnId!");
187 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
188 BigInteger localDpnId = createLocalFibEntry(vpnInstance.getVpnId(),
189 vrfTableKey.getRouteDistinguisher(), vrfEntry);
190 if (vpnToDpnList != null) {
191 for (VpnToDpnList curDpn : vpnToDpnList) {
192 if (!curDpn.getDpnId().equals(localDpnId)) {
193 createRemoteFibEntry(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(),
194 vrfTableKey, vrfEntry);
200 public BigInteger createLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
201 BigInteger localDpnId = BigInteger.ZERO;
202 Prefixes localNextHopInfo = getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
203 String localNextHopIP = vrfEntry.getDestPrefix();
205 if(localNextHopInfo == null) {
206 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
207 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
208 if (extra_route != null) {
209 localNextHopInfo = getPrefixToInterface(vpnId, extra_route.getNexthopIp() + "/32");
210 localNextHopIP = extra_route.getNexthopIp() + "/32";
214 if(localNextHopInfo != null) {
215 localDpnId = localNextHopInfo.getDpnId();
216 long groupId = nextHopManager.createLocalNextHop(vpnId, localDpnId, localNextHopInfo.getVpnInterfaceName(), localNextHopIP);
217 List<ActionInfo> actionInfos = new ArrayList<ActionInfo>();
219 actionInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId)}));
221 makeConnectedRoute(localDpnId, vpnId, vrfEntry, rd, actionInfos, NwConstants.ADD_FLOW);
222 makeLFibTableEntry(localDpnId, vrfEntry.getLabel(), groupId, vrfEntry.getNextHopAddress(), NwConstants.ADD_FLOW);
224 LOG.debug("Installing tunnel table entry on dpn {} for interface {} with label {}",
225 localDpnId, localNextHopInfo.getVpnInterfaceName(), vrfEntry.getLabel());
226 makeTunnelTableEntry(localDpnId, vrfEntry.getLabel(), groupId);
232 private void makeTunnelTableEntry(BigInteger dpId, long label, long groupId/*String egressInterfaceName*/) {
233 List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
234 actionsInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId) }));
237 createTerminatingServiceActions(dpId, (int)label, actionsInfos);
239 LOG.debug("Terminating service Entry for dpID {} : label : {} egress : {} installed successfully {}",
240 dpId, label, groupId);
243 public void createTerminatingServiceActions( BigInteger destDpId, int label, List<ActionInfo> actionsInfos) {
244 List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
246 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", destDpId , label,actionsInfos);
249 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
250 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(label)}));
252 List<InstructionInfo> mkInstructions = new ArrayList<InstructionInfo>();
253 mkInstructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
255 FlowEntity terminatingServiceTableFlowEntity = MDSALUtil.buildFlowEntity(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,
256 getFlowRef(destDpId, NwConstants.INTERNAL_TUNNEL_TABLE,label), 5, String.format("%s:%d","TST Flow Entry ",label),
257 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(label)),mkMatches, mkInstructions);
259 mdsalManager.installFlow(terminatingServiceTableFlowEntity);
262 private void removeTunnelTableEntry(BigInteger dpId, long label) {
263 FlowEntity flowEntity;
264 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpId , label);
265 List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
267 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(label)}));
268 flowEntity = MDSALUtil.buildFlowEntity(dpId,
269 NwConstants.INTERNAL_TUNNEL_TABLE,
270 getFlowRef(dpId, NwConstants.INTERNAL_TUNNEL_TABLE, (int)label),
271 5, String.format("%s:%d","TST Flow Entry ",label), 0, 0,
272 COOKIE_TUNNEL.add(BigInteger.valueOf(label)), mkMatches, null);
273 mdsalManager.removeFlow(flowEntity);
274 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpId, label);
277 public BigInteger deleteLocalFibEntry(Long vpnId, String rd, VrfEntry vrfEntry) {
278 BigInteger localDpnId = BigInteger.ZERO;
279 boolean isExtraRoute = false;
280 VpnNexthop localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
281 String localNextHopIP = vrfEntry.getDestPrefix();
283 if(localNextHopInfo == null) {
284 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
285 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
286 if (extra_route != null) {
287 localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, extra_route.getNexthopIp());
288 localNextHopIP = extra_route.getNexthopIp() + "/32";
294 if(localNextHopInfo != null) {
295 localDpnId = localNextHopInfo.getDpnId();
296 Prefixes prefix = getPrefixToInterface(vpnId, isExtraRoute ? localNextHopIP : vrfEntry.getDestPrefix());
297 Optional<OpState> opStateData = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
298 FibUtil.getVpnInterfaceOpStateIdentifier(prefix.getVpnInterfaceName()));
299 if(opStateData.isPresent() && !opStateData.get().isStateUp())
301 makeConnectedRoute(localDpnId, vpnId, vrfEntry, rd, null /* invalid */,
302 NwConstants.DEL_FLOW);
303 makeLFibTableEntry(localDpnId, vrfEntry.getLabel(), 0 /* invalid */,
304 vrfEntry.getNextHopAddress(), NwConstants.DEL_FLOW);
305 removeTunnelTableEntry(localDpnId, vrfEntry.getLabel());
306 deleteLocalAdjacency(localDpnId, vpnId, localNextHopIP);
312 private InstanceIdentifier<Prefixes> getPrefixToInterfaceIdentifier(Long vpnId, String ipPrefix) {
313 return InstanceIdentifier.builder(PrefixToInterface.class)
314 .child(VpnIds.class, new VpnIdsKey(vpnId)).child(Prefixes.class, new PrefixesKey(ipPrefix)).build();
317 private Prefixes getPrefixToInterface(Long vpnId, String ipPrefix) {
318 Optional<Prefixes> localNextHopInfoData =
319 FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, getPrefixToInterfaceIdentifier(vpnId, ipPrefix));
320 return localNextHopInfoData.isPresent() ? localNextHopInfoData.get() : null;
323 private InstanceIdentifier<Extraroute> getVpnToExtrarouteIdentifier(String vrfId, String ipPrefix) {
324 return InstanceIdentifier.builder(VpnToExtraroute.class)
325 .child(Vpn.class, new VpnKey(vrfId)).child(Extraroute.class,
326 new ExtrarouteKey(ipPrefix)).build();
329 private Extraroute getVpnToExtraroute(String rd, String ipPrefix) {
330 Optional<Extraroute> extraRouteInfo =
331 FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, getVpnToExtrarouteIdentifier(rd, ipPrefix));
332 return extraRouteInfo.isPresent() ? extraRouteInfo.get() : null;
336 private Class<? extends TunnelTypeBase> getTunnelType(String ifName) {
338 Future<RpcResult<GetTunnelTypeOutput>> result = interfaceManager.getTunnelType(
339 new GetTunnelTypeInputBuilder().setIntfName(ifName).build());
340 RpcResult<GetTunnelTypeOutput> rpcResult = result.get();
341 if(!rpcResult.isSuccessful()) {
342 LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
344 return rpcResult.getResult().getTunnelType();
347 } catch (InterruptedException | ExecutionException e) {
348 LOG.warn("Exception when getting tunnel interface Id for tunnel type {}", e);
354 private void createRemoteFibEntry(final BigInteger localDpnId, final BigInteger remoteDpnId,
355 final long vpnId, final VrfTablesKey vrfTableKey,
356 final VrfEntry vrfEntry) {
357 String rd = vrfTableKey.getRouteDistinguisher();
358 LOG.debug("adding route " + vrfEntry.getDestPrefix() + " " + rd);
359 /********************************************/
360 String tunnelInterface = resolveAdjacency(localDpnId, remoteDpnId, vpnId, vrfEntry, rd);
361 if(tunnelInterface == null) {
362 LOG.error("Could not get interface for nexthop: {} in vpn {}",
363 vrfEntry.getNextHopAddress(), rd);
364 LOG.warn("Failed to add Route: {} in vpn: {}",
365 vrfEntry.getDestPrefix(), rd);
368 List<ActionInfo> actionInfos = new ArrayList<>();
369 Class<? extends TunnelTypeBase> tunnel_type = getTunnelType(tunnelInterface);
370 if (tunnel_type.equals(TunnelTypeMplsOverGre.class)) {
371 LOG.debug("Push label action for prefix {}", vrfEntry.getDestPrefix());
372 actionInfos.add(new ActionInfo(ActionType.push_mpls, new String[] { null }));
373 actionInfos.add(new ActionInfo(ActionType.set_field_mpls_label, new String[] { Long.toString(vrfEntry.getLabel())}));
375 int label = vrfEntry.getLabel().intValue();
377 // FIXME vxlan vni bit set is not working properly with OVS.need to revisit
378 if(tunnel_type.equals(TunnelTypeVxlan.class)) {
379 tunnelId = BigInteger.valueOf(label);
381 tunnelId = BigInteger.valueOf(label);
383 LOG.debug("adding set tunnel id action for label {}", label);
384 actionInfos.add(new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[]{
387 actionInfos.addAll(nextHopManager.getEgressActionsForInterface(tunnelInterface));
389 List<ActionInfo> actionInfos = resolveAdjacency(localDpnId, remoteDpnId, vpnId, vrfEntry);
390 if(actionInfos == null) {
391 LOG.error("Could not get nexthop group id for nexthop: {} in vpn {}",
392 vrfEntry.getNextHopAddress(), rd);
393 LOG.warn("Failed to add Route: {} in vpn: {}",
394 vrfEntry.getDestPrefix(), rd);
397 BigInteger dpnId = nextHopManager.getDpnForPrefix(vpnId, vrfEntry.getDestPrefix());
399 //This route may be extra route... try to query with nexthop Ip
400 LOG.debug("Checking for extra route to install remote fib entry {}", vrfEntry.getDestPrefix());
401 dpnId = nextHopManager.getDpnForPrefix(vpnId, vrfEntry.getNextHopAddress() + "/32");
404 LOG.debug("Push label action for prefix {}", vrfEntry.getDestPrefix());
405 actionInfos.add(new ActionInfo(ActionType.push_mpls, new String[] { null }));
406 actionInfos.add(new ActionInfo(ActionType.set_field_mpls_label, new String[] { Long.toString(vrfEntry.getLabel())}));
408 int label = vrfEntry.getLabel().intValue();
409 LOG.debug("adding set tunnel id action for label {}", label);
410 actionInfos.add(new ActionInfo(ActionType.set_field_tunnel_id, new BigInteger[] {
411 MetaDataUtil.getTunnelIdWithValidVniBitAndVniSet(label),
412 MetaDataUtil.METADA_MASK_VALID_TUNNEL_ID_BIT_AND_TUNNEL_ID }));
415 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, actionInfos, NwConstants.ADD_FLOW);
417 "Successfully added fib entry for " + vrfEntry.getDestPrefix() + " vpnId " + vpnId);
420 private void delIntfFromDpnToVpnList(long vpnId, BigInteger dpnId, String intfName, String rd) {
421 InstanceIdentifier<VpnToDpnList> id = FibUtil.getVpnToDpnListIdentifier(rd, dpnId);
422 Optional<VpnToDpnList> dpnInVpn = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
423 if (dpnInVpn.isPresent()) {
424 List<org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
425 .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
426 org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces
427 currVpnInterface = new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesBuilder().setInterfaceName(intfName).build();
429 if (vpnInterfaces.remove(currVpnInterface)) {
430 if (vpnInterfaces.isEmpty()) {
431 //FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id);
432 LOG.trace("cleanUpOpDataForFib: cleanUpDpnForVpn: {}, {}", dpnId, vpnId);
433 cleanUpDpnForVpn(dpnId, vpnId, rd);
435 LOG.trace("cleanUpOpDataForFib: delete interface: {} on {}", intfName, dpnId);
436 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id.child(
437 org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data
438 .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
439 new org.opendaylight.yang.gen.v1.urn.opendaylight.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesKey(intfName)));
445 private void cleanUpOpDataForFib(Long vpnId, String rd, final VrfEntry vrfEntry) {
446 /* Get interface info from prefix to interface mapping;
447 Use the interface info to get the corresponding vpn interface op DS entry,
448 remove the adjacency corresponding to this fib entry.
449 If adjacency removed is the last adjacency, clean up the following:
450 - vpn interface from dpntovpn list, dpn if last vpn interface on dpn
451 - prefix to interface entry
452 - vpn interface op DS
454 Prefixes prefixInfo = getPrefixToInterface(vpnId, vrfEntry.getDestPrefix());
455 boolean extra_route = false;
456 if (prefixInfo == null) {
457 prefixInfo = getPrefixToInterface(vpnId, vrfEntry.getNextHopAddress() + "/32");
460 if (prefixInfo == null)
461 return; //Don't have any info for this prefix (shouldn't happen); need to return
462 String ifName = prefixInfo.getVpnInterfaceName();
463 Optional<OpState> opStateData = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
464 FibUtil.getVpnInterfaceOpStateIdentifier(ifName));
465 if(opStateData.isPresent() && !opStateData.get().isStateUp()) {
466 Optional<Adjacencies>
468 FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, FibUtil.getAdjListPath(ifName));
470 if (optAdjacencies.isPresent()) {
471 numAdj = optAdjacencies.get().getAdjacency().size();
473 LOG.trace("cleanUpOpDataForFib: remove adjacency for prefix: {} {}", vpnId,
474 vrfEntry.getDestPrefix());
475 //remove adjacency corr to prefix
476 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
477 FibUtil.getAdjacencyIdentifier(ifName, vrfEntry.getDestPrefix()));
479 if ((numAdj - 1) == 0) { //there are no adjacencies left for this vpn interface, clean up
480 //clean up the vpn interface from DpnToVpn list
481 delIntfFromDpnToVpnList(vpnId, prefixInfo.getDpnId(), ifName, rd);
482 LOG.trace("cleanUpOpDataForFib: Delete prefix to interface and vpnInterface ");
483 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
484 FibUtil.getPrefixToInterfaceIdentifier(
486 (extra_route) ? vrfEntry.getNextHopAddress() + "/32"
487 : vrfEntry.getDestPrefix()));
488 FibUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
489 FibUtil.getVpnInterfaceIdentifier(ifName));
494 private void deleteFibEntries(final InstanceIdentifier<VrfEntry> identifier,
495 final VrfEntry vrfEntry) {
496 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class, VrfTablesKey.class);
497 Preconditions.checkNotNull(vrfTableKey, "VrfTablesKey cannot be null or empty!");
498 Preconditions.checkNotNull(vrfEntry, "VrfEntry cannot be null or empty!");
500 VpnInstanceOpDataEntry vpnInstance = getVpnInstance(vrfTableKey.getRouteDistinguisher());
501 Preconditions.checkNotNull(vpnInstance, "Vpn Instance not available!");
502 Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
503 BigInteger localDpnId = deleteLocalFibEntry(vpnInstance.getVpnId(),
504 vrfTableKey.getRouteDistinguisher(), vrfEntry);
505 if (vpnToDpnList != null) {
506 for (VpnToDpnList curDpn : vpnToDpnList) {
507 if (!curDpn.getDpnId().equals(localDpnId)) {
508 deleteRemoteRoute(localDpnId, curDpn.getDpnId(), vpnInstance.getVpnId(), vrfTableKey, vrfEntry);
512 //The flow/group entry has been deleted from config DS; need to clean up associated operational
513 //DS entries in VPN Op DS, VpnInstanceOpData and PrefixToInterface to complete deletion
514 cleanUpOpDataForFib(vpnInstance.getVpnId(), vrfTableKey.getRouteDistinguisher(), vrfEntry);
517 public void deleteRemoteRoute(final BigInteger localDpnId, final BigInteger remoteDpnId,
518 final long vpnId, final VrfTablesKey vrfTableKey,
519 final VrfEntry vrfEntry) {
520 LOG.debug("deleting route "+ vrfEntry.getDestPrefix() + " "+vpnId);
521 String rd = vrfTableKey.getRouteDistinguisher();
522 boolean isRemoteRoute = true;
523 if (localDpnId == null) {
524 // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
525 VpnNexthop localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
526 if(localNextHopInfo == null) {
527 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
528 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
529 if (extra_route != null) {
530 localNextHopInfo = nextHopManager.getVpnNexthop(vpnId, extra_route.getNexthopIp());
533 isRemoteRoute = ((localNextHopInfo != null) && (!remoteDpnId.equals(localNextHopInfo.getDpnId())));
536 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, null, NwConstants.DEL_FLOW);
537 LOG.debug("Successfully delete fib entry for "+ vrfEntry.getDestPrefix() + " vpnId "+vpnId);
539 LOG.debug("Did not delete fib entry rd: {} =, prefix: {} as it is local to dpn {}", rd, vrfEntry.getDestPrefix(), remoteDpnId);
543 private long getIpAddress(byte[] rawIpAddress) {
544 return (((rawIpAddress[0] & 0xFF) << (3 * 8)) + ((rawIpAddress[1] & 0xFF) << (2 * 8))
545 + ((rawIpAddress[2] & 0xFF) << (1 * 8)) + (rawIpAddress[3] & 0xFF)) & 0xffffffffL;
548 private void makeConnectedRoute(BigInteger dpId, long vpnId, VrfEntry vrfEntry, String rd,
549 List<ActionInfo> actionInfos, int addOrRemove) {
550 LOG.trace("makeConnectedRoute: vrfEntry {}",vrfEntry);
551 String values[] = vrfEntry.getDestPrefix().split("/");
552 String ipAddress = values[0];
553 int prefixLength = (values.length == 1) ? 0 : Integer.parseInt(values[1]);
554 LOG.debug("Adding route to DPN. ip {} masklen {}", ipAddress, prefixLength);
555 InetAddress destPrefix = null;
557 destPrefix = InetAddress.getByName(ipAddress);
558 } catch (UnknownHostException e) {
559 LOG.error("UnknowHostException in addRoute. Failed to add Route for ipPrefix {}", vrfEntry.getDestPrefix());
563 List<MatchInfo> matches = new ArrayList<MatchInfo>();
565 matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
566 BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID }));
568 matches.add(new MatchInfo(MatchFieldType.eth_type,
569 new long[] { 0x0800L }));
571 if(prefixLength != 0) {
572 matches.add(new MatchInfo(MatchFieldType.ipv4_dst, new long[] {
573 getIpAddress(destPrefix.getAddress()), prefixLength }));
576 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
577 if(addOrRemove == NwConstants.ADD_FLOW) {
578 instructions.add(new InstructionInfo(InstructionType.write_actions, actionInfos));
581 String flowRef = getFlowRef(dpId, NwConstants.L3_FIB_TABLE, rd, destPrefix);
583 FlowEntity flowEntity;
585 int priority = DEFAULT_FIB_FLOW_PRIORITY + prefixLength;
586 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_FIB_TABLE, flowRef,
587 priority, flowRef, 0, 0,
588 COOKIE_VM_FIB_TABLE, matches, instructions);
590 if (addOrRemove == NwConstants.ADD_FLOW) {
591 /* We need to call sync API to install flow so that 2 DS operations on the same object do not
592 * happen at same time. However, MDSALManager's syncInstallFlow takes a delay time (or uses a default one) to wait
593 * for or for notification that operational DS write for flows is done. We do not turn on the stats writing for flows,
594 * so that notification never comes, so we do not need that wait. Sending the lowest value of wait "1 ms" since 0 wait means
595 * wait indefinitely. */
596 mdsalManager.syncInstallFlow(flowEntity, 1);
598 mdsalManager.syncRemoveFlow(flowEntity, 1);
602 private void makeLFibTableEntry(BigInteger dpId, long label, long groupId,
603 String nextHop, int addOrRemove) {
604 List<MatchInfo> matches = new ArrayList<MatchInfo>();
605 matches.add(new MatchInfo(MatchFieldType.eth_type,
606 new long[] { 0x8847L }));
607 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(label)}));
609 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
610 List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
611 actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
612 actionsInfos.add(new ActionInfo(ActionType.group, new String[] { String.valueOf(groupId) }));
613 instructions.add(new InstructionInfo(InstructionType.write_actions, actionsInfos));
615 // Install the flow entry in L3_LFIB_TABLE
616 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, label, nextHop);
618 FlowEntity flowEntity;
619 flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_LFIB_TABLE, flowRef,
620 DEFAULT_FIB_FLOW_PRIORITY, flowRef, 0, 0,
621 COOKIE_VM_LFIB_TABLE, matches, instructions);
623 if (addOrRemove == NwConstants.ADD_FLOW) {
624 /* We need to call sync API to install flow so that 2 DS operations on the same object do not
625 * happen at same time. However, MDSALManager's syncInstallFlow takes a delay time (or uses a default one) to wait
626 * for or for notification that operational DS write for flows is done. We do not turn on the stats writing for flows,
627 * so that notification never comes, so we do not need that wait. Sending the lowest value of wait "1 ms" since 0 wait means
628 * wait indefinitely. */
630 mdsalManager.syncInstallFlow(flowEntity, 1);
632 mdsalManager.syncRemoveFlow(flowEntity, 1);
634 LOG.debug("LFIB Entry for dpID {} : label : {} group {} modified successfully {}",dpId, label, groupId );
637 private void deleteLocalAdjacency(final BigInteger dpId, final long vpnId, final String ipAddress) {
638 LOG.trace("deleteLocalAdjacency called with dpid {}, vpnId{}, ipAddress {}",dpId, vpnId, ipAddress);
640 nextHopManager.removeLocalNextHop(dpId, vpnId, ipAddress);
641 } catch (NullPointerException e) {
646 public void populateFibOnNewDpn(BigInteger dpnId, long vpnId, String rd) {
647 LOG.trace("New dpn {} for vpn {} : populateFibOnNewDpn", dpnId, rd);
648 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
649 Optional<VrfTables> vrfTable = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
650 if(vrfTable.isPresent()) {
651 for(VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
652 // Passing null as we don't know the dpn
653 // to which prefix is attached at this point
654 createRemoteFibEntry(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry);
659 public void cleanUpDpnForVpn(BigInteger dpnId, long vpnId, String rd) {
660 LOG.trace("Remove dpn {} for vpn {} : cleanUpDpnForVpn", dpnId, rd);
661 InstanceIdentifier<VrfTables> id = buildVrfId(rd);
662 Optional<VrfTables> vrfTable = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
663 if(vrfTable.isPresent()) {
664 for(VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
665 // Passing null as we don't know the dpn
666 // to which prefix is attached at this point
667 deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().getKey(), vrfEntry);
672 public static InstanceIdentifier<VrfTables> buildVrfId(String rd) {
673 InstanceIdentifierBuilder<VrfTables> idBuilder =
674 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
675 InstanceIdentifier<VrfTables> id = idBuilder.build();
679 private String getFlowRef(BigInteger dpnId, short tableId, long label, String nextHop) {
680 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
681 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
682 .append(label).append(NwConstants.FLOWID_SEPARATOR)
683 .append(nextHop).toString();
686 private String getFlowRef(BigInteger dpnId, short tableId, String rd, InetAddress destPrefix) {
687 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
688 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
689 .append(rd).append(NwConstants.FLOWID_SEPARATOR)
690 .append(destPrefix.getHostAddress()).toString();
693 protected String resolveAdjacency(final BigInteger localDpnId, final BigInteger remoteDpnId,
694 final long vpnId, final VrfEntry vrfEntry, String rd) {
695 String adjacency = null;
696 boolean staticRoute = false;
697 LOG.trace("resolveAdjacency called with localdpid{} remotedpid {}, vpnId{}, VrfEntry {}", localDpnId, remoteDpnId, vpnId, vrfEntry);
699 Extraroute extra_route = getVpnToExtraroute(rd, vrfEntry.getDestPrefix());
700 if(extra_route != null) {
705 nextHopManager.getRemoteNextHopPointer(localDpnId, remoteDpnId, vpnId,
706 vrfEntry.getDestPrefix(),
707 (staticRoute == true) ? extra_route.getNexthopIp() : vrfEntry.getNextHopAddress());
708 } catch (NullPointerException e) {
714 protected VpnInstanceOpDataEntry getVpnInstance(String rd) {
715 InstanceIdentifier<VpnInstanceOpDataEntry> id = InstanceIdentifier.create(VpnInstanceOpData.class).child(
716 VpnInstanceOpDataEntry.class, new VpnInstanceOpDataEntryKey(rd));
717 Optional<VpnInstanceOpDataEntry> vpnInstanceOpData = FibUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
718 if(vpnInstanceOpData.isPresent()) {
719 return vpnInstanceOpData.get();
724 public void processNodeAdd(BigInteger dpnId) {
725 LOG.debug("Received notification to install TableMiss entries for dpn {} ", dpnId);
726 makeTableMissFlow(dpnId, NwConstants.ADD_FLOW);
727 makeL3IntfTblMissFlow(dpnId, NwConstants.ADD_FLOW);
730 private void makeTableMissFlow(BigInteger dpnId, int addOrRemove) {
731 final BigInteger COOKIE_TABLE_MISS = new BigInteger("1030000", 16);
732 // Instruction to goto L3 InterfaceTable
733 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
734 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.L3_INTERFACE_TABLE }));
735 List<MatchInfo> matches = new ArrayList<MatchInfo>();
736 FlowEntity flowEntityLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_LFIB_TABLE,
737 getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, NwConstants.TABLE_MISS_FLOW),
738 NwConstants.TABLE_MISS_PRIORITY, "Table Miss", 0, 0, COOKIE_TABLE_MISS, matches, instructions);
740 FlowEntity flowEntityFib = MDSALUtil.buildFlowEntity(dpnId,NwConstants.L3_FIB_TABLE, getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, NwConstants.TABLE_MISS_FLOW),
741 NwConstants.TABLE_MISS_PRIORITY, "FIB Table Miss Flow", 0, 0, COOKIE_VM_FIB_TABLE,
742 matches, instructions);
744 if (addOrRemove == NwConstants.ADD_FLOW) {
745 LOG.debug("Invoking MDSAL to install Table Miss Entries");
746 mdsalManager.installFlow(flowEntityLfib);
747 mdsalManager.installFlow(flowEntityFib);
749 mdsalManager.removeFlow(flowEntityLfib);
750 mdsalManager.removeFlow(flowEntityFib);
755 private String getFlowRef(BigInteger dpnId, short tableId, int tableMiss) {
756 return new StringBuffer().append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
757 .append(tableId).append(NwConstants.FLOWID_SEPARATOR).append(tableMiss)
758 .append(FLOWID_PREFIX).toString();
762 * Install flow entry in protocol table to forward mpls
763 * coming through gre tunnel to LFIB table.
765 private void makeProtocolTableFlow(BigInteger dpnId, int addOrRemove) {
766 final BigInteger COOKIE_PROTOCOL_TABLE = new BigInteger("1070000", 16);
767 // Instruction to goto L3 InterfaceTable
768 List<InstructionInfo> instructions = new ArrayList<>();
769 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] {NwConstants.L3_LFIB_TABLE}));
770 List<MatchInfo> matches = new ArrayList<MatchInfo>();
771 matches.add(new MatchInfo(MatchFieldType.eth_type,
772 new long[] { 0x8847L }));
773 FlowEntity flowEntityToLfib = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_PROTOCOL_TABLE,
774 getFlowRef(dpnId, NwConstants.L3_PROTOCOL_TABLE,
775 NwConstants.L3_LFIB_TABLE),
776 DEFAULT_FIB_FLOW_PRIORITY,
777 "Protocol Table For LFIB",
779 COOKIE_PROTOCOL_TABLE,
780 matches, instructions);
782 if (addOrRemove == NwConstants.ADD_FLOW) {
783 LOG.debug("Invoking MDSAL to install Protocol Entries for dpn {}", dpnId);
784 mdsalManager.installFlow(flowEntityToLfib);
786 mdsalManager.removeFlow(flowEntityToLfib);
790 public List<String> printFibEntries() {
791 List<String> result = new ArrayList<String>();
792 result.add(String.format(" %-7s %-20s %-20s %-7s", "RD", "Prefix", "Nexthop", "Label"));
793 result.add("-------------------------------------------------------------------");
794 InstanceIdentifier<FibEntries> id = InstanceIdentifier.create(FibEntries.class);
795 Optional<FibEntries> fibEntries = FibUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
796 if (fibEntries.isPresent()) {
797 List<VrfTables> vrfTables = fibEntries.get().getVrfTables();
798 for (VrfTables vrfTable : vrfTables) {
799 for (VrfEntry vrfEntry : vrfTable.getVrfEntry()) {
800 result.add(String.format(" %-7s %-20s %-20s %-7s", vrfTable.getRouteDistinguisher(),
801 vrfEntry.getDestPrefix(), vrfEntry.getNextHopAddress(), vrfEntry.getLabel()));
808 private void makeL3IntfTblMissFlow(BigInteger dpnId, int addOrRemove) {
809 List<InstructionInfo> instructions = new ArrayList<InstructionInfo>();
810 List<MatchInfo> matches = new ArrayList<MatchInfo>();
811 final BigInteger COOKIE_TABLE_MISS = new BigInteger("1030000", 16);
812 // Instruction to clear metadata except SI and LportTag bits
813 instructions.add(new InstructionInfo(InstructionType.write_metadata, new BigInteger[] {
814 CLEAR_METADATA, METADATA_MASK_CLEAR }));
815 // Instruction to clear action
816 instructions.add(new InstructionInfo(InstructionType.clear_actions));
817 // Instruction to goto L3 InterfaceTable
819 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.LPORT_DISPATCHER_TABLE }));
821 FlowEntity flowEntityL3Intf = MDSALUtil.buildFlowEntity(dpnId, NwConstants.L3_INTERFACE_TABLE,
822 getFlowRef(dpnId, NwConstants.L3_INTERFACE_TABLE, NwConstants.TABLE_MISS_FLOW),
823 NwConstants.TABLE_MISS_PRIORITY, "L3 Interface Table Miss", 0, 0, COOKIE_TABLE_MISS, matches, instructions);
824 if (addOrRemove == NwConstants.ADD_FLOW) {
825 LOG.info("Invoking MDSAL to install L3 interface Table Miss Entries");
826 mdsalManager.installFlow(flowEntityL3Intf);
828 mdsalManager.removeFlow(flowEntityL3Intf);