rpc send-neighbor-solicitation {
- input {
- leaf target-ip-address {
- type inet:ipv6-address;
- }
- uses interfaces;
- }
+ input {
+ leaf target-ip-address {
+ type inet:ipv6-address;
+ }
+ uses interfaces;
+ }
+ }
+
+ rpc send-neighbor-solicitation-to-of-group {
+ input {
+ leaf source-ipv6 {
+ type inet:ipv6-address;
+ mandatory "true";
+ }
+ leaf target-ip-address {
+ type inet:ipv6-address;
+ mandatory "true";
+ }
+ leaf source-ll-address {
+ type yang:mac-address;
+ mandatory "true";
+ }
+ leaf dp-id {
+ type uint64;
+ mandatory "true";
+ }
+ leaf of-group-id {
+ type uint32;
+ mandatory "true";
+ description "NS will be sent to the specified OpenFlow group ID.";
+ }
+ }
+ }
+
+ notification na-received {
+ leaf source-mac {
+ type yang:mac-address;
+ }
+ leaf destination-mac {
+ type yang:mac-address;
+ }
+ leaf source-ipv6 {
+ type inet:ipv6-address;
+ }
+ leaf destination-ipv6 {
+ type inet:ipv6-address;
+ }
+ leaf target-address {
+ type inet:ipv6-address;
+ }
+ leaf target-ll-address {
+ type yang:mac-address;
+ }
+ leaf of-table-id {
+ type uint32;
+ }
+ leaf metadata {
+ type uint64;
+ }
+ leaf interface {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ }
}
}
package org.opendaylight.netvirt.ipv6service;
+import com.google.common.collect.Lists;
import com.google.common.net.InetAddresses;
import io.netty.util.Timeout;
import java.math.BigInteger;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
*/
programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6Constants.ADD_FLOW);
programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), llAddr, Ipv6Constants.ADD_FLOW);
+ programIcmpv6NaForwardFlows(intf, snetId, Ipv6Constants.ADD_FLOW);
} else {
intf = prevIntf;
intf.setSubnetInfo(snetId, fixedIp);
}
programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), fixedIp.getIpv6Address(), Ipv6Constants.ADD_FLOW);
+ programIcmpv6NaPuntFlow(networkId, intf.getSubnets(), Ipv6Constants.ADD_FLOW);
if (newIntf) {
LOG.debug("start the periodic RA Timer for routerIntf {}", portId);
}
}
- public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList) {
+ public void updateRouterIntf(Uuid portId, Uuid rtrId, List<FixedIps> fixedIpsList, Set<FixedIps> deletedIps) {
LOG.info("updateRouterIntf portId {}, fixedIpsList {} ", portId, fixedIpsList);
VirtualPort intf = getPort(portId);
if (intf == null) {
LOG.info("Skip Router interface update for non-ipv6 port {}", portId);
return;
}
-
- List<Ipv6Address> existingIPv6AddressList = intf.getIpv6AddressesWithoutLLA();
- List<Ipv6Address> newlyAddedIpv6AddressList = new ArrayList<>();
+ Uuid networkID = intf.getNetworkID();
intf.clearSubnetInfo();
for (FixedIps fip : fixedIpsList) {
IpAddress fixedIp = fip.getIpAddress();
addUnprocessed(unprocessedSubnetIntfs, subnetId, intf);
}
-
- Uuid networkID = intf.getNetworkID();
if (networkID != null) {
vrouterv6IntfMap.put(networkID, intf);
}
-
- if (existingIPv6AddressList.contains(fixedIp.getIpv6Address())) {
- existingIPv6AddressList.remove(fixedIp.getIpv6Address());
- } else {
- newlyAddedIpv6AddressList.add(fixedIp.getIpv6Address());
- }
}
- /* This is a port update event for routerPort. Check if any IPv6 subnet is added
- or removed from the router port. Depending on subnet added/removed, we add/remove
- the corresponding flows from IPV6_TABLE(45).
+ /*
+ * This is a port update event for routerPort. Check if any IPv6 subnet is added or removed from the
+ * router port. Depending on subnet added/removed, we add/remove the corresponding flows from
+ * IPV6_TABLE(45). Add is handled in addInterfaceInfo(), delete case is handled here.
*/
- for (Ipv6Address ipv6Address: newlyAddedIpv6AddressList) {
- // Some v6 subnets are associated to the routerPort add the corresponding NS Flows.
- programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address, Ipv6Constants.ADD_FLOW);
- }
-
- for (Ipv6Address ipv6Address: existingIPv6AddressList) {
- // Some v6 subnets are disassociated from the routerPort, remove the corresponding NS Flows.
- programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address, Ipv6Constants.DEL_FLOW);
+ for (FixedIps ips : deletedIps) {
+ VirtualSubnet snet = getSubnet(ips.getSubnetId());
+ programIcmpv6NaPuntFlow(networkID, Lists.newArrayList(snet), Ipv6Constants.DEL_FLOW);
+ programIcmpv6NSPuntFlowForAddress(networkID, ips.getIpAddress().getIpv6Address(), Ipv6Constants.DEL_FLOW);
}
}
List<String> ofportIds = interfaceState.getLowerLayerIf();
NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
- BigInteger dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
+ BigInteger dpId = ipv6ServiceUtils.getDpIdFromInterfaceState(interfaceState);
if (!dpId.equals(Ipv6Constants.INVALID_DPID)) {
Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
updateDpnInfo(portId, dpId, ofPort);
the dpnIds which were hosting the VMs on the network.
*/
programIcmpv6RSPuntFlows(intf.getNetworkID(), Ipv6Constants.DEL_FLOW);
+ programIcmpv6NaPuntFlow(networkID, intf.getSubnets(), Ipv6Constants.DEL_FLOW);
for (Ipv6Address ipv6Address: intf.getIpv6Addresses()) {
programIcmpv6NSPuntFlowForAddress(intf.getNetworkID(), ipv6Address, Ipv6Constants.DEL_FLOW);
}
+ for (VirtualSubnet subnet : intf.getSubnets()) {
+ programIcmpv6NaForwardFlows(intf, subnet.getSubnetUUID(), Ipv6Constants.DEL_FLOW);
+ }
transmitRouterAdvertisement(intf, Ipv6RtrAdvertType.CEASE_ADVERTISEMENT);
timer.cancelPeriodicTransmissionTimeout(intf.getPeriodicTimeout());
intf.resetPeriodicTimeout();
if (vnet != null) {
Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
- if (action == Ipv6Constants.ADD_FLOW && !dpnIfaceInfo.ndTargetFlowsPunted.contains(ipv6Address)
+ if (action == Ipv6Constants.ADD_FLOW && !dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)
&& dpnIfaceInfo.getDpId() != null) {
- ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
- elanTag, ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
+ ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
+ ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
- } else if (action == Ipv6Constants.DEL_FLOW && dpnIfaceInfo.ndTargetFlowsPunted.contains(ipv6Address)) {
- ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(),
- elanTag, ipv6Address.getValue(), Ipv6Constants.DEL_FLOW);
+ } else if (action == Ipv6Constants.DEL_FLOW
+ && dpnIfaceInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
+ ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpnIfaceInfo.getDpId(), elanTag,
+ ipv6Address.getValue(), Ipv6Constants.DEL_FLOW);
dpnIfaceInfo.updateNDTargetAddress(ipv6Address, action);
}
}
}
}
-
- public void programIcmpv6PuntFlowsIfNecessary(Uuid vmPortId, BigInteger dpId, VirtualPort routerPort) {
+ private void programIcmpv6NaPuntFlow(Uuid networkID, List<VirtualSubnet> subnets, int action) {
if (!ipv6ServiceEosHandler.isClusterOwner()) {
LOG.trace("Not a cluster Owner, skip flow programming.");
return;
}
+ Long elanTag = getNetworkElanTag(networkID);
+ VirtualNetwork vnet = getNetwork(networkID);
+ if (vnet != null) {
+ Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
+ for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
+ if (dpnIfaceInfo.getDpId() == null) {
+ continue;
+ }
+ for (VirtualSubnet subnet : subnets) {
+ Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
+ if (ipv6SubnetPrefix != null) {
+ if (action == Ipv6Constants.ADD_FLOW
+ && !dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+ ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
+ dpnIfaceInfo.getDpId(), elanTag, action);
+ dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
+ } else if (action == Ipv6Constants.DEL_FLOW
+ && dpnIfaceInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+ ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix,
+ dpnIfaceInfo.getDpId(), elanTag, action);
+ dpnIfaceInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), action);
+ }
+ }
+ }
+ }
+ }
+ }
- IVirtualPort vmPort = getPort(vmPortId);
- if (null != vmPort) {
- VirtualNetwork vnet = getNetwork(vmPort.getNetworkID());
- if (null != vnet) {
- VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
- if (null != dpnInfo) {
- Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
- if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6Constants.FLOWS_NOT_CONFIGURED) {
- ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
- Ipv6Constants.ADD_FLOW);
- vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6Constants.FLOWS_CONFIGURED);
+ public void handleInterfaceStateEvent(IVirtualPort port, BigInteger dpId, VirtualPort routerPort, int addOrRemove) {
+ Long elanTag = getNetworkElanTag(port.getNetworkID());
+ if (addOrRemove == Ipv6Constants.ADD_FLOW && routerPort != null) {
+ // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
+ programIcmpv6PuntFlows(port, dpId, elanTag, routerPort);
+ }
+ programIcmpv6NaForwardFlow(port, dpId, elanTag, addOrRemove);
+ }
+
+ private void programIcmpv6PuntFlows(IVirtualPort vmPort, BigInteger dpId, Long elanTag, VirtualPort routerPort) {
+ VirtualNetwork vnet = getNetwork(vmPort.getNetworkID());
+ if (null != vnet) {
+ VirtualNetwork.DpnInterfaceInfo dpnInfo = vnet.getDpnIfaceInfo(dpId);
+ if (null != dpnInfo) {
+ if (vnet.getRSPuntFlowStatusOnDpnId(dpId) == Ipv6Constants.FLOWS_NOT_CONFIGURED) {
+ ipv6ServiceUtils.installIcmpv6RsPuntFlow(NwConstants.IPV6_TABLE, dpId, elanTag,
+ Ipv6Constants.ADD_FLOW);
+ vnet.setRSPuntFlowStatusOnDpnId(dpId, Ipv6Constants.FLOWS_CONFIGURED);
+ }
+
+ for (Ipv6Address ipv6Address : routerPort.getIpv6Addresses()) {
+ if (!dpnInfo.isNdTargetFlowAlreadyConfigured(ipv6Address)) {
+ ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
+ elanTag, ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
+ dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6Constants.ADD_FLOW);
}
+ }
- for (Ipv6Address ipv6Address: routerPort.getIpv6Addresses()) {
- if (!dpnInfo.ndTargetFlowsPunted.contains(ipv6Address)) {
- ipv6ServiceUtils.installIcmpv6NsPuntFlow(NwConstants.IPV6_TABLE, dpId,
- elanTag, ipv6Address.getValue(), Ipv6Constants.ADD_FLOW);
- dpnInfo.updateNDTargetAddress(ipv6Address, Ipv6Constants.ADD_FLOW);
- }
+ for (VirtualSubnet subnet : routerPort.getSubnets()) {
+ Ipv6Prefix ipv6SubnetPrefix = subnet.getSubnetCidr().getIpv6Prefix();
+ if (ipv6SubnetPrefix != null
+ && !dpnInfo.isSubnetCidrFlowAlreadyConfigured(subnet.getSubnetUUID())) {
+ ipv6ServiceUtils.installIcmpv6NaPuntFlow(NwConstants.IPV6_TABLE, ipv6SubnetPrefix, dpId,
+ elanTag, Ipv6Constants.ADD_FLOW);
+ dpnInfo.updateSubnetCidrFlowStatus(subnet.getSubnetUUID(), Ipv6Constants.ADD_FLOW);
+ }
+ }
+ }
+ }
+ }
+
+ private void programIcmpv6NaForwardFlows(IVirtualPort routerPort, Uuid snetId, int action) {
+ if (!ipv6ServiceEosHandler.isClusterOwner()) {
+ LOG.trace("Not a cluster Owner, skip flow programming.");
+ return;
+ }
+
+ VirtualNetwork vnet = getNetwork(routerPort.getNetworkID());
+ if (vnet != null) {
+ Long elanTag = getNetworkElanTag(routerPort.getNetworkID());
+ Collection<VirtualNetwork.DpnInterfaceInfo> dpnIfaceList = vnet.getDpnIfaceList();
+ for (VirtualNetwork.DpnInterfaceInfo dpnIfaceInfo : dpnIfaceList) {
+ if (dpnIfaceInfo.getDpId() == null) {
+ continue;
+ }
+ List<VirtualPort> vmPorts = getVmPortsInSubnetByDpId(snetId, dpnIfaceInfo.getDpId());
+ for (VirtualPort vmPort : vmPorts) {
+ programIcmpv6NaForwardFlow(vmPort, dpnIfaceInfo.getDpId(), elanTag, action);
+ }
+ }
+ }
+ }
+
+ /**
+ * Programs ICMPv6 NA forwarding flow for fixed IPs of neutron port. NA's from non-fixed IPs are
+ * punted to controller for learning.
+ *
+ * @param vmPort the VM port
+ * @param dpId the DP ID
+ * @param elanTag the ELAN tag
+ * @param addOrRemove the add or remove flag
+ */
+ private void programIcmpv6NaForwardFlow(IVirtualPort vmPort, BigInteger dpId, Long elanTag, int addOrRemove) {
+ ipv6ServiceUtils.installIcmpv6NaForwardFlow(NwConstants.IPV6_TABLE, vmPort, dpId, elanTag, addOrRemove);
+ }
+
+ public List<VirtualPort> getVmPortsInSubnetByDpId(Uuid snetId, BigInteger dpId) {
+ List<VirtualPort> vmPorts = new ArrayList<>();
+ for (VirtualPort port : vintfs.values()) {
+ if (dpId.equals(port.getDpId()) && Ipv6ServiceUtils.isVmPort(port.getDeviceOwner())) {
+ for (VirtualSubnet subnet : port.getSubnets()) {
+ if (subnet.getSubnetUUID().equals(snetId)) {
+ vmPorts.add(port);
}
}
}
}
+ return vmPorts;
}
public String getInterfaceNameFromTag(long portTag) {
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.Ipv6NdutilService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.SendNeighborSolicitationInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.SendNeighborSolicitationOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.SendNeighborSolicitationToOfGroupInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.SendNeighborSolicitationToOfGroupOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.interfaces.InterfaceAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
import org.opendaylight.yangtools.yang.common.RpcError;
return result;
}
+ @Override
+ public ListenableFuture<RpcResult<SendNeighborSolicitationToOfGroupOutput>> sendNeighborSolicitationToOfGroup(
+ SendNeighborSolicitationToOfGroupInput ndInput) {
+ RpcResultBuilder<SendNeighborSolicitationToOfGroupOutput> successBuilder = RpcResultBuilder.success();
+ ipv6NeighborSolicitation.transmitNeighborSolicitationToOfGroup(ndInput.getDpId(), ndInput.getSourceLlAddress(),
+ ndInput.getSourceIpv6(), ndInput.getTargetIpAddress(), ndInput.getOfGroupId());
+
+ return successBuilder.buildFuture();
+ }
+
}
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
import org.opendaylight.genius.mdsalutil.MDSALUtil;
import org.opendaylight.genius.mdsalutil.NwConstants;
-import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
+import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
+import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
// Tx the packet out of the controller.
LOG.debug("Transmitting the Neighbor Solicitation packet out on {}", dpnId);
- JdkFutures.addErrorLogging(packetService.transmitPacket(input), LOG, "transmitPacket");
+ ListenableFutures.addErrorLogging(packetService.transmitPacket(input), LOG, "transmitPacket");
return true;
}
+ public void transmitNeighborSolicitationToOfGroup(BigInteger dpId, MacAddress srcMacAddress,
+ Ipv6Address srcIpv6Address, Ipv6Address targetIpv6Address, long ofGroupId) {
+ byte[] txPayload = frameNeighborSolicitationRequest(srcMacAddress, srcIpv6Address, targetIpv6Address);
+ List<ActionInfo> lstActionInfo = new ArrayList<>();
+ lstActionInfo.add(new ActionGroup(ofGroupId));
+
+ TransmitPacketInput input = MDSALUtil.getPacketOutDefault(lstActionInfo, txPayload, dpId);
+ // Tx the packet out of the controller.
+ LOG.debug(
+ "Transmitting Neighbor Solicitation packet out. srcMacAddress={}, srcIpv6Address={}, "
+ + "targetIpv6Address={}, dpId={}, ofGroupId={}",
+ srcMacAddress.getValue(), srcIpv6Address.getValue(), targetIpv6Address.getValue(), dpId, ofGroupId);
+ ListenableFutures.addErrorLogging(packetService.transmitPacket(input), LOG, "transmitPacket");
+ }
}
*/
package org.opendaylight.netvirt.ipv6service;
+import com.google.common.util.concurrent.ListenableFuture;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
import org.opendaylight.genius.mdsalutil.MetaDataUtil;
import org.opendaylight.infrautils.utils.concurrent.JdkFutures;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.NaReceivedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacket;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacketBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.Notification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private final AtomicLong pktProccessedCounter = new AtomicLong(0);
private final PacketProcessingService pktService;
private final IfMgr ifMgr;
+ private final NotificationPublishService notificationPublishService;
+
private final ExecutorService packetProcessor = Executors.newCachedThreadPool();
@Inject
- public Ipv6PktHandler(PacketProcessingService pktService, IfMgr ifMgr) {
+ public Ipv6PktHandler(PacketProcessingService pktService, IfMgr ifMgr,
+ NotificationPublishService notificationPublishService) {
this.pktService = pktService;
this.ifMgr = ifMgr;
+ this.notificationPublishService = notificationPublishService;
}
@Override
if (v6NxtHdr == Ipv6Constants.ICMP_V6_TYPE) {
int icmpv6Type = BitBufferHelper.getInt(BitBufferHelper.getBits(data,
Ipv6Constants.ICMPV6_HDR_START, Ipv6Constants.ONE_BYTE));
- if (icmpv6Type == Ipv6Constants.ICMP_V6_RS_CODE
- || icmpv6Type == Ipv6Constants.ICMP_V6_NS_CODE) {
+ if (isOfInterest(icmpv6Type)) {
packetProcessor.execute(new PacketHandler(icmpv6Type, packetReceived));
}
} else {
}
}
+ private boolean isOfInterest(int icmpv6Type) {
+ return icmpv6Type == Ipv6Constants.ICMP_V6_RS_CODE || icmpv6Type == Ipv6Constants.ICMP_V6_NS_CODE
+ || icmpv6Type == Ipv6Constants.ICMP_V6_NA_CODE;
+ }
+
public long getPacketProcessedCounter() {
return pktProccessedCounter.get();
}
} else if (type == Ipv6Constants.ICMP_V6_RS_CODE) {
LOG.info("Received Router Solicitation request");
processRouterSolicitationRequest();
+ } else if (type == Ipv6Constants.ICMP_V6_NA_CODE) {
+ LOG.trace("Received Neighbor Advertisement packet");
+ processNeighborAdvertisementPacket();
}
}
}
return rsPdu.build();
}
+
+ private void processNeighborAdvertisementPacket() {
+ byte[] data = packet.getPayload();
+ NeighborAdvertisePacket naPdu;
+ try {
+ naPdu = deserializeNAPacket(data);
+ } catch (UnknownHostException | BufferException e) {
+ LOG.warn("Exception occured during deserializing NA packet", e);
+ return;
+ }
+ Ipv6Header ipv6Header = naPdu;
+ if (Ipv6ServiceUtils.validateChecksum(data, ipv6Header, naPdu.getIcmp6Chksum()) == false) {
+ pktProccessedCounter.incrementAndGet();
+ LOG.warn("Received Neighbor Advertisement with invalid checksum on {}. Ignoring the packet.",
+ packet.getIngress());
+ return;
+ }
+
+ short tableId = packet.getTableId().getValue();
+ BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
+ long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
+ String interfaceName = ifMgr.getInterfaceNameFromTag(portTag);
+
+ NaReceivedBuilder naReceivedBuilder = new NaReceivedBuilder().setSourceMac(naPdu.getSourceMac())
+ .setDestinationMac(naPdu.getDestinationMac()).setSourceIpv6(naPdu.getSourceIpv6())
+ .setDestinationIpv6(naPdu.getDestinationIpv6()).setTargetAddress(naPdu.getTargetAddress())
+ .setTargetLlAddress(naPdu.getTargetLlAddress()).setOfTableId((long) tableId).setMetadata(metadata)
+ .setInterface(interfaceName);
+ fireNotification(naReceivedBuilder.build());
+ }
+
+ private NeighborAdvertisePacket deserializeNAPacket(byte[] data) throws BufferException, UnknownHostException {
+ NeighborAdvertisePacketBuilder naPdu = new NeighborAdvertisePacketBuilder();
+ int bitOffset = 0;
+
+ naPdu.setDestinationMac(
+ new MacAddress(Ipv6ServiceUtils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
+ bitOffset = bitOffset + 48;
+ naPdu.setSourceMac(
+ new MacAddress(Ipv6ServiceUtils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
+ bitOffset = bitOffset + 48;
+ naPdu.setEthertype(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+
+ bitOffset = Ipv6Constants.IP_V6_HDR_START;
+ naPdu.setVersion(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 4)));
+ bitOffset = bitOffset + 4;
+ naPdu.setFlowLabel(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 28)));
+ bitOffset = bitOffset + 28;
+ naPdu.setIpv6Length(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+ bitOffset = bitOffset + 16;
+ naPdu.setNextHeader(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ naPdu.setHopLimit(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ naPdu.setSourceIpv6(Ipv6Address.getDefaultInstance(
+ InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+ bitOffset = bitOffset + 128;
+ naPdu.setDestinationIpv6(Ipv6Address.getDefaultInstance(
+ InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+ bitOffset = bitOffset + 128;
+
+ naPdu.setIcmp6Type(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ naPdu.setIcmp6Code(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ naPdu.setIcmp6Chksum(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
+ bitOffset = bitOffset + 16;
+ naPdu.setFlags(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 32)));
+ bitOffset = bitOffset + 32;
+ naPdu.setTargetAddress(Ipv6Address.getDefaultInstance(
+ InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
+
+ if (naPdu.getIpv6Length() > Ipv6Constants.ICMPV6_NA_LENGTH_WO_OPTIONS) {
+ bitOffset = bitOffset + 128;
+ naPdu.setOptionType(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ naPdu.setTargetAddrLength(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
+ bitOffset = bitOffset + 8;
+ if (naPdu.getOptionType() == Ipv6Constants.ICMP_V6_OPTION_TARGET_LLA) {
+ naPdu.setTargetLlAddress(new MacAddress(
+ Ipv6ServiceUtils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
+ }
+ }
+ return naPdu.build();
+ }
+ }
+
+ private void fireNotification(Notification notification) {
+ ListenableFuture<?> offerNotification = notificationPublishService.offerNotification(notification);
+ if (offerNotification != null && offerNotification.equals(NotificationPublishService.REJECTED)) {
+ LOG.warn("Offered Notification was rejected: {}", notification);
+ }
}
@Override
private final IfMgr ifMgr;
private final Ipv6ServiceUtils ipv6ServiceUtils;
private final JobCoordinator jobCoordinator;
+ private final Ipv6ServiceEosHandler ipv6ServiceEosHandler;
/**
* Intialize the member variables.
*/
@Inject
public Ipv6ServiceInterfaceEventListener(DataBroker broker, IfMgr ifMgr, Ipv6ServiceUtils ipv6ServiceUtils,
- final JobCoordinator jobCoordinator) {
+ final JobCoordinator jobCoordinator, Ipv6ServiceEosHandler ipv6ServiceEosHandler) {
super(Interface.class, Ipv6ServiceInterfaceEventListener.class);
this.dataBroker = broker;
this.ifMgr = ifMgr;
this.ipv6ServiceUtils = ipv6ServiceUtils;
this.jobCoordinator = jobCoordinator;
+ this.ipv6ServiceEosHandler = ipv6ServiceEosHandler;
}
@PostConstruct
return;
}
+ if (!ipv6ServiceEosHandler.isClusterOwner()) {
+ LOG.trace("Not a cluster Owner, skipping further IPv6 processing on this node.");
+ return;
+ }
Uuid portId = new Uuid(del.getName());
VirtualPort port = ifMgr.obtainV6Interface(portId);
if (port == null) {
return Collections.emptyList();
}, SystemPropertyReader.getDataStoreJobCoordinatorMaxRetries());
}
+
+ VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
+ ifMgr.handleInterfaceStateEvent(port, ipv6ServiceUtils.getDpIdFromInterfaceState(del), routerPort,
+ Ipv6Constants.DEL_FLOW);
}
@Override
Long ofPort = MDSALUtil.getOfPortNumberFromPortName(nodeConnectorId);
ifMgr.updateDpnInfo(portId, dpId, ofPort);
+ if (!ipv6ServiceEosHandler.isClusterOwner()) {
+ LOG.trace("Not a cluster Owner, skipping further IPv6 processing on this node.");
+ return;
+ }
+
VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
if (routerPort == null) {
LOG.info("Port {} is not associated to a Router, skipping.", portId);
return;
}
- // Check and program icmpv6 punt flows on the dpnID if its the first VM on the host.
- ifMgr.programIcmpv6PuntFlowsIfNecessary(portId, dpId, routerPort);
+ ifMgr.handleInterfaceStateEvent(port, dpId, routerPort, Ipv6Constants.ADD_FLOW);
if (!port.getServiceBindingStatus()) {
jobCoordinator.enqueueJob("IPv6-" + String.valueOf(portId), () -> {
LOG.debug("update port notification handler is invoked for port {} ", update);
- Set<FixedIps> oldIPs = getFixedIpSet(original.getFixedIps());
- Set<FixedIps> newIPs = getFixedIpSet(update.getFixedIps());
- if (!oldIPs.equals(newIPs)) {
+ Set<FixedIps> ipsBefore = getFixedIpSet(original.getFixedIps());
+ Set<FixedIps> ipsAfter = getFixedIpSet(update.getFixedIps());
+
+ Set<FixedIps> deletedIps = new HashSet<>(ipsBefore);
+ deletedIps.removeAll(ipsAfter);
+
+ if (!ipsBefore.equals(ipsAfter)) {
Boolean portIncludesV6Address = Boolean.FALSE;
ifMgr.clearAnyExistingSubnetInfo(update.getUuid());
- List<FixedIps> ipList = update.getFixedIps();
- for (FixedIps fixedip : ipList) {
+
+ Set<FixedIps> remainingIps = new HashSet<>(ipsAfter);
+ remainingIps.removeAll(deletedIps);
+ for (FixedIps fixedip : remainingIps) {
if (fixedip.getIpAddress().getIpv4Address() != null) {
continue;
}
}
if (update.getDeviceOwner().equalsIgnoreCase(Ipv6Constants.NETWORK_ROUTER_INTERFACE)) {
- ifMgr.updateRouterIntf(update.getUuid(), new Uuid(update.getDeviceId()), update.getFixedIps());
+ ifMgr.updateRouterIntf(update.getUuid(), new Uuid(update.getDeviceId()), update.getFixedIps(),
+ deletedIps);
} else {
ifMgr.updateHostIntf(update.getUuid(), portIncludesV6Address);
}
import java.util.stream.Stream;
import org.opendaylight.netvirt.ipv6service.api.IVirtualNetwork;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
+import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
dpnIfaceList.values().forEach(dpnInterfaceInfo -> {
dpnInterfaceInfo.clearOfPortList();
dpnInterfaceInfo.clearNdTargetFlowInfo();
+ dpnInterfaceInfo.clearsubnetCidrPuntFlowInfo();
});
clearDpnInterfaceList();
public static class DpnInterfaceInfo {
BigInteger dpId;
int rsPuntFlowConfigured;
- Set<Long> ofPortList;
- Set<Ipv6Address> ndTargetFlowsPunted;
+ final Set<Uuid> subnetCidrPuntFlowList = ConcurrentHashMap.newKeySet();
+ final Set<Long> ofPortList = ConcurrentHashMap.newKeySet();
+ final Set<Ipv6Address> ndTargetFlowsPunted = ConcurrentHashMap.newKeySet();
DpnInterfaceInfo(BigInteger dpnId) {
dpId = dpnId;
- ofPortList = ConcurrentHashMap.newKeySet();
- ndTargetFlowsPunted = ConcurrentHashMap.newKeySet();
rsPuntFlowConfigured = Ipv6Constants.FLOWS_NOT_CONFIGURED;
}
return rsPuntFlowConfigured;
}
+ public void updateSubnetCidrFlowStatus(Uuid subnetUUID, int addOrRemove) {
+ if (addOrRemove == Ipv6Constants.ADD_FLOW) {
+ this.subnetCidrPuntFlowList.add(subnetUUID);
+ } else {
+ this.subnetCidrPuntFlowList.remove(subnetUUID);
+ }
+ }
+
+ public boolean isSubnetCidrFlowAlreadyConfigured(Uuid subnetUUID) {
+ return subnetCidrPuntFlowList.contains(subnetUUID);
+ }
+
public Set<Ipv6Address> getNDTargetFlows() {
return ndTargetFlowsPunted;
}
public void updateNDTargetAddress(Ipv6Address ipv6Address, int addOrRemove) {
+ Ipv6Address ipv6 =
+ Ipv6Address.getDefaultInstance(Ipv6ServiceUtils.getFormattedIpv6Address(ipv6Address.getValue()));
if (addOrRemove == Ipv6Constants.ADD_ENTRY) {
- this.ndTargetFlowsPunted.add(ipv6Address);
+ this.ndTargetFlowsPunted.add(ipv6);
} else {
- this.ndTargetFlowsPunted.remove(ipv6Address);
+ this.ndTargetFlowsPunted.remove(ipv6);
}
}
+ public boolean isNdTargetFlowAlreadyConfigured(Ipv6Address ipv6Address) {
+ Ipv6Address ipv6 =
+ Ipv6Address.getDefaultInstance(Ipv6ServiceUtils.getFormattedIpv6Address(ipv6Address.getValue()));
+ return this.ndTargetFlowsPunted.contains(ipv6);
+ }
+
public void clearNdTargetFlowInfo() {
this.ndTargetFlowsPunted.clear();
}
this.ofPortList.clear();
}
+ public void clearsubnetCidrPuntFlowInfo() {
+ this.subnetCidrPuntFlowList.clear();
+ }
+
@Override
public String toString() {
- return "DpnInterfaceInfo [dpId=" + dpId + " rsPuntFlowConfigured=" + rsPuntFlowConfigured + " ofPortList="
+ return "DpnInterfaceInfo [dpId=" + dpId + " rsPuntFlowConfigured=" + rsPuntFlowConfigured
+ + "subnetCidrPuntFlowList=" + subnetCidrPuntFlowList + " ofPortList="
+ ofPortList + "]";
}
}
int ICMPV6_OPTION_SOURCE_LLA_LENGTH = 8;
int ICMPV6_OPTION_PREFIX_LENGTH = 32;
+ int ICMPV6_NA_LENGTH_WO_OPTIONS = 24;
+
int IPV6_DEFAULT_HOP_LIMIT = 64;
int IPV6_ROUTER_LIFETIME = 4500;
int IPV6_RA_VALID_LIFETIME = 2592000;
String NETWORK_ROUTER_INTERFACE = "network:router_interface";
String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
String DEVICE_OWNER_DHCP = "network:dhcp";
+ String DEVICE_OWNER_COMPUTE_NOVA = "compute:nova";
BigInteger INVALID_DPID = new BigInteger("-1");
short DEFAULT_FLOW_PRIORITY = 50;
+ short PUNT_NA_FLOW_PRIORITY = 40;
String FLOWID_PREFIX = "IPv6.";
String FLOWID_SEPARATOR = ".";
import org.opendaylight.genius.mdsalutil.MatchInfo;
import org.opendaylight.genius.mdsalutil.MetaDataUtil;
import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
import org.opendaylight.genius.mdsalutil.actions.ActionPuntToController;
import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
import org.opendaylight.genius.mdsalutil.matches.MatchIcmpv6;
import org.opendaylight.genius.mdsalutil.matches.MatchIpProtocol;
import org.opendaylight.genius.mdsalutil.matches.MatchIpv6NdTarget;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
import org.opendaylight.genius.utils.ServiceIndex;
+import org.opendaylight.netvirt.ipv6service.api.IVirtualPort;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServicesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
import org.opendaylight.yangtools.yang.binding.DataObject;
return matches;
}
+ private List<MatchInfo> getIcmpv6NAMatch(Long elanTag) {
+ List<MatchInfo> matches = new ArrayList<>();
+ matches.add(MatchEthernetType.IPV6);
+ matches.add(MatchIpProtocol.ICMPV6);
+ matches.add(new MatchIcmpv6(Ipv6Constants.ICMP_V6_NA_CODE, (short) 0));
+ matches.add(new MatchMetadata(MetaDataUtil.getElanTagMetadata(elanTag), MetaDataUtil.METADATA_MASK_SERVICE));
+ return matches;
+ }
+
private static String getIPv6FlowRef(BigInteger dpId, Long elanTag, String flowType) {
return new StringBuffer().append(Ipv6Constants.FLOWID_PREFIX)
.append(dpId).append(Ipv6Constants.FLOWID_SEPARATOR)
.append(flowType).toString();
}
+ /**
+ * Gets the formatted IPv6 address. <br>
+ * e.g., <br>
+ * 1. input = "1001:db8:0:2::1", return = "1001:db8:0:2:0:0:0:1" <br>
+ * 2. input = "2607:f0d0:1002:51::4", return = "2607:f0d0:1002:51:0:0:0:4" <br>
+ * 3. input = "1001:db8:0:2:0:0:0:1", return = "1001:db8:0:2:0:0:0:1"
+ *
+ * @param ipv6Address the ipv6 address
+ * @return the formatted ipv6 address
+ */
+ public static String getFormattedIpv6Address(String ipv6Address) {
+ try {
+ return InetAddress.getByName(ipv6Address).getHostAddress();
+ } catch (UnknownHostException e) {
+ LOG.warn("Unknown host {}", ipv6Address, e);
+ return null;
+ }
+ }
+
public void installIcmpv6NsPuntFlow(short tableId, BigInteger dpId, Long elanTag, String ipv6Address,
int addOrRemove) {
List<MatchInfo> neighborSolicitationMatch = getIcmpv6NSMatch(elanTag, ipv6Address);
List<ActionInfo> actionsInfos = new ArrayList<>();
actionsInfos.add(new ActionPuntToController());
instructions.add(new InstructionApplyActions(actionsInfos));
+
+ String formattedIp = getFormattedIpv6Address(ipv6Address);
FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
- getIPv6FlowRef(dpId, elanTag, ipv6Address),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
+ getIPv6FlowRef(dpId, elanTag, formattedIp),Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NS",
0, 0, NwConstants.COOKIE_IPV6_TABLE, neighborSolicitationMatch, instructions);
if (addOrRemove == Ipv6Constants.DEL_FLOW) {
LOG.trace("Removing IPv6 Neighbor Solicitation Flow DpId {}, elanTag {}", dpId, elanTag);
}
}
+ public void installIcmpv6NaForwardFlow(short tableId, IVirtualPort vmPort, BigInteger dpId, Long elanTag,
+ int addOrRemove) {
+ List<MatchInfo> matches = getIcmpv6NAMatch(elanTag);
+ List<InstructionInfo> instructions = new ArrayList<>();
+ List<ActionInfo> actionsInfos = new ArrayList<>();
+ actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
+ instructions.add(new InstructionApplyActions(actionsInfos));
+
+ for (Ipv6Address ipv6Address : vmPort.getIpv6Addresses()) {
+ matches.add(new MatchIpv6Source(ipv6Address.getValue() + NwConstants.IPV6PREFIX));
+ String flowId = getIPv6FlowRef(dpId, elanTag,
+ vmPort.getIntfUUID().getValue() + Ipv6Constants.FLOWID_SEPARATOR + ipv6Address.getValue());
+ FlowEntity rsFlowEntity =
+ MDSALUtil.buildFlowEntity(dpId, tableId, flowId, Ipv6Constants.DEFAULT_FLOW_PRIORITY, "IPv6NA", 0,
+ 0, NwConstants.COOKIE_IPV6_TABLE, matches, instructions);
+ if (addOrRemove == Ipv6Constants.DEL_FLOW) {
+ LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
+ elanTag, ipv6Address.getValue());
+ mdsalUtil.removeFlow(rsFlowEntity);
+ } else {
+ LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}, ipv6Address {}", dpId,
+ elanTag, ipv6Address.getValue());
+ mdsalUtil.installFlow(rsFlowEntity);
+ }
+ }
+ }
+
+ public void installIcmpv6NaPuntFlow(short tableId, Ipv6Prefix ipv6Prefix, BigInteger dpId, Long elanTag,
+ int addOrRemove) {
+ List<MatchInfo> naMatch = getIcmpv6NAMatch(elanTag);
+ naMatch.add(new MatchIpv6Source(ipv6Prefix));
+
+ List<InstructionInfo> instructions = new ArrayList<>();
+ List<ActionInfo> actionsInfos = new ArrayList<>();
+ actionsInfos.add(new ActionPuntToController());
+ actionsInfos.add(new ActionNxResubmit(NwConstants.LPORT_DISPATCHER_TABLE));
+ instructions.add(new InstructionApplyActions(actionsInfos));
+
+ String flowId = getIPv6FlowRef(dpId, elanTag, "IPv6NA." + ipv6Prefix.getValue());
+ FlowEntity rsFlowEntity = MDSALUtil.buildFlowEntity(dpId, tableId,
+ flowId, Ipv6Constants.PUNT_NA_FLOW_PRIORITY,
+ "IPv6NA", 0, 0, NwConstants.COOKIE_IPV6_TABLE, naMatch, instructions);
+ if (addOrRemove == Ipv6Constants.DEL_FLOW) {
+ LOG.trace("Removing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
+ mdsalUtil.removeFlow(rsFlowEntity);
+ } else {
+ LOG.trace("Installing IPv6 Neighbor Advertisement Flow DpId {}, elanTag {}", dpId, elanTag);
+ mdsalUtil.installFlow(rsFlowEntity);
+ }
+ }
+
public BoundServices getBoundServices(String serviceName, short servicePriority, int flowPriority,
BigInteger cookie, List<Instruction> instructions) {
StypeOpenflowBuilder augBuilder = new StypeOpenflowBuilder().setFlowCookie(cookie)
NwConstants.IPV6_SERVICE_INDEX)));
}
+ public BigInteger getDpIdFromInterfaceState(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf
+ .interfaces.rev140508.interfaces.state.Interface interfaceState) {
+ BigInteger dpId = null;
+ List<String> ofportIds = interfaceState.getLowerLayerIf();
+ if (ofportIds != null && !ofportIds.isEmpty()) {
+ NodeConnectorId nodeConnectorId = new NodeConnectorId(ofportIds.get(0));
+ dpId = BigInteger.valueOf(MDSALUtil.getDpnIdFromPortName(nodeConnectorId));
+ }
+ return dpId;
+ }
+
public static long getRemoteBCGroup(long elanTag) {
return Ipv6Constants.ELAN_GID_MIN + elanTag % Ipv6Constants.ELAN_GID_MIN * 2;
}
+
+ public static boolean isVmPort(String deviceOwner) {
+ // FIXME: Currently for VM ports, Neutron is sending deviceOwner as empty instead of "compute:nova".
+ // return Ipv6Constants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner);
+ return Ipv6Constants.DEVICE_OWNER_COMPUTE_NOVA.equalsIgnoreCase(deviceOwner)
+ || StringUtils.isEmpty(deviceOwner);
+ }
}
interface="org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService" />
<reference id="jobCoordinator"
interface="org.opendaylight.infrautils.jobcoordinator.JobCoordinator" />
+ <reference id="notificationPublishService"
+ interface="org.opendaylight.controller.md.sal.binding.api.NotificationPublishService" />
<odl:rpc-service id="odlInterfaceRpcService"
interface="org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService" />
<odl:rpc-service id="packetProcessingService"
interface="org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService" />
+ <odl:rpc-implementation ref="ipv6NdUtilServiceImpl" />
+
<odl:notification-listener ref="ipv6PktHandler" />
<service ref="ifMgr" interface="org.opendaylight.netvirt.ipv6service.api.ElementCache" />
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
import org.opendaylight.genius.mdsalutil.NwConstants;
import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Metadata;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.MetadataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.NaReceived;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.ipv6util.rev170210.NaReceivedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceivedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.packet.received.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.TableId;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
public class Ipv6PktHandlerTest {
private PacketProcessingService pktProcessService;
private Ipv6PktHandler pktHandler;
private IfMgr ifMgrInstance;
+ private NotificationPublishService notificationPublishService;
private long counter;
private static final int THREAD_WAIT_TIME = 100;
private Ipv6TestUtils ipv6TestUtils;
public void initTest() {
pktProcessService = Mockito.mock(PacketProcessingService.class);
ifMgrInstance = Mockito.mock(IfMgr.class);
- pktHandler = new Ipv6PktHandler(pktProcessService, ifMgrInstance);
+ notificationPublishService = Mockito.mock(NotificationPublishService.class);
+ pktHandler = new Ipv6PktHandler(pktProcessService, ifMgrInstance, notificationPublishService);
counter = pktHandler.getPacketProcessedCounter();
ipv6TestUtils = new Ipv6TestUtils();
}
.setEgress(ncRef).setIngress(ncRef).setAction(any(List.class)).build());
}
+ @Test
+ public void testonPacketReceivedNeighborAdvertisementWithValidPayload() throws Exception {
+ VirtualPort intf = Mockito.mock(VirtualPort.class);
+ when(intf.getNetworkID()).thenReturn(new Uuid("eeec9dba-d831-4ad7-84b9-00d7f65f0555"));
+ when(ifMgrInstance.getInterfaceNameFromTag(anyLong())).thenReturn("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
+ when(ifMgrInstance.obtainV6Interface(any())).thenReturn(intf);
+
+ InstanceIdentifier<Node> ncId = InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
+ NodeConnectorRef ncRef = new NodeConnectorRef(ncId);
+
+ BigInteger mdata = new BigInteger(String.valueOf(0x1000000));
+ Metadata metadata = new MetadataBuilder().setMetadata(mdata).build();
+ MatchBuilder matchbuilder = new MatchBuilder().setMetadata(metadata);
+ pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(ipv6TestUtils.buildPacket(
+ "FA 16 3E F7 69 4E", // Destination MAC
+ "FA 16 3E A9 38 94", // Source MAC
+ "86 DD", // IPv6
+ "60 00 00 00", // Version 6, traffic class 0, no flowlabel
+ "00 20", // Payload length
+ "3A", // Next header is ICMPv6
+ "FF", // Hop limit
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Source IP
+ "10 01 0D B8 00 00 00 02 F8 16 3E FF FE F7 69 4E", // Destination IP
+ "88", // ICMPv6 neighbor advertisement
+ "00", // Code
+ "C9 9F", // Checksum (valid)
+ "00 00 00 00", // ICMPv6 message body
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Target
+ "02", // ICMPv6 Option: Target Link Layer Address
+ "01", // Length
+ "FA 16 3E A9 38 94" // Link Layer Address
+ )).setIngress(ncRef).setMatch(matchbuilder.build()).setTableId(new TableId((short) 45)).build());
+ //wait on this thread until the async job is completed in the packet handler.
+ waitForPacketProcessing();
+
+ NaReceivedBuilder naReceivedBuilder = new NaReceivedBuilder().setSourceMac(new MacAddress("fa:16:3e:a9:38:94"))
+ .setDestinationMac(new MacAddress("fa:16:3e:f7:69:4e"))
+ .setSourceIpv6(Ipv6Address.getDefaultInstance("2001:db8:0:2:0:0:0:1111"))
+ .setDestinationIpv6(Ipv6Address.getDefaultInstance("1001:db8:0:2:f816:3eff:fef7:694e"))
+ .setTargetAddress(Ipv6Address.getDefaultInstance("2001:db8:0:2:0:0:0:1111"))
+ .setTargetLlAddress(new MacAddress("fa:16:3e:a9:38:94")).setOfTableId(45L).setMetadata(mdata)
+ .setInterface("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
+ verify(notificationPublishService).offerNotification(naReceivedBuilder.build());
+ }
+
+ @Test
+ public void testonPacketReceivedNeighborAdvertisementWithInvalidPayload() throws Exception {
+ //incorrect checksum
+ VirtualPort intf = Mockito.mock(VirtualPort.class);
+ when(intf.getNetworkID()).thenReturn(new Uuid("eeec9dba-d831-4ad7-84b9-00d7f65f0555"));
+ when(ifMgrInstance.getInterfaceNameFromTag(anyLong())).thenReturn("ddec9dba-d831-4ad7-84b9-00d7f65f052f");
+ when(ifMgrInstance.obtainV6Interface(any())).thenReturn(intf);
+
+ InstanceIdentifier<Node> ncId = InstanceIdentifier.builder(Nodes.class)
+ .child(Node.class, new NodeKey(new NodeId("openflow:1"))).build();
+ NodeConnectorRef ncRef = new NodeConnectorRef(ncId);
+
+ BigInteger mdata = new BigInteger(String.valueOf(0x1000000));
+ Metadata metadata = new MetadataBuilder().setMetadata(mdata).build();
+ MatchBuilder matchbuilder = new MatchBuilder().setMetadata(metadata);
+
+ // incorrect checksum
+ pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(ipv6TestUtils.buildPacket(
+ "FA 16 3E F7 69 4E", // Destination MAC
+ "FA 16 3E A9 38 94", // Source MAC
+ "86 DD", // IPv6
+ "60 00 00 00", // Version 6, traffic class 0, no flowlabel
+ "00 20", // Payload length
+ "3A", // Next header is ICMPv6
+ "FF", // Hop limit
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Source IP
+ "10 01 0D B8 00 00 00 02 F8 16 3E FF FE F7 69 4E", // Destination IP
+ "88", // ICMPv6 neighbor advertisement
+ "00", // Code
+ "C9 9A", // Checksum (valid)
+ "00 00 00 00", // ICMPv6 message body
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Target
+ "02", // ICMPv6 Option: Target Link Layer Address
+ "01", // Length
+ "FA 16 3E A9 38 94" // Link Layer Address
+ )).setIngress(ncRef).setMatch(matchbuilder.build()).setTableId(new TableId((short) 45)).build());
+ //wait on this thread until the async job is completed in the packet handler.
+ waitForPacketProcessing();
+ verify(notificationPublishService, times(0)).offerNotification(any(NaReceived.class));
+
+ // incorrect ICMPv6 type
+ pktHandler.onPacketReceived(new PacketReceivedBuilder().setPayload(ipv6TestUtils.buildPacket(
+ "FA 16 3E F7 69 4E", // Destination MAC
+ "FA 16 3E A9 38 94", // Source MAC
+ "86 DD", // IPv6
+ "60 00 00 00", // Version 6, traffic class 0, no flowlabel
+ "00 20", // Payload length
+ "3A", // Next header is ICMPv6
+ "FF", // Hop limit
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Source IP
+ "10 01 0D B8 00 00 00 02 F8 16 3E FF FE F7 69 4E", // Destination IP
+ "87", // ICMPv6 neighbor solicitation (invalid)
+ "00", // Code
+ "C9 9F", // Checksum (valid)
+ "00 00 00 00", // ICMPv6 message body
+ "20 01 0D B8 00 00 00 02 00 00 00 00 00 00 11 11", // Target
+ "02", // ICMPv6 Option: Target Link Layer Address
+ "01", // Length
+ "FA 16 3E A9 38 94" // Link Layer Address
+ )).setIngress(ncRef).setMatch(matchbuilder.build()).setTableId(new TableId((short) 45)).build());
+ //wait on this thread until the async job is completed in the packet handler.
+ waitForPacketProcessing();
+ verify(notificationPublishService, times(0)).offerNotification(any(NaReceived.class));
+ }
+
private void waitForPacketProcessing() throws InterruptedException {
int timeOut = 1;
while (timeOut < 20) {